diff --git a/Jenkinsfile b/Jenkinsfile index 570994b47a..a810cc389a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,7 +3,7 @@ pipeline { triggers { pollSCM 'H/10 * * * *' - upstream(upstreamProjects: "spring-data-commons/master", threshold: hudson.model.Result.SUCCESS) + upstream(upstreamProjects: "spring-data-commons/2.5.x", threshold: hudson.model.Result.SUCCESS) } options { @@ -68,7 +68,7 @@ pipeline { stage("test: baseline (jdk8)") { when { anyOf { - branch 'master' + branch '3.2.x' not { triggeredBy 'UpstreamCause' } } } @@ -76,6 +76,9 @@ pipeline { label 'data' } options { timeout(time: 30, unit: 'MINUTES') } + environment { + ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + } steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { @@ -85,7 +88,7 @@ pipeline { sh 'sleep 10' sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"' sh 'sleep 15' - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B' + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B' } } } @@ -95,7 +98,7 @@ pipeline { stage("Test other configurations") { when { allOf { - branch 'master' + branch '3.2.x' not { triggeredBy 'UpstreamCause' } } } @@ -105,6 +108,9 @@ pipeline { label 'data' } options { timeout(time: 30, unit: 'MINUTES') } + environment { + ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + } steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { @@ -114,7 +120,7 @@ pipeline { sh 'sleep 10' sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"' sh 'sleep 15' - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B' + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B' } } } @@ -126,6 +132,9 @@ pipeline { label 'data' } options { timeout(time: 30, unit: 'MINUTES') } + environment { + ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + } steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { @@ -135,7 +144,7 @@ pipeline { sh 'sleep 10' sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"' sh 'sleep 15' - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Duser.name=jenkins -Dsort -U -B' + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml clean dependency:list test -Duser.name=jenkins -Dsort -U -B' } } } @@ -147,6 +156,9 @@ pipeline { label 'data' } options { timeout(time: 30, unit: 'MINUTES') } + environment { + ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c') + } steps { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { @@ -156,7 +168,7 @@ pipeline { sh 'sleep 10' sh 'mongo --eval "rs.initiate({_id: \'rs0\', members:[{_id: 0, host: \'127.0.0.1:27017\'}]});"' sh 'sleep 15' - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Duser.name=jenkins -Dsort -U -B' + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pjava11 clean dependency:list test -Duser.name=jenkins -Dsort -U -B' } } } @@ -168,7 +180,7 @@ pipeline { stage('Release to artifactory') { when { anyOf { - branch 'master' + branch '3.2.x' not { triggeredBy 'UpstreamCause' } } } @@ -185,7 +197,7 @@ pipeline { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,artifactory ' + + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory ' + '-Dartifactory.server=https://repo.spring.io ' + "-Dartifactory.username=${ARTIFACTORY_USR} " + "-Dartifactory.password=${ARTIFACTORY_PSW} " + @@ -201,7 +213,7 @@ pipeline { stage('Publish documentation') { when { - branch 'master' + branch '3.2.x' } agent { label 'data' @@ -216,7 +228,7 @@ pipeline { script { docker.withRegistry('', 'hub.docker.com-springbuildmaster') { docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') { - sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,distribute ' + + sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute ' + '-Dartifactory.server=https://repo.spring.io ' + "-Dartifactory.username=${ARTIFACTORY_USR} " + "-Dartifactory.password=${ARTIFACTORY_PSW} " + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..ff77379631 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/pom.xml b/pom.xml index afb8a87e61..d25b0bf19a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 3.2.0 + 3.2.1 pom Spring Data MongoDB @@ -15,7 +15,7 @@ org.springframework.data.build spring-data-parent - 2.5.0 + 2.5.1 @@ -26,7 +26,7 @@ multi spring-data-mongodb - 2.5.0 + 2.5.1 4.2.3 ${mongo} 1.19 @@ -158,11 +158,6 @@ spring-libs-milestone https://repo.spring.io/libs-milestone - - bintray-plugins - bintray-plugins - https://jcenter.bintray.com - diff --git a/settings.xml b/settings.xml new file mode 100644 index 0000000000..b3227cc110 --- /dev/null +++ b/settings.xml @@ -0,0 +1,29 @@ + + + + + spring-plugins-release + ${env.ARTIFACTORY_USR} + ${env.ARTIFACTORY_PSW} + + + spring-libs-snapshot + ${env.ARTIFACTORY_USR} + ${env.ARTIFACTORY_PSW} + + + spring-libs-milestone + ${env.ARTIFACTORY_USR} + ${env.ARTIFACTORY_PSW} + + + spring-libs-release + ${env.ARTIFACTORY_USR} + ${env.ARTIFACTORY_PSW} + + + + \ No newline at end of file diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index 43e83d8175..8492697f33 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 - 3.2.0 + 3.2.1 ../pom.xml diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 8988a14c40..22481b2e4a 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 - 3.2.0 + 3.2.1 ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index e36a1bb829..fce0162425 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 3.2.0 + 3.2.1 ../pom.xml @@ -316,31 +316,6 @@ - - com.mysema.maven - apt-maven-plugin - ${apt} - - - com.querydsl - querydsl-apt - ${querydsl} - - - - - generate-test-sources - - test-process - - - target/generated-test-sources - org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor - - - - - org.apache.maven.plugins maven-surefire-plugin diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index d6743db9d0..aed4747bbf 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -1565,7 +1565,6 @@ protected Mono insertDocument(String collectionName, Document dbDoc, Cla } protected Flux insertDocumentList(String collectionName, List dbDocList) { - if (dbDocList.isEmpty()) { return Flux.empty(); } @@ -1582,10 +1581,10 @@ protected Flux insertDocumentList(String collectionName, List collectionToUse = prepareCollection(collection, writeConcernToUse); - + InsertManyOptions insertManyOptions =new InsertManyOptions(); + insertManyOptions.ordered(false); documents.addAll(toDocuments(dbDocList)); - - return collectionToUse.insertMany(documents); + return collectionToUse.insertMany(documents, insertManyOptions); }).flatMap(s -> { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java index 699e7b158d..5a023b2b09 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java @@ -39,7 +39,6 @@ import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments; import org.springframework.data.repository.core.support.RepositoryFactorySupport; -import org.springframework.data.repository.core.support.RepositoryFragment; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; @@ -92,8 +91,21 @@ protected Class getRepositoryBaseClass(RepositoryMetadata metadata) { */ @Override protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) { + return getRepositoryFragments(metadata, operations); + } - RepositoryFragments fragments = RepositoryFragments.empty(); + /** + * Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add Mongo-specific extensions. Typically + * adds a {@link QuerydslMongoPredicateExecutor} if the repository interface uses Querydsl. + *

+ * Can be overridden by subclasses to customize {@link RepositoryFragments}. + * + * @param metadata repository metadata. + * @param operations the MongoDB operations manager. + * @return + * @since 3.2.1 + */ + protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata, MongoOperations operations) { boolean isQueryDslRepository = QUERY_DSL_PRESENT && QuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface()); @@ -105,14 +117,11 @@ protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata "Cannot combine Querydsl and reactive repository support in a single interface"); } - MongoEntityInformation entityInformation = getEntityInformation(metadata.getDomainType(), - metadata); - - fragments = fragments.append(RepositoryFragment.implemented( - getTargetRepositoryViaReflection(QuerydslMongoPredicateExecutor.class, entityInformation, operations))); + return RepositoryFragments + .just(new QuerydslMongoPredicateExecutor<>(getEntityInformation(metadata.getDomainType()), operations)); } - return fragments; + return RepositoryFragments.empty(); } /* diff --git a/spring-data-mongodb/src/test/java/ConfigClassInDefaultPackage.java b/spring-data-mongodb/src/test/java/ConfigClassInDefaultPackage.java deleted file mode 100644 index 01816ff4f9..0000000000 --- a/spring-data-mongodb/src/test/java/ConfigClassInDefaultPackage.java +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright 2014-2021 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. - */ - -import java.util.Collections; -import java.util.Set; - -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; - -/** - * Sample configuration class in default package. - * - * @author Oliver Gierke - */ -@Configuration -public class ConfigClassInDefaultPackage extends AbstractMongoClientConfiguration { - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.config.AbstractMongoClientConfiguration#getDatabaseName() - */ - @Override - protected String getDatabaseName() { - return "default"; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.config.AbstractMongoClientConfiguration#mongoClient() - */ - @Override - public MongoClient mongoClient() { - return MongoClients.create(); - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } -} diff --git a/spring-data-mongodb/src/test/java/ConfigClassInDefaultPackageUnitTests.java b/spring-data-mongodb/src/test/java/ConfigClassInDefaultPackageUnitTests.java deleted file mode 100644 index 7f59a436d7..0000000000 --- a/spring-data-mongodb/src/test/java/ConfigClassInDefaultPackageUnitTests.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2014-2021 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. - */ - -import org.junit.jupiter.api.Test; - -import org.springframework.context.annotation.AnnotationConfigApplicationContext; - -/** - * Unit test for {@link ConfigClassInDefaultPackage}. - * - * @author Oliver Gierke - */ -public class ConfigClassInDefaultPackageUnitTests { - - @Test // DATAMONGO-877 - public void loadsConfigClassFromDefaultPackage() { - new AnnotationConfigApplicationContext(ConfigClassInDefaultPackage.class).close(); - } -} diff --git a/spring-data-mongodb/src/test/java/example/first/First.java b/spring-data-mongodb/src/test/java/example/first/First.java deleted file mode 100644 index 2cd7cda056..0000000000 --- a/spring-data-mongodb/src/test/java/example/first/First.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2016-2021 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 example.first; - -import org.springframework.data.mongodb.core.mapping.Document; - -/** - * @author Oliver Gierke - */ -@Document -public class First { - -} diff --git a/spring-data-mongodb/src/test/java/example/second/Second.java b/spring-data-mongodb/src/test/java/example/second/Second.java deleted file mode 100644 index 851f2fca7f..0000000000 --- a/spring-data-mongodb/src/test/java/example/second/Second.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2016-2021 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 example.second; - -import org.springframework.data.mongodb.core.mapping.Document; - -/** - * @author Oliver Gierke - */ -@Document -public class Second { - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/DependencyTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/DependencyTests.java deleted file mode 100644 index 9294c83555..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/DependencyTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017-2021 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; - -import static de.schauderhaft.degraph.check.JCheck.*; -import static org.hamcrest.MatcherAssert.*; - -import de.schauderhaft.degraph.configuration.NamedPattern; - -import org.junit.jupiter.api.Test; - -/** - * Tests package dependency constraints. - * - * @author Jens Schauder - * @author Oliver Gierke - */ -class DependencyTests { - - @Test - void noInternalPackageCycles() { - - assertThat(classpath() // - .noJars() // - .including("org.springframework.data.mongodb.**") // - .filterClasspath("*target/classes") // - .printOnFailure("degraph.graphml"), // - violationFree() // - ); - } - - @Test - void onlyConfigMayUseRepository() { - - assertThat(classpath() // - .including("org.springframework.data.**") // - .filterClasspath("*target/classes") // - .printOnFailure("onlyConfigMayUseRepository.graphml") // - .withSlicing("slices", // - "**.(config).**", // - new NamedPattern("**.cdi.**", "config"), // - "**.(repository).**", // - new NamedPattern("**", "other")) - .allow("config", "repository", "other"), // - violationFree() // - ); - } - - @Test - void commonsInternaly() { - - assertThat(classpath() // - .noJars() // - .including("org.springframework.data.**") // - .excluding("org.springframework.data.mongodb.**") // - .filterClasspath("*target/classes") // - .printTo("commons.graphml"), // - violationFree() // - ); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java deleted file mode 100644 index 8cb222f0e6..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2018-2021 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; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import javax.transaction.Status; -import javax.transaction.UserTransaction; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.jta.JtaTransactionManager; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; -import org.springframework.transaction.support.TransactionSynchronizationManager; -import org.springframework.transaction.support.TransactionTemplate; - -import com.mongodb.client.ClientSession; -import com.mongodb.client.MongoDatabase; -import com.mongodb.session.ServerSession; - -/** - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -class MongoDatabaseUtilsUnitTests { - - @Mock ClientSession session; - @Mock ServerSession serverSession; - @Mock MongoDatabaseFactory dbFactory; - @Mock MongoDatabase db; - - @Mock UserTransaction userTransaction; - - @AfterEach - void verifyTransactionSynchronizationManagerState() { - - assertThat(TransactionSynchronizationManager.getResourceMap().isEmpty()).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - assertThat(TransactionSynchronizationManager.getCurrentTransactionName()).isNull(); - assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse(); - assertThat(TransactionSynchronizationManager.getCurrentTransactionIsolationLevel()).isNull(); - assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse(); - } - - @Test // DATAMONGO-2130 - void isTransactionActiveShouldDetectTxViaFactory() { - - when(dbFactory.isTransactionActive()).thenReturn(true); - - assertThat(MongoDatabaseUtils.isTransactionActive(dbFactory)).isTrue(); - } - - @Test // DATAMONGO-2130 - void isTransactionActiveShouldReturnFalseIfNoTxActive() { - - when(dbFactory.isTransactionActive()).thenReturn(false); - - assertThat(MongoDatabaseUtils.isTransactionActive(dbFactory)).isFalse(); - } - - @Test // DATAMONGO-2130 - void isTransactionActiveShouldLookupTxForActiveTransactionSynchronizationViaTxManager() { - - when(dbFactory.getSession(any())).thenReturn(session); - when(session.getServerSession()).thenReturn(serverSession); - when(session.hasActiveTransaction()).thenReturn(true); - when(serverSession.isClosed()).thenReturn(false); - - when(dbFactory.isTransactionActive()).thenReturn(false); - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { - assertThat(MongoDatabaseUtils.isTransactionActive(dbFactory)).isTrue(); - } - }); - } - - @Test // DATAMONGO-1920 - void shouldNotStartSessionWhenNoTransactionOngoing() { - - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ON_ACTUAL_TRANSACTION); - - verify(dbFactory, never()).getSession(any()); - verify(dbFactory, never()).withSession(any(ClientSession.class)); - } - - @Test // DATAMONGO-1920 - void shouldParticipateInOngoingJtaTransactionWithCommitWhenSessionSychronizationIsAny() throws Exception { - - when(dbFactory.getSession(any())).thenReturn(session); - when(dbFactory.withSession(session)).thenReturn(dbFactory); - when(dbFactory.getMongoDatabase()).thenReturn(db); - when(session.getServerSession()).thenReturn(serverSession); - when(session.hasActiveTransaction()).thenReturn(true); - when(serverSession.isClosed()).thenReturn(false); - - when(userTransaction.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, - Status.STATUS_ACTIVE); - - JtaTransactionManager txManager = new JtaTransactionManager(userTransaction); - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { - - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(transactionStatus.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isFalse(); - - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ALWAYS); - - assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isTrue(); - } - }); - - verify(userTransaction).begin(); - - verify(session).startTransaction(); - verify(session).commitTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-1920 - void shouldParticipateInOngoingJtaTransactionWithRollbackWhenSessionSychronizationIsAny() throws Exception { - - when(dbFactory.getSession(any())).thenReturn(session); - when(dbFactory.withSession(session)).thenReturn(dbFactory); - when(dbFactory.getMongoDatabase()).thenReturn(db); - when(session.getServerSession()).thenReturn(serverSession); - when(session.hasActiveTransaction()).thenReturn(true); - when(serverSession.isClosed()).thenReturn(false); - - when(userTransaction.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, - Status.STATUS_ACTIVE); - - JtaTransactionManager txManager = new JtaTransactionManager(userTransaction); - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { - - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(transactionStatus.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isFalse(); - - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ALWAYS); - - assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isTrue(); - - transactionStatus.setRollbackOnly(); - } - }); - - verify(userTransaction).rollback(); - - verify(session).startTransaction(); - verify(session).abortTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-1920 - void shouldNotParticipateInOngoingJtaTransactionWithRollbackWhenSessionSychronizationIsNative() throws Exception { - - when(userTransaction.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, - Status.STATUS_ACTIVE); - - JtaTransactionManager txManager = new JtaTransactionManager(userTransaction); - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { - - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(transactionStatus.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isFalse(); - - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ON_ACTUAL_TRANSACTION); - - assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isFalse(); - - transactionStatus.setRollbackOnly(); - } - }); - - verify(userTransaction).rollback(); - - verify(session, never()).startTransaction(); - verify(session, never()).abortTransaction(); - verify(session, never()).close(); - } - - @Test // DATAMONGO-1920 - void shouldParticipateInOngoingMongoTransactionWhenSessionSychronizationIsNative() { - - when(dbFactory.getSession(any())).thenReturn(session); - when(dbFactory.withSession(session)).thenReturn(dbFactory); - when(dbFactory.getMongoDatabase()).thenReturn(db); - when(session.getServerSession()).thenReturn(serverSession); - when(serverSession.isClosed()).thenReturn(false); - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { - - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(transactionStatus.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isTrue(); - - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ON_ACTUAL_TRANSACTION); - - transactionStatus.setRollbackOnly(); - } - }); - - verify(session).startTransaction(); - verify(session).abortTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-1920 - void shouldParticipateInOngoingMongoTransactionWhenSessionSynchronizationIsAny() { - - when(dbFactory.getSession(any())).thenReturn(session); - when(dbFactory.withSession(session)).thenReturn(dbFactory); - when(dbFactory.getMongoDatabase()).thenReturn(db); - when(session.getServerSession()).thenReturn(serverSession); - when(serverSession.isClosed()).thenReturn(false); - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { - - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(transactionStatus.isNewTransaction()).isTrue(); - assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isTrue(); - - MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.ALWAYS); - - transactionStatus.setRollbackOnly(); - } - }); - - verify(session).startTransaction(); - verify(session).abortTransaction(); - verify(session).close(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionManagerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionManagerUnitTests.java deleted file mode 100644 index dfb48fdbb1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionManagerUnitTests.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright 2018-2021 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; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.UnexpectedRollbackException; -import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; -import org.springframework.transaction.support.TransactionSynchronizationManager; -import org.springframework.transaction.support.TransactionTemplate; - -import com.mongodb.client.ClientSession; -import com.mongodb.client.MongoDatabase; -import com.mongodb.session.ServerSession; - -/** - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -public class MongoTransactionManagerUnitTests { - - @Mock ClientSession session; - @Mock ClientSession session2; - @Mock ServerSession serverSession; - @Mock MongoDatabaseFactory dbFactory; - @Mock MongoDatabaseFactory dbFactory2; - @Mock MongoDatabase db; - @Mock MongoDatabase db2; - - @BeforeEach - public void setUp() { - - when(dbFactory.getSession(any())).thenReturn(session, session2); - when(dbFactory.withSession(session)).thenReturn(dbFactory); - when(dbFactory.getMongoDatabase()).thenReturn(db); - when(session.getServerSession()).thenReturn(serverSession); - } - - @AfterEach - public void verifyTransactionSynchronizationManager() { - - assertThat(TransactionSynchronizationManager.getResourceMap().isEmpty()).isTrue(); - assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse(); - } - - @Test // DATAMONGO-1920 - public void triggerCommitCorrectly() { - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - TransactionStatus txStatus = txManager.getTransaction(new DefaultTransactionDefinition()); - - MongoTemplate template = new MongoTemplate(dbFactory); - - template.execute(db -> { - db.drop(); - return null; - }); - - verify(dbFactory).withSession(eq(session)); - - txManager.commit(txStatus); - - verify(session).startTransaction(); - verify(session).commitTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-1920 - public void participateInOnGoingTransactionWithCommit() { - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - TransactionStatus txStatus = txManager.getTransaction(new DefaultTransactionDefinition()); - - MongoTemplate template = new MongoTemplate(dbFactory); - - template.execute(db -> { - db.drop(); - return null; - }); - - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - - template.execute(db -> { - db.drop(); - return null; - }); - } - }); - - verify(dbFactory, times(2)).withSession(eq(session)); - - txManager.commit(txStatus); - - verify(session).startTransaction(); - verify(session).commitTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-1920 - public void participateInOnGoingTransactionWithRollbackOnly() { - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - TransactionStatus txStatus = txManager.getTransaction(new DefaultTransactionDefinition()); - - MongoTemplate template = new MongoTemplate(dbFactory); - - template.execute(db -> { - db.drop(); - return null; - }); - - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - - template.execute(db -> { - db.drop(); - return null; - }); - - status.setRollbackOnly(); - } - }); - - verify(dbFactory, times(2)).withSession(eq(session)); - - assertThatExceptionOfType(UnexpectedRollbackException.class).isThrownBy(() -> txManager.commit(txStatus)); - - verify(session).startTransaction(); - verify(session).abortTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-1920 - public void triggerRollbackCorrectly() { - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - TransactionStatus txStatus = txManager.getTransaction(new DefaultTransactionDefinition()); - - MongoTemplate template = new MongoTemplate(dbFactory); - - template.execute(db -> { - db.drop(); - return null; - }); - - verify(dbFactory).withSession(eq(session)); - - txManager.rollback(txStatus); - - verify(session).startTransaction(); - verify(session).abortTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-1920 - public void suspendTransactionWhilePropagationNotSupported() { - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - TransactionStatus txStatus = txManager.getTransaction(new DefaultTransactionDefinition()); - - MongoTemplate template = new MongoTemplate(dbFactory); - - template.execute(db -> { - db.drop(); - return null; - }); - - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - - template.execute(db -> { - db.drop(); - return null; - }); - } - }); - - template.execute(MongoDatabase::listCollections); - txManager.commit(txStatus); - - verify(session).startTransaction(); - verify(session2, never()).startTransaction(); - - verify(dbFactory, times(2)).withSession(eq(session)); - verify(dbFactory, never()).withSession(eq(session2)); - - verify(db, times(2)).drop(); - verify(db).listCollections(); - - verify(session).close(); - verify(session2, never()).close(); - } - - @Test // DATAMONGO-1920 - public void suspendTransactionWhilePropagationRequiresNew() { - - when(dbFactory.withSession(session2)).thenReturn(dbFactory2); - when(dbFactory2.getMongoDatabase()).thenReturn(db2); - when(session2.getServerSession()).thenReturn(serverSession); - when(serverSession.isClosed()).thenReturn(false); - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - TransactionStatus txStatus = txManager.getTransaction(new DefaultTransactionDefinition()); - - MongoTemplate template = new MongoTemplate(dbFactory); - - template.execute(db -> { - db.drop(); - return null; - }); - - TransactionTemplate txTemplate = new TransactionTemplate(txManager); - txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - txTemplate.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - - template.execute(db -> { - db.drop(); - return null; - }); - } - }); - - template.execute(MongoDatabase::listCollections); - txManager.commit(txStatus); - - verify(session).startTransaction(); - verify(session2).startTransaction(); - - verify(dbFactory, times(2)).withSession(eq(session)); - verify(dbFactory).withSession(eq(session2)); - - verify(db).drop(); - verify(db2).drop(); - verify(db).listCollections(); - - verify(session).close(); - verify(session2).close(); - } - - @Test // DATAMONGO-1920 - public void readonlyShouldInitiateASessionStartAndCommitTransaction() { - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - - DefaultTransactionDefinition readonlyTxDefinition = new DefaultTransactionDefinition(); - readonlyTxDefinition.setReadOnly(true); - - TransactionStatus txStatus = txManager.getTransaction(readonlyTxDefinition); - - MongoTemplate template = new MongoTemplate(dbFactory); - - template.execute(db -> { - db.drop(); - return null; - }); - - verify(dbFactory).withSession(eq(session)); - - txManager.commit(txStatus); - - verify(session).startTransaction(); - verify(session).commitTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-1920 - public void readonlyShouldInitiateASessionStartAndRollbackTransaction() { - - MongoTransactionManager txManager = new MongoTransactionManager(dbFactory); - - DefaultTransactionDefinition readonlyTxDefinition = new DefaultTransactionDefinition(); - readonlyTxDefinition.setReadOnly(true); - - TransactionStatus txStatus = txManager.getTransaction(readonlyTxDefinition); - - MongoTemplate template = new MongoTemplate(dbFactory); - - template.execute(db -> { - db.drop(); - return null; - }); - - verify(dbFactory).withSession(eq(session)); - - txManager.rollback(txStatus); - - verify(session).startTransaction(); - verify(session).abortTransaction(); - verify(session).close(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtilsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtilsUnitTests.java deleted file mode 100644 index 60a7ff9a47..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtilsUnitTests.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2019-2021 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; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.transaction.reactive.TransactionSynchronizationManager; -import org.springframework.transaction.reactive.TransactionalOperator; -import org.springframework.transaction.support.DefaultTransactionDefinition; - -import com.mongodb.reactivestreams.client.ClientSession; -import com.mongodb.reactivestreams.client.MongoDatabase; -import com.mongodb.session.ServerSession; - -/** - * Unit tests for {@link ReactiveMongoDatabaseUtils}. - * - * @author Mark Paluch - * @author Christoph Strobl - * @author Mathieu Ouellet - */ -@ExtendWith(MockitoExtension.class) -class ReactiveMongoDatabaseUtilsUnitTests { - - @Mock ClientSession session; - @Mock ServerSession serverSession; - @Mock ReactiveMongoDatabaseFactory databaseFactory; - @Mock MongoDatabase db; - - @Test // DATAMONGO-2265 - void isTransactionActiveShouldDetectTxViaFactory() { - - when(databaseFactory.isTransactionActive()).thenReturn(true); - - ReactiveMongoDatabaseUtils.isTransactionActive(databaseFactory) // - .as(StepVerifier::create) // - .expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-2265 - void isTransactionActiveShouldReturnFalseIfNoTxActive() { - - when(databaseFactory.isTransactionActive()).thenReturn(false); - - ReactiveMongoDatabaseUtils.isTransactionActive(databaseFactory) // - .as(StepVerifier::create) // - .expectNext(false).verifyComplete(); - } - - @Test // DATAMONGO-2265 - void isTransactionActiveShouldLookupTxForActiveTransactionSynchronizationViaTxManager() { - - when(session.getServerSession()).thenReturn(serverSession); - when(session.hasActiveTransaction()).thenReturn(true); - when(databaseFactory.getSession(any())).thenReturn(Mono.just(session)); - when(databaseFactory.isTransactionActive()).thenReturn(false); - when(session.commitTransaction()).thenReturn(Mono.empty()); - - ReactiveMongoTransactionManager txManager = new ReactiveMongoTransactionManager(databaseFactory); - TransactionalOperator operator = TransactionalOperator.create(txManager, new DefaultTransactionDefinition()); - - operator.execute(tx -> { - - return ReactiveMongoDatabaseUtils.isTransactionActive(databaseFactory); - }).as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-2265 - void shouldNotStartSessionWhenNoTransactionOngoing() { - - when(databaseFactory.getMongoDatabase()).thenReturn(Mono.just(db)); - - ReactiveMongoDatabaseUtils.getDatabase(databaseFactory, SessionSynchronization.ON_ACTUAL_TRANSACTION) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - verify(databaseFactory, never()).getSession(any()); - verify(databaseFactory, never()).withSession(any(ClientSession.class)); - } - - @Test // DATAMONGO-2265 - void shouldParticipateInOngoingMongoTransactionWhenSessionSychronizationIsNative() { - - when(session.getServerSession()).thenReturn(serverSession); - when(databaseFactory.getSession(any())).thenReturn(Mono.just(session)); - - ReactiveMongoTransactionManager txManager = new ReactiveMongoTransactionManager(databaseFactory); - when(session.abortTransaction()).thenReturn(Mono.empty()); - - TransactionalOperator operator = TransactionalOperator.create(txManager, new DefaultTransactionDefinition()); - - operator.execute(tx -> { - - return TransactionSynchronizationManager.forCurrentTransaction().doOnNext(synchronizationManager -> { - - assertThat(synchronizationManager.isSynchronizationActive()).isTrue(); - assertThat(tx.isNewTransaction()).isTrue(); - - assertThat(synchronizationManager.hasResource(databaseFactory)).isTrue(); - - }).then(Mono.fromRunnable(tx::setRollbackOnly)); - }).as(StepVerifier::create).verifyComplete(); - - verify(session).startTransaction(); - verify(session).abortTransaction(); - verify(session).close(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoTransactionManagerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoTransactionManagerUnitTests.java deleted file mode 100644 index cf20421309..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoTransactionManagerUnitTests.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2019-2021 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; - -import static org.mockito.Mockito.*; - -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.reactive.TransactionalOperator; -import org.springframework.transaction.support.DefaultTransactionDefinition; - -import com.mongodb.reactivestreams.client.ClientSession; -import com.mongodb.reactivestreams.client.MongoDatabase; -import com.mongodb.session.ServerSession; - -/** - * Unit tests for {@link ReactiveMongoTransactionManager}. - * - * @author Mark Paluch - * @author Christoph Strobl - * @author Mathieu Ouellet - */ -@ExtendWith(MockitoExtension.class) -class ReactiveMongoTransactionManagerUnitTests { - - @Mock ClientSession session; - @Mock ClientSession session2; - @Mock ServerSession serverSession; - @Mock ReactiveMongoDatabaseFactory databaseFactory; - @Mock ReactiveMongoDatabaseFactory databaseFactory2; - @Mock MongoDatabase db; - @Mock MongoDatabase db2; - - @BeforeEach - void setUp() { - when(databaseFactory.getSession(any())).thenReturn(Mono.just(session), Mono.just(session2)); - when(databaseFactory.withSession(session)).thenReturn(databaseFactory); - when(databaseFactory.getMongoDatabase()).thenReturn(Mono.just(db)); - when(session.getServerSession()).thenReturn(serverSession); - } - - @Test // DATAMONGO-2265 - void triggerCommitCorrectly() { - - ReactiveMongoTransactionManager txManager = new ReactiveMongoTransactionManager(databaseFactory); - ReactiveMongoTemplate template = new ReactiveMongoTemplate(databaseFactory); - when(session.commitTransaction()).thenReturn(Mono.empty()); - - TransactionalOperator operator = TransactionalOperator.create(txManager, new DefaultTransactionDefinition()); - - template.execute(db -> { - db.drop(); - return Mono.empty(); - - }).as(operator::transactional) // - .as(StepVerifier::create) // - .verifyComplete(); - - verify(databaseFactory).withSession(eq(session)); - - verify(session).startTransaction(); - verify(session).commitTransaction(); - - verify(session).close(); - } - - @Test // DATAMONGO-2265 - void participateInOnGoingTransactionWithCommit() { - - ReactiveMongoTransactionManager txManager = new ReactiveMongoTransactionManager(databaseFactory); - ReactiveMongoTemplate template = new ReactiveMongoTemplate(databaseFactory); - when(session.commitTransaction()).thenReturn(Mono.empty()); - - TransactionalOperator operator = TransactionalOperator.create(txManager, new DefaultTransactionDefinition()); - - template.execute(db -> { - db.drop(); - return Mono.empty(); - }).as(StepVerifier::create).verifyComplete(); - - template.execute(db -> { - db.drop(); - return Mono.empty(); - }).as(operator::transactional) // - .as(StepVerifier::create) // - .verifyComplete(); - - verify(databaseFactory, times(1)).withSession(eq(session)); - - verify(session).startTransaction(); - verify(session).commitTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-2265 - void participateInOnGoingTransactionWithRollbackOnly() { - - ReactiveMongoTransactionManager txManager = new ReactiveMongoTransactionManager(databaseFactory); - ReactiveMongoTemplate template = new ReactiveMongoTemplate(databaseFactory); - when(session.abortTransaction()).thenReturn(Mono.empty()); - - TransactionalOperator operator = TransactionalOperator.create(txManager, new DefaultTransactionDefinition()); - - operator.execute(tx -> { - - return template.execute(db -> { - db.drop(); - tx.setRollbackOnly(); - return Mono.empty(); - }); - }).as(StepVerifier::create).verifyComplete(); - - verify(databaseFactory, times(1)).withSession(eq(session)); - - verify(session).startTransaction(); - verify(session).abortTransaction(); - verify(session).close(); - } - - @Test // DATAMONGO-2265 - void suspendTransactionWhilePropagationNotSupported() { - - ReactiveMongoTransactionManager txManager = new ReactiveMongoTransactionManager(databaseFactory); - ReactiveMongoTemplate template = new ReactiveMongoTemplate(databaseFactory); - when(session.commitTransaction()).thenReturn(Mono.empty()); - - TransactionalOperator outer = TransactionalOperator.create(txManager, new DefaultTransactionDefinition()); - - DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); - definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); - TransactionalOperator inner = TransactionalOperator.create(txManager, definition); - - outer.execute(tx1 -> { - - return template.execute(db -> { - - db.drop(); - - return inner.execute(tx2 -> { - return template.execute(db2 -> { - db2.drop(); - return Mono.empty(); - }); - }); - }); - }).as(StepVerifier::create).verifyComplete(); - - verify(session).startTransaction(); - verify(session2, never()).startTransaction(); - - verify(databaseFactory, times(1)).withSession(eq(session)); - verify(databaseFactory, never()).withSession(eq(session2)); - - verify(db, times(2)).drop(); - - verify(session2, never()).close(); - } - - @Test // DATAMONGO-2265 - void suspendTransactionWhilePropagationRequiresNew() { - - when(databaseFactory.withSession(session2)).thenReturn(databaseFactory2); - when(databaseFactory2.getMongoDatabase()).thenReturn(Mono.just(db2)); - when(session2.getServerSession()).thenReturn(serverSession); - - ReactiveMongoTransactionManager txManager = new ReactiveMongoTransactionManager(databaseFactory); - ReactiveMongoTemplate template = new ReactiveMongoTemplate(databaseFactory); - when(session.commitTransaction()).thenReturn(Mono.empty()); - when(session2.commitTransaction()).thenReturn(Mono.empty()); - - TransactionalOperator outer = TransactionalOperator.create(txManager, new DefaultTransactionDefinition()); - - DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); - definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - TransactionalOperator inner = TransactionalOperator.create(txManager, definition); - - outer.execute(tx1 -> { - - return template.execute(db -> { - - db.drop(); - - return inner.execute(tx2 -> { - return template.execute(db2 -> { - db2.drop(); - return Mono.empty(); - }); - }); - }); - }).as(StepVerifier::create).verifyComplete(); - - verify(session).startTransaction(); - verify(session2).startTransaction(); - - verify(databaseFactory, times(1)).withSession(eq(session)); - verify(databaseFactory).withSession(eq(session2)); - - verify(db).drop(); - verify(db2).drop(); - - verify(session).close(); - verify(session2).close(); - } - - @Test // DATAMONGO-2265 - void readonlyShouldInitiateASessionStartAndCommitTransaction() { - - ReactiveMongoTransactionManager txManager = new ReactiveMongoTransactionManager(databaseFactory); - ReactiveMongoTemplate template = new ReactiveMongoTemplate(databaseFactory); - when(session.commitTransaction()).thenReturn(Mono.empty()); - - DefaultTransactionDefinition readonlyTxDefinition = new DefaultTransactionDefinition(); - readonlyTxDefinition.setReadOnly(true); - TransactionalOperator operator = TransactionalOperator.create(txManager, readonlyTxDefinition); - - template.execute(db -> { - db.drop(); - return Mono.empty(); - - }).as(operator::transactional) // - .as(StepVerifier::create) // - .verifyComplete(); - - verify(databaseFactory).withSession(eq(session)); - - verify(session).startTransaction(); - verify(session).commitTransaction(); - verify(session).close(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveTransactionIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveTransactionIntegrationTests.java deleted file mode 100644 index 9fedeb5498..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveTransactionIntegrationTests.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright 2019-2021 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; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.RequiredArgsConstructor; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.time.Duration; -import java.util.Collections; -import java.util.Set; - -import org.bson.types.ObjectId; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.reactive.TransactionalOperator; -import org.springframework.transaction.support.DefaultTransactionDefinition; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Integration tests for reactive transaction management. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -@ExtendWith(MongoClientExtension.class) -@EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") -@EnableIfReplicaSetAvailable -@DisabledIfSystemProperty(named = "user.name", matches = "jenkins") -public class ReactiveTransactionIntegrationTests { - - private static final String DATABASE = "rxtx-test"; - - static @Client MongoClient mongoClient; - static GenericApplicationContext context; - - PersonService personService; - ReactiveMongoOperations operations; - - @BeforeAll - public static void init() { - context = new AnnotationConfigApplicationContext(TestMongoConfig.class, PersonService.class); - } - - @AfterAll - public static void after() { - context.close(); - } - - @BeforeEach - public void setUp() { - - personService = context.getBean(PersonService.class); - operations = context.getBean(ReactiveMongoOperations.class); - - try (MongoClient client = MongoTestUtils.reactiveClient()) { - - Flux.merge( // - MongoTestUtils.createOrReplaceCollection(DATABASE, operations.getCollectionName(Person.class), client), - MongoTestUtils.createOrReplaceCollection(DATABASE, operations.getCollectionName(EventLog.class), client) // - ).then().as(StepVerifier::create).verifyComplete(); - } - } - - @Test // DATAMONGO-2265 - public void shouldRollbackAfterException() { - - personService.savePersonErrors(new Person(null, "Walter", "White")) // - .as(StepVerifier::create) // - .verifyError(RuntimeException.class); - - operations.count(new Query(), Person.class) // - .as(StepVerifier::create) // - .expectNext(0L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2265 - public void shouldRollbackAfterExceptionOfTxAnnotatedMethod() { - - personService.declarativeSavePersonErrors(new Person(null, "Walter", "White")) // - .as(StepVerifier::create) // - .verifyError(RuntimeException.class); - - operations.count(new Query(), Person.class) // - .as(StepVerifier::create) // - .expectNext(0L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2265 - public void commitShouldPersistTxEntries() { - - personService.savePerson(new Person(null, "Walter", "White")) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - operations.count(new Query(), Person.class) // - .as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2265 - public void commitShouldPersistTxEntriesOfTxAnnotatedMethod() { - - personService.declarativeSavePerson(new Person(null, "Walter", "White")) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - operations.count(new Query(), Person.class) // - .as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2265 - public void commitShouldPersistTxEntriesAcrossCollections() { - - personService.saveWithLogs(new Person(null, "Walter", "White")) // - .then() // - .as(StepVerifier::create) // - .verifyComplete(); - - operations.count(new Query(), Person.class) // - .as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - - operations.count(new Query(), EventLog.class) // - .as(StepVerifier::create) // - .expectNext(4L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2265 - public void rollbackShouldAbortAcrossCollections() { - - personService.saveWithErrorLogs(new Person(null, "Walter", "White")) // - .then() // - .as(StepVerifier::create) // - .verifyError(); - - operations.count(new Query(), Person.class) // - .as(StepVerifier::create) // - .expectNext(0L) // - .verifyComplete(); - - operations.count(new Query(), EventLog.class) // - .as(StepVerifier::create) // - .expectNext(0L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2265 - public void countShouldWorkInsideTransaction() { - - personService.countDuringTx(new Person(null, "Walter", "White")) // - .as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2265 - public void emitMultipleElementsDuringTransaction() { - - personService.saveWithLogs(new Person(null, "Walter", "White")) // - .as(StepVerifier::create) // - .expectNextCount(4L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2265 - public void errorAfterTxShouldNotAffectPreviousStep() { - - personService.savePerson(new Person(null, "Walter", "White")) // - .delayElement(Duration.ofMillis(10)) // - .then(Mono.error(new RuntimeException("my big bad evil error"))).as(StepVerifier::create) // - .expectError() // - .verify(); - - operations.count(new Query(), Person.class) // - .as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - } - - @Configuration - static class TestMongoConfig extends AbstractReactiveMongoConfiguration { - - @Override - public MongoClient reactiveMongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return DATABASE; - } - - @Bean - public ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory factory) { - return new ReactiveMongoTransactionManager(factory); - } - - @Override - protected Set> getInitialEntitySet() { - return Collections.singleton(Person.class); - } - } - - @RequiredArgsConstructor - static class PersonService { - - final ReactiveMongoOperations operations; - final ReactiveMongoTransactionManager manager; - - public Mono savePersonErrors(Person person) { - - TransactionalOperator transactionalOperator = TransactionalOperator.create(manager, - new DefaultTransactionDefinition()); - - return operations.save(person) // - . flatMap(it -> Mono.error(new RuntimeException("poof!"))) // - .as(transactionalOperator::transactional); - } - - public Mono savePerson(Person person) { - - TransactionalOperator transactionalOperator = TransactionalOperator.create(manager, - new DefaultTransactionDefinition()); - - return operations.save(person) // - .flatMap(Mono::just) // - .as(transactionalOperator::transactional); - } - - public Mono countDuringTx(Person person) { - - TransactionalOperator transactionalOperator = TransactionalOperator.create(manager, - new DefaultTransactionDefinition()); - - return operations.save(person) // - .then(operations.count(new Query(), Person.class)) // - .as(transactionalOperator::transactional); - } - - public Flux saveWithLogs(Person person) { - - TransactionalOperator transactionalOperator = TransactionalOperator.create(manager, - new DefaultTransactionDefinition()); - - return Flux.merge(operations.save(new EventLog(new ObjectId(), "beforeConvert")), // - operations.save(new EventLog(new ObjectId(), "afterConvert")), // - operations.save(new EventLog(new ObjectId(), "beforeInsert")), // - operations.save(person), // - operations.save(new EventLog(new ObjectId(), "afterInsert"))) // - .thenMany(operations.query(EventLog.class).all()) // - .as(transactionalOperator::transactional); - } - - public Flux saveWithErrorLogs(Person person) { - - TransactionalOperator transactionalOperator = TransactionalOperator.create(manager, - new DefaultTransactionDefinition()); - - return Flux.merge(operations.save(new EventLog(new ObjectId(), "beforeConvert")), // - operations.save(new EventLog(new ObjectId(), "afterConvert")), // - operations.save(new EventLog(new ObjectId(), "beforeInsert")), // - operations.save(person), // - operations.save(new EventLog(new ObjectId(), "afterInsert"))) // - . flatMap(it -> Mono.error(new RuntimeException("poof!"))) // - .as(transactionalOperator::transactional); - } - - @Transactional - public Flux declarativeSavePerson(Person person) { - - TransactionalOperator transactionalOperator = TransactionalOperator.create(manager, - new DefaultTransactionDefinition()); - - return transactionalOperator.execute(reactiveTransaction -> { - return operations.save(person); - }); - } - - @Transactional - public Flux declarativeSavePersonErrors(Person person) { - - TransactionalOperator transactionalOperator = TransactionalOperator.create(manager, - new DefaultTransactionDefinition()); - - return transactionalOperator.execute(reactiveTransaction -> { - - return operations.save(person) // - . flatMap(it -> Mono.error(new RuntimeException("poof!"))); - }); - } - } - - @Data - @AllArgsConstructor - @Document("person-rx") - static class Person { - - ObjectId id; - String firstname, lastname; - } - - @Data - @AllArgsConstructor - static class EventLog { - - ObjectId id; - String action; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/SessionAwareMethodInterceptorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/SessionAwareMethodInterceptorUnitTests.java deleted file mode 100644 index c6e12e2f5d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/SessionAwareMethodInterceptorUnitTests.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2018-2021 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; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.any; - -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.data.mongodb.SessionAwareMethodInterceptor.MethodCache; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.ClassUtils; - -import com.mongodb.MongoClientSettings; -import com.mongodb.client.ClientSession; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; - -/** - * Unit tests for {@link SessionAwareMethodInterceptor}. - * - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -public class SessionAwareMethodInterceptorUnitTests { - - @Mock ClientSession session; - @Mock MongoCollection targetCollection; - @Mock MongoDatabase targetDatabase; - - MongoCollection collection; - MongoDatabase database; - - @BeforeEach - public void setUp() { - - collection = createProxyInstance(session, targetCollection, MongoCollection.class); - database = createProxyInstance(session, targetDatabase, MongoDatabase.class); - } - - @Test // DATAMONGO-1880 - public void proxyFactoryOnCollectionDelegatesToMethodWithSession() { - - collection.find(); - - verify(targetCollection).find(eq(session)); - } - - @Test // DATAMONGO-1880 - public void proxyFactoryOnCollectionWithSessionInArgumentListProceedsWithExecution() { - - ClientSession yetAnotherSession = mock(ClientSession.class); - collection.find(yetAnotherSession); - - verify(targetCollection).find(eq(yetAnotherSession)); - } - - @Test // DATAMONGO-1880 - public void proxyFactoryOnDatabaseDelegatesToMethodWithSession() { - - database.drop(); - - verify(targetDatabase).drop(eq(session)); - } - - @Test // DATAMONGO-1880 - public void proxyFactoryOnDatabaseWithSessionInArgumentListProceedsWithExecution() { - - ClientSession yetAnotherSession = mock(ClientSession.class); - database.drop(yetAnotherSession); - - verify(targetDatabase).drop(eq(yetAnotherSession)); - } - - @Test // DATAMONGO-1880 - public void justMoveOnIfNoOverloadWithSessionAvailable() { - - collection.getReadPreference(); - - verify(targetCollection).getReadPreference(); - } - - @Test // DATAMONGO-1880 - public void usesCacheForMethodLookup() { - - MethodCache cache = (MethodCache) ReflectionTestUtils.getField(SessionAwareMethodInterceptor.class, "METHOD_CACHE"); - Method countMethod = ClassUtils.getMethod(MongoCollection.class, "countDocuments"); - - assertThat(cache.contains(countMethod, MongoCollection.class)).isFalse(); - - collection.countDocuments(); - - assertThat(cache.contains(countMethod, MongoCollection.class)).isTrue(); - } - - @Test // DATAMONGO-1880 - public void cachesNullForMethodsThatDoNotHaveASessionOverload() { - - MethodCache cache = (MethodCache) ReflectionTestUtils.getField(SessionAwareMethodInterceptor.class, "METHOD_CACHE"); - Method readConcernMethod = ClassUtils.getMethod(MongoCollection.class, "getReadConcern"); - - assertThat(cache.contains(readConcernMethod, MongoCollection.class)).isFalse(); - - collection.getReadConcern(); - - collection.getReadConcern(); - - assertThat(cache.contains(readConcernMethod, MongoCollection.class)).isTrue(); - assertThat(cache.lookup(readConcernMethod, MongoCollection.class, ClientSession.class)).isEmpty(); - } - - @Test // DATAMONGO-1880 - public void proxiesNewDbInstanceReturnedByMethod() { - - MongoDatabase otherDb = mock(MongoDatabase.class); - when(targetDatabase.withCodecRegistry(any())).thenReturn(otherDb); - - MongoDatabase target = database.withCodecRegistry(MongoClientSettings.getDefaultCodecRegistry()); - assertThat(target).isInstanceOf(Proxy.class).isNotSameAs(database).isNotSameAs(targetDatabase); - - target.drop(); - - verify(otherDb).drop(eq(session)); - } - - @Test // DATAMONGO-1880 - public void proxiesNewCollectionInstanceReturnedByMethod() { - - MongoCollection otherCollection = mock(MongoCollection.class); - when(targetCollection.withCodecRegistry(any())).thenReturn(otherCollection); - - MongoCollection target = collection.withCodecRegistry(MongoClientSettings.getDefaultCodecRegistry()); - assertThat(target).isInstanceOf(Proxy.class).isNotSameAs(collection).isNotSameAs(targetCollection); - - target.drop(); - - verify(otherCollection).drop(eq(session)); - } - - private MongoDatabase proxyDatabase(com.mongodb.session.ClientSession session, MongoDatabase database) { - return createProxyInstance(session, database, MongoDatabase.class); - } - - private MongoCollection proxyCollection(com.mongodb.session.ClientSession session, MongoCollection collection) { - return createProxyInstance(session, collection, MongoCollection.class); - } - - private T createProxyInstance(com.mongodb.session.ClientSession session, T target, Class targetType) { - - ProxyFactory factory = new ProxyFactory(); - factory.setTarget(target); - factory.setInterfaces(targetType); - factory.setOpaque(true); - - factory.addAdvice(new SessionAwareMethodInterceptor<>(session, target, ClientSession.class, MongoDatabase.class, - this::proxyDatabase, MongoCollection.class, this::proxyCollection)); - - return targetType.cast(factory.getProxy()); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/SpringDataMongoDBTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/SpringDataMongoDBTests.java deleted file mode 100644 index 56cfff43b1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/SpringDataMongoDBTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020-2021 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; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -/** - * @author Christoph Strobl - */ -class SpringDataMongoDBTests { - - @Test // DATAMONGO-2427 - void driverInformationHoldsSpringDataHint() { - assertThat(SpringDataMongoDB.driverInformation().getDriverNames()).contains("spring-data"); - } - - @Test // DATAMONGO-2427 - void versionIsDetectedFromPackage() { - assertThat(SpringDataMongoDB.version()).isNotNull(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractIntegrationTests.java deleted file mode 100644 index 701e2eb986..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractIntegrationTests.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2013-2021 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.config; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Collections; -import java.util.Set; - -import org.bson.Document; -import org.junit.After; -import org.junit.Before; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.DataAccessException; -import org.springframework.data.mongodb.core.CollectionCallback; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.MongoException; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; - -/** - * @author Oliver Gierke - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public abstract class AbstractIntegrationTests { - - @Configuration - static class TestConfig extends AbstractMongoClientConfiguration { - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Override - public MongoClient mongoClient() { - return MongoTestUtils.client(); - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - - @Override - protected boolean autoIndexCreation() { - return true; - } - } - - @Autowired MongoOperations operations; - - @Before - @After - public void cleanUp() { - - for (String collectionName : operations.getCollectionNames()) { - if (!collectionName.startsWith("system")) { - operations.execute(collectionName, new CollectionCallback() { - @Override - public Void doInCollection(MongoCollection collection) throws MongoException, DataAccessException { - collection.deleteMany(new Document()); - assertThat(collection.find().iterator().hasNext()).isFalse(); - return null; - } - }); - } - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java deleted file mode 100644 index 1ec266bacf..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractMongoConfigurationUnitTests.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2012-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import example.first.First; -import example.second.Second; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Set; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.convert.MongoTypeMapper; -import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.spel.EvaluationContextProvider; -import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; - -/** - * Unit tests for {@link AbstractMongoClientConfiguration}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Mark Paluch - */ -public class AbstractMongoConfigurationUnitTests { - - @Test // DATAMONGO-496 - public void usesConfigClassPackageAsBaseMappingPackage() throws ClassNotFoundException { - - AbstractMongoClientConfiguration configuration = new SampleMongoConfiguration(); - assertThat(configuration.getMappingBasePackage()).isEqualTo(SampleMongoConfiguration.class.getPackage().getName()); - assertThat(configuration.getInitialEntitySet()).hasSize(2); - assertThat(configuration.getInitialEntitySet()).contains(Entity.class); - } - - @Test // DATAMONGO-496 - public void doesNotScanPackageIfMappingPackageIsNull() throws ClassNotFoundException { - assertScanningDisabled(null); - } - - @Test // DATAMONGO-496 - public void doesNotScanPackageIfMappingPackageIsEmpty() throws ClassNotFoundException { - - assertScanningDisabled(""); - assertScanningDisabled(" "); - } - - @Test // DATAMONGO-569 - public void containsMongoDbFactoryButNoMongoBean() { - - AbstractApplicationContext context = new AnnotationConfigApplicationContext(SampleMongoConfiguration.class); - - assertThat(context.getBean(MongoDatabaseFactory.class)).isNotNull(); - assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> context.getBean(MongoClient.class)); - - context.close(); - } - - @Test - public void returnsUninitializedMappingContext() throws Exception { - - SampleMongoConfiguration configuration = new SampleMongoConfiguration(); - MongoMappingContext context = configuration.mongoMappingContext(configuration.customConversions()); - - assertThat(context.getPersistentEntities()).isEmpty(); - context.initialize(); - assertThat(context.getPersistentEntities()).isNotEmpty(); - } - - @Test // DATAMONGO-717 - public void lifecycleCallbacksAreInvokedInAppropriateOrder() { - - AbstractApplicationContext context = new AnnotationConfigApplicationContext(SampleMongoConfiguration.class); - MongoMappingContext mappingContext = context.getBean(MongoMappingContext.class); - MongoPersistentEntity entity = mappingContext.getRequiredPersistentEntity(Entity.class); - EvaluationContextProvider provider = (EvaluationContextProvider) ReflectionTestUtils.getField(entity, - "evaluationContextProvider"); - - assertThat(provider).isInstanceOf(ExtensionAwareEvaluationContextProvider.class); - context.close(); - } - - @Test // DATAMONGO-725 - public void shouldBeAbleToConfigureCustomTypeMapperViaJavaConfig() { - - AbstractApplicationContext context = new AnnotationConfigApplicationContext(SampleMongoConfiguration.class); - MongoTypeMapper typeMapper = context.getBean(CustomMongoTypeMapper.class); - MappingMongoConverter mmc = context.getBean(MappingMongoConverter.class); - - assertThat(mmc).isNotNull(); - assertThat(mmc.getTypeMapper()).isEqualTo(typeMapper); - context.close(); - } - - @Test // DATAMONGO-1470 - @SuppressWarnings("unchecked") - public void allowsMultipleEntityBasePackages() throws ClassNotFoundException { - - ConfigurationWithMultipleBasePackages config = new ConfigurationWithMultipleBasePackages(); - Set> entities = config.getInitialEntitySet(); - - assertThat(entities).hasSize(2); - assertThat(entities).contains(First.class, Second.class); - } - - private static void assertScanningDisabled(final String value) throws ClassNotFoundException { - - AbstractMongoClientConfiguration configuration = new SampleMongoConfiguration() { - @Override - protected Collection getMappingBasePackages() { - return Collections.singleton(value); - } - }; - - assertThat(configuration.getMappingBasePackages()).contains(value); - assertThat(configuration.getInitialEntitySet()).hasSize(0); - } - - @Configuration - static class SampleMongoConfiguration extends AbstractMongoClientConfiguration { - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Override - public MongoClient mongoClient() { - return MongoClients.create(); - } - - @Override - public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory databaseFactory, - MongoCustomConversions customConversions, MongoMappingContext mappingContext) { - MappingMongoConverter converter = super.mappingMongoConverter(databaseFactory, customConversions, mappingContext); - converter.setTypeMapper(typeMapper()); - - return converter; - } - - @Bean - public MongoTypeMapper typeMapper() { - return new CustomMongoTypeMapper(); - } - - } - - static class ConfigurationWithMultipleBasePackages extends AbstractMongoClientConfiguration { - - @Override - protected String getDatabaseName() { - return "test"; - } - - @Override - public MongoClient mongoClient() { - return MongoClients.create(); - } - - @Override - protected Collection getMappingBasePackages() { - return Arrays.asList("example.first", "example.second"); - } - } - - @Document - static class Entity {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationIntegrationTests.java deleted file mode 100644 index 701d0280d8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationIntegrationTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2016-2021 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.config; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.*; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Integration tests for {@link AbstractReactiveMongoConfiguration}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class AbstractReactiveMongoConfigurationIntegrationTests { - - @Autowired ApplicationContext context; - - @Test // DATAMONGO-1444 - public void contextShouldContainTemplate() { - - assertThat(context.getBean(SimpleReactiveMongoDatabaseFactory.class)).isNotNull(); - assertThat(context.getBean(ReactiveMongoOperations.class)).isNotNull(); - assertThat(context.getBean(ReactiveMongoTemplate.class)).isNotNull(); - } - - @Configuration - static class ReactiveConfiguration extends AbstractReactiveMongoConfiguration { - - @Override - public MongoClient reactiveMongoClient() { - return Mockito.mock(MongoClient.class); - } - - @Override - protected String getDatabaseName() { - return "database"; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java deleted file mode 100644 index abbb7e0287..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AbstractReactiveMongoConfigurationUnitTests.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2016-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import example.first.First; -import example.second.Second; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Set; - -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.convert.MongoTypeMapper; -import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.data.spel.EvaluationContextProvider; -import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Unit tests for {@link AbstractReactiveMongoConfiguration}. - * - * @author Mark Paluch - */ -public class AbstractReactiveMongoConfigurationUnitTests { - - @Test // DATAMONGO-1444 - public void usesConfigClassPackageAsBaseMappingPackage() throws ClassNotFoundException { - - AbstractReactiveMongoConfiguration configuration = new SampleMongoConfiguration(); - assertThat(configuration.getMappingBasePackages()).contains(SampleMongoConfiguration.class.getPackage().getName()); - assertThat(configuration.getInitialEntitySet()).hasSize(2); - assertThat(configuration.getInitialEntitySet()).contains(Entity.class); - } - - @Test // DATAMONGO-1444 - public void doesNotScanPackageIfMappingPackageIsNull() throws ClassNotFoundException { - assertScanningDisabled(null); - } - - @Test // DATAMONGO-1444 - public void doesNotScanPackageIfMappingPackageIsEmpty() throws ClassNotFoundException { - - assertScanningDisabled(""); - assertScanningDisabled(" "); - } - - @Test // DATAMONGO-1444 - public void containsMongoDbFactoryButNoMongoBean() { - - AbstractApplicationContext context = new AnnotationConfigApplicationContext(SampleMongoConfiguration.class); - - assertThat(context.getBean(SimpleReactiveMongoDatabaseFactory.class)).isNotNull(); - assertThatExceptionOfType(NoSuchBeanDefinitionException.class) - .isThrownBy(() -> context.getBean(com.mongodb.client.MongoClient.class)); - - context.close(); - } - - @Test // DATAMONGO-1444 - public void returnsUninitializedMappingContext() throws Exception { - - SampleMongoConfiguration configuration = new SampleMongoConfiguration(); - MongoMappingContext context = configuration.mongoMappingContext(configuration.customConversions()); - - assertThat(context.getPersistentEntities()).isEmpty(); - context.initialize(); - assertThat(context.getPersistentEntities()).isNotEmpty(); - } - - @Test // DATAMONGO-1444 - public void lifecycleCallbacksAreInvokedInAppropriateOrder() { - - AbstractApplicationContext context = new AnnotationConfigApplicationContext(SampleMongoConfiguration.class); - MongoMappingContext mappingContext = context.getBean(MongoMappingContext.class); - MongoPersistentEntity entity = mappingContext.getRequiredPersistentEntity(Entity.class); - EvaluationContextProvider provider = (EvaluationContextProvider) ReflectionTestUtils.getField(entity, - "evaluationContextProvider"); - - assertThat(provider).isInstanceOf(ExtensionAwareEvaluationContextProvider.class); - context.close(); - } - - @Test // DATAMONGO-1444 - public void shouldBeAbleToConfigureCustomTypeMapperViaJavaConfig() { - - AbstractApplicationContext context = new AnnotationConfigApplicationContext(SampleMongoConfiguration.class); - MongoTypeMapper typeMapper = context.getBean(CustomMongoTypeMapper.class); - MappingMongoConverter mmc = context.getBean(MappingMongoConverter.class); - - assertThat(mmc).isNotNull(); - assertThat(mmc.getTypeMapper()).isEqualTo(typeMapper); - context.close(); - } - - @Test // DATAMONGO-1444 - @SuppressWarnings("unchecked") - public void allowsMultipleEntityBasePackages() throws ClassNotFoundException { - - ConfigurationWithMultipleBasePackages config = new ConfigurationWithMultipleBasePackages(); - Set> entities = config.getInitialEntitySet(); - - assertThat(entities).hasSize(2); - assertThat(entities).contains(First.class, Second.class); - } - - private static void assertScanningDisabled(final String value) throws ClassNotFoundException { - - AbstractReactiveMongoConfiguration configuration = new SampleMongoConfiguration() { - @Override - protected Collection getMappingBasePackages() { - return Collections.singleton(value); - } - }; - - assertThat(configuration.getMappingBasePackages()).contains(value); - assertThat(configuration.getInitialEntitySet()).hasSize(0); - } - - @Configuration - static class SampleMongoConfiguration extends AbstractReactiveMongoConfiguration { - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Override - public MongoClient reactiveMongoClient() { - return MongoTestUtils.reactiveClient(); - } - - @Override - public MappingMongoConverter mappingMongoConverter(ReactiveMongoDatabaseFactory databaseFactory, - MongoCustomConversions customConversions, MongoMappingContext mappingContext) { - - MappingMongoConverter converter = super.mappingMongoConverter(databaseFactory, customConversions, mappingContext); - converter.setTypeMapper(typeMapper()); - - return converter; - } - - @Bean - public MongoTypeMapper typeMapper() { - return new CustomMongoTypeMapper(); - } - } - - static class ConfigurationWithMultipleBasePackages extends AbstractReactiveMongoConfiguration { - - @Override - protected String getDatabaseName() { - return "test"; - } - - @Override - public MongoClient reactiveMongoClient() { - return Mockito.mock(MongoClient.class); - } - - @Override - protected Collection getMappingBasePackages() { - return Arrays.asList("example.first", "example.second"); - } - } - - @Document - static class Entity {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingIntegrationTests.java deleted file mode 100644 index 92b295a707..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingIntegrationTests.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2012-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.joda.time.DateTime; -import org.junit.jupiter.api.Test; - -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.mapping.callback.EntityCallbacks; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback; - -/** - * Integration test for the auditing support. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -public class AuditingIntegrationTests { - - @Test // DATAMONGO-577, DATAMONGO-800, DATAMONGO-883, DATAMONGO-2261 - public void enablesAuditingAndSetsPropertiesAccordingly() throws Exception { - - AbstractApplicationContext context = new ClassPathXmlApplicationContext("auditing.xml", getClass()); - - MongoMappingContext mappingContext = context.getBean(MongoMappingContext.class); - mappingContext.getPersistentEntity(Entity.class); - - EntityCallbacks callbacks = EntityCallbacks.create(context); - - Entity entity = new Entity(); - entity = callbacks.callback(BeforeConvertCallback.class, entity, "collection-1"); - - assertThat(entity.created).isNotNull(); - assertThat(entity.modified).isEqualTo(entity.created); - - Thread.sleep(10); - entity.id = 1L; - - entity = callbacks.callback(BeforeConvertCallback.class, entity, "collection-1"); - - assertThat(entity.created).isNotNull(); - assertThat(entity.modified).isNotEqualTo(entity.created); - context.close(); - } - - class Entity { - - @Id Long id; - @CreatedDate DateTime created; - DateTime modified; - - @LastModifiedDate - public DateTime getModified() { - return modified; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingViaJavaConfigRepositoriesTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingViaJavaConfigRepositoriesTests.java deleted file mode 100644 index 3f7d15018e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingViaJavaConfigRepositoriesTests.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2013-2021 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.config; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.core.ResolvableType; -import org.springframework.data.annotation.Version; -import org.springframework.data.domain.AuditorAware; -import org.springframework.data.mapping.callback.EntityCallback; -import org.springframework.data.mongodb.core.AuditablePerson; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback; -import org.springframework.data.mongodb.core.mapping.event.ReactiveAuditingEntityCallback; -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.stereotype.Repository; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.client.MongoClient; - -/** - * Integration tests for auditing via Java config. - * - * @author Thomas Darimont - * @author Oliver Gierke - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@ContextConfiguration -class AuditingViaJavaConfigRepositoriesTests { - - static @Client MongoClient mongoClient; - - @Autowired AuditablePersonRepository auditablePersonRepository; - @Autowired AuditorAware auditorAware; - @Autowired MongoMappingContext context; - @Autowired MongoOperations operations; - - AuditablePerson auditor; - - @Configuration - @EnableMongoAuditing(auditorAwareRef = "auditorProvider") - @EnableMongoRepositories(basePackageClasses = AuditablePersonRepository.class, considerNestedRepositories = true, - includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = AuditablePersonRepository.class)) - static class Config extends AbstractMongoClientConfiguration { - - @Override - protected String getDatabaseName() { - - return "database"; - } - - @Override - public MongoClient mongoClient() { - return mongoClient; - } - - @Bean - @SuppressWarnings("unchecked") - public AuditorAware auditorProvider() { - return mock(AuditorAware.class); - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return new HashSet<>( - Arrays.asList(AuditablePerson.class, VersionedAuditablePerson.class, SimpleVersionedAuditablePerson.class)); - } - } - - @BeforeEach - void setup() { - auditablePersonRepository.deleteAll(); - this.auditor = auditablePersonRepository.save(new AuditablePerson("auditor")); - } - - @Test // DATAMONGO-792, DATAMONGO-883 - void basicAuditing() { - - doReturn(Optional.of(this.auditor)).when(this.auditorAware).getCurrentAuditor(); - - AuditablePerson savedUser = auditablePersonRepository.save(new AuditablePerson("user")); - - AuditablePerson createdBy = savedUser.getCreatedBy(); - - assertThat(createdBy).isNotNull(); - assertThat(createdBy.getFirstname()).isEqualTo(this.auditor.getFirstname()); - assertThat(savedUser.getCreatedAt()).isNotNull(); - } - - @Test // DATAMONGO-843 - @SuppressWarnings("resource") - void auditingUsesFallbackMappingContextIfNoneConfiguredWithRepositories() { - new AnnotationConfigApplicationContext(SimpleConfigWithRepositories.class); - } - - @Test // DATAMONGO-843 - @SuppressWarnings("resource") - void auditingUsesFallbackMappingContextIfNoneConfigured() { - new AnnotationConfigApplicationContext(SimpleConfig.class); - } - - @Test // DATAMONGO-2139 - void auditingWorksForVersionedEntityWithWrapperVersion() { - - verifyAuditingViaVersionProperty(new VersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - auditablePersonRepository::save, // - null, 0L, 1L); - } - - @Test // DATAMONGO-2179 - void auditingWorksForVersionedEntityBatchWithWrapperVersion() { - - verifyAuditingViaVersionProperty(new VersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - s -> auditablePersonRepository.saveAll(Collections.singletonList(s)).get(0), // - null, 0L, 1L); - } - - @Test // DATAMONGO-2139 - void auditingWorksForVersionedEntityWithSimpleVersion() { - - verifyAuditingViaVersionProperty(new SimpleVersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - auditablePersonRepository::save, // - 0L, 1L, 2L); - } - - @Test // DATAMONGO-2139 - void auditingWorksForVersionedEntityWithWrapperVersionOnTemplate() { - - verifyAuditingViaVersionProperty(new VersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - operations::save, // - null, 0L, 1L); - } - - @Test // DATAMONGO-2139 - void auditingWorksForVersionedEntityWithSimpleVersionOnTemplate() { - - verifyAuditingViaVersionProperty(new SimpleVersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - operations::save, // - 0L, 1L, 2L); - } - - @Test // DATAMONGO-2586 - void auditingShouldOnlyRegisterImperativeAuditingCallback() { - - Object callbacks = ReflectionTestUtils.getField(operations, "entityCallbacks"); - Object callbackDiscoverer = ReflectionTestUtils.getField(callbacks, "callbackDiscoverer"); - List> actualCallbacks = ReflectionTestUtils.invokeMethod(callbackDiscoverer, "getEntityCallbacks", - AuditablePerson.class, ResolvableType.forClass(EntityCallback.class)); - - assertThat(actualCallbacks) // - .hasAtLeastOneElementOfType(AuditingEntityCallback.class) // - .doesNotHaveAnyElementsOfTypes(ReactiveAuditingEntityCallback.class); - } - - private void verifyAuditingViaVersionProperty(T instance, - Function versionExtractor, Function createdDateExtractor, Function persister, - Object... expectedValues) { - - MongoPersistentEntity entity = context.getRequiredPersistentEntity(instance.getClass()); - - assertThat(versionExtractor.apply(instance)).isEqualTo(expectedValues[0]); - assertThat(createdDateExtractor.apply(instance)).isNull(); - assertThat(entity.isNew(instance)).isTrue(); - - instance = persister.apply(instance); - - assertThat(versionExtractor.apply(instance)).isEqualTo(expectedValues[1]); - assertThat(createdDateExtractor.apply(instance)).isNotNull(); - assertThat(entity.isNew(instance)).isFalse(); - - instance = persister.apply(instance); - - assertThat(versionExtractor.apply(instance)).isEqualTo(expectedValues[2]); - assertThat(entity.isNew(instance)).isFalse(); - } - - @Repository - interface AuditablePersonRepository extends MongoRepository {} - - @Configuration - @EnableMongoRepositories - static class SimpleConfigWithRepositories extends SimpleConfig {} - - @Configuration - @EnableMongoAuditing - static class SimpleConfig extends AbstractMongoClientConfiguration { - - @Override - public MongoClient mongoClient() { - return MongoTestUtils.client(); - } - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - } - - static class VersionedAuditablePerson extends AuditablePerson { - @Version Long version; - } - - static class SimpleVersionedAuditablePerson extends AuditablePerson { - @Version long version; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/CustomMongoTypeMapper.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/CustomMongoTypeMapper.java deleted file mode 100644 index 9cf0141f00..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/CustomMongoTypeMapper.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2013-2021 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.config; - -import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper; - -/** - * @author Thomas Darimont - */ -class CustomMongoTypeMapper extends DefaultMongoTypeMapper {} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/GeoJsonConfigurationIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/GeoJsonConfigurationIntegrationTests.java deleted file mode 100644 index b257c3ecfa..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/GeoJsonConfigurationIntegrationTests.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2015-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.core.geo.GeoJsonModule; -import org.springframework.data.web.config.EnableSpringDataWebSupport; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration tests for {@link GeoJsonConfiguration}. - * - * @author Oliver Gierke - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class GeoJsonConfigurationIntegrationTests { - - @Configuration - @EnableSpringDataWebSupport - static class Config {} - - @Autowired GeoJsonModule geoJsonModule; - - @Test // DATAMONGO-1181 - public void picksUpGeoJsonModuleConfigurationByDefault() { - assertThat(geoJsonModule).isNotNull(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java deleted file mode 100644 index 9d92372522..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserIntegrationTests.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2011-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Collections; -import java.util.Set; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanReference; -import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.core.io.ClassPathResource; -import org.springframework.data.convert.CustomConversions; -import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoTypeMapper; -import org.springframework.data.mongodb.core.mapping.Account; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.stereotype.Component; - -/** - * Integration tests for {@link MappingMongoConverterParser}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Ryan Tenney - */ -public class MappingMongoConverterParserIntegrationTests { - - DefaultListableBeanFactory factory; - - @Test // DATAMONGO-243 - public void allowsDbFactoryRefAttribute() { - - loadValidConfiguration(); - factory.getBeanDefinition("converter"); - factory.getBean("converter"); - } - - @Test // DATAMONGO-725 - public void hasCustomTypeMapper() { - - loadValidConfiguration(); - MappingMongoConverter converter = factory.getBean("converter", MappingMongoConverter.class); - MongoTypeMapper customMongoTypeMapper = factory.getBean(CustomMongoTypeMapper.class); - - assertThat(converter.getTypeMapper()).isEqualTo(customMongoTypeMapper); - } - - @Test // DATAMONGO-301 - public void scansForConverterAndSetsUpCustomConversionsAccordingly() { - - loadValidConfiguration(); - CustomConversions conversions = factory.getBean(CustomConversions.class); - assertThat(conversions.hasCustomWriteTarget(Person.class)).isTrue(); - assertThat(conversions.hasCustomWriteTarget(Account.class)).isTrue(); - } - - @Test // DATAMONGO-607 - public void activatesAbbreviatingPropertiesCorrectly() { - - loadValidConfiguration(); - BeanDefinition definition = factory.getBeanDefinition("abbreviatingConverter.mongoMappingContext"); - Object value = definition.getPropertyValues().getPropertyValue("fieldNamingStrategy").getValue(); - - assertThat(value).isInstanceOf(BeanDefinition.class); - BeanDefinition strategy = (BeanDefinition) value; - assertThat(strategy.getBeanClassName()).isEqualTo(CamelCaseAbbreviatingFieldNamingStrategy.class.getName()); - } - - @Test // DATAMONGO-866 - public void rejectsInvalidFieldNamingStrategyConfiguration() { - - BeanDefinitionRegistry factory = new DefaultListableBeanFactory(); - XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); - - assertThatExceptionOfType(BeanDefinitionParsingException.class) - .isThrownBy(() -> reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-invalid.xml"))) - .withMessageContaining("abbreviation").withMessageContaining("field-naming-strategy-ref"); - } - - @Test // DATAMONGO-892 - public void shouldThrowBeanDefinitionParsingExceptionIfConverterDefinedAsNestedBean() { - - assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(this::loadNestedBeanConfiguration) - .withMessageContaining("Mongo Converter must not be defined as nested bean."); - - } - - @Test // DATAMONGO-925, DATAMONGO-928 - public void shouldSupportCustomFieldNamingStrategy() { - assertStrategyReferenceSetFor("mappingConverterWithCustomFieldNamingStrategy"); - } - - @Test // DATAMONGO-925, DATAMONGO-928 - public void shouldNotFailLoadingConfigIfAbbreviationIsDisabledAndStrategySet() { - assertStrategyReferenceSetFor("mappingConverterWithCustomFieldNamingStrategyAndAbbreviationDisabled"); - } - - private void loadValidConfiguration() { - this.loadConfiguration("namespace/converter.xml"); - } - - private void loadNestedBeanConfiguration() { - this.loadConfiguration("namespace/converter-nested-bean-definition.xml"); - } - - private void loadConfiguration(String configLocation) { - factory = new DefaultListableBeanFactory(); - XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); - reader.loadBeanDefinitions(new ClassPathResource(configLocation)); - } - - private static void assertStrategyReferenceSetFor(String beanId) { - - BeanDefinitionRegistry factory = new DefaultListableBeanFactory(); - XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); - reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-custom-fieldnamingstrategy.xml")); - - BeanDefinition definition = reader.getRegistry().getBeanDefinition(beanId.concat(".mongoMappingContext")); - BeanReference value = (BeanReference) definition.getPropertyValues().getPropertyValue("fieldNamingStrategy") - .getValue(); - - assertThat(value.getBeanName()).isEqualTo("customFieldNamingStrategy"); - } - - @Component - public static class SampleConverter implements Converter { - public Document convert(Person source) { - return null; - } - } - - @Component - public static class SampleConverterFactory implements GenericConverter { - - public Set getConvertibleTypes() { - return Collections.singleton(new ConvertiblePair(Account.class, Document.class)); - } - - public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - return null; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserValidationIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserValidationIntegrationTests.java deleted file mode 100644 index 84be7079f1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MappingMongoConverterParserValidationIntegrationTests.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2012-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.Before; -import org.junit.Test; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.support.BeanDefinitionReader; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.core.io.ClassPathResource; - -/** - * Integration test for creation of instance of - * {@link org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener} by defining - * {@code } in context XML. - * - * @author Maciej Walkowiak - * @author Thomas Darimont - * @author Oliver Gierke - */ -public class MappingMongoConverterParserValidationIntegrationTests { - - DefaultListableBeanFactory factory; - BeanDefinitionReader reader; - - @Before - public void setUp() { - factory = new DefaultListableBeanFactory(); - reader = new XmlBeanDefinitionReader(factory); - } - - @Test // DATAMONGO-36 - public void validatingEventListenerCreatedWithDefaultConfig() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-default.xml")); - assertThat(factory.getBean(BeanNames.VALIDATING_EVENT_LISTENER_BEAN_NAME)).isNotNull(); - } - - @Test // DATAMONGO-36 - public void validatingEventListenerCreatedWhenValidationEnabled() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-validation-enabled.xml")); - assertThat(factory.getBean(BeanNames.VALIDATING_EVENT_LISTENER_BEAN_NAME)).isNotNull(); - } - - @Test(expected = NoSuchBeanDefinitionException.class) // DATAMONGO-36 - public void validatingEventListenersIsNotCreatedWhenDisabled() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-validation-disabled.xml")); - factory.getBean(BeanNames.VALIDATING_EVENT_LISTENER_BEAN_NAME); - } - - @Test // DATAMONGO-36 - public void validatingEventListenerCreatedWithCustomTypeMapperConfig() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-custom-typeMapper.xml")); - assertThat(factory.getBean(BeanNames.VALIDATING_EVENT_LISTENER_BEAN_NAME)).isNotNull(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoAuditingRegistrarUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoAuditingRegistrarUnitTests.java deleted file mode 100644 index 4a37adcd9a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoAuditingRegistrarUnitTests.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.core.type.AnnotationMetadata; - -/** - * Unit tests for {@link MongoAuditingRegistrar}. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -class MongoAuditingRegistrarUnitTests { - - private MongoAuditingRegistrar registrar = new MongoAuditingRegistrar(); - - @Mock AnnotationMetadata metadata; - @Mock BeanDefinitionRegistry registry; - - @Test // DATAMONGO-792 - void rejectsNullAnnotationMetadata() { - assertThatIllegalArgumentException().isThrownBy(() -> registrar.registerBeanDefinitions(null, registry)); - } - - @Test // DATAMONGO-792 - void rejectsNullBeanDefinitionRegistry() { - assertThatIllegalArgumentException().isThrownBy(() -> registrar.registerBeanDefinitions(metadata, null)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientNamespaceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientNamespaceTests.java deleted file mode 100644 index 47dd85e07a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientNamespaceTests.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2019-2021 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.config; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.test.util.ReflectionTestUtils.*; - -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -import org.bson.UuidRepresentation; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.data.mongodb.core.MongoClientFactoryBean; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.mongodb.ConnectionString; -import com.mongodb.MongoClientSettings; -import com.mongodb.MongoCredential; -import com.mongodb.ServerAddress; -import com.mongodb.connection.ClusterType; - -/** - * Integration tests for the MongoDB namespace. - * - * @author Christoph Strobl - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration -public class MongoClientNamespaceTests { - - @Autowired ApplicationContext ctx; - - @Test // DATAMONGO-2384 - public void clientWithJustHostAndPort() { - - assertThat(ctx.containsBean("client-with-just-host-port")).isTrue(); - MongoClientFactoryBean factoryBean = ctx.getBean("&client-with-just-host-port", MongoClientFactoryBean.class); - - assertThat(getField(factoryBean, "host")).isEqualTo("127.0.0.1"); - assertThat(getField(factoryBean, "port")).isEqualTo(27017); - assertThat(getField(factoryBean, "connectionString")).isNull(); - assertThat(getField(factoryBean, "credential")).isNull(); - assertThat(getField(factoryBean, "replicaSet")).isNull(); - assertThat(getField(factoryBean, "mongoClientSettings")).isNull(); - } - - @Test // DATAMONGO-2384 - public void clientWithConnectionString() { - - assertThat(ctx.containsBean("client-with-connection-string")).isTrue(); - MongoClientFactoryBean factoryBean = ctx.getBean("&client-with-connection-string", MongoClientFactoryBean.class); - - assertThat(getField(factoryBean, "host")).isNull(); - assertThat(getField(factoryBean, "port")).isNull(); - assertThat(getField(factoryBean, "connectionString")) - .isEqualTo(new ConnectionString("mongodb://127.0.0.1:27017/?replicaSet=rs0")); - assertThat(getField(factoryBean, "credential")).isNull(); - assertThat(getField(factoryBean, "replicaSet")).isNull(); - assertThat(getField(factoryBean, "mongoClientSettings")).isNull(); - } - - @Test // DATAMONGO-2384 - public void clientWithReplicaSet() { - - assertThat(ctx.containsBean("client-with-replica-set")).isTrue(); - MongoClientFactoryBean factoryBean = ctx.getBean("&client-with-replica-set", MongoClientFactoryBean.class); - - assertThat(getField(factoryBean, "host")).isNull(); - assertThat(getField(factoryBean, "port")).isNull(); - assertThat(getField(factoryBean, "connectionString")).isNull(); - assertThat(getField(factoryBean, "credential")).isNull(); - assertThat(getField(factoryBean, "replicaSet")).isEqualTo("rs0"); - assertThat(getField(factoryBean, "mongoClientSettings")).isNull(); - } - - @Test // DATAMONGO-2384 - public void clientWithCredential() { - - assertThat(ctx.containsBean("client-with-auth")).isTrue(); - MongoClientFactoryBean factoryBean = ctx.getBean("&client-with-auth", MongoClientFactoryBean.class); - - assertThat(getField(factoryBean, "host")).isNull(); - assertThat(getField(factoryBean, "port")).isNull(); - assertThat(getField(factoryBean, "connectionString")).isNull(); - assertThat(getField(factoryBean, "credential")).isEqualTo( - Collections.singletonList(MongoCredential.createPlainCredential("jon", "snow", "warg".toCharArray()))); - assertThat(getField(factoryBean, "replicaSet")).isNull(); - assertThat(getField(factoryBean, "mongoClientSettings")).isNull(); - } - - @Test // DATAMONGO-2384 - public void clientWithClusterSettings() { - - assertThat(ctx.containsBean("client-with-cluster-settings")).isTrue(); - MongoClientFactoryBean factoryBean = ctx.getBean("&client-with-cluster-settings", MongoClientFactoryBean.class); - - MongoClientSettings settings = (MongoClientSettings) getField(factoryBean, "mongoClientSettings"); - - assertThat(settings.getClusterSettings().getRequiredClusterType()).isEqualTo(ClusterType.REPLICA_SET); - assertThat(settings.getClusterSettings().getServerSelectionTimeout(TimeUnit.MILLISECONDS)).isEqualTo(10); - assertThat(settings.getClusterSettings().getLocalThreshold(TimeUnit.MILLISECONDS)).isEqualTo(5); - assertThat(settings.getClusterSettings().getHosts()).contains(new ServerAddress("localhost", 27018), - new ServerAddress("localhost", 27019), new ServerAddress("localhost", 27020)); - } - - @Test // DATAMONGO-2384 - public void clientWithConnectionPoolSettings() { - - assertThat(ctx.containsBean("client-with-connection-pool-settings")).isTrue(); - MongoClientFactoryBean factoryBean = ctx.getBean("&client-with-connection-pool-settings", - MongoClientFactoryBean.class); - - MongoClientSettings settings = (MongoClientSettings) getField(factoryBean, "mongoClientSettings"); - - assertThat(settings.getConnectionPoolSettings().getMaxConnectionLifeTime(TimeUnit.MILLISECONDS)).isEqualTo(10); - assertThat(settings.getConnectionPoolSettings().getMinSize()).isEqualTo(10); - assertThat(settings.getConnectionPoolSettings().getMaxSize()).isEqualTo(20); - assertThat(settings.getConnectionPoolSettings().getMaintenanceFrequency(TimeUnit.MILLISECONDS)).isEqualTo(10); - assertThat(settings.getConnectionPoolSettings().getMaintenanceInitialDelay(TimeUnit.MILLISECONDS)).isEqualTo(11); - assertThat(settings.getConnectionPoolSettings().getMaxConnectionIdleTime(TimeUnit.MILLISECONDS)).isEqualTo(30); - assertThat(settings.getConnectionPoolSettings().getMaxWaitTime(TimeUnit.MILLISECONDS)).isEqualTo(15); - } - - @Test // DATAMONGO-2427 - public void clientWithUUidSettings() { - - assertThat(ctx.containsBean("client-with-uuid-settings")).isTrue(); - MongoClientFactoryBean factoryBean = ctx.getBean("&client-with-uuid-settings", MongoClientFactoryBean.class); - - MongoClientSettings settings = (MongoClientSettings) getField(factoryBean, "mongoClientSettings"); - assertThat(settings.getUuidRepresentation()).isEqualTo(UuidRepresentation.STANDARD); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java deleted file mode 100644 index 8a7f1f1d97..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoClientParserIntegrationTests.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2015-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.support.BeanDefinitionReader; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.io.ClassPathResource; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.MongoClientSettings; -import com.mongodb.MongoCredential; -import com.mongodb.ReadPreference; -import com.mongodb.ServerAddress; -import com.mongodb.WriteConcern; -import com.mongodb.client.MongoClient; - -/** - * Integration tests for {@link MongoClientParser}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -public class MongoClientParserIntegrationTests { - - DefaultListableBeanFactory factory; - BeanDefinitionReader reader; - - @BeforeEach - public void setUp() { - - this.factory = new DefaultListableBeanFactory(); - this.reader = new XmlBeanDefinitionReader(factory); - } - - @Test // DATAMONGO-1158 - public void createsMongoClientCorrectlyWhenGivenHostAndPort() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml")); - - assertThat(factory.getBean("mongo-client-with-host-and-port")).isInstanceOf(MongoClient.class); - } - - @Test // DATAMONGO-1158, DATAMONGO-2199 - public void createsMongoClientWithOptionsCorrectly() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml")); - - try (AbstractApplicationContext context = new GenericApplicationContext(factory)) { - context.refresh(); - - MongoClientSettings settings = extractClientSettingsFromBean(context, - "mongo-client-with-options-for-write-concern-and-read-preference"); - assertThat(settings.getReadPreference()).isEqualTo(ReadPreference.secondary()); - assertThat(settings.getWriteConcern()).isEqualTo(WriteConcern.UNACKNOWLEDGED); - } - } - - @Test // DATAMONGO-1158 - public void createsMongoClientWithDefaultsCorrectly() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml")); - - try (AbstractApplicationContext context = new GenericApplicationContext(factory)) { - - context.refresh(); - - MongoClient client = context.getBean("mongoClient", MongoClient.class); - assertThat(client.getClusterDescription().getClusterSettings().getHosts()).containsExactly(new ServerAddress()); - } - } - - @Test // DATAMONGO-1158 - public void createsMongoClientWithCredentialsCorrectly() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml")); - - try (AbstractApplicationContext context = new GenericApplicationContext(factory)) { - - context.refresh(); - - MongoClientSettings settings = extractClientSettingsFromBean(context, "mongo-client-with-credentials"); - - assertThat(settings.getCredential()) - .isEqualTo(MongoCredential.createPlainCredential("jon", "snow", "warg".toCharArray())); - } - } - - @Test // DATAMONGO-1620 - public void createsMongoClientWithServerSelectionTimeoutCorrectly() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongoClient-bean.xml")); - - try (AbstractApplicationContext context = new GenericApplicationContext(factory)) { - context.refresh(); - - MongoClientSettings settings = extractClientSettingsFromBean(context, - "mongo-client-with-server-selection-timeout"); - assertThat(settings.getClusterSettings().getServerSelectionTimeout(TimeUnit.MILLISECONDS)).isEqualTo(100); - } - } - - private MongoClientSettings extractClientSettingsFromBean(AbstractApplicationContext context, String beanName) { - return extractClientSettings(context.getBean(beanName, MongoClient.class)); - } - - private MongoClientSettings extractClientSettings(MongoClient client) { - return (MongoClientSettings) ReflectionTestUtils.getField(client, "settings"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java deleted file mode 100644 index d564bd830b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2015-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.Arrays; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.util.StringUtils; - -import com.mongodb.MongoCredential; - -/** - * Unit tests for {@link MongoCredentialPropertyEditor}. - * - * @author Christoph Strobl - * @author Stephen Tyler Conrad - */ -public class MongoCredentialPropertyEditorUnitTests { - - static final String USER_1_NAME = "tyrion"; - static final String USER_1_PWD = "dwarf"; - static final String USER_1_DB = "lannister"; - - static final String USER_2_NAME = "jon"; - static final String USER_2_PWD = "warg"; - static final String USER_2_DB = "snow"; - - static final String USER_3_NAME = "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry"; - static final String USER_3_DB = "stark"; - - static final String USER_4_PLAIN_NAME = "m0ng0@dmin"; - static final String USER_4_ENCODED_NAME; - static final String USER_4_PLAIN_PWD = "mo_res:bw6},Qsdxx@admin"; - static final String USER_4_ENCODED_PWD; - static final String USER_4_DB = "targaryen"; - - static final String USER_5_NAME = "lyanna"; - static final String USER_5_PWD = "random?password"; - static final String USER_5_DB = "mormont"; - - static final String USER_1_AUTH_STRING = USER_1_NAME + ":" + USER_1_PWD + "@" + USER_1_DB; - static final String USER_1_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM = USER_1_AUTH_STRING + "?uri.authMechanism=PLAIN"; - - static final String USER_2_AUTH_STRING = USER_2_NAME + ":" + USER_2_PWD + "@" + USER_2_DB; - static final String USER_2_AUTH_STRING_WITH_MONGODB_CR_AUTH_MECHANISM = USER_2_AUTH_STRING - + "?uri.authMechanism=MONGODB-CR"; - - static final String USER_3_AUTH_STRING_WITH_X509_AUTH_MECHANISM = "'" + USER_3_NAME + "@" + USER_3_DB - + "?uri.authMechanism=MONGODB-X509'"; - - static final String USER_4_AUTH_STRING; - - static final String USER_5_AUTH_STRING = USER_5_NAME + ":" + USER_5_PWD + "@" + USER_5_DB; - static final String USER_5_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM = USER_5_AUTH_STRING + "?uri.authMechanism=PLAIN"; - static final String USER_5_AUTH_STRING_WITH_QUERY_ARGS = USER_5_AUTH_STRING + "?uri.authMechanism=PLAIN&foo=&bar"; - - static final String SCRAM_SHA_256_AUTH_STRING = USER_1_NAME + ":" + USER_1_PWD + "@" + USER_1_DB - + "?uri.authMechanism=SCRAM-SHA-256"; - - static final MongoCredential USER_1_CREDENTIALS = MongoCredential.createCredential(USER_1_NAME, USER_1_DB, - USER_1_PWD.toCharArray()); - static final MongoCredential USER_1_CREDENTIALS_PLAIN_AUTH = MongoCredential.createPlainCredential(USER_1_NAME, - USER_1_DB, USER_1_PWD.toCharArray()); - - static final MongoCredential USER_2_CREDENTIALS = MongoCredential.createCredential(USER_2_NAME, USER_2_DB, - USER_2_PWD.toCharArray()); - - static final MongoCredential USER_3_CREDENTIALS_X509_AUTH = MongoCredential.createMongoX509Credential(USER_3_NAME); - - static final MongoCredential USER_4_CREDENTIALS = MongoCredential.createCredential(USER_4_PLAIN_NAME, USER_4_DB, - USER_4_PLAIN_PWD.toCharArray()); - - static final MongoCredential USER_5_CREDENTIALS = MongoCredential.createCredential(USER_5_NAME, USER_5_DB, - USER_5_PWD.toCharArray()); - static final MongoCredential USER_5_CREDENTIALS_PLAIN_AUTH = MongoCredential.createPlainCredential(USER_5_NAME, - USER_5_DB, USER_5_PWD.toCharArray()); - - static final MongoCredential SCRAM_SHA_256_CREDENTIALS = MongoCredential.createScramSha256Credential(USER_1_NAME, - USER_1_DB, USER_1_PWD.toCharArray()); - - MongoCredentialPropertyEditor editor; - - static { - - String encodedUserName = null; - String encodedUserPassword = null; - try { - encodedUserName = URLEncoder.encode(USER_4_PLAIN_NAME, "UTF-8"); - encodedUserPassword = URLEncoder.encode(USER_4_PLAIN_PWD, "UTF-8"); - } catch (UnsupportedEncodingException e) {} - - USER_4_ENCODED_NAME = encodedUserName; - USER_4_ENCODED_PWD = encodedUserPassword; - USER_4_AUTH_STRING = USER_4_ENCODED_NAME + ":" + USER_4_ENCODED_PWD + "@" + USER_4_DB; - - } - - @Before - public void setUp() { - this.editor = new MongoCredentialPropertyEditor(); - } - - @Test // DATAMONGO-1158 - public void shouldReturnNullValueForNullText() { - - editor.setAsText(null); - - assertThat(getValue()).isNull(); - } - - @Test // DATAMONGO-1158 - public void shouldReturnNullValueForEmptyText() { - - editor.setAsText(" "); - - assertThat(getValue()).isNull(); - } - - @Test // DATAMONGO-1158 - public void shouldThrowExceptionForMalformatedCredentialsString() { - assertThatIllegalArgumentException().isThrownBy(() -> editor.setAsText("tyrion")); - } - - @Test // DATAMONGO-1158 - public void shouldThrowExceptionForMalformatedAuthMechanism() { - assertThatIllegalArgumentException() - .isThrownBy(() -> editor.setAsText(USER_2_AUTH_STRING + "?uri.authMechanism=Targaryen")); - } - - @Test // DATAMONGO-1158 - @SuppressWarnings("unchecked") - public void shouldReturnCredentialsValueCorrectlyWhenGivenSingleUserNamePasswordStringWithDatabaseAndNoOptions() { - - editor.setAsText(USER_1_AUTH_STRING); - - assertThat(getValue()).contains(USER_1_CREDENTIALS); - } - - @Test // DATAMONGO-1158 - @SuppressWarnings("unchecked") - public void shouldReturnCredentialsValueCorrectlyWhenGivenSingleUserNamePasswordStringWithDatabaseAndAuthOptions() { - - editor.setAsText(USER_1_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM); - - assertThat(getValue()).contains(USER_1_CREDENTIALS_PLAIN_AUTH); - } - - @Test // DATAMONGO-1158 - @SuppressWarnings("unchecked") - public void shouldReturnCredentialsValueCorrectlyWhenGivenMultipleUserNamePasswordStringWithDatabaseAndNoOptions() { - - editor - .setAsText(StringUtils.collectionToCommaDelimitedString(Arrays.asList(USER_1_AUTH_STRING, USER_2_AUTH_STRING))); - - assertThat(getValue()).contains(USER_1_CREDENTIALS, USER_2_CREDENTIALS); - } - - @Test // DATAMONGO-1158 - @SuppressWarnings("unchecked") - public void shouldReturnCredentialsValueCorrectlyWhenGivenMultipleUserNamePasswordStringWithDatabaseAndMixedOptions() { - - editor.setAsText(StringUtils.collectionToCommaDelimitedString( - Arrays.asList(USER_1_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM, USER_2_AUTH_STRING))); - - assertThat(getValue()).contains(USER_1_CREDENTIALS_PLAIN_AUTH, USER_2_CREDENTIALS); - } - - @Test // DATAMONGO-1257 - @SuppressWarnings("unchecked") - public void shouldReturnCredentialsValueCorrectlyWhenGivenMultipleQuotedUserNamePasswordStringWithDatabaseAndNoOptions() { - - editor.setAsText(StringUtils.collectionToCommaDelimitedString( - Arrays.asList("'" + USER_1_AUTH_STRING + "'", "'" + USER_2_AUTH_STRING + "'"))); - - assertThat(getValue()).contains(USER_1_CREDENTIALS, USER_2_CREDENTIALS); - } - - @Test // DATAMONGO-1257 - @SuppressWarnings("unchecked") - public void shouldReturnCredentialsValueCorrectlyWhenGivenSingleQuotedUserNamePasswordStringWithDatabaseAndNoOptions() { - - editor.setAsText("'" + USER_1_AUTH_STRING + "'"); - - assertThat(getValue()).contains(USER_1_CREDENTIALS); - } - - @Test // DATAMONGO-1257 - @SuppressWarnings("unchecked") - public void shouldReturnX509CredentialsCorrectly() { - - editor.setAsText(USER_3_AUTH_STRING_WITH_X509_AUTH_MECHANISM); - - assertThat(getValue()).contains(USER_3_CREDENTIALS_X509_AUTH); - } - - @Test // DATAMONGO-1257 - @SuppressWarnings("unchecked") - public void shouldReturnX509CredentialsCorrectlyWhenNoDbSpecified() { - - editor.setAsText("tyrion?uri.authMechanism=MONGODB-X509"); - - assertThat(getValue()).contains(MongoCredential.createMongoX509Credential("tyrion")); - } - - @Test(expected = IllegalArgumentException.class) // DATAMONGO-1257 - public void shouldThrowExceptionWhenNoDbSpecifiedForMongodbCR() { - - editor.setAsText("tyrion?uri.authMechanism=MONGODB-CR"); - - getValue(); - } - - @Test(expected = IllegalArgumentException.class) // DATAMONGO-1257 - public void shouldThrowExceptionWhenDbIsEmptyForMongodbCR() { - - editor.setAsText("tyrion@?uri.authMechanism=MONGODB-CR"); - - getValue(); - } - - @Test // DATAMONGO-1317 - @SuppressWarnings("unchecked") - public void encodedUserNameAndPasswordShouldBeDecoded() { - - editor.setAsText(USER_4_AUTH_STRING); - - assertThat(getValue()).contains(USER_4_CREDENTIALS); - } - - @Test // DATAMONGO-2016 - @SuppressWarnings("unchecked") - public void passwordWithQuestionMarkShouldNotBeInterpretedAsOptionString() { - - editor.setAsText(USER_5_AUTH_STRING); - - assertThat(getValue()).contains(USER_5_CREDENTIALS); - } - - @Test // DATAMONGO-2016 - @SuppressWarnings("unchecked") - public void passwordWithQuestionMarkShouldNotBreakParsingOfOptionString() { - - editor.setAsText(USER_5_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM); - - assertThat(getValue()).contains(USER_5_CREDENTIALS_PLAIN_AUTH); - } - - @Test // DATAMONGO-2051 - public void shouldReturnScramSha256Credentials() { - - editor.setAsText(SCRAM_SHA_256_AUTH_STRING); - - assertThat(getValue()).contains(SCRAM_SHA_256_CREDENTIALS); - } - - @Test // DATAMONGO-2016 - @SuppressWarnings("unchecked") - public void failsGracefullyOnEmptyQueryArgument() { - assertThatIllegalArgumentException().isThrownBy(() -> editor.setAsText(USER_5_AUTH_STRING_WITH_QUERY_ARGS)); - } - - @SuppressWarnings("unchecked") - private List getValue() { - return (List) editor.getValue(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryNoDatabaseRunningTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryNoDatabaseRunningTests.java deleted file mode 100644 index 610473aee6..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryNoDatabaseRunningTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2011-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataAccessResourceFailureException; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration tests for {@link MongoDatabaseFactory}. - * - * @author Thomas Risberg - * @author Oliver Gierke - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class MongoDbFactoryNoDatabaseRunningTests { - - @Autowired MongoTemplate mongoTemplate; - - @Test // DATAMONGO-139 - public void startsUpWithoutADatabaseRunning() { - assertThat(mongoTemplate.getClass().getName()).isEqualTo("org.springframework.data.mongodb.core.MongoTemplate"); - } - - @Test - public void failsDataAccessWithoutADatabaseRunning() { - assertThatExceptionOfType(DataAccessResourceFailureException.class) - .isThrownBy(() -> mongoTemplate.getCollectionNames()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java deleted file mode 100644 index edd8cc9ba8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2011-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; -import org.springframework.beans.factory.support.BeanDefinitionReader; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.core.io.ClassPathResource; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.ConnectionString; -import com.mongodb.WriteConcern; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoDatabase; - -/** - * Integration tests for {@link MongoDbFactoryParser}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Viktor Khoroshko - */ -public class MongoDbFactoryParserIntegrationTests { - - DefaultListableBeanFactory factory; - BeanDefinitionReader reader; - - @BeforeEach - public void setUp() { - factory = new DefaultListableBeanFactory(); - reader = new XmlBeanDefinitionReader(factory); - } - - @Test // DATAMONGO-2199 - public void testWriteConcern() throws Exception { - - try (MongoClient client = MongoTestUtils.client()) { - SimpleMongoClientDatabaseFactory dbFactory = new SimpleMongoClientDatabaseFactory(client, "database"); - dbFactory.setWriteConcern(WriteConcern.ACKNOWLEDGED); - dbFactory.getMongoDatabase(); - - assertThat(ReflectionTestUtils.getField(dbFactory, "writeConcern")).isEqualTo(WriteConcern.ACKNOWLEDGED); - } - } - - @Test // DATAMONGO-2199 - public void parsesWriteConcern() { - - ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("namespace/db-factory-bean.xml"); - assertWriteConcern(ctx, WriteConcern.ACKNOWLEDGED); - } - - @Test // DATAMONGO-2199 - public void parsesCustomWriteConcern() { - - ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( - "namespace/db-factory-bean-custom-write-concern.xml"); - assertWriteConcern(ctx, new WriteConcern("rack1")); - } - - @Test // DATAMONGO-331 - public void readsReplicasWriteConcernCorrectly() { - - AbstractApplicationContext ctx = new ClassPathXmlApplicationContext( - "namespace/db-factory-bean-custom-write-concern.xml"); - MongoDatabaseFactory factory = ctx.getBean("second", MongoDatabaseFactory.class); - MongoDatabase db = factory.getMongoDatabase(); - - assertThat(db.getWriteConcern()).isEqualTo(WriteConcern.W2); - ctx.close(); - } - - // This test will fail since equals in WriteConcern uses == for _w and not .equals - public void testWriteConcernEquality() { - - String s1 = new String("rack1"); - String s2 = new String("rack1"); - WriteConcern wc1 = new WriteConcern(s1); - WriteConcern wc2 = new WriteConcern(s2); - assertThat(wc1).isEqualTo(wc2); - } - - @Test - public void createsDbFactoryBean() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/db-factory-bean.xml")); - factory.getBean("first"); - } - - @Test // DATAMONGO-306 - public void setsUpMongoDbFactoryUsingAMongoUriWithoutCredentials() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-uri-no-credentials.xml")); - BeanDefinition definition = factory.getBeanDefinition("mongoDbFactory"); - ConstructorArgumentValues constructorArguments = definition.getConstructorArgumentValues(); - - assertThat(constructorArguments.getArgumentCount()).isOne(); - ValueHolder argument = constructorArguments.getArgumentValue(0, ConnectionString.class); - assertThat(argument).isNotNull(); - - MongoDatabaseFactory dbFactory = factory.getBean("mongoDbFactory", MongoDatabaseFactory.class); - MongoDatabase db = dbFactory.getMongoDatabase(); - assertThat(db.getName()).isEqualTo("database"); - } - - @Test // DATAMONGO-1218 - public void setsUpMongoDbFactoryUsingAMongoClientUri() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-client-uri.xml")); - BeanDefinition definition = factory.getBeanDefinition("mongoDbFactory"); - ConstructorArgumentValues constructorArguments = definition.getConstructorArgumentValues(); - - assertThat(constructorArguments.getArgumentCount()).isOne(); - ValueHolder argument = constructorArguments.getArgumentValue(0, ConnectionString.class); - assertThat(argument).isNotNull(); - } - - @Test // DATAMONGO-1293 - public void setsUpClientUriWithId() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-client-uri-and-id.xml")); - BeanDefinition definition = factory.getBeanDefinition("testMongo"); - ConstructorArgumentValues constructorArguments = definition.getConstructorArgumentValues(); - - assertThat(constructorArguments.getArgumentCount()).isOne(); - ValueHolder argument = constructorArguments.getArgumentValue(0, ConnectionString.class); - assertThat(argument).isNotNull(); - } - - @Test // DATAMONGO-1293 - public void setsUpUriWithId() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-uri-and-id.xml")); - BeanDefinition definition = factory.getBeanDefinition("testMongo"); - ConstructorArgumentValues constructorArguments = definition.getConstructorArgumentValues(); - - assertThat(constructorArguments.getArgumentCount()).isOne(); - ValueHolder argument = constructorArguments.getArgumentValue(0, ConnectionString.class); - assertThat(argument).isNotNull(); - } - - @Test // DATAMONGO-2384 - public void usesConnectionStringToCreateClientClient() { - - ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("namespace/db-factory-bean.xml"); - - MongoDatabaseFactory dbFactory = ctx.getBean("with-connection-string", MongoDatabaseFactory.class); - assertThat(dbFactory).isInstanceOf(SimpleMongoClientDatabaseFactory.class); - assertThat(ReflectionTestUtils.getField(dbFactory, "mongoClient")) - .isInstanceOf(com.mongodb.client.MongoClient.class); - } - - @Test // DATAMONGO-2384 - public void usesMongoClientClientRef() { - - ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("namespace/db-factory-bean.xml"); - - MongoDatabaseFactory dbFactory = ctx.getBean("with-mongo-client-client-ref", MongoDatabaseFactory.class); - assertThat(dbFactory).isInstanceOf(SimpleMongoClientDatabaseFactory.class); - assertThat(ReflectionTestUtils.getField(dbFactory, "mongoClient")) - .isInstanceOf(com.mongodb.client.MongoClient.class); - } - - private static void assertWriteConcern(ClassPathXmlApplicationContext ctx, WriteConcern expectedWriteConcern) { - - SimpleMongoClientDatabaseFactory dbFactory = ctx.getBean("first", SimpleMongoClientDatabaseFactory.class); - MongoDatabase db = dbFactory.getMongoDatabase(); - assertThat(db.getName()).isEqualTo("db"); - - WriteConcern configuredConcern = (WriteConcern) ReflectionTestUtils.getField(dbFactory, "writeConcern"); - - assertThat(configuredConcern).isEqualTo(expectedWriteConcern); - assertThat(db.getWriteConcern()).isEqualTo(expectedWriteConcern); - assertThat(db.getWriteConcern()).isEqualTo(expectedWriteConcern); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests.java deleted file mode 100644 index f81db94d37..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2011-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; - -import org.bson.Document; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.data.mongodb.core.MongoClientFactoryBean; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.MongoClientSettings; -import com.mongodb.ServerAddress; -import com.mongodb.client.MongoClient; - -/** - * @author Mark Pollack - * @author Oliver Gierke - * @author Thomas Darimont - * @author Mark Paluch - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class MongoNamespaceReplicaSetTests { - - @Autowired private ApplicationContext ctx; - - @Test - @SuppressWarnings("unchecked") - public void testParsingMongoWithReplicaSets() throws Exception { - - assertThat(ctx.containsBean("replicaSetMongo")).isTrue(); - MongoClientFactoryBean mfb = (MongoClientFactoryBean) ctx.getBean("&replicaSetMongo"); - - MongoClientSettings settings = (MongoClientSettings) ReflectionTestUtils.getField(mfb, "mongoClientSettings"); - List replicaSetSeeds = settings.getClusterSettings().getHosts(); - - assertThat(replicaSetSeeds).isNotNull(); - assertThat(replicaSetSeeds).contains(new ServerAddress(InetAddress.getByName("127.0.0.1"), 10001), - new ServerAddress(InetAddress.getByName("localhost"), 10002)); - } - - @Test - @SuppressWarnings("unchecked") - public void testParsingWithPropertyPlaceHolder() throws Exception { - - assertThat(ctx.containsBean("manyReplicaSetMongo")).isTrue(); - MongoClientFactoryBean mfb = (MongoClientFactoryBean) ctx.getBean("&manyReplicaSetMongo"); - - MongoClientSettings settings = (MongoClientSettings) ReflectionTestUtils.getField(mfb, "mongoClientSettings"); - List replicaSetSeeds = settings.getClusterSettings().getHosts(); - - assertThat(replicaSetSeeds).isNotNull(); - assertThat(replicaSetSeeds).hasSize(3); - - List ports = new ArrayList(); - for (ServerAddress replicaSetSeed : replicaSetSeeds) { - ports.add(replicaSetSeed.getPort()); - } - - assertThat(ports).contains(27017, 27018, 27019); - } - - @Test - @Ignore("CI infrastructure does not yet support replica sets") - public void testMongoWithReplicaSets() { - - MongoClient mongo = ctx.getBean(MongoClient.class); - assertThat(mongo.getClusterDescription().getClusterSettings().getHosts()).isEqualTo(2); - List servers = mongo.getClusterDescription().getClusterSettings().getHosts(); - assertThat(servers.get(0).getHost()).isEqualTo("127.0.0.1"); - assertThat(servers.get(1).getHost()).isEqualTo("localhost"); - assertThat(servers.get(0).getPort()).isEqualTo(10001); - assertThat(servers.get(1).getPort()).isEqualTo(10002); - - MongoTemplate template = new MongoTemplate(mongo, "admin"); - Document result = template.executeCommand("{replSetGetStatus : 1}"); - assertThat(result.get("set").toString()).isEqualTo("blort"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java deleted file mode 100644 index 29cee1e488..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoNamespaceTests.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2010-2021 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.config; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.test.util.ReflectionTestUtils.*; - -import javax.net.ssl.SSLSocketFactory; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoClientFactoryBean; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.gridfs.GridFsOperations; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.MongoClientSettings; -import com.mongodb.ServerAddress; -import com.mongodb.WriteConcern; -import com.mongodb.client.MongoClient; - -/** - * Integration tests for the MongoDB namespace. - * - * @author Mark Pollack - * @author Oliver Gierke - * @author Martin Baumgartner - * @author Thomas Darimont - * @author Christoph Strobl - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class MongoNamespaceTests { - - @Autowired ApplicationContext ctx; - - @Test - public void testMongoSingleton() throws Exception { - - assertThat(ctx.containsBean("noAttrMongo")).isTrue(); - MongoClientFactoryBean mfb = (MongoClientFactoryBean) ctx.getBean("&noAttrMongo"); - - assertThat(getField(mfb, "host")).isNull(); - assertThat(getField(mfb, "port")).isNull(); - } - - @Test - public void testMongoSingletonWithAttributes() throws Exception { - - assertThat(ctx.containsBean("defaultMongo")).isTrue(); - MongoClientFactoryBean mfb = (MongoClientFactoryBean) ctx.getBean("&defaultMongo"); - - String host = (String) getField(mfb, "host"); - Integer port = (Integer) getField(mfb, "port"); - - assertThat(host).isEqualTo("localhost"); - assertThat(port).isEqualTo(new Integer(27017)); - - MongoClientSettings options = (MongoClientSettings) getField(mfb, "mongoClientSettings"); - assertThat(options).isNull(); - } - - @Test // DATAMONGO-764 - public void testMongoSingletonWithSslEnabled() throws Exception { - - assertThat(ctx.containsBean("mongoSsl")).isTrue(); - MongoClientFactoryBean mfb = (MongoClientFactoryBean) ctx.getBean("&mongoSsl"); - - MongoClientSettings options = (MongoClientSettings) getField(mfb, "mongoClientSettings"); - assertThat(options.getSslSettings().getContext().getSocketFactory() instanceof SSLSocketFactory) - .as("socketFactory should be a SSLSocketFactory").isTrue(); - } - - @Test // DATAMONGO-1490 - public void testMongoClientSingletonWithSslEnabled() { - - assertThat(ctx.containsBean("mongoClientSsl")).isTrue(); - MongoClientFactoryBean mfb = (MongoClientFactoryBean) ctx.getBean("&mongoClientSsl"); - - MongoClientSettings options = (MongoClientSettings) getField(mfb, "mongoClientSettings"); - assertThat(options.getSslSettings().getContext().getSocketFactory() instanceof SSLSocketFactory) - .as("socketFactory should be a SSLSocketFactory").isTrue(); - } - - @Test // DATAMONGO-764 - public void testMongoSingletonWithSslEnabledAndCustomSslSocketFactory() throws Exception { - - assertThat(ctx.containsBean("mongoSslWithCustomSslFactory")).isTrue(); - MongoClientFactoryBean mfb = (MongoClientFactoryBean) ctx.getBean("&mongoSslWithCustomSslFactory"); - - MongoClientSettings options = (MongoClientSettings) getField(mfb, "mongoClientSettings"); - - assertThat(options.getSslSettings().getContext().getSocketFactory() instanceof SSLSocketFactory) - .as("socketFactory should be a SSLSocketFactory").isTrue(); - assertThat(options.getSslSettings().getContext().getProvider().getName()).isEqualTo("SunJSSE"); - } - - @Test - public void testSecondMongoDbFactory() { - - assertThat(ctx.containsBean("secondMongoDbFactory")).isTrue(); - MongoDatabaseFactory dbf = (MongoDatabaseFactory) ctx.getBean("secondMongoDbFactory"); - - MongoClient mongo = (MongoClient) getField(dbf, "mongoClient"); - assertThat(mongo.getClusterDescription().getClusterSettings().getHosts()).containsExactly(new ServerAddress()); - assertThat(getField(dbf, "databaseName")).isEqualTo("database"); - } - - @Test // DATAMONGO-789 - public void testThirdMongoDbFactory() { - - assertThat(ctx.containsBean("thirdMongoDbFactory")).isTrue(); - - MongoDatabaseFactory dbf = (MongoDatabaseFactory) ctx.getBean("thirdMongoDbFactory"); - MongoClient mongo = (MongoClient) getField(dbf, "mongoClient"); - - assertThat(mongo.getClusterDescription().getClusterSettings().getHosts()).containsExactly(new ServerAddress()); - assertThat(getField(dbf, "databaseName")).isEqualTo("database"); - } - - @Test // DATAMONGO-140 - public void testMongoTemplateFactory() { - - assertThat(ctx.containsBean("mongoTemplate")).isTrue(); - MongoOperations operations = (MongoOperations) ctx.getBean("mongoTemplate"); - - MongoDatabaseFactory dbf = (MongoDatabaseFactory) getField(operations, "mongoDbFactory"); - assertThat(getField(dbf, "databaseName")).isEqualTo("database"); - - MongoConverter converter = (MongoConverter) getField(operations, "mongoConverter"); - assertThat(converter).isNotNull(); - } - - @Test // DATAMONGO-140 - public void testSecondMongoTemplateFactory() { - - assertThat(ctx.containsBean("anotherMongoTemplate")).isTrue(); - MongoOperations operations = (MongoOperations) ctx.getBean("anotherMongoTemplate"); - - MongoDatabaseFactory dbf = (MongoDatabaseFactory) getField(operations, "mongoDbFactory"); - assertThat(getField(dbf, "databaseName")).isEqualTo("database"); - - WriteConcern writeConcern = (WriteConcern) getField(operations, "writeConcern"); - assertThat(writeConcern).isEqualTo(WriteConcern.ACKNOWLEDGED); - } - - @Test // DATAMONGO-628 - public void testGridFsTemplateFactory() { - - assertThat(ctx.containsBean("gridFsTemplate")).isTrue(); - GridFsOperations operations = (GridFsOperations) ctx.getBean("gridFsTemplate"); - - MongoDatabaseFactory dbf = (MongoDatabaseFactory) getField(operations, "dbFactory"); - assertThat(getField(dbf, "databaseName")).isEqualTo("database"); - - MongoConverter converter = (MongoConverter) getField(operations, "converter"); - assertThat(converter).isNotNull(); - } - - @Test // DATAMONGO-628 - public void testSecondGridFsTemplateFactory() { - - assertThat(ctx.containsBean("secondGridFsTemplate")).isTrue(); - GridFsOperations operations = (GridFsOperations) ctx.getBean("secondGridFsTemplate"); - - MongoDatabaseFactory dbf = (MongoDatabaseFactory) getField(operations, "dbFactory"); - assertThat(getField(dbf, "databaseName")).isEqualTo("database"); - assertThat(getField(operations, "bucket")).isEqualTo(null); - - MongoConverter converter = (MongoConverter) getField(operations, "converter"); - assertThat(converter).isNotNull(); - } - - @Test // DATAMONGO-823 - public void testThirdGridFsTemplateFactory() { - - assertThat(ctx.containsBean("thirdGridFsTemplate")).isTrue(); - GridFsOperations operations = (GridFsOperations) ctx.getBean("thirdGridFsTemplate"); - - MongoDatabaseFactory dbf = (MongoDatabaseFactory) getField(operations, "dbFactory"); - assertThat(getField(dbf, "databaseName")).isEqualTo("database"); - assertThat(getField(operations, "bucket")).isEqualTo("bucketString"); - - MongoConverter converter = (MongoConverter) getField(operations, "converter"); - assertThat(converter).isNotNull(); - } - - @Test - public void testMongoSingletonWithPropertyPlaceHolders() { - - assertThat(ctx.containsBean("mongoClient")).isTrue(); - MongoClientFactoryBean mfb = (MongoClientFactoryBean) ctx.getBean("&mongoClient"); - - String host = (String) getField(mfb, "host"); - Integer port = (Integer) getField(mfb, "port"); - - assertThat(host).isEqualTo("127.0.0.1"); - assertThat(port).isEqualTo(new Integer(27017)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoParserIntegrationTests.java deleted file mode 100644 index 4081b7e85c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoParserIntegrationTests.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2011-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.PropertyValue; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionReader; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.io.ClassPathResource; - -import com.mongodb.client.MongoClient; - -/** - * Integration tests for {@link MongoClientParser}. - * - * @author Oliver Gierke - */ -public class MongoParserIntegrationTests { - - DefaultListableBeanFactory factory; - BeanDefinitionReader reader; - - @BeforeEach - public void setUp() { - - this.factory = new DefaultListableBeanFactory(); - this.reader = new XmlBeanDefinitionReader(factory); - } - - @Test - @Disabled - public void readsMongoAttributesCorrectly() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-bean.xml")); - BeanDefinition definition = factory.getBeanDefinition("mongoClient"); - - List values = definition.getPropertyValues().getPropertyValueList(); - - assertThat(values.get(2).getValue()).isInstanceOf(BeanDefinition.class); - BeanDefinition x = (BeanDefinition) values.get(2).getValue(); - - assertThat(x.getPropertyValues().getPropertyValueList()).contains(new PropertyValue("writeConcern", "SAFE")); - - factory.getBean("mongoClient"); - } - - @Test // DATAMONGO-343 - public void readsServerAddressesCorrectly() { - - reader.loadBeanDefinitions(new ClassPathResource("namespace/mongo-bean.xml")); - - AbstractApplicationContext context = new GenericApplicationContext(factory); - context.refresh(); - - assertThat(context.getBean("mongo2", MongoClient.class)).isNotNull(); - context.close(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReactiveAuditingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReactiveAuditingTests.java deleted file mode 100644 index 972d5df3f2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReactiveAuditingTests.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2018-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.springframework.core.ResolvableType; -import org.springframework.data.mapping.callback.EntityCallback; -import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback; -import org.springframework.data.mongodb.core.mapping.event.ReactiveAuditingEntityCallback; -import org.springframework.test.util.ReflectionTestUtils; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.data.annotation.Version; -import org.springframework.data.domain.ReactiveAuditorAware; -import org.springframework.data.mongodb.core.AuditablePerson; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.repository.ReactiveMongoRepository; -import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Integration test for the auditing support via {@link org.springframework.data.mongodb.core.ReactiveMongoTemplate}. - * - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@ContextConfiguration -class ReactiveAuditingTests { - - static @Client MongoClient mongoClient; - - @Autowired ReactiveAuditablePersonRepository auditablePersonRepository; - @Autowired MongoMappingContext context; - @Autowired ReactiveMongoOperations operations; - - @Configuration - @EnableReactiveMongoAuditing - @EnableReactiveMongoRepositories(basePackageClasses = ReactiveAuditingTests.class, considerNestedRepositories = true, - includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ReactiveAuditablePersonRepository.class)) - static class Config extends AbstractReactiveMongoConfiguration { - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Override - public MongoClient reactiveMongoClient() { - return mongoClient; - } - - @Override - protected Set> getInitialEntitySet() { - return new HashSet<>( - Arrays.asList(AuditablePerson.class, VersionedAuditablePerson.class, SimpleVersionedAuditablePerson.class)); - } - - @Bean - public ReactiveAuditorAware auditorProvider() { - - AuditablePerson person = new AuditablePerson("some-person"); - person.setId("foo"); - - return () -> Mono.just(person); - } - } - - @Test // DATAMONGO-2139, DATAMONGO-2150, DATAMONGO-2586 - void auditingWorksForVersionedEntityWithWrapperVersion() { - - verifyAuditingViaVersionProperty(new VersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - auditablePersonRepository::save, // - null, 0L, 1L); - } - - @Test // DATAMONGO-2179 - void auditingWorksForVersionedEntityBatchWithWrapperVersion() { - - verifyAuditingViaVersionProperty(new VersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - s -> auditablePersonRepository.saveAll(Collections.singletonList(s)).next(), // - null, 0L, 1L); - } - - @Test // DATAMONGO-2139, DATAMONGO-2150, DATAMONGO-2586 - void auditingWorksForVersionedEntityWithSimpleVersion() { - - verifyAuditingViaVersionProperty(new SimpleVersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - auditablePersonRepository::save, // - 0L, 1L, 2L); - } - - @Test // DATAMONGO-2139, DATAMONGO-2150, DATAMONGO-2586 - void auditingWorksForVersionedEntityWithWrapperVersionOnTemplate() { - - verifyAuditingViaVersionProperty(new VersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - operations::save, // - null, 0L, 1L); - } - - @Test // DATAMONGO-2139, DATAMONGO-2150, DATAMONGO-2586 - void auditingWorksForVersionedEntityWithSimpleVersionOnTemplate() { - verifyAuditingViaVersionProperty(new SimpleVersionedAuditablePerson(), // - it -> it.version, // - AuditablePerson::getCreatedAt, // - operations::save, // - 0L, 1L, 2L); - } - - @Test // DATAMONGO-2586 - void auditingShouldOnlyRegisterReactiveAuditingCallback() { - - Object callbacks = ReflectionTestUtils.getField(operations, "entityCallbacks"); - Object callbackDiscoverer = ReflectionTestUtils.getField(callbacks, "callbackDiscoverer"); - List> actualCallbacks = ReflectionTestUtils.invokeMethod(callbackDiscoverer, "getEntityCallbacks", - AuditablePerson.class, ResolvableType.forClass(EntityCallback.class)); - - assertThat(actualCallbacks) // - .hasAtLeastOneElementOfType(ReactiveAuditingEntityCallback.class) // - .doesNotHaveAnyElementsOfTypes(AuditingEntityCallback.class); - } - - private void verifyAuditingViaVersionProperty(T instance, - Function versionExtractor, Function createdDateExtractor, Function> persister, - Object... expectedValues) { - - AtomicReference instanceHolder = new AtomicReference<>(instance); - MongoPersistentEntity entity = context.getRequiredPersistentEntity(instance.getClass()); - - assertThat(versionExtractor.apply(instance)).isEqualTo(expectedValues[0]); - assertThat(createdDateExtractor.apply(instance)).isNull(); - assertThat(entity.isNew(instance)).isTrue(); - - persister.apply(instanceHolder.get()) // - .as(StepVerifier::create).consumeNextWith(actual -> { - - instanceHolder.set(actual); - - assertThat(versionExtractor.apply(actual)).isEqualTo(expectedValues[1]); - assertThat(createdDateExtractor.apply(instance)).isNotNull(); - assertThat(entity.isNew(actual)).isFalse(); - }).verifyComplete(); - - persister.apply(instanceHolder.get()) // - .as(StepVerifier::create).consumeNextWith(actual -> { - - instanceHolder.set(actual); - - assertThat(versionExtractor.apply(actual)).isEqualTo(expectedValues[2]); - assertThat(entity.isNew(actual)).isFalse(); - }).verifyComplete(); - } - - interface ReactiveAuditablePersonRepository extends ReactiveMongoRepository {} - - static class VersionedAuditablePerson extends AuditablePerson { - @Version Long version; - } - - static class SimpleVersionedAuditablePerson extends AuditablePerson { - @Version long version; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditorUnitTests.java deleted file mode 100644 index c8c496fcbb..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditorUnitTests.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2015-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.mongodb.ReadPreference; - -/** - * Unit tests for {@link ReadPreferencePropertyEditor}. - * - * @author Christoph Strobl - */ -public class ReadPreferencePropertyEditorUnitTests { - - ReadPreferencePropertyEditor editor; - - @BeforeEach - public void setUp() { - editor = new ReadPreferencePropertyEditor(); - } - - @Test // DATAMONGO-1158 - public void shouldThrowExceptionOnUndefinedPreferenceString() { - - assertThatIllegalArgumentException().isThrownBy(() -> editor.setAsText("foo")).withMessageContaining("foo") - .withMessageContaining("ReadPreference"); - } - - @Test // DATAMONGO-1158 - public void shouldAllowUsageNativePreferenceStrings() { - - editor.setAsText("secondary"); - - assertThat(editor.getValue()).isEqualTo((Object) ReadPreference.secondary()); - } - - @Test // DATAMONGO-1158 - public void shouldAllowUsageOfUppcaseEnumStringsForPreferences() { - - editor.setAsText("NEAREST"); - - assertThat(editor.getValue()).isEqualTo((Object) ReadPreference.nearest()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditorUnitTests.java deleted file mode 100644 index 0206748044..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditorUnitTests.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2012-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.Collection; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIfSystemProperty; - -import com.mongodb.ServerAddress; - -/** - * Unit tests for {@link ServerAddressPropertyEditor}. - * - * @author Oliver Gierke - * @author Thomas Darimont - */ -public class ServerAddressPropertyEditorUnitTests { - - ServerAddressPropertyEditor editor; - - @BeforeEach - public void setUp() { - editor = new ServerAddressPropertyEditor(); - } - - @Test // DATAMONGO-454, DATAMONGO-1062 - public void rejectsAddressConfigWithoutASingleParsableAndResolvableServerAddress() { - - String unknownHost1 = "gugu.nonexistant.example.org"; - String unknownHost2 = "gaga.nonexistant.example.org"; - - assertUnresolveableHostnames(unknownHost1, unknownHost2); - - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> editor.setAsText(unknownHost1 + "," + unknownHost2)); - } - - @Test // DATAMONGO-454 - @EnabledIfSystemProperty(named = "user.name", matches = "jenkins") - public void skipsUnparsableAddressIfAtLeastOneIsParsable() throws UnknownHostException { - - editor.setAsText("foo, localhost"); - assertSingleAddressOfLocalhost(editor.getValue()); - } - - @Test // DATAMONGO-454 - public void handlesEmptyAddressAsParseError() throws UnknownHostException { - - editor.setAsText(", localhost"); - assertSingleAddressOfLocalhost(editor.getValue()); - } - - @Test // DATAMONGO-693 - public void interpretEmptyStringAsNull() { - - editor.setAsText(""); - assertThat(editor.getValue()).isNull(); - } - - @Test // DATAMONGO-808 - public void handleIPv6HostaddressLoopbackShort() throws UnknownHostException { - - String hostAddress = "::1"; - editor.setAsText(hostAddress); - - assertSingleAddressWithPort(hostAddress, null, editor.getValue()); - } - - @Test // DATAMONGO-808 - public void handleIPv6HostaddressLoopbackShortWithPort() throws UnknownHostException { - - String hostAddress = "::1"; - int port = 27017; - editor.setAsText(hostAddress + ":" + port); - - assertSingleAddressWithPort(hostAddress, port, editor.getValue()); - } - - /** - * Here we detect no port since the last segment of the address contains leading zeros. - */ - @Test // DATAMONGO-808 - public void handleIPv6HostaddressLoopbackLong() throws UnknownHostException { - - String hostAddress = "0000:0000:0000:0000:0000:0000:0000:0001"; - editor.setAsText(hostAddress); - - assertSingleAddressWithPort(hostAddress, null, editor.getValue()); - } - - @Test // DATAMONGO-808 - public void handleIPv6HostaddressLoopbackLongWithBrackets() throws UnknownHostException { - - String hostAddress = "[0000:0000:0000:0000:0000:0000:0000:0001]"; - editor.setAsText(hostAddress); - - assertSingleAddressWithPort(hostAddress, null, editor.getValue()); - } - - /** - * We can't tell whether the last part of the hostAddress represents a port or not. - */ - @Test // DATAMONGO-808 - public void shouldFailToHandleAmbiguousIPv6HostaddressLongWithoutPortAndWithoutBrackets() { - - String hostAddress = "0000:0000:0000:0000:0000:0000:0000:128"; - - assertThatIllegalArgumentException().isThrownBy(() -> editor.setAsText(hostAddress)); - } - - @Test // DATAMONGO-808 - public void handleIPv6HostaddressExampleAddressWithPort() throws UnknownHostException { - - String hostAddress = "0000:0000:0000:0000:0000:0000:0000:0001"; - int port = 27017; - editor.setAsText(hostAddress + ":" + port); - - assertSingleAddressWithPort(hostAddress, port, editor.getValue()); - } - - @Test // DATAMONGO-808 - public void handleIPv6HostaddressExampleAddressInBracketsWithPort() throws UnknownHostException { - - String hostAddress = "[0000:0000:0000:0000:0000:0000:0000:0001]"; - int port = 27017; - editor.setAsText(hostAddress + ":" + port); - - assertSingleAddressWithPort(hostAddress, port, editor.getValue()); - } - - private static void assertSingleAddressOfLocalhost(Object result) throws UnknownHostException { - assertSingleAddressWithPort("localhost", null, result); - } - - private static void assertSingleAddressWithPort(String hostAddress, Integer port, Object result) - throws UnknownHostException { - - assertThat(result).isInstanceOf(ServerAddress[].class); - Collection addresses = Arrays.asList((ServerAddress[]) result); - assertThat(addresses).hasSize(1); - if (port == null) { - assertThat(addresses).contains(new ServerAddress(InetAddress.getByName(hostAddress))); - } else { - assertThat(addresses).contains(new ServerAddress(InetAddress.getByName(hostAddress), port)); - } - } - - private void assertUnresolveableHostnames(String... hostnames) { - - for (String hostname : hostnames) { - try { - InetAddress.getByName(hostname).isReachable(1500); - fail("Supposedly unresolveable hostname '" + hostname + "' can be resolved."); - } catch (IOException expected) { - // ok - } - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/StringToWriteConcernConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/StringToWriteConcernConverterUnitTests.java deleted file mode 100644 index a63bd1aa3c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/StringToWriteConcernConverterUnitTests.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -import com.mongodb.WriteConcern; - -/** - * Unit tests for {@link StringToWriteConcernConverter}. - * - * @author Oliver Gierke - * @author Christoph Strobl - */ -public class StringToWriteConcernConverterUnitTests { - - StringToWriteConcernConverter converter = new StringToWriteConcernConverter(); - - @Test // DATAMONGO-2199 - public void createsWellKnownConstantsCorrectly() { - assertThat(converter.convert("ACKNOWLEDGED")).isEqualTo(WriteConcern.ACKNOWLEDGED); - } - - @Test - public void createsWriteConcernForUnknownValue() { - assertThat(converter.convert("-1")).isEqualTo(new WriteConcern("-1")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditorUnitTests.java deleted file mode 100644 index ae2dcd59ab..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditorUnitTests.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012-2021 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.config; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.mongodb.WriteConcern; - -/** - * Unit tests for {@link WriteConcernPropertyEditor}. - * - * @author Oliver Gierke - * @author Christoph Strobl - */ -public class WriteConcernPropertyEditorUnitTests { - - WriteConcernPropertyEditor editor; - - @BeforeEach - public void setUp() { - editor = new WriteConcernPropertyEditor(); - } - - @Test // DATAMONGO-2199 - public void createsWriteConcernForWellKnownConstants() { - - editor.setAsText("JOURNALED"); - assertThat(editor.getValue()).isEqualTo(WriteConcern.JOURNALED); - } - - @Test - public void createsWriteConcernForUnknownConstants() { - - editor.setAsText("-1"); - assertThat(editor.getValue()).isEqualTo(new WriteConcern("-1")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/AuditablePerson.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/AuditablePerson.java deleted file mode 100644 index 07982c295d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/AuditablePerson.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2013-2021 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.Date; - -import org.springframework.data.annotation.CreatedBy; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.DBRef; - -/** - * Domain class for auditing functionality testing. - * - * @author Thomas Darimont - */ -public class AuditablePerson { - - private @Id String id; - private String firstname; - private @DBRef @CreatedBy AuditablePerson createdBy; - private @CreatedDate Date createdAt; - - public AuditablePerson() {} - - public AuditablePerson(String firstName) { - this.firstname = firstName; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstName) { - this.firstname = firstName; - } - - public AuditablePerson getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(AuditablePerson createdBy) { - this.createdBy = createdBy; - } - - public Date getCreatedAt() { - return createdAt; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/BaseDoc.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/BaseDoc.java deleted file mode 100644 index 8bd671b740..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/BaseDoc.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.springframework.data.mongodb.core; - -import org.springframework.data.annotation.Id; - -public class BaseDoc { - @Id String id; - String value; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ChangeStreamOptionsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ChangeStreamOptionsUnitTests.java deleted file mode 100644 index 7c09583a38..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ChangeStreamOptionsUnitTests.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; - -import org.bson.BsonDocument; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link ChangeStreamOptions}. - * - * @author Mark Paluch - */ -public class ChangeStreamOptionsUnitTests { - - @Test // DATAMONGO-2258 - public void shouldReportResumeAfter() { - - ChangeStreamOptions options = ChangeStreamOptions.builder().resumeAfter(new BsonDocument()).build(); - - assertThat(options.isResumeAfter()).isTrue(); - assertThat(options.isStartAfter()).isFalse(); - } - - @Test // DATAMONGO-2258 - public void shouldReportStartAfter() { - - ChangeStreamOptions options = ChangeStreamOptions.builder().startAfter(new BsonDocument()).build(); - - assertThat(options.isResumeAfter()).isFalse(); - assertThat(options.isStartAfter()).isTrue(); - } - - @Test // DATAMONGO-2258 - public void shouldNotReportResumeStartAfter() { - - ChangeStreamOptions options = ChangeStreamOptions.empty(); - - assertThat(options.isResumeAfter()).isFalse(); - assertThat(options.isStartAfter()).isFalse(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ClientSessionTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ClientSessionTests.java deleted file mode 100644 index 01bd934ef3..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ClientSessionTests.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.AllArgsConstructor; -import lombok.Data; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.data.mongodb.test.util.ReplSetClient; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.ClientSessionOptions; -import com.mongodb.client.ClientSession; -import com.mongodb.client.MongoClient; - -/** - * Integration tests for {@link ClientSession} through {@link MongoTemplate#withSession(ClientSession)}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class }) -@EnableIfReplicaSetAvailable -@EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") -class ClientSessionTests { - - private static final String DB_NAME = "client-session-tests"; - private static final String COLLECTION_NAME = "test"; - private static final String REF_COLLECTION_NAME = "test-with-ref"; - - private static @ReplSetClient MongoClient mongoClient; - - private MongoTemplate template; - - @BeforeEach - void setUp() { - - MongoTestUtils.createOrReplaceCollection(DB_NAME, COLLECTION_NAME, mongoClient); - - template = new MongoTemplate(mongoClient, DB_NAME); - template.getDb().getCollection(COLLECTION_NAME).insertOne(new Document("_id", "id-1").append("value", "spring")); - } - - @Test // DATAMONGO-1880 - void shouldApplyClientSession() { - - ClientSession session = mongoClient.startSession(ClientSessionOptions.builder().causallyConsistent(true).build()); - - assertThat(session.getOperationTime()).isNull(); - - Document doc = template.withSession(() -> session) - .execute(action -> action.findOne(new Query(), Document.class, "test")); - - assertThat(doc).isNotNull(); - assertThat(session.getOperationTime()).isNotNull(); - assertThat(session.getServerSession().isClosed()).isFalse(); - - session.close(); - } - - @Test // DATAMONGO-2241 - void shouldReuseConfiguredInfrastructure() { - - ClientSession session = mongoClient.startSession(ClientSessionOptions.builder().causallyConsistent(true).build()); - - MappingMongoConverter source = MappingMongoConverter.class.cast(template.getConverter()); - MappingMongoConverter sessionTemplateConverter = MappingMongoConverter.class - .cast(template.withSession(() -> session).execute(MongoOperations::getConverter)); - - assertThat(sessionTemplateConverter.getMappingContext()).isSameAs(source.getMappingContext()); - assertThat(ReflectionTestUtils.getField(sessionTemplateConverter, "conversions")) - .isSameAs(ReflectionTestUtils.getField(source, "conversions")); - assertThat(ReflectionTestUtils.getField(sessionTemplateConverter, "instantiators")) - .isSameAs(ReflectionTestUtils.getField(source, "instantiators")); - } - - @Test // DATAMONGO-1920 - void withCommittedTransaction() { - - ClientSession session = mongoClient.startSession(ClientSessionOptions.builder().causallyConsistent(true).build()); - - assertThat(session.getOperationTime()).isNull(); - - session.startTransaction(); - - SomeDoc saved = template.withSession(() -> session).execute(action -> { - - SomeDoc doc = new SomeDoc("id-2", "value2"); - action.insert(doc); - return doc; - }); - - session.commitTransaction(); - session.close(); - - assertThat(saved).isNotNull(); - assertThat(session.getOperationTime()).isNotNull(); - - assertThat(template.exists(query(where("id").is(saved.getId())), SomeDoc.class)).isTrue(); - } - - @Test // DATAMONGO-1920 - void withAbortedTransaction() { - - ClientSession session = mongoClient.startSession(ClientSessionOptions.builder().causallyConsistent(true).build()); - - assertThat(session.getOperationTime()).isNull(); - - session.startTransaction(); - - SomeDoc saved = template.withSession(() -> session).execute(action -> { - - SomeDoc doc = new SomeDoc("id-2", "value2"); - action.insert(doc); - return doc; - }); - - session.abortTransaction(); - session.close(); - - assertThat(saved).isNotNull(); - assertThat(session.getOperationTime()).isNotNull(); - - assertThat(template.exists(query(where("id").is(saved.getId())), SomeDoc.class)).isFalse(); - } - - @Test // DATAMONGO-2490 - void shouldBeAbleToReadDbRefDuringTransaction() { - - SomeDoc ref = new SomeDoc("ref-1", "da value"); - WithDbRef source = new WithDbRef("source-1", "da source", ref); - - ClientSession session = mongoClient.startSession(ClientSessionOptions.builder().causallyConsistent(true).build()); - - assertThat(session.getOperationTime()).isNull(); - - session.startTransaction(); - - WithDbRef saved = template.withSession(() -> session).execute(action -> { - - template.save(ref); - template.save(source); - - return template.findOne(query(where("id").is(source.id)), WithDbRef.class); - }); - - assertThat(saved.getSomeDocRef()).isEqualTo(ref); - - session.abortTransaction(); - } - - @Data - @AllArgsConstructor - @org.springframework.data.mongodb.core.mapping.Document(COLLECTION_NAME) - static class SomeDoc { - - @Id String id; - String value; - } - - @Data - @AllArgsConstructor - @org.springframework.data.mongodb.core.mapping.Document(REF_COLLECTION_NAME) - static class WithDbRef { - - @Id String id; - String value; - @DBRef SomeDoc someDocRef; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/CloseableIterableCursorAdapterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/CloseableIterableCursorAdapterUnitTests.java deleted file mode 100644 index 4d5f762f55..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/CloseableIterableCursorAdapterUnitTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2015-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.dao.support.PersistenceExceptionTranslator; -import org.springframework.data.mongodb.core.MongoTemplate.CloseableIterableCursorAdapter; -import org.springframework.data.mongodb.core.MongoTemplate.DocumentCallback; -import org.springframework.data.util.CloseableIterator; - -import com.mongodb.client.MongoCursor; - -/** - * Unit tests for {@link CloseableIterableCursorAdapter}. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -class CloseableIterableCursorAdapterUnitTests { - - @Mock PersistenceExceptionTranslator exceptionTranslator; - @Mock DocumentCallback callback; - - private MongoCursor cursor; - private CloseableIterator adapter; - - @BeforeEach - void setUp() { - this.cursor = mock(MongoCursor.class); - this.adapter = new CloseableIterableCursorAdapter<>(cursor, exceptionTranslator, callback); - } - - @Test // DATAMONGO-1276 - void propagatesOriginalExceptionFromAdapterDotNext() { - - doThrow(IllegalArgumentException.class).when(cursor).next(); - assertThatIllegalArgumentException().isThrownBy(() -> adapter.next()); - } - - @Test // DATAMONGO-1276 - void propagatesOriginalExceptionFromAdapterDotHasNext() { - - doThrow(IllegalArgumentException.class).when(cursor).hasNext(); - assertThatIllegalArgumentException().isThrownBy(() -> adapter.hasNext()); - } - - @Test // DATAMONGO-1276 - void propagatesOriginalExceptionFromAdapterDotClose() { - - doThrow(IllegalArgumentException.class).when(cursor).close(); - assertThatIllegalArgumentException().isThrownBy(() -> adapter.close()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/CollationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/CollationUnitTests.java deleted file mode 100644 index 499c17cf37..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/CollationUnitTests.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; - -import java.util.Locale; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Collation.Alternate; -import org.springframework.data.mongodb.core.query.Collation.CaseFirst; -import org.springframework.data.mongodb.core.query.Collation.CollationLocale; -import org.springframework.data.mongodb.core.query.Collation.ComparisonLevel; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -public class CollationUnitTests { - - static final Document BINARY_COMPARISON = new Document().append("locale", "simple"); - static final Document JUST_LOCALE = new Document().append("locale", "en_US"); - static final Document LOCALE_WITH_VARIANT = new Document().append("locale", "de_AT@collation=phonebook"); - static final Document WITH_STRENGTH_PRIMARY = new Document(JUST_LOCALE).append("strength", 1); - static final Document WITH_STRENGTH_PRIMARY_INCLUDE_CASE = new Document(WITH_STRENGTH_PRIMARY).append("caseLevel", - true); - static final Document WITH_NORMALIZATION = new Document(JUST_LOCALE).append("normalization", true); - static final Document WITH_BACKWARDS = new Document(JUST_LOCALE).append("backwards", true); - static final Document WITH_NUMERIC_ORDERING = new Document(JUST_LOCALE).append("numericOrdering", true); - static final Document WITH_CASE_FIRST_UPPER = new Document(JUST_LOCALE).append("strength", 3).append("caseFirst", - "upper"); - static final Document WITH_ALTERNATE_SHIFTED = new Document(JUST_LOCALE).append("alternate", "shifted"); - static final Document WITH_ALTERNATE_SHIFTED_MAX_VARIABLE_PUNCT = new Document(WITH_ALTERNATE_SHIFTED) - .append("maxVariable", "punct"); - static final Document ALL_THE_THINGS = new Document(LOCALE_WITH_VARIANT).append("strength", 1) - .append("caseLevel", true).append("backwards", true).append("numericOrdering", true) - .append("alternate", "shifted").append("maxVariable", "punct").append("normalization", true); - - @Test // DATAMONGO-1518 - public void justLocale() { - assertThat(Collation.of("en_US").toDocument()).isEqualTo(JUST_LOCALE); - } - - @Test // DATAMONGO-1518 - public void justLocaleFromDocument() { - assertThat(Collation.from(JUST_LOCALE).toDocument()).isEqualTo(JUST_LOCALE); - } - - @Test // DATAMONGO-1518 - public void localeWithVariant() { - assertThat(Collation.of(CollationLocale.of("de_AT").variant("phonebook")).toDocument()) - .isEqualTo(LOCALE_WITH_VARIANT); - } - - @Test // DATAMONGO-1518 - public void localeWithVariantFromDocument() { - assertThat(Collation.from(LOCALE_WITH_VARIANT).toDocument()).isEqualTo(LOCALE_WITH_VARIANT); - } - - @Test // DATAMONGO-1518 - public void localeFromJavaUtilLocale() { - - assertThat(Collation.of(java.util.Locale.US).toDocument()).isEqualTo(new Document().append("locale", "en_US")); - assertThat(Collation.of(Locale.ENGLISH).toDocument()).isEqualTo(new Document().append("locale", "en")); - } - - @Test // DATAMONGO-1518 - public void withStrenghPrimary() { - assertThat(Collation.of("en_US").strength(ComparisonLevel.primary()).toDocument()).isEqualTo(WITH_STRENGTH_PRIMARY); - } - - @Test // DATAMONGO-1518 - public void withStrenghPrimaryFromDocument() { - assertThat(Collation.from(WITH_STRENGTH_PRIMARY).toDocument()).isEqualTo(WITH_STRENGTH_PRIMARY); - } - - @Test // DATAMONGO-1518 - public void withStrenghPrimaryAndIncludeCase() { - - assertThat(Collation.of("en_US").strength(ComparisonLevel.primary().includeCase()).toDocument()) - .isEqualTo(WITH_STRENGTH_PRIMARY_INCLUDE_CASE); - } - - @Test // DATAMONGO-1518 - public void withStrenghPrimaryAndIncludeCaseFromDocument() { - - assertThat(Collation.from(WITH_STRENGTH_PRIMARY_INCLUDE_CASE).toDocument()) - .isEqualTo(WITH_STRENGTH_PRIMARY_INCLUDE_CASE); - } - - @Test // DATAMONGO-1518 - public void withNormalization() { - assertThat(Collation.of("en_US").normalization(true).toDocument()).isEqualTo(WITH_NORMALIZATION); - } - - @Test // DATAMONGO-1518 - public void withNormalizationFromDocument() { - assertThat(Collation.from(WITH_NORMALIZATION).toDocument()).isEqualTo(WITH_NORMALIZATION); - } - - @Test // DATAMONGO-1518 - public void withBackwards() { - assertThat(Collation.of("en_US").backwards(true).toDocument()).isEqualTo(WITH_BACKWARDS); - } - - @Test // DATAMONGO-1518 - public void withBackwardsFromDocument() { - assertThat(Collation.from(WITH_BACKWARDS).toDocument()).isEqualTo(WITH_BACKWARDS); - } - - @Test // DATAMONGO-1518 - public void withNumericOrdering() { - assertThat(Collation.of("en_US").numericOrdering(true).toDocument()).isEqualTo(WITH_NUMERIC_ORDERING); - } - - @Test // DATAMONGO-1518 - public void withNumericOrderingFromDocument() { - assertThat(Collation.from(WITH_NUMERIC_ORDERING).toDocument()).isEqualTo(WITH_NUMERIC_ORDERING); - } - - @Test // DATAMONGO-1518 - public void withCaseFirst() { - assertThat(Collation.of("en_US").caseFirst(CaseFirst.upper()).toDocument()).isEqualTo(WITH_CASE_FIRST_UPPER); - } - - @Test // DATAMONGO-1518 - public void withCaseFirstFromDocument() { - assertThat(Collation.from(WITH_CASE_FIRST_UPPER).toDocument()).isEqualTo(WITH_CASE_FIRST_UPPER); - } - - @Test // DATAMONGO-1518 - public void withAlternate() { - assertThat(Collation.of("en_US").alternate(Alternate.shifted()).toDocument()).isEqualTo(WITH_ALTERNATE_SHIFTED); - } - - @Test // DATAMONGO-1518 - public void withAlternateFromDocument() { - assertThat(Collation.from(WITH_ALTERNATE_SHIFTED).toDocument()).isEqualTo(WITH_ALTERNATE_SHIFTED); - } - - @Test // DATAMONGO-1518 - public void withAlternateAndMaxVariable() { - - assertThat(Collation.of("en_US").alternate(Alternate.shifted().punct()).toDocument()) - .isEqualTo(WITH_ALTERNATE_SHIFTED_MAX_VARIABLE_PUNCT); - } - - @Test // DATAMONGO-1518 - public void withAlternateAndMaxVariableFromDocument() { - - assertThat(Collation.from(WITH_ALTERNATE_SHIFTED_MAX_VARIABLE_PUNCT).toDocument()) - .isEqualTo(WITH_ALTERNATE_SHIFTED_MAX_VARIABLE_PUNCT); - } - - @Test // DATAMONGO-1518 - public void allTheThings() { - - assertThat(Collation.of(CollationLocale.of("de_AT").variant("phonebook")) - .strength(ComparisonLevel.primary().includeCase()).normalizationEnabled().backwardDiacriticSort() - .numericOrderingEnabled().alternate(Alternate.shifted().punct()).toDocument()).isEqualTo(ALL_THE_THINGS); - } - - @Test // DATAMONGO-1518 - public void allTheThingsFromDocument() { - assertThat(Collation.from(ALL_THE_THINGS).toDocument()).isEqualTo(ALL_THE_THINGS); - } - - @Test // DATAMONGO-1518 - public void justTheDefault() { - assertThat(Collation.simple().toDocument()).isEqualTo(BINARY_COMPARISON); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/CountQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/CountQueryUnitTests.java deleted file mode 100644 index 4b5ed6f2a8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/CountQueryUnitTests.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; - -/** - * Unit tests for {@link CountQuery}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -public class CountQueryUnitTests { - - QueryMapper mapper; - MongoMappingContext context; - MappingMongoConverter converter; - - MongoDatabaseFactory factory = mock(MongoDatabaseFactory.class); - - @BeforeEach - public void setUp() { - - this.context = new MongoMappingContext(); - - this.converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context); - this.converter.afterPropertiesSet(); - - this.mapper = new QueryMapper(converter); - } - - @Test // DATAMONGO-2059 - public void nearToGeoWithinWithoutDistance() { - - Query source = query(where("location").near(new Point(-73.99171, 40.738868))); - org.bson.Document target = postProcessQueryForCount(source); - - assertThat(target).isEqualTo(org.bson.Document - .parse("{\"location\": {\"$geoWithin\": {\"$center\": [[-73.99171, 40.738868], 1.7976931348623157E308]}}}")); - } - - @Test // DATAMONGO-2059 - public void nearAndExisting$and() { - - Query source = query(where("location").near(new Point(-73.99171, 40.738868)).minDistance(0.01)) - .addCriteria(new Criteria().andOperator(where("foo").is("bar"))); - org.bson.Document target = postProcessQueryForCount(source); - - assertThat(target).isEqualTo(org.bson.Document.parse("{\"$and\":[" // - + "{\"foo\":\"bar\"}" // - + "{\"$nor\":[{\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 0.01]}}}]},"// - + " {\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 1.7976931348623157E308]}}},"// - + "]}")); - } - - @Test // DATAMONGO-2059 - public void nearSphereToGeoWithinWithoutDistance() { - - Query source = query(where("location").nearSphere(new Point(-73.99171, 40.738868))); - org.bson.Document target = postProcessQueryForCount(source); - - assertThat(target).isEqualTo(org.bson.Document.parse( - "{\"location\": {\"$geoWithin\": {\"$centerSphere\": [[-73.99171, 40.738868], 1.7976931348623157E308]}}}")); - } - - @Test // DATAMONGO-2059 - public void nearToGeoWithinWithMaxDistance() { - - Query source = query(where("location").near(new Point(-73.99171, 40.738868)).maxDistance(10)); - org.bson.Document target = postProcessQueryForCount(source); - - assertThat(target).isEqualTo( - org.bson.Document.parse("{\"location\": {\"$geoWithin\": {\"$center\": [[-73.99171, 40.738868], 10.0]}}}")); - } - - @Test // DATAMONGO-2059 - public void nearSphereToGeoWithinWithMaxDistance() { - - Query source = query(where("location").nearSphere(new Point(-73.99171, 40.738868)).maxDistance(10)); - org.bson.Document target = postProcessQueryForCount(source); - - assertThat(target).isEqualTo(org.bson.Document - .parse("{\"location\": {\"$geoWithin\": {\"$centerSphere\": [[-73.99171, 40.738868], 10.0]}}}")); - } - - @Test // DATAMONGO-2059 - public void nearToGeoWithinWithMinDistance() { - - Query source = query(where("location").near(new Point(-73.99171, 40.738868)).minDistance(0.01)); - org.bson.Document target = postProcessQueryForCount(source); - - assertThat(target).isEqualTo(org.bson.Document.parse( - "{\"$and\":[{\"$nor\":[{\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 0.01]}}}]}," - + " {\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 1.7976931348623157E308]}}}]}")); - } - - @Test // DATAMONGO-2059 - public void nearToGeoWithinWithMaxDistanceAndCombinedWithOtherCriteria() { - - Query source = query( - where("name").is("food").and("location").near(new Point(-73.99171, 40.738868)).maxDistance(10)); - org.bson.Document target = postProcessQueryForCount(source); - - assertThat(target).isEqualTo(org.bson.Document - .parse("{\"name\": \"food\", \"location\": {\"$geoWithin\": {\"$center\": [[-73.99171, 40.738868], 10.0]}}}")); - } - - @Test // DATAMONGO-2059 - public void nearToGeoWithinWithMinDistanceOrCombinedWithOtherCriteria() { - - Query source = query(new Criteria().orOperator(where("name").is("food"), - where("location").near(new Point(-73.99171, 40.738868)).minDistance(0.01))); - org.bson.Document target = postProcessQueryForCount(source); - - assertThat(target).isEqualTo(org.bson.Document.parse( - "{\"$or\" : [ { \"name\": \"food\" }, {\"$and\":[{\"$nor\":[{\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 0.01]}}}]},{\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 1.7976931348623157E308]}}}]} ]}")); - } - - @Test // DATAMONGO-2059 - public void nearToGeoWithinWithMaxDistanceOrCombinedWithOtherCriteria() { - - Query source = query(new Criteria().orOperator(where("name").is("food"), - where("location").near(new Point(-73.99171, 40.738868)).maxDistance(10))); - org.bson.Document target = postProcessQueryForCount(source); - - assertThat(target).isEqualTo(org.bson.Document.parse( - "{\"$or\" : [ { \"name\": \"food\" }, {\"location\": {\"$geoWithin\": {\"$center\": [[-73.99171, 40.738868], 10.0]}}} ]}")); - } - - private org.bson.Document postProcessQueryForCount(Query source) { - - org.bson.Document intermediate = mapper.getMappedObject(source.getQueryObject(), (MongoPersistentEntity) null); - return CountQuery.of(intermediate).toQueryDocument(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsIntegrationTests.java deleted file mode 100644 index d194c12493..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsIntegrationTests.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright 2015-2021 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 static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.data.mongodb.BulkOperationException; -import org.springframework.data.mongodb.core.BulkOperations.BulkMode; -import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.convert.UpdateMapper; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; -import org.springframework.data.util.Pair; - -import com.mongodb.MongoBulkWriteException; -import com.mongodb.WriteConcern; -import com.mongodb.client.MongoCollection; - -/** - * Integration tests for {@link DefaultBulkOperations}. - * - * @author Tobias Trelle - * @author Oliver Gierke - * @author Christoph Strobl - * @author Minsu Kim - */ -@ExtendWith(MongoTemplateExtension.class) -public class DefaultBulkOperationsIntegrationTests { - - static final String COLLECTION_NAME = "bulk_ops"; - - @Template(initialEntitySet = BaseDoc.class) // - static MongoTestTemplate operations; - - @BeforeEach - public void setUp() { - operations.flush(COLLECTION_NAME); - } - - @Test // DATAMONGO-934 - public void rejectsNullMongoOperations() { - assertThatIllegalArgumentException().isThrownBy(() -> new DefaultBulkOperations(null, COLLECTION_NAME, - new BulkOperationContext(BulkMode.ORDERED, Optional.empty(), null, null, null, null))); - } - - @Test // DATAMONGO-934 - public void rejectsNullCollectionName() { - assertThatIllegalArgumentException().isThrownBy(() -> new DefaultBulkOperations(operations, null, - new BulkOperationContext(BulkMode.ORDERED, Optional.empty(), null, null, null, null))); - } - - @Test // DATAMONGO-934 - public void rejectsEmptyCollectionName() { - assertThatIllegalArgumentException().isThrownBy(() -> new DefaultBulkOperations(operations, "", - new BulkOperationContext(BulkMode.ORDERED, Optional.empty(), null, null, null, null))); - } - - @Test // DATAMONGO-934 - public void insertOrdered() { - - List documents = Arrays.asList(newDoc("1"), newDoc("2")); - - assertThat(createBulkOps(BulkMode.ORDERED).insert(documents).execute().getInsertedCount()).isEqualTo(2); - } - - @Test // DATAMONGO-934, DATAMONGO-2285 - public void insertOrderedFails() { - - List documents = Arrays.asList(newDoc("1"), newDoc("1"), newDoc("2")); - - assertThatThrownBy(() -> createBulkOps(BulkMode.ORDERED).insert(documents).execute()) // - .isInstanceOf(BulkOperationException.class) // - .hasCauseInstanceOf(MongoBulkWriteException.class) // - .extracting(Throwable::getCause) // - .satisfies(it -> { - - MongoBulkWriteException ex = (MongoBulkWriteException) it; - assertThat(ex.getWriteResult().getInsertedCount()).isOne(); - assertThat(ex.getWriteErrors()).isNotNull(); - assertThat(ex.getWriteErrors().size()).isOne(); - }); - } - - @Test // DATAMONGO-934 - public void insertUnOrdered() { - - List documents = Arrays.asList(newDoc("1"), newDoc("2")); - - assertThat(createBulkOps(BulkMode.UNORDERED).insert(documents).execute().getInsertedCount()).isEqualTo(2); - } - - @Test // DATAMONGO-934, DATAMONGO-2285 - public void insertUnOrderedContinuesOnError() { - - List documents = Arrays.asList(newDoc("1"), newDoc("1"), newDoc("2")); - - assertThatThrownBy(() -> createBulkOps(BulkMode.UNORDERED).insert(documents).execute()) // - .isInstanceOf(BulkOperationException.class) // - .hasCauseInstanceOf(MongoBulkWriteException.class) // - .extracting(Throwable::getCause) // - .satisfies(it -> { - - MongoBulkWriteException ex = (MongoBulkWriteException) it; - assertThat(ex.getWriteResult().getInsertedCount()).isEqualTo(2); - assertThat(ex.getWriteErrors()).isNotNull(); - assertThat(ex.getWriteErrors().size()).isOne(); - }); - } - - @Test // DATAMONGO-934 - public void upsertDoesUpdate() { - - insertSomeDocuments(); - - com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED).// - upsert(where("value", "value1"), set("value", "value2")).// - execute(); - - assertThat(result).isNotNull(); - assertThat(result.getMatchedCount()).isEqualTo(2); - assertThat(result.getModifiedCount()).isEqualTo(2); - assertThat(result.getInsertedCount()).isZero(); - assertThat(result.getUpserts()).isNotNull(); - assertThat(result.getUpserts().size()).isZero(); - } - - @Test // DATAMONGO-934 - public void upsertDoesInsert() { - - com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED).// - upsert(where("_id", "1"), set("value", "v1")).// - execute(); - - assertThat(result).isNotNull(); - assertThat(result.getMatchedCount()).isZero(); - assertThat(result.getModifiedCount()).isZero(); - assertThat(result.getUpserts()).isNotNull(); - assertThat(result.getUpserts().size()).isOne(); - } - - @Test // DATAMONGO-934 - public void updateOneOrdered() { - testUpdate(BulkMode.ORDERED, false, 2); - } - - @Test // DATAMONGO-934 - public void updateMultiOrdered() { - testUpdate(BulkMode.ORDERED, true, 4); - } - - @Test // DATAMONGO-934 - public void updateOneUnOrdered() { - testUpdate(BulkMode.UNORDERED, false, 2); - } - - @Test // DATAMONGO-934 - public void updateMultiUnOrdered() { - testUpdate(BulkMode.UNORDERED, true, 4); - } - - @Test // DATAMONGO-934 - public void removeOrdered() { - testRemove(BulkMode.ORDERED); - } - - @Test // DATAMONGO-934 - public void removeUnordered() { - testRemove(BulkMode.UNORDERED); - } - - @Test // DATAMONGO-2218 - public void replaceOneOrdered() { - testReplaceOne(BulkMode.ORDERED); - } - - @Test // DATAMONGO-2218 - public void replaceOneUnordered() { - testReplaceOne(BulkMode.UNORDERED); - } - - @Test // DATAMONGO-2218 - public void replaceOneDoesReplace() { - - insertSomeDocuments(); - - com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED).// - replaceOne(where("_id", "1"), rawDoc("1", "value2")).// - execute(); - - assertThat(result).isNotNull(); - assertThat(result.getMatchedCount()).isOne(); - assertThat(result.getModifiedCount()).isOne(); - assertThat(result.getInsertedCount()).isZero(); - } - - @Test // DATAMONGO-2218 - public void replaceOneWithUpsert() { - - com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED).// - replaceOne(where("_id", "1"), rawDoc("1", "value2"), FindAndReplaceOptions.options().upsert()).// - execute(); - - assertThat(result).isNotNull(); - assertThat(result.getMatchedCount()).isZero(); - assertThat(result.getInsertedCount()).isZero(); - assertThat(result.getModifiedCount()).isZero(); - assertThat(result.getUpserts().size()).isOne(); - } - - /** - * If working on the same set of documents, only an ordered bulk operation will yield predictable results. - */ - @Test // DATAMONGO-934 - public void mixedBulkOrdered() { - - com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED, BaseDoc.class).insert(newDoc("1", "v1")).// - updateOne(where("_id", "1"), set("value", "v2")).// - remove(where("value", "v2")).// - execute(); - - assertThat(result).isNotNull(); - assertThat(result.getInsertedCount()).isOne(); - assertThat(result.getModifiedCount()).isOne(); - assertThat(result.getDeletedCount()).isOne(); - } - - /** - * If working on the same set of documents, only an ordered bulk operation will yield predictable results. - */ - @Test - @SuppressWarnings("unchecked") - public void mixedBulkOrderedWithList() { - - List inserts = Arrays.asList(newDoc("1", "v1"), newDoc("2", "v2"), newDoc("3", "v2")); - List> updates = Arrays.asList(Pair.of(where("value", "v2"), set("value", "v3"))); - List removes = Arrays.asList(where("_id", "1")); - - com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED, BaseDoc.class).insert(inserts) - .updateMulti(updates).remove(removes).execute(); - - assertThat(result).isNotNull(); - assertThat(result.getInsertedCount()).isEqualTo(3); - assertThat(result.getModifiedCount()).isEqualTo(2); - assertThat(result.getDeletedCount()).isOne(); - } - - @Test // DATAMONGO-1534 - public void insertShouldConsiderInheritance() { - - SpecialDoc specialDoc = new SpecialDoc(); - specialDoc.id = "id-special"; - specialDoc.value = "normal-value"; - specialDoc.specialValue = "special-value"; - - createBulkOps(BulkMode.ORDERED, SpecialDoc.class).insert(Arrays.asList(specialDoc)).execute(); - - BaseDoc doc = operations.findOne(where("_id", specialDoc.id), BaseDoc.class, COLLECTION_NAME); - - assertThat(doc).isNotNull(); - assertThat(doc).isInstanceOf(SpecialDoc.class); - } - - private void testUpdate(BulkMode mode, boolean multi, int expectedUpdates) { - - BulkOperations bulkOps = createBulkOps(mode); - - insertSomeDocuments(); - - List> updates = new ArrayList>(); - updates.add(Pair.of(where("value", "value1"), set("value", "value3"))); - updates.add(Pair.of(where("value", "value2"), set("value", "value4"))); - - int modifiedCount = multi ? bulkOps.updateMulti(updates).execute().getModifiedCount() - : bulkOps.updateOne(updates).execute().getModifiedCount(); - - assertThat(modifiedCount).isEqualTo(expectedUpdates); - } - - private void testRemove(BulkMode mode) { - - insertSomeDocuments(); - - List removes = Arrays.asList(where("_id", "1"), where("value", "value2")); - - assertThat(createBulkOps(mode).remove(removes).execute().getDeletedCount()).isEqualTo(3); - } - - private void testReplaceOne(BulkMode mode) { - - BulkOperations bulkOps = createBulkOps(mode); - - insertSomeDocuments(); - - Query query = where("_id", "1"); - Document document = rawDoc("1", "value2"); - int modifiedCount = bulkOps.replaceOne(query, document).execute().getModifiedCount(); - - assertThat(modifiedCount).isOne(); - } - - private BulkOperations createBulkOps(BulkMode mode) { - return createBulkOps(mode, null); - } - - private BulkOperations createBulkOps(BulkMode mode, Class entityType) { - - Optional> entity = entityType != null - ? Optional.of(operations.getConverter().getMappingContext().getPersistentEntity(entityType)) - : Optional.empty(); - - BulkOperationContext bulkOperationContext = new BulkOperationContext(mode, entity, - new QueryMapper(operations.getConverter()), new UpdateMapper(operations.getConverter()), null, null); - - DefaultBulkOperations bulkOps = new DefaultBulkOperations(operations, COLLECTION_NAME, bulkOperationContext); - bulkOps.setDefaultWriteConcern(WriteConcern.ACKNOWLEDGED); - - return bulkOps; - } - - private void insertSomeDocuments() { - - final MongoCollection coll = operations.getCollection(COLLECTION_NAME); - - coll.insertOne(rawDoc("1", "value1")); - coll.insertOne(rawDoc("2", "value1")); - coll.insertOne(rawDoc("3", "value2")); - coll.insertOne(rawDoc("4", "value2")); - } - - private static BaseDoc newDoc(String id) { - - BaseDoc doc = new BaseDoc(); - doc.id = id; - - return doc; - } - - private static BaseDoc newDoc(String id, String value) { - - BaseDoc doc = newDoc(id); - doc.value = value; - - return doc; - } - - private static Query where(String field, String value) { - return new Query().addCriteria(Criteria.where(field).is(value)); - } - - private static Update set(String field, String value) { - return new Update().set(field, value); - } - - private static Document rawDoc(String id, String value) { - return new Document("_id", id).append("value", value); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsUnitTests.java deleted file mode 100644 index 405b595f1f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsUnitTests.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyList; -import static org.mockito.Mockito.eq; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import org.bson.BsonDocument; -import org.bson.BsonString; -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.support.PersistenceExceptionTranslator; -import org.springframework.data.annotation.Id; -import org.springframework.data.mapping.callback.EntityCallbacks; -import org.springframework.data.mongodb.BulkOperationException; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.BulkOperations.BulkMode; -import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.convert.UpdateMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback; -import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; -import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback; -import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent; -import org.springframework.data.mongodb.core.mapping.event.BeforeSaveCallback; -import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Update; - -import com.mongodb.MongoBulkWriteException; -import com.mongodb.MongoWriteException; -import com.mongodb.ServerAddress; -import com.mongodb.WriteConcern; -import com.mongodb.WriteError; -import com.mongodb.bulk.BulkWriteError; -import com.mongodb.bulk.WriteConcernError; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.model.BulkWriteOptions; -import com.mongodb.client.model.DeleteManyModel; -import com.mongodb.client.model.InsertOneModel; -import com.mongodb.client.model.ReplaceOneModel; -import com.mongodb.client.model.UpdateManyModel; -import com.mongodb.client.model.UpdateOneModel; -import com.mongodb.client.model.WriteModel; - -/** - * Unit tests for {@link DefaultBulkOperations}. - * - * @author Christoph Strobl - * @author Mark Paluch - * @author Minsu Kim - * @author Jens Schauder - * @author Roman Puchkovskiy - * @author Jacob Botuck - */ -@ExtendWith(MockitoExtension.class) -class DefaultBulkOperationsUnitTests { - - private MongoTemplate template; - @Mock MongoDatabase database; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) MongoCollection collection; - @Mock MongoDatabaseFactory factory; - @Mock DbRefResolver dbRefResolver; - @Captor ArgumentCaptor>> captor; - private MongoConverter converter; - private MongoMappingContext mappingContext; - - private DefaultBulkOperations ops; - - @BeforeEach - void setUp() { - - when(factory.getMongoDatabase()).thenReturn(database); - when(factory.getExceptionTranslator()).thenReturn(new NullExceptionTranslator()); - when(database.getCollection(anyString(), eq(Document.class))).thenReturn(collection); - - mappingContext = new MongoMappingContext(); - mappingContext.afterPropertiesSet(); - - converter = new MappingMongoConverter(dbRefResolver, mappingContext); - template = new MongoTemplate(factory, converter); - - ops = new DefaultBulkOperations(template, "collection-1", - new BulkOperationContext(BulkMode.ORDERED, - Optional.of(mappingContext.getPersistentEntity(SomeDomainType.class)), new QueryMapper(converter), - new UpdateMapper(converter), null, null)); - } - - @Test // DATAMONGO-1518 - void updateOneShouldUseCollationWhenPresent() { - - ops.updateOne(new BasicQuery("{}").collation(Collation.of("de")), new Update().set("lastName", "targaryen")) - .execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - assertThat(captor.getValue().get(0)).isInstanceOf(UpdateOneModel.class); - assertThat(((UpdateOneModel) captor.getValue().get(0)).getOptions().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de").build()); - } - - @Test // DATAMONGO-1518 - void updateManyShouldUseCollationWhenPresent() { - - ops.updateMulti(new BasicQuery("{}").collation(Collation.of("de")), new Update().set("lastName", "targaryen")) - .execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - assertThat(captor.getValue().get(0)).isInstanceOf(UpdateManyModel.class); - assertThat(((UpdateManyModel) captor.getValue().get(0)).getOptions().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de").build()); - } - - @Test // DATAMONGO-1518 - void removeShouldUseCollationWhenPresent() { - - ops.remove(new BasicQuery("{}").collation(Collation.of("de"))).execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - assertThat(captor.getValue().get(0)).isInstanceOf(DeleteManyModel.class); - assertThat(((DeleteManyModel) captor.getValue().get(0)).getOptions().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de").build()); - } - - @Test // DATAMONGO-2218 - void replaceOneShouldUseCollationWhenPresent() { - - ops.replaceOne(new BasicQuery("{}").collation(Collation.of("de")), new SomeDomainType()).execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - assertThat(captor.getValue().get(0)).isInstanceOf(ReplaceOneModel.class); - assertThat(((ReplaceOneModel) captor.getValue().get(0)).getReplaceOptions().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de").build()); - } - - @Test // DATAMONGO-1678 - void bulkUpdateShouldMapQueryAndUpdateCorrectly() { - - ops.updateOne(query(where("firstName").is("danerys")), Update.update("firstName", "queen danerys")).execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - UpdateOneModel updateModel = (UpdateOneModel) captor.getValue().get(0); - assertThat(updateModel.getFilter()).isEqualTo(new Document("first_name", "danerys")); - assertThat(updateModel.getUpdate()).isEqualTo(new Document("$set", new Document("first_name", "queen danerys"))); - } - - @Test // DATAMONGO-1678 - void bulkRemoveShouldMapQueryCorrectly() { - - ops.remove(query(where("firstName").is("danerys"))).execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - DeleteManyModel updateModel = (DeleteManyModel) captor.getValue().get(0); - assertThat(updateModel.getFilter()).isEqualTo(new Document("first_name", "danerys")); - } - - @Test // DATAMONGO-2218 - void bulkReplaceOneShouldMapQueryCorrectly() { - - SomeDomainType replacement = new SomeDomainType(); - replacement.firstName = "Minsu"; - replacement.lastName = "Kim"; - - ops.replaceOne(query(where("firstName").is("danerys")), replacement).execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - ReplaceOneModel updateModel = (ReplaceOneModel) captor.getValue().get(0); - assertThat(updateModel.getFilter()).isEqualTo(new Document("first_name", "danerys")); - assertThat(updateModel.getReplacement().getString("first_name")).isEqualTo("Minsu"); - assertThat(updateModel.getReplacement().getString("lastName")).isEqualTo("Kim"); - } - - @Test // DATAMONGO-2261, DATAMONGO-2479 - void bulkInsertInvokesEntityCallbacks() { - - BeforeConvertPersonCallback beforeConvertCallback = spy(new BeforeConvertPersonCallback()); - BeforeSavePersonCallback beforeSaveCallback = spy(new BeforeSavePersonCallback()); - AfterSavePersonCallback afterSaveCallback = spy(new AfterSavePersonCallback()); - - ops = new DefaultBulkOperations(template, "collection-1", - new BulkOperationContext(BulkMode.ORDERED, Optional.of(mappingContext.getPersistentEntity(Person.class)), - new QueryMapper(converter), new UpdateMapper(converter), null, - EntityCallbacks.create(beforeConvertCallback, beforeSaveCallback, afterSaveCallback))); - - Person entity = new Person("init"); - ops.insert(entity); - - ArgumentCaptor personArgumentCaptor = ArgumentCaptor.forClass(Person.class); - verify(beforeConvertCallback).onBeforeConvert(personArgumentCaptor.capture(), eq("collection-1")); - verifyZeroInteractions(beforeSaveCallback); - - ops.execute(); - - verify(beforeSaveCallback).onBeforeSave(personArgumentCaptor.capture(), any(), eq("collection-1")); - verify(afterSaveCallback).onAfterSave(personArgumentCaptor.capture(), any(), eq("collection-1")); - assertThat(personArgumentCaptor.getAllValues()).extracting("firstName").containsExactly("init", "before-convert", - "before-convert"); - verify(collection).bulkWrite(captor.capture(), any()); - - InsertOneModel updateModel = (InsertOneModel) captor.getValue().get(0); - assertThat(updateModel.getDocument()).containsEntry("firstName", "after-save"); - } - - @Test // DATAMONGO-2290 - void bulkReplaceOneEmitsEventsCorrectly() { - - ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class); - - ops = new DefaultBulkOperations(template, "collection-1", - new BulkOperationContext(BulkMode.ORDERED, Optional.of(mappingContext.getPersistentEntity(Person.class)), - new QueryMapper(converter), new UpdateMapper(converter), eventPublisher, null)); - - ops.replaceOne(query(where("firstName").is("danerys")), new SomeDomainType()); - - verify(eventPublisher).publishEvent(any(BeforeConvertEvent.class)); - verify(eventPublisher, never()).publishEvent(any(BeforeSaveEvent.class)); - verify(eventPublisher, never()).publishEvent(any(AfterSaveEvent.class)); - - ops.execute(); - - verify(eventPublisher).publishEvent(any(BeforeSaveEvent.class)); - verify(eventPublisher).publishEvent(any(AfterSaveEvent.class)); - } - - @Test // DATAMONGO-2290 - void bulkInsertEmitsEventsCorrectly() { - - ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class); - - ops = new DefaultBulkOperations(template, "collection-1", - new BulkOperationContext(BulkMode.ORDERED, Optional.of(mappingContext.getPersistentEntity(Person.class)), - new QueryMapper(converter), new UpdateMapper(converter), eventPublisher, null)); - - ops.insert(new SomeDomainType()); - - verify(eventPublisher).publishEvent(any(BeforeConvertEvent.class)); - verify(eventPublisher, never()).publishEvent(any(BeforeSaveEvent.class)); - verify(eventPublisher, never()).publishEvent(any(AfterSaveEvent.class)); - - ops.execute(); - - verify(eventPublisher).publishEvent(any(BeforeSaveEvent.class)); - verify(eventPublisher).publishEvent(any(AfterSaveEvent.class)); - } - - @Test // DATAMONGO-2290 - void noAfterSaveEventOnFailure() { - - ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class); - when(collection.bulkWrite(anyList(), any(BulkWriteOptions.class))).thenThrow(new MongoWriteException( - new WriteError(89, "NetworkTimeout", new BsonDocument("hi", new BsonString("there!"))), null)); - - ops = new DefaultBulkOperations(template, "collection-1", - new BulkOperationContext(BulkMode.ORDERED, Optional.of(mappingContext.getPersistentEntity(Person.class)), - new QueryMapper(converter), new UpdateMapper(converter), eventPublisher, null)); - - ops.insert(new SomeDomainType()); - - verify(eventPublisher).publishEvent(any(BeforeConvertEvent.class)); - - try { - ops.execute(); - fail("Missing MongoWriteException"); - } catch (MongoWriteException expected) { - - } - - verify(eventPublisher).publishEvent(any(BeforeSaveEvent.class)); - } - - @Test // DATAMONGO-2330 - void writeConcernNotAppliedWhenNotSet() { - - ops.updateOne(new BasicQuery("{}").collation(Collation.of("de")), new Update().set("lastName", "targaryen")) - .execute(); - - verify(collection, never()).withWriteConcern(any()); - } - - @Test // DATAMONGO-2330 - void writeConcernAppliedCorrectlyWhenSet() { - - ops.setDefaultWriteConcern(WriteConcern.MAJORITY); - - ops.updateOne(new BasicQuery("{}").collation(Collation.of("de")), new Update().set("lastName", "targaryen")) - .execute(); - - verify(collection).withWriteConcern(eq(WriteConcern.MAJORITY)); - } - - @Test // DATAMONGO-2450 - void appliesArrayFilterWhenPresent() { - - ops.updateOne(new BasicQuery("{}"), new Update().filterArray(Criteria.where("element").gte(100))).execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - UpdateOneModel updateModel = (UpdateOneModel) captor.getValue().get(0); - assertThat(updateModel.getOptions().getArrayFilters().get(0)) - .isEqualTo(new org.bson.Document("element", new Document("$gte", 100))); - } - - @Test // DATAMONGO-2502 - void shouldRetainNestedArrayPathWithPlaceholdersForNoMatchingPaths() { - - ops.updateOne(new BasicQuery("{}"), new Update().set("items.$.documents.0.fileId", "new-id")).execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - UpdateOneModel updateModel = (UpdateOneModel) captor.getValue().get(0); - assertThat(updateModel.getUpdate()) - .isEqualTo(new Document("$set", new Document("items.$.documents.0.fileId", "new-id"))); - } - - @Test // DATAMONGO-2502 - void shouldRetainNestedArrayPathWithPlaceholdersForMappedEntity() { - - DefaultBulkOperations ops = new DefaultBulkOperations(template, "collection-1", - new BulkOperationContext(BulkMode.ORDERED, Optional.of(mappingContext.getPersistentEntity(OrderTest.class)), - new QueryMapper(converter), new UpdateMapper(converter), null, null)); - - ops.updateOne(new BasicQuery("{}"), Update.update("items.$.documents.0.fileId", "file-id")).execute(); - - verify(collection).bulkWrite(captor.capture(), any()); - - UpdateOneModel updateModel = (UpdateOneModel) captor.getValue().get(0); - assertThat(updateModel.getUpdate()) - .isEqualTo(new Document("$set", new Document("items.$.documents.0.the_file_id", "file-id"))); - } - - @Test // DATAMONGO-2285 - public void translateMongoBulkOperationExceptionWithWriteConcernError() { - - when(collection.bulkWrite(anyList(), any(BulkWriteOptions.class))).thenThrow(new MongoBulkWriteException(null, - Collections.emptyList(), - new WriteConcernError(42, "codename", "writeconcern error happened", new BsonDocument()), new ServerAddress())); - - assertThatExceptionOfType(DataIntegrityViolationException.class) - .isThrownBy(() -> ops.insert(new SomeDomainType()).execute()); - - } - - @Test // DATAMONGO-2285 - public void translateMongoBulkOperationExceptionWithoutWriteConcernError() { - - when(collection.bulkWrite(anyList(), any(BulkWriteOptions.class))).thenThrow(new MongoBulkWriteException(null, - Collections.singletonList(new BulkWriteError(42, "a write error happened", new BsonDocument(), 49)), null, - new ServerAddress())); - - assertThatExceptionOfType(BulkOperationException.class) - .isThrownBy(() -> ops.insert(new SomeDomainType()).execute()); - } - - static class OrderTest { - - String id; - List items; - } - - static class OrderTestItem { - - private String cartId; - private List documents; - } - - static class OrderTestDocument { - - @Field("the_file_id") - private String fileId; - } - - class SomeDomainType { - - @Id String id; - Gender gender; - @Field("first_name") String firstName; - @Field String lastName; - } - - enum Gender { - M, F - } - - static class BeforeConvertPersonCallback implements BeforeConvertCallback { - - @Override - public Person onBeforeConvert(Person entity, String collection) { - return new Person("before-convert"); - } - } - - static class BeforeSavePersonCallback implements BeforeSaveCallback { - - @Override - public Person onBeforeSave(Person entity, Document document, String collection) { - - document.put("firstName", "before-save"); - return new Person("before-save"); - } - } - - static class AfterSavePersonCallback implements AfterSaveCallback { - - @Override - public Person onAfterSave(Person entity, Document document, String collection) { - - document.put("firstName", "after-save"); - return new Person("after-save"); - } - } - - static class NullExceptionTranslator implements PersistenceExceptionTranslator { - - @Override - public DataAccessException translateExceptionIfPossible(RuntimeException ex) { - return null; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java deleted file mode 100644 index 4a934a960b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsIntegrationTests.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2014-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.index.PartialIndexFilter.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import org.bson.BsonDocument; -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.index.Index; -import org.springframework.data.mongodb.core.index.IndexDefinition; -import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.data.mongodb.core.index.IndexOperations; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Collation.CaseFirst; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; -import org.springframework.util.ObjectUtils; - -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.IndexOptions; - -/** - * Integration tests for {@link DefaultIndexOperations}. - * - * @author Christoph Strobl - * @author Oliver Gierke - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -public class DefaultIndexOperationsIntegrationTests { - - static final String COLLECTION_NAME = "default-index-operations-tests"; - static final org.bson.Document GEO_SPHERE_2D = new org.bson.Document("loaction", "2dsphere"); - - @Template // - static MongoTestTemplate template; - - MongoCollection collection = template.getCollection(COLLECTION_NAME); - IndexOperations indexOps = template.indexOps(COLLECTION_NAME); - - @BeforeEach - public void setUp() { - template.dropIndexes(COLLECTION_NAME); - } - - @Test // DATAMONGO-1008 - public void getIndexInfoShouldBeAbleToRead2dsphereIndex() { - - template.getCollection(COLLECTION_NAME).createIndex(GEO_SPHERE_2D); - - IndexInfo info = findAndReturnIndexInfo(GEO_SPHERE_2D); - assertThat(info.getIndexFields().get(0).isGeo()).isEqualTo(true); - } - - @Test // DATAMONGO-1467, DATAMONGO-2198 - public void shouldApplyPartialFilterCorrectly() { - - IndexDefinition id = new Index().named("partial-with-criteria").on("k3y", Direction.ASC) - .partial(of(where("q-t-y").gte(10))); - - indexOps.ensureIndex(id); - - IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-criteria"); - assertThat(Document.parse(info.getPartialFilterExpression())) - .isEqualTo(Document.parse("{ \"q-t-y\" : { \"$gte\" : 10 } }")); - } - - @Test // DATAMONGO-1467, DATAMONGO-2198 - public void shouldApplyPartialFilterWithMappedPropertyCorrectly() { - - IndexDefinition id = new Index().named("partial-with-mapped-criteria").on("k3y", Direction.ASC) - .partial(of(where("quantity").gte(10))); - - template.indexOps(DefaultIndexOperationsIntegrationTestsSample.class).ensureIndex(id); - - IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-mapped-criteria"); - assertThat(Document.parse(info.getPartialFilterExpression())) - .isEqualTo(Document.parse("{ \"qty\" : { \"$gte\" : 10 } }")); - } - - @Test // DATAMONGO-1467, DATAMONGO-2198 - public void shouldApplyPartialDBOFilterCorrectly() { - - IndexDefinition id = new Index().named("partial-with-dbo").on("k3y", Direction.ASC) - .partial(of(new org.bson.Document("qty", new org.bson.Document("$gte", 10)))); - - indexOps.ensureIndex(id); - - IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-dbo"); - assertThat(Document.parse(info.getPartialFilterExpression())) - .isEqualTo(Document.parse("{ \"qty\" : { \"$gte\" : 10 } }")); - } - - @Test // DATAMONGO-1467, DATAMONGO-2198 - public void shouldFavorExplicitMappingHintViaClass() { - - IndexDefinition id = new Index().named("partial-with-inheritance").on("k3y", Direction.ASC) - .partial(of(where("age").gte(10))); - - indexOps = new DefaultIndexOperations(template.getMongoDbFactory(), COLLECTION_NAME, - new QueryMapper(template.getConverter()), MappingToSameCollection.class); - - indexOps.ensureIndex(id); - - IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-inheritance"); - assertThat(Document.parse(info.getPartialFilterExpression())) - .isEqualTo(Document.parse("{ \"a_g_e\" : { \"$gte\" : 10 } }")); - } - - @Test // DATAMONGO-2388 - public void shouldReadIndexWithPartialFilterContainingDbRefCorrectly() { - - BsonDocument partialFilter = BsonDocument.parse( - "{ \"the-ref\" : { \"$ref\" : \"other-collection\", \"$id\" : { \"$oid\" : \"59ce08baf264b906810fe8c5\"} } }"); - IndexOptions indexOptions = new IndexOptions(); - indexOptions.name("partial-with-dbref"); - indexOptions.partialFilterExpression(partialFilter); - - collection.createIndex(BsonDocument.parse("{ \"key-1\" : 1, \"key-2\": 1}"), indexOptions); - - IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-dbref"); - assertThat(BsonDocument.parse(info.getPartialFilterExpression())).isEqualTo(partialFilter); - } - - @Test // DATAMONGO-1518 - public void shouldCreateIndexWithCollationCorrectly() { - - IndexDefinition id = new Index().named("with-collation").on("xyz", Direction.ASC) - .collation(Collation.of("de_AT").caseFirst(CaseFirst.off())); - - new DefaultIndexOperations(template.getMongoDbFactory(), COLLECTION_NAME, new QueryMapper(template.getConverter()), - MappingToSameCollection.class); - - indexOps.ensureIndex(id); - - Document expected = new Document("locale", "de_AT") // - .append("caseLevel", false) // - .append("caseFirst", "off") // - .append("strength", 3) // - .append("numericOrdering", false) // - .append("alternate", "non-ignorable") // - .append("maxVariable", "punct") // - .append("normalization", false) // - .append("backwards", false); - - IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "with-collation"); - - assertThat(info.getCollation()).isPresent(); - - // version is set by MongoDB server - we remove it to avoid errors when upgrading server version. - Document result = info.getCollation().get(); - result.remove("version"); - - assertThat(result).isEqualTo(expected); - } - - private IndexInfo findAndReturnIndexInfo(org.bson.Document keys) { - return findAndReturnIndexInfo(indexOps.getIndexInfo(), keys); - } - - private static IndexInfo findAndReturnIndexInfo(Iterable candidates, org.bson.Document keys) { - return findAndReturnIndexInfo(candidates, genIndexName(keys)); - } - - private static IndexInfo findAndReturnIndexInfo(Iterable candidates, String name) { - - for (IndexInfo info : candidates) { - if (ObjectUtils.nullSafeEquals(name, info.getName())) { - return info; - } - } - throw new AssertionError(String.format("Index with %s was not found", name)); - } - - private static String genIndexName(Document keys) { - - StringBuilder name = new StringBuilder(); - - for (String s : keys.keySet()) { - - if (name.length() > 0) { - name.append('_'); - } - - name.append(s).append('_'); - Object val = keys.get(s); - - if (val instanceof Number || val instanceof String) { - name.append(val.toString().replace(' ', '_')); - } - } - - return name.toString(); - } - - @org.springframework.data.mongodb.core.mapping.Document(collection = "default-index-operations-tests") - static class DefaultIndexOperationsIntegrationTestsSample { - - @Field("qty") Integer quantity; - } - - @org.springframework.data.mongodb.core.mapping.Document(collection = "default-index-operations-tests") - static class MappingToSameCollection extends DefaultIndexOperationsIntegrationTestsSample { - - @Field("a_g_e") Integer age; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsUnitTests.java deleted file mode 100644 index ca16ea312c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultIndexOperationsUnitTests.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import lombok.Data; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.index.HashedIndex; -import org.springframework.data.mongodb.core.index.Index; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.Collation; - -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.model.IndexOptions; - -/** - * Unit tests for {@link DefaultIndexOperations}. - * - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -public class DefaultIndexOperationsUnitTests { - - private MongoTemplate template; - - @Mock MongoDatabaseFactory factory; - @Mock MongoDatabase db; - @Mock MongoCollection collection; - - private MongoExceptionTranslator exceptionTranslator = new MongoExceptionTranslator(); - private MappingMongoConverter converter; - private MongoMappingContext mappingContext; - - @BeforeEach - void setUp() { - - when(factory.getMongoDatabase()).thenReturn(db); - when(factory.getExceptionTranslator()).thenReturn(exceptionTranslator); - when(db.getCollection(any(), any(Class.class))).thenReturn(collection); - when(collection.createIndex(any(), any(IndexOptions.class))).thenReturn("OK"); - - this.mappingContext = new MongoMappingContext(); - this.converter = spy(new MappingMongoConverter(new DefaultDbRefResolver(factory), mappingContext)); - this.template = new MongoTemplate(factory, converter); - } - - @Test // DATAMONGO-1183 - void indexOperationsMapFieldNameCorrectly() { - - indexOpsFor(Jedi.class).ensureIndex(new Index("name", Direction.DESC)); - - verify(collection).createIndex(eq(new Document("firstname", -1)), any()); - } - - @Test // DATAMONGO-1854 - void ensureIndexDoesNotSetCollectionIfNoDefaultDefined() { - - indexOpsFor(Jedi.class).ensureIndex(new Index("firstname", Direction.DESC)); - - ArgumentCaptor options = ArgumentCaptor.forClass(IndexOptions.class); - verify(collection).createIndex(any(), options.capture()); - - assertThat(options.getValue().getCollation()).isNull(); - } - - @Test // DATAMONGO-1854 - void ensureIndexUsesDefaultCollationIfNoneDefinedInOptions() { - - indexOpsFor(Sith.class).ensureIndex(new Index("firstname", Direction.DESC)); - - ArgumentCaptor options = ArgumentCaptor.forClass(IndexOptions.class); - verify(collection).createIndex(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void ensureIndexDoesNotUseDefaultCollationIfExplicitlySpecifiedInTheIndex() { - - indexOpsFor(Sith.class).ensureIndex(new Index("firstname", Direction.DESC).collation(Collation.of("en_US"))); - - ArgumentCaptor options = ArgumentCaptor.forClass(IndexOptions.class); - verify(collection).createIndex(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("en_US").build()); - } - - @Test // DATAMONGO-1183 - void shouldCreateHashedIndexCorrectly() { - - indexOpsFor(Jedi.class).ensureIndex(HashedIndex.hashed("name")); - - verify(collection).createIndex(eq(new Document("firstname", "hashed")), any()); - } - - private DefaultIndexOperations indexOpsFor(Class type) { - return new DefaultIndexOperations(template, template.getCollectionName(type), type); - } - - @Data - static class Jedi { - @Field("firstname") String name; - } - - @org.springframework.data.mongodb.core.mapping.Document(collation = "de_AT") - static class Sith { - @Field("firstname") String name; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java deleted file mode 100644 index c3e9fe9ac6..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsTests.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.index.PartialIndexFilter.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import reactor.test.StepVerifier; - -import java.util.function.Predicate; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.index.Index; -import org.springframework.data.mongodb.core.index.IndexDefinition; -import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Collation.CaseFirst; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.ReactiveMongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -import com.mongodb.reactivestreams.client.MongoCollection; - -/** - * @author Christoph Strobl - * @author Mark Paluch - * @author Mathieu Ouellet - */ -@ExtendWith(MongoTemplateExtension.class) -public class DefaultReactiveIndexOperationsTests { - - @Template(initialEntitySet = DefaultIndexOperationsIntegrationTestsSample.class) - static ReactiveMongoTestTemplate template; - - String collectionName = template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class); - - DefaultReactiveIndexOperations indexOps = new DefaultReactiveIndexOperations(template, collectionName, - new QueryMapper(template.getConverter())); - - @BeforeEach - public void setUp() { - template.getCollection(collectionName).flatMapMany(MongoCollection::dropIndexes) // - .as(StepVerifier::create) // - .verifyComplete(); - } - - @Test // DATAMONGO-1518 - public void shouldCreateIndexWithCollationCorrectly() { - - IndexDefinition id = new Index().named("with-collation").on("xyz", Direction.ASC) - .collation(Collation.of("de_AT").caseFirst(CaseFirst.off())); - - indexOps.ensureIndex(id).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - Document expected = new Document("locale", "de_AT") // - .append("caseLevel", false) // - .append("caseFirst", "off") // - .append("strength", 3) // - .append("numericOrdering", false) // - .append("alternate", "non-ignorable") // - .append("maxVariable", "punct") // - .append("normalization", false) // - .append("backwards", false); - - indexOps.getIndexInfo().filter(this.indexByName("with-collation")).as(StepVerifier::create) // - .consumeNextWith(indexInfo -> { - - assertThat(indexInfo.getCollation()).isPresent(); - - // version is set by MongoDB server - we remove it to avoid errors when upgrading server version. - Document result = indexInfo.getCollation().get(); - result.remove("version"); - - assertThat(result).isEqualTo(expected); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1682, DATAMONGO-2198 - public void shouldApplyPartialFilterCorrectly() { - - IndexDefinition id = new Index().named("partial-with-criteria").on("k3y", Direction.ASC) - .partial(of(where("q-t-y").gte(10))); - - indexOps.ensureIndex(id).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - indexOps.getIndexInfo().filter(this.indexByName("partial-with-criteria")).as(StepVerifier::create) // - .consumeNextWith(indexInfo -> { - assertThat(Document.parse(indexInfo.getPartialFilterExpression())) - .isEqualTo(Document.parse("{ \"q-t-y\" : { \"$gte\" : 10 } }")); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1682, DATAMONGO-2198 - public void shouldApplyPartialFilterWithMappedPropertyCorrectly() { - - IndexDefinition id = new Index().named("partial-with-mapped-criteria").on("k3y", Direction.ASC) - .partial(of(where("quantity").gte(10))); - - indexOps.ensureIndex(id).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - indexOps.getIndexInfo().filter(this.indexByName("partial-with-mapped-criteria")).as(StepVerifier::create) // - .consumeNextWith(indexInfo -> { - assertThat(Document.parse(indexInfo.getPartialFilterExpression())) - .isEqualTo(Document.parse("{ \"qty\" : { \"$gte\" : 10 } }")); - }).verifyComplete(); - } - - @Test // DATAMONGO-1682, DATAMONGO-2198 - public void shouldApplyPartialDBOFilterCorrectly() { - - IndexDefinition id = new Index().named("partial-with-dbo").on("k3y", Direction.ASC) - .partial(of(new org.bson.Document("qty", new org.bson.Document("$gte", 10)))); - - indexOps.ensureIndex(id).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - indexOps.getIndexInfo().filter(this.indexByName("partial-with-dbo")).as(StepVerifier::create) // - .consumeNextWith(indexInfo -> { - assertThat(Document.parse(indexInfo.getPartialFilterExpression())) - .isEqualTo(Document.parse("{ \"qty\" : { \"$gte\" : 10 } }")); - }) // - .verifyComplete(); - - } - - @Test // DATAMONGO-1682, DATAMONGO-2198 - public void shouldFavorExplicitMappingHintViaClass() { - - IndexDefinition id = new Index().named("partial-with-inheritance").on("k3y", Direction.ASC) - .partial(of(where("age").gte(10))); - - indexOps = new DefaultReactiveIndexOperations(template, - this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class), - new QueryMapper(template.getConverter()), MappingToSameCollection.class); - - indexOps.ensureIndex(id).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - indexOps.getIndexInfo().filter(this.indexByName("partial-with-inheritance")).as(StepVerifier::create) // - .consumeNextWith(indexInfo -> { - assertThat(Document.parse(indexInfo.getPartialFilterExpression())) - .isEqualTo(Document.parse("{ \"a_g_e\" : { \"$gte\" : 10 } }")); - }) // - .verifyComplete(); - } - - Predicate indexByName(String name) { - return indexInfo -> indexInfo.getName().equals(name); - } - - @org.springframework.data.mongodb.core.mapping.Document(collection = "default-index-operations-tests") - static class DefaultIndexOperationsIntegrationTestsSample { - - @Field("qty") Integer quantity; - } - - @org.springframework.data.mongodb.core.mapping.Document(collection = "default-index-operations-tests") - static class MappingToSameCollection - extends DefaultIndexOperationsIntegrationTests.DefaultIndexOperationsIntegrationTestsSample { - - @Field("a_g_e") Integer age; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsUnitTests.java deleted file mode 100644 index d5dd1b4769..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperationsUnitTests.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import lombok.Data; -import reactor.core.publisher.Mono; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.reactivestreams.Publisher; - -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.index.Index; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.Collation; - -import com.mongodb.client.model.IndexOptions; -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * @author Christoph Strobl - * @author Mathieu Ouellet - */ -@ExtendWith(MockitoExtension.class) -public class DefaultReactiveIndexOperationsUnitTests { - - private ReactiveMongoTemplate template; - - @Mock ReactiveMongoDatabaseFactory factory; - @Mock MongoDatabase db; - @Mock MongoCollection collection; - @Mock Publisher publisher; - - private MongoExceptionTranslator exceptionTranslator = new MongoExceptionTranslator(); - private MappingMongoConverter converter; - private MongoMappingContext mappingContext; - - @BeforeEach - void setUp() { - - when(factory.getMongoDatabase()).thenReturn(Mono.just(db)); - when(factory.getExceptionTranslator()).thenReturn(exceptionTranslator); - when(db.getCollection(any(), any(Class.class))).thenReturn(collection); - when(collection.createIndex(any(), any(IndexOptions.class))).thenReturn(publisher); - - this.mappingContext = new MongoMappingContext(); - this.converter = spy(new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext)); - this.template = new ReactiveMongoTemplate(factory, converter); - } - - @Test // DATAMONGO-1854 - void ensureIndexDoesNotSetCollectionIfNoDefaultDefined() { - - indexOpsFor(Jedi.class).ensureIndex(new Index("firstname", Direction.DESC)).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(IndexOptions.class); - verify(collection).createIndex(any(), options.capture()); - - assertThat(options.getValue().getCollation()).isNull(); - } - - @Test // DATAMONGO-1854 - void ensureIndexUsesDefaultCollationIfNoneDefinedInOptions() { - - indexOpsFor(Sith.class).ensureIndex(new Index("firstname", Direction.DESC)).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(IndexOptions.class); - verify(collection).createIndex(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void ensureIndexDoesNotUseDefaultCollationIfExplicitlySpecifiedInTheIndex() { - - indexOpsFor(Sith.class).ensureIndex(new Index("firstname", Direction.DESC).collation(Collation.of("en_US"))) - .subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(IndexOptions.class); - verify(collection).createIndex(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("en_US").build()); - } - - private DefaultReactiveIndexOperations indexOpsFor(Class type) { - return new DefaultReactiveIndexOperations(template, template.getCollectionName(type), - new QueryMapper(template.getConverter()), type); - } - - @Data - static class Jedi { - @Field("firstname") String name; - } - - @org.springframework.data.mongodb.core.mapping.Document(collation = "de_AT") - static class Sith { - @Field("firstname") String name; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultScriptOperationsTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultScriptOperationsTests.java deleted file mode 100644 index e07e0616f7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultScriptOperationsTests.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2014-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.UncategorizedDataAccessException; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.script.ExecutableMongoScript; -import org.springframework.data.mongodb.core.script.NamedMongoScript; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.client.MongoClient; - -/** - * Integration tests for {@link DefaultScriptOperations}. - * - * @author Christoph Strobl - * @author Oliver Gierke - * @since 1.7 - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@EnableIfMongoServerVersion(isLessThan = "4.1.0") -@ContextConfiguration -public class DefaultScriptOperationsTests { - - static @Client MongoClient mongoClient; - - @Configuration - static class Config { - - private static final String DB_NAME = "script-tests"; - - @Bean - public MongoClient mongoClient() { - return mongoClient; - } - - @Bean - public MongoTemplate template() throws Exception { - return new MongoTemplate(mongoClient(), DB_NAME); - } - } - - static final String JAVASCRIPT_COLLECTION_NAME = "system.js"; - static final String SCRIPT_NAME = "echo"; - static final String JS_FUNCTION = "function(x) { return x; }"; - static final ExecutableMongoScript EXECUTABLE_SCRIPT = new ExecutableMongoScript(JS_FUNCTION); - static final NamedMongoScript CALLABLE_SCRIPT = new NamedMongoScript(SCRIPT_NAME, JS_FUNCTION); - - @Autowired MongoTemplate template; - DefaultScriptOperations scriptOps; - - @BeforeEach - public void setUp() { - - template.getCollection(JAVASCRIPT_COLLECTION_NAME).deleteMany(new Document()); - this.scriptOps = new DefaultScriptOperations(template); - } - - @Test // DATAMONGO-479 - public void executeShouldDirectlyRunExecutableMongoScript() { - assertThat(scriptOps.execute(EXECUTABLE_SCRIPT, 10)).isEqualTo((Object) 10D); - } - - @Test // DATAMONGO-479 - public void saveShouldStoreCallableScriptCorrectly() { - - Query query = query(where("_id").is(SCRIPT_NAME)); - assertThat(template.exists(query, JAVASCRIPT_COLLECTION_NAME)).isFalse(); - - scriptOps.register(CALLABLE_SCRIPT); - - assertThat(template.exists(query, JAVASCRIPT_COLLECTION_NAME)).isTrue(); - } - - @Test // DATAMONGO-479 - public void saveShouldStoreExecutableScriptCorrectly() { - - NamedMongoScript script = scriptOps.register(EXECUTABLE_SCRIPT); - - Query query = query(where("_id").is(script.getName())); - assertThat(template.exists(query, JAVASCRIPT_COLLECTION_NAME)).isTrue(); - } - - @Test // DATAMONGO-479 - public void executeShouldRunCallableScriptThatHasBeenSavedBefore() { - - scriptOps.register(CALLABLE_SCRIPT); - - Query query = query(where("_id").is(SCRIPT_NAME)); - assertThat(template.exists(query, JAVASCRIPT_COLLECTION_NAME)).isTrue(); - - Object result = scriptOps.call(CALLABLE_SCRIPT.getName(), 10); - - assertThat(result).isEqualTo(10D); - } - - @Test // DATAMONGO-479 - public void existsShouldReturnTrueIfScriptAvailableOnServer() { - - scriptOps.register(CALLABLE_SCRIPT); - - assertThat(scriptOps.exists(SCRIPT_NAME)).isTrue(); - } - - @Test // DATAMONGO-479 - public void existsShouldReturnFalseIfScriptNotAvailableOnServer() { - assertThat(scriptOps.exists(SCRIPT_NAME)).isFalse(); - } - - @Test // DATAMONGO-479 - public void callShouldExecuteExistingScript() { - - scriptOps.register(CALLABLE_SCRIPT); - - Object result = scriptOps.call(SCRIPT_NAME, 10); - - assertThat(result).isEqualTo((Object) 10D); - } - - @Test // DATAMONGO-479 - public void callShouldThrowExceptionWhenCallingScriptThatDoesNotExist() { - assertThatExceptionOfType(UncategorizedDataAccessException.class).isThrownBy(() -> scriptOps.call(SCRIPT_NAME, 10)); - } - - @Test // DATAMONGO-479 - public void scriptNamesShouldContainNameOfRegisteredScript() { - - scriptOps.register(CALLABLE_SCRIPT); - - assertThat(scriptOps.getScriptNames()).contains("echo"); - } - - @Test // DATAMONGO-479 - public void scriptNamesShouldReturnEmptySetWhenNoScriptRegistered() { - assertThat(scriptOps.getScriptNames()).isEmpty(); - } - - @Test // DATAMONGO-1465 - public void executeShouldNotQuoteStrings() { - assertThat(scriptOps.execute(EXECUTABLE_SCRIPT, "spring-data")).isEqualTo((Object) "spring-data"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultScriptOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultScriptOperationsUnitTests.java deleted file mode 100644 index ac6f6e79d3..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultScriptOperationsUnitTests.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2014-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.script.ExecutableMongoScript; -import org.springframework.data.mongodb.core.script.NamedMongoScript; - -/** - * Unit tests for {@link DefaultScriptOperations}. - * - * @author Christoph Strobl - * @author Oliver Gierke - * @since 1.7 - */ -@ExtendWith(MockitoExtension.class) -class DefaultScriptOperationsUnitTests { - - private DefaultScriptOperations scriptOps; - @Mock MongoOperations mongoOperations; - - @BeforeEach - void setUp() { - this.scriptOps = new DefaultScriptOperations(mongoOperations); - } - - @Test // DATAMONGO-479 - void rejectsNullExecutableMongoScript() { - assertThatIllegalArgumentException().isThrownBy(() -> scriptOps.register((ExecutableMongoScript) null)); - } - - @Test // DATAMONGO-479 - void rejectsNullNamedMongoScript() { - assertThatIllegalArgumentException().isThrownBy(() -> scriptOps.register((NamedMongoScript) null)); - } - - @Test // DATAMONGO-479 - void saveShouldUseCorrectCollectionName() { - - scriptOps.register(new NamedMongoScript("foo", "function...")); - - verify(mongoOperations, times(1)).save(any(NamedMongoScript.class), eq("system.js")); - } - - @Test // DATAMONGO-479 - void saveShouldGenerateScriptNameForExecutableMongoScripts() { - - scriptOps.register(new ExecutableMongoScript("function...")); - - ArgumentCaptor captor = ArgumentCaptor.forClass(NamedMongoScript.class); - - verify(mongoOperations, times(1)).save(captor.capture(), eq("system.js")); - assertThat(captor.getValue().getName()).isNotNull(); - } - - @Test // DATAMONGO-479 - void executeShouldThrowExceptionWhenScriptIsNull() { - assertThatIllegalArgumentException().isThrownBy(() -> scriptOps.execute(null)); - } - - @Test // DATAMONGO-479 - void existsShouldThrowExceptionWhenScriptNameIsNull() { - assertThatIllegalArgumentException().isThrownBy(() -> scriptOps.exists(null)); - } - - @Test // DATAMONGO-479 - void existsShouldThrowExceptionWhenScriptNameIsEmpty() { - assertThatIllegalArgumentException().isThrownBy(() -> scriptOps.exists("")); - } - - @Test // DATAMONGO-479 - void callShouldThrowExceptionWhenScriptNameIsNull() { - assertThatIllegalArgumentException().isThrownBy(() -> scriptOps.call(null)); - } - - @Test // DATAMONGO-479 - void callShouldThrowExceptionWhenScriptNameIsEmpty() { - assertThatIllegalArgumentException().isThrownBy(() -> scriptOps.call("")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DocumentTestUtils.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DocumentTestUtils.java deleted file mode 100644 index bc7bddacd2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DocumentTestUtils.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2012-2021 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 static org.assertj.core.api.Assertions.*; - -import java.util.Iterator; -import java.util.List; - -import org.bson.Document; - -import com.mongodb.BasicDBList; - -/** - * Helper classes to ease assertions on {@link Document}s. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -public abstract class DocumentTestUtils { - - private DocumentTestUtils() {} - - /** - * Expects the field with the given key to be not {@literal null} and a {@link Document} in turn and returns it. - * - * @param source the {@link Document} to lookup the nested one - * @param key the key of the field to lokup the nested {@link Document} - * @return - */ - public static Document getAsDocument(Document source, String key) { - return getTypedValue(source, key, Document.class); - } - - /** - * Expects the field with the given key to be not {@literal null} and a {@link BasicDBList}. - * - * @param source the {@link Document} to lookup the {@link List} in - * @param key the key of the field to find the {@link List} in - * @return - */ - public static List getAsDBList(Document source, String key) { - return getTypedValue(source, key, List.class); - } - - /** - * Expects the list element with the given index to be a non-{@literal null} {@link Document} and returns it. - * - * @param source the {@link List} to look up the {@link Document} element in - * @param index the index of the element expected to contain a {@link Document} - * @return - */ - public static Document getAsDocument(List source, int index) { - - assertThat(source.size()).isGreaterThanOrEqualTo(index + 1); - Object value = source.get(index); - assertThat(value).isInstanceOf(Document.class); - return (Document) value; - } - - @SuppressWarnings("unchecked") - public static T getTypedValue(Document source, String key, Class type) { - - Object value = source.get(key); - assertThat(value).isNotNull(); - assertThat(value).isInstanceOf(type); - - return (T) value; - } - - public static void assertTypeHint(Document document, Class type) { - assertTypeHint(document, type.getName()); - } - - public static void assertTypeHint(Document document, String expectedTypeString) { - - Iterator keyIterator = document.keySet().iterator(); - while (keyIterator.hasNext()) { - String key = keyIterator.next(); - if (key.equals("_class")) { - assertThat(document.get(key)).isEqualTo(expectedTypeString); - assertThat(keyIterator.hasNext()).isFalse(); - return; - } - } - - fail(String.format("Expected to find type info %s in %s.", document, expectedTypeString)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/EntityOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/EntityOperationUnitTests.java deleted file mode 100644 index d24a8e8027..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/EntityOperationUnitTests.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -/** - * @author Christoph Strobl - */ -public class EntityOperationUnitTests { - - EntityOperations ops; - MongoMappingContext mappingContext = new MongoMappingContext(); - ConversionService conversionService = new DefaultConversionService(); - - @BeforeEach - public void setUp() { - ops = new EntityOperations(mappingContext); - } - - @Test // DATAMONGO-2293 - public void populateIdShouldReturnTargetBeanWhenIdIsNull() { - assertThat(initAdaptibleEntity(new DomainTypeWithIdProperty()).populateIdIfNecessary(null)).isNotNull(); - } - - AdaptibleEntity initAdaptibleEntity(T source) { - return ops.forEntity(source, conversionService); - } - - private static class DomainTypeWithIdProperty { - - @Id String id; - String value; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupportUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupportUnitTests.java deleted file mode 100644 index 8b35793944..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupportUnitTests.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.aggregation.Aggregation; - -/** - * Unit tests for {@link ExecutableAggregationOperationSupport}. - * - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -public class ExecutableAggregationOperationSupportUnitTests { - - @Mock MongoTemplate template; - private ExecutableAggregationOperationSupport opSupport; - - @BeforeEach - void setUp() { - opSupport = new ExecutableAggregationOperationSupport(template); - } - - @Test // DATAMONGO-1563 - void throwsExceptionOnNullDomainType() { - assertThatIllegalArgumentException().isThrownBy(() -> opSupport.aggregateAndReturn(null)); - } - - @Test // DATAMONGO-1563 - void throwsExceptionOnNullCollectionWhenUsed() { - assertThatIllegalArgumentException() - .isThrownBy(() -> opSupport.aggregateAndReturn(Person.class).inCollection(null)); - } - - @Test // DATAMONGO-1563 - void throwsExceptionOnEmptyCollectionWhenUsed() { - assertThatIllegalArgumentException().isThrownBy(() -> opSupport.aggregateAndReturn(Person.class).inCollection("")); - } - - @Test // DATAMONGO-1563 - void throwsExceptionOnNullAggregation() { - assertThatIllegalArgumentException().isThrownBy(() -> opSupport.aggregateAndReturn(Person.class).by(null)); - } - - @Test // DATAMONGO-1563 - void aggregateWithUntypedAggregationAndExplicitCollection() { - - opSupport.aggregateAndReturn(Person.class).inCollection("star-wars").by(newAggregation(project("foo"))).all(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - verify(template).aggregate(any(Aggregation.class), eq("star-wars"), captor.capture()); - assertThat(captor.getValue()).isEqualTo(Person.class); - } - - @Test // DATAMONGO-1563 - void aggregateWithUntypedAggregation() { - - when(template.getCollectionName(any(Class.class))).thenReturn("person"); - - opSupport.aggregateAndReturn(Person.class).by(newAggregation(project("foo"))).all(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(captor.capture()); - verify(template).aggregate(any(Aggregation.class), eq("person"), captor.capture()); - - assertThat(captor.getAllValues()).containsExactly(Person.class, Person.class); - } - - @Test // DATAMONGO-1563 - void aggregateWithTypeAggregation() { - - when(template.getCollectionName(any(Class.class))).thenReturn("person"); - - opSupport.aggregateAndReturn(Jedi.class).by(newAggregation(Person.class, project("foo"))).all(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(captor.capture()); - verify(template).aggregate(any(Aggregation.class), eq("person"), captor.capture()); - - assertThat(captor.getAllValues()).containsExactly(Person.class, Jedi.class); - } - - @Test // DATAMONGO-1563 - void aggregateStreamWithUntypedAggregationAndExplicitCollection() { - - opSupport.aggregateAndReturn(Person.class).inCollection("star-wars").by(newAggregation(project("foo"))).stream(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - verify(template).aggregateStream(any(Aggregation.class), eq("star-wars"), captor.capture()); - assertThat(captor.getValue()).isEqualTo(Person.class); - } - - @Test // DATAMONGO-1563 - void aggregateStreamWithUntypedAggregation() { - - when(template.getCollectionName(any(Class.class))).thenReturn("person"); - - opSupport.aggregateAndReturn(Person.class).by(newAggregation(project("foo"))).stream(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(captor.capture()); - verify(template).aggregateStream(any(Aggregation.class), eq("person"), captor.capture()); - - assertThat(captor.getAllValues()).containsExactly(Person.class, Person.class); - } - - @Test // DATAMONGO-1563 - void aggregateStreamWithTypeAggregation() { - - when(template.getCollectionName(any(Class.class))).thenReturn("person"); - - opSupport.aggregateAndReturn(Jedi.class).by(newAggregation(Person.class, project("foo"))).stream(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(captor.capture()); - verify(template).aggregateStream(any(Aggregation.class), eq("person"), captor.capture()); - - assertThat(captor.getAllValues()).containsExactly(Person.class, Jedi.class); - } - - static class Person {} - - static class Jedi {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java deleted file mode 100644 index 8ebf72e130..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java +++ /dev/null @@ -1,640 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.Date; -import java.util.stream.Stream; - -import org.bson.BsonString; -import org.bson.BsonValue; -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.annotation.Id; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -/** - * Integration tests for {@link ExecutableFindOperationSupport}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -class ExecutableFindOperationSupportTests { - - private static final String STAR_WARS = "star-wars"; - private static final String STAR_WARS_PLANETS = "star-wars-universe"; - - @Template(database = "executable-find-operation-support-tests", initialEntitySet = { Person.class, Planet.class }) // - private static MongoTestTemplate template; - - private Person han; - private Person luke; - - private Planet alderan; - private Planet dantooine; - - @BeforeEach - void setUp() { - - template.flush(); - - template.indexOps(Planet.class).ensureIndex( - new GeospatialIndex("coordinates").typed(GeoSpatialIndexType.GEO_2DSPHERE).named("planet-coordinate-idx")); - - initPersons(); - initPlanets(); - } - - @Test // DATAMONGO-1563 - void domainTypeIsRequired() { - assertThatIllegalArgumentException().isThrownBy(() -> template.query(null)); - } - - @Test // DATAMONGO-1563 - void returnTypeIsRequiredOnSet() { - assertThatIllegalArgumentException().isThrownBy(() -> template.query(Person.class).as(null)); - } - - @Test // DATAMONGO-1563 - void collectionIsRequiredOnSet() { - assertThatIllegalArgumentException().isThrownBy(() -> template.query(Person.class).inCollection(null)); - } - - @Test // DATAMONGO-1563 - void findAll() { - assertThat(template.query(Person.class).all()).containsExactlyInAnyOrder(han, luke); - } - - @Test // DATAMONGO-1563 - void findAllWithCollection() { - assertThat(template.query(Human.class).inCollection(STAR_WARS).all()).hasSize(2); - } - - @Test // DATAMONGO-1563 - void findAllWithProjection() { - assertThat(template.query(Person.class).as(Jedi.class).all()).hasOnlyElementsOfType(Jedi.class).hasSize(2); - } - - @Test // DATAMONGO-2041 - void findAllWithProjectionOnEmbeddedType() { - - luke.father = new Person(); - luke.father.firstname = "anakin"; - - template.save(luke); - - assertThat(template.query(Person.class).as(PersonDtoProjection.class).matching(query(where("id").is(luke.id))) - .firstValue()).hasFieldOrPropertyWithValue("father", luke.father); - } - - @Test // DATAMONGO-1733 - void findByReturningAllValuesAsClosedInterfaceProjection() { - - assertThat(template.query(Person.class).as(PersonProjection.class).all()) - .hasOnlyElementsOfTypes(PersonProjection.class); - } - - @Test // DATAMONGO-1563 - void findAllBy() { - - assertThat(template.query(Person.class).matching(query(where("firstname").is("luke"))).all()) - .containsExactlyInAnyOrder(luke); - } - - @Test // DATAMONGO-1563 - void findAllByWithCollectionUsingMappingInformation() { - - assertThat(template.query(Jedi.class).inCollection(STAR_WARS).matching(query(where("name").is("luke"))).all()) - .hasSize(1).hasOnlyElementsOfType(Jedi.class); - } - - @Test // DATAMONGO-1563 - void findAllByWithCollection() { - assertThat(template.query(Human.class).inCollection(STAR_WARS).matching(query(where("firstname").is("luke"))).all()) - .hasSize(1); - } - - @Test // DATAMONGO-2323 - void findAllAsDocument() { - assertThat( - template.query(Document.class).inCollection(STAR_WARS).matching(query(where("firstname").is("luke"))).all()) - .hasSize(1); - } - - @Test // DATAMONGO-1563 - void findAllByWithProjection() { - - assertThat(template.query(Person.class).as(Jedi.class).matching(query(where("firstname").is("luke"))).all()) - .hasOnlyElementsOfType(Jedi.class).hasSize(1); - } - - @Test // DATAMONGO-1563 - void findBy() { - assertThat(template.query(Person.class).matching(query(where("firstname").is("luke"))).one()).contains(luke); - } - - @Test // DATAMONGO-2416 - void findByCriteria() { - assertThat(template.query(Person.class).matching(where("firstname").is("luke")).one()).contains(luke); - } - - @Test // DATAMONGO-1563 - void findByNoMatch() { - assertThat(template.query(Person.class).matching(query(where("firstname").is("spock"))).one()).isEmpty(); - } - - @Test // DATAMONGO-1563 - void findByTooManyResults() { - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) - .isThrownBy(() -> template.query(Person.class).matching(query(where("firstname").in("han", "luke"))).one()); - } - - @Test // DATAMONGO-1726 - void findByReturningOneValue() { - assertThat(template.query(Person.class).matching(query(where("firstname").is("luke"))).oneValue()).isEqualTo(luke); - } - - @Test // DATAMONGO-1726 - void findByReturningOneValueButTooManyResults() { - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class).isThrownBy( - () -> template.query(Person.class).matching(query(where("firstname").in("han", "luke"))).oneValue()); - } - - @Test // DATAMONGO-1726 - void findByReturningFirstValue() { - - assertThat(template.query(Person.class).matching(query(where("firstname").is("luke"))).firstValue()) - .isEqualTo(luke); - } - - @Test // DATAMONGO-1726 - void findByReturningFirstValueForManyResults() { - - assertThat(template.query(Person.class).matching(query(where("firstname").in("han", "luke"))).firstValue()) - .isIn(han, luke); - } - - @Test // DATAMONGO-1733 - void findByReturningFirstValueAsClosedInterfaceProjection() { - - PersonProjection result = template.query(Person.class).as(PersonProjection.class) - .matching(query(where("firstname").is("han"))).firstValue(); - - assertThat(result).isInstanceOf(PersonProjection.class); - assertThat(result.getFirstname()).isEqualTo("han"); - } - - @Test // DATAMONGO-1733 - void findByReturningFirstValueAsOpenInterfaceProjection() { - - PersonSpELProjection result = template.query(Person.class).as(PersonSpELProjection.class) - .matching(query(where("firstname").is("han"))).firstValue(); - - assertThat(result).isInstanceOf(PersonSpELProjection.class); - assertThat(result.getName()).isEqualTo("han"); - } - - @Test // DATAMONGO-1563 - void streamAll() { - - try (Stream stream = template.query(Person.class).stream()) { - assertThat(stream).containsExactlyInAnyOrder(han, luke); - } - } - - @Test // DATAMONGO-1563 - void streamAllWithCollection() { - - try (Stream stream = template.query(Human.class).inCollection(STAR_WARS).stream()) { - assertThat(stream).hasSize(2); - } - } - - @Test // DATAMONGO-1563 - void streamAllWithProjection() { - - try (Stream stream = template.query(Person.class).as(Jedi.class).stream()) { - assertThat(stream).hasOnlyElementsOfType(Jedi.class).hasSize(2); - } - } - - @Test // DATAMONGO-1733 - void streamAllReturningResultsAsClosedInterfaceProjection() { - - TerminatingFind operation = template.query(Person.class).as(PersonProjection.class); - - assertThat(operation.stream()) // - .hasSize(2) // - .allSatisfy(it -> { - assertThat(it).isInstanceOf(PersonProjection.class); - assertThat(it.getFirstname()).isNotBlank(); - }); - } - - @Test // DATAMONGO-1733 - void streamAllReturningResultsAsOpenInterfaceProjection() { - - TerminatingFind operation = template.query(Person.class).as(PersonSpELProjection.class); - - assertThat(operation.stream()) // - .hasSize(2) // - .allSatisfy(it -> { - assertThat(it).isInstanceOf(PersonSpELProjection.class); - assertThat(it.getName()).isNotBlank(); - }); - } - - @Test // DATAMONGO-1563 - void streamAllBy() { - - try (Stream stream = template.query(Person.class).matching(query(where("firstname").is("luke"))).stream()) { - assertThat(stream).containsExactlyInAnyOrder(luke); - } - } - - @Test // DATAMONGO-1563 - void findAllNearBy() { - - GeoResults results = template.query(Planet.class).near(NearQuery.near(-73.9667, 40.78).spherical(true)) - .all(); - assertThat(results.getContent()).hasSize(2); - assertThat(results.getContent().get(0).getDistance()).isNotNull(); - } - - @Test // DATAMONGO-1563 - void findAllNearByWithCollectionAndProjection() { - - GeoResults results = template.query(Object.class).inCollection(STAR_WARS_PLANETS).as(Human.class) - .near(NearQuery.near(-73.9667, 40.78).spherical(true)).all(); - - assertThat(results.getContent()).hasSize(2); - assertThat(results.getContent().get(0).getDistance()).isNotNull(); - assertThat(results.getContent().get(0).getContent()).isInstanceOf(Human.class); - assertThat(results.getContent().get(0).getContent().getId()).isEqualTo("alderan"); - } - - @Test // DATAMONGO-1733 - void findAllNearByReturningGeoResultContentAsClosedInterfaceProjection() { - - GeoResults results = template.query(Planet.class).as(PlanetProjection.class) - .near(NearQuery.near(-73.9667, 40.78).spherical(true)).all(); - - assertThat(results.getContent()).allSatisfy(it -> { - - assertThat(it.getContent()).isInstanceOf(PlanetProjection.class); - assertThat(it.getContent().getName()).isNotBlank(); - }); - } - - @Test // DATAMONGO-1733 - void findAllNearByReturningGeoResultContentAsOpenInterfaceProjection() { - - GeoResults results = template.query(Planet.class).as(PlanetSpELProjection.class) - .near(NearQuery.near(-73.9667, 40.78).spherical(true)).all(); - - assertThat(results.getContent()).allSatisfy(it -> { - - assertThat(it.getContent()).isInstanceOf(PlanetSpELProjection.class); - assertThat(it.getContent().getId()).isNotBlank(); - }); - } - - @Test // DATAMONGO-1728 - void firstShouldReturnFirstEntryInCollection() { - assertThat(template.query(Person.class).first()).isNotEmpty(); - } - - @Test // DATAMONGO-1734 - void countShouldReturnNrOfElementsInCollectionWhenNoQueryPresent() { - assertThat(template.query(Person.class).count()).isEqualTo(2); - } - - @Test // DATAMONGO-1734 - void countShouldReturnNrOfElementsMatchingQuery() { - - assertThat(template.query(Person.class).matching(query(where("firstname").is(luke.getFirstname()))).count()) - .isEqualTo(1); - } - - @Test // DATAMONGO-1734 - void existsShouldReturnTrueIfAtLeastOneElementExistsInCollection() { - assertThat(template.query(Person.class).exists()).isTrue(); - } - - @Test // DATAMONGO-1734 - void existsShouldReturnFalseIfNoElementExistsInCollection() { - - template.remove(new BasicQuery("{}"), STAR_WARS); - - assertThat(template.query(Person.class).exists()).isFalse(); - } - - @Test // DATAMONGO-1734 - void existsShouldReturnTrueIfAtLeastOneElementMatchesQuery() { - - assertThat(template.query(Person.class).matching(query(where("firstname").is(luke.getFirstname()))).exists()) - .isTrue(); - } - - @Test // DATAMONGO-1734 - void existsShouldReturnFalseWhenNoElementMatchesQuery() { - assertThat(template.query(Person.class).matching(query(where("firstname").is("spock"))).exists()).isFalse(); - } - - @Test // DATAMONGO-1734 - void returnsTargetObjectDirectlyIfProjectionInterfaceIsImplemented() { - assertThat(template.query(Person.class).as(Contact.class).all()).allMatch(it -> it instanceof Person); - } - - @Test // DATAMONGO-1761 - void distinctReturnsEmptyListIfNoMatchFound() { - assertThat(template.query(Person.class).distinct("actually-not-property-in-use").as(String.class).all()).isEmpty(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsSimpleFieldValuesCorrectlyForCollectionHavingReturnTypeSpecifiedThatCanBeConvertedDirectlyByACodec() { - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.lastname = luke.lastname; - - template.save(anakin); - - assertThat(template.query(Person.class).distinct("lastname").as(String.class).all()) - .containsExactlyInAnyOrder("solo", "skywalker"); - } - - @Test // DATAMONGO-1761 - void distinctReturnsSimpleFieldValuesCorrectly() { - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.ability = "dark-lord"; - - Person padme = new Person(); - padme.firstname = "padme"; - padme.ability = 42L; - - Person jaja = new Person(); - jaja.firstname = "jaja"; - jaja.ability = new Date(); - - template.save(anakin); - template.save(padme); - template.save(jaja); - - assertThat(template.query(Person.class).distinct("ability").all()).containsExactlyInAnyOrder(anakin.ability, - padme.ability, jaja.ability); - } - - @Test // DATAMONGO-1761 - void distinctReturnsComplexValuesCorrectly() { - - Sith sith = new Sith(); - sith.rank = "lord"; - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.ability = sith; - - template.save(anakin); - - assertThat(template.query(Person.class).distinct("ability").all()).containsExactlyInAnyOrder(anakin.ability); - } - - @Test // DATAMONGO-1761 - void distinctReturnsComplexValuesCorrectlyHavingReturnTypeSpecified() { - - Sith sith = new Sith(); - sith.rank = "lord"; - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.ability = sith; - - template.save(anakin); - - assertThat(template.query(Person.class).distinct("ability").as(Sith.class).all()) - .containsExactlyInAnyOrder((Sith) anakin.ability); - } - - @Test // DATAMONGO-1761 - void distinctReturnsComplexValuesCorrectlyHavingReturnTypeDocumentSpecified() { - - Sith sith = new Sith(); - sith.rank = "lord"; - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.ability = sith; - - template.save(anakin); - - assertThat(template.query(Person.class).distinct("ability").as(Document.class).all()) - .containsExactlyInAnyOrder(new Document("rank", "lord").append("_class", Sith.class.getName())); - } - - @Test // DATAMONGO-1761 - void distinctMapsFieldNameCorrectly() { - - assertThat(template.query(Jedi.class).inCollection(STAR_WARS).distinct("name").as(String.class).all()) - .containsExactlyInAnyOrder("han", "luke"); - } - - @Test // DATAMONGO-1761 - void distinctReturnsRawValuesIfReturnTypeIsBsonValue() { - - assertThat(template.query(Person.class).distinct("lastname").as(BsonValue.class).all()) - .containsExactlyInAnyOrder(new BsonString("solo"), new BsonString("skywalker")); - } - - @Test // DATAMONGO-1761 - void distinctReturnsValuesMappedToTheirJavaTypeEvenWhenNotExplicitlyDefinedByTheDomainType() { - - template.save(new Document("darth", "vader"), STAR_WARS); - - assertThat(template.query(Person.class).distinct("darth").all()).containsExactlyInAnyOrder("vader"); - } - - @Test // DATAMONGO-1761 - void distinctReturnsMappedDomainTypeForProjections() { - - luke.father = new Person(); - luke.father.firstname = "anakin"; - - template.save(luke); - - assertThat(template.query(Person.class).distinct("father").as(Jedi.class).all()) - .containsExactlyInAnyOrder(new Jedi("anakin")); - } - - @Test // DATAMONGO-1761 - void distinctAlllowsQueryUsingObjectSourceType() { - - luke.father = new Person(); - luke.father.firstname = "anakin"; - - template.save(luke); - - assertThat(template.query(Object.class).inCollection(STAR_WARS).distinct("father").as(Jedi.class).all()) - .containsExactlyInAnyOrder(new Jedi("anakin")); - } - - @Test // DATAMONGO-1761 - void distinctReturnsMappedDomainTypeExtractedFromPropertyWhenNoExplicitTypePresent() { - - luke.father = new Person(); - luke.father.firstname = "anakin"; - - template.save(luke); - - Person expected = new Person(); - expected.firstname = luke.father.firstname; - - assertThat(template.query(Person.class).distinct("father").all()).containsExactlyInAnyOrder(expected); - } - - @Test // DATAMONGO-1761 - void distinctThrowsExceptionWhenExplicitMappingTypeCannotBeApplied() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) - .isThrownBy(() -> template.query(Person.class).distinct("firstname").as(Long.class).all()); - } - - @Test // DATAMONGO-2507 - void distinctAppliesFilterQuery() { - - assertThat(template.query(Person.class).inCollection(STAR_WARS).distinct("firstname") // - .matching(where("lastname").is(luke.lastname)) // - .as(String.class) // - .all() // - ).containsExactlyInAnyOrder("luke"); - } - - interface Contact {} - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person implements Contact { - - @Id String id; - String firstname; - String lastname; - Object ability; - Person father; - } - - interface PersonProjection { - String getFirstname(); - } - - public interface PersonSpELProjection { - - @Value("#{target.firstname}") - String getName(); - } - - static class PersonDtoProjection { - - @Field("firstname") String name; - Person father; - } - - @Data - static class Human { - @Id String id; - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - static class Jedi { - - @Field("firstname") String name; - } - - @Data - static class Sith { - - String rank; - } - - @Data - @AllArgsConstructor - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS_PLANETS) - static class Planet { - - @Id String name; - Point coordinates; - } - - interface PlanetProjection { - String getName(); - } - - interface PlanetSpELProjection { - - @Value("#{target.name}") - String getId(); - } - - private void initPersons() { - - han = new Person(); - han.firstname = "han"; - han.lastname = "solo"; - han.id = "id-1"; - - luke = new Person(); - luke.firstname = "luke"; - luke.lastname = "skywalker"; - luke.id = "id-2"; - - template.save(han); - template.save(luke); - } - - private void initPlanets() { - - alderan = new Planet("alderan", new Point(-73.9836, 40.7538)); - dantooine = new Planet("dantooine", new Point(-73.9928, 40.7193)); - - template.save(alderan); - template.save(dantooine); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupportUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupportUnitTests.java deleted file mode 100644 index dfb4af90a4..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupportUnitTests.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import lombok.Data; - -import java.util.Arrays; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.BulkOperations.BulkMode; - -/** - * Unit tests for {@link ExecutableInsertOperationSupport}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class ExecutableInsertOperationSupportUnitTests { - - private static final String STAR_WARS = "star-wars"; - - @Mock MongoTemplate template; - @Mock BulkOperations bulkOperations; - - private ExecutableInsertOperationSupport ops; - - private Person luke, han; - - @BeforeEach - void setUp() { - - ops = new ExecutableInsertOperationSupport(template); - - luke = new Person(); - luke.id = "id-1"; - luke.firstname = "luke"; - - han = new Person(); - han.firstname = "han"; - han.id = "id-2"; - } - - @Test // DATAMONGO-1563 - void nullCollectionShouldThrowException() { - assertThatIllegalArgumentException().isThrownBy(() -> ops.insert(Person.class).inCollection(null)); - } - - @Test // DATAMONGO-1563 - void nullBulkModeShouldThrowException() { - assertThatIllegalArgumentException().isThrownBy(() -> ops.insert(Person.class).withBulkMode(null)); - } - - @Test // DATAMONGO-1563 - void insertShouldUseDerivedCollectionName() { - - when(template.getCollectionName(any(Class.class))).thenReturn(STAR_WARS); - - ops.insert(Person.class).one(luke); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(captor.capture()); - verify(template).insert(eq(luke), eq(STAR_WARS)); - - assertThat(captor.getAllValues()).containsExactly(Person.class); - } - - @Test // DATAMONGO-1563 - void insertShouldUseExplicitCollectionName() { - - ops.insert(Person.class).inCollection(STAR_WARS).one(luke); - - verify(template, never()).getCollectionName(any(Class.class)); - verify(template).insert(eq(luke), eq(STAR_WARS)); - } - - @Test // DATAMONGO-1563 - void insertCollectionShouldDelegateCorrectly() { - - when(template.getCollectionName(any(Class.class))).thenReturn(STAR_WARS); - - ops.insert(Person.class).all(Arrays.asList(luke, han)); - - verify(template).getCollectionName(any(Class.class)); - verify(template).insert(anyList(), eq(STAR_WARS)); - } - - @Test // DATAMONGO-1563 - void bulkInsertCollectionShouldDelegateCorrectly() { - - when(template.getCollectionName(any(Class.class))).thenReturn(STAR_WARS); - when(template.bulkOps(any(), any(), any())).thenReturn(bulkOperations); - when(bulkOperations.insert(anyList())).thenReturn(bulkOperations); - - ops.insert(Person.class).bulk(Arrays.asList(luke, han)); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(any(Class.class)); - verify(template).bulkOps(eq(BulkMode.ORDERED), captor.capture(), eq(STAR_WARS)); - verify(bulkOperations).insert(anyList()); - verify(bulkOperations).execute(); - } - - @Test // DATAMONGO-1563 - void bulkInsertWithBulkModeShouldDelegateCorrectly() { - - when(template.getCollectionName(any(Class.class))).thenReturn(STAR_WARS); - when(template.bulkOps(any(), any(), any())).thenReturn(bulkOperations); - when(bulkOperations.insert(anyList())).thenReturn(bulkOperations); - - ops.insert(Person.class).withBulkMode(BulkMode.UNORDERED).bulk(Arrays.asList(luke, han)); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(any(Class.class)); - verify(template).bulkOps(eq(BulkMode.UNORDERED), captor.capture(), eq(STAR_WARS)); - verify(bulkOperations).insert(anyList()); - verify(bulkOperations).execute(); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person { - @Id String id; - String firstname; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupportUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupportUnitTests.java deleted file mode 100644 index d0aa83f445..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupportUnitTests.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Query; - -/** - * Unit tests for {@link ExecutableMapReduceOperationSupport}. - * - * @author Christoph Strobl - * @author Mark Paluch - * @currentRead Beyond the Shadows - Brent Weeks - */ -@ExtendWith(MockitoExtension.class) -class ExecutableMapReduceOperationSupportUnitTests { - - private static final String STAR_WARS = "star-wars"; - private static final String MAP_FUNCTION = "function() { emit(this.id, this.firstname) }"; - private static final String REDUCE_FUNCTION = "function(id, name) { return sum(id, name); }"; - - @Mock MongoTemplate template; - - private ExecutableMapReduceOperationSupport mapReduceOpsSupport; - - @BeforeEach - void setUp() { - mapReduceOpsSupport = new ExecutableMapReduceOperationSupport(template); - } - - @Test // DATAMONGO-1929 - void throwsExceptionOnNullTemplate() { - assertThatIllegalArgumentException().isThrownBy(() -> new ExecutableMapReduceOperationSupport(null)); - } - - @Test // DATAMONGO-1929 - void throwsExceptionOnNullDomainType() { - assertThatIllegalArgumentException().isThrownBy(() -> mapReduceOpsSupport.mapReduce(null)); - } - - @Test // DATAMONGO-1929 - void usesExtractedCollectionName() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).all(); - - verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), - isNull(), eq(Person.class)); - } - - @Test // DATAMONGO-1929 - void usesExplicitCollectionName() { - - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION) - .inCollection("the-night-angel").all(); - - verify(template).mapReduce(any(Query.class), eq(Person.class), eq("the-night-angel"), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), isNull(), eq(Person.class)); - } - - @Test // DATAMONGO-1929 - void usesMapReduceOptionsWhenPresent() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - MapReduceOptions options = MapReduceOptions.options(); - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).with(options).all(); - - verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), - eq(options), eq(Person.class)); - } - - @Test // DATAMONGO-1929 - void usesQueryWhenPresent() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - Query query = new BasicQuery("{ 'lastname' : 'skywalker' }"); - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).matching(query).all(); - - verify(template).mapReduce(eq(query), eq(Person.class), eq(STAR_WARS), eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), - isNull(), eq(Person.class)); - } - - @Test // DATAMONGO-2416 - void usesCriteriaWhenPresent() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - Query query = Query.query(where("lastname").is("skywalker")); - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION) - .matching(where("lastname").is("skywalker")).all(); - - verify(template).mapReduce(eq(query), eq(Person.class), eq(STAR_WARS), eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), - isNull(), eq(Person.class)); - } - - @Test // DATAMONGO-1929 - void usesProjectionWhenPresent() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).as(Jedi.class).all(); - - verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), - isNull(), eq(Jedi.class)); - } - - interface Contact {} - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person implements Contact { - - @Id String id; - String firstname; - String lastname; - Object ability; - Person father; - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - static class Jedi { - - @Field("firstname") String name; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupportTests.java deleted file mode 100644 index aade6330b2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupportTests.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; - -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -import com.mongodb.client.result.DeleteResult; - -/** - * Integration tests for {@link ExecutableRemoveOperationSupport}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -class ExecutableRemoveOperationSupportTests { - - private static final String STAR_WARS = "star-wars"; - - @Template(initialEntitySet = Person.class) // - private static MongoTestTemplate template; - - private Person han; - private Person luke; - - @BeforeEach - void setUp() { - - template.flush(); - - han = new Person(); - han.firstname = "han"; - han.id = "id-1"; - - luke = new Person(); - luke.firstname = "luke"; - luke.id = "id-2"; - - template.save(han); - template.save(luke); - } - - @Test // DATAMONGO-1563 - void removeAll() { - - DeleteResult result = template.remove(Person.class).all(); - - assertThat(result.getDeletedCount()).isEqualTo(2L); - } - - @Test // DATAMONGO-1563 - void removeAllMatching() { - - DeleteResult result = template.remove(Person.class).matching(query(where("firstname").is("han"))).all(); - - assertThat(result.getDeletedCount()).isEqualTo(1L); - } - - @Test // DATAMONGO-2416 - void removeAllMatchingCriteria() { - - DeleteResult result = template.remove(Person.class).matching(where("firstname").is("han")).all(); - - assertThat(result.getDeletedCount()).isEqualTo(1L); - } - - @Test // DATAMONGO-1563 - void removeAllMatchingWithAlternateDomainTypeAndCollection() { - - DeleteResult result = template.remove(Jedi.class).inCollection(STAR_WARS).matching(query(where("name").is("luke"))) - .all(); - - assertThat(result.getDeletedCount()).isEqualTo(1L); - } - - @Test // DATAMONGO-1563 - void removeAndReturnAllMatching() { - - List result = template.remove(Person.class).matching(query(where("firstname").is("han"))).findAndRemove(); - - assertThat(result).containsExactly(han); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person { - @Id String id; - String firstname; - } - - @Data - static class Jedi { - - @Field("firstname") String name; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupportTests.java deleted file mode 100644 index d103b73614..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupportTests.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; - -import java.util.Optional; - -import org.bson.BsonString; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -import com.mongodb.client.result.UpdateResult; - -/** - * Integration tests for {@link ExecutableUpdateOperationSupport}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -class ExecutableUpdateOperationSupportTests { - - private static final String STAR_WARS = "star-wars"; - - @Template(initialEntitySet = { Human.class, Jedi.class, Person.class }) // - private static MongoTestTemplate template; - - private Person han; - private Person luke; - - @BeforeEach - void setUp() { - - template.flush(); - - han = new Person(); - han.firstname = "han"; - han.id = "id-1"; - - luke = new Person(); - luke.firstname = "luke"; - luke.id = "id-2"; - - template.save(han); - template.save(luke); - } - - @Test // DATAMONGO-1563 - void domainTypeIsRequired() { - assertThatIllegalArgumentException().isThrownBy(() -> template.update(null)); - } - - @Test // DATAMONGO-1563 - void updateIsRequired() { - assertThatIllegalArgumentException().isThrownBy(() -> template.update(Person.class).apply(null)); - } - - @Test // DATAMONGO-1563 - void collectionIsRequiredOnSet() { - assertThatIllegalArgumentException().isThrownBy(() -> template.update(Person.class).inCollection(null)); - } - - @Test // DATAMONGO-1563 - void findAndModifyOptionsAreRequiredOnSet() { - assertThatIllegalArgumentException() - .isThrownBy(() -> template.update(Person.class).apply(new Update()).withOptions(null)); - } - - @Test // DATAMONGO-1563 - void updateFirst() { - - UpdateResult result = template.update(Person.class).apply(new Update().set("firstname", "Han")).first(); - - assertThat(result.getModifiedCount()).isEqualTo(1L); - assertThat(result.getUpsertedId()).isNull(); - } - - @Test // DATAMONGO-1563 - void updateAll() { - - UpdateResult result = template.update(Person.class).apply(new Update().set("firstname", "Han")).all(); - - assertThat(result.getModifiedCount()).isEqualTo(2L); - assertThat(result.getUpsertedId()).isNull(); - } - - @Test // DATAMONGO-1563 - void updateAllMatching() { - - UpdateResult result = template.update(Person.class).matching(queryHan()).apply(new Update().set("firstname", "Han")) - .all(); - - assertThat(result.getModifiedCount()).isEqualTo(1L); - assertThat(result.getUpsertedId()).isNull(); - } - - @Test // DATAMONGO-2416 - void updateAllMatchingCriteria() { - - UpdateResult result = template.update(Person.class).matching(where("id").is(han.getId())) - .apply(new Update().set("firstname", "Han")) - .all(); - - assertThat(result.getModifiedCount()).isEqualTo(1L); - assertThat(result.getUpsertedId()).isNull(); - } - - @Test // DATAMONGO-1563 - void updateWithDifferentDomainClassAndCollection() { - - UpdateResult result = template.update(Jedi.class).inCollection(STAR_WARS) - .matching(query(where("_id").is(han.getId()))).apply(new Update().set("name", "Han")).all(); - - assertThat(result.getModifiedCount()).isEqualTo(1L); - assertThat(result.getUpsertedId()).isNull(); - assertThat(template.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Han"); - } - - @Test // DATAMONGO-1719 - void findAndModifyValue() { - - Person result = template.update(Person.class).matching(queryHan()).apply(new Update().set("firstname", "Han")) - .findAndModifyValue(); - - assertThat(result).isEqualTo(han); - assertThat(template.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Han"); - } - - @Test // DATAMONGO-1563 - void findAndModify() { - - Optional result = template.update(Person.class).matching(queryHan()) - .apply(new Update().set("firstname", "Han")).findAndModify(); - - assertThat(result).contains(han); - assertThat(template.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Han"); - } - - @Test // DATAMONGO-1563 - void findAndModifyWithDifferentDomainTypeAndCollection() { - - Optional result = template.update(Jedi.class).inCollection(STAR_WARS) - .matching(query(where("_id").is(han.getId()))).apply(new Update().set("name", "Han")).findAndModify(); - - assertThat(result.get()).hasFieldOrPropertyWithValue("name", "han"); - assertThat(template.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Han"); - } - - @Test // DATAMONGO-1563 - void findAndModifyWithOptions() { - - Optional result = template.update(Person.class).matching(queryHan()) - .apply(new Update().set("firstname", "Han")).withOptions(FindAndModifyOptions.options().returnNew(true)) - .findAndModify(); - - assertThat(result.get()).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", "Han"); - } - - @Test // DATAMONGO-1563 - void upsert() { - - UpdateResult result = template.update(Person.class).matching(query(where("id").is("id-3"))) - .apply(new Update().set("firstname", "Chewbacca")).upsert(); - - assertThat(result.getModifiedCount()).isEqualTo(0L); - assertThat(result.getUpsertedId()).isEqualTo(new BsonString("id-3")); - } - - @Test // DATAMONGO-1827 - void findAndReplaceValue() { - - Person luke = new Person(); - luke.firstname = "Luke"; - - Person result = template.update(Person.class).matching(queryHan()).replaceWith(luke).findAndReplaceValue(); - - assertThat(result).isEqualTo(han); - assertThat(template.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Luke"); - } - - @Test // DATAMONGO-1827 - void findAndReplace() { - - Person luke = new Person(); - luke.firstname = "Luke"; - - Optional result = template.update(Person.class).matching(queryHan()).replaceWith(luke).findAndReplace(); - - assertThat(result).contains(han); - assertThat(template.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Luke"); - } - - @Test // DATAMONGO-1827 - void findAndReplaceWithCollection() { - - Person luke = new Person(); - luke.firstname = "Luke"; - - Optional result = template.update(Person.class).inCollection(STAR_WARS).matching(queryHan()) - .replaceWith(luke).findAndReplace(); - - assertThat(result).contains(han); - assertThat(template.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Luke"); - } - - @Test // DATAMONGO-1827 - void findAndReplaceWithOptions() { - - Person luke = new Person(); - luke.firstname = "Luke"; - - Person result = template.update(Person.class).matching(queryHan()).replaceWith(luke) - .withOptions(FindAndReplaceOptions.options().returnNew()).findAndReplaceValue(); - - assertThat(result).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", "Luke"); - } - - @Test // DATAMONGO-1827 - void findAndReplaceWithProjection() { - - Person luke = new Person(); - luke.firstname = "Luke"; - - Jedi result = template.update(Person.class).matching(queryHan()).replaceWith(luke).as(Jedi.class) - .findAndReplaceValue(); - - assertThat(result.getName()).isEqualTo(han.firstname); - } - - private Query queryHan() { - return query(where("id").is(han.getId())); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person { - @Id String id; - String firstname; - } - - @Data - static class Human { - @Id String id; - } - - @Data - static class Jedi { - - @Field("firstname") String name; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Friend.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Friend.java deleted file mode 100644 index a1e6a56496..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Friend.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2010-2021 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; - -public class Friend { - - private String id; - - private String firstName; - - private int age; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - -} 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 deleted file mode 100644 index 40f56a61ba..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/GeoCommandStatisticsUnitTests.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2016-2021 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 static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link GeoCommandStatistics}. - * - * @author Oliver Gierke - * @author Mark Paluch - * @soundtrack Fruitcake - Jeff Coffin (The Inside of the Outside) - */ -public class GeoCommandStatisticsUnitTests { - - @Test // DATAMONGO-1361 - public void rejectsNullCommandResult() { - assertThatIllegalArgumentException().isThrownBy(() -> GeoCommandStatistics.from(null)); - } - - @Test // DATAMONGO-1361 - public void fallsBackToNanIfNoAverageDistanceIsAvailable() { - - GeoCommandStatistics statistics = GeoCommandStatistics.from(new Document("stats", null)); - assertThat(statistics.getAverageDistance()).isNaN(); - - statistics = GeoCommandStatistics.from(new Document("stats", new Document())); - assertThat(statistics.getAverageDistance()).isNaN(); - } - - @Test // DATAMONGO-1361 - public void returnsAverageDistanceIfPresent() { - - GeoCommandStatistics statistics = GeoCommandStatistics - .from(new Document("stats", new Document("avgDistance", 1.5))); - - assertThat(statistics.getAverageDistance()).isEqualTo(1.5); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JmxServer.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JmxServer.java deleted file mode 100644 index e3cafe93b2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JmxServer.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2002-2021 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 org.springframework.context.support.ClassPathXmlApplicationContext; - -/** - * Server application than can be run as an app or unit test. - * - * @author Mark Pollack - * @author Oliver Gierke - */ -public class JmxServer { - - public static void main(String[] args) { - new JmxServer().run(); - } - - @SuppressWarnings("resource") - public void run() { - new ClassPathXmlApplicationContext(new String[] { "infrastructure.xml", "server-jmx.xml" }); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JsonSchemaQueryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JsonSchemaQueryTests.java deleted file mode 100644 index f59b87f191..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JsonSchemaQueryTests.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.*; - -import lombok.Data; -import reactor.test.StepVerifier; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.schema.MongoJsonSchema; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -public class JsonSchemaQueryTests { - - public static final String DATABASE_NAME = "json-schema-query-tests"; - - static @Client com.mongodb.reactivestreams.client.MongoClient reactiveClient; - - @Template(database = DATABASE_NAME, initialEntitySet = Person.class) // - static MongoTestTemplate template; - - Person jellyBelly, roseSpringHeart, kazmardBoombub; - - @BeforeEach - public void setUp() { - - template.flush(); - - jellyBelly = new Person(); - jellyBelly.id = "1"; - jellyBelly.name = "Jelly Belly"; - jellyBelly.gender = Gender.PIXY; - jellyBelly.address = new Address(); - jellyBelly.address.city = "Candy Hill"; - jellyBelly.address.street = "Apple Mint Street"; - jellyBelly.value = 42; - - roseSpringHeart = new Person(); - roseSpringHeart.id = "2"; - roseSpringHeart.name = "Rose SpringHeart"; - roseSpringHeart.gender = Gender.UNICORN; - roseSpringHeart.address = new Address(); - roseSpringHeart.address.city = "Rainbow Valley"; - roseSpringHeart.address.street = "Twinkle Ave."; - roseSpringHeart.value = 42L; - - kazmardBoombub = new Person(); - kazmardBoombub.id = "3"; - kazmardBoombub.name = "Kazmard Boombub"; - kazmardBoombub.gender = Gender.GOBLIN; - kazmardBoombub.value = "green"; - - template.save(jellyBelly); - template.save(roseSpringHeart); - template.save(kazmardBoombub); - } - - @Test // DATAMONGO-1835 - public void findsDocumentsWithRequiredFieldsCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder().required("address").build(); - - assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class)) - .containsExactlyInAnyOrder(jellyBelly, roseSpringHeart); - } - - @Test // DATAMONGO-1835 - public void findsDocumentsWithRequiredFieldsReactively() { - - MongoJsonSchema schema = MongoJsonSchema.builder().required("address").build(); - - new ReactiveMongoTemplate(reactiveClient, DATABASE_NAME) - .find(query(matchingDocumentStructure(schema)), Person.class).as(StepVerifier::create).expectNextCount(2) - .verifyComplete(); - } - - @Test // DATAMONGO-1835 - public void findsDocumentsWithBsonFieldTypesCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder().property(int32("value")).build(); - - assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class)) - .containsExactlyInAnyOrder(jellyBelly); - } - - @Test // DATAMONGO-1835 - public void findsDocumentsWithJsonFieldTypesCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder().property(number("value")).build(); - - assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class)) - .containsExactlyInAnyOrder(jellyBelly, roseSpringHeart); - } - - @Test // DATAMONGO-1835 - public void combineSchemaWithOtherCriteria() { - - MongoJsonSchema schema = MongoJsonSchema.builder().property(number("value")).build(); - - assertThat( - template.find(query(matchingDocumentStructure(schema).and("name").is(roseSpringHeart.name)), Person.class)) - .containsExactlyInAnyOrder(roseSpringHeart); - } - - @Test // DATAMONGO-1835 - public void usesMappedFieldNameForRequiredProperties() { - - MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build(); - - assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class)) - .containsExactlyInAnyOrder(jellyBelly, roseSpringHeart, kazmardBoombub); - } - - @Test // DATAMONGO-1835 - public void usesMappedFieldNameForProperties() { - - MongoJsonSchema schema = MongoJsonSchema.builder().property(string("name").matching("^R.*")).build(); - - assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class)) - .containsExactlyInAnyOrder(roseSpringHeart); - } - - @Test // DATAMONGO-1835 - public void mapsNestedFieldName() { - - MongoJsonSchema schema = MongoJsonSchema.builder() // - .required("address") // - .property(object("address").properties(string("street").matching("^Apple.*"))).build(); - - assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class)) - .containsExactlyInAnyOrder(jellyBelly); - } - - @Test // DATAMONGO-1835 - public void mapsEnumValuesCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder() - .property(untyped("gender").possibleValues(Gender.PIXY, Gender.GOBLIN)).build(); - - assertThat(template.find(query(matchingDocumentStructure(schema)), Person.class)) - .containsExactlyInAnyOrder(jellyBelly, kazmardBoombub); - } - - @Test // DATAMONGO-1835 - public void useTypeOperatorOnFieldLevel() { - assertThat(template.find(query(where("value").type(Type.intType())), Person.class)).containsExactly(jellyBelly); - } - - @Test // DATAMONGO-1835 - public void useTypeOperatorWithMultipleTypesOnFieldLevel() { - - assertThat(template.find(query(where("value").type(Type.intType(), Type.stringType())), Person.class)) - .containsExactlyInAnyOrder(jellyBelly, kazmardBoombub); - } - - @Test // DATAMONGO-1835 - public void findsWithSchemaReturningRawDocument() { - - MongoJsonSchema schema = MongoJsonSchema.builder().required("address").build(); - - assertThat(template.find(query(matchingDocumentStructure(schema)), Document.class, - template.getCollectionName(Person.class))).hasSize(2); - } - - @Data - static class Person { - - @Id String id; - - @Field("full_name") String name; - Gender gender; - Address address; - Object value; - } - - @Data - static class Address { - - String city; - - @Field("str") String street; - } - - static enum Gender { - PIXY, UNICORN, GOBLIN - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java deleted file mode 100644 index 9c52bbe628..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.data.annotation.Transient; -import org.springframework.data.convert.WritingConverter; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.FieldType; -import org.springframework.data.mongodb.core.mapping.MongoId; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.schema.MongoJsonSchema; - -/** - * Unit tests for {@link MappingMongoJsonSchemaCreator}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -public class MappingMongoJsonSchemaCreatorUnitTests { - - MappingMongoConverter converter; - MongoMappingContext mappingContext; - MappingMongoJsonSchemaCreator schemaCreator; - - @BeforeEach - public void setUp() { - - mappingContext = new MongoMappingContext(); - converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); - schemaCreator = new MappingMongoJsonSchemaCreator(converter); - } - - @Test // DATAMONGO-1849 - public void simpleTypes() { - - MongoJsonSchema schema = schemaCreator.createSchemaFor(VariousFieldTypes.class); - - assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo(Document.parse(VARIOUS_FIELD_TYPES)); - } - - @Test // DATAMONGO-1849 - public void withRemappedIdType() { - - MongoJsonSchema schema = schemaCreator.createSchemaFor(WithExplicitMongoIdTypeMapping.class); - assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo(WITH_EXPLICIT_MONGO_ID_TYPE_MAPPING); - } - - @Test // DATAMONGO-1849 - public void cyclic() { - - MongoJsonSchema schema = schemaCreator.createSchemaFor(Cyclic.class); - assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo(CYCLIC); - } - - @Test // DATAMONGO-1849 - public void converterRegistered() { - - MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); - MongoCustomConversions mcc = new MongoCustomConversions( - Collections.singletonList(SimpleToDocumentConverter.INSTANCE)); - converter.setCustomConversions(mcc); - converter.afterPropertiesSet(); - - schemaCreator = new MappingMongoJsonSchemaCreator(converter); - - MongoJsonSchema schema = schemaCreator.createSchemaFor(WithNestedDomainType.class); - assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo( - "{ 'type' : 'object', 'properties' : { '_id' : { 'type' : 'object' }, 'nested' : { 'type' : 'object' } } }"); - } - - // --> TYPES AND JSON - - // --> ENUM - - static final String JUST_SOME_ENUM = "{ 'type' : 'string', 'enum' : ['ONE', 'TWO'] }"; - - enum JustSomeEnum { - ONE, TWO - } - - // --> VARIOUS FIELD TYPES - - static final String VARIOUS_FIELD_TYPES = "" + // - "{" + // - " 'type' : 'object'," + // - " 'required' : ['primitiveInt']," + // - " 'properties' : {" + // - " 'id' : { 'type' : 'string' }," + // - " 're-named-property' : { 'type' : 'string' }," + // - " 'retypedProperty' : { 'bsonType' : 'javascript' }," + // - " 'primitiveInt' : { 'bsonType' : 'int' }," + // - " 'booleanProperty' : { 'type' : 'boolean' }," + // - " 'longProperty' : { 'bsonType' : 'long' }," + // - " 'intProperty' : { 'bsonType' : 'int' }," + // - " 'dateProperty' : { 'bsonType' : 'date' }," + // - " 'arrayProperty' : { 'type' : 'array' }," + // - " 'binaryDataProperty' : { 'bsonType' : 'binData' }," + // - " 'collectionProperty' : { 'type' : 'array' }," + // - " 'mapProperty' : { 'type' : 'object' }," + // - " 'objectProperty' : { 'type' : 'object' }," + // - " 'enumProperty' : " + JUST_SOME_ENUM + // - " }" + // - "}"; - - static class VariousFieldTypes { - - @Field("id") String id; - @Field("re-named-property") String renamedProperty; - @Field(targetType = FieldType.SCRIPT) String retypedProperty; - @Transient String transientProperty; - int primitiveInt; - Boolean booleanProperty; - Long longProperty; - Integer intProperty; - Date dateProperty; - Object[] arrayProperty; - byte[] binaryDataProperty; - List collectionProperty; - Map mapProperty; - Object objectProperty; - JustSomeEnum enumProperty; - } - - // --> NESTED DOMAIN TYPE - - static final String WITH_NESTED_DOMAIN_TYPE = "" + // - "{" + // - " 'type' : 'object'," + // - " 'properties' : {" + // - " '_id' : { 'type' : 'object' }," + // - " 'nested' : " + VARIOUS_FIELD_TYPES + // - " }" + // - "}"; - - static class WithNestedDomainType { - - String id; - VariousFieldTypes nested; - } - - // --> EXPLICIT MONGO_ID MAPPING - - final String WITH_EXPLICIT_MONGO_ID_TYPE_MAPPING = "" + // - "{" + // - " 'type' : 'object'," + // - " 'properties' : {" + // - " '_id' : { 'bsonType' : 'objectId' }," + // - " 'nested' : " + VARIOUS_FIELD_TYPES + // - " }" + // - "}"; - - static class WithExplicitMongoIdTypeMapping { - - @MongoId(targetType = FieldType.OBJECT_ID) String id; - VariousFieldTypes nested; - } - - // --> OH NO - A CYCLIC PROPERTY RELATIONSHIP 😱 - - static final String CYCLIC_FIN = "" + // - "{" + // - " 'type' : 'object'," + // - " 'properties' : {" + // - " 'root' : { 'type' : 'string' }" + // - " 'cyclic' : { 'type' : 'object' }" + // - " }" + // - "}"; - - static final String CYCLIC_2 = "" + // - "{" + // - " 'type' : 'object'," + // - " 'properties' : {" + // - " 'nested2' : { 'type' : 'string' }," + // - " 'cyclic' : " + CYCLIC_FIN + // - " }" + // - "}"; - - class Cyclic2 { - - String nested2; - Cyclic cyclic; - } - - static final String CYCLIC_1 = "" + // - "{" + // - " 'type' : 'object'," + // - " 'properties' : {" + // - " 'nested1' : { 'type' : 'string' }," + // - " 'cyclic2' : " + CYCLIC_2 + // - " }" + // - "}"; - - class Cyclic1 { - - String nested1; - Cyclic2 cyclic2; - } - - static final String CYCLIC = "" + // - "{" + // - " 'type' : 'object'," + // - " 'properties' : {" + // - " 'root' : { 'type' : 'string' }," + // - " 'cyclic1' : " + CYCLIC_1 + // - " }" + // - "}"; - - class Cyclic { - - String root; - Cyclic1 cyclic1; - } - - @WritingConverter - enum SimpleToDocumentConverter - implements org.springframework.core.convert.converter.Converter { - INSTANCE; - - @Override - public org.bson.Document convert(VariousFieldTypes source) { - return null; - } - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoAdminIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoAdminIntegrationTests.java deleted file mode 100644 index 86ae1f73f4..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoAdminIntegrationTests.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2021 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 org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.client.MongoClient; - -/** - * This test class assumes that you are already running the MongoDB server. - * - * @author Mark Pollack - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:infrastructure.xml") -public class MongoAdminIntegrationTests { - - private static final Log logger = LogFactory.getLog(MongoAdminIntegrationTests.class); - - @Autowired MongoClient mongoClient; - - MongoAdmin mongoAdmin; - - @Before - public void setUp() { - mongoAdmin = new MongoAdmin(mongoClient); - } - - @Test - public void serverStats() { - logger.info("stats = " + mongoAdmin.getServerStatus()); - } - - @Test - public void databaseStats() { - logger.info(mongoAdmin.getDatabaseStats("testAdminDb")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientFactoryBeanUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientFactoryBeanUnitTests.java deleted file mode 100644 index 6391f4b0f8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientFactoryBeanUnitTests.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; - -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.Test; - -import com.mongodb.ConnectionString; -import com.mongodb.MongoClientSettings; -import com.mongodb.ServerAddress; - -/** - * Unit tests for {@link MongoClientFactoryBean}. - * - * @author Christoph Strobl - */ -class MongoClientFactoryBeanUnitTests { - - static final String CONNECTION_STRING_STRING = "mongodb://db1.example.net:27017,db2.example.net:2500/?replicaSet=test&connectTimeoutMS=300000"; - static final ConnectionString CONNECTION_STRING = new ConnectionString(CONNECTION_STRING_STRING); - - @Test // DATAMONGO-2427 - void connectionStringParametersNotOverriddenByDefaults() { - - MongoClientFactoryBean factoryBean = new MongoClientFactoryBean(); - factoryBean.setConnectionString(CONNECTION_STRING); - factoryBean.setMongoClientSettings(MongoClientSettings.builder().build()); - - MongoClientSettings settings = factoryBean.computeClientSetting(); - - assertThat(settings.getClusterSettings().getRequiredReplicaSetName()).isEqualTo("test"); - assertThat(settings.getSocketSettings().getConnectTimeout(TimeUnit.MILLISECONDS)).isEqualTo(300000); - assertThat(settings.getClusterSettings().getHosts()).hasSize(2); - } - - @Test // DATAMONGO-2427 - void hostPortParametersNotOverriddenByDefaults() { - - MongoClientFactoryBean factoryBean = new MongoClientFactoryBean(); - factoryBean.setPort(2500); - factoryBean.setHost("db2.example.net"); - factoryBean.setReplicaSet("rs0"); - factoryBean.setMongoClientSettings(MongoClientSettings.builder().build()); - - MongoClientSettings settings = factoryBean.computeClientSetting(); - - assertThat(settings.getClusterSettings().getRequiredReplicaSetName()).isEqualTo("rs0"); - assertThat(settings.getClusterSettings().getHosts()).containsExactly(new ServerAddress("db2.example.net", 2500)); - } - - @Test // DATAMONGO-2427 - void explicitSettingsOverrideConnectionStringOnes() { - - MongoClientFactoryBean factoryBean = new MongoClientFactoryBean(); - factoryBean.setConnectionString(CONNECTION_STRING); - factoryBean.setMongoClientSettings( - MongoClientSettings.builder().applyToClusterSettings(it -> it.requiredReplicaSetName("rs0")) - .applyToSocketSettings(it -> it.connectTimeout(100, TimeUnit.MILLISECONDS)).build()); - - MongoClientSettings settings = factoryBean.computeClientSetting(); - - assertThat(settings.getClusterSettings().getRequiredReplicaSetName()).isEqualTo("rs0"); - assertThat(settings.getSocketSettings().getConnectTimeout(TimeUnit.MILLISECONDS)).isEqualTo(100); - assertThat(settings.getClusterSettings().getHosts()).hasSize(2); - } - - @Test // DATAMONGO-2427 - void hostAndPortPlusConnectionStringError() { - - MongoClientFactoryBean factoryBean = new MongoClientFactoryBean(); - factoryBean.setConnectionString(CONNECTION_STRING); - factoryBean.setHost("localhost"); - factoryBean.setPort(27017); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(factoryBean::createInstance); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBeanIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBeanIntegrationTests.java deleted file mode 100644 index 8b7e14db44..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBeanIntegrationTests.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2015-2021 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 static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.data.mongodb.config.ReadPreferencePropertyEditor; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.ReadPreference; - -/** - * Integration tests for {@link MongoClientSettingsFactoryBean}. - * - * @author Christoph Strobl - */ -public class MongoClientSettingsFactoryBeanIntegrationTests { - - @Test // DATAMONGO-1158 - public void convertsReadPreferenceConcernCorrectly() { - - RootBeanDefinition definition = new RootBeanDefinition(MongoClientSettingsFactoryBean.class); - definition.getPropertyValues().addPropertyValue("readPreference", "NEAREST"); - - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - factory.registerCustomEditor(ReadPreference.class, ReadPreferencePropertyEditor.class); - - factory.registerBeanDefinition("factory", definition); - - MongoClientSettingsFactoryBean bean = factory.getBean("&factory", MongoClientSettingsFactoryBean.class); - assertThat(ReflectionTestUtils.getField(bean, "readPreference")).isEqualTo((Object) ReadPreference.nearest()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBeanUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBeanUnitTests.java deleted file mode 100644 index c4a22ab10e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBeanUnitTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; - -import org.bson.UuidRepresentation; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.data.mongodb.config.ReadConcernPropertyEditor; -import org.springframework.data.mongodb.config.ReadPreferencePropertyEditor; -import org.springframework.data.mongodb.config.UUidRepresentationPropertyEditor; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.ReadConcern; -import com.mongodb.ReadPreference; - -/** - * Unit tests for {@link MongoClientSettingsFactoryBean}. - * - * @author Christoph Strobl - */ -public class MongoClientSettingsFactoryBeanUnitTests { - - @Test // DATAMONGO-2384 - public void convertsReadPreferenceConcernCorrectly() { - - RootBeanDefinition definition = new RootBeanDefinition(MongoClientSettingsFactoryBean.class); - definition.getPropertyValues().addPropertyValue("readPreference", "NEAREST"); - - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - factory.registerCustomEditor(ReadPreference.class, ReadPreferencePropertyEditor.class); - - factory.registerBeanDefinition("factory", definition); - - MongoClientSettingsFactoryBean bean = factory.getBean("&factory", MongoClientSettingsFactoryBean.class); - assertThat(ReflectionTestUtils.getField(bean, "readPreference")).isEqualTo(ReadPreference.nearest()); - } - - @Test // DATAMONGO-2384 - public void convertsReadConcernConcernCorrectly() { - - RootBeanDefinition definition = new RootBeanDefinition(MongoClientSettingsFactoryBean.class); - definition.getPropertyValues().addPropertyValue("readConcern", "MAJORITY"); - - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - factory.registerCustomEditor(ReadPreference.class, ReadConcernPropertyEditor.class); - - factory.registerBeanDefinition("factory", definition); - - MongoClientSettingsFactoryBean bean = factory.getBean("&factory", MongoClientSettingsFactoryBean.class); - assertThat(ReflectionTestUtils.getField(bean, "readConcern")).isEqualTo(ReadConcern.MAJORITY); - } - - @Test // DATAMONGO-2427 - public void convertsUuidRepresentationCorrectly() { - - RootBeanDefinition definition = new RootBeanDefinition(MongoClientSettingsFactoryBean.class); - definition.getPropertyValues().addPropertyValue("uUidRepresentation", "STANDARD"); - - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - factory.registerCustomEditor(ReadPreference.class, UUidRepresentationPropertyEditor.class); - - factory.registerBeanDefinition("factory", definition); - - MongoClientSettingsFactoryBean bean = factory.getBean("&factory", MongoClientSettingsFactoryBean.class); - assertThat(ReflectionTestUtils.getField(bean, "uUidRepresentation")).isEqualTo(UuidRepresentation.STANDARD); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBeanTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBeanTests.java deleted file mode 100644 index 0ada9b2b6c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBeanTests.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.AutoEncryptionSettings; - -/** - * Integration tests for {@link MongoEncryptionSettingsFactoryBean}. - * - * @author Christoph Strobl - */ -public class MongoEncryptionSettingsFactoryBeanTests { - - @Test // DATAMONGO-2306 - public void createsAutoEncryptionSettings() { - - RootBeanDefinition definition = new RootBeanDefinition(MongoEncryptionSettingsFactoryBean.class); - definition.getPropertyValues().addPropertyValue("bypassAutoEncryption", true); - definition.getPropertyValues().addPropertyValue("keyVaultNamespace", "ns"); - - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - factory.registerBeanDefinition("factory", definition); - - MongoEncryptionSettingsFactoryBean bean = factory.getBean("&factory", MongoEncryptionSettingsFactoryBean.class); - assertThat(ReflectionTestUtils.getField(bean, "bypassAutoEncryption")).isEqualTo(true); - - AutoEncryptionSettings target = factory.getBean(AutoEncryptionSettings.class); - assertThat(target.getKeyVaultNamespace()).isEqualTo("ns"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java deleted file mode 100644 index 9f7cb92b46..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2013-2021 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 static org.assertj.core.api.Assertions.*; - -import org.bson.BsonDocument; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.core.NestedRuntimeException; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.DataAccessResourceFailureException; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.dao.InvalidDataAccessResourceUsageException; -import org.springframework.data.mongodb.ClientSessionException; -import org.springframework.data.mongodb.MongoTransactionException; -import org.springframework.data.mongodb.UncategorizedMongoDbException; -import org.springframework.lang.Nullable; - -import com.mongodb.MongoCursorNotFoundException; -import com.mongodb.MongoException; -import com.mongodb.MongoInternalException; -import com.mongodb.MongoSocketException; -import com.mongodb.MongoSocketReadTimeoutException; -import com.mongodb.MongoSocketWriteException; -import com.mongodb.ServerAddress; - -/** - * Unit tests for {@link MongoExceptionTranslator}. - * - * @author Michal Vich - * @author Oliver Gierke - * @author Christoph Strobl - * @author Brice Vandeputte - */ -class MongoExceptionTranslatorUnitTests { - - private static final String EXCEPTION_MESSAGE = "IOException"; - private MongoExceptionTranslator translator; - - @BeforeEach - void setUp() { - translator = new MongoExceptionTranslator(); - } - - @Test - void translateDuplicateKey() { - - expectExceptionWithCauseMessage( - translator.translateExceptionIfPossible( - new com.mongodb.DuplicateKeyException(new BsonDocument(), new ServerAddress(), null)), - DuplicateKeyException.class, null); - } - - @Test // GH-3568 - void translateSocketException() { - - expectExceptionWithCauseMessage( - translator.translateExceptionIfPossible(new MongoSocketException(EXCEPTION_MESSAGE, new ServerAddress())), - DataAccessResourceFailureException.class, EXCEPTION_MESSAGE); - } - - @Test // GH-3568 - void translateSocketExceptionSubclasses() { - - expectExceptionWithCauseMessage( - translator.translateExceptionIfPossible( - new MongoSocketWriteException("intermediate message", new ServerAddress(), new Exception(EXCEPTION_MESSAGE)) - ), - DataAccessResourceFailureException.class, EXCEPTION_MESSAGE); - - expectExceptionWithCauseMessage( - translator.translateExceptionIfPossible( - new MongoSocketReadTimeoutException("intermediate message", new ServerAddress(), new Exception(EXCEPTION_MESSAGE)) - ), - DataAccessResourceFailureException.class, EXCEPTION_MESSAGE); - - } - - @Test - void translateCursorNotFound() { - - expectExceptionWithCauseMessage( - translator.translateExceptionIfPossible(new MongoCursorNotFoundException(1L, new ServerAddress())), - DataAccessResourceFailureException.class); - } - - @Test - void translateToDuplicateKeyException() { - - checkTranslatedMongoException(DuplicateKeyException.class, 11000); - checkTranslatedMongoException(DuplicateKeyException.class, 11001); - } - - @Test - void translateToDataAccessResourceFailureException() { - - checkTranslatedMongoException(DataAccessResourceFailureException.class, 12000); - checkTranslatedMongoException(DataAccessResourceFailureException.class, 13440); - } - - @Test - void translateToInvalidDataAccessApiUsageException() { - - checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 10003); - checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 12001); - checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 12010); - checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 12011); - checkTranslatedMongoException(InvalidDataAccessApiUsageException.class, 12012); - } - - @Test - void translateToUncategorizedMongoDbException() { - - MongoException exception = new MongoException(0, ""); - DataAccessException translatedException = translator.translateExceptionIfPossible(exception); - - expectExceptionWithCauseMessage(translatedException, UncategorizedMongoDbException.class); - } - - @Test - void translateMongoInternalException() { - - MongoInternalException exception = new MongoInternalException("Internal exception"); - DataAccessException translatedException = translator.translateExceptionIfPossible(exception); - - expectExceptionWithCauseMessage(translatedException, InvalidDataAccessResourceUsageException.class); - } - - @Test - void translateUnsupportedException() { - - RuntimeException exception = new RuntimeException(); - assertThat(translator.translateExceptionIfPossible(exception)).isNull(); - } - - @Test // DATAMONGO-2045 - void translateSessionExceptions() { - - checkTranslatedMongoException(ClientSessionException.class, 206); - checkTranslatedMongoException(ClientSessionException.class, 213); - checkTranslatedMongoException(ClientSessionException.class, 228); - checkTranslatedMongoException(ClientSessionException.class, 264); - } - - @Test // DATAMONGO-2045 - void translateTransactionExceptions() { - - checkTranslatedMongoException(MongoTransactionException.class, 217); - checkTranslatedMongoException(MongoTransactionException.class, 225); - checkTranslatedMongoException(MongoTransactionException.class, 244); - checkTranslatedMongoException(MongoTransactionException.class, 251); - checkTranslatedMongoException(MongoTransactionException.class, 256); - checkTranslatedMongoException(MongoTransactionException.class, 257); - checkTranslatedMongoException(MongoTransactionException.class, 263); - checkTranslatedMongoException(MongoTransactionException.class, 267); - } - - private void checkTranslatedMongoException(Class clazz, int code) { - - DataAccessException translated = translator.translateExceptionIfPossible(new MongoException(code, "")); - - assertThat(translated).as("Expected exception of type " + clazz.getName() + "!").isNotNull(); - - Throwable cause = translated.getRootCause(); - assertThat(cause).isInstanceOf(MongoException.class); - assertThat(((MongoException) cause).getCode()).isEqualTo(code); - } - - private static void expectExceptionWithCauseMessage(@Nullable NestedRuntimeException e, - Class type) { - expectExceptionWithCauseMessage(e, type, null); - } - - private static void expectExceptionWithCauseMessage(@Nullable NestedRuntimeException e, - Class type, @Nullable String message) { - - assertThat(e).isInstanceOf(type); - - if (message != null) { - assertThat(e.getRootCause()).isNotNull(); - assertThat(e.getRootCause().getMessage()).contains(message); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java deleted file mode 100644 index 3bbff3fcde..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright 2011-2021 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 static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.bson.conversions.Bson; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.dao.DataAccessException; -import org.springframework.data.geo.Point; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mongodb.core.convert.AbstractMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.MongoTypeMapper; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.util.TypeInformation; - -import com.mongodb.DBRef; - -/** - * Abstract base class for unit tests to specify behaviour we expect from {@link MongoOperations}. Subclasses return - * instances of their implementation and thus can see if it correctly implements the {@link MongoOperations} interface. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -public abstract class MongoOperationsUnitTests { - - @Mock CollectionCallback collectionCallback; - @Mock DbCallback dbCallback; - - MongoConverter converter; - Person person; - List persons; - - @BeforeEach - public final void operationsSetUp() { - - person = new Person("Oliver"); - persons = Arrays.asList(person); - - converter = new AbstractMongoConverter(null) { - - public void write(Object t, Bson bson) { - ((Document) bson).put("firstName", person.getFirstName()); - } - - @SuppressWarnings("unchecked") - public S read(Class clazz, Bson bson) { - return (S) person; - } - - public MappingContext, MongoPersistentProperty> getMappingContext() { - return null; - } - - public Object convertToMongoType(Object obj, TypeInformation typeInformation) { - return null; - } - - public DBRef toDBRef(Object object, MongoPersistentProperty referingProperty) { - return null; - } - - @Override - public MongoTypeMapper getTypeMapper() { - return null; - } - }; - } - - @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void rejectsNullForCollectionCallback() { - assertThatIllegalArgumentException().isThrownBy(() -> getOperations().execute("test", (CollectionCallback) null)); - } - - @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void rejectsNullForCollectionCallback2() { - assertThatIllegalArgumentException() - .isThrownBy(() -> getOperations().execute("collection", (CollectionCallback) null)); - } - - @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void rejectsNullForDbCallback() { - assertThatIllegalArgumentException().isThrownBy(() -> getOperations().execute((DbCallback) null)); - } - - @Test - public void convertsExceptionForCollectionExists() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.collectionExists("foo"); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForCreateCollection() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.createCollection("foo"); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForCreateCollection2() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.createCollection("foo", CollectionOptions.empty().size(1).maxDocuments(1).capped()); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForDropCollection() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.dropCollection("foo"); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForExecuteCollectionCallback() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.execute("test", collectionCallback); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForExecuteDbCallback() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.execute(dbCallback); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForExecuteCollectionCallbackAndCollection() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.execute("collection", collectionCallback); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForExecuteCommand() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.executeCommand(new Document()); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForExecuteStringCommand() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.executeCommand(""); - } - }.assertException(IllegalArgumentException.class); - } - - @Test - public void convertsExceptionForGetCollection() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.findAll(Object.class); - } - }.assertException(MappingException.class); - } - - @Test - public void convertsExceptionForGetCollectionWithCollectionName() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.getCollection("collection"); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForGetCollectionWithCollectionNameAndType() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.findAll(Object.class, "collection"); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForGetCollectionWithCollectionNameTypeAndReader() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.findAll(Object.class, "collection"); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForGetCollectionNames() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.getCollectionNames(); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForInsert() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.insert(person); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForInsert2() { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.insert(person, "collection"); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForInsertList() throws Exception { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.insertAll(persons); - } - }.assertDataAccessException(); - } - - @Test - public void convertsExceptionForGetInsertList2() throws Exception { - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.insert(persons, "collection"); - } - }.assertDataAccessException(); - } - - @Test // DATAMONGO-341 - public void geoNearRejectsNullNearQuery() { - - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.geoNear(null, Person.class); - } - }.assertDataAccessException(); - } - - @Test // DATAMONGO-341 - public void geoNearRejectsNullNearQueryifCollectionGiven() { - - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.geoNear(null, Person.class, "collection"); - } - }.assertDataAccessException(); - } - - @Test // DATAMONGO-341 - public void geoNearRejectsNullEntityClass() { - - final NearQuery query = NearQuery.near(new Point(10, 20)); - - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.geoNear(query, null); - } - }.assertDataAccessException(); - } - - @Test // DATAMONGO-341 - public void geoNearRejectsNullEntityClassIfCollectionGiven() { - - final NearQuery query = NearQuery.near(new Point(10, 20)); - - new Execution() { - @Override - public void doWith(MongoOperations operations) { - operations.geoNear(query, null, "collection"); - } - }.assertDataAccessException(); - } - - private abstract class Execution { - - public void assertDataAccessException() { - assertException(DataAccessException.class); - } - - public void assertException(Class exception) { - - assertThatThrownBy(() -> doWith(getOperationsForExceptionHandling())).isInstanceOf(exception); - } - - public abstract void doWith(MongoOperations operations); - } - - /** - * Expects an {@link MongoOperations} instance that will be used to check that invoking methods on it will only cause - * {@link DataAccessException}s. - * - * @return - */ - protected abstract MongoOperations getOperationsForExceptionHandling(); - - /** - * Returns a plain {@link MongoOperations}. - * - * @return - */ - protected abstract MongoOperations getOperations(); - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateCollationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateCollationTests.java deleted file mode 100644 index 99afc2c82f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateCollationTests.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; - -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Set; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Collation.Alternate; -import org.springframework.data.mongodb.core.query.Collation.ComparisonLevel; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.client.MongoClient; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -public class MongoTemplateCollationTests { - - public static final String COLLECTION_NAME = "collation-1"; - static @Client MongoClient mongoClient; - - @Configuration - static class Config extends AbstractMongoClientConfiguration { - - @Override - public MongoClient mongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return "collation-tests"; - } - - @Override - protected boolean autoIndexCreation() { - return false; - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - } - - @Autowired MongoTemplate template; - - @BeforeEach - public void setUp() { - template.dropCollection(COLLECTION_NAME); - } - - @Test // DATAMONGO-1518 - public void createCollectionWithCollation() { - - template.createCollection(COLLECTION_NAME, CollectionOptions.just(Collation.of("en_US"))); - - Document collation = getCollationInfo(COLLECTION_NAME); - assertThat(collation.get("locale")).isEqualTo("en_US"); - } - - @Test // DATAMONGO-1518 - public void createCollectionWithCollationHavingLocaleVariant() { - - template.createCollection(COLLECTION_NAME, - CollectionOptions.just(Collation.of(new Locale("de", "AT", "phonebook")))); - - Document collation = getCollationInfo(COLLECTION_NAME); - assertThat(collation.get("locale")).isEqualTo("de_AT@collation=phonebook"); - } - - @Test // DATAMONGO-1518 - public void createCollectionWithCollationHavingStrength() { - - template.createCollection(COLLECTION_NAME, - CollectionOptions.just(Collation.of("en_US").strength(ComparisonLevel.primary().includeCase()))); - - Document collation = getCollationInfo(COLLECTION_NAME); - assertThat(collation.get("strength")).isEqualTo(1); - assertThat(collation.get("caseLevel")).isEqualTo(true); - } - - @Test // DATAMONGO-1518 - public void createCollectionWithCollationHavingBackwardsAndNumericOrdering() { - - template.createCollection(COLLECTION_NAME, - CollectionOptions.just(Collation.of("en_US").backwardDiacriticSort().numericOrderingEnabled())); - - Document collation = getCollationInfo(COLLECTION_NAME); - assertThat(collation.get("backwards")).isEqualTo(true); - assertThat(collation.get("numericOrdering")).isEqualTo(true); - } - - @Test // DATAMONGO-1518 - public void createCollationWithCollationHavingAlternate() { - - template.createCollection(COLLECTION_NAME, - CollectionOptions.just(Collation.of("en_US").alternate(Alternate.shifted().punct()))); - - Document collation = getCollationInfo(COLLECTION_NAME); - assertThat(collation.get("alternate")).isEqualTo("shifted"); - assertThat(collation.get("maxVariable")).isEqualTo("punct"); - } - - private Document getCollationInfo(String collectionName) { - return getCollectionInfo(collectionName).get("options", Document.class).get("collation", Document.class); - } - - private Document getCollectionInfo(String collectionName) { - - return template.execute(db -> { - - Document result = db.runCommand( - new Document().append("listCollections", 1).append("filter", new Document("name", collectionName))); - return (Document) result.get("cursor", Document.class).get("firstBatch", List.class).get(0); - }); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDbRefTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDbRefTests.java deleted file mode 100644 index 17196219d4..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDbRefTests.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.convert.LazyLoadingProxy; -import org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.MongoId; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -import com.mongodb.client.model.Filters; - -/** - * {@link org.springframework.data.mongodb.core.mapping.DBRef} related integration tests for - * {@link org.springframework.data.mongodb.core.MongoTemplate}. - * - * @author Christoph Strobl - */ -@ExtendWith(MongoTemplateExtension.class) -public class MongoTemplateDbRefTests { - - @Template(database = "mongo-template-dbref-tests", - initialEntitySet = { RefCycleLoadingIntoDifferentTypeRoot.class, - RefCycleLoadingIntoDifferentTypeIntermediate.class, RefCycleLoadingIntoDifferentTypeRootView.class, - WithDBRefOnRawStringId.class, WithLazyDBRefOnRawStringId.class, WithRefToAnotherDb.class, - WithLazyRefToAnotherDb.class, WithListRefToAnotherDb.class, WithLazyListRefToAnotherDb.class }) // - static MongoTestTemplate template; - - @Template(database = "mongo-template-dbref-tests-other-db", initialEntitySet = JustSomeType.class) // - static MongoTestTemplate otherDbTemplate; - - @BeforeEach - public void setUp() { - - template.flush(); - otherDbTemplate.flush(); - } - - @Test // DATAMONGO-1703 - public void shouldLoadRefIntoDifferentTypeCorrectly() { - - // init root - RefCycleLoadingIntoDifferentTypeRoot root = new RefCycleLoadingIntoDifferentTypeRoot(); - root.id = "root-1"; - root.content = "jon snow"; - template.save(root); - - // init one and set view id ref to root.id - RefCycleLoadingIntoDifferentTypeIntermediate intermediate = new RefCycleLoadingIntoDifferentTypeIntermediate(); - intermediate.id = "one-1"; - intermediate.refToRootView = new RefCycleLoadingIntoDifferentTypeRootView(); - intermediate.refToRootView.id = root.id; - - template.save(intermediate); - - // add one ref to root - root.refToIntermediate = intermediate; - template.save(root); - - RefCycleLoadingIntoDifferentTypeRoot loaded = template.findOne(query(where("id").is(root.id)), - RefCycleLoadingIntoDifferentTypeRoot.class); - - assertThat(loaded.content).isEqualTo("jon snow"); - assertThat(loaded.getRefToIntermediate()).isInstanceOf(RefCycleLoadingIntoDifferentTypeIntermediate.class); - assertThat(loaded.getRefToIntermediate().getRefToRootView()) - .isInstanceOf(RefCycleLoadingIntoDifferentTypeRootView.class); - assertThat(loaded.getRefToIntermediate().getRefToRootView().getContent()).isEqualTo("jon snow"); - } - - @Test // DATAMONGO-1798 - public void stringDBRefLoading() { - - RawStringId ref = new RawStringId(); - ref.id = new ObjectId().toHexString(); - ref.value = "new value"; - - template.save(ref); - - WithDBRefOnRawStringId source = new WithDBRefOnRawStringId(); - source.id = "foo"; - source.value = ref; - - template.save(source); - - org.bson.Document result = template - .execute(db -> (org.bson.Document) db.getCollection(template.getCollectionName(WithDBRefOnRawStringId.class)) - .find(Filters.eq("_id", source.id)).limit(1).into(new ArrayList()).iterator().next()); - - assertThat(result).isNotNull(); - assertThat(result.get("value")) - .isEqualTo(new com.mongodb.DBRef(template.getCollectionName(RawStringId.class), ref.getId())); - - WithDBRefOnRawStringId target = template.findOne(query(where("id").is(source.id)), WithDBRefOnRawStringId.class); - assertThat(target.value).isEqualTo(ref); - } - - @Test // DATAMONGO-1798 - public void stringDBRefLazyLoading() { - - RawStringId ref = new RawStringId(); - ref.id = new ObjectId().toHexString(); - ref.value = "new value"; - - template.save(ref); - - WithLazyDBRefOnRawStringId source = new WithLazyDBRefOnRawStringId(); - source.id = "foo"; - source.value = ref; - - template.save(source); - - org.bson.Document result = template.execute( - db -> (org.bson.Document) db.getCollection(template.getCollectionName(WithLazyDBRefOnRawStringId.class)) - .find(Filters.eq("_id", source.id)).limit(1).into(new ArrayList()).iterator().next()); - - assertThat(result).isNotNull(); - assertThat(result.get("value")) - .isEqualTo(new com.mongodb.DBRef(template.getCollectionName(RawStringId.class), ref.getId())); - - WithLazyDBRefOnRawStringId target = template.findOne(query(where("id").is(source.id)), - WithLazyDBRefOnRawStringId.class); - - assertThat(target.value).isInstanceOf(LazyLoadingProxy.class); - assertThat(target.getValue()).isEqualTo(ref); - } - - @Test // DATAMONGO-2223 - public void shouldResolveSingleDBRefToAnotherDb() { - - JustSomeType one = new JustSomeType(); - one.value = "one"; - - otherDbTemplate.insert(one); - - WithRefToAnotherDb source = new WithRefToAnotherDb(); - source.value = one; - - template.save(source); - - WithRefToAnotherDb target = template.findOne(query(where("id").is(source.id)), WithRefToAnotherDb.class); - assertThat(target.getValue()).isEqualTo(one); - } - - @Test // DATAMONGO-2223 - public void shouldResolveSingleLazyDBRefToAnotherDb() { - - JustSomeType one = new JustSomeType(); - one.value = "one"; - - otherDbTemplate.insert(one); - - WithLazyRefToAnotherDb source = new WithLazyRefToAnotherDb(); - source.value = one; - - template.save(source); - - WithLazyRefToAnotherDb target = template.findOne(query(where("id").is(source.id)), WithLazyRefToAnotherDb.class); - LazyLoadingTestUtils.assertProxyIsResolved(target.value, false); - assertThat(target.getValue()).isEqualTo(one); - } - - @Test // DATAMONGO-2223 - public void shouldResolveListDBRefToAnotherDb() { - - JustSomeType one = new JustSomeType(); - one.value = "one"; - - JustSomeType two = new JustSomeType(); - two.value = "two"; - - otherDbTemplate.insertAll(Arrays.asList(one, two)); - - WithListRefToAnotherDb source = new WithListRefToAnotherDb(); - source.value = Arrays.asList(one, two); - - template.save(source); - - WithListRefToAnotherDb target = template.findOne(query(where("id").is(source.id)), WithListRefToAnotherDb.class); - assertThat(target.getValue()).containsExactlyInAnyOrder(one, two); - } - - @Test // DATAMONGO-2223 - public void shouldResolveLazyListDBRefToAnotherDb() { - - JustSomeType one = new JustSomeType(); - one.value = "one"; - - JustSomeType two = new JustSomeType(); - two.value = "two"; - - otherDbTemplate.insertAll(Arrays.asList(one, two)); - - WithLazyListRefToAnotherDb source = new WithLazyListRefToAnotherDb(); - source.value = Arrays.asList(one, two); - - template.save(source); - - WithLazyListRefToAnotherDb target = template.findOne(query(where("id").is(source.id)), - WithLazyListRefToAnotherDb.class); - LazyLoadingTestUtils.assertProxyIsResolved(target.value, false); - assertThat(target.getValue()).containsExactlyInAnyOrder(one, two); - } - - @Data - @Document("cycle-with-different-type-root") - static class RefCycleLoadingIntoDifferentTypeRoot { - - @Id String id; - String content; - @DBRef RefCycleLoadingIntoDifferentTypeIntermediate refToIntermediate; - } - - @Data - @Document("cycle-with-different-type-intermediate") - static class RefCycleLoadingIntoDifferentTypeIntermediate { - - @Id String id; - @DBRef RefCycleLoadingIntoDifferentTypeRootView refToRootView; - } - - @Data - @Document("cycle-with-different-type-root") - static class RefCycleLoadingIntoDifferentTypeRootView { - - @Id String id; - String content; - } - - @Data - static class RawStringId { - - @MongoId String id; - String value; - } - - @Data - static class WithDBRefOnRawStringId { - - @Id String id; - @DBRef RawStringId value; - } - - @Data - static class WithLazyDBRefOnRawStringId { - - @Id String id; - @DBRef(lazy = true) RawStringId value; - } - - @Data - static class WithRefToAnotherDb { - - @Id String id; - @DBRef(db = "mongo-template-dbref-tests-other-db") JustSomeType value; - } - - @Data - static class WithLazyRefToAnotherDb { - - @Id String id; - @DBRef(lazy = true, db = "mongo-template-dbref-tests-other-db") JustSomeType value; - } - - @Data - static class WithListRefToAnotherDb { - - @Id String id; - @DBRef(db = "mongo-template-dbref-tests-other-db") List value; - } - - @Data - static class WithLazyListRefToAnotherDb { - - @Id String id; - @DBRef(lazy = true, db = "mongo-template-dbref-tests-other-db") List value; - } - - @Data - static class JustSomeType { - - @Id String id; - String value; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateFieldProjectionTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateFieldProjectionTests.java deleted file mode 100644 index e39b4f4f1f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateFieldProjectionTests.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2021 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 static org.assertj.core.api.Assertions.*; - -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.util.function.Consumer; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.MongoExpression; -import org.springframework.data.mongodb.core.aggregation.AggregationSpELExpression; -import org.springframework.data.mongodb.core.aggregation.StringOperators; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -/** - * Integration tests for {@link org.springframework.data.mongodb.core.query.Field}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -@EnableIfMongoServerVersion(isGreaterThanEqual = "4.4") -class MongoTemplateFieldProjectionTests { - - private static @Template MongoTestTemplate template; - - private Person luke; - - @BeforeEach - void beforeEach() { - - luke = new Person(); - luke.id = "luke"; - luke.firstname = "luke"; - luke.lastname = "skywalker"; - - template.save(luke); - } - - @AfterEach - void afterEach() { - template.flush(Person.class, Wrapper.class); - } - - @Test // GH-3583 - void usesMongoExpressionAsIs() { - - Person result = findLuke(fields -> { - fields.include("firstname").project(MongoExpression.create("'$toUpper' : '$last_name'")) - .as("last_name"); - }); - - assertThat(result).isEqualTo(luke.upperCaseLastnameClone()); - } - - @Test // GH-3583 - void usesMongoExpressionWithPlaceholdersAsIs() { - - Person result = findLuke(fields -> { - fields.include("firstname").project(MongoExpression.create("'$toUpper' : '$?0'", "last_name")) - .as("last_name"); - }); - - assertThat(result).isEqualTo(luke.upperCaseLastnameClone()); - } - - @Test // GH-3583 - void mapsAggregationExpressionToDomainType() { - - Person result = findLuke(fields -> { - fields.include("firstname").project(StringOperators.valueOf("lastname").toUpper()).as("last_name"); - }); - - assertThat(result).isEqualTo(luke.upperCaseLastnameClone()); - } - - @Test // GH-3583 - void mapsAggregationSpELExpressionToDomainType() { - - Person result = findLuke(fields -> { - fields.include("firstname").project(AggregationSpELExpression.expressionOf("toUpper(lastname)")).as("last_name"); - }); - - assertThat(result).isEqualTo(luke.upperCaseLastnameClone()); - } - - @Test // GH-3583 - void mapsNestedPathAggregationExpressionToDomainType() { - - Wrapper wrapper = new Wrapper(); - wrapper.id = "wrapper"; - wrapper.person = luke; - - template.save(wrapper); - - Query query = Query.query(Criteria.where("id").is(wrapper.id)); - query.fields().include("person.firstname", "person.id") - .project(StringOperators.valueOf("person.lastname").toUpper()).as("person.last_name"); - - Wrapper result = template.findOne(query, Wrapper.class); - assertThat(result.person).isEqualTo(luke.upperCaseLastnameClone()); - } - - @Test // GH-3583 - void mapsProjectionOnUnwrapped() { - - luke.address = new Address(); - luke.address.planet = "tatoine"; - - template.save(luke); - - Person result = findLuke(fields -> { - fields.project(StringOperators.valueOf("address.planet").toUpper()).as("planet"); - }); - - assertThat(result.address.planet).isEqualTo("TATOINE"); - } - - private Person findLuke(Consumer projection) { - - Query query = Query.query(Criteria.where("id").is(luke.id)); - projection.accept(query.fields()); - return template.findOne(query, Person.class); - } - - @EqualsAndHashCode - @ToString - static class Wrapper { - @Id String id; - Person person; - } - - @EqualsAndHashCode - @ToString - static class Person { - - @Id String id; - String firstname; - - @Field("last_name") // - String lastname; - - @Unwrapped.Nullable Address address; - - Person toUpperCaseLastnameClone(Person source) { - - Person target = new Person(); - target.id = source.id; - target.firstname = source.firstname; - target.lastname = source.lastname.toUpperCase(); - target.address = source.address; - - return target; - } - - Person upperCaseLastnameClone() { - return toUpperCaseLastnameClone(this); - } - } - - @EqualsAndHashCode - @ToString - static class Address { - String planet; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateMappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateMappingTests.java deleted file mode 100644 index 878f89f70e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateMappingTests.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2011-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.dao.DataAccessException; -import org.springframework.data.annotation.Id; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.MongoException; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.geojson.Geometry; -import com.mongodb.client.model.geojson.MultiPolygon; -import com.mongodb.client.model.geojson.PolygonCoordinates; -import com.mongodb.client.model.geojson.Position; - -/** - * Integration test for {@link MongoTemplate}. - * - * @author Oliver Gierke - * @author Thomas Risberg - * @author Mark Paluch - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:template-mapping.xml") -public class MongoTemplateMappingTests { - - @Autowired @Qualifier("mongoTemplate1") MongoTemplate template1; - - @Autowired @Qualifier("mongoTemplate2") MongoTemplate template2; - - @Before - public void setUp() { - template1.dropCollection(template1.getCollectionName(Person.class)); - } - - @Test - public void insertsEntityCorrectly1() { - - addAndRetrievePerson(template1); - checkPersonPersisted(template1); - } - - @Test - public void insertsEntityCorrectly2() { - - addAndRetrievePerson(template2); - checkPersonPersisted(template2); - } - - @Test // DATAMONGO-2357 - public void writesAndReadsEntityWithNativeMongoGeoJsonTypesCorrectly() { - - WithMongoGeoJson source = new WithMongoGeoJson(); - source.id = "id-2"; - source.multiPolygon = new MultiPolygon(Arrays.asList(new PolygonCoordinates(Arrays.asList(new Position(0, 0), - new Position(0, 1), new Position(1, 1), new Position(1, 0), new Position(0, 0))))); - - template1.save(source); - - assertThat(template1.findOne(query(where("id").is(source.id)), WithMongoGeoJson.class)).isEqualTo(source); - } - - @Test // DATAMONGO-2357 - public void writesAndReadsEntityWithOpenNativeMongoGeoJsonTypesCorrectly() { - - WithOpenMongoGeoJson source = new WithOpenMongoGeoJson(); - source.id = "id-2"; - source.geometry = new MultiPolygon(Arrays.asList(new PolygonCoordinates(Arrays.asList(new Position(0, 0), - new Position(0, 1), new Position(1, 1), new Position(1, 0), new Position(0, 0))))); - - template1.save(source); - - assertThat(template1.findOne(query(where("id").is(source.id)), WithOpenMongoGeoJson.class)).isEqualTo(source); - } - - @Data - static class WithMongoGeoJson { - - @Id String id; - MultiPolygon multiPolygon; - } - - @Data - static class WithOpenMongoGeoJson { - - @Id String id; - Geometry geometry; - } - - private void addAndRetrievePerson(MongoTemplate template) { - Person person = new Person("Oliver"); - person.setAge(25); - template.insert(person); - - Person result = template.findById(person.getId(), Person.class); - assertThat(result.getFirstName()).isEqualTo("Oliver"); - assertThat(result.getAge()).isEqualTo(25); - } - - private void checkPersonPersisted(MongoTemplate template) { - template.execute(Person.class, new CollectionCallback() { - public Object doInCollection(MongoCollection collection) throws MongoException, DataAccessException { - Document document = collection.find(new Document()).first(); - assertThat((String) document.get("name")).isEqualTo("Oliver"); - return null; - } - }); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java deleted file mode 100644 index b18e1066f5..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ /dev/null @@ -1,4250 +0,0 @@ -/* - * Copyright 2011-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.core.query.Update.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.Value; -import lombok.With; - -import java.lang.reflect.InvocationTargetException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.bson.types.ObjectId; -import org.joda.time.DateTime; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.convert.converter.Converter; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.dao.OptimisticLockingFailureException; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.annotation.Version; -import org.springframework.data.auditing.IsNewAwareAuditingHandler; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.LazyLoadingProxy; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.index.Index; -import org.springframework.data.mongodb.core.index.IndexField; -import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoId; -import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener; -import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; -import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent; -import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.MongoVersion; -import org.springframework.data.util.CloseableIterator; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - -import com.mongodb.BasicDBObject; -import com.mongodb.DBObject; -import com.mongodb.DBRef; -import com.mongodb.MongoException; -import com.mongodb.ReadPreference; -import com.mongodb.WriteConcern; -import com.mongodb.client.FindIterable; -import com.mongodb.client.ListIndexesIterable; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoCursor; -import com.mongodb.client.model.Filters; -import com.mongodb.client.model.IndexOptions; -import com.mongodb.client.result.DeleteResult; -import com.mongodb.client.result.UpdateResult; - -/** - * Integration test for {@link MongoTemplate}. - * - * @author Oliver Gierke - * @author Thomas Risberg - * @author Amol Nayak - * @author Patryk Wasik - * @author Thomas Darimont - * @author Komi Innocent - * @author Christoph Strobl - * @author Mark Paluch - * @author Laszlo Csontos - * @author duozhilin - */ -@ExtendWith(MongoClientExtension.class) -public class MongoTemplateTests { - - public static final String DB_NAME = "mongo-template-tests"; - - static @Client MongoClient client; - - ConfigurableApplicationContext context = new GenericApplicationContext(); - - MongoTestTemplate template = new MongoTestTemplate(cfg -> { - - cfg.configureDatabaseFactory(it -> { - - it.client(client); - it.defaultDb(DB_NAME); - }); - - cfg.configureMappingContext(it -> { - it.autocreateIndex(false); - it.intitalEntitySet(AuditablePerson.class); - - }); - - cfg.configureApplicationContext(it -> { - it.applicationContext(context); - it.addEventListener(new PersonWithIdPropertyOfTypeUUIDListener()); - }); - - cfg.configureAuditing(it -> { - it.auditingHandler(IsNewAwareAuditingHandler::new); - }); - }); - - MongoTestTemplate mappingTemplate = new MongoTestTemplate(cfg -> { - - cfg.configureDatabaseFactory(it -> { - - it.client(client); - it.defaultDb(DB_NAME); - }); - - cfg.configureConversion(it -> { - it.customConverters(DateToDateTimeConverter.INSTANCE, DateTimeToDateConverter.INSTANCE); - }); - - cfg.configureMappingContext(it -> { - it.autocreateIndex(false); - }); - - cfg.configureApplicationContext(it -> { - it.applicationContext(new GenericApplicationContext()); - it.addEventListener(new PersonWithIdPropertyOfTypeUUIDListener()); - }); - }); - - MongoDatabaseFactory factory = template.getMongoDbFactory(); - - @AfterEach - public void cleanUp() { - - template.flush(); - template.flush("collection", "personX", "findandreplace"); - - mappingTemplate.flush(); - - template.dropCollection(Person.class); - } - - @Test - public void insertsSimpleEntityCorrectly() throws Exception { - - Person person = new Person("Oliver"); - person.setAge(25); - template.insert(person); - - List result = template.find(new Query(Criteria.where("_id").is(person.getId())), Person.class); - assertThat(result.size()).isEqualTo(1); - assertThat(result).contains(person); - } - - @Test - public void bogusUpdateDoesNotTriggerException() throws Exception { - - MongoTemplate mongoTemplate = new MongoTemplate(factory); - mongoTemplate.setWriteResultChecking(WriteResultChecking.EXCEPTION); - - Person person = new Person("Oliver2"); - person.setAge(25); - mongoTemplate.insert(person); - - Query q = new Query(Criteria.where("BOGUS").gt(22)); - Update u = new Update().set("firstName", "Sven"); - mongoTemplate.updateFirst(q, u, Person.class); - } - - @Test // DATAMONGO-480 - public void throwsExceptionForDuplicateIds() { - - MongoTemplate template = new MongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - - Person person = new Person(new ObjectId(), "Amol"); - person.setAge(28); - - template.insert(person); - - try { - template.insert(person); - fail("Expected DataIntegrityViolationException!"); - } catch (DataIntegrityViolationException e) { - assertThat(e.getMessage()).contains("E11000 duplicate key error"); - } - } - - @Test // DATAMONGO-480, DATAMONGO-799 - public void throwsExceptionForUpdateWithInvalidPushOperator() { - - MongoTemplate template = new MongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - - ObjectId id = new ObjectId(); - Person person = new Person(id, "Amol"); - person.setAge(28); - - template.insert(person); - - Query query = new Query(Criteria.where("firstName").is("Amol")); - Update upd = new Update().push("age", 29); - - assertThatExceptionOfType(DataIntegrityViolationException.class) - .isThrownBy(() -> template.updateFirst(query, upd, Person.class)).withMessageContaining("array") - .withMessageContaining("age"); - } - - @Test // DATAMONGO-480 - public void throwsExceptionForIndexViolationIfConfigured() { - - MongoTemplate template = new MongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - template.indexOps(Person.class).ensureIndex(new Index().on("firstName", Direction.DESC).unique()); - - Person person = new Person(new ObjectId(), "Amol"); - person.setAge(28); - - template.save(person); - - person = new Person(new ObjectId(), "Amol"); - person.setAge(28); - - try { - template.save(person); - fail("Expected DataIntegrityViolationException!"); - } catch (DataIntegrityViolationException e) { - assertThat(e.getMessage()).contains("E11000 duplicate key error"); - } - } - - @Test // DATAMONGO-480 - public void rejectsDuplicateIdInInsertAll() { - - MongoTemplate template = new MongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - - ObjectId id = new ObjectId(); - Person person = new Person(id, "Amol"); - person.setAge(28); - - List records = new ArrayList<>(); - records.add(person); - records.add(person); - - assertThatExceptionOfType(DataIntegrityViolationException.class).isThrownBy(() -> template.insertAll(records)) - .withMessageContaining("E11000 duplicate key error"); - } - - @Test // DATAMONGO-1687 - public void createCappedCollection() { - - template.createCollection(Person.class, CollectionOptions.empty().capped().size(1000).maxDocuments(1000)); - - org.bson.Document collectionOptions = getCollectionInfo(template.getCollectionName(Person.class)).get("options", - org.bson.Document.class); - assertThat(collectionOptions.get("capped")).isEqualTo(true); - } - - private org.bson.Document getCollectionInfo(String collectionName) { - - return template.execute(db -> { - - org.bson.Document result = db.runCommand(new org.bson.Document().append("listCollections", 1).append("filter", - new org.bson.Document("name", collectionName))); - return (org.bson.Document) result.get("cursor", org.bson.Document.class).get("firstBatch", List.class).get(0); - }); - } - - @Test - public void testEnsureIndex() throws Exception { - - Person p1 = new Person("Oliver"); - p1.setAge(25); - template.insert(p1); - Person p2 = new Person("Sven"); - p2.setAge(40); - template.insert(p2); - - template.indexOps(Person.class).ensureIndex(new Index().on("age", Direction.DESC).unique()); - - MongoCollection coll = template.getCollection(template.getCollectionName(Person.class)); - List indexInfo = new ArrayList<>(); - coll.listIndexes().into(indexInfo); - - assertThat(indexInfo.size()).isEqualTo(2); - Object indexKey = null; - boolean unique = false; - for (org.bson.Document ix : indexInfo) { - - if ("age_-1".equals(ix.get("name"))) { - indexKey = ix.get("key"); - unique = (Boolean) ix.get("unique"); - assertThat(ix.get("dropDups")).isNull(); - } - } - assertThat(((org.bson.Document) indexKey)).containsEntry("age", -1); - assertThat(unique).isTrue(); - - List indexInfoList = template.indexOps(Person.class).getIndexInfo(); - - assertThat(indexInfoList.size()).isEqualTo(2); - IndexInfo ii = indexInfoList.get(1); - assertThat(ii.isUnique()).isTrue(); - assertThat(ii.isSparse()).isFalse(); - - List indexFields = ii.getIndexFields(); - IndexField field = indexFields.get(0); - - assertThat(field).isEqualTo(IndexField.create("age", Direction.DESC)); - } - - @Test // DATAMONGO-746, DATAMONGO-2264 - public void testReadIndexInfoForIndicesCreatedViaMongoShellCommands() throws Exception { - - template.dropCollection(Person.class); - - assertThat(template.indexOps(Person.class).getIndexInfo().isEmpty()).isTrue(); - - factory.getMongoDatabase().getCollection(template.getCollectionName(Person.class)) - .createIndex(new org.bson.Document("age", -1), new IndexOptions().name("age_-1").unique(true).sparse(true)); - - ListIndexesIterable indexInfo = template.getCollection(template.getCollectionName(Person.class)) - .listIndexes(); - org.bson.Document indexKey = null; - boolean unique = false; - - MongoCursor cursor = indexInfo.iterator(); - - while (cursor.hasNext()) { - - org.bson.Document ix = cursor.next(); - - if ("age_-1".equals(ix.get("name"))) { - indexKey = (org.bson.Document) ix.get("key"); - unique = (Boolean) ix.get("unique"); - } - } - - assertThat(indexKey).containsEntry("age", -1); - assertThat(unique).isTrue(); - - IndexInfo info = template.indexOps(Person.class).getIndexInfo().get(1); - assertThat(info.isUnique()).isTrue(); - assertThat(info.isSparse()).isTrue(); - - List indexFields = info.getIndexFields(); - IndexField field = indexFields.get(0); - - assertThat(field).isEqualTo(IndexField.create("age", Direction.DESC)); - } - - @Test - public void testProperHandlingOfDifferentIdTypesWithMappingMongoConverter() throws Exception { - testProperHandlingOfDifferentIdTypes(this.mappingTemplate); - } - - private void testProperHandlingOfDifferentIdTypes(MongoTemplate mongoTemplate) throws Exception { - - // String id - generated - PersonWithIdPropertyOfTypeString p1 = new PersonWithIdPropertyOfTypeString(); - p1.setFirstName("Sven_1"); - p1.setAge(22); - // insert - mongoTemplate.insert(p1); - // also try save - mongoTemplate.save(p1); - assertThat(p1.getId()).isNotNull(); - PersonWithIdPropertyOfTypeString p1q = mongoTemplate.findOne(new Query(where("id").is(p1.getId())), - PersonWithIdPropertyOfTypeString.class); - assertThat(p1q).isNotNull(); - assertThat(p1q.getId()).isEqualTo(p1.getId()); - checkCollectionContents(PersonWithIdPropertyOfTypeString.class, 1); - - // String id - provided - PersonWithIdPropertyOfTypeString p2 = new PersonWithIdPropertyOfTypeString(); - p2.setFirstName("Sven_2"); - p2.setAge(22); - p2.setId("TWO"); - // insert - mongoTemplate.insert(p2); - // also try save - mongoTemplate.save(p2); - assertThat(p2.getId()).isNotNull(); - PersonWithIdPropertyOfTypeString p2q = mongoTemplate.findOne(new Query(where("id").is(p2.getId())), - PersonWithIdPropertyOfTypeString.class); - assertThat(p2q).isNotNull(); - assertThat(p2q.getId()).isEqualTo(p2.getId()); - checkCollectionContents(PersonWithIdPropertyOfTypeString.class, 2); - - // String _id - generated - PersonWith_idPropertyOfTypeString p3 = new PersonWith_idPropertyOfTypeString(); - p3.setFirstName("Sven_3"); - p3.setAge(22); - // insert - mongoTemplate.insert(p3); - // also try save - mongoTemplate.save(p3); - assertThat(p3.get_id()).isNotNull(); - PersonWith_idPropertyOfTypeString p3q = mongoTemplate.findOne(new Query(where("_id").is(p3.get_id())), - PersonWith_idPropertyOfTypeString.class); - assertThat(p3q).isNotNull(); - assertThat(p3q.get_id()).isEqualTo(p3.get_id()); - checkCollectionContents(PersonWith_idPropertyOfTypeString.class, 1); - - // String _id - provided - PersonWith_idPropertyOfTypeString p4 = new PersonWith_idPropertyOfTypeString(); - p4.setFirstName("Sven_4"); - p4.setAge(22); - p4.set_id("FOUR"); - // insert - mongoTemplate.insert(p4); - // also try save - mongoTemplate.save(p4); - assertThat(p4.get_id()).isNotNull(); - PersonWith_idPropertyOfTypeString p4q = mongoTemplate.findOne(new Query(where("_id").is(p4.get_id())), - PersonWith_idPropertyOfTypeString.class); - assertThat(p4q).isNotNull(); - assertThat(p4q.get_id()).isEqualTo(p4.get_id()); - checkCollectionContents(PersonWith_idPropertyOfTypeString.class, 2); - - // ObjectId id - generated - PersonWithIdPropertyOfTypeObjectId p5 = new PersonWithIdPropertyOfTypeObjectId(); - p5.setFirstName("Sven_5"); - p5.setAge(22); - // insert - mongoTemplate.insert(p5); - // also try save - mongoTemplate.save(p5); - assertThat(p5.getId()).isNotNull(); - PersonWithIdPropertyOfTypeObjectId p5q = mongoTemplate.findOne(new Query(where("id").is(p5.getId())), - PersonWithIdPropertyOfTypeObjectId.class); - assertThat(p5q).isNotNull(); - assertThat(p5q.getId()).isEqualTo(p5.getId()); - checkCollectionContents(PersonWithIdPropertyOfTypeObjectId.class, 1); - - // ObjectId id - provided - PersonWithIdPropertyOfTypeObjectId p6 = new PersonWithIdPropertyOfTypeObjectId(); - p6.setFirstName("Sven_6"); - p6.setAge(22); - p6.setId(new ObjectId()); - // insert - mongoTemplate.insert(p6); - // also try save - mongoTemplate.save(p6); - assertThat(p6.getId()).isNotNull(); - PersonWithIdPropertyOfTypeObjectId p6q = mongoTemplate.findOne(new Query(where("id").is(p6.getId())), - PersonWithIdPropertyOfTypeObjectId.class); - assertThat(p6q).isNotNull(); - assertThat(p6q.getId()).isEqualTo(p6.getId()); - checkCollectionContents(PersonWithIdPropertyOfTypeObjectId.class, 2); - - // ObjectId _id - generated - PersonWith_idPropertyOfTypeObjectId p7 = new PersonWith_idPropertyOfTypeObjectId(); - p7.setFirstName("Sven_7"); - p7.setAge(22); - // insert - mongoTemplate.insert(p7); - // also try save - mongoTemplate.save(p7); - assertThat(p7.get_id()).isNotNull(); - PersonWith_idPropertyOfTypeObjectId p7q = mongoTemplate.findOne(new Query(where("_id").is(p7.get_id())), - PersonWith_idPropertyOfTypeObjectId.class); - assertThat(p7q).isNotNull(); - assertThat(p7q.get_id()).isEqualTo(p7.get_id()); - checkCollectionContents(PersonWith_idPropertyOfTypeObjectId.class, 1); - - // ObjectId _id - provided - PersonWith_idPropertyOfTypeObjectId p8 = new PersonWith_idPropertyOfTypeObjectId(); - p8.setFirstName("Sven_8"); - p8.setAge(22); - p8.set_id(new ObjectId()); - // insert - mongoTemplate.insert(p8); - // also try save - mongoTemplate.save(p8); - assertThat(p8.get_id()).isNotNull(); - PersonWith_idPropertyOfTypeObjectId p8q = mongoTemplate.findOne(new Query(where("_id").is(p8.get_id())), - PersonWith_idPropertyOfTypeObjectId.class); - assertThat(p8q).isNotNull(); - assertThat(p8q.get_id()).isEqualTo(p8.get_id()); - checkCollectionContents(PersonWith_idPropertyOfTypeObjectId.class, 2); - - // Integer id - provided - PersonWithIdPropertyOfTypeInteger p9 = new PersonWithIdPropertyOfTypeInteger(); - p9.setFirstName("Sven_9"); - p9.setAge(22); - p9.setId(Integer.valueOf(12345)); - // insert - mongoTemplate.insert(p9); - // also try save - mongoTemplate.save(p9); - assertThat(p9.getId()).isNotNull(); - PersonWithIdPropertyOfTypeInteger p9q = mongoTemplate.findOne(new Query(where("id").in(p9.getId())), - PersonWithIdPropertyOfTypeInteger.class); - assertThat(p9q).isNotNull(); - assertThat(p9q.getId()).isEqualTo(p9.getId()); - checkCollectionContents(PersonWithIdPropertyOfTypeInteger.class, 1); - - // DATAMONGO-602 - // BigInteger id - provided - PersonWithIdPropertyOfTypeBigInteger p9bi = new PersonWithIdPropertyOfTypeBigInteger(); - p9bi.setFirstName("Sven_9bi"); - p9bi.setAge(22); - p9bi.setId(BigInteger.valueOf(12345)); - // insert - mongoTemplate.insert(p9bi); - // also try save - mongoTemplate.save(p9bi); - assertThat(p9bi.getId()).isNotNull(); - PersonWithIdPropertyOfTypeBigInteger p9qbi = mongoTemplate.findOne(new Query(where("id").in(p9bi.getId())), - PersonWithIdPropertyOfTypeBigInteger.class); - assertThat(p9qbi).isNotNull(); - assertThat(p9qbi.getId()).isEqualTo(p9bi.getId()); - checkCollectionContents(PersonWithIdPropertyOfTypeBigInteger.class, 1); - - // int id - provided - PersonWithIdPropertyOfPrimitiveInt p10 = new PersonWithIdPropertyOfPrimitiveInt(); - p10.setFirstName("Sven_10"); - p10.setAge(22); - p10.setId(12345); - // insert - mongoTemplate.insert(p10); - // also try save - mongoTemplate.save(p10); - assertThat(p10.getId()).isNotNull(); - PersonWithIdPropertyOfPrimitiveInt p10q = mongoTemplate.findOne(new Query(where("id").in(p10.getId())), - PersonWithIdPropertyOfPrimitiveInt.class); - assertThat(p10q).isNotNull(); - assertThat(p10q.getId()).isEqualTo(p10.getId()); - checkCollectionContents(PersonWithIdPropertyOfPrimitiveInt.class, 1); - - // Long id - provided - PersonWithIdPropertyOfTypeLong p11 = new PersonWithIdPropertyOfTypeLong(); - p11.setFirstName("Sven_9"); - p11.setAge(22); - p11.setId(Long.valueOf(12345L)); - // insert - mongoTemplate.insert(p11); - // also try save - mongoTemplate.save(p11); - assertThat(p11.getId()).isNotNull(); - PersonWithIdPropertyOfTypeLong p11q = mongoTemplate.findOne(new Query(where("id").in(p11.getId())), - PersonWithIdPropertyOfTypeLong.class); - assertThat(p11q).isNotNull(); - assertThat(p11q.getId()).isEqualTo(p11.getId()); - checkCollectionContents(PersonWithIdPropertyOfTypeLong.class, 1); - - // long id - provided - PersonWithIdPropertyOfPrimitiveLong p12 = new PersonWithIdPropertyOfPrimitiveLong(); - p12.setFirstName("Sven_10"); - p12.setAge(22); - p12.setId(12345L); - // insert - mongoTemplate.insert(p12); - // also try save - mongoTemplate.save(p12); - assertThat(p12.getId()).isNotNull(); - PersonWithIdPropertyOfPrimitiveLong p12q = mongoTemplate.findOne(new Query(where("id").in(p12.getId())), - PersonWithIdPropertyOfPrimitiveLong.class); - assertThat(p12q).isNotNull(); - assertThat(p12q.getId()).isEqualTo(p12.getId()); - checkCollectionContents(PersonWithIdPropertyOfPrimitiveLong.class, 1); - - // DATAMONGO-1617 - // UUID id - provided - PersonWithIdPropertyOfTypeUUID p13 = new PersonWithIdPropertyOfTypeUUID(); - p13.setFirstName("Sven_10"); - p13.setAge(22); - // insert - mongoTemplate.insert(p13); - // also try save - mongoTemplate.save(p13); - assertThat(p13.getId()).isNotNull(); - PersonWithIdPropertyOfTypeUUID p13q = mongoTemplate.findOne(new Query(where("id").in(p13.getId())), - PersonWithIdPropertyOfTypeUUID.class); - assertThat(p13q).isNotNull(); - assertThat(p13q.getId()).isEqualTo(p13.getId()); - checkCollectionContents(PersonWithIdPropertyOfTypeUUID.class, 1); - } - - private void checkCollectionContents(Class entityClass, int count) { - assertThat(template.findAll(entityClass).size()).isEqualTo(count); - } - - @Test // DATAMONGO-234 - public void testFindAndUpdate() { - - template.insert(new Person("Tom", 21)); - template.insert(new Person("Dick", 22)); - template.insert(new Person("Harry", 23)); - - Query query = new Query(Criteria.where("firstName").is("Harry")); - Update update = new Update().inc("age", 1); - Person p = template.findAndModify(query, update, Person.class); // return old - assertThat(p.getFirstName()).isEqualTo("Harry"); - assertThat(p.getAge()).isEqualTo(23); - p = template.findOne(query, Person.class); - assertThat(p.getAge()).isEqualTo(24); - - p = template.findAndModify(query, update, Person.class, "person"); - assertThat(p.getAge()).isEqualTo(24); - p = template.findOne(query, Person.class); - assertThat(p.getAge()).isEqualTo(25); - - p = template.findAndModify(query, update, new FindAndModifyOptions().returnNew(true), Person.class); - assertThat(p.getAge()).isEqualTo(26); - - p = template.findAndModify(query, update, new FindAndModifyOptions(), Person.class, "person"); - assertThat(p.getAge()).isEqualTo(26); - p = template.findOne(query, Person.class); - assertThat(p.getAge()).isEqualTo(27); - - Query query2 = new Query(Criteria.where("firstName").is("Mary")); - p = template.findAndModify(query2, update, new FindAndModifyOptions().returnNew(true).upsert(true), Person.class); - assertThat(p.getFirstName()).isEqualTo("Mary"); - assertThat(p.getAge()).isEqualTo(1); - - } - - @Test - public void testFindAndUpdateUpsert() { - template.insert(new Person("Tom", 21)); - template.insert(new Person("Dick", 22)); - Query query = new Query(Criteria.where("firstName").is("Harry")); - Update update = new Update().set("age", 23); - Person p = template.findAndModify(query, update, new FindAndModifyOptions().upsert(true).returnNew(true), - Person.class); - assertThat(p.getFirstName()).isEqualTo("Harry"); - assertThat(p.getAge()).isEqualTo(23); - } - - @Test - public void testFindAndRemove() throws Exception { - - Message m1 = new Message("Hello Spring"); - template.insert(m1); - Message m2 = new Message("Hello Mongo"); - template.insert(m2); - - Query q = new Query(Criteria.where("text").regex("^Hello.*")); - Message found1 = template.findAndRemove(q, Message.class); - Message found2 = template.findAndRemove(q, Message.class); - - Message notFound = template.findAndRemove(q, Message.class); - assertThat(found1).isNotNull(); - assertThat(found2).isNotNull(); - assertThat(notFound).isNull(); - } - - @Test // DATAMONGO-1761 - public void testDistinct() { - - Address address1 = new Address(); - address1.state = "PA"; - address1.city = "Philadelphia"; - - Address address2 = new Address(); - address2.state = "PA"; - address2.city = " New York"; - - MyPerson person1 = new MyPerson(); - person1.name = "Ben"; - person1.address = address1; - - MyPerson person2 = new MyPerson(); - person2.name = "Eric"; - person2.address = address2; - - template.save(person1); - template.save(person2); - - assertThat(template.findDistinct("name", MyPerson.class, String.class)).containsExactlyInAnyOrder(person1.getName(), - person2.getName()); - assertThat(template.findDistinct(new BasicQuery("{'address.state' : 'PA'}"), "name", MyPerson.class, String.class)) - .containsExactlyInAnyOrder(person1.getName(), person2.getName()); - assertThat(template.findDistinct(new BasicQuery("{'address.state' : 'PA'}"), "name", - template.getCollectionName(MyPerson.class), MyPerson.class, String.class)) - .containsExactlyInAnyOrder(person1.getName(), person2.getName()); - } - - @Test // DATAMONGO-1761 - public void testDistinctCovertsResultToPropertyTargetTypeCorrectly() { - - template.insert(new Person("garvin")); - - assertThat(template.findDistinct("firstName", Person.class, Object.class)).allSatisfy(String.class::isInstance); - } - - @Test // DATAMONGO-1761 - public void testDistinctResolvesDbRefsCorrectly() { - - SomeContent content1 = new SomeContent(); - content1.text = "content-1"; - - SomeContent content2 = new SomeContent(); - content2.text = "content-2"; - - template.save(content1); - template.save(content2); - - SomeTemplate t1 = new SomeTemplate(); - t1.content = content1; - - SomeTemplate t2 = new SomeTemplate(); - t2.content = content2; - - SomeTemplate t3 = new SomeTemplate(); - t3.content = content2; - - template.insert(t1); - template.insert(t2); - template.insert(t3); - - assertThat(template.findDistinct("content", SomeTemplate.class, SomeContent.class)) - .containsExactlyInAnyOrder(content1, content2); - } - - @Test - public void testUsingAnInQueryWithObjectId() throws Exception { - - template.remove(new Query(), PersonWithIdPropertyOfTypeObjectId.class); - - PersonWithIdPropertyOfTypeObjectId p1 = new PersonWithIdPropertyOfTypeObjectId(); - p1.setFirstName("Sven"); - p1.setAge(11); - template.insert(p1); - PersonWithIdPropertyOfTypeObjectId p2 = new PersonWithIdPropertyOfTypeObjectId(); - p2.setFirstName("Mary"); - p2.setAge(21); - template.insert(p2); - PersonWithIdPropertyOfTypeObjectId p3 = new PersonWithIdPropertyOfTypeObjectId(); - p3.setFirstName("Ann"); - p3.setAge(31); - template.insert(p3); - PersonWithIdPropertyOfTypeObjectId p4 = new PersonWithIdPropertyOfTypeObjectId(); - p4.setFirstName("John"); - p4.setAge(41); - template.insert(p4); - - Query q1 = new Query(Criteria.where("age").in(11, 21, 41)); - List results1 = template.find(q1, PersonWithIdPropertyOfTypeObjectId.class); - Query q2 = new Query(Criteria.where("firstName").in("Ann", "Mary")); - List results2 = template.find(q2, PersonWithIdPropertyOfTypeObjectId.class); - Query q3 = new Query(Criteria.where("id").in(p3.getId())); - List results3 = template.find(q3, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(results1.size()).isEqualTo(3); - assertThat(results2.size()).isEqualTo(2); - assertThat(results3.size()).isEqualTo(1); - } - - @Test - public void testUsingAnInQueryWithStringId() throws Exception { - - template.remove(new Query(), PersonWithIdPropertyOfTypeString.class); - - PersonWithIdPropertyOfTypeString p1 = new PersonWithIdPropertyOfTypeString(); - p1.setFirstName("Sven"); - p1.setAge(11); - template.insert(p1); - PersonWithIdPropertyOfTypeString p2 = new PersonWithIdPropertyOfTypeString(); - p2.setFirstName("Mary"); - p2.setAge(21); - template.insert(p2); - PersonWithIdPropertyOfTypeString p3 = new PersonWithIdPropertyOfTypeString(); - p3.setFirstName("Ann"); - p3.setAge(31); - template.insert(p3); - PersonWithIdPropertyOfTypeString p4 = new PersonWithIdPropertyOfTypeString(); - p4.setFirstName("John"); - p4.setAge(41); - template.insert(p4); - - Query q1 = new Query(Criteria.where("age").in(11, 21, 41)); - List results1 = template.find(q1, PersonWithIdPropertyOfTypeString.class); - Query q2 = new Query(Criteria.where("firstName").in("Ann", "Mary")); - List results2 = template.find(q2, PersonWithIdPropertyOfTypeString.class); - Query q3 = new Query(Criteria.where("id").in(p3.getId(), p4.getId())); - List results3 = template.find(q3, PersonWithIdPropertyOfTypeString.class); - assertThat(results1.size()).isEqualTo(3); - assertThat(results2.size()).isEqualTo(2); - assertThat(results3.size()).isEqualTo(2); - } - - @Test - public void testUsingAnInQueryWithLongId() throws Exception { - - template.remove(new Query(), PersonWithIdPropertyOfTypeLong.class); - - PersonWithIdPropertyOfTypeLong p1 = new PersonWithIdPropertyOfTypeLong(); - p1.setFirstName("Sven"); - p1.setAge(11); - p1.setId(1001L); - template.insert(p1); - PersonWithIdPropertyOfTypeLong p2 = new PersonWithIdPropertyOfTypeLong(); - p2.setFirstName("Mary"); - p2.setAge(21); - p2.setId(1002L); - template.insert(p2); - PersonWithIdPropertyOfTypeLong p3 = new PersonWithIdPropertyOfTypeLong(); - p3.setFirstName("Ann"); - p3.setAge(31); - p3.setId(1003L); - template.insert(p3); - PersonWithIdPropertyOfTypeLong p4 = new PersonWithIdPropertyOfTypeLong(); - p4.setFirstName("John"); - p4.setAge(41); - p4.setId(1004L); - template.insert(p4); - - Query q1 = new Query(Criteria.where("age").in(11, 21, 41)); - List results1 = template.find(q1, PersonWithIdPropertyOfTypeLong.class); - Query q2 = new Query(Criteria.where("firstName").in("Ann", "Mary")); - List results2 = template.find(q2, PersonWithIdPropertyOfTypeLong.class); - Query q3 = new Query(Criteria.where("id").in(1001L, 1004L)); - List results3 = template.find(q3, PersonWithIdPropertyOfTypeLong.class); - assertThat(results1.size()).isEqualTo(3); - assertThat(results2.size()).isEqualTo(2); - assertThat(results3.size()).isEqualTo(2); - } - - @Test // DATAMONGO-602 - public void testUsingAnInQueryWithBigIntegerId() throws Exception { - - template.remove(new Query(), PersonWithIdPropertyOfTypeBigInteger.class); - - PersonWithIdPropertyOfTypeBigInteger p1 = new PersonWithIdPropertyOfTypeBigInteger(); - p1.setFirstName("Sven"); - p1.setAge(11); - p1.setId(new BigInteger("2666666666666666665069473312490162649510603601")); - template.insert(p1); - PersonWithIdPropertyOfTypeBigInteger p2 = new PersonWithIdPropertyOfTypeBigInteger(); - p2.setFirstName("Mary"); - p2.setAge(21); - p2.setId(new BigInteger("2666666666666666665069473312490162649510603602")); - template.insert(p2); - PersonWithIdPropertyOfTypeBigInteger p3 = new PersonWithIdPropertyOfTypeBigInteger(); - p3.setFirstName("Ann"); - p3.setAge(31); - p3.setId(new BigInteger("2666666666666666665069473312490162649510603603")); - template.insert(p3); - PersonWithIdPropertyOfTypeBigInteger p4 = new PersonWithIdPropertyOfTypeBigInteger(); - p4.setFirstName("John"); - p4.setAge(41); - p4.setId(new BigInteger("2666666666666666665069473312490162649510603604")); - template.insert(p4); - - Query q1 = new Query(Criteria.where("age").in(11, 21, 41)); - List results1 = template.find(q1, PersonWithIdPropertyOfTypeBigInteger.class); - Query q2 = new Query(Criteria.where("firstName").in("Ann", "Mary")); - List results2 = template.find(q2, PersonWithIdPropertyOfTypeBigInteger.class); - Query q3 = new Query(Criteria.where("id").in(new BigInteger("2666666666666666665069473312490162649510603601"), - new BigInteger("2666666666666666665069473312490162649510603604"))); - List results3 = template.find(q3, PersonWithIdPropertyOfTypeBigInteger.class); - assertThat(results1.size()).isEqualTo(3); - assertThat(results2.size()).isEqualTo(2); - assertThat(results3.size()).isEqualTo(2); - } - - @Test - public void testUsingAnInQueryWithPrimitiveIntId() throws Exception { - - template.remove(new Query(), PersonWithIdPropertyOfPrimitiveInt.class); - - PersonWithIdPropertyOfPrimitiveInt p1 = new PersonWithIdPropertyOfPrimitiveInt(); - p1.setFirstName("Sven"); - p1.setAge(11); - p1.setId(1001); - template.insert(p1); - PersonWithIdPropertyOfPrimitiveInt p2 = new PersonWithIdPropertyOfPrimitiveInt(); - p2.setFirstName("Mary"); - p2.setAge(21); - p2.setId(1002); - template.insert(p2); - PersonWithIdPropertyOfPrimitiveInt p3 = new PersonWithIdPropertyOfPrimitiveInt(); - p3.setFirstName("Ann"); - p3.setAge(31); - p3.setId(1003); - template.insert(p3); - PersonWithIdPropertyOfPrimitiveInt p4 = new PersonWithIdPropertyOfPrimitiveInt(); - p4.setFirstName("John"); - p4.setAge(41); - p4.setId(1004); - template.insert(p4); - - Query q1 = new Query(Criteria.where("age").in(11, 21, 41)); - List results1 = template.find(q1, PersonWithIdPropertyOfPrimitiveInt.class); - Query q2 = new Query(Criteria.where("firstName").in("Ann", "Mary")); - List results2 = template.find(q2, PersonWithIdPropertyOfPrimitiveInt.class); - Query q3 = new Query(Criteria.where("id").in(1001, 1003)); - List results3 = template.find(q3, PersonWithIdPropertyOfPrimitiveInt.class); - assertThat(results1.size()).isEqualTo(3); - assertThat(results2.size()).isEqualTo(2); - assertThat(results3.size()).isEqualTo(2); - } - - @Test - public void testUsingInQueryWithList() throws Exception { - - template.remove(new Query(), PersonWithIdPropertyOfTypeObjectId.class); - - PersonWithIdPropertyOfTypeObjectId p1 = new PersonWithIdPropertyOfTypeObjectId(); - p1.setFirstName("Sven"); - p1.setAge(11); - template.insert(p1); - PersonWithIdPropertyOfTypeObjectId p2 = new PersonWithIdPropertyOfTypeObjectId(); - p2.setFirstName("Mary"); - p2.setAge(21); - template.insert(p2); - PersonWithIdPropertyOfTypeObjectId p3 = new PersonWithIdPropertyOfTypeObjectId(); - p3.setFirstName("Ann"); - p3.setAge(31); - template.insert(p3); - PersonWithIdPropertyOfTypeObjectId p4 = new PersonWithIdPropertyOfTypeObjectId(); - p4.setFirstName("John"); - p4.setAge(41); - template.insert(p4); - - List l1 = new ArrayList<>(); - l1.add(11); - l1.add(21); - l1.add(41); - Query q1 = new Query(Criteria.where("age").in(l1)); - List results1 = template.find(q1, PersonWithIdPropertyOfTypeObjectId.class); - Query q2 = new Query(Criteria.where("age").in(l1.toArray())); - List results2 = template.find(q2, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(results1.size()).isEqualTo(3); - assertThat(results2.size()).isEqualTo(3); - try { - List l2 = new ArrayList<>(); - l2.add(31); - Query q3 = new Query(Criteria.where("age").in(l1, l2)); - template.find(q3, PersonWithIdPropertyOfTypeObjectId.class); - fail("Should have trown an InvalidDocumentStoreApiUsageException"); - } catch (InvalidMongoDbApiUsageException e) {} - } - - @Test - public void testUsingRegexQueryWithOptions() throws Exception { - - template.remove(new Query(), PersonWithIdPropertyOfTypeObjectId.class); - - PersonWithIdPropertyOfTypeObjectId p1 = new PersonWithIdPropertyOfTypeObjectId(); - p1.setFirstName("Sven"); - p1.setAge(11); - template.insert(p1); - PersonWithIdPropertyOfTypeObjectId p2 = new PersonWithIdPropertyOfTypeObjectId(); - p2.setFirstName("Mary"); - p2.setAge(21); - template.insert(p2); - PersonWithIdPropertyOfTypeObjectId p3 = new PersonWithIdPropertyOfTypeObjectId(); - p3.setFirstName("Ann"); - p3.setAge(31); - template.insert(p3); - PersonWithIdPropertyOfTypeObjectId p4 = new PersonWithIdPropertyOfTypeObjectId(); - p4.setFirstName("samantha"); - p4.setAge(41); - template.insert(p4); - - Query q1 = new Query(Criteria.where("firstName").regex("S.*")); - List results1 = template.find(q1, PersonWithIdPropertyOfTypeObjectId.class); - Query q2 = new Query(Criteria.where("firstName").regex("S.*", "i")); - List results2 = template.find(q2, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(results1.size()).isEqualTo(1); - assertThat(results2.size()).isEqualTo(2); - } - - @Test - public void testUsingAnOrQuery() throws Exception { - - template.remove(new Query(), PersonWithIdPropertyOfTypeObjectId.class); - - PersonWithIdPropertyOfTypeObjectId p1 = new PersonWithIdPropertyOfTypeObjectId(); - p1.setFirstName("Sven"); - p1.setAge(11); - template.insert(p1); - PersonWithIdPropertyOfTypeObjectId p2 = new PersonWithIdPropertyOfTypeObjectId(); - p2.setFirstName("Mary"); - p2.setAge(21); - template.insert(p2); - PersonWithIdPropertyOfTypeObjectId p3 = new PersonWithIdPropertyOfTypeObjectId(); - p3.setFirstName("Ann"); - p3.setAge(31); - template.insert(p3); - PersonWithIdPropertyOfTypeObjectId p4 = new PersonWithIdPropertyOfTypeObjectId(); - p4.setFirstName("John"); - p4.setAge(41); - template.insert(p4); - - Query orQuery = new Query(new Criteria().orOperator(where("age").in(11, 21), where("age").is(31))); - List results = template.find(orQuery, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(results.size()).isEqualTo(3); - for (PersonWithIdPropertyOfTypeObjectId p : results) { - assertThat(p.getAge()).isIn(11, 21, 31); - } - } - - @Test - public void testUsingUpdateWithMultipleSet() throws Exception { - - template.remove(new Query(), PersonWithIdPropertyOfTypeObjectId.class); - - PersonWithIdPropertyOfTypeObjectId p1 = new PersonWithIdPropertyOfTypeObjectId(); - p1.setFirstName("Sven"); - p1.setAge(11); - template.insert(p1); - PersonWithIdPropertyOfTypeObjectId p2 = new PersonWithIdPropertyOfTypeObjectId(); - p2.setFirstName("Mary"); - p2.setAge(21); - template.insert(p2); - - Update u = new Update().set("firstName", "Bob").set("age", 10); - - UpdateResult wr = template.updateMulti(new Query(), u, PersonWithIdPropertyOfTypeObjectId.class); - - if (wr.wasAcknowledged()) { - assertThat(wr.getModifiedCount()).isEqualTo(2L); - } - - Query q1 = new Query(Criteria.where("age").in(11, 21)); - List r1 = template.find(q1, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(r1.size()).isEqualTo(0); - Query q2 = new Query(Criteria.where("age").is(10)); - List r2 = template.find(q2, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(r2.size()).isEqualTo(2); - for (PersonWithIdPropertyOfTypeObjectId p : r2) { - assertThat(p.getAge()).isEqualTo(10); - assertThat(p.getFirstName()).isEqualTo("Bob"); - } - } - - @Test - public void testRemovingDocument() throws Exception { - - PersonWithIdPropertyOfTypeObjectId p1 = new PersonWithIdPropertyOfTypeObjectId(); - p1.setFirstName("Sven_to_be_removed"); - p1.setAge(51); - template.insert(p1); - - Query q1 = new Query(Criteria.where("id").is(p1.getId())); - PersonWithIdPropertyOfTypeObjectId found1 = template.findOne(q1, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(found1).isNotNull(); - Query _q = new Query(Criteria.where("_id").is(p1.getId())); - template.remove(_q, PersonWithIdPropertyOfTypeObjectId.class); - PersonWithIdPropertyOfTypeObjectId notFound1 = template.findOne(q1, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(notFound1).isNull(); - - PersonWithIdPropertyOfTypeObjectId p2 = new PersonWithIdPropertyOfTypeObjectId(); - p2.setFirstName("Bubba_to_be_removed"); - p2.setAge(51); - template.insert(p2); - - Query q2 = new Query(Criteria.where("id").is(p2.getId())); - PersonWithIdPropertyOfTypeObjectId found2 = template.findOne(q2, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(found2).isNotNull(); - template.remove(q2, PersonWithIdPropertyOfTypeObjectId.class); - PersonWithIdPropertyOfTypeObjectId notFound2 = template.findOne(q2, PersonWithIdPropertyOfTypeObjectId.class); - assertThat(notFound2).isNull(); - } - - @Test - public void testAddingToList() { - PersonWithAList p = new PersonWithAList(); - p.setFirstName("Sven"); - p.setAge(22); - template.insert(p); - - Query q1 = new Query(Criteria.where("id").is(p.getId())); - PersonWithAList p2 = template.findOne(q1, PersonWithAList.class); - assertThat(p2).isNotNull(); - assertThat(p2.getWishList().size()).isEqualTo(0); - - p2.addToWishList("please work!"); - - template.save(p2); - - PersonWithAList p3 = template.findOne(q1, PersonWithAList.class); - assertThat(p3).isNotNull(); - assertThat(p3.getWishList().size()).isEqualTo(1); - - Friend f = new Friend(); - p.setFirstName("Erik"); - p.setAge(21); - - p3.addFriend(f); - template.save(p3); - - PersonWithAList p4 = template.findOne(q1, PersonWithAList.class); - assertThat(p4).isNotNull(); - assertThat(p4.getWishList().size()).isEqualTo(1); - assertThat(p4.getFriends().size()).isEqualTo(1); - - } - - @Test - public void testFindOneWithSort() { - PersonWithAList p = new PersonWithAList(); - p.setFirstName("Sven"); - p.setAge(22); - template.insert(p); - - PersonWithAList p2 = new PersonWithAList(); - p2.setFirstName("Erik"); - p2.setAge(21); - template.insert(p2); - - PersonWithAList p3 = new PersonWithAList(); - p3.setFirstName("Mark"); - p3.setAge(40); - template.insert(p3); - - // test query with a sort - Query q2 = new Query(Criteria.where("age").gt(10)); - q2.with(Sort.by(Direction.DESC, "age")); - PersonWithAList p5 = template.findOne(q2, PersonWithAList.class); - assertThat(p5.getFirstName()).isEqualTo("Mark"); - } - - @Test // DATAMONGO-2572 - public void testUsingReadPreference() throws Exception { - this.template.execute("readPref", new CollectionCallback() { - public Object doInCollection(MongoCollection collection) - throws MongoException, DataAccessException { - - // assertThat(collection.getOptions(), is(0)); - // assertThat(collection.read.getDB().getOptions(), is(0)); - return null; - } - }); - MongoTemplate secondaryTemplate = new MongoTemplate(factory); - secondaryTemplate.setReadPreference(ReadPreference.secondary()); - secondaryTemplate.execute("readPref", new CollectionCallback() { - public Object doInCollection(MongoCollection collection) - throws MongoException, DataAccessException { - assertThat(collection.getReadPreference()).isEqualTo(ReadPreference.secondary()); - // assertThat(collection.getDB().getOptions(), is(0)); - return null; - } - }); - } - - @Test // DATADOC-166, DATAMONGO-1762 - public void removingNullIsANoOp() { - assertThatIllegalArgumentException().isThrownBy(() -> template.remove((Object) null)); - } - - @Test // DATADOC-240, DATADOC-212 - public void updatesObjectIdsCorrectly() { - - PersonWithIdPropertyOfTypeObjectId person = new PersonWithIdPropertyOfTypeObjectId(); - person.setId(new ObjectId()); - person.setFirstName("Dave"); - - template.save(person); - template.updateFirst(query(where("id").is(person.getId())), update("firstName", "Carter"), - PersonWithIdPropertyOfTypeObjectId.class); - - PersonWithIdPropertyOfTypeObjectId result = template.findById(person.getId(), - PersonWithIdPropertyOfTypeObjectId.class); - assertThat(result).isNotNull(); - assertThat(result.getId()).isEqualTo(person.getId()); - assertThat(result.getFirstName()).isEqualTo("Carter"); - } - - @Test - public void testWriteConcernResolver() { - - PersonWithIdPropertyOfTypeObjectId person = new PersonWithIdPropertyOfTypeObjectId(); - person.setId(new ObjectId()); - person.setFirstName("Dave"); - - template.setWriteConcern(WriteConcern.UNACKNOWLEDGED); - template.save(person); - template.updateFirst(query(where("id").is(person.getId())), update("firstName", "Carter"), - PersonWithIdPropertyOfTypeObjectId.class); - - FsyncSafeWriteConcernResolver resolver = new FsyncSafeWriteConcernResolver(); - template.setWriteConcernResolver(resolver); - Query q = query(where("_id").is(person.getId())); - Update u = update("firstName", "Carter"); - template.updateFirst(q, u, PersonWithIdPropertyOfTypeObjectId.class); - - MongoAction lastMongoAction = resolver.getMongoAction(); - assertThat(lastMongoAction.getCollectionName()).isEqualTo("personWithIdPropertyOfTypeObjectId"); - assertThat(lastMongoAction.getDefaultWriteConcern()).isEqualTo(WriteConcern.UNACKNOWLEDGED); - assertThat(lastMongoAction.getDocument()).isNotNull(); - assertThat(lastMongoAction.getEntityType().toString()) - .isEqualTo(PersonWithIdPropertyOfTypeObjectId.class.toString()); - assertThat(lastMongoAction.getMongoActionOperation()).isEqualTo(MongoActionOperation.UPDATE); - assertThat(lastMongoAction.getQuery()).isEqualTo(q.getQueryObject()); - } - - private class FsyncSafeWriteConcernResolver implements WriteConcernResolver { - - private MongoAction mongoAction; - - public WriteConcern resolve(MongoAction action) { - this.mongoAction = action; - return WriteConcern.JOURNALED; - } - - public MongoAction getMongoAction() { - return mongoAction; - } - } - - @Test // DATADOC-246 - public void updatesDBRefsCorrectly() { - - DBRef first = new DBRef("foo", new ObjectId()); - DBRef second = new DBRef("bar", new ObjectId()); - - template.updateFirst(new Query(), update("dbRefs", Arrays.asList(first, second)), ClassWithDBRefs.class); - } - - class ClassWithDBRefs { - List dbrefs; - } - - @Test // DATADOC-202 - public void executeDocument() { - - template.insert(new Person("Tom")); - template.insert(new Person("Dick")); - template.insert(new Person("Harry")); - final List names = new ArrayList<>(); - template.executeQuery(new Query(), template.getCollectionName(Person.class), new DocumentCallbackHandler() { - public void processDocument(org.bson.Document document) { - String name = (String) document.get("firstName"); - if (name != null) { - names.add(name); - } - } - }); - assertThat(names.size()).isEqualTo(3); - // template.remove(new Query(), Person.class); - } - - @Test // DATADOC-202 - public void executeDocumentWithCursorPreparer() { - template.insert(new Person("Tom")); - template.insert(new Person("Dick")); - template.insert(new Person("Harry")); - final List names = new ArrayList<>(); - template.executeQuery(new Query(), template.getCollectionName(Person.class), new DocumentCallbackHandler() { - public void processDocument(org.bson.Document document) { - String name = (String) document.get("firstName"); - if (name != null) { - names.add(name); - } - } - }, new CursorPreparer() { - - public FindIterable prepare(FindIterable iterable) { - iterable.limit(1); - return iterable; - } - - }); - assertThat(names.size()).isEqualTo(1); - template.remove(new Query(), Person.class); - } - - @Test // DATADOC-183 - public void countsDocumentsCorrectly() { - - assertThat(template.count(new Query(), Person.class)).isEqualTo(0L); - - Person dave = new Person("Dave"); - Person carter = new Person("Carter"); - - template.save(dave); - template.save(carter); - - assertThat(template.count(new Query(), Person.class)).isEqualTo(2L); - assertThat(template.count(query(where("firstName").is("Carter")), Person.class)).isEqualTo(1L); - } - - @Test // DATADOC-183 - public void countRejectsNullEntityClass() { - assertThatIllegalArgumentException().isThrownBy(() -> template.count(null, (Class) null)); - } - - @Test // DATADOC-183 - public void countRejectsEmptyCollectionName() { - assertThatIllegalArgumentException().isThrownBy(() -> template.count(null, "")); - } - - @Test // DATADOC-183 - public void countRejectsNullCollectionName() { - assertThatIllegalArgumentException().isThrownBy(() -> template.count(null, (String) null)); - } - - @Test - public void returnsEntityWhenQueryingForDateTime() { - - DateTime dateTime = new DateTime(2011, 3, 3, 12, 0, 0, 0); - TestClass testClass = new TestClass(dateTime); - mappingTemplate.save(testClass); - - List testClassList = mappingTemplate.find(new Query(Criteria.where("myDate").is(dateTime.toDate())), - TestClass.class); - assertThat(testClassList.size()).isEqualTo(1); - assertThat(testClassList.get(0).myDate).isEqualTo(testClass.myDate); - } - - @Test // DATADOC-230 - public void removesEntityFromCollection() { - - template.remove(new Query(), "mycollection"); - - Person person = new Person("Dave"); - - template.save(person, "mycollection"); - assertThat(template.findAll(TestClass.class, "mycollection").size()).isEqualTo(1); - - template.remove(person, "mycollection"); - assertThat(template.findAll(Person.class, "mycollection").isEmpty()).isTrue(); - } - - @Test // DATADOC-349 - public void removesEntityWithAnnotatedIdIfIdNeedsMassaging() { - - String id = new ObjectId().toString(); - - Sample sample = new Sample(); - sample.id = id; - - template.save(sample); - - assertThat(template.findOne(query(where("id").is(id)), Sample.class).id).isEqualTo(id); - - template.remove(sample); - assertThat(template.findOne(query(where("id").is(id)), Sample.class)).isNull(); - } - - @Test // DATAMONGO-423 - public void executesQueryWithNegatedRegexCorrectly() { - - Sample first = new Sample(); - first.field = "Matthews"; - - Sample second = new Sample(); - second.field = "Beauford"; - - template.save(first); - template.save(second); - - Query query = query(where("field").not().regex("Matthews")); - - List result = template.find(query, Sample.class); - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).field).isEqualTo("Beauford"); - } - - @Test // DATAMONGO-447 - public void storesAndRemovesTypeWithComplexId() { - - MyId id = new MyId(); - id.first = "foo"; - id.second = "bar"; - - TypeWithMyId source = new TypeWithMyId(); - source.id = id; - - template.save(source); - template.remove(query(where("id").is(id)), TypeWithMyId.class); - } - - @Test // DATAMONGO-506 - public void exceutesBasicQueryCorrectly() { - - Address address = new Address(); - address.state = "PA"; - address.city = "Philadelphia"; - - MyPerson person = new MyPerson(); - person.name = "Oleg"; - person.address = address; - - template.save(person); - - Query query = new BasicQuery("{'name' : 'Oleg'}"); - List result = template.find(query, MyPerson.class); - - assertThat(result).hasSize(1); - assertThat(result.get(0).getName()).isEqualTo("Oleg"); - - query = new BasicQuery("{'address.state' : 'PA' }"); - result = template.find(query, MyPerson.class); - - assertThat(result).hasSize(1); - assertThat(result.get(0).getName()).isEqualTo("Oleg"); - } - - @Test // DATAMONGO-279 - public void optimisticLockingHandling() { - - // Init version - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.age = 29; - person.firstName = "Patryk"; - template.save(person); - - List result = template - .findAll(PersonWithVersionPropertyOfTypeInteger.class); - - assertThat(result).hasSize(1); - assertThat(result.get(0).version).isEqualTo(0); - - // Version change - person = result.get(0); - person.firstName = "Patryk2"; - - template.save(person); - - assertThat(person.version).isEqualTo(1); - - result = mappingTemplate.findAll(PersonWithVersionPropertyOfTypeInteger.class); - - assertThat(result).hasSize(1); - assertThat(result.get(0).version).isEqualTo(1); - - // Optimistic lock exception - person.version = 0; - person.firstName = "Patryk3"; - - final PersonWithVersionPropertyOfTypeInteger toBeSaved = person; - - assertThatExceptionOfType(OptimisticLockingFailureException.class).isThrownBy(() -> template.save(toBeSaved)); - } - - @Test // DATAMONGO-562 - public void optimisticLockingHandlingWithExistingId() { - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.id = new ObjectId().toString(); - person.age = 29; - person.firstName = "Patryk"; - template.save(person); - } - - @Test // DATAMONGO-617 - public void doesNotFailOnVersionInitForUnversionedEntity() { - - org.bson.Document document = new org.bson.Document(); - document.put("firstName", "Oliver"); - - template.insert(document, template.getCollectionName(PersonWithVersionPropertyOfTypeInteger.class)); - } - - @Test // DATAMONGO-1617 - public void doesNotFailOnInsertForEntityWithNonAutogeneratableId() { - - PersonWithIdPropertyOfTypeUUID person = new PersonWithIdPropertyOfTypeUUID(); - person.setFirstName("Laszlo"); - person.setAge(33); - - template.insert(person); - assertThat(person.getId()).isNotNull(); - } - - @Test // DATAMONGO-539 - public void removesObjectFromExplicitCollection() { - - String collectionName = "explicit"; - template.remove(new Query(), collectionName); - - PersonWithConvertedId person = new PersonWithConvertedId(); - person.name = "Dave"; - template.save(person, collectionName); - assertThat(template.findAll(PersonWithConvertedId.class, collectionName).isEmpty()).isFalse(); - - template.remove(person, collectionName); - assertThat(template.findAll(PersonWithConvertedId.class, collectionName).isEmpty()).isTrue(); - } - - // DATAMONGO-549 - public void savesMapCorrectly() { - - Map map = new HashMap<>(); - map.put("key", "value"); - - template.save(map, "maps"); - } - - @Test // DATAMONGO-549, DATAMONGO-1730 - public void savesMongoPrimitiveObjectCorrectly() { - assertThatExceptionOfType(MappingException.class).isThrownBy(() -> template.save(new Object(), "collection")); - } - - @Test // DATAMONGO-549 - public void rejectsNullObjectToBeSaved() { - assertThatIllegalArgumentException().isThrownBy(() -> template.save(null)); - } - - @Test // DATAMONGO-550 - public void savesPlainDocumentCorrectly() { - - org.bson.Document document = new org.bson.Document("foo", "bar"); - template.save(document, "collection"); - - assertThat(document.containsKey("_id")).isTrue(); - } - - @Test // DATAMONGO-550, DATAMONGO-1730 - public void rejectsPlainObjectWithOutExplicitCollection() { - - org.bson.Document document = new org.bson.Document("foo", "bar"); - template.save(document, "collection"); - - assertThatExceptionOfType(MappingException.class) - .isThrownBy(() -> template.findById(document.get("_id"), org.bson.Document.class)); - } - - @Test // DATAMONGO-550 - public void readsPlainDocumentById() { - - org.bson.Document document = new org.bson.Document("foo", "bar"); - template.save(document, "collection"); - - org.bson.Document result = template.findById(document.get("_id"), org.bson.Document.class, "collection"); - assertThat(result.get("foo")).isEqualTo(document.get("foo")); - assertThat(result.get("_id")).isEqualTo(document.get("_id")); - } - - @Test // DATAMONGO-551 - public void writesPlainString() { - template.save("{ 'foo' : 'bar' }", "collection"); - } - - @Test // DATAMONGO-551 - public void rejectsNonJsonStringForSave() { - assertThatExceptionOfType(MappingException.class).isThrownBy(() -> template.save("Foobar!", "collection")); - } - - @Test // DATAMONGO-588 - public void initializesVersionOnInsert() { - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.insert(person); - - assertThat(person.version).isEqualTo(0); - } - - @Test // DATAMONGO-2195 - public void removeVersionedEntityConsidersVersion() { - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.insert(person); - assertThat(person.version).isEqualTo(0); - template.update(PersonWithVersionPropertyOfTypeInteger.class).matching(query(where("id").is(person.id))) - .apply(new Update().set("firstName", "Walter")).first(); - - DeleteResult deleteResult = template.remove(person); - - assertThat(deleteResult.wasAcknowledged()).isTrue(); - assertThat(deleteResult.getDeletedCount()).isZero(); - assertThat(template.count(new Query(), PersonWithVersionPropertyOfTypeInteger.class)).isOne(); - } - - @Test // DATAMONGO-588 - public void initializesVersionOnBatchInsert() { - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.insertAll(Arrays.asList(person)); - - assertThat(person.version).isEqualTo(0); - } - - @Test // DATAMONGO-1992 - public void initializesIdAndVersionAndOfImmutableObject() { - - ImmutableVersioned versioned = new ImmutableVersioned(); - - ImmutableVersioned saved = template.insert(versioned); - - assertThat(saved).isNotSameAs(versioned); - assertThat(versioned.id).isNull(); - assertThat(versioned.version).isNull(); - - assertThat(saved.id).isNotNull(); - assertThat(saved.version).isEqualTo(0L); - } - - @Test // DATAMONGO-568, DATAMONGO-1762 - public void queryCantBeNull() { - assertThatIllegalArgumentException() - .isThrownBy(() -> template.find(null, PersonWithIdPropertyOfTypeObjectId.class)); - } - - @Test // DATAMONGO-620 - public void versionsObjectIntoDedicatedCollection() { - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.save(person, "personX"); - assertThat(person.version).isEqualTo(0); - - template.save(person, "personX"); - assertThat(person.version).isEqualTo(1); - } - - @Test // DATAMONGO-621 - public void correctlySetsLongVersionProperty() { - - PersonWithVersionPropertyOfTypeLong person = new PersonWithVersionPropertyOfTypeLong(); - person.firstName = "Dave"; - - template.save(person); - assertThat(person.version).isEqualTo(0L); - } - - @Test // DATAMONGO-622 - public void preventsDuplicateInsert() { - - template.setWriteConcern(WriteConcern.ACKNOWLEDGED); - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.save(person); - assertThat(person.version).isEqualTo(0); - - person.version = null; - assertThatExceptionOfType(DuplicateKeyException.class).isThrownBy(() -> template.save(person)); - } - - @Test // DATAMONGO-629 - public void countAndFindWithoutTypeInformation() { - - Person person = new Person(); - template.save(person); - - Query query = query(where("_id").is(person.getId())); - String collectionName = template.getCollectionName(Person.class); - - assertThat(template.find(query, HashMap.class, collectionName)).hasSize(1); - assertThat(template.count(query, collectionName)).isEqualTo(1L); - } - - @Test // DATAMONGO-571 - public void nullsPropertiesForVersionObjectUpdates() { - - VersionedPerson person = new VersionedPerson(); - person.firstname = "Dave"; - person.lastname = "Matthews"; - - template.save(person); - assertThat(person.id).isNotNull(); - - person.lastname = null; - template.save(person); - - person = template.findOne(query(where("id").is(person.id)), VersionedPerson.class); - assertThat(person.lastname).isNull(); - } - - @Test // DATAMONGO-571 - public void nullsValuesForUpdatesOfUnversionedEntity() { - - Person person = new Person("Dave"); - template.save(person); - - person.setFirstName(null); - template.save(person); - - person = template.findOne(query(where("id").is(person.getId())), Person.class); - assertThat(person.getFirstName()).isNull(); - } - - @Test // DATAMONGO-679 - public void savesJsonStringCorrectly() { - - org.bson.Document document = new org.bson.Document().append("first", "first").append("second", "second"); - - template.save(document, "collection"); - - List result = template.findAll(org.bson.Document.class, "collection"); - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).containsKey("first")).isTrue(); - } - - @Test - public void executesExistsCorrectly() { - - Sample sample = new Sample(); - template.save(sample); - - Query query = query(where("id").is(sample.id)); - - assertThat(template.exists(query, Sample.class)).isTrue(); - assertThat(template.exists(query(where("_id").is(sample.id)), template.getCollectionName(Sample.class))).isTrue(); - assertThat(template.exists(query, Sample.class, template.getCollectionName(Sample.class))).isTrue(); - } - - @Test // DATAMONGO-675 - public void updateConsidersMappingAnnotations() { - - TypeWithFieldAnnotation entity = new TypeWithFieldAnnotation(); - entity.emailAddress = "old"; - - template.save(entity); - - Query query = query(where("_id").is(entity.id)); - Update update = Update.update("emailAddress", "new"); - - FindAndModifyOptions options = new FindAndModifyOptions().returnNew(true); - TypeWithFieldAnnotation result = template.findAndModify(query, update, options, TypeWithFieldAnnotation.class); - assertThat(result.emailAddress).isEqualTo("new"); - } - - @Test // DATAMONGO-671 - public void findsEntityByDateReference() { - - TypeWithDate entity = new TypeWithDate(); - entity.date = new Date(System.currentTimeMillis() - 10); - template.save(entity); - - Query query = query(where("date").lt(new Date())); - List result = template.find(query, TypeWithDate.class); - - assertThat(result).hasSize(1); - assertThat(result.get(0).date).isNotNull(); - } - - @Test // DATAMONGO-540 - public void findOneAfterUpsertForNonExistingObjectReturnsTheInsertedObject() { - - String idValue = "4711"; - Query query = new Query(Criteria.where("id").is(idValue)); - - String fieldValue = "bubu"; - Update update = Update.update("field", fieldValue); - - template.upsert(query, update, Sample.class); - Sample result = template.findOne(query, Sample.class); - - assertThat(result).isNotNull(); - assertThat(result.field).isEqualTo(fieldValue); - assertThat(result.id).isEqualTo(idValue); - } - - @Test // DATAMONGO-392 - public void updatesShouldRetainTypeInformation() { - - Document doc = new Document(); - doc.id = "4711"; - doc.model = new ModelA("foo"); - template.insert(doc); - - Query query = new Query(Criteria.where("id").is(doc.id)); - String newModelValue = "bar"; - Update update = Update.update("model", new ModelA(newModelValue)); - template.updateFirst(query, update, Document.class); - - Document result = template.findOne(query, Document.class); - - assertThat(result).isNotNull(); - assertThat(result.id).isEqualTo(doc.id); - assertThat(result.model).isNotNull(); - assertThat(result.model.value()).isEqualTo(newModelValue); - } - - @Test // DATAMONGO-702 - public void queryShouldSupportRealAndAliasedPropertyNamesForFieldInclusions() { - - ObjectWith3AliasedFields obj = new ObjectWith3AliasedFields(); - obj.id = "4711"; - obj.property1 = "P1"; - obj.property2 = "P2"; - obj.property3 = "P3"; - - template.insert(obj); - - Query query = new Query(Criteria.where("id").is(obj.id)); - query.fields() // - .include("property2") // real property name - .include("prop3"); // aliased property name - - ObjectWith3AliasedFields result = template.findOne(query, ObjectWith3AliasedFields.class); - - assertThat(result.id).isEqualTo(obj.id); - assertThat(result.property1).isNull(); - assertThat(result.property2).isEqualTo(obj.property2); - assertThat(result.property3).isEqualTo(obj.property3); - } - - @Test // DATAMONGO-702, DATAMONGO-2294 - public void queryShouldSupportRealAndAliasedPropertyNamesForFieldExclusions() { - - ObjectWith3AliasedFields obj = new ObjectWith3AliasedFields(); - obj.id = "4711"; - obj.property1 = "P1"; - obj.property2 = "P2"; - obj.property3 = "P3"; - - template.insert(obj); - - Query query = new Query(Criteria.where("id").is(obj.id)); - query.fields() // - .exclude("property2", "prop3"); // real property name, aliased property name - - ObjectWith3AliasedFields result = template.findOne(query, ObjectWith3AliasedFields.class); - - assertThat(result.id).isEqualTo(obj.id); - assertThat(result.property1).isEqualTo(obj.property1); - assertThat(result.property2).isNull(); - assertThat(result.property3).isNull(); - } - - @Test // DATAMONGO-702 - public void findMultipleWithQueryShouldSupportRealAndAliasedPropertyNamesForFieldExclusions() { - - ObjectWith3AliasedFields obj0 = new ObjectWith3AliasedFields(); - obj0.id = "4711"; - obj0.property1 = "P10"; - obj0.property2 = "P20"; - obj0.property3 = "P30"; - ObjectWith3AliasedFields obj1 = new ObjectWith3AliasedFields(); - obj1.id = "4712"; - obj1.property1 = "P11"; - obj1.property2 = "P21"; - obj1.property3 = "P31"; - - template.insert(obj0); - template.insert(obj1); - - Query query = new Query(Criteria.where("id").in(obj0.id, obj1.id)); - query.fields() // - .exclude("property2") // real property name - .exclude("prop3"); // aliased property name - - List results = template.find(query, ObjectWith3AliasedFields.class); - - assertThat(results).isNotNull(); - assertThat(results.size()).isEqualTo(2); - - ObjectWith3AliasedFields result0 = results.get(0); - assertThat(result0).isNotNull(); - assertThat(result0.id).isEqualTo(obj0.id); - assertThat(result0.property1).isEqualTo(obj0.property1); - assertThat(result0.property2).isNull(); - assertThat(result0.property3).isNull(); - - ObjectWith3AliasedFields result1 = results.get(1); - assertThat(result1).isNotNull(); - assertThat(result1.id).isEqualTo(obj1.id); - assertThat(result1.property1).isEqualTo(obj1.property1); - assertThat(result1.property2).isNull(); - assertThat(result1.property3).isNull(); - } - - @Test // DATAMONGO-702 - public void queryShouldSupportNestedPropertyNamesForFieldInclusions() { - - ObjectWith3AliasedFieldsAndNestedAddress obj = new ObjectWith3AliasedFieldsAndNestedAddress(); - obj.id = "4711"; - obj.property1 = "P1"; - obj.property2 = "P2"; - obj.property3 = "P3"; - Address address = new Address(); - String stateValue = "WA"; - address.state = stateValue; - address.city = "Washington"; - obj.address = address; - - template.insert(obj); - - Query query = new Query(Criteria.where("id").is(obj.id)); - query.fields() // - .include("property2") // real property name - .include("address.state"); // aliased property name - - ObjectWith3AliasedFieldsAndNestedAddress result = template.findOne(query, - ObjectWith3AliasedFieldsAndNestedAddress.class); - - assertThat(result.id).isEqualTo(obj.id); - assertThat(result.property1).isNull(); - assertThat(result.property2).isEqualTo(obj.property2); - assertThat(result.property3).isNull(); - assertThat(result.address).isNotNull(); - assertThat(result.address.city).isNull(); - assertThat(result.address.state).isEqualTo(stateValue); - } - - @Test // DATAMONGO-709 - public void aQueryRestrictedWithOneRestrictedResultTypeShouldReturnOnlyInstancesOfTheRestrictedType() { - - BaseDoc doc0 = new BaseDoc(); - doc0.value = "foo"; - SpecialDoc doc1 = new SpecialDoc(); - doc1.value = "foo"; - doc1.specialValue = "specialfoo"; - VerySpecialDoc doc2 = new VerySpecialDoc(); - doc2.value = "foo"; - doc2.specialValue = "specialfoo"; - doc2.verySpecialValue = 4711; - - String collectionName = template.getCollectionName(BaseDoc.class); - template.insert(doc0, collectionName); - template.insert(doc1, collectionName); - template.insert(doc2, collectionName); - - Query query = Query.query(where("value").is("foo")).restrict(SpecialDoc.class); - List result = template.find(query, BaseDoc.class); - - assertThat(result).isNotNull(); - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0)).isInstanceOf(SpecialDoc.class); - } - - @Test // DATAMONGO-709 - public void aQueryRestrictedWithMultipleRestrictedResultTypesShouldReturnOnlyInstancesOfTheRestrictedTypes() { - - BaseDoc doc0 = new BaseDoc(); - doc0.value = "foo"; - SpecialDoc doc1 = new SpecialDoc(); - doc1.value = "foo"; - doc1.specialValue = "specialfoo"; - VerySpecialDoc doc2 = new VerySpecialDoc(); - doc2.value = "foo"; - doc2.specialValue = "specialfoo"; - doc2.verySpecialValue = 4711; - - String collectionName = template.getCollectionName(BaseDoc.class); - template.insert(doc0, collectionName); - template.insert(doc1, collectionName); - template.insert(doc2, collectionName); - - Query query = Query.query(where("value").is("foo")).restrict(BaseDoc.class, VerySpecialDoc.class); - List result = template.find(query, BaseDoc.class); - - assertThat(result).isNotNull(); - assertThat(result.size()).isEqualTo(2); - assertThat(result.get(0).getClass()).isEqualTo((Object) BaseDoc.class); - assertThat(result.get(1).getClass()).isEqualTo((Object) VerySpecialDoc.class); - } - - @Test // DATAMONGO-709 - public void aQueryWithNoRestrictedResultTypesShouldReturnAllInstancesWithinTheGivenCollection() { - - BaseDoc doc0 = new BaseDoc(); - doc0.value = "foo"; - SpecialDoc doc1 = new SpecialDoc(); - doc1.value = "foo"; - doc1.specialValue = "specialfoo"; - VerySpecialDoc doc2 = new VerySpecialDoc(); - doc2.value = "foo"; - doc2.specialValue = "specialfoo"; - doc2.verySpecialValue = 4711; - - String collectionName = template.getCollectionName(BaseDoc.class); - template.insert(doc0, collectionName); - template.insert(doc1, collectionName); - template.insert(doc2, collectionName); - - Query query = Query.query(where("value").is("foo")); - List result = template.find(query, BaseDoc.class); - - assertThat(result).isNotNull(); - assertThat(result.size()).isEqualTo(3); - assertThat(result.get(0).getClass()).isEqualTo((Object) BaseDoc.class); - assertThat(result.get(1).getClass()).isEqualTo((Object) SpecialDoc.class); - assertThat(result.get(2).getClass()).isEqualTo((Object) VerySpecialDoc.class); - } - - @Test // DATAMONGO-771 - public void allowInsertWithPlainJsonString() { - - String id = "4711"; - String value = "bubu"; - String json = String.format("{_id:%s, field: '%s'}", id, value); - - template.insert(json, "sample"); - List result = template.findAll(Sample.class); - - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).id).isEqualTo(id); - assertThat(result.get(0).field).isEqualTo(value); - } - - @Test // DATAMONGO-2028 - public void allowInsertOfDbObjectWithMappedTypes() { - - DBObject dbObject = new BasicDBObject("_id", "foo").append("duration", Duration.ofSeconds(100)); - template.insert(dbObject, "sample"); - List result = template.findAll(org.bson.Document.class, "sample"); - - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).getString("_id")).isEqualTo("foo"); - assertThat(result.get(0).getString("duration")).isEqualTo("PT1M40S"); - } - - @Test // DATAMONGO-816 - public void shouldExecuteQueryShouldMapQueryBeforeQueryExecution() { - - ObjectWithEnumValue o = new ObjectWithEnumValue(); - o.value = EnumValue.VALUE2; - template.save(o); - - Query q = Query.query(Criteria.where("value").in(EnumValue.VALUE2)); - - template.executeQuery(q, StringUtils.uncapitalize(ObjectWithEnumValue.class.getSimpleName()), - new DocumentCallbackHandler() { - - @Override - public void processDocument(org.bson.Document document) throws MongoException, DataAccessException { - - assertThat(document).isNotNull(); - - ObjectWithEnumValue result = template.getConverter().read(ObjectWithEnumValue.class, document); - - assertThat(result.value).isEqualTo(EnumValue.VALUE2); - } - }); - } - - @Test // DATAMONGO-811 - public void updateFirstShouldIncreaseVersionForVersionedEntity() { - - VersionedPerson person = new VersionedPerson(); - person.firstname = "Dave"; - person.lastname = "Matthews"; - template.save(person); - assertThat(person.id).isNotNull(); - - Query qry = query(where("id").is(person.id)); - VersionedPerson personAfterFirstSave = template.findOne(qry, VersionedPerson.class); - assertThat(personAfterFirstSave.version).isEqualTo(0L); - - template.updateFirst(qry, Update.update("lastname", "Bubu"), VersionedPerson.class); - - VersionedPerson personAfterUpdateFirst = template.findOne(qry, VersionedPerson.class); - assertThat(personAfterUpdateFirst.version).isEqualTo(1L); - assertThat(personAfterUpdateFirst.lastname).isEqualTo("Bubu"); - } - - @Test // DATAMONGO-811 - public void updateFirstShouldIncreaseVersionOnlyForFirstMatchingEntity() { - - VersionedPerson person1 = new VersionedPerson(); - person1.firstname = "Dave"; - - VersionedPerson person2 = new VersionedPerson(); - person2.firstname = "Dave"; - - template.save(person1); - template.save(person2); - Query q = query(where("id").in(person1.id, person2.id)); - - template.updateFirst(q, Update.update("lastname", "Metthews"), VersionedPerson.class); - - for (VersionedPerson p : template.find(q, VersionedPerson.class)) { - if ("Metthews".equals(p.lastname)) { - assertThat(p.version).isEqualTo(Long.valueOf(1)); - } else { - assertThat(p.version).isEqualTo(Long.valueOf(0)); - } - } - } - - @Test // DATAMONGO-811 - public void updateMultiShouldIncreaseVersionOfAllUpdatedEntities() { - - VersionedPerson person1 = new VersionedPerson(); - person1.firstname = "Dave"; - - VersionedPerson person2 = new VersionedPerson(); - person2.firstname = "Dave"; - - template.save(person1); - template.save(person2); - - Query q = query(where("id").in(person1.id, person2.id)); - template.updateMulti(q, Update.update("lastname", "Metthews"), VersionedPerson.class); - - for (VersionedPerson p : template.find(q, VersionedPerson.class)) { - assertThat(p.version).isEqualTo(Long.valueOf(1)); - } - } - - @Test // DATAMONGO-686 - public void itShouldBePossibleToReuseAnExistingQuery() { - - Sample sample = new Sample(); - sample.id = "42"; - sample.field = "A"; - - template.save(sample); - - Query query = new Query(); - query.addCriteria(where("_id").in("42", "43")); - - assertThat(template.count(query, Sample.class)).isEqualTo(1L); - - query.with(PageRequest.of(0, 10)); - query.with(Sort.by("field")); - - assertThat(template.find(query, Sample.class)).isNotEmpty(); - } - - @Test // DATAMONGO-807 - public void findAndModifyShouldRetrainTypeInformationWithinUpdatedType() { - - Document document = new Document(); - document.model = new ModelA("value1"); - - template.save(document); - - Query query = query(where("id").is(document.id)); - Update update = Update.update("model", new ModelA("value2")); - template.findAndModify(query, update, Document.class); - - Document retrieved = template.findOne(query, Document.class); - assertThat(retrieved.model).isInstanceOf(ModelA.class); - assertThat(retrieved.model.value()).isEqualTo("value2"); - } - - @Test // DATAMONGO-1210 - public void findAndModifyShouldRetainTypeInformationWithinUpdatedTypeOnDocumentWithNestedCollectionWhenWholeCollectionIsReplaced() { - - DocumentWithNestedCollection doc = new DocumentWithNestedCollection(); - - Map entry = new HashMap<>(); - entry.put("key1", new ModelA("value1")); - doc.models.add(entry); - - template.save(doc); - - entry.put("key2", new ModelA("value2")); - - Query query = query(where("id").is(doc.id)); - Update update = Update.update("models", Collections.singletonList(entry)); - - assertThat(template.findOne(query, DocumentWithNestedCollection.class)).isNotNull(); - - template.findAndModify(query, update, DocumentWithNestedCollection.class); - - DocumentWithNestedCollection retrieved = template.findOne(query, DocumentWithNestedCollection.class); - - assertThat(retrieved).isNotNull(); - assertThat(retrieved.id).isEqualTo(doc.id); - - assertThat(retrieved.models.get(0).entrySet()).hasSize(2); - - assertThat(retrieved.models.get(0).get("key1")).isInstanceOf(ModelA.class); - assertThat(retrieved.models.get(0).get("key1").value()).isEqualTo("value1"); - - assertThat(retrieved.models.get(0).get("key2")).isInstanceOf(ModelA.class); - assertThat(retrieved.models.get(0).get("key2").value()).isEqualTo("value2"); - } - - @Test // DATAMONGO-1210 - public void findAndModifyShouldRetainTypeInformationWithinUpdatedTypeOnDocumentWithNestedCollectionWhenFirstElementIsReplaced() { - - DocumentWithNestedCollection doc = new DocumentWithNestedCollection(); - - Map entry = new HashMap<>(); - entry.put("key1", new ModelA("value1")); - doc.models.add(entry); - - template.save(doc); - - entry.put("key2", new ModelA("value2")); - - Query query = query(where("id").is(doc.id)); - Update update = Update.update("models.0", entry); - - assertThat(template.findOne(query, DocumentWithNestedCollection.class)).isNotNull(); - - template.findAndModify(query, update, DocumentWithNestedCollection.class); - - DocumentWithNestedCollection retrieved = template.findOne(query, DocumentWithNestedCollection.class); - - assertThat(retrieved).isNotNull(); - assertThat(retrieved.id).isEqualTo(doc.id); - - assertThat(retrieved.models.get(0).entrySet()).hasSize(2); - - assertThat(retrieved.models.get(0).get("key1")).isInstanceOf(ModelA.class); - assertThat(retrieved.models.get(0).get("key1").value()).isEqualTo("value1"); - - assertThat(retrieved.models.get(0).get("key2")).isInstanceOf(ModelA.class); - assertThat(retrieved.models.get(0).get("key2").value()).isEqualTo("value2"); - } - - @Test // DATAMONGO-1210 - public void findAndModifyShouldAddTypeInformationOnDocumentWithNestedCollectionObjectInsertedAtSecondIndex() { - - DocumentWithNestedCollection doc = new DocumentWithNestedCollection(); - - Map entry = new HashMap<>(); - entry.put("key1", new ModelA("value1")); - doc.models.add(entry); - - template.save(doc); - - Query query = query(where("id").is(doc.id)); - Update update = Update.update("models.1", Collections.singletonMap("key2", new ModelA("value2"))); - - assertThat(template.findOne(query, DocumentWithNestedCollection.class)).isNotNull(); - - template.findAndModify(query, update, DocumentWithNestedCollection.class); - - DocumentWithNestedCollection retrieved = template.findOne(query, DocumentWithNestedCollection.class); - - assertThat(retrieved).isNotNull(); - assertThat(retrieved.id).isEqualTo(doc.id); - - assertThat(retrieved.models.get(0).entrySet()).hasSize(1); - assertThat(retrieved.models.get(1).entrySet()).hasSize(1); - - assertThat(retrieved.models.get(0).get("key1")).isInstanceOf(ModelA.class); - assertThat(retrieved.models.get(0).get("key1").value()).isEqualTo("value1"); - - assertThat(retrieved.models.get(1).get("key2")).isInstanceOf(ModelA.class); - assertThat(retrieved.models.get(1).get("key2").value()).isEqualTo("value2"); - } - - @Test // DATAMONGO-1210 - public void findAndModifyShouldRetainTypeInformationWithinUpdatedTypeOnEmbeddedDocumentWithCollectionWhenUpdatingPositionedElement() - throws Exception { - - List models = new ArrayList<>(); - models.add(new ModelA("value1")); - - DocumentWithEmbeddedDocumentWithCollection doc = new DocumentWithEmbeddedDocumentWithCollection( - new DocumentWithCollection(models)); - - template.save(doc); - - Query query = query(where("id").is(doc.id)); - Update update = Update.update("embeddedDocument.models.0", new ModelA("value2")); - - assertThat(template.findOne(query, DocumentWithEmbeddedDocumentWithCollection.class)).isNotNull(); - - template.findAndModify(query, update, DocumentWithEmbeddedDocumentWithCollection.class); - - DocumentWithEmbeddedDocumentWithCollection retrieved = template.findOne(query, - DocumentWithEmbeddedDocumentWithCollection.class); - - assertThat(retrieved).isNotNull(); - assertThat(retrieved.embeddedDocument.models).hasSize(1); - assertThat(retrieved.embeddedDocument.models.get(0).value()).isEqualTo("value2"); - } - - @Test // DATAMONGO-1210 - public void findAndModifyShouldAddTypeInformationWithinUpdatedTypeOnEmbeddedDocumentWithCollectionWhenUpdatingSecondElement() - throws Exception { - - List models = new ArrayList<>(); - models.add(new ModelA("value1")); - - DocumentWithEmbeddedDocumentWithCollection doc = new DocumentWithEmbeddedDocumentWithCollection( - new DocumentWithCollection(models)); - - template.save(doc); - - Query query = query(where("id").is(doc.id)); - Update update = Update.update("embeddedDocument.models.1", new ModelA("value2")); - - assertThat(template.findOne(query, DocumentWithEmbeddedDocumentWithCollection.class)).isNotNull(); - - template.findAndModify(query, update, DocumentWithEmbeddedDocumentWithCollection.class); - - DocumentWithEmbeddedDocumentWithCollection retrieved = template.findOne(query, - DocumentWithEmbeddedDocumentWithCollection.class); - - assertThat(retrieved).isNotNull(); - assertThat(retrieved.embeddedDocument.models).hasSize(2); - assertThat(retrieved.embeddedDocument.models.get(0).value()).isEqualTo("value1"); - assertThat(retrieved.embeddedDocument.models.get(1).value()).isEqualTo("value2"); - } - - @Test // DATAMONGO-1210 - public void findAndModifyShouldAddTypeInformationWithinUpdatedTypeOnEmbeddedDocumentWithCollectionWhenRewriting() - throws Exception { - - List models = Arrays. asList(new ModelA("value1")); - - DocumentWithEmbeddedDocumentWithCollection doc = new DocumentWithEmbeddedDocumentWithCollection( - new DocumentWithCollection(models)); - - template.save(doc); - - Query query = query(where("id").is(doc.id)); - Update update = Update.update("embeddedDocument", - new DocumentWithCollection(Arrays. asList(new ModelA("value2")))); - - assertThat(template.findOne(query, DocumentWithEmbeddedDocumentWithCollection.class)).isNotNull(); - - template.findAndModify(query, update, DocumentWithEmbeddedDocumentWithCollection.class); - - DocumentWithEmbeddedDocumentWithCollection retrieved = template.findOne(query, - DocumentWithEmbeddedDocumentWithCollection.class); - - assertThat(retrieved).isNotNull(); - assertThat(retrieved.embeddedDocument.models).hasSize(1); - assertThat(retrieved.embeddedDocument.models.get(0).value()).isEqualTo("value2"); - } - - @Test // DATAMONGO-1210 - public void findAndModifyShouldAddTypeInformationWithinUpdatedTypeOnDocumentWithNestedLists() { - - DocumentWithNestedList doc = new DocumentWithNestedList(); - - List entry = new ArrayList<>(); - entry.add(new ModelA("value1")); - doc.models.add(entry); - - template.save(doc); - - Query query = query(where("id").is(doc.id)); - - assertThat(template.findOne(query, DocumentWithNestedList.class)).isNotNull(); - - Update update = Update.update("models.0.1", new ModelA("value2")); - - template.findAndModify(query, update, DocumentWithNestedList.class); - - DocumentWithNestedList retrieved = template.findOne(query, DocumentWithNestedList.class); - - assertThat(retrieved).isNotNull(); - assertThat(retrieved.id).isEqualTo(doc.id); - - assertThat(retrieved.models.get(0)).hasSize(2); - - assertThat(retrieved.models.get(0).get(0)).isInstanceOf(ModelA.class); - assertThat(retrieved.models.get(0).get(0).value()).isEqualTo("value1"); - - assertThat(retrieved.models.get(0).get(1)).isInstanceOf(ModelA.class); - assertThat(retrieved.models.get(0).get(1).value()).isEqualTo("value2"); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceShouldReplaceDocument() { - - org.bson.Document doc = new org.bson.Document("foo", "bar"); - template.save(doc, "findandreplace"); - - org.bson.Document replacement = new org.bson.Document("foo", "baz"); - org.bson.Document previous = template.findAndReplace(query(where("foo").is("bar")), replacement, - FindAndReplaceOptions.options(), org.bson.Document.class, "findandreplace"); - - assertThat(previous).containsEntry("foo", "bar"); - assertThat(template.findOne(query(where("foo").is("baz")), org.bson.Document.class, "findandreplace")).isNotNull(); - } - - @Test // DATAMONGO-1827 - @MongoVersion(asOf = "3.6") - public void findAndReplaceShouldErrorOnIdPresent() { - - template.save(new MyPerson("Walter")); - - MyPerson replacement = new MyPerson("Heisenberg"); - replacement.id = "invalid-id"; - - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) - .isThrownBy(() -> template.findAndReplace(query(where("name").is("Walter")), replacement)); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceShouldErrorOnSkip() { - - assertThatIllegalArgumentException().isThrownBy( - () -> template.findAndReplace(query(where("name").is("Walter")).skip(10), new MyPerson("Heisenberg"))); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceShouldErrorOnLimit() { - - assertThatIllegalArgumentException().isThrownBy( - () -> template.findAndReplace(query(where("name").is("Walter")).limit(10), new MyPerson("Heisenberg"))); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceShouldConsiderSortAndUpdateFirstIfMultipleFound() { - - MyPerson walter1 = new MyPerson("Walter 1"); - MyPerson walter2 = new MyPerson("Walter 2"); - - template.save(walter1); - template.save(walter2); - - MyPerson replacement = new MyPerson("Heisenberg"); - - template.findAndReplace(query(where("name").regex("Walter.*")).with(Sort.by(Direction.DESC, "name")), replacement); - - assertThat(template.findAll(MyPerson.class)).hasSize(2).contains(walter1).doesNotContain(walter2); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceShouldReplaceObject() { - - MyPerson person = new MyPerson("Walter"); - template.save(person); - - MyPerson previous = template.findAndReplace(query(where("name").is("Walter")), new MyPerson("Heisenberg")); - - assertThat(previous.getName()).isEqualTo("Walter"); - assertThat(template.findOne(query(where("id").is(person.id)), MyPerson.class)).hasFieldOrPropertyWithValue("name", - "Heisenberg"); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceShouldConsiderFields() { - - MyPerson person = new MyPerson("Walter"); - person.address = new Address("TX", "Austin"); - template.save(person); - - Query query = query(where("name").is("Walter")); - query.fields().include("address"); - - MyPerson previous = template.findAndReplace(query, new MyPerson("Heisenberg")); - - assertThat(previous.getName()).isNull(); - assertThat(previous.getAddress()).isEqualTo(person.address); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceNonExistingWithUpsertFalse() { - - MyPerson previous = template.findAndReplace(query(where("name").is("Walter")), new MyPerson("Heisenberg")); - - assertThat(previous).isNull(); - assertThat(template.findAll(MyPerson.class)).isEmpty(); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceNonExistingWithUpsertTrue() { - - MyPerson previous = template.findAndReplace(query(where("name").is("Walter")), new MyPerson("Heisenberg"), - FindAndReplaceOptions.options().upsert()); - - assertThat(previous).isNull(); - assertThat(template.findAll(MyPerson.class)).hasSize(1); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceShouldReplaceObjectReturingNew() { - - MyPerson person = new MyPerson("Walter"); - template.save(person); - - MyPerson updated = template.findAndReplace(query(where("name").is("Walter")), new MyPerson("Heisenberg"), - FindAndReplaceOptions.options().returnNew()); - - assertThat(updated.getName()).isEqualTo("Heisenberg"); - } - - @Test // DATAMONGO-1827 - public void findAndReplaceShouldProjectReturnedObjectCorrectly() { - - template.save(new MyPerson("Walter")); - - MyPersonProjection projection = template.findAndReplace(query(where("name").is("Walter")), - new MyPerson("Heisenberg"), FindAndReplaceOptions.empty(), MyPerson.class, MyPersonProjection.class); - - assertThat(projection.getName()).isEqualTo("Walter"); - } - - @Test // DATAMONGO-407 - public void updatesShouldRetainTypeInformationEvenForCollections() { - - List models = Arrays. asList(new ModelA("foo")); - - DocumentWithCollection doc = new DocumentWithCollection(models); - doc.id = "4711"; - template.insert(doc); - - Query query = new Query(Criteria.where("id").is(doc.id)); - query.addCriteria(where("models.value").is("foo")); - String newModelValue = "bar"; - Update update = Update.update("models.$", new ModelA(newModelValue)); - template.updateFirst(query, update, DocumentWithCollection.class); - - Query findQuery = new Query(Criteria.where("id").is(doc.id)); - DocumentWithCollection result = template.findOne(findQuery, DocumentWithCollection.class); - - assertThat(result).isNotNull(); - assertThat(result.id).isEqualTo(doc.id); - assertThat(result.models).isNotNull(); - assertThat(result.models).hasSize(1); - assertThat(result.models.get(0).value()).isEqualTo(newModelValue); - } - - @Test // DATAMONGO-812 - @MongoVersion(asOf = "2.4") - public void updateMultiShouldAddValuesCorrectlyWhenUsingPushEachWithComplexTypes() { - - DocumentWithCollection document = new DocumentWithCollection(Collections. emptyList()); - template.save(document); - Query query = query(where("id").is(document.id)); - assertThat(template.findOne(query, DocumentWithCollection.class).models).isEmpty(); - - Update update = new Update().push("models").each(new ModelA("model-b"), new ModelA("model-c")); - template.updateMulti(query, update, DocumentWithCollection.class); - - assertThat(template.findOne(query, DocumentWithCollection.class).models).hasSize(2); - } - - @Test // DATAMONGO-812 - @MongoVersion(asOf = "2.4") - public void updateMultiShouldAddValuesCorrectlyWhenUsingPushEachWithSimpleTypes() { - - DocumentWithCollectionOfSimpleType document = new DocumentWithCollectionOfSimpleType(); - document.values = Arrays.asList("spring"); - template.save(document); - - Query query = query(where("id").is(document.id)); - assertThat(template.findOne(query, DocumentWithCollectionOfSimpleType.class).values).hasSize(1); - - Update update = new Update().push("values").each("data", "mongodb"); - template.updateMulti(query, update, DocumentWithCollectionOfSimpleType.class); - - assertThat(template.findOne(query, DocumentWithCollectionOfSimpleType.class).values).hasSize(3); - } - - @Test // DATAMONOGO-828 - public void updateFirstShouldDoNothingWhenCalledForEntitiesThatDoNotExist() { - - Query q = query(where("id").is(Long.MIN_VALUE)); - - template.updateFirst(q, Update.update("lastname", "supercalifragilisticexpialidocious"), VersionedPerson.class); - assertThat(template.findOne(q, VersionedPerson.class)).isNull(); - } - - @Test // DATAMONGO-354, DATAMONGO-1824 - @MongoVersion(until = "3.6") - @SuppressWarnings("deprecation") - public void testUpdateShouldAllowMultiplePushAll() { - - DocumentWithMultipleCollections doc = new DocumentWithMultipleCollections(); - doc.id = "1234"; - doc.string1 = Arrays.asList("spring"); - doc.string2 = Arrays.asList("one"); - - template.save(doc); - - Update update = new Update().pushAll("string1", new Object[] { "data", "mongodb" }); - update.pushAll("string2", new String[] { "two", "three" }); - - Query findQuery = new Query(Criteria.where("id").is(doc.id)); - template.updateFirst(findQuery, update, DocumentWithMultipleCollections.class); - - DocumentWithMultipleCollections result = template.findOne(findQuery, DocumentWithMultipleCollections.class); - assertThat(result.string1).contains("spring", "data", "mongodb"); - assertThat(result.string2).contains("one", "two", "three"); - - } - - @Test // DATAMONGO-404 - public void updateWithPullShouldRemoveNestedItemFromDbRefAnnotatedCollection() { - - Sample sample1 = new Sample("1", "A"); - Sample sample2 = new Sample("2", "B"); - template.save(sample1); - template.save(sample2); - - DocumentWithDBRefCollection doc = new DocumentWithDBRefCollection(); - doc.id = "1"; - doc.dbRefAnnotatedList = Arrays.asList( // - sample1, // - sample2 // - ); - template.save(doc); - - Update update = new Update().pull("dbRefAnnotatedList", doc.dbRefAnnotatedList.get(1)); - - Query qry = query(where("id").is("1")); - template.updateFirst(qry, update, DocumentWithDBRefCollection.class); - - DocumentWithDBRefCollection result = template.findOne(qry, DocumentWithDBRefCollection.class); - - assertThat(result).isNotNull(); - assertThat(result.dbRefAnnotatedList).hasSize(1); - assertThat(result.dbRefAnnotatedList.get(0)).isNotNull(); - assertThat(result.dbRefAnnotatedList.get(0).id).isEqualTo((Object) "1"); - } - - @Test // DATAMONGO-404 - public void updateWithPullShouldRemoveNestedItemFromDbRefAnnotatedCollectionWhenGivenAnIdValueOfComponentTypeEntity() { - - Sample sample1 = new Sample("1", "A"); - Sample sample2 = new Sample("2", "B"); - template.save(sample1); - template.save(sample2); - - DocumentWithDBRefCollection doc = new DocumentWithDBRefCollection(); - doc.id = "1"; - doc.dbRefAnnotatedList = Arrays.asList( // - sample1, // - sample2 // - ); - template.save(doc); - - Update update = new Update().pull("dbRefAnnotatedList.id", "2"); - - Query qry = query(where("id").is("1")); - template.updateFirst(qry, update, DocumentWithDBRefCollection.class); - - DocumentWithDBRefCollection result = template.findOne(qry, DocumentWithDBRefCollection.class); - - assertThat(result).isNotNull(); - assertThat(result.dbRefAnnotatedList).hasSize(1); - assertThat(result.dbRefAnnotatedList.get(0)).isNotNull(); - assertThat(result.dbRefAnnotatedList.get(0).id).isEqualTo((Object) "1"); - } - - @Test // DATAMONGO-852 - public void updateShouldNotBumpVersionNumberIfVersionPropertyIncludedInUpdate() { - - VersionedPerson person = new VersionedPerson(); - person.firstname = "Dave"; - person.lastname = "Matthews"; - template.save(person); - assertThat(person.id).isNotNull(); - - Query qry = query(where("id").is(person.id)); - VersionedPerson personAfterFirstSave = template.findOne(qry, VersionedPerson.class); - assertThat(personAfterFirstSave.version).isEqualTo(0L); - - template.updateFirst(qry, Update.update("lastname", "Bubu").set("version", 100L), VersionedPerson.class); - - VersionedPerson personAfterUpdateFirst = template.findOne(qry, VersionedPerson.class); - assertThat(personAfterUpdateFirst.version).isEqualTo(100L); - assertThat(personAfterUpdateFirst.lastname).isEqualTo("Bubu"); - } - - @Test // DATAMONGO-468 - public void shouldBeAbleToUpdateDbRefPropertyWithDomainObject() { - - Sample sample1 = new Sample("1", "A"); - Sample sample2 = new Sample("2", "B"); - template.save(sample1); - template.save(sample2); - - DocumentWithDBRefCollection doc = new DocumentWithDBRefCollection(); - doc.id = "1"; - doc.dbRefProperty = sample1; - template.save(doc); - - Update update = new Update().set("dbRefProperty", sample2); - - Query qry = query(where("id").is("1")); - template.updateFirst(qry, update, DocumentWithDBRefCollection.class); - - DocumentWithDBRefCollection updatedDoc = template.findOne(qry, DocumentWithDBRefCollection.class); - - assertThat(updatedDoc).isNotNull(); - assertThat(updatedDoc.dbRefProperty).isNotNull(); - assertThat(updatedDoc.dbRefProperty.id).isEqualTo(sample2.id); - assertThat(updatedDoc.dbRefProperty.field).isEqualTo(sample2.field); - } - - @Test // DATAMONGO-862 - public void testUpdateShouldWorkForPathsOnInterfaceMethods() { - - DocumentWithCollection document = new DocumentWithCollection( - Arrays. asList(new ModelA("spring"), new ModelA("data"))); - - template.save(document); - - Query query = query(where("id").is(document.id).and("models.value").exists(true)); - Update update = new Update().set("models.$.value", "mongodb"); - template.findAndModify(query, update, DocumentWithCollection.class); - - DocumentWithCollection result = template.findOne(query(where("id").is(document.id)), DocumentWithCollection.class); - assertThat(result.models.get(0).value()).isEqualTo("mongodb"); - } - - @Test // DATAMONGO-773 - public void testShouldSupportQueryWithIncludedDbRefField() { - - Sample sample = new Sample("47111", "foo"); - template.save(sample); - - DocumentWithDBRefCollection doc = new DocumentWithDBRefCollection(); - doc.id = "4711"; - doc.dbRefProperty = sample; - - template.save(doc); - - Query qry = query(where("id").is(doc.id)); - qry.fields().include("dbRefProperty"); - - List result = template.find(qry, DocumentWithDBRefCollection.class); - - assertThat(result).isNotNull(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isNotNull(); - assertThat(result.get(0).dbRefProperty).isNotNull(); - assertThat(result.get(0).dbRefProperty.field).isEqualTo(sample.field); - } - - @Test // DATAMONGO-566 - public void testFindAllAndRemoveFullyReturnsAndRemovesDocuments() { - - Sample spring = new Sample("100", "spring"); - Sample data = new Sample("200", "data"); - Sample mongodb = new Sample("300", "mongodb"); - template.insert(Arrays.asList(spring, data, mongodb), Sample.class); - - Query qry = query(where("field").in("spring", "mongodb")); - List result = template.findAllAndRemove(qry, Sample.class); - - assertThat(result).hasSize(2); - - assertThat(template.getDb().getCollection("sample").countDocuments( - new org.bson.Document("field", new org.bson.Document("$in", Arrays.asList("spring", "mongodb"))))) - .isEqualTo(0L); - assertThat(template.getDb().getCollection("sample").countDocuments(new org.bson.Document("field", "data"))) - .isEqualTo(1L); - } - - @Test // DATAMONGO-1001 - public void shouldAllowSavingOfLazyLoadedDbRefs() { - - template.dropCollection(SomeTemplate.class); - template.dropCollection(SomeMessage.class); - template.dropCollection(SomeContent.class); - - SomeContent content = new SomeContent(); - content.id = "content-1"; - content.text = "spring"; - template.save(content); - - SomeTemplate tmpl = new SomeTemplate(); - tmpl.id = "template-1"; - tmpl.content = content; // @DBRef(lazy=true) tmpl.content - - template.save(tmpl); - - SomeTemplate savedTmpl = template.findById(tmpl.id, SomeTemplate.class); - - SomeContent loadedContent = savedTmpl.getContent(); - loadedContent.setText("data"); - template.save(loadedContent); - - assertThat(template.findById(content.id, SomeContent.class).getText()).isEqualTo("data"); - - } - - @Test // DATAMONGO-880 - public void savingAndReassigningLazyLoadingProxies() { - - template.dropCollection(SomeTemplate.class); - template.dropCollection(SomeMessage.class); - template.dropCollection(SomeContent.class); - - SomeContent content = new SomeContent(); - content.id = "C1"; - content.text = "BUBU"; - template.save(content); - - SomeTemplate tmpl = new SomeTemplate(); - tmpl.id = "T1"; - tmpl.content = content; // @DBRef(lazy=true) tmpl.content - - template.save(tmpl); - - SomeTemplate savedTmpl = template.findById(tmpl.id, SomeTemplate.class); - - SomeMessage message = new SomeMessage(); - message.id = "M1"; - message.dbrefContent = savedTmpl.content; // @DBRef message.dbrefContent - message.normalContent = savedTmpl.content; - - template.save(message); - - SomeMessage savedMessage = template.findById(message.id, SomeMessage.class); - - assertThat(savedMessage.dbrefContent.text).isEqualTo(content.text); - assertThat(savedMessage.normalContent.text).isEqualTo(content.text); - } - - @Test // DATAMONGO-884 - public void callingNonObjectMethodsOnLazyLoadingProxyShouldReturnNullIfUnderlyingDbrefWasDeletedInbetween() { - - template.dropCollection(SomeTemplate.class); - template.dropCollection(SomeContent.class); - - SomeContent content = new SomeContent(); - content.id = "C1"; - content.text = "BUBU"; - template.save(content); - - SomeTemplate tmpl = new SomeTemplate(); - tmpl.id = "T1"; - tmpl.content = content; // @DBRef(lazy=true) tmpl.content - - template.save(tmpl); - - SomeTemplate savedTmpl = template.findById(tmpl.id, SomeTemplate.class); - - template.remove(content); - - assertThat(savedTmpl.getContent().toString()).isEqualTo("someContent:C1$LazyLoadingProxy"); - assertThat(savedTmpl.getContent()).isInstanceOf(LazyLoadingProxy.class); - assertThat(savedTmpl.getContent().getText()).isNull(); - } - - @Test // DATAMONGO-471 - public void updateMultiShouldAddValuesCorrectlyWhenUsingAddToSetWithEach() { - - DocumentWithCollectionOfSimpleType document = new DocumentWithCollectionOfSimpleType(); - document.values = Arrays.asList("spring"); - template.save(document); - - Query query = query(where("id").is(document.id)); - assertThat(template.findOne(query, DocumentWithCollectionOfSimpleType.class).values).hasSize(1); - - Update update = new Update().addToSet("values").each("data", "mongodb"); - template.updateMulti(query, update, DocumentWithCollectionOfSimpleType.class); - - assertThat(template.findOne(query, DocumentWithCollectionOfSimpleType.class).values).hasSize(3); - } - - @Test // DATAMONGO-1210 - public void findAndModifyAddToSetWithEachShouldNotAddDuplicatesNorTypeHintForSimpleDocuments() { - - DocumentWithCollectionOfSamples doc = new DocumentWithCollectionOfSamples(); - doc.samples = Arrays.asList(new Sample(null, "sample1")); - - template.save(doc); - - Query query = query(where("id").is(doc.id)); - - assertThat(template.findOne(query, DocumentWithCollectionOfSamples.class)).isNotNull(); - - Update update = new Update().addToSet("samples").each(new Sample(null, "sample2"), new Sample(null, "sample1")); - - template.findAndModify(query, update, DocumentWithCollectionOfSamples.class); - - DocumentWithCollectionOfSamples retrieved = template.findOne(query, DocumentWithCollectionOfSamples.class); - - assertThat(retrieved).isNotNull(); - assertThat(retrieved.samples).hasSize(2); - assertThat(retrieved.samples.get(0).field).isEqualTo("sample1"); - assertThat(retrieved.samples.get(1).field).isEqualTo("sample2"); - } - - @Test // DATAMONGO-888 - public void sortOnIdFieldPropertyShouldBeMappedCorrectly() { - - DoucmentWithNamedIdField one = new DoucmentWithNamedIdField(); - one.someIdKey = "1"; - one.value = "a"; - - DoucmentWithNamedIdField two = new DoucmentWithNamedIdField(); - two.someIdKey = "2"; - two.value = "b"; - - template.save(one); - template.save(two); - - Query query = query(where("_id").in("1", "2")).with(Sort.by(Direction.DESC, "someIdKey")); - assertThat(template.find(query, DoucmentWithNamedIdField.class)).containsExactly(two, one); - } - - @Test // DATAMONGO-888 - public void sortOnAnnotatedFieldPropertyShouldBeMappedCorrectly() { - - DoucmentWithNamedIdField one = new DoucmentWithNamedIdField(); - one.someIdKey = "1"; - one.value = "a"; - - DoucmentWithNamedIdField two = new DoucmentWithNamedIdField(); - two.someIdKey = "2"; - two.value = "b"; - - template.save(one); - template.save(two); - - Query query = query(where("_id").in("1", "2")).with(Sort.by(Direction.DESC, "value")); - assertThat(template.find(query, DoucmentWithNamedIdField.class)).containsExactly(two, one); - } - - @Test // DATAMONGO-913 - public void shouldRetrieveInitializedValueFromDbRefAssociationAfterLoad() { - - SomeContent content = new SomeContent(); - content.id = "content-1"; - content.name = "Content 1"; - content.text = "Some text"; - - template.save(content); - - SomeTemplate tmpl = new SomeTemplate(); - tmpl.id = "template-1"; - tmpl.content = content; - - template.save(tmpl); - - SomeTemplate result = template.findOne(query(where("content").is(tmpl.getContent())), SomeTemplate.class); - - assertThat(result).isNotNull(); - assertThat(result.getContent()).isNotNull(); - assertThat(result.getContent().getId()).isNotNull(); - assertThat(result.getContent().getName()).isNotNull(); - assertThat(result.getContent().getText()).isEqualTo(content.getText()); - } - - @Test // DATAMONGO-913 - public void shouldReuseExistingDBRefInQueryFromDbRefAssociationAfterLoad() { - - SomeContent content = new SomeContent(); - content.id = "content-1"; - content.name = "Content 1"; - content.text = "Some text"; - - template.save(content); - - SomeTemplate tmpl = new SomeTemplate(); - tmpl.id = "template-1"; - tmpl.content = content; - - template.save(tmpl); - - SomeTemplate result = template.findOne(query(where("content").is(tmpl.getContent())), SomeTemplate.class); - - // Use lazy-loading-proxy in query - result = template.findOne(query(where("content").is(result.getContent())), SomeTemplate.class); - - assertThat(result.getContent().getName()).isNotNull(); - assertThat(result.getContent().getName()).isEqualTo(content.getName()); - } - - @Test // DATAMONGO-970 - public void insertsAndRemovesBasicDocumentCorrectly() { - - org.bson.Document object = new org.bson.Document("key", "value"); - template.insert(object, "collection"); - - assertThat(object.get("_id")).isNotNull(); - assertThat(template.findAll(Document.class, "collection")).hasSize(1); - - template.remove(object, "collection"); - assertThat(template.findAll(Document.class, "collection")).hasSize(0); - } - - @Test // DATAMONGO-1207 - public void ignoresNullElementsForInsertAll() { - - Address newYork = new Address("NY", "New York"); - Address washington = new Address("DC", "Washington"); - - template.insertAll(Arrays.asList(newYork, null, washington)); - - List
result = template.findAll(Address.class); - - assertThat(result).hasSize(2); - assertThat(result).contains(newYork, washington); - } - - @Test // DATAMONGO-1176 - public void generatesIdForInsertAll() { - - Person walter = new Person(null, "Walter"); - Person jesse = new Person(null, "Jesse"); - - template.insertAll(Arrays.asList(walter, jesse)); - - List result = template.findAll(Person.class); - - assertThat(result).hasSize(2); - assertThat(walter.getId()).isNotNull(); - assertThat(jesse.getId()).isNotNull(); - } - - @Test // DATAMONGO-1208 - public void takesSortIntoAccountWhenStreaming() { - - Person youngestPerson = new Person("John", 20); - Person oldestPerson = new Person("Jane", 42); - - template.insertAll(Arrays.asList(oldestPerson, youngestPerson)); - - Query q = new Query(); - q.with(Sort.by(Direction.ASC, "age")); - CloseableIterator stream = template.stream(q, Person.class); - - assertThat(stream.next().getAge()).isEqualTo(youngestPerson.getAge()); - assertThat(stream.next().getAge()).isEqualTo(oldestPerson.getAge()); - } - - @Test // DATAMONGO-1208 - public void takesLimitIntoAccountWhenStreaming() { - - Person youngestPerson = new Person("John", 20); - Person oldestPerson = new Person("Jane", 42); - - template.insertAll(Arrays.asList(oldestPerson, youngestPerson)); - - Query q = new Query(); - q.with(PageRequest.of(0, 1, Sort.by(Direction.ASC, "age"))); - CloseableIterator stream = template.stream(q, Person.class); - - assertThat(stream.next().getAge()).isEqualTo(youngestPerson.getAge()); - assertThat(stream.hasNext()).isFalse(); - } - - @Test // DATAMONGO-1204 - public void resolvesCyclicDBRefCorrectly() { - - SomeMessage message = new SomeMessage(); - SomeContent content = new SomeContent(); - - template.save(message); - template.save(content); - - message.dbrefContent = content; - content.dbrefMessage = message; - - template.save(message); - template.save(content); - - SomeMessage messageLoaded = template.findOne(query(where("id").is(message.id)), SomeMessage.class); - SomeContent contentLoaded = template.findOne(query(where("id").is(content.id)), SomeContent.class); - - assertThat(messageLoaded.dbrefContent.id).isEqualTo(contentLoaded.id); - assertThat(contentLoaded.dbrefMessage.id).isEqualTo(messageLoaded.id); - } - - @Test // DATAMONGO-1287, DATAMONGO-2004 - public void shouldReuseAlreadyResolvedLazyLoadedDBRefWhenUsedAsPersistenceConstructorArgument() { - - Document docInCtor = new Document(); - docInCtor.id = "doc-in-ctor"; - template.save(docInCtor); - - DocumentWithLazyDBrefUsedInPresistenceConstructor source = new DocumentWithLazyDBrefUsedInPresistenceConstructor( - docInCtor); - - template.save(source); - - DocumentWithLazyDBrefUsedInPresistenceConstructor loaded = template.findOne(query(where("id").is(source.id)), - DocumentWithLazyDBrefUsedInPresistenceConstructor.class); - assertThat(loaded.refToDocUsedInCtor).isInstanceOf(LazyLoadingProxy.class); - assertThat(loaded.refToDocNotUsedInCtor).isNull(); - } - - @Test // DATAMONGO-1287 - public void shouldNotReuseLazyLoadedDBRefWhenTypeUsedInPersistenceConstrcutorButValueRefersToAnotherProperty() { - - Document docNotUsedInCtor = new Document(); - docNotUsedInCtor.id = "doc-but-not-used-in-ctor"; - template.save(docNotUsedInCtor); - - DocumentWithLazyDBrefUsedInPresistenceConstructor source = new DocumentWithLazyDBrefUsedInPresistenceConstructor( - null); - source.refToDocNotUsedInCtor = docNotUsedInCtor; - - template.save(source); - - DocumentWithLazyDBrefUsedInPresistenceConstructor loaded = template.findOne(query(where("id").is(source.id)), - DocumentWithLazyDBrefUsedInPresistenceConstructor.class); - assertThat(loaded.refToDocNotUsedInCtor).isInstanceOf(LazyLoadingProxy.class); - assertThat(loaded.refToDocUsedInCtor).isNull(); - } - - @Test // DATAMONGO-1287, DATAMONGO-2004 - public void shouldRespectParameterValueWhenAttemptingToReuseLazyLoadedDBRefUsedInPersistenceConstructor() { - - Document docInCtor = new Document(); - docInCtor.id = "doc-in-ctor"; - template.save(docInCtor); - - Document docNotUsedInCtor = new Document(); - docNotUsedInCtor.id = "doc-but-not-used-in-ctor"; - template.save(docNotUsedInCtor); - - DocumentWithLazyDBrefUsedInPresistenceConstructor source = new DocumentWithLazyDBrefUsedInPresistenceConstructor( - docInCtor); - source.refToDocNotUsedInCtor = docNotUsedInCtor; - - template.save(source); - - DocumentWithLazyDBrefUsedInPresistenceConstructor loaded = template.findOne(query(where("id").is(source.id)), - DocumentWithLazyDBrefUsedInPresistenceConstructor.class); - assertThat(loaded.refToDocUsedInCtor).isInstanceOf(LazyLoadingProxy.class); - assertThat(loaded.refToDocNotUsedInCtor).isInstanceOf(LazyLoadingProxy.class); - } - - @Test // DATAMONGO-1401 - public void updateShouldWorkForTypesContainingGeoJsonTypes() { - - WithGeoJson wgj = new WithGeoJson(); - wgj.id = "1"; - wgj.description = "datamongo-1401"; - wgj.point = new GeoJsonPoint(1D, 2D); - - template.save(wgj); - - wgj.description = "datamongo-1401-update"; - template.save(wgj); - - assertThat(template.findOne(query(where("id").is(wgj.id)), WithGeoJson.class).point).isEqualTo(wgj.point); - } - - @Test // DATAMONGO-1404 - public void updatesDateValueCorrectlyWhenUsingMinOperator() { - - Calendar cal = Calendar.getInstance(Locale.US); - cal.set(2013, 10, 13, 0, 0, 0); - - TypeWithDate twd = new TypeWithDate(); - twd.date = new Date(); - template.save(twd); - template.updateFirst(query(where("id").is(twd.id)), new Update().min("date", cal.getTime()), TypeWithDate.class); - - TypeWithDate loaded = template.find(query(where("id").is(twd.id)), TypeWithDate.class).get(0); - assertThat(loaded.date).isEqualTo(cal.getTime()); - } - - @Test // DATAMONGO-1404 - public void updatesNumericValueCorrectlyWhenUsingMinOperator() { - - TypeWithNumbers twn = new TypeWithNumbers(); - twn.byteVal = 100; - twn.doubleVal = 200D; - twn.floatVal = 300F; - twn.intVal = 400; - twn.longVal = 500L; - - // Note that $min operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. - twn.bigIntegerVal = new BigInteger("600"); - twn.bigDeciamVal = new BigDecimal("700.0"); - - template.save(twn); - - byte byteVal = 90; - Update update = new Update()// - .min("byteVal", byteVal) // - .min("doubleVal", 190D) // - .min("floatVal", 290F) // - .min("intVal", 390) // - .min("longVal", 490) // - .min("bigIntegerVal", new BigInteger("590")) // - .min("bigDeciamVal", new BigDecimal("690")) // - ; - - template.updateFirst(query(where("id").is(twn.id)), update, TypeWithNumbers.class); - - TypeWithNumbers loaded = template.find(query(where("id").is(twn.id)), TypeWithNumbers.class).get(0); - assertThat(loaded.byteVal).isEqualTo(byteVal); - assertThat(loaded.doubleVal).isEqualTo(190D); - assertThat(loaded.floatVal).isEqualTo(290F); - assertThat(loaded.intVal).isEqualTo(390); - assertThat(loaded.longVal).isEqualTo(490L); - assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("590")); - assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("690")); - } - - @Test // DATAMONGO-1404 - public void updatesDateValueCorrectlyWhenUsingMaxOperator() { - - Calendar cal = Calendar.getInstance(Locale.US); - cal.set(2013, 10, 13, 0, 0, 0); - - TypeWithDate twd = new TypeWithDate(); - twd.date = cal.getTime(); - template.save(twd); - - cal.set(2019, 10, 13, 0, 0, 0); - template.updateFirst(query(where("id").is(twd.id)), new Update().max("date", cal.getTime()), TypeWithDate.class); - - TypeWithDate loaded = template.find(query(where("id").is(twd.id)), TypeWithDate.class).get(0); - assertThat(loaded.date).isEqualTo(cal.getTime()); - } - - @Test // DATAMONGO-1404 - public void updatesNumericValueCorrectlyWhenUsingMaxOperator() { - - TypeWithNumbers twn = new TypeWithNumbers(); - twn.byteVal = 100; - twn.doubleVal = 200D; - twn.floatVal = 300F; - twn.intVal = 400; - twn.longVal = 500L; - - // Note that $max operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. - twn.bigIntegerVal = new BigInteger("600"); - twn.bigDeciamVal = new BigDecimal("700.0"); - - template.save(twn); - - byte byteVal = 101; - Update update = new Update()// - .max("byteVal", byteVal) // - .max("doubleVal", 290D) // - .max("floatVal", 390F) // - .max("intVal", 490) // - .max("longVal", 590) // - .max("bigIntegerVal", new BigInteger("690")) // - .max("bigDeciamVal", new BigDecimal("790")) // - ; - - template.updateFirst(query(where("id").is(twn.id)), update, TypeWithNumbers.class); - - TypeWithNumbers loaded = template.find(query(where("id").is(twn.id)), TypeWithNumbers.class).get(0); - assertThat(loaded.byteVal).isEqualTo(byteVal); - assertThat(loaded.doubleVal).isEqualTo(290D); - assertThat(loaded.floatVal).isEqualTo(390F); - assertThat(loaded.intVal).isEqualTo(490); - assertThat(loaded.longVal).isEqualTo(590L); - assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("690")); - assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("790")); - } - - @Test // DATAMONGO-1404 - public void updatesBigNumberValueUsingStringComparisonWhenUsingMaxOperator() { - - TypeWithNumbers twn = new TypeWithNumbers(); - - // Note that $max operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. - // Therefore "80" is considered greater than "700" - twn.bigIntegerVal = new BigInteger("600"); - twn.bigDeciamVal = new BigDecimal("700.0"); - - template.save(twn); - - Update update = new Update()// - .max("bigIntegerVal", new BigInteger("70")) // - .max("bigDeciamVal", new BigDecimal("80")) // - ; - - template.updateFirst(query(where("id").is(twn.id)), update, TypeWithNumbers.class); - - TypeWithNumbers loaded = template.find(query(where("id").is(twn.id)), TypeWithNumbers.class).get(0); - assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("70")); - assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("80")); - } - - @Test // DATAMONGO-1404 - public void updatesBigNumberValueUsingStringComparisonWhenUsingMinOperator() { - - TypeWithNumbers twn = new TypeWithNumbers(); - - // Note that $max operator uses String comparison for BigDecimal/BigInteger comparison according to BSON sort rules. - // Therefore "80" is considered greater than "700" - twn.bigIntegerVal = new BigInteger("80"); - twn.bigDeciamVal = new BigDecimal("90.0"); - - template.save(twn); - - Update update = new Update()// - .min("bigIntegerVal", new BigInteger("700")) // - .min("bigDeciamVal", new BigDecimal("800")) // - ; - - template.updateFirst(query(where("id").is(twn.id)), update, TypeWithNumbers.class); - - TypeWithNumbers loaded = template.find(query(where("id").is(twn.id)), TypeWithNumbers.class).get(0); - assertThat(loaded.bigIntegerVal).isEqualTo(new BigInteger("700")); - assertThat(loaded.bigDeciamVal).isEqualTo(new BigDecimal("800")); - } - - @Test // DATAMONGO-1431, DATAMONGO-2323 - public void streamExecutionUsesExplicitCollectionName() { - - template.remove(new Query(), "some_special_collection"); - template.remove(new Query(), Document.class); - - Document document = new Document(); - - template.insert(document, "some_special_collection"); - - CloseableIterator stream = template.stream(new Query(), Document.class); - assertThat(stream.hasNext()).isFalse(); - - CloseableIterator stream2 = template.stream(new Query(where("_id").is(document.id)), - org.bson.Document.class, "some_special_collection"); - - assertThat(stream2.hasNext()).isTrue(); - assertThat(stream2.next().get("_id")).isEqualTo(new ObjectId(document.id)); - assertThat(stream2.hasNext()).isFalse(); - } - - @Test // DATAMONGO-1194 - public void shouldFetchListOfReferencesCorrectly() { - - Sample one = new Sample("1", "jon snow"); - Sample two = new Sample("2", "tyrion lannister"); - - template.save(one); - template.save(two); - - DocumentWithDBRefCollection source = new DocumentWithDBRefCollection(); - source.dbRefAnnotatedList = Arrays.asList(two, one); - - template.save(source); - - assertThat(template.findOne(query(where("id").is(source.id)), DocumentWithDBRefCollection.class)).isEqualTo(source); - } - - @Test // DATAMONGO-1194 - public void shouldFetchListOfLazyReferencesCorrectly() { - - Sample one = new Sample("1", "jon snow"); - Sample two = new Sample("2", "tyrion lannister"); - - template.save(one); - template.save(two); - - DocumentWithDBRefCollection source = new DocumentWithDBRefCollection(); - source.lazyDbRefAnnotatedList = Arrays.asList(two, one); - - template.save(source); - - DocumentWithDBRefCollection target = template.findOne(query(where("id").is(source.id)), - DocumentWithDBRefCollection.class); - - assertThat(target.lazyDbRefAnnotatedList).isInstanceOf(LazyLoadingProxy.class); - assertThat(target.getLazyDbRefAnnotatedList()).containsExactly(two, one); - } - - @Test // DATAMONGO-1194 - public void shouldFetchMapOfLazyReferencesCorrectly() { - - Sample one = new Sample("1", "jon snow"); - Sample two = new Sample("2", "tyrion lannister"); - - template.save(one); - template.save(two); - - DocumentWithDBRefCollection source = new DocumentWithDBRefCollection(); - source.lazyDbRefAnnotatedMap = new LinkedHashMap<>(); - source.lazyDbRefAnnotatedMap.put("tyrion", two); - source.lazyDbRefAnnotatedMap.put("jon", one); - template.save(source); - - DocumentWithDBRefCollection target = template.findOne(query(where("id").is(source.id)), - DocumentWithDBRefCollection.class); - - assertThat(target.lazyDbRefAnnotatedMap).isInstanceOf(LazyLoadingProxy.class); - assertThat(target.lazyDbRefAnnotatedMap.values()).containsExactly(two, one); - } - - @Test // DATAMONGO-2004 - public void shouldFetchLazyReferenceWithConstructorCreationCorrectly() { - - Sample one = new Sample("1", "jon snow"); - - template.save(one); - - DocumentWithLazyDBRefsAndConstructorCreation source = new DocumentWithLazyDBRefsAndConstructorCreation(null, one, - null, null); - - template.save(source); - - DocumentWithLazyDBRefsAndConstructorCreation target = template.findOne(query(where("id").is(source.id)), - DocumentWithLazyDBRefsAndConstructorCreation.class); - - assertThat(target.lazyDbRefProperty).isInstanceOf(LazyLoadingProxy.class); - assertThat(target.lazyDbRefProperty).isEqualTo(one); - } - - @Test // DATAMONGO-2004 - public void shouldFetchMapOfLazyReferencesWithConstructorCreationCorrectly() { - - Sample one = new Sample("1", "jon snow"); - Sample two = new Sample("2", "tyrion lannister"); - - template.save(one); - template.save(two); - - Map map = new LinkedHashMap<>(); - map.put("tyrion", two); - map.put("jon", one); - - DocumentWithLazyDBRefsAndConstructorCreation source = new DocumentWithLazyDBRefsAndConstructorCreation(null, null, - null, map); - - template.save(source); - - DocumentWithLazyDBRefsAndConstructorCreation target = template.findOne(query(where("id").is(source.id)), - DocumentWithLazyDBRefsAndConstructorCreation.class); - - assertThat(target.lazyDbRefAnnotatedMap).isInstanceOf(LazyLoadingProxy.class); - assertThat(target.lazyDbRefAnnotatedMap.values()).containsExactly(two, one); - } - - @Test // DATAMONGO-2004 - public void shouldFetchListOfLazyReferencesWithConstructorCreationCorrectly() { - - Sample one = new Sample("1", "jon snow"); - Sample two = new Sample("2", "tyrion lannister"); - - template.save(one); - template.save(two); - - List list = Arrays.asList(two, one); - - DocumentWithLazyDBRefsAndConstructorCreation source = new DocumentWithLazyDBRefsAndConstructorCreation(null, null, - list, null); - - template.save(source); - - DocumentWithLazyDBRefsAndConstructorCreation target = template.findOne(query(where("id").is(source.id)), - DocumentWithLazyDBRefsAndConstructorCreation.class); - - assertThat(target.lazyDbRefAnnotatedList).isInstanceOf(LazyLoadingProxy.class); - assertThat(target.getLazyDbRefAnnotatedList()).containsExactly(two, one); - } - - @Test // DATAMONGO-1513 - @DirtiesContext - public void populatesIdsAddedByEventListener() { - - context.addApplicationListener(new AbstractMongoEventListener() { - - @Override - public void onBeforeSave(BeforeSaveEvent event) { - event.getDocument().put("_id", UUID.randomUUID().toString()); - } - }); - - Document document = new Document(); - - template.insertAll(Collections.singletonList(document)); - - assertThat(document.id).isNotNull(); - } - - @Test // DATAMONGO-2189 - @DirtiesContext - public void afterSaveEventContainsSavedObjectUsingInsertAll() { - - AtomicReference saved = createAfterSaveReference(); - ImmutableVersioned source = new ImmutableVersioned(); - - template.insertAll(Collections.singletonList(source)); - - assertThat(saved.get()).isNotNull(); - assertThat(saved.get()).isNotSameAs(source); - assertThat(saved.get().id).isNotNull(); - - } - - @Test // DATAMONGO-2189 - @DirtiesContext - public void afterSaveEventContainsSavedObjectUsingInsert() { - - AtomicReference saved = createAfterSaveReference(); - ImmutableVersioned source = new ImmutableVersioned(); - - template.insert(source); - - assertThat(saved.get()).isNotNull(); - assertThat(saved.get()).isNotSameAs(source); - assertThat(saved.get().id).isNotNull(); - } - - @Test // DATAMONGO-1509 - public void findsByGenericNestedListElements() { - - List modelList = Collections.singletonList(new ModelA("value")); - DocumentWithCollection dwc = new DocumentWithCollection(modelList); - - template.insert(dwc); - - Query query = query(where("models").is(modelList)); - assertThat(template.findOne(query, DocumentWithCollection.class)).isEqualTo(dwc); - } - - @Test // DATAMONGO-1517 - @MongoVersion(asOf = "3.4") - public void decimal128TypeShouldBeSavedAndLoadedCorrectly() - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - - Class decimal128Type = ClassUtils.resolveClassName("org.bson.types.Decimal128", null); - - WithObjectTypeProperty source = new WithObjectTypeProperty(); - source.id = "decimal128-property-value"; - source.value = decimal128Type.getConstructor(BigDecimal.class).newInstance(new BigDecimal(100)); - - template.save(source); - - WithObjectTypeProperty loaded = template.findOne(query(where("id").is(source.id)), WithObjectTypeProperty.class); - assertThat(loaded.getValue()).isInstanceOf(decimal128Type); - } - - @Test // DATAMONGO-1718 - public void findAndRemoveAllWithoutExplicitDomainTypeShouldRemoveAndReturnEntitiesCorrectly() { - - Sample jon = new Sample("1", "jon snow"); - Sample bran = new Sample("2", "bran stark"); - Sample rickon = new Sample("3", "rickon stark"); - - template.save(jon); - template.save(bran); - template.save(rickon); - - List result = template.findAllAndRemove(query(where("field").regex(".*stark$")), - template.getCollectionName(Sample.class)); - - assertThat(result).hasSize(2); - assertThat(result).contains(bran, rickon); - assertThat(template.count(new BasicQuery("{}"), template.getCollectionName(Sample.class))).isEqualTo(1L); - } - - @Test // DATAMONGO-1779 - public void appliesQueryLimitToEmptyQuery() { - - Sample first = new Sample("1", "Dave Matthews"); - Sample second = new Sample("2", "Carter Beauford"); - - template.insertAll(Arrays.asList(first, second)); - - assertThat(template.find(new Query().limit(1), Sample.class)).hasSize(1); - } - - @Test // DATAMONGO-1870 - public void removeShouldConsiderLimit() { - - List samples = IntStream.range(0, 100) // - .mapToObj(i -> new Sample("id-" + i, i % 2 == 0 ? "stark" : "lannister")) // - .collect(Collectors.toList()); - - template.insertAll(samples); - - DeleteResult wr = template.remove(query(where("field").is("lannister")).limit(25), Sample.class); - - assertThat(wr.getDeletedCount()).isEqualTo(25L); - assertThat(template.count(new Query(), Sample.class)).isEqualTo(75L); - } - - @Test // DATAMONGO-1870 - public void removeShouldConsiderSkipAndSort() { - - List samples = IntStream.range(0, 100) // - .mapToObj(i -> new Sample("id-" + i, i % 2 == 0 ? "stark" : "lannister")) // - .collect(Collectors.toList()); - - template.insertAll(samples); - - DeleteResult wr = template.remove(new Query().skip(25).with(Sort.by("field")), Sample.class); - - assertThat(wr.getDeletedCount()).isEqualTo(75L); - assertThat(template.count(new Query(), Sample.class)).isEqualTo(25L); - assertThat(template.count(query(where("field").is("lannister")), Sample.class)).isEqualTo(25L); - assertThat(template.count(query(where("field").is("stark")), Sample.class)).isEqualTo(0L); - } - - @Test // DATAMONGO-1988 - public void findByNestedDocumentWithStringIdMappingToObjectIdMatchesDocumentsCorrectly() { - - DocumentWithNestedTypeHavingStringIdProperty source = new DocumentWithNestedTypeHavingStringIdProperty(); - source.id = "id-1"; - source.sample = new Sample(); - source.sample.id = new ObjectId().toHexString(); - - template.save(source); - - DocumentWithNestedTypeHavingStringIdProperty target = template - .query(DocumentWithNestedTypeHavingStringIdProperty.class) - .matching(query(where("sample.id").is(source.sample.id))).firstValue(); - - assertThat(target).isEqualTo(source); - } - - @Test // DATAMONGO-1992 - public void writesAuditingMetadataForImmutableTypes() { - - ImmutableAudited source = new ImmutableAudited(null, null); - ImmutableAudited result = template.save(source); - - assertThat(result).isNotSameAs(source).describedAs("Expected a different instances to be returned!"); - assertThat(result.modified).isNotNull().describedAs("Auditing field must not be null!"); - - ImmutableAudited read = template.findOne(query(where("id").is(result.getId())), ImmutableAudited.class); - - assertThat(read.modified).isEqualTo(result.modified.truncatedTo(ChronoUnit.MILLIS)) - .describedAs("Expected auditing information to be read!"); - } - - @Test // DATAMONGO-1798 - public void saveAndLoadStringThatIsAnObjectIdAsString() { - - RawStringId source = new RawStringId(); - source.id = new ObjectId().toHexString(); - source.value = "new value"; - - template.save(source); - - org.bson.Document result = template - .execute(db -> (org.bson.Document) db.getCollection(template.getCollectionName(RawStringId.class)) - .find(Filters.eq("_id", source.id)).limit(1).into(new ArrayList()).iterator().next()); - - assertThat(result).isNotNull(); - assertThat(result.get("_id")).isEqualTo(source.id); - - RawStringId target = template.findOne(query(where("id").is(source.id)), RawStringId.class); - assertThat(target).isEqualTo(source); - } - - @Test // DATAMONGO-2193 - public void shouldNotConvertStringToObjectIdForNonIdField() { - - ObjectId outerId = new ObjectId(); - String innerId = new ObjectId().toHexString(); - - org.bson.Document source = new org.bson.Document() // - .append("_id", outerId) // - .append("inner", new org.bson.Document("id", innerId).append("value", "boooh")); - - template.getDb().getCollection(template.getCollectionName(Outer.class)).insertOne(source); - - Outer target = template.findOne(query(where("inner.id").is(innerId)), Outer.class); - assertThat(target).isNotNull(); - assertThat(target.id).isEqualTo(outerId); - assertThat(target.inner.id).isEqualTo(innerId); - } - - @Test // DATAMONGO-2294 - public void shouldProjectWithCollections() { - - MyPerson person = new MyPerson("Walter"); - person.address = new Address("TX", "Austin"); - template.save(person); - - Query queryByChainedInclude = query(where("name").is("Walter")); - queryByChainedInclude.fields().include("id").include("name"); - - Query queryByCollectionInclude = query(where("name").is("Walter")); - queryByCollectionInclude.fields().include("id", "name"); - - MyPerson first = template.findAndReplace(queryByChainedInclude, new MyPerson("Walter")); - MyPerson second = template.findAndReplace(queryByCollectionInclude, new MyPerson("Walter")); - - assertThat(first).isEqualTo(second); - assertThat(first.address).isNull(); - assertThat(second.address).isNull(); - } - - @Test // DATAMONGO-2451 - public void sortOnIdFieldWithExplicitTypeShouldWork() { - - template.dropCollection(WithIdAndFieldAnnotation.class); - - WithIdAndFieldAnnotation f = new WithIdAndFieldAnnotation(); - f.id = new ObjectId().toHexString(); - f.value = "value"; - - template.save(f); - - assertThat(template.find(new BasicQuery("{}").with(Sort.by("id")), WithIdAndFieldAnnotation.class)).isNotEmpty(); - } - - private AtomicReference createAfterSaveReference() { - - AtomicReference saved = new AtomicReference<>(); - context.addApplicationListener(new AbstractMongoEventListener() { - - @Override - public void onAfterSave(AfterSaveEvent event) { - saved.set(event.getSource()); - } - }); - - return saved; - } - - static class TypeWithNumbers { - - @Id String id; - Integer intVal; - Float floatVal; - Long longVal; - Double doubleVal; - BigDecimal bigDeciamVal; - BigInteger bigIntegerVal; - Byte byteVal; - } - - static class DoucmentWithNamedIdField { - - @Id String someIdKey; - - @Field(value = "val") // - String value; - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (someIdKey == null ? 0 : someIdKey.hashCode()); - result = prime * result + (value == null ? 0 : value.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof DoucmentWithNamedIdField)) { - return false; - } - DoucmentWithNamedIdField other = (DoucmentWithNamedIdField) obj; - if (someIdKey == null) { - if (other.someIdKey != null) { - return false; - } - } else if (!someIdKey.equals(other.someIdKey)) { - return false; - } - if (value == null) { - if (other.value != null) { - return false; - } - } else if (!value.equals(other.value)) { - return false; - } - return true; - } - - } - - @Data - static class DocumentWithDBRefCollection { - - @Id public String id; - - @Field("db_ref_list") // DATAMONGO-1058 - @org.springframework.data.mongodb.core.mapping.DBRef // - public List dbRefAnnotatedList; - - @org.springframework.data.mongodb.core.mapping.DBRef // - public Sample dbRefProperty; - - @Field("lazy_db_ref_list") // DATAMONGO-1194 - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) // - public List lazyDbRefAnnotatedList; - - @Field("lazy_db_ref_map") // DATAMONGO-1194 - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) public Map lazyDbRefAnnotatedMap; - } - - @Data - @AllArgsConstructor - static class DocumentWithLazyDBRefsAndConstructorCreation { - - @Id public String id; - - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) // - public Sample lazyDbRefProperty; - - @Field("lazy_db_ref_list") @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) // - public List lazyDbRefAnnotatedList; - - @Field("lazy_db_ref_map") @org.springframework.data.mongodb.core.mapping.DBRef( - lazy = true) public Map lazyDbRefAnnotatedMap; - } - - @EqualsAndHashCode - static class DocumentWithCollection { - - @Id String id; - List models; - - DocumentWithCollection(List models) { - this.models = models; - } - } - - static class DocumentWithCollectionOfSimpleType { - - @Id String id; - List values; - } - - static class DocumentWithCollectionOfSamples { - @Id String id; - List samples; - } - - @EqualsAndHashCode - static class DocumentWithNestedTypeHavingStringIdProperty { - - @Id String id; - Sample sample; - } - - static class DocumentWithMultipleCollections { - @Id String id; - List string1; - List string2; - } - - static class DocumentWithNestedCollection { - @Id String id; - List> models = new ArrayList<>(); - } - - static class DocumentWithNestedList { - @Id String id; - List> models = new ArrayList<>(); - } - - static class DocumentWithEmbeddedDocumentWithCollection { - @Id String id; - DocumentWithCollection embeddedDocument; - - DocumentWithEmbeddedDocumentWithCollection(DocumentWithCollection embeddedDocument) { - this.embeddedDocument = embeddedDocument; - } - } - - static interface Model { - String value(); - - String id(); - } - - @EqualsAndHashCode - static class ModelA implements Model { - - @Id String id; - private String value; - - ModelA(String value) { - this.value = value; - } - - @Override - public String value() { - return this.value; - } - - @Override - public String id() { - return id; - } - } - - static class Document { - - @Id public String id; - public Model model; - } - - static class MyId { - - String first; - String second; - } - - static class TypeWithMyId { - - @Id MyId id; - } - - @EqualsAndHashCode - @NoArgsConstructor - static class Sample { - - @Id String id; - String field; - - public Sample(String id, String field) { - this.id = id; - this.field = field; - } - } - - static class TestClass { - - DateTime myDate; - - @PersistenceConstructor - TestClass(DateTime myDate) { - this.myDate = myDate; - } - } - - static class PersonWithConvertedId { - - String id; - String name; - } - - static enum DateTimeToDateConverter implements Converter { - - INSTANCE; - - public Date convert(DateTime source) { - return source == null ? null : source.toDate(); - } - } - - static enum DateToDateTimeConverter implements Converter { - - INSTANCE; - - public DateTime convert(Date source) { - return source == null ? null : new DateTime(source.getTime()); - } - } - - @Data - @NoArgsConstructor - @AllArgsConstructor - public static class MyPerson { - - String id; - String name; - Address address; - - public MyPerson(String name) { - this.name = name; - } - } - - interface MyPersonProjection { - - String getName(); - } - - static class Address { - - String state; - String city; - - Address() {} - - Address(String state, String city) { - this.state = state; - this.city = city; - } - - @Override - public boolean equals(Object obj) { - - if (obj == this) { - return true; - } - - if (!(obj instanceof Address)) { - return false; - } - - Address that = (Address) obj; - - return ObjectUtils.nullSafeEquals(this.city, that.city) && // - ObjectUtils.nullSafeEquals(this.state, that.state); - } - - @Override - public int hashCode() { - - int result = 17; - - result += 31 * ObjectUtils.nullSafeHashCode(this.city); - result += 31 * ObjectUtils.nullSafeHashCode(this.state); - - return result; - } - } - - static class VersionedPerson { - - @Version Long version; - String id, firstname, lastname; - } - - static class TypeWithFieldAnnotation { - - @Id ObjectId id; - @Field("email") String emailAddress; - } - - static class TypeWithDate { - - @Id String id; - Date date; - } - - static class ObjectWith3AliasedFields { - - @Id String id; - @Field("prop1") String property1; - @Field("prop2") String property2; - @Field("prop3") String property3; - } - - static class ObjectWith3AliasedFieldsAndNestedAddress extends ObjectWith3AliasedFields { - @Field("adr") Address address; - } - - static enum EnumValue { - VALUE1, VALUE2, VALUE3 - } - - static class ObjectWithEnumValue { - - @Id String id; - EnumValue value; - } - - public static class SomeTemplate { - - String id; - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) SomeContent content; - - public SomeContent getContent() { - return content; - } - } - - @EqualsAndHashCode - public static class SomeContent { - - String id; - String text; - String name; - @org.springframework.data.mongodb.core.mapping.DBRef SomeMessage dbrefMessage; - - public String getName() { - return name; - } - - public void setText(String text) { - this.text = text; - - } - - public String getId() { - return id; - } - - public String getText() { - return text; - } - } - - static class SomeMessage { - String id; - @org.springframework.data.mongodb.core.mapping.DBRef SomeContent dbrefContent; - SomeContent normalContent; - } - - static class DocumentWithLazyDBrefUsedInPresistenceConstructor { - - @Id String id; - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) Document refToDocUsedInCtor; - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) Document refToDocNotUsedInCtor; - - @PersistenceConstructor - public DocumentWithLazyDBrefUsedInPresistenceConstructor(Document refToDocUsedInCtor) { - this.refToDocUsedInCtor = refToDocUsedInCtor; - } - - } - - static class WithGeoJson { - - @Id String id; - @Version // - Integer version; - String description; - GeoJsonPoint point; - } - - @Data - static class WithObjectTypeProperty { - - @Id String id; - Object value; - } - - static class PersonWithIdPropertyOfTypeUUIDListener - extends AbstractMongoEventListener { - - @Override - public void onBeforeConvert(BeforeConvertEvent event) { - - PersonWithIdPropertyOfTypeUUID person = event.getSource(); - - if (person.getId() != null) { - return; - } - - person.setId(UUID.randomUUID()); - } - } - - public static class Message { - - private ObjectId id; - - private String text; - - private Date timestamp; - - public Message() {} - - public Message(String text) { - super(); - this.text = text; - this.timestamp = new Date(); - } - - public Message(String text, Date timestamp) { - super(); - this.text = text; - this.timestamp = timestamp; - } - - public ObjectId getId() { - return id; - } - - public void setId(ObjectId id) { - this.id = id; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - - public Date getTimestamp() { - return timestamp; - } - - public void setTimestamp(Date timestamp) { - this.timestamp = timestamp; - } - - @Override - public String toString() { - return "Message [id=" + id + ", text=" + text + ", timestamp=" + timestamp + "]"; - } - - } - - // DATAMONGO-1992 - - @AllArgsConstructor - @With - static class ImmutableVersioned { - - final @Id String id; - final @Version Long version; - - public ImmutableVersioned() { - id = null; - version = null; - } - } - - @Value - @With - static class ImmutableAudited { - @Id String id; - @LastModifiedDate Instant modified; - } - - @Data - static class RawStringId { - - @MongoId String id; - String value; - } - - static class Outer { - - @Id ObjectId id; - Inner inner; - } - - static class Inner { - - @Field("id") String id; - String value; - } - - @Data - static class WithIdAndFieldAnnotation { - - @Id // - @Field(name = "_id") // - String id; - String value; - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTransactionTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTransactionTests.java deleted file mode 100644 index 22fed139e7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTransactionTests.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.test.util.MongoTestUtils.*; - -import lombok.AllArgsConstructor; -import lombok.Data; - -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Persistable; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.MongoTransactionManager; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.test.util.AfterTransactionAssertion; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.ReplSetClient; -import org.springframework.test.annotation.Rollback; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.context.transaction.AfterTransaction; -import org.springframework.test.context.transaction.BeforeTransaction; -import org.springframework.transaction.annotation.Transactional; - -import com.mongodb.ReadPreference; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.Filters; - -/** - * @author Christoph Strobl - * @currentRead Shadow's Edge - Brent Weeks - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@EnableIfReplicaSetAvailable -@EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") -@ContextConfiguration -@Transactional(transactionManager = "txManager") -public class MongoTemplateTransactionTests { - - static final String DB_NAME = "template-tx-tests"; - static final String COLLECTION_NAME = "assassins"; - - static @ReplSetClient MongoClient mongoClient; - - @Configuration - static class Config extends AbstractMongoClientConfiguration { - - @Bean - public MongoClient mongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return DB_NAME; - } - - @Override - protected boolean autoIndexCreation() { - return false; - } - - @Bean - MongoTransactionManager txManager(MongoDatabaseFactory dbFactory) { - return new MongoTransactionManager(dbFactory); - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - } - - @Autowired MongoTemplate template; - @Autowired MongoClient client; - - List>> assertionList; - - @BeforeEach - public void setUp() { - - template.setReadPreference(ReadPreference.primary()); - assertionList = new CopyOnWriteArrayList<>(); - } - - @BeforeTransaction - public void beforeTransaction() { - createOrReplaceCollection(DB_NAME, COLLECTION_NAME, client); - } - - @AfterTransaction - public void verifyDbState() { - - MongoCollection collection = client.getDatabase(DB_NAME).withReadPreference(ReadPreference.primary()) - .getCollection(COLLECTION_NAME); - - assertionList.forEach(it -> { - - boolean isPresent = collection.countDocuments(Filters.eq("_id", it.getId())) != 0; - - assertThat(isPresent).isEqualTo(it.shouldBePresent()) - .withFailMessage(String.format("After transaction entity %s should %s.", it.getPersistable(), - it.shouldBePresent() ? "be present" : "NOT be present")); - }); - } - - @Rollback(false) - @Test // DATAMONGO-1920 - public void shouldOperateCommitCorrectly() { - - Assassin hu = new Assassin("hu", "Hu Gibbet"); - template.save(hu); - - assertAfterTransaction(hu).isPresent(); - } - - @Test // DATAMONGO-1920 - public void shouldOperateRollbackCorrectly() { - - Assassin vi = new Assassin("vi", "Viridiana Sovari"); - template.save(vi); - - assertAfterTransaction(vi).isNotPresent(); - } - - @Test // DATAMONGO-1920 - public void shouldBeAbleToViewChangesDuringTransaction() throws InterruptedException { - - Assassin durzo = new Assassin("durzo", "Durzo Blint"); - template.save(durzo); - - Thread.sleep(100); - Assassin retrieved = template.findOne(query(where("id").is(durzo.getId())), Assassin.class); - - assertThat(retrieved).isEqualTo(durzo); - - assertAfterTransaction(durzo).isNotPresent(); - } - - // --- Just some helpers and tests entities - - private AfterTransactionAssertion assertAfterTransaction(Assassin assassin) { - - AfterTransactionAssertion assertion = new AfterTransactionAssertion<>(assassin); - assertionList.add(assertion); - return assertion; - } - - @Data - @AllArgsConstructor - @org.springframework.data.mongodb.core.mapping.Document(COLLECTION_NAME) - static class Assassin implements Persistable { - - @Id String id; - String name; - - @Override - public boolean isNew() { - return id == null; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java deleted file mode 100644 index 150ba5a861..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java +++ /dev/null @@ -1,2514 +0,0 @@ -/* - * Copyright 2010-2021 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 static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.math.BigInteger; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; - -import org.assertj.core.api.Assertions; -import org.bson.Document; -import org.bson.conversions.Bson; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationListener; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.context.support.StaticApplicationContext; -import org.springframework.core.convert.converter.Converter; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.Transient; -import org.springframework.data.annotation.Version; -import org.springframework.data.convert.CustomConversions; -import org.springframework.data.domain.Sort; -import org.springframework.data.geo.Point; -import org.springframework.data.mapping.callback.EntityCallbacks; -import org.springframework.data.mapping.context.InvalidPersistentPropertyPath; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.aggregation.*; -import org.springframework.data.mongodb.core.aggregation.ComparisonOperators.Gte; -import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.Sharded; -import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener; -import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback; -import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback; -import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; -import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback; -import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent; -import org.springframework.data.mongodb.core.mapping.event.BeforeSaveCallback; -import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent; -import org.springframework.data.mongodb.core.mapreduce.GroupBy; -import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.lang.Nullable; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.CollectionUtils; - -import com.mongodb.MongoClientSettings; -import com.mongodb.MongoException; -import com.mongodb.MongoNamespace; -import com.mongodb.ReadPreference; -import com.mongodb.ServerAddress; -import com.mongodb.ServerCursor; -import com.mongodb.WriteConcern; -import com.mongodb.client.AggregateIterable; -import com.mongodb.client.DistinctIterable; -import com.mongodb.client.FindIterable; -import com.mongodb.client.MapReduceIterable; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoCursor; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.model.CountOptions; -import com.mongodb.client.model.CreateCollectionOptions; -import com.mongodb.client.model.DeleteOptions; -import com.mongodb.client.model.FindOneAndDeleteOptions; -import com.mongodb.client.model.FindOneAndReplaceOptions; -import com.mongodb.client.model.FindOneAndUpdateOptions; -import com.mongodb.client.model.MapReduceAction; -import com.mongodb.client.model.ReplaceOptions; -import com.mongodb.client.model.UpdateOptions; -import com.mongodb.client.result.DeleteResult; -import com.mongodb.client.result.UpdateResult; - -/** - * Unit tests for {@link MongoTemplate}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - * @author Michael J. Simons - * @author Roman Puchkovskiy - * @author Yadhukrishna S Pai - */ -@MockitoSettings(strictness = Strictness.LENIENT) -public class MongoTemplateUnitTests extends MongoOperationsUnitTests { - - private MongoTemplate template; - - @Mock MongoDatabaseFactory factory; - @Mock MongoClient mongo; - @Mock MongoDatabase db; - @Mock MongoCollection collection; - @Mock MongoCollection collectionWithWriteConcern; - @Mock MongoCursor cursor; - @Mock FindIterable findIterable; - @Mock AggregateIterable aggregateIterable; - @Mock MapReduceIterable mapReduceIterable; - @Mock DistinctIterable distinctIterable; - @Mock UpdateResult updateResult; - @Mock DeleteResult deleteResult; - - private Document commandResultDocument = new Document(); - - private MongoExceptionTranslator exceptionTranslator = new MongoExceptionTranslator(); - private MappingMongoConverter converter; - private MongoMappingContext mappingContext; - - @BeforeEach - void beforeEach() { - - when(findIterable.iterator()).thenReturn(cursor); - when(factory.getMongoDatabase()).thenReturn(db); - when(factory.getExceptionTranslator()).thenReturn(exceptionTranslator); - when(factory.getCodecRegistry()).thenReturn(MongoClientSettings.getDefaultCodecRegistry()); - when(db.getCollection(any(String.class), eq(Document.class))).thenReturn(collection); - when(db.runCommand(any(), any(Class.class))).thenReturn(commandResultDocument); - when(collection.find(any(org.bson.Document.class), any(Class.class))).thenReturn(findIterable); - when(collection.mapReduce(any(), any(), eq(Document.class))).thenReturn(mapReduceIterable); - when(collection.countDocuments(any(Bson.class), any(CountOptions.class))).thenReturn(1L); - when(collection.estimatedDocumentCount(any())).thenReturn(1L); - when(collection.getNamespace()).thenReturn(new MongoNamespace("db.mock-collection")); - when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable); - when(collection.withReadPreference(any())).thenReturn(collection); - when(collection.replaceOne(any(), any(), any(ReplaceOptions.class))).thenReturn(updateResult); - when(collection.withWriteConcern(any())).thenReturn(collectionWithWriteConcern); - when(collection.distinct(anyString(), any(Document.class), any())).thenReturn(distinctIterable); - when(collectionWithWriteConcern.deleteOne(any(Bson.class), any())).thenReturn(deleteResult); - when(findIterable.projection(any())).thenReturn(findIterable); - when(findIterable.sort(any(org.bson.Document.class))).thenReturn(findIterable); - when(findIterable.collation(any())).thenReturn(findIterable); - when(findIterable.limit(anyInt())).thenReturn(findIterable); - when(mapReduceIterable.collation(any())).thenReturn(mapReduceIterable); - when(mapReduceIterable.sort(any())).thenReturn(mapReduceIterable); - when(mapReduceIterable.iterator()).thenReturn(cursor); - when(mapReduceIterable.filter(any())).thenReturn(mapReduceIterable); - when(mapReduceIterable.collectionName(any())).thenReturn(mapReduceIterable); - when(mapReduceIterable.databaseName(any())).thenReturn(mapReduceIterable); - when(mapReduceIterable.action(any())).thenReturn(mapReduceIterable); - when(aggregateIterable.collation(any())).thenReturn(aggregateIterable); - when(aggregateIterable.allowDiskUse(any())).thenReturn(aggregateIterable); - when(aggregateIterable.batchSize(anyInt())).thenReturn(aggregateIterable); - when(aggregateIterable.map(any())).thenReturn(aggregateIterable); - when(aggregateIterable.maxTime(anyLong(), any())).thenReturn(aggregateIterable); - when(aggregateIterable.into(any())).thenReturn(Collections.emptyList()); - when(distinctIterable.collation(any())).thenReturn(distinctIterable); - when(distinctIterable.map(any())).thenReturn(distinctIterable); - when(distinctIterable.into(any())).thenReturn(Collections.emptyList()); - - this.mappingContext = new MongoMappingContext(); - mappingContext.setAutoIndexCreation(true); - mappingContext.afterPropertiesSet(); - - this.converter = spy(new MappingMongoConverter(new DefaultDbRefResolver(factory), mappingContext)); - converter.afterPropertiesSet(); - this.template = new MongoTemplate(factory, converter); - } - - @Test - void rejectsNullDatabaseName() { - assertThatIllegalArgumentException().isThrownBy(() -> new MongoTemplate(mongo, null)); - } - - @Test // DATAMONGO-1968 - void rejectsNullMongo() { - assertThatIllegalArgumentException().isThrownBy(() -> new MongoTemplate((MongoClient) null, "database")); - } - - @Test // DATAMONGO-1968 - void rejectsNullMongoClient() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new MongoTemplate((com.mongodb.client.MongoClient) null, "database")); - } - - @Test // DATAMONGO-1870 - void removeHandlesMongoExceptionProperly() { - - MongoTemplate template = mockOutGetDb(); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> template.remove(null, "collection")); - } - - @Test - void defaultsConverterToMappingMongoConverter() { - MongoTemplate template = new MongoTemplate(mongo, "database"); - assertThat(ReflectionTestUtils.getField(template, "mongoConverter") instanceof MappingMongoConverter).isTrue(); - } - - @Test - void rejectsNotFoundMapReduceResource() { - - GenericApplicationContext ctx = new GenericApplicationContext(); - ctx.refresh(); - template.setApplicationContext(ctx); - - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) - .isThrownBy(() -> template.mapReduce("foo", "classpath:doesNotExist.js", "function() {}", Person.class)); - } - - @Test // DATAMONGO-322 - void rejectsEntityWithNullIdIfNotSupportedIdType() { - - Object entity = new NotAutogenerateableId(); - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class).isThrownBy(() -> template.save(entity)); - } - - @Test // DATAMONGO-322 - void storesEntityWithSetIdAlthoughNotAutogenerateable() { - - NotAutogenerateableId entity = new NotAutogenerateableId(); - entity.id = 1; - - template.save(entity); - } - - @Test // DATAMONGO-322 - void autogeneratesIdForEntityWithAutogeneratableId() { - - this.converter.afterPropertiesSet(); - - MongoTemplate template = spy(this.template); - doReturn(new ObjectId()).when(template).saveDocument(any(String.class), any(Document.class), any(Class.class)); - - AutogenerateableId entity = new AutogenerateableId(); - template.save(entity); - - assertThat(entity.id).isNotNull(); - } - - @Test // DATAMONGO-1912 - void autogeneratesIdForMap() { - - MongoTemplate template = spy(this.template); - doReturn(new ObjectId()).when(template).saveDocument(any(String.class), any(Document.class), any(Class.class)); - - Map entity = new LinkedHashMap<>(); - template.save(entity, "foo"); - - assertThat(entity).containsKey("_id"); - } - - @Test // DATAMONGO-374 - void convertsUpdateConstraintsUsingConverters() { - - CustomConversions conversions = new MongoCustomConversions(Collections.singletonList(MyConverter.INSTANCE)); - this.converter.setCustomConversions(conversions); - this.converter.afterPropertiesSet(); - - Query query = new Query(); - Update update = new Update().set("foo", new AutogenerateableId()); - - template.updateFirst(query, update, Wrapper.class); - - QueryMapper queryMapper = new QueryMapper(converter); - Document reference = queryMapper.getMappedObject(update.getUpdateObject(), Optional.empty()); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), eq(reference), any(UpdateOptions.class)); - } - - @Test // DATAMONGO-474 - void setsUnpopulatedIdField() { - - NotAutogenerateableId entity = new NotAutogenerateableId(); - - template.populateIdIfNecessary(entity, 5); - assertThat(entity.id).isEqualTo(5); - } - - @Test // DATAMONGO-474 - void doesNotSetAlreadyPopulatedId() { - - NotAutogenerateableId entity = new NotAutogenerateableId(); - entity.id = 5; - - template.populateIdIfNecessary(entity, 7); - assertThat(entity.id).isEqualTo(5); - } - - @Test // DATAMONGO-868 - void findAndModifyShouldBumpVersionByOneWhenVersionFieldNotIncludedInUpdate() { - - VersionedEntity v = new VersionedEntity(); - v.id = 1; - v.version = 0; - - ArgumentCaptor captor = ArgumentCaptor.forClass(org.bson.Document.class); - - template.findAndModify(new Query(), new Update().set("id", "10"), VersionedEntity.class); - - verify(collection, times(1)).findOneAndUpdate(any(org.bson.Document.class), captor.capture(), - any(FindOneAndUpdateOptions.class)); - assertThat(captor.getValue().get("$inc")).isEqualTo(new Document("version", 1L)); - } - - @Test // DATAMONGO-868 - void findAndModifyShouldNotBumpVersionByOneWhenVersionFieldAlreadyIncludedInUpdate() { - - VersionedEntity v = new VersionedEntity(); - v.id = 1; - v.version = 0; - - ArgumentCaptor captor = ArgumentCaptor.forClass(org.bson.Document.class); - - template.findAndModify(new Query(), new Update().set("version", 100), VersionedEntity.class); - - verify(collection, times(1)).findOneAndUpdate(any(org.bson.Document.class), captor.capture(), - any(FindOneAndUpdateOptions.class)); - - assertThat(captor.getValue().get("$set")).isEqualTo(new Document("version", 100)); - assertThat(captor.getValue().get("$inc")).isNull(); - } - - @Test // DATAMONGO-533 - void registersDefaultEntityIndexCreatorIfApplicationContextHasOneForDifferentMappingContext() { - - GenericApplicationContext applicationContext = new GenericApplicationContext(); - applicationContext.getBeanFactory().registerSingleton("foo", - new MongoPersistentEntityIndexCreator(new MongoMappingContext(), template)); - applicationContext.refresh(); - - GenericApplicationContext spy = spy(applicationContext); - - MongoTemplate mongoTemplate = new MongoTemplate(factory, converter); - mongoTemplate.setApplicationContext(spy); - - verify(spy, times(1)).addApplicationListener(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(MongoPersistentEntityIndexCreator argument) { - return argument.isIndexCreatorFor(mappingContext); - } - })); - } - - @Test // DATAMONGO-566 - void findAllAndRemoveShouldRetrieveMatchingDocumentsPriorToRemoval() { - - BasicQuery query = new BasicQuery("{'foo':'bar'}"); - template.findAllAndRemove(query, VersionedEntity.class); - verify(collection, times(1)).find(Mockito.eq(query.getQueryObject()), any(Class.class)); - } - - @Test // DATAMONGO-566 - void findAllAndRemoveShouldRemoveDocumentsReturedByFindQuery() { - - Mockito.when(cursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false); - Mockito.when(cursor.next()).thenReturn(new org.bson.Document("_id", Integer.valueOf(0))) - .thenReturn(new org.bson.Document("_id", Integer.valueOf(1))); - - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(org.bson.Document.class); - BasicQuery query = new BasicQuery("{'foo':'bar'}"); - template.findAllAndRemove(query, VersionedEntity.class); - - verify(collection, times(1)).deleteMany(queryCaptor.capture(), any()); - - Document idField = DocumentTestUtils.getAsDocument(queryCaptor.getValue(), "_id"); - assertThat((List) idField.get("$in")).containsExactly(Integer.valueOf(0), Integer.valueOf(1)); - } - - @Test // DATAMONGO-566 - void findAllAndRemoveShouldNotTriggerRemoveIfFindResultIsEmpty() { - - template.findAllAndRemove(new BasicQuery("{'foo':'bar'}"), VersionedEntity.class); - verify(collection, never()).deleteMany(any(org.bson.Document.class)); - } - - @Test // DATAMONGO-948 - void sortShouldBeTakenAsIsWhenExecutingQueryWithoutSpecificTypeInformation() { - - Query query = Query.query(Criteria.where("foo").is("bar")).with(Sort.by("foo")); - template.executeQuery(query, "collection1", new DocumentCallbackHandler() { - - @Override - public void processDocument(Document document) throws MongoException, DataAccessException { - // nothing to do - just a test - } - }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(org.bson.Document.class); - - verify(findIterable, times(1)).sort(captor.capture()); - assertThat(captor.getValue()).isEqualTo(new Document("foo", 1)); - } - - @Test // DATAMONGO-1166, DATAMONGO-1824 - void aggregateShouldHonorReadPreferenceWhenSet() { - - template.setReadPreference(ReadPreference.secondary()); - - template.aggregate(newAggregation(Aggregation.unwind("foo")), "collection-1", Wrapper.class); - - verify(collection).withReadPreference(eq(ReadPreference.secondary())); - } - - @Test // DATAMONGO-1166, DATAMONGO-1824 - void aggregateShouldIgnoreReadPreferenceWhenNotSet() { - - template.aggregate(newAggregation(Aggregation.unwind("foo")), "collection-1", Wrapper.class); - - verify(collection, never()).withReadPreference(any()); - } - - @Test // DATAMONGO-2153 - void aggregateShouldHonorOptionsComment() { - - AggregationOptions options = AggregationOptions.builder().comment("expensive").build(); - - template.aggregate(newAggregation(Aggregation.unwind("foo")).withOptions(options), "collection-1", Wrapper.class); - - verify(aggregateIterable).comment("expensive"); - } - - @Test // DATAMONGO-1836 - void aggregateShouldHonorOptionsHint() { - - Document hint = new Document("dummyField", 1); - AggregationOptions options = AggregationOptions.builder().hint(hint).build(); - - template.aggregate(newAggregation(Aggregation.unwind("foo")).withOptions(options), "collection-1", Wrapper.class); - - verify(aggregateIterable).hint(hint); - } - - @Test // GH-3542 - void aggregateShouldUseRelaxedMappingByDefault() { - - MongoTemplate template = new MongoTemplate(factory, converter) { - - @Override - protected AggregationResults doAggregate(Aggregation aggregation, String collectionName, - Class outputType, AggregationOperationContext context) { - - assertThat(context).isInstanceOf(RelaxedTypeBasedAggregationOperationContext.class); - return super.doAggregate(aggregation, collectionName, outputType, context); - } - }; - - template.aggregate( - newAggregation(Jedi.class, Aggregation.unwind("foo")).withOptions(AggregationOptions.builder().build()), - Jedi.class); - } - - @Test // GH-3542 - void aggregateShouldUseStrictMappingIfOptionsIndicate() { - - MongoTemplate template = new MongoTemplate(factory, converter) { - - @Override - protected AggregationResults doAggregate(Aggregation aggregation, String collectionName, - Class outputType, AggregationOperationContext context) { - - assertThat(context).isInstanceOf(TypeBasedAggregationOperationContext.class); - return super.doAggregate(aggregation, collectionName, outputType, context); - } - }; - - assertThatExceptionOfType(InvalidPersistentPropertyPath.class) - .isThrownBy(() -> template.aggregate(newAggregation(Jedi.class, Aggregation.unwind("foo")) - .withOptions(AggregationOptions.builder().strictMapping().build()), Jedi.class)); - } - - @Test // DATAMONGO-1166, DATAMONGO-2264 - void geoNearShouldHonorReadPreferenceWhenSet() { - - template.setReadPreference(ReadPreference.secondary()); - - NearQuery query = NearQuery.near(new Point(1, 1)); - template.geoNear(query, Wrapper.class); - - verify(collection).withReadPreference(eq(ReadPreference.secondary())); - } - - @Test // DATAMONGO-1166, DATAMONGO-2264 - void geoNearShouldIgnoreReadPreferenceWhenNotSet() { - - NearQuery query = NearQuery.near(new Point(1, 1)); - template.geoNear(query, Wrapper.class); - - verify(collection, never()).withReadPreference(any()); - } - - @Test // DATAMONGO-1334 - @Disabled("TODO: mongo3 - a bit hard to tests with the immutable object stuff") - void mapReduceShouldUseZeroAsDefaultLimit() { - - MongoCursor cursor = mock(MongoCursor.class); - MapReduceIterable output = mock(MapReduceIterable.class); - when(output.limit(anyInt())).thenReturn(output); - when(output.sort(any(Document.class))).thenReturn(output); - when(output.filter(any(Document.class))).thenReturn(output); - when(output.iterator()).thenReturn(cursor); - when(cursor.hasNext()).thenReturn(false); - - when(collection.mapReduce(anyString(), anyString())).thenReturn(output); - - Query query = new BasicQuery("{'foo':'bar'}"); - - template.mapReduce(query, "collection", "function(){}", "function(key,values){}", Wrapper.class); - - verify(output, times(1)).limit(1); - } - - @Test // DATAMONGO-1334 - void mapReduceShouldPickUpLimitFromQuery() { - - MongoCursor cursor = mock(MongoCursor.class); - MapReduceIterable output = mock(MapReduceIterable.class); - when(output.limit(anyInt())).thenReturn(output); - when(output.sort(any())).thenReturn(output); - when(output.filter(any(Document.class))).thenReturn(output); - when(output.iterator()).thenReturn(cursor); - when(cursor.hasNext()).thenReturn(false); - - when(collection.mapReduce(anyString(), anyString(), eq(Document.class))).thenReturn(output); - - Query query = new BasicQuery("{'foo':'bar'}"); - query.limit(100); - - template.mapReduce(query, "collection", "function(){}", "function(key,values){}", Wrapper.class); - - verify(output, times(1)).limit(100); - } - - @Test // DATAMONGO-1334 - void mapReduceShouldPickUpLimitFromOptions() { - - MongoCursor cursor = mock(MongoCursor.class); - MapReduceIterable output = mock(MapReduceIterable.class); - when(output.limit(anyInt())).thenReturn(output); - when(output.sort(any())).thenReturn(output); - when(output.filter(any(Document.class))).thenReturn(output); - when(output.iterator()).thenReturn(cursor); - when(cursor.hasNext()).thenReturn(false); - - when(collection.mapReduce(anyString(), anyString(), eq(Document.class))).thenReturn(output); - - Query query = new BasicQuery("{'foo':'bar'}"); - - template.mapReduce(query, "collection", "function(){}", "function(key,values){}", - new MapReduceOptions().limit(1000), Wrapper.class); - - verify(output, times(1)).limit(1000); - } - - @Test // DATAMONGO-1334 - void mapReduceShouldPickUpLimitFromOptionsWhenQueryIsNotPresent() { - - MongoCursor cursor = mock(MongoCursor.class); - MapReduceIterable output = mock(MapReduceIterable.class); - when(output.limit(anyInt())).thenReturn(output); - when(output.sort(any())).thenReturn(output); - when(output.filter(any())).thenReturn(output); - when(output.iterator()).thenReturn(cursor); - when(cursor.hasNext()).thenReturn(false); - - when(collection.mapReduce(anyString(), anyString(), eq(Document.class))).thenReturn(output); - - template.mapReduce("collection", "function(){}", "function(key,values){}", new MapReduceOptions().limit(1000), - Wrapper.class); - - verify(output, times(1)).limit(1000); - } - - @Test // DATAMONGO-1334 - void mapReduceShouldPickUpLimitFromOptionsEvenWhenQueryDefinesItDifferently() { - - MongoCursor cursor = mock(MongoCursor.class); - MapReduceIterable output = mock(MapReduceIterable.class); - when(output.limit(anyInt())).thenReturn(output); - when(output.sort(any())).thenReturn(output); - when(output.filter(any(Document.class))).thenReturn(output); - when(output.iterator()).thenReturn(cursor); - when(cursor.hasNext()).thenReturn(false); - - when(collection.mapReduce(anyString(), anyString(), eq(Document.class))).thenReturn(output); - - Query query = new BasicQuery("{'foo':'bar'}"); - query.limit(100); - - template.mapReduce(query, "collection", "function(){}", "function(key,values){}", - new MapReduceOptions().limit(1000), Wrapper.class); - - verify(output, times(1)).limit(1000); - } - - @Test // DATAMONGO-1639 - void beforeConvertEventForUpdateSeesNextVersion() { - - when(updateResult.getModifiedCount()).thenReturn(1L); - - final VersionedEntity entity = new VersionedEntity(); - entity.id = 1; - entity.version = 0; - - GenericApplicationContext context = new GenericApplicationContext(); - context.refresh(); - context.addApplicationListener(new AbstractMongoEventListener() { - - @Override - public void onBeforeConvert(BeforeConvertEvent event) { - assertThat(event.getSource().version).isEqualTo(1); - } - }); - - template.setApplicationContext(context); - - template.save(entity); - } - - @Test // DATAMONGO-1447 - void shouldNotAppend$isolatedToNonMulitUpdate() { - - template.updateFirst(new Query(), new Update().isolated().set("jon", "snow"), Wrapper.class); - - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor updateCaptor = ArgumentCaptor.forClass(Bson.class); - - verify(collection).updateOne(queryCaptor.capture(), updateCaptor.capture(), any()); - - assertThat((Document) queryCaptor.getValue()).doesNotContainKey("$isolated"); - assertThat((Document) updateCaptor.getValue()).containsEntry("$set.jon", "snow").doesNotContainKey("$isolated"); - } - - @Test // DATAMONGO-1447 - void shouldAppend$isolatedToUpdateMultiEmptyQuery() { - - template.updateMulti(new Query(), new Update().isolated().set("jon", "snow"), Wrapper.class); - - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor updateCaptor = ArgumentCaptor.forClass(Bson.class); - - verify(collection).updateMany(queryCaptor.capture(), updateCaptor.capture(), any()); - - assertThat((Document) queryCaptor.getValue()).hasSize(1).containsEntry("$isolated", 1); - assertThat((Document) updateCaptor.getValue()).containsEntry("$set.jon", "snow").doesNotContainKey("$isolated"); - } - - @Test // DATAMONGO-1447 - void shouldAppend$isolatedToUpdateMultiQueryIfNotPresentAndUpdateSetsValue() { - - Update update = new Update().isolated().set("jon", "snow"); - Query query = new BasicQuery("{'eddard':'stark'}"); - - template.updateMulti(query, update, Wrapper.class); - - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor updateCaptor = ArgumentCaptor.forClass(Bson.class); - - verify(collection).updateMany(queryCaptor.capture(), updateCaptor.capture(), any()); - - assertThat((Document) queryCaptor.getValue()).containsEntry("$isolated", 1).containsEntry("eddard", "stark"); - assertThat((Document) updateCaptor.getValue()).containsEntry("$set.jon", "snow").doesNotContainKey("$isolated"); - } - - @Test // DATAMONGO-1447 - void shouldNotAppend$isolatedToUpdateMultiQueryIfNotPresentAndUpdateDoesNotSetValue() { - - Update update = new Update().set("jon", "snow"); - Query query = new BasicQuery("{'eddard':'stark'}"); - - template.updateMulti(query, update, Wrapper.class); - - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor updateCaptor = ArgumentCaptor.forClass(Bson.class); - - verify(collection).updateMany(queryCaptor.capture(), updateCaptor.capture(), any()); - - assertThat((Document) queryCaptor.getValue()).doesNotContainKey("$isolated").containsEntry("eddard", "stark"); - assertThat((Document) updateCaptor.getValue()).containsEntry("$set.jon", "snow").doesNotContainKey("$isolated"); - } - - @Test // DATAMONGO-1447 - void shouldNotOverwrite$isolatedToUpdateMultiQueryIfPresentAndUpdateDoesNotSetValue() { - - Update update = new Update().set("jon", "snow"); - Query query = new BasicQuery("{'eddard':'stark', '$isolated' : 1}"); - - template.updateMulti(query, update, Wrapper.class); - - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor updateCaptor = ArgumentCaptor.forClass(Bson.class); - - verify(collection).updateMany(queryCaptor.capture(), updateCaptor.capture(), any()); - - assertThat((Document) queryCaptor.getValue()).containsEntry("$isolated", 1).containsEntry("eddard", "stark"); - assertThat((Document) updateCaptor.getValue()).containsEntry("$set.jon", "snow").doesNotContainKey("$isolated"); - } - - @Test // DATAMONGO-1447 - void shouldNotOverwrite$isolatedToUpdateMultiQueryIfPresentAndUpdateSetsValue() { - - Update update = new Update().isolated().set("jon", "snow"); - Query query = new BasicQuery("{'eddard':'stark', '$isolated' : 0}"); - - template.updateMulti(query, update, Wrapper.class); - - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor updateCaptor = ArgumentCaptor.forClass(Bson.class); - - verify(collection).updateMany(queryCaptor.capture(), updateCaptor.capture(), any()); - - assertThat((Document) queryCaptor.getValue()).containsEntry("$isolated", 0).containsEntry("eddard", "stark"); - assertThat((Document) updateCaptor.getValue()).containsEntry("$set.jon", "snow").doesNotContainKey("$isolated"); - } - - @Test // DATAMONGO-1311 - void executeQueryShouldUseBatchSizeWhenPresent() { - - when(findIterable.batchSize(anyInt())).thenReturn(findIterable); - - Query query = new Query().cursorBatchSize(1234); - template.find(query, Person.class); - - verify(findIterable).batchSize(1234); - } - - @Test // DATAMONGO-1518 - void executeQueryShouldUseCollationWhenPresent() { - - template.executeQuery(new BasicQuery("{}").collation(Collation.of("fr")), "collection-1", val -> {}); - - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1518 - void streamQueryShouldUseCollationWhenPresent() { - - template.stream(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class).next(); - - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1518 - void findShouldUseCollationWhenPresent() { - - template.find(new BasicQuery("{'foo' : 'bar'}").collation(Collation.of("fr")), AutogenerateableId.class); - - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1518 - void findOneShouldUseCollationWhenPresent() { - - template.findOne(new BasicQuery("{'foo' : 'bar'}").collation(Collation.of("fr")), AutogenerateableId.class); - - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1518 - void existsShouldUseCollationWhenPresent() { - - template.exists(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); - } - - @Test // DATAMONGO-1518 - void findAndModfiyShoudUseCollationWhenPresent() { - - template.findAndModify(new BasicQuery("{}").collation(Collation.of("fr")), new Update(), AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndUpdateOptions.class); - verify(collection).findOneAndUpdate(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518 - void findAndRemoveShouldUseCollationWhenPresent() { - - template.findAndRemove(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndDeleteOptions.class); - verify(collection).findOneAndDelete(any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-2196 - void removeShouldApplyWriteConcern() { - - Person person = new Person(); - person.id = "id-1"; - - template.setWriteConcern(WriteConcern.UNACKNOWLEDGED); - template.remove(person); - - verify(collection).withWriteConcern(eq(WriteConcern.UNACKNOWLEDGED)); - verify(collectionWithWriteConcern).deleteOne(any(Bson.class), any()); - } - - @Test // DATAMONGO-1518 - void findAndRemoveManyShouldUseCollationWhenPresent() { - - template.doRemove("collection-1", new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class, - true); - - ArgumentCaptor options = ArgumentCaptor.forClass(DeleteOptions.class); - verify(collection).deleteMany(any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518 - void updateOneShouldUseCollationWhenPresent() { - - template.updateFirst(new BasicQuery("{}").collation(Collation.of("fr")), new Update().set("foo", "bar"), - AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateOne(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518 - void updateManyShouldUseCollationWhenPresent() { - - template.updateMulti(new BasicQuery("{}").collation(Collation.of("fr")), new Update().set("foo", "bar"), - AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateMany(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518 - void replaceOneShouldUseCollationWhenPresent() { - - template.updateFirst(new BasicQuery("{}").collation(Collation.of("fr")), new Update(), AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(ReplaceOptions.class); - verify(collection).replaceOne(any(), any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518, DATAMONGO-1824 - void aggregateShouldUseCollationWhenPresent() { - - Aggregation aggregation = newAggregation(project("id")) - .withOptions(newAggregationOptions().collation(Collation.of("fr")).build()); - template.aggregate(aggregation, AutogenerateableId.class, Document.class); - - verify(aggregateIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1824 - void aggregateShouldUseBatchSizeWhenPresent() { - - Aggregation aggregation = newAggregation(project("id")) - .withOptions(newAggregationOptions().collation(Collation.of("fr")).cursorBatchSize(100).build()); - template.aggregate(aggregation, AutogenerateableId.class, Document.class); - - verify(aggregateIterable).batchSize(100); - } - - @Test // DATAMONGO-1518 - void mapReduceShouldUseCollationWhenPresent() { - - template.mapReduce("", "", "", MapReduceOptions.options().collation(Collation.of("fr")), AutogenerateableId.class); - - verify(mapReduceIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-2027 - void mapReduceShouldUseOutputCollectionWhenPresent() { - - template.mapReduce("", "", "", MapReduceOptions.options().outputCollection("out-collection"), - AutogenerateableId.class); - - verify(mapReduceIterable).collectionName(eq("out-collection")); - } - - @Test // DATAMONGO-2027 - void mapReduceShouldNotUseOutputCollectionForInline() { - - template.mapReduce("", "", "", MapReduceOptions.options().outputCollection("out-collection").outputTypeInline(), - AutogenerateableId.class); - - verify(mapReduceIterable, never()).collectionName(any()); - } - - @Test // DATAMONGO-2027 - void mapReduceShouldUseOutputActionWhenPresent() { - - template.mapReduce("", "", "", MapReduceOptions.options().outputCollection("out-collection").outputTypeMerge(), - AutogenerateableId.class); - - verify(mapReduceIterable).action(eq(MapReduceAction.MERGE)); - } - - @Test // DATAMONGO-2027 - void mapReduceShouldUseOutputDatabaseWhenPresent() { - - template.mapReduce("", "", "", - MapReduceOptions.options().outputDatabase("out-database").outputCollection("out-collection").outputTypeMerge(), - AutogenerateableId.class); - - verify(mapReduceIterable).databaseName(eq("out-database")); - } - - @Test // DATAMONGO-2027 - void mapReduceShouldNotUseOutputDatabaseForInline() { - - template.mapReduce("", "", "", MapReduceOptions.options().outputDatabase("out-database").outputTypeInline(), - AutogenerateableId.class); - - verify(mapReduceIterable, never()).databaseName(any()); - } - - @Test // DATAMONGO-1518, DATAMONGO-2264 - void geoNearShouldUseCollationWhenPresent() { - - NearQuery query = NearQuery.near(0D, 0D).query(new BasicQuery("{}").collation(Collation.of("fr"))); - template.geoNear(query, AutogenerateableId.class); - - verify(aggregateIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1518 - void groupShouldUseCollationWhenPresent() { - - commandResultDocument.append("retval", Collections.emptySet()); - template.group("collection-1", GroupBy.key("id").reduceFunction("bar").collation(Collation.of("fr")), - AutogenerateableId.class); - - ArgumentCaptor cmd = ArgumentCaptor.forClass(Document.class); - verify(db).runCommand(cmd.capture(), any(Class.class)); - - assertThat(cmd.getValue().get("group", Document.class).get("collation", Document.class)) - .isEqualTo(new Document("locale", "fr")); - } - - @Test // DATAMONGO-1880 - void countShouldUseCollationWhenPresent() { - - template.count(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); - } - - @Test // DATAMONGO-2360 - void countShouldApplyQueryHintIfPresent() { - - Document queryHint = new Document("age", 1); - template.count(new BasicQuery("{}").withHint(queryHint), AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getHint()).isEqualTo(queryHint); - } - - @Test // DATAMONGO-2365 - void countShouldApplyQueryHintAsIndexNameIfPresent() { - - template.count(new BasicQuery("{}").withHint("idx-1"), AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getHintString()).isEqualTo("idx-1"); - } - - @Test // DATAMONGO-1733 - void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, PersonProjection.class, - CursorPreparer.NO_OP_PREPARER); - - verify(findIterable).projection(eq(new Document("firstname", 1))); - } - - @Test // DATAMONGO-1733 - void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() { - - template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, PersonProjection.class, - CursorPreparer.NO_OP_PREPARER); - - verify(findIterable).projection(eq(new Document("bar", 1))); - } - - @Test // DATAMONGO-1733 - void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, PersonSpELProjection.class, - CursorPreparer.NO_OP_PREPARER); - - verify(findIterable).projection(eq(new Document())); - } - - @Test // DATAMONGO-1733, DATAMONGO-2041 - void appliesFieldsToDtoProjection() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, Jedi.class, - CursorPreparer.NO_OP_PREPARER); - - verify(findIterable).projection(eq(new Document("firstname", 1))); - } - - @Test // DATAMONGO-1733 - void doesNotApplyFieldsToDtoProjectionWhenQueryDefinesFields() { - - template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, Jedi.class, - CursorPreparer.NO_OP_PREPARER); - - verify(findIterable).projection(eq(new Document("bar", 1))); - } - - @Test // DATAMONGO-1733 - void doesNotApplyFieldsWhenTargetIsNotAProjection() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, Person.class, - CursorPreparer.NO_OP_PREPARER); - - verify(findIterable).projection(eq(new Document())); - } - - @Test // DATAMONGO-1733 - void doesNotApplyFieldsWhenTargetExtendsDomainType() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, PersonExtended.class, - CursorPreparer.NO_OP_PREPARER); - - verify(findIterable).projection(eq(new Document())); - } - - @Test // DATAMONGO-1348, DATAMONGO-2264 - void geoNearShouldMapQueryCorrectly() { - - NearQuery query = NearQuery.near(new Point(1, 1)); - query.query(Query.query(Criteria.where("customName").is("rand al'thor"))); - - template.geoNear(query, WithNamedFields.class); - - ArgumentCaptor> capture = ArgumentCaptor.forClass(List.class); - - verify(collection).aggregate(capture.capture(), eq(Document.class)); - Document $geoNear = capture.getValue().iterator().next(); - - assertThat($geoNear).containsEntry("$geoNear.query.custom-named-field", "rand al'thor") - .doesNotContainKey("query.customName"); - } - - @Test // DATAMONGO-1348, DATAMONGO-2264 - void geoNearShouldMapGeoJsonPointCorrectly() { - - NearQuery query = NearQuery.near(new GeoJsonPoint(1, 2)); - query.query(Query.query(Criteria.where("customName").is("rand al'thor"))); - - template.geoNear(query, WithNamedFields.class); - - ArgumentCaptor> capture = ArgumentCaptor.forClass(List.class); - - verify(collection).aggregate(capture.capture(), eq(Document.class)); - Document $geoNear = capture.getValue().iterator().next(); - - assertThat($geoNear).containsEntry("$geoNear.near.type", "Point").containsEntry("$geoNear.near.coordinates.[0]", 1D) - .containsEntry("$geoNear.near.coordinates.[1]", 2D); - } - - @Test // DATAMONGO-2155 - void saveVersionedEntityShouldCallUpdateCorrectly() { - - when(updateResult.getModifiedCount()).thenReturn(1L); - - VersionedEntity entity = new VersionedEntity(); - entity.id = 1; - entity.version = 10; - - ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(org.bson.Document.class); - ArgumentCaptor updateCaptor = ArgumentCaptor.forClass(org.bson.Document.class); - - template.save(entity); - - verify(collection, times(1)).replaceOne(queryCaptor.capture(), updateCaptor.capture(), any(ReplaceOptions.class)); - - assertThat(queryCaptor.getValue()).isEqualTo(new Document("_id", 1).append("version", 10)); - assertThat(updateCaptor.getValue()) - .isEqualTo(new Document("version", 11).append("_class", VersionedEntity.class.getName())); - } - - @Test // DATAMONGO-1783 - void usesQueryOffsetForCountOperation() { - - template.count(new BasicQuery("{}").skip(100), AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getSkip()).isEqualTo(100); - } - - @Test // DATAMONGO-1783 - void usesQueryLimitForCountOperation() { - - template.count(new BasicQuery("{}").limit(10), AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getLimit()).isEqualTo(10); - } - - @Test // DATAMONGO-2215 - void updateShouldApplyArrayFilters() { - - template.updateFirst(new BasicQuery("{}"), - new Update().set("grades.$[element]", 100).filterArray(Criteria.where("element").gte(100)), - EntityWithListOfSimple.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateOne(any(), any(Bson.class), options.capture()); - - Assertions.assertThat((List) options.getValue().getArrayFilters()) - .contains(new org.bson.Document("element", new Document("$gte", 100))); - } - - @Test // DATAMONGO-2215 - void findAndModifyShouldApplyArrayFilters() { - - template.findAndModify(new BasicQuery("{}"), - new Update().set("grades.$[element]", 100).filterArray(Criteria.where("element").gte(100)), - EntityWithListOfSimple.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndUpdateOptions.class); - verify(collection).findOneAndUpdate(any(), any(Bson.class), options.capture()); - - Assertions.assertThat((List) options.getValue().getArrayFilters()) - .contains(new org.bson.Document("element", new Document("$gte", 100))); - } - - @Test // DATAMONGO-1854 - void streamQueryShouldUseDefaultCollationWhenPresent() { - - template.stream(new BasicQuery("{}"), Sith.class).next(); - - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void findShouldNotUseCollationWhenNoDefaultPresent() { - - template.find(new BasicQuery("{'foo' : 'bar'}"), Jedi.class); - - verify(findIterable, never()).collation(any()); - } - - @Test // DATAMONGO-1854 - void findShouldUseDefaultCollationWhenPresent() { - - template.find(new BasicQuery("{'foo' : 'bar'}"), Sith.class); - - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void findOneShouldUseDefaultCollationWhenPresent() { - - template.findOne(new BasicQuery("{'foo' : 'bar'}"), Sith.class); - - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void existsShouldUseDefaultCollationWhenPresent() { - - template.exists(new BasicQuery("{}"), Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void findAndModfiyShoudUseDefaultCollationWhenPresent() { - - template.findAndModify(new BasicQuery("{}"), new Update(), Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndUpdateOptions.class); - verify(collection).findOneAndUpdate(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void findAndRemoveShouldUseDefaultCollationWhenPresent() { - - template.findAndRemove(new BasicQuery("{}"), Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndDeleteOptions.class); - verify(collection).findOneAndDelete(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void createCollectionShouldNotCollationIfNotPresent() { - - template.createCollection(AutogenerateableId.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(CreateCollectionOptions.class); - verify(db).createCollection(any(), options.capture()); - - Assertions.assertThat(options.getValue().getCollation()).isNull(); - } - - @Test // DATAMONGO-1854 - void createCollectionShouldApplyDefaultCollation() { - - template.createCollection(Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(CreateCollectionOptions.class); - verify(db).createCollection(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void createCollectionShouldFavorExplicitOptionsOverDefaultCollation() { - - template.createCollection(Sith.class, CollectionOptions.just(Collation.of("en_US"))); - - ArgumentCaptor options = ArgumentCaptor.forClass(CreateCollectionOptions.class); - verify(db).createCollection(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("en_US").build()); - } - - @Test // DATAMONGO-1854 - void createCollectionShouldUseDefaultCollationIfCollectionOptionsAreNull() { - - template.createCollection(Sith.class, null); - - ArgumentCaptor options = ArgumentCaptor.forClass(CreateCollectionOptions.class); - verify(db).createCollection(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void aggreateShouldUseDefaultCollationIfPresent() { - - template.aggregate(newAggregation(Sith.class, project("id")), AutogenerateableId.class, Document.class); - - verify(aggregateIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void aggreateShouldUseCollationFromOptionsEvenIfDefaultCollationIsPresent() { - - template.aggregateStream(newAggregation(Sith.class, project("id")).withOptions( - newAggregationOptions().collation(Collation.of("fr")).build()), AutogenerateableId.class, Document.class); - - verify(aggregateIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1854 - void aggreateStreamShouldUseDefaultCollationIfPresent() { - - template.aggregate(newAggregation(Sith.class, project("id")), AutogenerateableId.class, Document.class); - - verify(aggregateIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void aggreateStreamShouldUseCollationFromOptionsEvenIfDefaultCollationIsPresent() { - - template.aggregateStream(newAggregation(Sith.class, project("id")).withOptions( - newAggregationOptions().collation(Collation.of("fr")).build()), AutogenerateableId.class, Document.class); - - verify(aggregateIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-2390 - void aggregateShouldNoApplyZeroOrNegativeMaxTime() { - - template.aggregate( - newAggregation(Sith.class, project("id")).withOptions(newAggregationOptions().maxTime(Duration.ZERO).build()), - AutogenerateableId.class, Document.class); - template.aggregate(newAggregation(Sith.class, project("id")).withOptions( - newAggregationOptions().maxTime(Duration.ofSeconds(-1)).build()), AutogenerateableId.class, Document.class); - - verify(aggregateIterable, never()).maxTime(anyLong(), any()); - } - - @Test // DATAMONGO-2390 - void aggregateShouldApplyMaxTimeIfSet() { - - template.aggregate(newAggregation(Sith.class, project("id")).withOptions( - newAggregationOptions().maxTime(Duration.ofSeconds(10)).build()), AutogenerateableId.class, Document.class); - - verify(aggregateIterable).maxTime(eq(10000L), eq(TimeUnit.MILLISECONDS)); - } - - @Test // DATAMONGO-1854 - void findAndReplaceShouldUseCollationWhenPresent() { - - template.findAndReplace(new BasicQuery("{}").collation(Collation.of("fr")), new AutogenerateableId()); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndReplaceOptions.class); - verify(collection).findOneAndReplace(any(), any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1854 - void findOneWithSortShouldUseCollationWhenPresent() { - - template.findOne(new BasicQuery("{}").collation(Collation.of("fr")).with(Sort.by("id")), Sith.class); - - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1854 - void findOneWithSortShouldUseDefaultCollationWhenPresent() { - - template.findOne(new BasicQuery("{}").with(Sort.by("id")), Sith.class); - - verify(findIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void findAndReplaceShouldUseDefaultCollationWhenPresent() { - - template.findAndReplace(new BasicQuery("{}"), new Sith()); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndReplaceOptions.class); - verify(collection).findOneAndReplace(any(), any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("de_AT"); - } - - @Test // DATAMONGO-1854 - void findAndReplaceShouldUseCollationEvenIfDefaultCollationIsPresent() { - - template.findAndReplace(new BasicQuery("{}").collation(Collation.of("fr")), new Sith()); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndReplaceOptions.class); - verify(collection).findOneAndReplace(any(), any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1854 - void findDistinctShouldUseDefaultCollationWhenPresent() { - - template.findDistinct(new BasicQuery("{}"), "name", Sith.class, String.class); - - verify(distinctIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void findDistinctPreferCollationFromQueryOverDefaultCollation() { - - template.findDistinct(new BasicQuery("{}").collation(Collation.of("fr")), "name", Sith.class, String.class); - - verify(distinctIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1854 - void updateFirstShouldUseDefaultCollationWhenPresent() { - - template.updateFirst(new BasicQuery("{}"), Update.update("foo", "bar"), Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateOne(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void updateFirstShouldPreferExplicitCollationOverDefaultCollation() { - - template.updateFirst(new BasicQuery("{}").collation(Collation.of("fr")), Update.update("foo", "bar"), Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateOne(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); - } - - @Test // DATAMONGO-1854 - void updateMultiShouldUseDefaultCollationWhenPresent() { - - template.updateMulti(new BasicQuery("{}"), Update.update("foo", "bar"), Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateMany(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void updateMultiShouldPreferExplicitCollationOverDefaultCollation() { - - template.updateMulti(new BasicQuery("{}").collation(Collation.of("fr")), Update.update("foo", "bar"), Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateMany(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); - } - - @Test // DATAMONGO-1854 - void removeShouldUseDefaultCollationWhenPresent() { - - template.remove(new BasicQuery("{}"), Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(DeleteOptions.class); - verify(collection).deleteMany(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void removeShouldPreferExplicitCollationOverDefaultCollation() { - - template.remove(new BasicQuery("{}").collation(Collation.of("fr")), Sith.class); - - ArgumentCaptor options = ArgumentCaptor.forClass(DeleteOptions.class); - verify(collection).deleteMany(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); - } - - @Test // DATAMONGO-1854 - void mapReduceShouldUseDefaultCollationWhenPresent() { - - template.mapReduce("", "", "", MapReduceOptions.options(), Sith.class); - - verify(mapReduceIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void mapReduceShouldPreferExplicitCollationOverDefaultCollation() { - - template.mapReduce("", "", "", MapReduceOptions.options().collation(Collation.of("fr")), Sith.class); - - verify(mapReduceIterable).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-2261 - void saveShouldInvokeCallbacks() { - - ValueCapturingBeforeConvertCallback beforeConvertCallback = spy(new ValueCapturingBeforeConvertCallback()); - ValueCapturingBeforeSaveCallback beforeSaveCallback = spy(new ValueCapturingBeforeSaveCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(beforeConvertCallback, beforeSaveCallback)); - - Person entity = new Person(); - entity.id = "init"; - entity.firstname = "luke"; - - template.save(entity); - - verify(beforeConvertCallback).onBeforeConvert(eq(entity), anyString()); - verify(beforeSaveCallback).onBeforeSave(eq(entity), any(), anyString()); - } - - @Test // DATAMONGO-2261 - void insertShouldInvokeCallbacks() { - - ValueCapturingBeforeConvertCallback beforeConvertCallback = spy(new ValueCapturingBeforeConvertCallback()); - ValueCapturingBeforeSaveCallback beforeSaveCallback = spy(new ValueCapturingBeforeSaveCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(beforeConvertCallback, beforeSaveCallback)); - - Person entity = new Person(); - entity.id = "init"; - entity.firstname = "luke"; - - template.insert(entity); - - verify(beforeConvertCallback).onBeforeConvert(eq(entity), anyString()); - verify(beforeSaveCallback).onBeforeSave(eq(entity), any(), anyString()); - } - - @Test // DATAMONGO-2261 - void insertAllShouldInvokeCallbacks() { - - ValueCapturingBeforeConvertCallback beforeConvertCallback = spy(new ValueCapturingBeforeConvertCallback()); - ValueCapturingBeforeSaveCallback beforeSaveCallback = spy(new ValueCapturingBeforeSaveCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(beforeConvertCallback, beforeSaveCallback)); - - Person entity1 = new Person(); - entity1.id = "1"; - entity1.firstname = "luke"; - - Person entity2 = new Person(); - entity1.id = "2"; - entity1.firstname = "luke"; - - template.insertAll(Arrays.asList(entity1, entity2)); - - verify(beforeConvertCallback, times(2)).onBeforeConvert(any(), anyString()); - verify(beforeSaveCallback, times(2)).onBeforeSave(any(), any(), anyString()); - } - - @Test // DATAMONGO-2261 - void findAndReplaceShouldInvokeCallbacks() { - - ValueCapturingBeforeConvertCallback beforeConvertCallback = spy(new ValueCapturingBeforeConvertCallback()); - ValueCapturingBeforeSaveCallback beforeSaveCallback = spy(new ValueCapturingBeforeSaveCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(beforeConvertCallback, beforeSaveCallback)); - - Person entity = new Person(); - entity.id = "init"; - entity.firstname = "luke"; - - template.findAndReplace(new Query(), entity); - - verify(beforeConvertCallback).onBeforeConvert(eq(entity), anyString()); - verify(beforeSaveCallback).onBeforeSave(eq(entity), any(), anyString()); - } - - @Test // DATAMONGO-2261 - void publishesEventsAndEntityCallbacksInOrder() { - - BeforeConvertCallback beforeConvertCallback = new BeforeConvertCallback() { - - @Override - public Person onBeforeConvert(Person entity, String collection) { - - assertThat(entity.id).isEqualTo("before-convert-event"); - entity.id = "before-convert-callback"; - return entity; - } - }; - - BeforeSaveCallback beforeSaveCallback = new BeforeSaveCallback() { - - @Override - public Person onBeforeSave(Person entity, Document document, String collection) { - - assertThat(entity.id).isEqualTo("before-save-event"); - entity.id = "before-save-callback"; - return entity; - } - }; - - AbstractMongoEventListener eventListener = new AbstractMongoEventListener() { - - @Override - public void onBeforeConvert(BeforeConvertEvent event) { - - assertThat(event.getSource().id).isEqualTo("init"); - event.getSource().id = "before-convert-event"; - } - - @Override - public void onBeforeSave(BeforeSaveEvent event) { - - assertThat(event.getSource().id).isEqualTo("before-convert-callback"); - event.getSource().id = "before-save-event"; - } - }; - - StaticApplicationContext ctx = new StaticApplicationContext(); - ctx.registerBean(ApplicationListener.class, () -> eventListener); - ctx.registerBean(BeforeConvertCallback.class, () -> beforeConvertCallback); - ctx.registerBean(BeforeSaveCallback.class, () -> beforeSaveCallback); - ctx.refresh(); - - template.setApplicationContext(ctx); - - Person entity = new Person(); - entity.id = "init"; - entity.firstname = "luke"; - - Person saved = template.save(entity); - - assertThat(saved.id).isEqualTo("before-save-callback"); - } - - @Test // DATAMONGO-2261 - void beforeSaveCallbackAllowsTargetDocumentModifications() { - - BeforeSaveCallback beforeSaveCallback = new BeforeSaveCallback() { - - @Override - public Person onBeforeSave(Person entity, Document document, String collection) { - - document.append("added-by", "callback"); - return entity; - } - }; - - StaticApplicationContext ctx = new StaticApplicationContext(); - ctx.registerBean(BeforeSaveCallback.class, () -> beforeSaveCallback); - ctx.refresh(); - - template.setApplicationContext(ctx); - - Person entity = new Person(); - entity.id = "luke-skywalker"; - entity.firstname = "luke"; - - template.save(entity); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Document.class); - - verify(collection).replaceOne(any(), captor.capture(), any(ReplaceOptions.class)); - assertThat(captor.getValue()).containsEntry("added-by", "callback"); - } - - @Test // DATAMONGO-2307 - void beforeSaveCallbackAllowsTargetEntityModificationsUsingSave() { - - StaticApplicationContext ctx = new StaticApplicationContext(); - ctx.registerBean(BeforeSaveCallback.class, this::beforeSaveCallbackReturningNewPersonWithTransientAttribute); - ctx.refresh(); - - template.setApplicationContext(ctx); - - PersonWithTransientAttribute entity = new PersonWithTransientAttribute(); - entity.id = "luke-skywalker"; - entity.firstname = "luke"; - entity.isNew = true; - - PersonWithTransientAttribute savedPerson = template.save(entity); - assertThat(savedPerson.isNew).isFalse(); - } - - @Test // DATAMONGO-2307 - void beforeSaveCallbackAllowsTargetEntityModificationsUsingInsert() { - - StaticApplicationContext ctx = new StaticApplicationContext(); - ctx.registerBean(BeforeSaveCallback.class, this::beforeSaveCallbackReturningNewPersonWithTransientAttribute); - ctx.refresh(); - - template.setApplicationContext(ctx); - - PersonWithTransientAttribute entity = new PersonWithTransientAttribute(); - entity.id = "luke-skywalker"; - entity.firstname = "luke"; - entity.isNew = true; - - PersonWithTransientAttribute savedPerson = template.insert(entity); - assertThat(savedPerson.isNew).isFalse(); - } - - // TODO: additional tests for what is when saved. - - @Test // DATAMONGO-2261 - void entityCallbacksAreNotSetByDefault() { - Assertions.assertThat(ReflectionTestUtils.getField(template, "entityCallbacks")).isNull(); - } - - @Test // DATAMONGO-2261 - void entityCallbacksShouldBeInitiatedOnSettingApplicationContext() { - - ApplicationContext ctx = new StaticApplicationContext(); - template.setApplicationContext(ctx); - - Assertions.assertThat(ReflectionTestUtils.getField(template, "entityCallbacks")).isNotNull(); - } - - @Test // DATAMONGO-2261 - void setterForEntityCallbackOverridesContextInitializedOnes() { - - ApplicationContext ctx = new StaticApplicationContext(); - template.setApplicationContext(ctx); - - EntityCallbacks callbacks = EntityCallbacks.create(); - template.setEntityCallbacks(callbacks); - - Assertions.assertThat(ReflectionTestUtils.getField(template, "entityCallbacks")).isSameAs(callbacks); - } - - @Test // DATAMONGO-2261 - void setterForApplicationContextShouldNotOverrideAlreadySetEntityCallbacks() { - - EntityCallbacks callbacks = EntityCallbacks.create(); - ApplicationContext ctx = new StaticApplicationContext(); - - template.setEntityCallbacks(callbacks); - template.setApplicationContext(ctx); - - Assertions.assertThat(ReflectionTestUtils.getField(template, "entityCallbacks")).isSameAs(callbacks); - } - - @Test // DATAMONGO-2344, DATAMONGO-2572 - void allowSecondaryReadsQueryOptionShouldApplyPrimaryPreferredReadPreferenceForFind() { - - template.find(new Query().allowSecondaryReads(), AutogenerateableId.class); - - verify(collection).withReadPreference(eq(ReadPreference.primaryPreferred())); - } - - @Test // DATAMONGO-2344, DATAMONGO-2572 - void allowSecondaryReadsQueryOptionShouldApplyPrimaryPreferredReadPreferenceForFindOne() { - - template.findOne(new Query().allowSecondaryReads(), AutogenerateableId.class); - - verify(collection).withReadPreference(eq(ReadPreference.primaryPreferred())); - } - - @Test // DATAMONGO-2344, DATAMONGO-2572 - void allowSecondaryReadsQueryOptionShouldApplyPrimaryPreferredReadPreferenceForFindDistinct() { - - template.findDistinct(new Query().allowSecondaryReads(), "name", AutogenerateableId.class, String.class); - - verify(collection).withReadPreference(eq(ReadPreference.primaryPreferred())); - } - - @Test // DATAMONGO-2344, DATAMONGO-2572 - void allowSecondaryReadsQueryOptionShouldApplyPrimaryPreferredReadPreferenceForStream() { - - template.stream(new Query().allowSecondaryReads(), AutogenerateableId.class); - verify(collection).withReadPreference(eq(ReadPreference.primaryPreferred())); - } - - @Test // DATAMONGO-2331 - void updateShouldAllowAggregationExpressions() { - - AggregationUpdate update = AggregationUpdate.update().set("total") - .toValue(ArithmeticOperators.valueOf("val1").sum().and("val2")); - - template.updateFirst(new BasicQuery("{}"), update, Wrapper.class); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).isEqualTo( - Collections.singletonList(Document.parse("{ $set : { total : { $sum : [ \"$val1\",\"$val2\" ] } } }"))); - } - - @Test // DATAMONGO-2331 - void updateShouldAllowMultipleAggregationExpressions() { - - AggregationUpdate update = AggregationUpdate.update() // - .set("average").toValue(ArithmeticOperators.valueOf("tests").avg()) // - .set("grade").toValue(ConditionalOperators.switchCases( // - CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(90)).then("A"), // - CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(80)).then("B"), // - CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(70)).then("C"), // - CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(60)).then("D") // - ) // - .defaultTo("F"));// - - template.updateFirst(new BasicQuery("{}"), update, Wrapper.class); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).containsExactly(Document.parse("{ $set: { average : { $avg: \"$tests\" } } }"), - Document.parse("{ $set: { grade: { $switch: {\n" + " branches: [\n" - + " { case: { $gte: [ \"$average\", 90 ] }, then: \"A\" },\n" - + " { case: { $gte: [ \"$average\", 80 ] }, then: \"B\" },\n" - + " { case: { $gte: [ \"$average\", 70 ] }, then: \"C\" },\n" - + " { case: { $gte: [ \"$average\", 60 ] }, then: \"D\" }\n" - + " ],\n" + " default: \"F\"\n" + " } } } }")); - } - - @Test // DATAMONGO-2331 - void updateShouldMapAggregationExpressionToDomainType() { - - AggregationUpdate update = AggregationUpdate.update().set("name") - .toValue(ArithmeticOperators.valueOf("val1").sum().and("val2")); - - template.updateFirst(new BasicQuery("{}"), update, Jedi.class); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).isEqualTo( - Collections.singletonList(Document.parse("{ $set : { firstname : { $sum:[ \"$val1\",\"$val2\" ] } } }"))); - } - - @Test // DATAMONGO-2331 - void updateShouldPassOnUnsetCorrectly() { - - SetOperation setOperation = SetOperation.builder().set("status").toValue("Modified").and().set("comments") - .toValue(Fields.fields("misc1").and("misc2").asList()); - AggregationUpdate update = AggregationUpdate.update(); - update.set(setOperation); - update.unset("misc1", "misc2"); - - template.updateFirst(new BasicQuery("{}"), update, Wrapper.class); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).isEqualTo( - Arrays.asList(Document.parse("{ $set: { status: \"Modified\", comments: [ \"$misc1\", \"$misc2\" ] } }"), - Document.parse("{ $unset: [ \"misc1\", \"misc2\" ] }"))); - } - - @Test // DATAMONGO-2331 - void updateShouldMapAggregationUnsetToDomainType() { - - AggregationUpdate update = AggregationUpdate.update(); - update.unset("name"); - - template.updateFirst(new BasicQuery("{}"), update, Jedi.class); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).isEqualTo(Collections.singletonList(Document.parse("{ $unset : \"firstname\" }"))); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendNonDefaultShardKeyIfNotPresentInFilter() { - - template.save(new ShardedEntityWithNonDefaultShardKey("id-1", "AT", 4230)); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - verify(collection).replaceOne(filter.capture(), any(), any()); - - assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("country", "AT").append("userid", 4230)); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendNonDefaultShardKeyToVersionedEntityIfNotPresentInFilter() { - - when(collection.replaceOne(any(), any(), any(ReplaceOptions.class))) - .thenReturn(UpdateResult.acknowledged(1, 1L, null)); - - template.save(new ShardedVersionedEntityWithNonDefaultShardKey("id-1", 1L, "AT", 4230)); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - verify(collection).replaceOne(filter.capture(), any(), any()); - - assertThat(filter.getValue()) - .isEqualTo(new Document("_id", "id-1").append("version", 1L).append("country", "AT").append("userid", 4230)); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendNonDefaultShardKeyFromExistingDocumentIfNotPresentInFilter() { - - when(findIterable.first()).thenReturn(new Document("_id", "id-1").append("country", "US").append("userid", 4230)); - - template.save(new ShardedEntityWithNonDefaultShardKey("id-1", "AT", 4230)); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor replacement = ArgumentCaptor.forClass(Document.class); - - verify(collection).replaceOne(filter.capture(), replacement.capture(), any()); - - assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("country", "US").append("userid", 4230)); - assertThat(replacement.getValue()).containsEntry("country", "AT").containsEntry("userid", 4230); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendNonDefaultShardKeyFromGivenDocumentIfShardKeyIsImmutable() { - - template.save(new ShardedEntityWithNonDefaultImmutableShardKey("id-1", "AT", 4230)); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor replacement = ArgumentCaptor.forClass(Document.class); - - verify(collection).replaceOne(filter.capture(), replacement.capture(), any()); - - assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("country", "AT").append("userid", 4230)); - assertThat(replacement.getValue()).containsEntry("country", "AT").containsEntry("userid", 4230); - - verifyNoInteractions(findIterable); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendDefaultShardKeyIfNotPresentInFilter() { - - template.save(new ShardedEntityWithDefaultShardKey("id-1", "AT", 4230)); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - verify(collection).replaceOne(filter.capture(), any(), any()); - - assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1")); - verify(findIterable, never()).first(); - } - - @Test // GH-3590 - void shouldIncludeValueFromNestedShardKeyPath() { - - WithShardKeyPointingToNested source = new WithShardKeyPointingToNested(); - source.id = "id-1"; - source.value = "v1"; - source.nested = new WithNamedFields(); - source.nested.customName = "cname"; - source.nested.name = "name"; - - template.save(source); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - verify(collection).replaceOne(filter.capture(), any(), any()); - - assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("value", "v1").append("nested.custom-named-field", "cname")); - } - - @Test // DATAMONGO-2341 - void saveShouldProjectOnShardKeyWhenLoadingExistingDocument() { - - when(findIterable.first()).thenReturn(new Document("_id", "id-1").append("country", "US").append("userid", 4230)); - - template.save(new ShardedEntityWithNonDefaultShardKey("id-1", "AT", 4230)); - - verify(findIterable).projection(new Document("country", 1).append("userid", 1)); - } - - @Test // DATAMONGO-2341 - void saveVersionedShouldProjectOnShardKeyWhenLoadingExistingDocument() { - - when(collection.replaceOne(any(), any(), any(ReplaceOptions.class))) - .thenReturn(UpdateResult.acknowledged(1, 1L, null)); - when(findIterable.first()).thenReturn(new Document("_id", "id-1").append("country", "US").append("userid", 4230)); - - template.save(new ShardedVersionedEntityWithNonDefaultShardKey("id-1", 1L, "AT", 4230)); - - verify(findIterable).projection(new Document("country", 1).append("userid", 1)); - } - - @Test // DATAMONGO-2479 - void findShouldInvokeAfterConvertCallback() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(findIterable.iterator()).thenReturn(new OneElementCursor<>(document)); - - template.find(new Query(), Person.class); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - } - - @Test // DATAMONGO-2479 - void findByIdShouldInvokeAfterConvertCallback() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(findIterable.first()).thenReturn(document); - - template.findById("init", Person.class); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - } - - @Test // DATAMONGO-2479 - void findOneShouldInvokeAfterConvertCallback() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(findIterable.first()).thenReturn(document); - - template.findOne(new Query(), Person.class); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - } - - @Test // DATAMONGO-2479 - void findAllShouldInvokeAfterConvertCallback() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(findIterable.iterator()).thenReturn(new OneElementCursor<>(document)); - - template.findAll(Person.class); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - } - - @Test // DATAMONGO-2479 - void findAndModifyShouldInvokeAfterConvertCallback() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.findOneAndUpdate(any(Bson.class), any(Bson.class), any())).thenReturn(document); - - template.findAndModify(new Query(), new Update(), Person.class); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - } - - @Test // DATAMONGO-2479 - void findAndRemoveShouldInvokeAfterConvertCallback() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.findOneAndDelete(any(Bson.class), any())).thenReturn(document); - - template.findAndRemove(new Query(), Person.class); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - } - - @Test // DATAMONGO-2479 - void findAllAndRemoveShouldInvokeAfterConvertCallback() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(findIterable.iterator()).thenReturn(new OneElementCursor<>(document)); - - template.findAllAndRemove(new Query(), Person.class); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - } - - @Test // DATAMONGO-2479 - void findAndReplaceShouldInvokeAfterConvertCallbacks() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterConvertCallback)); - - Person entity = new Person("init", "luke"); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.findOneAndReplace(any(Bson.class), any(Document.class), any())).thenReturn(document); - - Person saved = template.findAndReplace(new Query(), entity); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - assertThat(saved.id).isEqualTo("after-convert"); - } - - @Test // DATAMONGO-2479 - void saveShouldInvokeAfterSaveCallbacks() { - - ValueCapturingAfterSaveCallback afterSaveCallback = spy(new ValueCapturingAfterSaveCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterSaveCallback)); - - Person entity = new Person("init", "luke"); - - Person saved = template.save(entity); - - verify(afterSaveCallback).onAfterSave(eq(entity), any(), anyString()); - assertThat(saved.id).isEqualTo("after-save"); - } - - @Test // DATAMONGO-2479 - void insertShouldInvokeAfterSaveCallbacks() { - - ValueCapturingAfterSaveCallback afterSaveCallback = spy(new ValueCapturingAfterSaveCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterSaveCallback)); - - Person entity = new Person("init", "luke"); - - Person saved = template.insert(entity); - - verify(afterSaveCallback).onAfterSave(eq(entity), any(), anyString()); - assertThat(saved.id).isEqualTo("after-save"); - } - - @Test // DATAMONGO-2479 - void insertAllShouldInvokeAfterSaveCallbacks() { - - ValueCapturingAfterSaveCallback afterSaveCallback = spy(new ValueCapturingAfterSaveCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterSaveCallback)); - - Person entity1 = new Person(); - entity1.id = "1"; - entity1.firstname = "luke"; - - Person entity2 = new Person(); - entity1.id = "2"; - entity1.firstname = "luke"; - - Collection saved = template.insertAll(Arrays.asList(entity1, entity2)); - - verify(afterSaveCallback, times(2)).onAfterSave(any(), any(), anyString()); - assertThat(saved.iterator().next().getId()).isEqualTo("after-save"); - } - - @Test // DATAMONGO-2479 - void findAndReplaceShouldInvokeAfterSaveCallbacks() { - - ValueCapturingAfterSaveCallback afterSaveCallback = spy(new ValueCapturingAfterSaveCallback()); - - template.setEntityCallbacks(EntityCallbacks.create(afterSaveCallback)); - - Person entity = new Person("init", "luke"); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.findOneAndReplace(any(Bson.class), any(Document.class), any())).thenReturn(document); - - Person saved = template.findAndReplace(new Query(), entity); - - verify(afterSaveCallback).onAfterSave(eq(new Person("init", "luke")), any(), anyString()); - assertThat(saved.id).isEqualTo("after-save"); - } - - @Test // DATAMONGO-2479 - void findAndReplaceShouldEmitAfterSaveEvent() { - - AbstractMongoEventListener eventListener = new AbstractMongoEventListener() { - - @Override - public void onAfterSave(AfterSaveEvent event) { - - assertThat(event.getSource().id).isEqualTo("init"); - event.getSource().id = "after-save-event"; - } - }; - - StaticApplicationContext ctx = new StaticApplicationContext(); - ctx.registerBean(ApplicationListener.class, () -> eventListener); - ctx.refresh(); - - template.setApplicationContext(ctx); - - Person entity = new Person("init", "luke"); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.findOneAndReplace(any(Bson.class), any(Document.class), any())).thenReturn(document); - - Person saved = template.findAndReplace(new Query(), entity); - - assertThat(saved.id).isEqualTo("after-save-event"); - } - - @Test // DATAMONGO-2556 - void esitmatedCountShouldBeDelegatedCorrectly() { - - template.estimatedCount(Person.class); - - verify(db).getCollection("star-wars", Document.class); - verify(collection).estimatedDocumentCount(any()); - } - - @Test // GH-2911 - void insertErrorsOnCustomIteratorImplementation() { - - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> template.insert(new TypeImplementingIterator())); - } - - @Test // GH-3570 - void saveErrorsOnCollectionLikeObjects() { - - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> template.save(new ArrayList<>(Arrays.asList(1, 2, 3)), "myList")); - } - - class AutogenerateableId { - - @Id BigInteger id; - } - - class NotAutogenerateableId { - - @Id Integer id; - - public Pattern getId() { - return Pattern.compile("."); - } - } - - static class VersionedEntity { - - @Id Integer id; - @Version Integer version; - } - - enum MyConverter implements Converter { - - INSTANCE; - - public String convert(AutogenerateableId source) { - return source.toString(); - } - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = "star-wars") - @AllArgsConstructor - @NoArgsConstructor - static class Person { - - @Id String id; - String firstname; - } - - static class PersonExtended extends Person { - - String lastname; - } - - static class PersonWithTransientAttribute extends Person { - - @Transient boolean isNew = true; - } - - interface PersonProjection { - String getFirstname(); - } - - public interface PersonSpELProjection { - - @Value("#{target.firstname}") - String getName(); - } - - @Data - static class Human { - @Id String id; - } - - @Data - static class Jedi { - - @Field("firstname") String name; - } - - class Wrapper { - - AutogenerateableId foo; - } - - static class EntityWithListOfSimple { - List grades; - } - - static class WithNamedFields { - - @Id String id; - - String name; - @Field("custom-named-field") String customName; - } - - @org.springframework.data.mongodb.core.mapping.Document(collation = "de_AT") - static class Sith { - - @Field("firstname") String name; - } - - @Sharded(shardKey = {"value", "nested.customName"}) - static class WithShardKeyPointingToNested { - String id; - String value; - WithNamedFields nested; - } - - static class TypeImplementingIterator implements Iterator { - - @Override - public boolean hasNext() { - return false; - } - - @Override - public Object next() { - return null; - } - } - - /** - * Mocks out the {@link MongoTemplate#getDb()} method to return the {@link DB} mock instead of executing the actual - * behaviour. - * - * @return - */ - private MongoTemplate mockOutGetDb() { - - MongoTemplate template = spy(this.template); - when(template.getDb()).thenReturn(db); - return template; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.core.core.MongoOperationsUnitTests#getOperations() - */ - @Override - protected MongoOperations getOperationsForExceptionHandling() { - MongoTemplate template = spy(this.template); - lenient().when(template.getDb()).thenThrow(new MongoException("Error!")); - return template; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.core.core.MongoOperationsUnitTests#getOperations() - */ - @Override - protected MongoOperations getOperations() { - return this.template; - } - - private BeforeSaveCallback beforeSaveCallbackReturningNewPersonWithTransientAttribute() { - return (entity, document, collection) -> { - - // Return a completely new instance, ie in case of an immutable entity; - PersonWithTransientAttribute newEntity = new PersonWithTransientAttribute(); - newEntity.id = entity.id; - newEntity.firstname = entity.firstname; - newEntity.isNew = false; - return newEntity; - }; - } - - static class ValueCapturingEntityCallback { - - private final List values = new ArrayList<>(1); - - protected void capture(T value) { - values.add(value); - } - - public List getValues() { - return values; - } - - @Nullable - public T getValue() { - return CollectionUtils.lastElement(values); - } - - } - - static class ValueCapturingBeforeConvertCallback extends ValueCapturingEntityCallback - implements BeforeConvertCallback { - - @Override - public Person onBeforeConvert(Person entity, String collection) { - - capture(entity); - return entity; - } - } - - static class ValueCapturingBeforeSaveCallback extends ValueCapturingEntityCallback - implements BeforeSaveCallback { - - @Override - public Person onBeforeSave(Person entity, Document document, String collection) { - - capture(entity); - return entity; - } - } - - static class ValueCapturingAfterSaveCallback extends ValueCapturingEntityCallback - implements AfterSaveCallback { - - @Override - public Person onAfterSave(Person entity, Document document, String collection) { - - capture(entity); - return new Person() { - { - id = "after-save"; - firstname = entity.firstname; - } - }; - } - } - - static class ValueCapturingAfterConvertCallback extends ValueCapturingEntityCallback - implements AfterConvertCallback { - - @Override - public Person onAfterConvert(Person entity, Document document, String collection) { - - capture(entity); - return new Person() { - { - id = "after-convert"; - firstname = entity.firstname; - } - }; - } - } - - static class OneElementCursor implements MongoCursor { - private final Iterator iterator; - - OneElementCursor(T element) { - iterator = Collections.singletonList(element).iterator(); - } - - @Override - public void close() { - // nothing to close - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public T next() { - return iterator.next(); - } - - @Override - public T tryNext() { - if (iterator.hasNext()) { - return iterator.next(); - } else { - return null; - } - } - - @Override - public ServerCursor getServerCursor() { - throw new IllegalStateException("Not implemented"); - } - - @Override - public ServerAddress getServerAddress() { - throw new IllegalStateException("Not implemented"); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnwrappedTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnwrappedTests.java deleted file mode 100644 index 6d02cf8f84..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnwrappedTests.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2020 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.util.Arrays; -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.Template; - -/** - * Integration tests for {@link Unwrapped}. - * - * @author Christoph Strobl - */ -@ExtendWith(MongoTemplateExtension.class) -class MongoTemplateUnwrappedTests { - - private static @Template MongoTemplate template; - - @Test // DATAMONGO-1902 - void readWrite() { - - WithUnwrapped source = new WithUnwrapped(); - source.id = "id-1"; - source.embeddableValue = new UnwrappableType(); - source.embeddableValue.stringValue = "string-val"; - source.embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - source.embeddableValue.atFieldAnnotatedValue = "@Field"; - - template.save(source); - - assertThat(template.findOne(query(where("id").is(source.id)), WithUnwrapped.class)).isEqualTo(source); - } - - @Test // DATAMONGO-1902 - void filterOnUnwrappedValue() { - - WithUnwrapped source = new WithUnwrapped(); - source.id = "id-1"; - source.embeddableValue = new UnwrappableType(); - source.embeddableValue.stringValue = "string-val"; - source.embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - source.embeddableValue.atFieldAnnotatedValue = "@Field"; - - template.save(source); - - assertThat(template.findOne( - Query.query(where("embeddableValue.stringValue").is(source.embeddableValue.stringValue)), WithUnwrapped.class)) - .isEqualTo(source); - } - - @Test // DATAMONGO-1902 - void readWritePrefixed() { - - WithPrefixedUnwrapped source = new WithPrefixedUnwrapped(); - source.id = "id-1"; - source.embeddableValue = new UnwrappableType(); - source.embeddableValue.stringValue = "string-val"; - source.embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - source.embeddableValue.atFieldAnnotatedValue = "@Field"; - - template.save(source); - - assertThat(template.findOne(query(where("id").is(source.id)), WithPrefixedUnwrapped.class)).isEqualTo(source); - } - - @Test // DATAMONGO-1902 - void filterOnPrefixedUnwrappedValue() { - - WithPrefixedUnwrapped source = new WithPrefixedUnwrapped(); - source.id = "id-1"; - source.embeddableValue = new UnwrappableType(); - source.embeddableValue.stringValue = "string-val"; - source.embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - source.embeddableValue.atFieldAnnotatedValue = "@Field"; - - template.save(source); - - assertThat( - template.findOne(Query.query(where("embeddableValue.stringValue").is(source.embeddableValue.stringValue)), - WithPrefixedUnwrapped.class)).isEqualTo(source); - } - - @EqualsAndHashCode - @ToString - static class WithUnwrapped { - - String id; - - @Unwrapped.Nullable UnwrappableType embeddableValue; - } - - @EqualsAndHashCode - @ToString - static class WithPrefixedUnwrapped { - - String id; - - @Unwrapped.Nullable("prefix-") UnwrappableType embeddableValue; - } - - @EqualsAndHashCode - @ToString - static class UnwrappableType { - - String stringValue; - List listValue; - - @Field("with-at-field-annotation") // - String atFieldAnnotatedValue; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUpdateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUpdateTests.java deleted file mode 100644 index c37bdb1dce..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUpdateTests.java +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; - -import lombok.EqualsAndHashCode; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.Version; -import org.springframework.data.mongodb.core.aggregation.AggregationUpdate; -import org.springframework.data.mongodb.core.aggregation.ArithmeticOperators; -import org.springframework.data.mongodb.core.aggregation.ReplaceWithOperation; -import org.springframework.data.mongodb.core.aggregation.SetOperation; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -import com.mongodb.client.MongoCollection; - -/** - * @author Christoph Strobl - */ -@ExtendWith({ MongoTemplateExtension.class }) -class MongoTemplateUpdateTests { - - @Template(initialEntitySet = { Score.class, Versioned.class, Book.class }) // - static MongoTestTemplate template; - - @BeforeEach - void setUp() { - template.flush(); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void aggregateUpdateWithSet() { - - Score score1 = new Score(1, "Maya", Arrays.asList(10, 5, 10), Arrays.asList(10, 8), 0); - Score score2 = new Score(2, "Ryan", Arrays.asList(5, 6, 5), Arrays.asList(8, 8), 8); - - template.insertAll(Arrays.asList(score1, score2)); - - AggregationUpdate update = AggregationUpdate.update().set(SetOperation.builder() // - .set("totalHomework").toValueOf(ArithmeticOperators.valueOf("homework").sum()).and() // - .set("totalQuiz").toValueOf(ArithmeticOperators.valueOf("quiz").sum())) // - .set(SetOperation.builder() // - .set("totalScore") - .toValueOf(ArithmeticOperators.valueOf("totalHomework").add("totalQuiz").add("extraCredit"))); - - template.update(Score.class).apply(update).all(); - - assertThat(collection(Score.class).find(new org.bson.Document()).into(new ArrayList<>())).containsExactlyInAnyOrder( // - org.bson.Document.parse( - "{\"_id\" : 1, \"student\" : \"Maya\", \"homework\" : [ 10, 5, 10 ], \"quiz\" : [ 10, 8 ], \"extraCredit\" : 0, \"totalHomework\" : 25, \"totalQuiz\" : 18, \"totalScore\" : 43, \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Score\"}"), - org.bson.Document.parse( - "{ \"_id\" : 2, \"student\" : \"Ryan\", \"homework\" : [ 5, 6, 5 ], \"quiz\" : [ 8, 8 ], \"extraCredit\" : 8, \"totalHomework\" : 16, \"totalQuiz\" : 16, \"totalScore\" : 40, \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Score\"}")); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void aggregateUpdateWithSetToValue() { - - Book one = new Book(); - one.id = 1; - one.author = new Author("John", "Backus"); - - template.insertAll(Arrays.asList(one)); - - AggregationUpdate update = AggregationUpdate.update().set("author").toValue(new Author("Ada", "Lovelace")); - - template.update(Book.class).matching(Query.query(Criteria.where("id").is(one.id))).apply(update).all(); - - assertThat(all(Book.class)).containsExactlyInAnyOrder(org.bson.Document.parse( - "{\"_id\" : 1, \"author\" : {\"first\" : \"Ada\", \"last\" : \"Lovelace\"}, \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Book\"}")); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void versionedAggregateUpdateWithSet() { - - Versioned source = template.insert(Versioned.class).one(new Versioned("id-1", "value-0")); - - AggregationUpdate update = AggregationUpdate.update().set("value").toValue("changed"); - template.update(Versioned.class).matching(Query.query(Criteria.where("id").is(source.id))).apply(update).first(); - - assertThat( - collection(Versioned.class).find(new org.bson.Document("_id", source.id)).limit(1).into(new ArrayList<>())) - .containsExactly(new org.bson.Document("_id", source.id).append("version", 1L).append("value", "changed") - .append("_class", "org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Versioned")); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void versionedAggregateUpdateTouchingVersionProperty() { - - Versioned source = template.insert(Versioned.class).one(new Versioned("id-1", "value-0")); - - AggregationUpdate update = AggregationUpdate.update() - .set(SetOperation.builder().set("value").toValue("changed").and().set("version").toValue(10L)); - template.update(Versioned.class).matching(Query.query(Criteria.where("id").is(source.id))).apply(update).first(); - - assertThat( - collection(Versioned.class).find(new org.bson.Document("_id", source.id)).limit(1).into(new ArrayList<>())) - .containsExactly(new org.bson.Document("_id", source.id).append("version", 10L).append("value", "changed") - .append("_class", "org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Versioned")); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void aggregateUpdateWithUnset() { - - Book antelopeAntics = new Book(); - antelopeAntics.id = 1; - antelopeAntics.title = "Antelope Antics"; - antelopeAntics.isbn = "0001122223334"; - antelopeAntics.author = new Author("Auntie", "An"); - antelopeAntics.stock = new ArrayList<>(); - antelopeAntics.stock.add(new Warehouse("A", 5)); - antelopeAntics.stock.add(new Warehouse("B", 15)); - - Book beesBabble = new Book(); - beesBabble.id = 2; - beesBabble.title = "Bees Babble"; - beesBabble.isbn = "999999999333"; - beesBabble.author = new Author("Bee", "Bumble"); - beesBabble.stock = new ArrayList<>(); - beesBabble.stock.add(new Warehouse("A", 2)); - beesBabble.stock.add(new Warehouse("B", 5)); - - template.insertAll(Arrays.asList(antelopeAntics, beesBabble)); - - AggregationUpdate update = AggregationUpdate.update().unset("isbn", "stock"); - template.update(Book.class).apply(update).all(); - - assertThat(all(Book.class)).containsExactlyInAnyOrder( // - org.bson.Document.parse( - "{ \"_id\" : 1, \"title\" : \"Antelope Antics\", \"author\" : { \"last\" : \"An\", \"first\" : \"Auntie\" }, \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Book\" }"), - org.bson.Document.parse( - "{ \"_id\" : 2, \"title\" : \"Bees Babble\", \"author\" : { \"last\" : \"Bumble\", \"first\" : \"Bee\" }, \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Book\" }")); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void aggregateUpdateWithReplaceWith() { - - Book one = new Book(); - one.id = 1; - one.author = new Author("John", "Backus"); - - Book two = new Book(); - two.id = 2; - two.author = new Author("Grace", "Hopper"); - - template.insertAll(Arrays.asList(one, two)); - - AggregationUpdate update = AggregationUpdate.update() - .replaceWith(ReplaceWithOperation.replaceWithValueOf("author")); - - template.update(Book.class).apply(update).all(); - - assertThat(all(Book.class)).containsExactlyInAnyOrder( - org.bson.Document.parse("{\"_id\" : 1, \"first\" : \"John\", \"last\" : \"Backus\"}"), - org.bson.Document.parse("{\"_id\" : 2, \"first\" : \"Grace\", \"last\" : \"Hopper\"}")); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void aggregateUpdateWithReplaceWithNewObject() { - - Book one = new Book(); - one.id = 1; - one.author = new Author("John", "Backus"); - - Book two = new Book(); - two.id = 2; - two.author = new Author("Grace", "Hopper"); - - template.insertAll(Arrays.asList(one, two)); - - AggregationUpdate update = AggregationUpdate.update().replaceWith(new Author("Ada", "Lovelace")); - - template.update(Book.class).matching(Query.query(Criteria.where("id").is(one.id))).apply(update).all(); - - assertThat(all(Book.class)).containsExactlyInAnyOrder(org.bson.Document.parse( - "{\"_id\" : 1, \"first\" : \"Ada\", \"last\" : \"Lovelace\", \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Author\"}"), - org.bson.Document.parse( - "{\"_id\" : 2, \"author\" : {\"first\" : \"Grace\", \"last\" : \"Hopper\"}, \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Book\"}")); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void aggregationUpdateUpsertsCorrectly() { - - AggregationUpdate update = AggregationUpdate.update().set("title").toValue("The Burning White"); - - template.update(Book.class).matching(Query.query(Criteria.where("id").is(1))).apply(update).upsert(); - - assertThat(all(Book.class)) - .containsExactly(org.bson.Document.parse("{\"_id\" : 1, \"title\" : \"The Burning White\" }")); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void aggregateUpdateFirstMatch() { - - Book one = new Book(); - one.id = 1; - one.title = "The Blood Mirror"; - - Book two = new Book(); - two.id = 2; - two.title = "The Broken Eye"; - - template.insertAll(Arrays.asList(one, two)); - - template.update(Book.class).apply(AggregationUpdate.update().set("title").toValue("The Blinding Knife")).first(); - - assertThat(all(Book.class)).containsExactly(org.bson.Document.parse( - "{\"_id\" : 1, \"title\" : \"The Blinding Knife\", \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Book\"}"), - org.bson.Document.parse( - "{\"_id\" : 2, \"title\" : \"The Broken Eye\", \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Book\"}")); - } - - @Test // DATAMONGO-2331 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") - void findAndModifyAppliesAggregationUpdateCorrectly() { - - Book one = new Book(); - one.id = 1; - one.title = "The Blood Mirror"; - - Book two = new Book(); - two.id = 2; - two.title = "The Broken Eye"; - - template.insertAll(Arrays.asList(one, two)); - - Book retrieved = template.update(Book.class).matching(Query.query(Criteria.where("id").is(one.id))) - .apply(AggregationUpdate.update().set("title").toValue("The Blinding Knife")).findAndModifyValue(); - assertThat(retrieved).isEqualTo(one); - - assertThat(all(Book.class)).containsExactly(org.bson.Document.parse( - "{\"_id\" : 1, \"title\" : \"The Blinding Knife\", \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Book\"}"), - org.bson.Document.parse( - "{\"_id\" : 2, \"title\" : \"The Broken Eye\", \"_class\" : \"org.springframework.data.mongodb.core.MongoTemplateUpdateTests$Book\"}")); - - } - - @Test // DATAMMONGO-2423 - void nullValueShouldBePropagatedToDatabase() { - - Book currentRead = new Book(); - currentRead.id = 1; - currentRead.author = new Author("Brent", "Weeks"); - currentRead.title = "The Burning White"; - - template.save(currentRead); - - template.update(Book.class).apply(new Update().set("title", null)).first(); - - assertThat(collection(Book.class).find(new org.bson.Document("_id", currentRead.id)).first()).containsEntry("title", - null); - } - - private List all(Class type) { - return collection(type).find(new org.bson.Document()).into(new ArrayList<>()); - } - - private MongoCollection collection(Class type) { - return template.getCollection(template.getCollectionName(type)); - } - - @Document("scores") - static class Score { - - Integer id; - String student; - List homework; - List quiz; - Integer extraCredit; - - public Score(Integer id, String student, List homework, List quiz, Integer extraCredit) { - - this.id = id; - this.student = student; - this.homework = homework; - this.quiz = quiz; - this.extraCredit = extraCredit; - } - } - - static class Versioned { - - String id; - @Version Long version; - String value; - - public Versioned(String id, String value) { - this.id = id; - this.value = value; - } - } - - @EqualsAndHashCode - static class Book { - - @Id Integer id; - String title; - String isbn; - Author author; - @Field("copies") Collection stock; - } - - static class Author { - - @Field("first") String firstname; - @Field("last") String lastname; - - public Author(String firstname, String lastname) { - this.firstname = firstname; - this.lastname = lastname; - } - } - - static class Warehouse { - - public Warehouse(String location, Integer qty) { - this.location = location; - this.qty = qty; - } - - @Field("warehouse") String location; - Integer qty; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java deleted file mode 100644 index b1ea415366..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.validation.Validator.*; - -import lombok.AllArgsConstructor; -import lombok.Data; - -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.CollectionOptions.ValidationOptions; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.lang.Nullable; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.model.ValidationAction; -import com.mongodb.client.model.ValidationLevel; - -/** - * Integration tests for {@link CollectionOptions#validation(ValidationOptions)} using - * {@link org.springframework.data.mongodb.core.validation.CriteriaValidator} and - * {@link org.springframework.data.mongodb.core.validation.DocumentValidator}. - * - * @author Andreas Zink - * @author Christoph Strobl - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -public class MongoTemplateValidationTests { - - static final String COLLECTION_NAME = "validation-1"; - static @Client MongoClient mongoClient; - - @Configuration - static class Config extends AbstractMongoClientConfiguration { - - @Override - public MongoClient mongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return "validation-tests"; - } - - @Override - protected boolean autoIndexCreation() { - return false; - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - } - - @Autowired MongoTemplate template; - - @BeforeEach - public void setUp() { - template.dropCollection(COLLECTION_NAME); - } - - @Test // DATAMONGO-1322 - public void testCollectionWithSimpleCriteriaBasedValidation() { - - Criteria criteria = where("nonNullString").ne(null).type(2).and("rangedInteger").ne(null).type(16).gte(0).lte(122); - - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().validator(criteria(criteria))); - - Document validator = getValidatorInfo(COLLECTION_NAME); - - assertThat(validator.get("nonNullString")).isEqualTo(new Document("$ne", null).append("$type", 2)); - assertThat(validator.get("rangedInteger")) - .isEqualTo(new Document("$ne", null).append("$type", 16).append("$gte", 0).append("$lte", 122)); - - template.save(new SimpleBean("hello", 101, null), COLLECTION_NAME); - - assertThatExceptionOfType(DataIntegrityViolationException.class) - .isThrownBy(() -> template.save(new SimpleBean(null, 101, null), COLLECTION_NAME)); - - assertThatExceptionOfType(DataIntegrityViolationException.class) - .isThrownBy(() -> template.save(new SimpleBean("hello", -1, null), COLLECTION_NAME)); - } - - @Test // DATAMONGO-1322 - public void testCollectionValidationActionError() { - - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationAction(ValidationAction.ERROR) - .validator(criteria(where("name").type(2)))); - - assertThat(getValidationActionInfo(COLLECTION_NAME)).isEqualTo(ValidationAction.ERROR.getValue()); - } - - @Test // DATAMONGO-1322 - public void testCollectionValidationActionWarn() { - - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationAction(ValidationAction.WARN) - .validator(criteria(where("name").type(2)))); - - assertThat(getValidationActionInfo(COLLECTION_NAME)).isEqualTo(ValidationAction.WARN.getValue()); - } - - @Test // DATAMONGO-1322 - public void testCollectionValidationLevelOff() { - - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationLevel(ValidationLevel.OFF) - .validator(criteria(where("name").type(2)))); - - assertThat(getValidationLevelInfo(COLLECTION_NAME)).isEqualTo(ValidationLevel.OFF.getValue()); - } - - @Test // DATAMONGO-1322 - public void testCollectionValidationLevelModerate() { - - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationLevel(ValidationLevel.MODERATE) - .validator(criteria(where("name").type(2)))); - - assertThat(getValidationLevelInfo(COLLECTION_NAME)).isEqualTo(ValidationLevel.MODERATE.getValue()); - } - - @Test // DATAMONGO-1322 - public void testCollectionValidationLevelStrict() { - - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationLevel(ValidationLevel.STRICT) - .validator(criteria(where("name").type(2)))); - - assertThat(getValidationLevelInfo(COLLECTION_NAME)).isEqualTo(ValidationLevel.STRICT.getValue()); - } - - @Test // DATAMONGO-1322 - public void mapsFieldNameCorrectlyWhenGivenDomainTypeInformation() { - - template.createCollection(SimpleBean.class, - CollectionOptions.empty().validator(criteria(where("customFieldName").type(8)))); - - assertThat(getValidatorInfo(COLLECTION_NAME)).isEqualTo(new Document("customName", new Document("$type", 8))); - } - - @Test // DATAMONGO-1322 - public void usesDocumentValidatorCorrectly() { - - Document rules = new Document("customFieldName", new Document("$type", "bool")); - - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().validator(document(rules))); - - assertThat(getValidatorInfo(COLLECTION_NAME)) - .isEqualTo(new Document("customFieldName", new Document("$type", "bool"))); - } - - @Test // DATAMONGO-1322 - public void mapsDocumentValidatorFieldsCorrectly() { - - Document rules = new Document("customFieldName", new Document("$type", "bool")); - - template.createCollection(SimpleBean.class, CollectionOptions.empty().validator(document(rules))); - - assertThat(getValidatorInfo(COLLECTION_NAME)).isEqualTo(new Document("customName", new Document("$type", "bool"))); - } - - private Document getCollectionOptions(String collectionName) { - return getCollectionInfo(collectionName).get("options", Document.class); - } - - private Document getValidatorInfo(String collectionName) { - return getCollectionOptions(collectionName).get("validator", Document.class); - } - - private String getValidationActionInfo(String collectionName) { - return getCollectionOptions(collectionName).get("validationAction", String.class); - } - - private String getValidationLevelInfo(String collectionName) { - return getCollectionOptions(collectionName).get("validationLevel", String.class); - } - - private Document getCollectionInfo(String collectionName) { - - return template.execute(db -> { - Document result = db.runCommand( - new Document().append("listCollections", 1).append("filter", new Document("name", collectionName))); - return (Document) result.get("cursor", Document.class).get("firstBatch", List.class).get(0); - }); - } - - @Data - @AllArgsConstructor - @org.springframework.data.mongodb.core.mapping.Document(collection = COLLECTION_NAME) - static class SimpleBean { - - private @Nullable String nonNullString; - private @Nullable Integer rangedInteger; - private @Field("customName") Object customFieldName; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/NoExplicitIdTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/NoExplicitIdTests.java deleted file mode 100644 index be2276bc2e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/NoExplicitIdTests.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2015-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.client.MongoClient; - -/** - * Integration tests for DATAMONGO-1289. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@ContextConfiguration -public class NoExplicitIdTests { - - static @Client MongoClient mongoClient; - - @Configuration - @EnableMongoRepositories(considerNestedRepositories = true, includeFilters=@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = TypeWithoutExplicitIdPropertyRepository.class)) - static class Config extends AbstractMongoClientConfiguration { - - @Override - protected String getDatabaseName() { - return "test"; - } - - @Override - public MongoClient mongoClient() { - return mongoClient; - } - - @Override - protected boolean autoIndexCreation() { - return false; - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - } - - @Autowired MongoOperations mongoOps; - @Autowired TypeWithoutExplicitIdPropertyRepository repo; - - @BeforeEach - public void setUp() { - mongoOps.dropCollection(TypeWithoutIdProperty.class); - } - - @Test // DATAMONGO-1289 - public void saveAndRetrieveTypeWithoutIdPropertyViaTemplate() { - - TypeWithoutIdProperty noid = new TypeWithoutIdProperty(); - noid.someString = "o.O"; - - mongoOps.save(noid); - - TypeWithoutIdProperty retrieved = mongoOps.findOne(query(where("someString").is(noid.someString)), - TypeWithoutIdProperty.class); - - assertThat(retrieved.someString).isEqualTo(noid.someString); - } - - @Test // DATAMONGO-1289 - public void saveAndRetrieveTypeWithoutIdPropertyViaRepository() { - - TypeWithoutIdProperty noid = new TypeWithoutIdProperty(); - noid.someString = "o.O"; - - repo.save(noid); - - TypeWithoutIdProperty retrieved = repo.findBySomeString(noid.someString); - assertThat(retrieved.someString).isEqualTo(noid.someString); - } - - @Test // DATAMONGO-1289 - @SuppressWarnings("unchecked") - public void saveAndRetrieveTypeWithoutIdPropertyViaRepositoryFindOne() { - - TypeWithoutIdProperty noid = new TypeWithoutIdProperty(); - noid.someString = "o.O"; - - repo.save(noid); - - Map map = mongoOps.findOne(query(where("someString").is(noid.someString)), Map.class, - "typeWithoutIdProperty"); - - Optional retrieved = repo.findById(map.get("_id").toString()); - assertThat(retrieved.get().someString).isEqualTo(noid.someString); - } - - static class TypeWithoutIdProperty { - - String someString; - } - - static interface TypeWithoutExplicitIdPropertyRepository extends MongoRepository { - - TypeWithoutIdProperty findBySomeString(String someString); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java deleted file mode 100644 index 24e85887d1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2010-2021 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 org.bson.types.ObjectId; - -public class Person { - - private ObjectId id; - - private String firstName; - - private int age; - - private Person friend; - - private boolean active = true; - - public Person() { - this.id = new ObjectId(); - } - - @Override - public String toString() { - return "Person [id=" + id + ", firstName=" + firstName + ", age=" + age + ", friend=" + friend + "]"; - } - - public Person(ObjectId id, String firstname) { - this.id = id; - this.firstName = firstname; - } - - public Person(String firstname, int age) { - this(); - this.firstName = firstname; - this.age = age; - } - - public Person(String firstname) { - this(); - this.firstName = firstname; - } - - public ObjectId getId() { - return id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public Person getFriend() { - return friend; - } - - public void setFriend(Person friend) { - this.friend = friend; - } - - /** - * @return the active - */ - public boolean isActive() { - return active; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (obj == null) { - return false; - } - - if (!(getClass().equals(obj.getClass()))) { - return false; - } - - Person that = (Person) obj; - - return this.id == null ? false : this.id.equals(that.id); - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - return id.hashCode(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonExample.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonExample.java deleted file mode 100644 index d935a85d5c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonExample.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2002-2021 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.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.support.AbstractApplicationContext; - -/** - * @author Jon Brisbin - * @author Oliver Gierke - */ -public class PersonExample { - - private static final Logger LOGGER = LoggerFactory.getLogger(PersonExample.class); - - @Autowired private MongoOperations mongoOps; - - public static void main(String[] args) { - AbstractApplicationContext applicationContext = new AnnotationConfigApplicationContext( - PersonExampleAppConfig.class); - PersonExample example = applicationContext.getBean(PersonExample.class); - example.doWork(); - applicationContext.close(); - } - - public void doWork() { - mongoOps.dropCollection("personexample"); - - PersonWithIdPropertyOfTypeString p = new PersonWithIdPropertyOfTypeString(); - p.setFirstName("Sven"); - p.setAge(22); - - mongoOps.save(p); - - PersonWithIdPropertyOfTypeString p2 = new PersonWithIdPropertyOfTypeString(); - p2.setFirstName("Jon"); - p2.setAge(23); - - mongoOps.save(p2); - - LOGGER.debug("Saved: " + p); - - p = mongoOps.findById(p.getId(), PersonWithIdPropertyOfTypeString.class); - - LOGGER.debug("Found: " + p); - - // mongoOps.updateFirst(new Query(where("firstName").is("Sven")), new Update().set("age", 24)); - - // mongoOps.updateFirst(new Query(where("firstName").is("Sven")), update("age", 24)); - - p = mongoOps.findById(p.getId(), PersonWithIdPropertyOfTypeString.class); - LOGGER.debug("Updated: " + p); - - List folks = mongoOps.findAll(PersonWithIdPropertyOfTypeString.class); - LOGGER.debug("Querying for all people..."); - for (PersonWithIdPropertyOfTypeString element : folks) { - LOGGER.debug(element.toString()); - } - - // mongoOps.remove( query(whereId().is(p.getId())), p.getClass()); - - mongoOps.remove(p); - - List people = mongoOps.findAll(PersonWithIdPropertyOfTypeString.class); - - LOGGER.debug("Number of people = : " + people.size()); - - } - - public void doWork2() { - mongoOps.dropCollection("personexample"); - - PersonWithIdPropertyOfTypeString p = new PersonWithIdPropertyOfTypeString(); - p.setFirstName("Sven"); - p.setAge(22); - - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonExampleAppConfig.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonExampleAppConfig.java deleted file mode 100644 index 0458f9e65b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonExampleAppConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2021 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 org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.test.util.MongoTestUtils; - -import com.mongodb.client.MongoClient; - -@Configuration -public class PersonExampleAppConfig { - - @Bean - public MongoClient mongoClient() { - return MongoTestUtils.client(); - } - - @Bean - public MongoTemplate mongoTemplate() throws Exception { - return new MongoTemplate(mongoClient(), "database"); - } - - @Bean - public PersonExample personExample() { - return new PersonExample(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonReadConverter.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonReadConverter.java deleted file mode 100644 index 888e659f1d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonReadConverter.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.springframework.data.mongodb.core; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.springframework.core.convert.converter.Converter; - -public class PersonReadConverter implements Converter { - - public Person convert(Document source) { - Person p = new Person((ObjectId) source.get("_id"), (String) source.get("name")); - p.setAge((Integer) source.get("age")); - return p; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithAList.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithAList.java deleted file mode 100644 index 48c9ca92d4..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithAList.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2010-2021 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.List; - -import org.bson.types.ObjectId; - -public class PersonWithAList { - - private ObjectId id; - - private String firstName; - - private int age; - - private List wishList = new ArrayList(); - - private List friends = new ArrayList(); - - public ObjectId getId() { - return id; - } - - public void setId(ObjectId id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public List getWishList() { - return wishList; - } - - public void setWishList(List wishList) { - this.wishList = wishList; - } - - public void addToWishList(String wish) { - this.wishList.add(wish); - } - - public List getFriends() { - return friends; - } - - public void setFriends(List friends) { - this.friends = friends; - } - - public void addFriend(Friend friend) { - this.friends.add(friend); - } - - @Override - public String toString() { - return "PersonWithAList [id=" + id + ", firstName=" + firstName + ", age=" + age + ", wishList=" + wishList + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfPrimitiveInt.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfPrimitiveInt.java deleted file mode 100644 index 74914cde5c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfPrimitiveInt.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2021 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; - -public class PersonWithIdPropertyOfPrimitiveInt { - - private int id; - - private String firstName; - - private int age; - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - @Override - public String toString() { - return "PersonWithIdPropertyOfTypeInteger [id=" + id + ", firstName=" + firstName + ", age=" + age + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfPrimitiveLong.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfPrimitiveLong.java deleted file mode 100644 index 79939f39fe..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfPrimitiveLong.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2021 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; - -public class PersonWithIdPropertyOfPrimitiveLong { - - private long id; - - private String firstName; - - private int age; - - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - @Override - public String toString() { - return "PersonWithIdPropertyOfTypeInteger [id=" + id + ", firstName=" + firstName + ", age=" + age + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeBigInteger.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeBigInteger.java deleted file mode 100644 index 432b584d9e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeBigInteger.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2021 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.math.BigInteger; - -public class PersonWithIdPropertyOfTypeBigInteger { - - private BigInteger id; - - private String firstName; - - private int age; - - public BigInteger getId() { - return id; - } - - public void setId(BigInteger id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - @Override - public String toString() { - return "PersonWithIdPropertyOfTypeInteger [id=" + id + ", firstName=" + firstName + ", age=" + age + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeInteger.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeInteger.java deleted file mode 100644 index d958003694..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeInteger.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2021 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; - -public class PersonWithIdPropertyOfTypeInteger { - - private Integer id; - - private String firstName; - - private int age; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - @Override - public String toString() { - return "PersonWithIdPropertyOfTypeInteger [id=" + id + ", firstName=" + firstName + ", age=" + age + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeLong.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeLong.java deleted file mode 100644 index 2be2d6e60f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeLong.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2021 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; - -public class PersonWithIdPropertyOfTypeLong { - - private Long id; - - private String firstName; - - private int age; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - @Override - public String toString() { - return "PersonWithIdPropertyOfTypeInteger [id=" + id + ", firstName=" + firstName + ", age=" + age + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeObjectId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeObjectId.java deleted file mode 100644 index 7b53ad8291..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeObjectId.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2010-2021 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 org.bson.types.ObjectId; - -public class PersonWithIdPropertyOfTypeObjectId { - - private ObjectId id; - - private String firstName; - - private int age; - - public ObjectId getId() { - return id; - } - - public void setId(ObjectId id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeString.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeString.java deleted file mode 100644 index 90802c5db1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeString.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2021 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; - -public class PersonWithIdPropertyOfTypeString { - - private String id; - - private String firstName; - - private int age; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - @Override - public String toString() { - return "PersonWithIdPropertyOfTypeString [id=" + id + ", firstName=" + firstName + ", age=" + age + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeUUID.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeUUID.java deleted file mode 100644 index 198ea2107f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithIdPropertyOfTypeUUID.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017-2021 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 lombok.Data; - -import java.util.UUID; - -@Data -public class PersonWithIdPropertyOfTypeUUID { - - private UUID id; - private String firstName; - private int age; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithVersionPropertyOfTypeInteger.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithVersionPropertyOfTypeInteger.java deleted file mode 100644 index da94a20ec7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithVersionPropertyOfTypeInteger.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2021 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 org.springframework.data.annotation.Version; - -public class PersonWithVersionPropertyOfTypeInteger { - - String id; - String firstName; - int age; - - @Version Integer version; - - @Override - public String toString() { - return "PersonWithVersionPropertyOfTypeInteger [id=" + id + ", firstName=" + firstName + ", age=" + age - + ", version=" + version + "]"; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithVersionPropertyOfTypeLong.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithVersionPropertyOfTypeLong.java deleted file mode 100644 index ec177dcd5c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWithVersionPropertyOfTypeLong.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2021 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 org.springframework.data.annotation.Version; - -public class PersonWithVersionPropertyOfTypeLong { - - String id; - String firstName; - int age; - - @Version Long version; - - @Override - public String toString() { - return "PersonWithVersionPropertyOfTypeLong [id=" + id + ", firstName=" + firstName + ", age=" + age + ", version=" - + version + "]"; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWith_idPropertyOfTypeObjectId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWith_idPropertyOfTypeObjectId.java deleted file mode 100644 index 3f9efaf725..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWith_idPropertyOfTypeObjectId.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2010-2021 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 org.bson.types.ObjectId; - -public class PersonWith_idPropertyOfTypeObjectId { - - private ObjectId _id; - - private String firstName; - - private int age; - - public ObjectId get_id() { - return _id; - } - - public void set_id(ObjectId _id) { - this._id = _id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWith_idPropertyOfTypeString.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWith_idPropertyOfTypeString.java deleted file mode 100644 index bacb1e9166..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWith_idPropertyOfTypeString.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2010-2021 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; - -public class PersonWith_idPropertyOfTypeString { - - private String _id; - - private String firstName; - - private int age; - - public String get_id() { - return _id; - } - - public void set_id(String _id) { - this._id = _id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWriteConverter.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWriteConverter.java deleted file mode 100644 index 39308cd567..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/PersonWriteConverter.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2016-2021 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 org.bson.Document; -import org.springframework.core.convert.converter.Converter; - -/** - * @author Thomas Risberg - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - */ -public class PersonWriteConverter implements Converter { - - public Document convert(Person source) { - Document document = new Document(); - document.put("_id", source.getId()); - document.put("name", source.getFirstName()); - document.put("age", source.getAge()); - return document; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Portfolio.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Portfolio.java deleted file mode 100644 index c20d17e1d2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Portfolio.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2010-2021 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.List; -import java.util.Map; - -public class Portfolio { - - private String portfolioName; - private User user; - private List trades; - private Map positions; - private Map portfolioManagers; - - public Map getPortfolioManagers() { - return portfolioManagers; - } - - public void setPortfolioManagers(Map portfolioManagers) { - this.portfolioManagers = portfolioManagers; - } - - public Map getPositions() { - return positions; - } - - public void setPositions(Map positions) { - this.positions = positions; - } - - public Portfolio() { - trades = new ArrayList(); - } - - public String getPortfolioName() { - return portfolioName; - } - - public void setPortfolioName(String portfolioName) { - this.portfolioName = portfolioName; - } - - public List getTrades() { - return trades; - } - - public void setTrades(List trades) { - this.trades = trades; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java deleted file mode 100644 index d69d9de542..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryByExampleTests.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2016-2021 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 static org.assertj.core.api.Assertions.*; - -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.ExampleMatcher; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.UntypedExampleMatcher; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -/** - * Integration tests for Query-by-example. - * - * @author Christoph Strobl - * @author Mark Paluch - * @author Oliver Gierke - */ -@ExtendWith(MongoTemplateExtension.class) -public class QueryByExampleTests { - - @Template(initialEntitySet = Person.class) // - static MongoTestTemplate operations; - - Person p1, p2, p3; - - @BeforeEach - public void setUp() { - - operations.flush(); - - p1 = new Person(); - p1.firstname = "bran"; - p1.middlename = "a"; - p1.lastname = "stark"; - - p2 = new Person(); - p2.firstname = "jon"; - p2.lastname = "snow"; - - p3 = new Person(); - p3.firstname = "arya"; - p3.lastname = "stark"; - - operations.save(p1); - operations.save(p2); - operations.save(p3); - } - - @Test // DATAMONGO-1245 - public void findByExampleShouldWorkForSimpleProperty() { - - Person sample = new Person(); - sample.lastname = "stark"; - - Query query = new Query(new Criteria().alike(Example.of(sample))); - List result = operations.find(query, Person.class); - - assertThat(result).containsExactlyInAnyOrder(p1, p3); - } - - @Test // DATAMONGO-1245 - public void findByExampleShouldWorkForMultipleProperties() { - - Person sample = new Person(); - sample.lastname = "stark"; - sample.firstname = "arya"; - - Query query = new Query(new Criteria().alike(Example.of(sample))); - List result = operations.find(query, Person.class); - - assertThat(result).containsExactly(p3); - } - - @Test // DATAMONGO-1245 - public void findByExampleShouldWorkForIdProperty() { - - Person p4 = new Person(); - operations.save(p4); - - Person sample = new Person(); - sample.id = p4.id; - - Query query = new Query(new Criteria().alike(Example.of(sample))); - List result = operations.find(query, Person.class); - - assertThat(result).containsExactly(p4); - } - - @Test // DATAMONGO-1245 - public void findByExampleShouldReturnEmptyListIfNotMatching() { - - Person sample = new Person(); - sample.firstname = "jon"; - sample.firstname = "stark"; - - Query query = new Query(new Criteria().alike(Example.of(sample))); - List result = operations.find(query, Person.class); - - assertThat(result).isEmpty(); - } - - @Test // DATAMONGO-1245 - public void findByExampleShouldReturnEverythingWhenSampleIsEmpty() { - - Person sample = new Person(); - - Query query = new Query(new Criteria().alike(Example.of(sample))); - List result = operations.find(query, Person.class); - - assertThat(result).containsExactlyInAnyOrder(p1, p2, p3); - } - - @Test // DATAMONGO-1245, GH-3544 - public void findByExampleWithCriteria() { - - Person sample = new Person(); - sample.lastname = "stark"; - - Query query = new Query(new Criteria().alike(Example.of(sample)).and("firstname").regex(".*n.*")); - assertThat(operations.find(query, Person.class)).containsExactly(p1); - } - - @Test // DATAMONGO-1459 - public void findsExampleUsingAnyMatch() { - - Person probe = new Person(); - probe.lastname = "snow"; - probe.middlename = "a"; - - Query query = Query.query(Criteria.byExample(Example.of(probe, ExampleMatcher.matchingAny()))); - List result = operations.find(query, Person.class); - - assertThat(result).containsExactlyInAnyOrder(p1, p2); - } - - @Test // DATAMONGO-1768 - public void typedExampleMatchesNothingIfTypesDoNotMatch() { - - NotAPersonButStillMatchingFields probe = new NotAPersonButStillMatchingFields(); - probe.lastname = "stark"; - - Query query = new Query(new Criteria().alike(Example.of(probe))); - List result = operations.find(query, Person.class); - - assertThat(result).isEmpty(); - } - - @Test // DATAMONGO-1768 - public void exampleIgnoringClassTypeKeyMatchesCorrectly() { - - NotAPersonButStillMatchingFields probe = new NotAPersonButStillMatchingFields(); - probe.lastname = "stark"; - - Query query = new Query( - new Criteria().alike(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_class")))); - List result = operations.find(query, Person.class); - - assertThat(result).containsExactlyInAnyOrder(p1, p3); - } - - @Test // DATAMONGO-1768 - public void untypedExampleMatchesCorrectly() { - - NotAPersonButStillMatchingFields probe = new NotAPersonButStillMatchingFields(); - probe.lastname = "stark"; - - Query query = new Query(new Criteria().alike(Example.of(probe, UntypedExampleMatcher.matching()))); - List result = operations.find(query, Person.class); - - assertThat(result).containsExactlyInAnyOrder(p1, p3); - } - - @Test // DATAMONGO-2314 - public void alikeShouldWorkOnNestedProperties() { - - PersonWrapper source1 = new PersonWrapper(); - source1.id = "with-child-doc-1"; - source1.child = p1; - - PersonWrapper source2 = new PersonWrapper(); - source2.id = "with-child-doc-2"; - source2.child = p2; - - operations.save(source1); - operations.save(source2); - - Query query = new Query( - new Criteria("child").alike(Example.of(p1, ExampleMatcher.matching().withIgnorePaths("_class")))); - List result = operations.find(query, PersonWrapper.class); - - assertThat(result).containsExactly(source1); - } - - @Document("dramatis-personae") - @EqualsAndHashCode - @ToString - static class Person { - - @Id String id; - String firstname, middlename; - @Field("last_name") String lastname; - } - - @EqualsAndHashCode - @ToString - static class NotAPersonButStillMatchingFields { - - String firstname, middlename; - @Field("last_name") String lastname; - } - - @Document("dramatis-personae") - @EqualsAndHashCode - @ToString - static class PersonWrapper { - - @Id String id; - Person child; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryCursorPreparerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryCursorPreparerUnitTests.java deleted file mode 100644 index 73f87618b2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryCursorPreparerUnitTests.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2011-2021 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 static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import java.util.concurrent.TimeUnit; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoTemplate.QueryCursorPreparer; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Query; - -import com.mongodb.MongoClientSettings; -import com.mongodb.client.FindIterable; - -/** - * Unit tests for {@link QueryCursorPreparer}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - * @author Anton Barkan - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class QueryCursorPreparerUnitTests { - - @Mock MongoDatabaseFactory factory; - @Mock MongoExceptionTranslator exceptionTranslatorMock; - @Mock FindIterable cursor; - - @BeforeEach - void setUp() { - - when(factory.getExceptionTranslator()).thenReturn(exceptionTranslatorMock); - when(factory.getCodecRegistry()).thenReturn(MongoClientSettings.getDefaultCodecRegistry()); - when(cursor.batchSize(anyInt())).thenReturn(cursor); - when(cursor.comment(anyString())).thenReturn(cursor); - when(cursor.allowDiskUse(anyBoolean())).thenReturn(cursor); - when(cursor.maxTime(anyLong(), any())).thenReturn(cursor); - when(cursor.hint(any())).thenReturn(cursor); - when(cursor.noCursorTimeout(anyBoolean())).thenReturn(cursor); - when(cursor.collation(any())).thenReturn(cursor); - } - - @Test // DATAMONGO-185 - void appliesHintsCorrectly() { - - Query query = query(where("foo").is("bar")).withHint("{ age: 1 }"); - prepare(query); - - verify(cursor).hint(new Document("age", 1)); - } - - @Test // DATAMONGO-2365 - void appliesIndexNameAsHintCorrectly() { - - Query query = query(where("foo").is("bar")).withHint("idx-1"); - prepare(query); - - verify(cursor).hintString("idx-1"); - } - - @Test // DATAMONGO-2319 - void appliesDocumentHintsCorrectly() { - - Query query = query(where("foo").is("bar")).withHint(Document.parse("{ age: 1 }")); - prepare(query); - - verify(cursor).hint(new Document("age", 1)); - } - - @Test // DATAMONGO-957 - void appliesMaxTimeCorrectly() { - - Query query = query(where("foo").is("bar")).maxTime(1, TimeUnit.SECONDS); - prepare(query); - - verify(cursor).maxTime(1000, TimeUnit.MILLISECONDS); - } - - @Test // DATAMONGO-957 - void appliesCommentCorrectly() { - - Query query = query(where("foo").is("bar")).comment("spring data"); - prepare(query); - - verify(cursor).comment("spring data"); - } - - @Test // DATAMONGO-2659 - void appliesAllowDiskUseCorrectly() { - - Query query = query(where("foo").is("bar")).allowDiskUse(true); - prepare(query); - - verify(cursor).allowDiskUse(true); - } - - @Test // DATAMONGO-1480 - void appliesNoCursorTimeoutCorrectly() { - - Query query = query(where("foo").is("bar")).noCursorTimeout(); - - prepare(query); - - verify(cursor).noCursorTimeout(eq(true)); - } - - @Test // DATAMONGO-1518 - void appliesCollationCorrectly() { - - prepare(new BasicQuery("{}").collation(Collation.of("fr"))); - - verify(cursor).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1311 - void appliesBatchSizeCorrectly() { - - prepare(new BasicQuery("{}").cursorBatchSize(100)); - - verify(cursor).batchSize(100); - } - - private FindIterable prepare(Query query) { - - CursorPreparer preparer = new MongoTemplate(factory).new QueryCursorPreparer(query, null); - return preparer.prepare(cursor); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryOperationsUnitTests.java deleted file mode 100644 index e463daa77b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryOperationsUnitTests.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition; -import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.aggregation.AggregationOptions; -import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext; -import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.convert.UpdateMapper; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -/** - * Unit tests for {@link QueryOperations}. - * - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -class QueryOperationsUnitTests { - - static final AggregationOptions NO_MAPPING = AggregationOptions.builder().noMapping().build(); - static final AggregationOptions STRICT_MAPPING = AggregationOptions.builder().strictMapping().build(); - - @Mock QueryMapper queryMapper; - @Mock UpdateMapper updateMapper; - @Mock EntityOperations entityOperations; - @Mock PropertyOperations propertyOperations; - @Mock MongoDatabaseFactory mongoDbFactory; - @Mock MongoMappingContext mappingContext; - - QueryOperations queryOperations; - - @BeforeEach - void beforeEach() { - - when(queryMapper.getMappingContext()).thenReturn((MappingContext) mappingContext); - - queryOperations = new QueryOperations(queryMapper, updateMapper, entityOperations, propertyOperations, - mongoDbFactory); - } - - @Test // GH-3542 - void createAggregationContextUsesRelaxedOneForUntypedAggregationsWhenNoInputTypeProvided() { - - Aggregation aggregation = Aggregation.newAggregation(Aggregation.project("name")); - AggregationDefinition ctx = queryOperations.createAggregation(aggregation, (Class) null); - - assertThat(ctx.getAggregationOperationContext()).isInstanceOf(RelaxedTypeBasedAggregationOperationContext.class); - } - - @Test // GH-3542 - void createAggregationContextUsesRelaxedOneForTypedAggregationsWhenNoInputTypeProvided() { - - Aggregation aggregation = Aggregation.newAggregation(Person.class, Aggregation.project("name")); - AggregationDefinition ctx = queryOperations.createAggregation(aggregation, (Class) null); - - assertThat(ctx.getAggregationOperationContext()).isInstanceOf(RelaxedTypeBasedAggregationOperationContext.class); - } - - @Test // GH-3542 - void createAggregationContextUsesRelaxedOneForUntypedAggregationsWhenInputTypeProvided() { - - Aggregation aggregation = Aggregation.newAggregation(Aggregation.project("name")); - AggregationDefinition ctx = queryOperations.createAggregation(aggregation, Person.class); - - assertThat(ctx.getAggregationOperationContext()).isInstanceOf(RelaxedTypeBasedAggregationOperationContext.class); - } - - @Test // GH-3542 - void createAggregationContextUsesDefaultIfNoMappingDesired() { - - Aggregation aggregation = Aggregation.newAggregation(Aggregation.project("name")).withOptions(NO_MAPPING); - AggregationDefinition ctx = queryOperations.createAggregation(aggregation, Person.class); - - assertThat(ctx.getAggregationOperationContext()).isEqualTo(Aggregation.DEFAULT_CONTEXT); - } - - @Test // GH-3542 - void createAggregationContextUsesStrictlyTypedContextForTypedAggregationsWhenRequested() { - - Aggregation aggregation = Aggregation.newAggregation(Person.class, Aggregation.project("name")) - .withOptions(STRICT_MAPPING); - AggregationDefinition ctx = queryOperations.createAggregation(aggregation, (Class) null); - - assertThat(ctx.getAggregationOperationContext()).isInstanceOf(TypeBasedAggregationOperationContext.class); - } - - static class Person { - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupportUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupportUnitTests.java deleted file mode 100644 index e694eab3d8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupportUnitTests.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.aggregation.Aggregation; - -/** - * Unit tests for {@link ReactiveAggregationOperationSupport}. - * - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class ReactiveAggregationOperationSupportUnitTests { - - @Mock ReactiveMongoTemplate template; - private ReactiveAggregationOperationSupport opSupport; - - @BeforeEach - void setUp() { - opSupport = new ReactiveAggregationOperationSupport(template); - } - - @Test // DATAMONGO-1719 - void throwsExceptionOnNullDomainType() { - assertThatIllegalArgumentException().isThrownBy(() -> opSupport.aggregateAndReturn(null)); - } - - @Test // DATAMONGO-1719 - void throwsExceptionOnNullCollectionWhenUsed() { - assertThatIllegalArgumentException() - .isThrownBy(() -> opSupport.aggregateAndReturn(Person.class).inCollection(null)); - } - - @Test // DATAMONGO-1719 - void throwsExceptionOnEmptyCollectionWhenUsed() { - assertThatIllegalArgumentException().isThrownBy(() -> opSupport.aggregateAndReturn(Person.class).inCollection("")); - } - - @Test // DATAMONGO-1719 - void throwsExceptionOnNullAggregation() { - assertThatIllegalArgumentException().isThrownBy(() -> opSupport.aggregateAndReturn(Person.class).by(null)); - } - - @Test // DATAMONGO-1719 - void aggregateWithUntypedAggregationAndExplicitCollection() { - - opSupport.aggregateAndReturn(Person.class).inCollection("star-wars").by(newAggregation(project("foo"))).all(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - verify(template).aggregate(any(Aggregation.class), eq("star-wars"), captor.capture()); - assertThat(captor.getValue()).isEqualTo(Person.class); - } - - @Test // DATAMONGO-1719 - void aggregateWithUntypedAggregation() { - - when(template.getCollectionName(any(Class.class))).thenReturn("person"); - - opSupport.aggregateAndReturn(Person.class).by(newAggregation(project("foo"))).all(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(captor.capture()); - verify(template).aggregate(any(Aggregation.class), eq("person"), captor.capture()); - - assertThat(captor.getAllValues()).containsExactly(Person.class, Person.class); - } - - @Test // DATAMONGO-1719 - void aggregateWithTypeAggregation() { - - when(template.getCollectionName(any(Class.class))).thenReturn("person"); - - opSupport.aggregateAndReturn(Jedi.class).by(newAggregation(Person.class, project("foo"))).all(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(captor.capture()); - verify(template).aggregate(any(Aggregation.class), eq("person"), captor.capture()); - - assertThat(captor.getAllValues()).containsExactly(Person.class, Jedi.class); - } - - static class Person {} - - static class Jedi {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupportTests.java deleted file mode 100644 index 494e691f40..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupportTests.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import lombok.SneakyThrows; -import reactor.core.Disposable; -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import java.time.Duration; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.stream.Collectors; - -import org.bson.Document; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.data.mongodb.test.util.ReplSetClient; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Tests for {@link ReactiveChangeStreamOperation}. - * - * @author Christoph Strobl - * @currentRead Dawn Cook - The Decoy Princess - */ -@ExtendWith(MongoClientExtension.class) -@EnableIfReplicaSetAvailable -public class ReactiveChangeStreamOperationSupportTests { - - static final String DATABASE_NAME = "rx-change-stream"; - static @ReplSetClient MongoClient mongoClient; - - ReactiveMongoTemplate template; - - @BeforeEach - public void setUp() { - - template = new ReactiveMongoTemplate(mongoClient, DATABASE_NAME); - - MongoTestUtils.createOrReplaceCollectionNow(DATABASE_NAME, "person", mongoClient); - } - - @AfterEach - public void tearDown() { - MongoTestUtils.dropCollectionNow(DATABASE_NAME, "person", mongoClient); - } - - @SneakyThrows - @Test // DATAMONGO-2089 - public void changeStreamEventsShouldBeEmittedCorrectly() { - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - - Disposable disposable = template.changeStream(Document.class) // - .watchCollection("person") // - .listen() // - .doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 39); - Person person3 = new Person("MongoDB", 37); - - Flux.merge(template.insert(person1).delayElement(Duration.ofMillis(2)), - template.insert(person2).delayElement(Duration.ofMillis(2)), - template.insert(person3).delayElement(Duration.ofMillis(2))) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(documents.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())).hasSize(3) - .allMatch(Document.class::isInstance); - } finally { - disposable.dispose(); - } - } - - @Test // DATAMONGO-1803 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - public void changeStreamEventsShouldBeConvertedCorrectly() throws InterruptedException { - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - - Disposable disposable = template.changeStream(Person.class).listen() // - .doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 39); - Person person3 = new Person("MongoDB", 37); - - Flux.merge(template.insert(person1).delayElement(Duration.ofMillis(2)), - template.insert(person2).delayElement(Duration.ofMillis(2)), - template.insert(person3).delayElement(Duration.ofMillis(2))) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(documents.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())).containsOnly(person1, - person2, person3); - } finally { - disposable.dispose(); - } - } - - @Test // DATAMONGO-1803 - public void changeStreamEventsShouldBeFilteredCorrectly() throws InterruptedException { - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - - Disposable disposable = template.changeStream(Person.class) // - .watchCollection(Person.class) // - .filter(where("age").gte(38)) // - .listen() // - .doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 37); - Person person3 = new Person("MongoDB", 39); - - Flux.merge(template.save(person1), template.save(person2).delayElement(Duration.ofMillis(50)), - template.save(person3).delayElement(Duration.ofMillis(100))) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(documents.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())).containsOnly(person1, - person3); - } finally { - disposable.dispose(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupportUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupportUnitTests.java deleted file mode 100644 index 99d21ccad7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupportUnitTests.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.isNull; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import reactor.core.publisher.Flux; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.aggregation.TypedAggregation; -import org.springframework.data.mongodb.core.query.Criteria; - -/** - * Unit tests for {@link ReactiveChangeStreamOperationSupport}. - * - * @author Christoph Strobl - * @currentRead Dawn Cook - The Decoy Princess - */ -@ExtendWith(MockitoExtension.class) -class ReactiveChangeStreamOperationSupportUnitTests { - - @Mock ReactiveMongoTemplate template; - private ReactiveChangeStreamOperationSupport changeStreamSupport; - - @BeforeEach - void setUp() { - when(template.changeStream(any(), any(), any())).thenReturn(Flux.empty()); - changeStreamSupport = new ReactiveChangeStreamOperationSupport(template); - } - - @Test // DATAMONGO-2089 - void listenWithoutDomainTypeUsesDocumentAsDefault() { - - changeStreamSupport.changeStream(Document.class).listen().subscribe(); - - verify(template).changeStream(isNull(), eq(ChangeStreamOptions.empty()), eq(Document.class)); - } - - @Test // DATAMONGO-2089 - void listenWithDomainTypeUsesSourceAsTarget() { - - changeStreamSupport.changeStream(Person.class).listen().subscribe(); - - verify(template).changeStream(isNull(), eq(ChangeStreamOptions.empty()), eq(Person.class)); - } - - @Test // DATAMONGO-2089 - void collectionNameIsPassedOnCorrectly() { - - changeStreamSupport.changeStream(Person.class).watchCollection("star-wars").listen().subscribe(); - - verify(template).changeStream(eq("star-wars"), eq(ChangeStreamOptions.empty()), eq(Person.class)); - } - - @Test // DATAMONGO-2089 - void listenWithDomainTypeCreatesTypedAggregation() { - - Criteria criteria = where("operationType").is("insert"); - changeStreamSupport.changeStream(Person.class).filter(criteria).listen().subscribe(); - - ArgumentCaptor optionsArgumentCaptor = ArgumentCaptor.forClass(ChangeStreamOptions.class); - verify(template).changeStream(isNull(), optionsArgumentCaptor.capture(), eq(Person.class)); - - assertThat(optionsArgumentCaptor.getValue().getFilter()).hasValueSatisfying(it -> { - - assertThat(it).isInstanceOf(TypedAggregation.class); - TypedAggregation aggregation = (TypedAggregation) it; - - assertThat(aggregation.getInputType()).isEqualTo(Person.class); - assertThat(extractPipeline(aggregation)) - .containsExactly(new Document("$match", new Document("operationType", "insert"))); - }); - } - - @Test // DATAMONGO-2089 - void listenWithoutDomainTypeCreatesUntypedAggregation() { - - Criteria criteria = where("operationType").is("insert"); - changeStreamSupport.changeStream(Document.class).filter(criteria).listen().subscribe(); - - ArgumentCaptor optionsArgumentCaptor = ArgumentCaptor.forClass(ChangeStreamOptions.class); - verify(template).changeStream(isNull(), optionsArgumentCaptor.capture(), eq(Document.class)); - - assertThat(optionsArgumentCaptor.getValue().getFilter()).hasValueSatisfying(it -> { - - assertThat(it).isInstanceOf(Aggregation.class); - assertThat(it).isNotInstanceOf(TypedAggregation.class); - - Aggregation aggregation = (Aggregation) it; - - assertThat(extractPipeline(aggregation)) - .containsExactly(new Document("$match", new Document("operationType", "insert"))); - }); - } - - @Test // DATAMONGO-2089 - void optionsShouldBePassedOnCorrectly() { - - Document filter = new Document("$match", new Document("operationType", "insert")); - - changeStreamSupport.changeStream(Document.class).withOptions(options -> { - options.filter(filter); - }).listen().subscribe(); - - ArgumentCaptor optionsArgumentCaptor = ArgumentCaptor.forClass(ChangeStreamOptions.class); - verify(template).changeStream(isNull(), optionsArgumentCaptor.capture(), eq(Document.class)); - - assertThat(optionsArgumentCaptor.getValue()).satisfies(it -> { - assertThat(it.getFilter().get()).isEqualTo(Collections.singletonList(filter)); - }); - } - - @Test // DATAMONGO-2089 - void optionsShouldBeCombinedCorrectly() { - - Document filter = new Document("$match", new Document("operationType", "insert")); - Instant resumeTimestamp = Instant.now(); - - changeStreamSupport.changeStream(Document.class).withOptions(options -> { - options.filter(filter); - }).resumeAt(resumeTimestamp).listen().subscribe(); - - ArgumentCaptor optionsArgumentCaptor = ArgumentCaptor.forClass(ChangeStreamOptions.class); - verify(template).changeStream(isNull(), optionsArgumentCaptor.capture(), eq(Document.class)); - - assertThat(optionsArgumentCaptor.getValue()).satisfies(it -> { - - assertThat(it.getFilter().get()).isEqualTo(Collections.singletonList(filter)); - assertThat(it.getResumeTimestamp()).contains(resumeTimestamp); - }); - } - - private static List extractPipeline(Aggregation aggregation) { - return aggregation.toDocument("person", Aggregation.DEFAULT_CONTEXT).get("pipeline", ArrayList.class); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveClientSessionTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveClientSessionTests.java deleted file mode 100644 index 32906fd77e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveClientSessionTests.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; - -import com.mongodb.ClientSessionOptions; -import com.mongodb.reactivestreams.client.ClientSession; -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * @author Christoph Strobl - * @author Mark Paluch - * @currentRead Beyond the Shadows - Brent Weeks - */ -@ExtendWith(MongoClientExtension.class) -@EnableIfReplicaSetAvailable -public class ReactiveClientSessionTests { - - static final String DATABASE_NAME = "reflective-client-session-tests"; - static final String COLLECTION_NAME = "test"; - - static @Client MongoClient client; - - ReactiveMongoTemplate template; - - @BeforeEach - public void setUp() { - - template = new ReactiveMongoTemplate(client, DATABASE_NAME); - - MongoTestUtils.createOrReplaceCollection(DATABASE_NAME, COLLECTION_NAME, client) // - .as(StepVerifier::create) // - .verifyComplete(); - - template.insert(new Document("_id", "id-1").append("value", "spring"), COLLECTION_NAME) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-1880 - public void shouldApplyClientSession() { - - ClientSession session = Mono - .from(client.startSession(ClientSessionOptions.builder().causallyConsistent(true).build())).block(); - - assertThat(session.getOperationTime()).isNull(); - - template.withSession(() -> session) // - .execute(action -> action.findAll(Document.class, COLLECTION_NAME)) // - .as(StepVerifier::create) // - .expectNextCount(1).verifyComplete(); - - assertThat(session.getOperationTime()).isNotNull(); - assertThat(session.getServerSession().isClosed()).isFalse(); - - session.close(); - } - - @Test // DATAMONGO-1880 - public void useMonoInCallback() { - - ClientSession session = Mono - .from(client.startSession(ClientSessionOptions.builder().causallyConsistent(true).build())).block(); - - assertThat(session.getOperationTime()).isNull(); - - template.withSession(() -> session).execute(action -> action.findOne(new Query(), Document.class, COLLECTION_NAME)) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(session.getOperationTime()).isNotNull(); - assertThat(session.getServerSession().isClosed()).isFalse(); - - session.close(); - } - - @Test // DATAMONGO-1880 - public void reusesClientSessionInSessionScopedCallback() { - - ClientSession session = Mono - .from(client.startSession(ClientSessionOptions.builder().causallyConsistent(true).build())).block(); - CountingSessionSupplier sessionSupplier = new CountingSessionSupplier(session); - - ReactiveSessionScoped sessionScoped = template.withSession(sessionSupplier); - - sessionScoped.execute(action -> action.findOne(new Query(), Document.class, COLLECTION_NAME)).blockFirst(); - assertThat(sessionSupplier.getInvocationCount()).isEqualTo(1); - - sessionScoped.execute(action -> action.findOne(new Query(), Document.class, COLLECTION_NAME)).blockFirst(); - assertThat(sessionSupplier.getInvocationCount()).isEqualTo(1); - } - - @Test // DATAMONGO-1970 - public void addsClientSessionToContext() { - - template.withSession(client.startSession(ClientSessionOptions.builder().causallyConsistent(true).build())) - .execute(action -> ReactiveMongoContext.getSession()) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-2001 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - public void countInTransactionShouldReturnCount() { - - ClientSession session = Mono - .from(client.startSession(ClientSessionOptions.builder().causallyConsistent(true).build())).block(); - - template.withSession(() -> session).execute(action -> { - - session.startTransaction(); - - return action.insert(new Document("_id", "id-2").append("value", "in transaction"), COLLECTION_NAME) // - .then(action.count(query(where("value").is("in transaction")), Document.class, COLLECTION_NAME)) // - .flatMap(it -> Mono.from(session.commitTransaction()).then(Mono.just(it))); - - }).as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - - template.withSession(() -> session).execute(action -> { - - session.startTransaction(); - - return action.insert(new Document("value", "in transaction"), COLLECTION_NAME) // - .then(action.count(query(where("value").is("foo")), Document.class, COLLECTION_NAME)) // - .flatMap(it -> Mono.from(session.commitTransaction()).then(Mono.just(it))); - - }).as(StepVerifier::create) // - .expectNext(0L) // - .verifyComplete(); - } - - static class CountingSessionSupplier implements Supplier { - - AtomicInteger invocationCount = new AtomicInteger(0); - final ClientSession session; - - public CountingSessionSupplier(ClientSession session) { - this.session = session; - } - - @Override - public ClientSession get() { - - invocationCount.incrementAndGet(); - return session; - } - - int getInvocationCount() { - return invocationCount.get(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupportTests.java deleted file mode 100644 index c81e6ee3af..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupportTests.java +++ /dev/null @@ -1,712 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import reactor.core.Disposable; -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import java.util.Date; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -import org.bson.BsonString; -import org.bson.BsonValue; -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.annotation.Id; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; - -import com.mongodb.client.MongoClient; - -/** - * Integration tests for {@link ReactiveFindOperationSupport}. - * - * @author Mark Paluch - * @author Christoph Strobl - * @author Juergen Zimmermann - */ -@ExtendWith(MongoClientExtension.class) -class ReactiveFindOperationSupportTests { - - private static final String STAR_WARS = "star-wars"; - private MongoTemplate blocking; - private ReactiveMongoTemplate template; - - private static @Client MongoClient client; - private static @Client com.mongodb.reactivestreams.client.MongoClient reactiveClient; - - private Person han; - private Person luke; - - @BeforeEach - void setUp() { - - blocking = new MongoTemplate(new SimpleMongoClientDatabaseFactory(client, "ExecutableFindOperationSupportTests")); - recreateCollection(STAR_WARS, false); - - insertObjects(); - - template = new ReactiveMongoTemplate(reactiveClient, "ExecutableFindOperationSupportTests"); - } - - void insertObjects() { - - han = new Person(); - han.firstname = "han"; - han.lastname = "solo"; - han.id = "id-1"; - - luke = new Person(); - luke.firstname = "luke"; - luke.lastname = "skywalker"; - luke.id = "id-2"; - - blocking.save(han); - blocking.save(luke); - } - - void recreateCollection(String collectionName, boolean capped) { - - blocking.dropCollection(STAR_WARS); - - CollectionOptions options = CollectionOptions.empty(); - if (capped) { - options = options.capped().size(1024 * 1024); - } - - blocking.createCollection(STAR_WARS, options); - } - - @Test // DATAMONGO-1719 - void domainTypeIsRequired() { - assertThatIllegalArgumentException().isThrownBy(() -> template.query(null)); - } - - @Test // DATAMONGO-1719 - void returnTypeIsRequiredOnSet() { - assertThatIllegalArgumentException().isThrownBy(() -> template.query(Person.class).as(null)); - } - - @Test // DATAMONGO-1719 - void collectionIsRequiredOnSet() { - assertThatIllegalArgumentException().isThrownBy(() -> template.query(Person.class).inCollection(null)); - } - - @Test // DATAMONGO-1719 - void findAll() { - - template.query(Person.class).all().collectList().as(StepVerifier::create).consumeNextWith(actual -> { - assertThat(actual).containsExactlyInAnyOrder(han, luke); - }).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllWithCollection() { - template.query(Human.class).inCollection(STAR_WARS).all().as(StepVerifier::create).expectNextCount(2) - .verifyComplete(); - } - - @Test // DATAMONGO-2323 - void findAllAsDocumentDocument() { - template.query(Document.class).inCollection(STAR_WARS).all().as(StepVerifier::create).expectNextCount(2) - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllWithProjection() { - - template.query(Person.class).as(Jedi.class).all().map(it -> it.getClass().getName()).as(StepVerifier::create) // - .expectNext(Jedi.class.getName(), Jedi.class.getName()) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllBy() { - - template.query(Person.class).matching(query(where("firstname").is("luke"))).all().as(StepVerifier::create) // - .expectNext(luke) // - .verifyComplete(); - } - - @Test // DATAMONGO-2416 - void findAllByCriteria() { - - template.query(Person.class).matching(where("firstname").is("luke")).all().as(StepVerifier::create) // - .expectNext(luke) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllByWithCollectionUsingMappingInformation() { - - template.query(Jedi.class).inCollection(STAR_WARS).matching(query(where("name").is("luke"))).all() - .as(StepVerifier::create).consumeNextWith(it -> assertThat(it).isInstanceOf(Jedi.class)) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllByWithCollection() { - - template.query(Human.class).inCollection(STAR_WARS).matching(query(where("firstname").is("luke"))).all() - .as(StepVerifier::create).expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllByWithProjection() { - - template.query(Person.class).as(Jedi.class).matching(query(where("firstname").is("luke"))).all() - .as(StepVerifier::create).consumeNextWith(it -> assertThat(it).isInstanceOf(Jedi.class)) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllByWithClosedInterfaceProjection() { - - template.query(Person.class).as(PersonProjection.class).matching(query(where("firstname").is("luke"))).all() - .as(StepVerifier::create).consumeNextWith(it -> { - - assertThat(it).isInstanceOf(PersonProjection.class); - assertThat(it.getFirstname()).isEqualTo("luke"); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllByWithOpenInterfaceProjection() { - - template.query(Person.class).as(PersonSpELProjection.class).matching(query(where("firstname").is("luke"))).all() - .as(StepVerifier::create).consumeNextWith(it -> { - - assertThat(it).isInstanceOf(PersonSpELProjection.class); - assertThat(it.getName()).isEqualTo("luke"); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findBy() { - - template.query(Person.class).matching(query(where("firstname").is("luke"))).one().as(StepVerifier::create) - .expectNext(luke) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findByNoMatch() { - - template.query(Person.class).matching(query(where("firstname").is("spock"))).one().as(StepVerifier::create) - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findByTooManyResults() { - - template.query(Person.class).matching(query(where("firstname").in("han", "luke"))).one().as(StepVerifier::create) - .expectError(IncorrectResultSizeDataAccessException.class) // - .verify(); - } - - @Test // DATAMONGO-1719 - void findAllNearBy() { - - blocking.indexOps(Planet.class).ensureIndex( - new GeospatialIndex("coordinates").typed(GeoSpatialIndexType.GEO_2DSPHERE).named("planet-coordinate-idx")); - - Planet alderan = new Planet("alderan", new Point(-73.9836, 40.7538)); - Planet dantooine = new Planet("dantooine", new Point(-73.9928, 40.7193)); - - blocking.save(alderan); - blocking.save(dantooine); - - template.query(Planet.class).near(NearQuery.near(-73.9667, 40.78).spherical(true)).all().as(StepVerifier::create) - .consumeNextWith(actual -> { - assertThat(actual.getDistance()).isNotNull(); - }) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllNearByWithCollectionAndProjection() { - - blocking.indexOps(Planet.class).ensureIndex( - new GeospatialIndex("coordinates").typed(GeoSpatialIndexType.GEO_2DSPHERE).named("planet-coordinate-idx")); - - Planet alderan = new Planet("alderan", new Point(-73.9836, 40.7538)); - Planet dantooine = new Planet("dantooine", new Point(-73.9928, 40.7193)); - - blocking.save(alderan); - blocking.save(dantooine); - - template.query(Object.class).inCollection(STAR_WARS).as(Human.class) - .near(NearQuery.near(-73.9667, 40.78).spherical(true)).all().as(StepVerifier::create) - .consumeNextWith(actual -> { - assertThat(actual.getDistance()).isNotNull(); - assertThat(actual.getContent()).isInstanceOf(Human.class); - assertThat(actual.getContent().getId()).isEqualTo("alderan"); - }) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllNearByReturningGeoResultContentAsClosedInterfaceProjection() { - - blocking.indexOps(Planet.class).ensureIndex( - new GeospatialIndex("coordinates").typed(GeoSpatialIndexType.GEO_2DSPHERE).named("planet-coordinate-idx")); - - Planet alderan = new Planet("alderan", new Point(-73.9836, 40.7538)); - Planet dantooine = new Planet("dantooine", new Point(-73.9928, 40.7193)); - - blocking.save(alderan); - blocking.save(dantooine); - - template.query(Planet.class).as(PlanetProjection.class).near(NearQuery.near(-73.9667, 40.78).spherical(true)).all() - .as(StepVerifier::create).consumeNextWith(it -> { - - assertThat(it.getDistance()).isNotNull(); - assertThat(it.getContent()).isInstanceOf(PlanetProjection.class); - assertThat(it.getContent().getName()).isEqualTo("alderan"); - }) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void findAllNearByReturningGeoResultContentAsOpenInterfaceProjection() { - - blocking.indexOps(Planet.class).ensureIndex( - new GeospatialIndex("coordinates").typed(GeoSpatialIndexType.GEO_2DSPHERE).named("planet-coordinate-idx")); - - Planet alderan = new Planet("alderan", new Point(-73.9836, 40.7538)); - Planet dantooine = new Planet("dantooine", new Point(-73.9928, 40.7193)); - - blocking.save(alderan); - blocking.save(dantooine); - - template.query(Planet.class).as(PlanetSpELProjection.class).near(NearQuery.near(-73.9667, 40.78).spherical(true)) - .all().as(StepVerifier::create).consumeNextWith(it -> { - - assertThat(it.getDistance()).isNotNull(); - assertThat(it.getContent()).isInstanceOf(PlanetSpELProjection.class); - assertThat(it.getContent().getId()).isEqualTo("alderan"); - }) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-2080 - void tail() throws InterruptedException { - - recreateCollection(STAR_WARS, true); - insertObjects(); - - BlockingQueue collector = new LinkedBlockingQueue<>(); - Flux tail = template.query(Person.class) - .matching(query(new Criteria().orOperator(where("firstname").is("chewbacca"), where("firstname").is("luke")))) - .tail().doOnNext(collector::add); - - Disposable subscription = tail.subscribe(); - - assertThat(collector.poll(1, TimeUnit.SECONDS)).isEqualTo(luke); - assertThat(collector).isEmpty(); - - Person chewbacca = new Person(); - chewbacca.firstname = "chewbacca"; - chewbacca.lastname = "chewie"; - chewbacca.id = "id-3"; - - blocking.save(chewbacca); - - assertThat(collector.poll(1, TimeUnit.SECONDS)).isEqualTo(chewbacca); - - subscription.dispose(); - } - - @Test // DATAMONGO-2080 - void tailWithProjection() { - - recreateCollection(STAR_WARS, true); - insertObjects(); - - template.query(Person.class).as(Jedi.class).matching(query(where("firstname").is("luke"))).tail() - .as(StepVerifier::create) // - .consumeNextWith(it -> assertThat(it).isInstanceOf(Jedi.class)) // - .thenCancel() // - .verify(); - } - - @Test // DATAMONGO-2080 - void tailWithClosedInterfaceProjection() { - - recreateCollection(STAR_WARS, true); - insertObjects(); - - template.query(Person.class).as(PersonProjection.class).matching(query(where("firstname").is("luke"))).tail() - .as(StepVerifier::create) // - .consumeNextWith(it -> { - - assertThat(it).isInstanceOf(PersonProjection.class); - assertThat(it.getFirstname()).isEqualTo("luke"); - }) // - .thenCancel() // - .verify(); - } - - @Test // DATAMONGO-2080 - void tailWithOpenInterfaceProjection() { - - recreateCollection(STAR_WARS, true); - insertObjects(); - - template.query(Person.class).as(PersonSpELProjection.class).matching(query(where("firstname").is("luke"))).tail() - .as(StepVerifier::create) // - .consumeNextWith(it -> { - - assertThat(it).isInstanceOf(PersonSpELProjection.class); - assertThat(it.getName()).isEqualTo("luke"); - }) // - .thenCancel() // - .verify(); - } - - @Test // DATAMONGO-1719 - void firstShouldReturnFirstEntryInCollection() { - template.query(Person.class).first().as(StepVerifier::create).expectNextCount(1).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void countShouldReturnNrOfElementsInCollectionWhenNoQueryPresent() { - template.query(Person.class).count().as(StepVerifier::create).expectNext(2L).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void countShouldReturnNrOfElementsMatchingQuery() { - - template.query(Person.class).matching(query(where("firstname").is(luke.getFirstname()))).count() - .as(StepVerifier::create).expectNext(1L) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void existsShouldReturnTrueIfAtLeastOneElementExistsInCollection() { - template.query(Person.class).exists().as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void existsShouldReturnFalseIfNoElementExistsInCollection() { - - blocking.remove(new BasicQuery("{}"), STAR_WARS); - - template.query(Person.class).exists().as(StepVerifier::create).expectNext(false).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void existsShouldReturnTrueIfAtLeastOneElementMatchesQuery() { - - template.query(Person.class).matching(query(where("firstname").is(luke.getFirstname()))).exists() - .as(StepVerifier::create).expectNext(true) // - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void existsShouldReturnFalseWhenNoElementMatchesQuery() { - - template.query(Person.class).matching(query(where("firstname").is("spock"))).exists().as(StepVerifier::create) - .expectNext(false) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsEmptyListIfNoMatchFound() { - - template.query(Person.class).distinct("actually-not-property-in-use").as(String.class).all() - .as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsSimpleFieldValuesCorrectlyForCollectionHavingReturnTypeSpecifiedThatCanBeConvertedDirectlyByACodec() { - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.lastname = luke.lastname; - - blocking.save(anakin); - - template.query(Person.class).distinct("lastname").as(String.class).all().as(StepVerifier::create) - .assertNext(in("solo", "skywalker")).assertNext(in("solo", "skywalker")) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsSimpleFieldValuesCorrectly() { - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.ability = "dark-lord"; - - Person padme = new Person(); - padme.firstname = "padme"; - padme.ability = 42L; - - Person jaja = new Person(); - jaja.firstname = "jaja"; - jaja.ability = new Date(); - - blocking.save(anakin); - blocking.save(padme); - blocking.save(jaja); - - Consumer containedInAbilities = in(anakin.ability, padme.ability, jaja.ability); - - template.query(Person.class).distinct("ability").all().as(StepVerifier::create) // - .assertNext(containedInAbilities) // - .assertNext(containedInAbilities) // - .assertNext(containedInAbilities) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsComplexValuesCorrectly() { - - Sith sith = new Sith(); - sith.rank = "lord"; - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.ability = sith; - - blocking.save(anakin); - - template.query(Person.class).distinct("ability").all().as(StepVerifier::create) // - .expectNext(anakin.ability) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsComplexValuesCorrectlyHavingReturnTypeSpecified() { - - Sith sith = new Sith(); - sith.rank = "lord"; - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.ability = sith; - - blocking.save(anakin); - - template.query(Person.class).distinct("ability").as(Sith.class).all().as(StepVerifier::create) // - .expectNext(sith) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsComplexValuesCorrectlyReturnTypeDocumentSpecified() { - - Sith sith = new Sith(); - sith.rank = "lord"; - - Person anakin = new Person(); - anakin.firstname = "anakin"; - anakin.ability = sith; - - blocking.save(anakin); - - template.query(Person.class).distinct("ability").as(Document.class).all().as(StepVerifier::create) - .expectNext(new Document("rank", "lord").append("_class", Sith.class.getName())) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctMapsFieldNameCorrectly() { - - template.query(Jedi.class).inCollection(STAR_WARS).distinct("name").as(String.class).all().as(StepVerifier::create) - .assertNext(in("han", "luke")).assertNext(in("han", "luke")) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsRawValuesIfReturnTypeIsBsonValue() { - - Consumer inValues = in(new BsonString("solo"), new BsonString("skywalker")); - template.query(Person.class).distinct("lastname").as(BsonValue.class).all().as(StepVerifier::create) - .assertNext(inValues) // - .assertNext(inValues) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsValuesMappedToTheirJavaTypeEvenWhenNotExplicitlyDefinedByTheDomainType() { - - blocking.save(new Document("darth", "vader"), STAR_WARS); - - template.query(Person.class).distinct("darth").all().as(StepVerifier::create) // - .expectNext("vader") // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsMappedDomainTypeForProjections() { - - luke.father = new Person(); - luke.father.firstname = "anakin"; - - blocking.save(luke); - - template.query(Person.class).distinct("father").as(Jedi.class).all().as(StepVerifier::create) - .expectNext(new Jedi("anakin")) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctAlllowsQueryUsingObjectSourceType() { - - luke.father = new Person(); - luke.father.firstname = "anakin"; - - blocking.save(luke); - - template.query(Object.class).inCollection(STAR_WARS).distinct("father").as(Jedi.class).all() - .as(StepVerifier::create).expectNext(new Jedi("anakin")) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctReturnsMappedDomainTypeExtractedFromPropertyWhenNoExplicitTypePresent() { - - luke.father = new Person(); - luke.father.firstname = "anakin"; - - blocking.save(luke); - - Person expected = new Person(); - expected.firstname = luke.father.firstname; - - template.query(Person.class).distinct("father").all().as(StepVerifier::create) // - .expectNext(expected) // - .verifyComplete(); - } - - @Test // DATAMONGO-1761 - void distinctThrowsExceptionWhenExplicitMappingTypeCannotBeApplied() { - - template.query(Person.class).distinct("firstname").as(Long.class).all().as(StepVerifier::create) - .expectError(InvalidDataAccessApiUsageException.class) // - .verify(); - } - - @Test // DATAMONGO-2507 - void distinctAppliesFilterQuery() { - - template.query(Person.class).inCollection(STAR_WARS).distinct("firstname") // - .matching(where("lastname").is(luke.lastname)) // - .as(String.class) // - .all() // - .as(StepVerifier::create).consumeNextWith(it -> assertThat(it).isEqualTo("luke")) // - .verifyComplete(); - } - - interface Contact {} - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person implements Contact { - - @Id String id; - String firstname; - String lastname; - Object ability; - Person father; - } - - interface PersonProjection { - String getFirstname(); - } - - public interface PersonSpELProjection { - - @Value("#{target.firstname}") - String getName(); - } - - @Data - static class Human { - @Id String id; - } - - @Data - @NoArgsConstructor - @AllArgsConstructor - static class Jedi { - - @Field("firstname") String name; - } - - @Data - static class Sith { - - String rank; - } - - @Data - @AllArgsConstructor - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Planet { - - @Id String name; - Point coordinates; - } - - interface PlanetProjection { - String getName(); - } - - interface PlanetSpELProjection { - - @Value("#{target.name}") - String getId(); - } - - static Consumer in(T... values) { - return (val) -> { - assertThat(values).contains(val); - }; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupportUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupportUnitTests.java deleted file mode 100644 index ad47bdac28..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupportUnitTests.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.anyList; - -import lombok.Data; - -import java.util.Arrays; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.annotation.Id; - -/** - * Unit tests for {@link ExecutableInsertOperationSupport}. - * - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class ReactiveInsertOperationSupportUnitTests { - - private static final String STAR_WARS = "star-wars"; - - @Mock ReactiveMongoTemplate template; - - private ReactiveInsertOperationSupport ops; - - private Person luke, han; - - @BeforeEach - void setUp() { - - ops = new ReactiveInsertOperationSupport(template); - - luke = new Person(); - luke.id = "id-1"; - luke.firstname = "luke"; - - han = new Person(); - han.firstname = "han"; - han.id = "id-2"; - } - - @Test // DATAMONGO-1719 - void nullCollectionShouldThrowException() { - assertThatIllegalArgumentException().isThrownBy(() -> ops.insert(Person.class).inCollection(null)); - } - - @Test // DATAMONGO-1719 - void insertShouldUseDerivedCollectionName() { - - when(template.getCollectionName(any(Class.class))).thenReturn(STAR_WARS); - - ops.insert(Person.class).one(luke); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Class.class); - - verify(template).getCollectionName(captor.capture()); - verify(template).insert(eq(luke), eq(STAR_WARS)); - - assertThat(captor.getAllValues()).containsExactly(Person.class); - } - - @Test // DATAMONGO-1719 - void insertShouldUseExplicitCollectionName() { - - ops.insert(Person.class).inCollection(STAR_WARS).one(luke); - - verify(template, never()).getCollectionName(any(Class.class)); - verify(template).insert(eq(luke), eq(STAR_WARS)); - } - - @Test // DATAMONGO-1719 - void insertCollectionShouldDelegateCorrectly() { - - when(template.getCollectionName(any(Class.class))).thenReturn(STAR_WARS); - - ops.insert(Person.class).all(Arrays.asList(luke, han)); - - verify(template).getCollectionName(any(Class.class)); - verify(template).insert(anyList(), eq(STAR_WARS)); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person { - @Id String id; - String firstname; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java deleted file mode 100644 index e4941585e1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Query; - -/** - * Unit tests for {@link ReactiveMapReduceOperationSupport}. - * - * @author Christoph Strobl - * @author Mark Paluch - * @currentRead Beyond the Shadows - Brent Weeks - */ -@ExtendWith(MockitoExtension.class) -public class ReactiveMapReduceOperationSupportUnitTests { - - private static final String STAR_WARS = "star-wars"; - private static final String MAP_FUNCTION = "function() { emit(this.id, this.firstname) }"; - private static final String REDUCE_FUNCTION = "function(id, name) { return sum(id, name); }"; - - @Mock ReactiveMongoTemplate template; - - private ReactiveMapReduceOperationSupport mapReduceOpsSupport; - - @BeforeEach - void setUp() { - mapReduceOpsSupport = new ReactiveMapReduceOperationSupport(template); - } - - @Test // DATAMONGO-1929 - void throwsExceptionOnNullTemplate() { - assertThatIllegalArgumentException().isThrownBy(() -> new ExecutableMapReduceOperationSupport(null)); - } - - @Test // DATAMONGO-1929 - void throwsExceptionOnNullDomainType() { - assertThatIllegalArgumentException().isThrownBy(() -> mapReduceOpsSupport.mapReduce(null)); - } - - @Test // DATAMONGO-1929 - void usesExtractedCollectionName() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).all(); - - verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), isNull()); - } - - @Test // DATAMONGO-1929 - void usesExplicitCollectionName() { - - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION) - .inCollection("the-night-angel").all(); - - verify(template).mapReduce(any(Query.class), eq(Person.class), eq("the-night-angel"), eq(Person.class), - eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), isNull()); - } - - @Test // DATAMONGO-1929 - void usesMapReduceOptionsWhenPresent() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - - MapReduceOptions options = MapReduceOptions.options(); - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).with(options).all(); - - verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), eq(options)); - } - - @Test // DATAMONGO-1929 - void usesQueryWhenPresent() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - - Query query = new BasicQuery("{ 'lastname' : 'skywalker' }"); - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).matching(query).all(); - - verify(template).mapReduce(eq(query), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), isNull()); - } - - @Test // DATAMONGO-2416 - void usesCriteriaWhenPresent() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - - Query query = Query.query(where("lastname").is("skywalker")); - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION) - .matching(where("lastname").is("skywalker")).all(); - - verify(template).mapReduce(eq(query), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), isNull()); - } - - @Test // DATAMONGO-1929 - void usesProjectionWhenPresent() { - - when(template.getCollectionName(eq(Person.class))).thenReturn(STAR_WARS); - - mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).as(Jedi.class).all(); - - verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(Jedi.class), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), isNull()); - } - - interface Contact {} - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person implements Contact { - - @Id String id; - String firstname; - String lastname; - Object ability; - Person father; - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - static class Jedi { - - @Field("firstname") String name; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateCollationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateCollationTests.java deleted file mode 100644 index 1d05846743..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateCollationTests.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * @author Mark Paluch - * @author Christoph Strobl - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@ContextConfiguration -public class ReactiveMongoTemplateCollationTests { - - public static final String COLLECTION_NAME = "collation-1"; - static @Client MongoClient mongoClient; - - @Configuration - static class Config extends AbstractReactiveMongoConfiguration { - - @Override - public MongoClient reactiveMongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return "collation-tests"; - } - - @Override - protected Set> getInitialEntitySet() { - return Collections.emptySet(); - } - } - - @Autowired ReactiveMongoTemplate template; - - @BeforeEach - public void setUp() { - template.dropCollection(COLLECTION_NAME).as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1693 - public void createCollectionWithCollation() { - - template.createCollection(COLLECTION_NAME, CollectionOptions.just(Collation.of("en_US"))).as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Mono collation = getCollationInfo(COLLECTION_NAME); - collation.as(StepVerifier::create) // - .consumeNextWith(document -> assertThat(document.get("locale")).isEqualTo("en_US")) // - .verifyComplete(); - - } - - private Mono getCollationInfo(String collectionName) { - - return getCollectionInfo(collectionName) // - .map(it -> it.get("options", Document.class)) // - .map(it -> it.get("collation", Document.class)); - } - - @SuppressWarnings("unchecked") - private Mono getCollectionInfo(String collectionName) { - - return template.execute(db -> { - - return Flux.from(db.runCommand(new Document() // - .append("listCollections", 1) // - .append("filter", new Document("name", collectionName)))) // - .map(it -> it.get("cursor", Document.class)) - .flatMapIterable(it -> (List) it.get("firstBatch", List.class)); - }).next(); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateExecuteTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateExecuteTests.java deleted file mode 100644 index e2926145ab..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateExecuteTests.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2016-2021 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 static org.assertj.core.api.Assertions.*; -import static org.assertj.core.data.Offset.offset; -import static org.junit.Assume.*; - -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import org.bson.Document; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.mongodb.UncategorizedMongoDbException; -import org.springframework.data.util.Version; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.MongoException; -import com.mongodb.ReadPreference; -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * Integration test for {@link ReactiveMongoTemplate} execute methods. - * - * @author Mark Paluch - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:reactive-infrastructure.xml") -public class ReactiveMongoTemplateExecuteTests { - - private static final Version THREE = Version.parse("3.0"); - - @Autowired SimpleReactiveMongoDatabaseFactory factory; - @Autowired ReactiveMongoOperations operations; - - Version mongoVersion; - - @Before - public void setUp() { - - Flux cleanup = operations.dropCollection("person") // - .mergeWith(operations.dropCollection("execute_test")) // - .mergeWith(operations.dropCollection("execute_test1")) // - .mergeWith(operations.dropCollection("execute_test2")); - - cleanup.as(StepVerifier::create).verifyComplete(); - - if (mongoVersion == null) { - mongoVersion = operations.executeCommand("{ buildInfo: 1 }") // - .map(it -> it.get("version").toString())// - .map(Version::parse) // - .block(); - } - } - - @Test // DATAMONGO-1444 - public void executeCommandJsonCommandShouldReturnSingleResponse() { - - operations.executeCommand("{ buildInfo: 1 }").as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual).containsKey("version"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void executeCommandDocumentCommandShouldReturnSingleResponse() { - - operations.executeCommand(new Document("buildInfo", 1)).as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual).containsKey("version"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void executeCommandJsonCommandShouldReturnMultipleResponses() { - - assumeTrue(mongoVersion.isGreaterThan(THREE)); - - operations.executeCommand("{ insert: 'execute_test', documents: [{},{},{}]}").as(StepVerifier::create) - .expectNextCount(1).verifyComplete(); - - operations.executeCommand("{ find: 'execute_test'}").as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.get("ok", Double.class)).isCloseTo(1D, offset(0D)); - assertThat(actual).containsKey("cursor"); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void executeCommandJsonCommandShouldTranslateExceptions() { - - operations.executeCommand("{ unknown: 1 }").as(StepVerifier::create) // - .expectError(InvalidDataAccessApiUsageException.class) // - .verify(); - } - - @Test // DATAMONGO-1444 - public void executeCommandDocumentCommandShouldTranslateExceptions() { - - operations.executeCommand(new Document("unknown", 1)).as(StepVerifier::create) // - .expectError(InvalidDataAccessApiUsageException.class) // - .verify(); - - } - - @Test // DATAMONGO-1444 - public void executeCommandWithReadPreferenceCommandShouldTranslateExceptions() { - - operations.executeCommand(new Document("unknown", 1), ReadPreference.nearest()).as(StepVerifier::create) // - .expectError(InvalidDataAccessApiUsageException.class) // - .verify(); - } - - @Test // DATAMONGO-1444 - public void executeOnDatabaseShouldExecuteCommand() { - - Flux documentFlux = operations.executeCommand("{ insert: 'execute_test', documents: [{},{},{}]}") - .mergeWith(operations.executeCommand("{ insert: 'execute_test1', documents: [{},{},{}]}")) - .mergeWith(operations.executeCommand("{ insert: 'execute_test2', documents: [{},{},{}]}")); - - documentFlux.as(StepVerifier::create).expectNextCount(3).verifyComplete(); - - Flux execute = operations.execute(MongoDatabase::listCollections); - - execute.filter(document -> document.getString("name").startsWith("execute_test")).as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void executeOnDatabaseShouldDeferExecution() { - - operations.execute(db -> { - throw new MongoException(50, "hi there"); - }); - - // the assertion here is that the exception is not thrown - } - - @Test // DATAMONGO-1444 - public void executeOnDatabaseShouldShouldTranslateExceptions() { - - Flux execute = operations.execute(db -> { - throw new MongoException(50, "hi there"); - }); - - execute.as(StepVerifier::create).expectError(UncategorizedMongoDbException.class).verify(); - } - - @Test // DATAMONGO-1444 - public void executeOnCollectionWithTypeShouldReturnFindResults() { - - operations.executeCommand("{ insert: 'person', documents: [{},{},{}]}").as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - operations.execute(Person.class, MongoCollection::find).as(StepVerifier::create).expectNextCount(3) - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void executeOnCollectionWithNameShouldReturnFindResults() { - - operations.executeCommand("{ insert: 'execute_test', documents: [{},{},{}]}").as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - operations.execute("execute_test", MongoCollection::find).as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateIndexTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateIndexTests.java deleted file mode 100644 index 72a1360735..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateIndexTests.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2016-2021 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 static org.assertj.core.data.Index.atIndex; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import lombok.Data; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import java.time.Duration; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -import org.bson.Document; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junitpioneer.jupiter.RepeatFailedTest; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.index.Index; -import org.springframework.data.mongodb.core.index.IndexField; -import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.data.mongodb.core.index.Indexed; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; - -import com.mongodb.client.model.IndexOptions; -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoCollection; - -/** - * Integration test for index creation via {@link ReactiveMongoTemplate}. - * - * @author Mark Paluch - * @author Christoph Strobl - * @author Mathieu Ouellet - */ -@ExtendWith(MongoClientExtension.class) -public class ReactiveMongoTemplateIndexTests { - - private static @Client MongoClient client; - - private SimpleReactiveMongoDatabaseFactory factory; - private ReactiveMongoTemplate template; - - @BeforeEach - void setUp() { - - factory = new SimpleReactiveMongoDatabaseFactory(client, "reactive-template-index-tests"); - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setAutoIndexCreation(true); - template = new ReactiveMongoTemplate(factory, new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext)); - - MongoTestUtils.dropCollectionNow("reactive-template-index-tests", "person", client); - MongoTestUtils.dropCollectionNow("reactive-template-index-tests", "indexfail", client); - MongoTestUtils.dropCollectionNow("reactive-template-index-tests", "indexedSample", client); - } - - @AfterEach - void cleanUp() {} - - @RepeatFailedTest(3) // DATAMONGO-1444 - void testEnsureIndexShouldCreateIndex() { - - Person p1 = new Person("Oliver"); - p1.setAge(25); - template.insert(p1); - Person p2 = new Person("Sven"); - p2.setAge(40); - template.insert(p2); - - template.indexOps(Person.class) // - .ensureIndex(new Index().on("age", Direction.DESC).unique()) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.getCollection(template.getCollectionName(Person.class)).flatMapMany(MongoCollection::listIndexes) - .collectList() // - .as(StepVerifier::create) // - .consumeNextWith(indexInfo -> { - - assertThat(indexInfo).hasSize(2); - Object indexKey = null; - boolean unique = false; - for (Document ix : indexInfo) { - - if ("age_-1".equals(ix.get("name"))) { - indexKey = ix.get("key"); - unique = (Boolean) ix.get("unique"); - } - } - assertThat((Document) indexKey).containsEntry("age", -1); - assertThat(unique).isTrue(); - }).verifyComplete(); - } - - @RepeatFailedTest(3) // DATAMONGO-1444 - void getIndexInfoShouldReturnCorrectIndex() { - - Person p1 = new Person("Oliver"); - p1.setAge(25); - template.insert(p1) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.indexOps(Person.class) // - .ensureIndex(new Index().on("age", Direction.DESC).unique()) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.indexOps(Person.class).getIndexInfo().collectList() // - .as(StepVerifier::create) // - .consumeNextWith(indexInfos -> { - - assertThat(indexInfos).hasSize(2); - - IndexInfo ii = indexInfos.get(1); - assertThat(ii.isUnique()).isTrue(); - assertThat(ii.isSparse()).isFalse(); - - assertThat(ii.getIndexFields()).contains(IndexField.create("age", Direction.DESC), atIndex(0)); - }).verifyComplete(); - } - - @RepeatFailedTest(3) // DATAMONGO-1444, DATAMONGO-2264 - void testReadIndexInfoForIndicesCreatedViaMongoShellCommands() { - - template.indexOps(Person.class).dropAllIndexes() // - .as(StepVerifier::create) // - .verifyComplete(); - - template.indexOps(Person.class).getIndexInfo() // - .as(StepVerifier::create) // - .verifyComplete(); - - factory.getMongoDatabase() // - .flatMapMany(db -> db.getCollection(template.getCollectionName(Person.class)) - .createIndex(new Document("age", -1), new IndexOptions().unique(true).sparse(true))) - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.getCollection(template.getCollectionName(Person.class)).flatMapMany(MongoCollection::listIndexes) - .collectList() // - .as(StepVerifier::create) // - .consumeNextWith(indexInfos -> { - - Document indexKey = null; - boolean unique = false; - - for (Document document : indexInfos) { - - if ("age_-1".equals(document.get("name"))) { - indexKey = (org.bson.Document) document.get("key"); - unique = (Boolean) document.get("unique"); - } - } - - assertThat(indexKey).containsEntry("age", -1); - assertThat(unique).isTrue(); - }).verifyComplete(); - - Flux.from(template.indexOps(Person.class).getIndexInfo().collectList()) // - .as(StepVerifier::create) // - .consumeNextWith(indexInfos -> { - - IndexInfo info = indexInfos.get(1); - assertThat(info.isUnique()).isTrue(); - assertThat(info.isSparse()).isTrue(); - - assertThat(info.getIndexFields()).contains(IndexField.create("age", Direction.DESC), atIndex(0)); - }).verifyComplete(); - } - - @RepeatFailedTest(3) // DATAMONGO-1928 - void shouldCreateIndexOnAccess() { - - template.getCollection("indexedSample").flatMapMany(it -> it.listIndexes(Document.class)) // - .as(StepVerifier::create) // - .expectNextCount(0) // - .verifyComplete(); - - template.findAll(IndexedSample.class).defaultIfEmpty(new IndexedSample()) // - .delayElements(Duration.ofMillis(500)) // TODO: check if 4.2.0 server GA still requires this timeout - .then() - .as(StepVerifier::create) // - .verifyComplete(); - - template.getCollection("indexedSample").flatMapMany(it -> it.listIndexes(Document.class)) // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - } - - @RepeatFailedTest(3) // DATAMONGO-1928, DATAMONGO-2264 - void indexCreationShouldFail() throws InterruptedException { - - factory.getMongoDatabase() // - .flatMapMany(db -> db.getCollection("indexfail") // - .createIndex(new Document("field", 1), new IndexOptions().name("foo").unique(true).sparse(true))) - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - BlockingQueue queue = new LinkedBlockingQueue<>(); - ReactiveMongoTemplate template = new ReactiveMongoTemplate(factory, this.template.getConverter(), queue::add); - - template.findAll(IndexCreationShouldFail.class).subscribe(); - - Throwable failure = queue.poll(10, TimeUnit.SECONDS); - - assertThat(failure).isNotNull().isInstanceOf(DataIntegrityViolationException.class); - } - - @Data - static class Sample { - - @Id String id; - String field; - - public Sample() {} - - public Sample(String id, String field) { - this.id = id; - this.field = field; - } - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document - static class IndexedSample { - - @Id String id; - @Indexed String field; - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document("indexfail") - static class IndexCreationShouldFail { - - @Id String id; - @Indexed(name = "foo") String field; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java deleted file mode 100644 index 87b314fa04..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java +++ /dev/null @@ -1,1824 +0,0 @@ -/* - * Copyright 2016-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Wither; -import reactor.core.Disposable; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.bson.BsonDocument; -import org.bson.BsonTimestamp; -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.dao.OptimisticLockingFailureException; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.Version; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.geo.Metrics; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoTemplateTests.Address; -import org.springframework.data.mongodb.core.MongoTemplateTests.PersonWithConvertedId; -import org.springframework.data.mongodb.core.MongoTemplateTests.VersionedPerson; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.index.Index; -import org.springframework.data.mongodb.core.index.IndexOperationsAdapter; -import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener; -import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoServerCondition; -import org.springframework.data.mongodb.test.util.ReactiveMongoTestTemplate; - -import com.mongodb.WriteConcern; -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Integration test for {@link MongoTemplate}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -@ExtendWith({ MongoClientExtension.class, MongoServerCondition.class }) -public class ReactiveMongoTemplateTests { - - private static final String DB_NAME = "reactive-mongo-template-tests"; - private static @Client MongoClient client; - - private ConfigurableApplicationContext context = new GenericApplicationContext(); - private ReactiveMongoTestTemplate template = new ReactiveMongoTestTemplate(cfg -> { - - cfg.configureDatabaseFactory(it -> { - - it.client(client); - it.defaultDb(DB_NAME); - }); - - cfg.configureApplicationContext(it -> { - it.applicationContext(context); - }); - }); - - @BeforeEach - void setUp() { - - template - .flush(Person.class, MyPerson.class, Sample.class, Venue.class, PersonWithVersionPropertyOfTypeInteger.class) // - .as(StepVerifier::create) // - .verifyComplete(); - - template.flush("people", "collection", "personX", "unique_person").as(StepVerifier::create).verifyComplete(); - } - - private ReactiveMongoDatabaseFactory factory = template.getDatabaseFactory(); - - @Test // DATAMONGO-1444 - void insertSetsId() { - - PersonWithAList person = new PersonWithAList(); - assert person.getId() == null; - - template.insert(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(person.getId()).isNotNull(); - } - - @Test // DATAMONGO-1444 - void insertAllSetsId() { - - PersonWithAList person = new PersonWithAList(); - - template.insertAll(Collections.singleton(person)) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(person.getId()).isNotNull(); - } - - @Test // DATAMONGO-1444 - void insertCollectionSetsId() { - - PersonWithAList person = new PersonWithAList(); - - template.insert(Collections.singleton(person), PersonWithAList.class) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(person.getId()).isNotNull(); - } - - @Test // DATAMONGO-1444 - void saveSetsId() { - - PersonWithAList person = new PersonWithAList(); - assert person.getId() == null; - - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(person.getId()).isNotNull(); - } - - @Test // DATAMONGO-1444 - void insertsSimpleEntityCorrectly() { - - Person person = new Person("Mark"); - person.setAge(35); - template.insert(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.find(new Query(where("_id").is(person.getId())), Person.class) // - .as(StepVerifier::create) // - .expectNext(person) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void simpleInsertDoesNotAllowArrays() { - - Person person = new Person("Mark"); - person.setAge(35); - - assertThatIllegalArgumentException().isThrownBy(() -> template.insert(new Person[] { person })); - } - - @Test // DATAMONGO-1444 - void simpleInsertDoesNotAllowCollections() { - - Person person = new Person("Mark"); - person.setAge(35); - - assertThatIllegalArgumentException().isThrownBy(() -> template.insert(Collections.singletonList(person))); - } - - @Test // DATAMONGO-1444 - void insertsSimpleEntityWithSuppliedCollectionNameCorrectly() { - - Person person = new Person("Homer"); - person.setAge(35); - template.insert(person, "people") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.find(new Query(where("_id").is(person.getId())), Person.class, "people") // - .as(StepVerifier::create) // - .expectNext(person) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void insertBatchCorrectly() { - - List people = Arrays.asList(new Person("Dick", 22), new Person("Harry", 23), new Person("Tom", 21)); - - template.insertAll(people) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - template.find(new Query().with(Sort.by("firstname")), Person.class) // - .as(StepVerifier::create) // - .expectNextCount(3) /// - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void insertBatchWithSuppliedCollectionNameCorrectly() { - - List people = Arrays.asList(new Person("Dick", 22), new Person("Harry", 23), new Person("Tom", 21)); - - template.insert(people, "people") // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - template.find(new Query().with(Sort.by("firstname")), Person.class, "people") // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void insertBatchWithSuppliedEntityTypeCorrectly() { - - List people = Arrays.asList(new Person("Dick", 22), new Person("Harry", 23), new Person("Tom", 21)); - - template.insert(people, Person.class) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - template.find(new Query().with(Sort.by("firstname")), Person.class) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void testAddingToList() { - - PersonWithAList person = createPersonWithAList("Sven", 22); - template.insert(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Query query = new Query(where("id").is(person.getId())); - - template.findOne(query, PersonWithAList.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.getWishList()).isEmpty(); - }).verifyComplete(); - - person.addToWishList("please work!"); - - template.save(person).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - template.findOne(query, PersonWithAList.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.getWishList()).hasSize(1); - }).verifyComplete(); - - Friend friend = new Friend(); - person.setFirstName("Erik"); - person.setAge(21); - - person.addFriend(friend); - template.save(person).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - template.findOne(query, PersonWithAList.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.getWishList()).hasSize(1); - assertThat(actual.getFriends()).hasSize(1); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void testFindOneWithSort() { - - PersonWithAList sven = createPersonWithAList("Sven", 22); - PersonWithAList erik = createPersonWithAList("Erik", 21); - PersonWithAList mark = createPersonWithAList("Mark", 40); - - template.insertAll(Arrays.asList(sven, erik, mark)) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - // test query with a sort - Query query = new Query(where("age").gt(10)); - query.with(Sort.by(Direction.DESC, "age")); - - template.findOne(query, PersonWithAList.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.getFirstName()).isEqualTo("Mark"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void bogusUpdateDoesNotTriggerException() { - - ReactiveMongoTemplate mongoTemplate = new ReactiveMongoTemplate(factory); - mongoTemplate.setWriteResultChecking(WriteResultChecking.EXCEPTION); - - Person oliver = new Person("Oliver2", 25); - template.insert(oliver) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Query q = new Query(where("BOGUS").gt(22)); - Update u = new Update().set("firstName", "Sven"); - - mongoTemplate.updateFirst(q, u, Person.class) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void updateFirstByEntityTypeShouldUpdateObject() { - - Person person = new Person("Oliver2", 25); - template.insert(person) // - .then(template.updateFirst(new Query(where("age").is(25)), new Update().set("firstName", "Sven"), Person.class)) // - .flatMapMany(p -> template.find(new Query(where("age").is(25)), Person.class)) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.getFirstName()).isEqualTo("Sven"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void updateFirstByCollectionNameShouldUpdateObjects() { - - Person person = new Person("Oliver2", 25); - template.insert(person, "people") // - .then(template.updateFirst(new Query(where("age").is(25)), new Update().set("firstName", "Sven"), "people")) // - .flatMapMany(p -> template.find(new Query(where("age").is(25)), Person.class, "people")) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.getFirstName()).isEqualTo("Sven"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void updateMultiByEntityTypeShouldUpdateObjects() { - - Query query = new Query( - new Criteria().orOperator(where("firstName").is("Walter Jr"), where("firstName").is("Walter"))); - - template - .insertAll( - Mono.just(Arrays.asList(new Person("Walter", 50), new Person("Skyler", 43), new Person("Walter Jr", 16)))) // - .flatMap(a -> template.updateMulti(query, new Update().set("firstName", "Walt"), Person.class)) // - .thenMany(template.find(new Query(where("firstName").is("Walt")), Person.class)) // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void updateMultiByCollectionNameShouldUpdateObject() { - - Query query = new Query( - new Criteria().orOperator(where("firstName").is("Walter Jr"), where("firstName").is("Walter"))); - - List people = Arrays.asList(new Person("Walter", 50), // - new Person("Skyler", 43), // - new Person("Walter Jr", 16)); - - Flux personFlux = template.insertAll(Mono.just(people), "people") // - .collectList() // - .flatMap(a -> template.updateMulti(query, new Update().set("firstName", "Walt"), Person.class, "people")) // - .flatMapMany(p -> template.find(new Query(where("firstName").is("Walt")), Person.class, "people")); - - personFlux // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void throwsExceptionForDuplicateIds() { - - ReactiveMongoTemplate template = new ReactiveMongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - - Person person = new Person(new ObjectId(), "Amol"); - person.setAge(28); - - template.insert(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.insert(person) // - .as(StepVerifier::create) // - .expectError(DataIntegrityViolationException.class) // - .verify(); - } - - @Test // DATAMONGO-1444 - void throwsExceptionForUpdateWithInvalidPushOperator() { - - ReactiveMongoTemplate template = new ReactiveMongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - - ObjectId id = new ObjectId(); - Person person = new Person(id, "Amol"); - person.setAge(28); - - template.insert(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Query query = new Query(where("firstName").is("Amol")); - Update upd = new Update().push("age", 29); - - template.updateFirst(query, upd, Person.class) // - .as(StepVerifier::create) // - .verifyError(DataIntegrityViolationException.class); - } - - @Test // DATAMONGO-1444 - void rejectsDuplicateIdInInsertAll() { - - ReactiveMongoTemplate template = new ReactiveMongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - - ObjectId id = new ObjectId(); - Person person = new Person(id, "Amol"); - person.setAge(28); - - template.insertAll(Arrays.asList(person, person)) // - .as(StepVerifier::create) // - .verifyError(DataIntegrityViolationException.class); - } - - @Test // DATAMONGO-1444 - void testFindAndUpdate() { - - template.insertAll(Arrays.asList(new Person("Tom", 21), new Person("Dick", 22), new Person("Harry", 23))) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Query query = new Query(where("firstName").is("Harry")); - Update update = new Update().inc("age", 1); - - Person p = template.findAndModify(query, update, Person.class).block(); // return old - assertThat(p.getFirstName()).isEqualTo("Harry"); - assertThat(p.getAge()).isEqualTo(23); - p = template.findOne(query, Person.class).block(); - assertThat(p.getAge()).isEqualTo(24); - - p = template.findAndModify(query, update, Person.class, "person").block(); - assertThat(p.getAge()).isEqualTo(24); - p = template.findOne(query, Person.class).block(); - assertThat(p.getAge()).isEqualTo(25); - - p = template.findAndModify(query, update, new FindAndModifyOptions().returnNew(true), Person.class).block(); - assertThat(p.getAge()).isEqualTo(26); - - p = template.findAndModify(query, update, FindAndModifyOptions.none(), Person.class, "person").block(); - assertThat(p.getAge()).isEqualTo(26); - p = template.findOne(query, Person.class).block(); - assertThat(p.getAge()).isEqualTo(27); - - Query query2 = new Query(where("firstName").is("Mary")); - p = template.findAndModify(query2, update, new FindAndModifyOptions().returnNew(true).upsert(true), Person.class) - .block(); - assertThat(p.getFirstName()).isEqualTo("Mary"); - assertThat(p.getAge()).isEqualTo(1); - } - - @Test // DATAMONGO-1827 - void findAndReplaceShouldReplaceDocument() { - - org.bson.Document doc = new org.bson.Document("foo", "bar"); - template.save(doc, "findandreplace").as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - org.bson.Document replacement = new org.bson.Document("foo", "baz"); - template - .findAndReplace(query(where("foo").is("bar")), replacement, FindAndReplaceOptions.options(), - org.bson.Document.class, "findandreplace") // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual).containsEntry("foo", "bar"); - }).verifyComplete(); - - template.findOne(query(where("foo").is("baz")), org.bson.Document.class, "findandreplace") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceShouldErrorOnIdPresent() { - - template.save(new MyPerson("Walter")).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - MyPerson replacement = new MyPerson("Heisenberg"); - replacement.id = "invalid-id"; - - template.findAndReplace(query(where("name").is("Walter")), replacement) // - .as(StepVerifier::create) // - .expectError(InvalidDataAccessApiUsageException.class); - } - - @Test // DATAMONGO-1827 - void findAndReplaceShouldErrorOnSkip() { - - assertThatIllegalArgumentException().isThrownBy(() -> template - .findAndReplace(query(where("name").is("Walter")).skip(10), new MyPerson("Heisenberg")).subscribe()); - } - - @Test // DATAMONGO-1827 - void findAndReplaceShouldErrorOnLimit() { - - assertThatIllegalArgumentException().isThrownBy(() -> template - .findAndReplace(query(where("name").is("Walter")).limit(10), new MyPerson("Heisenberg")).subscribe()); - } - - @Test // DATAMONGO-1827 - void findAndReplaceShouldConsiderSortAndUpdateFirstIfMultipleFound() { - - MyPerson walter1 = new MyPerson("Walter 1"); - MyPerson walter2 = new MyPerson("Walter 2"); - - template.save(walter1).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - template.save(walter2).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - MyPerson replacement = new MyPerson("Heisenberg"); - - template.findAndReplace(query(where("name").regex("Walter.*")).with(Sort.by(Direction.DESC, "name")), replacement) - .as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - template.findAll(MyPerson.class).buffer(10).as(StepVerifier::create) - .consumeNextWith(it -> assertThat(it).hasSize(2).contains(walter1).doesNotContain(walter2)).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceShouldReplaceObject() { - - MyPerson person = new MyPerson("Walter"); - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.findAndReplace(query(where("name").is("Walter")), new MyPerson("Heisenberg")) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual.getName()).isEqualTo("Walter"); - }).verifyComplete(); - - template.findOne(query(where("name").is("Heisenberg")), MyPerson.class) // - .as(StepVerifier::create).expectNextCount(1).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceShouldConsiderFields() { - - MyPerson person = new MyPerson("Walter"); - person.address = new Address("TX", "Austin"); - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Query query = query(where("name").is("Walter")); - query.fields().include("address"); - - template.findAndReplace(query, new MyPerson("Heisenberg")) // - .as(StepVerifier::create) // - .consumeNextWith(it -> { - - assertThat(it.getName()).isNull(); - assertThat(it.getAddress()).isEqualTo(person.address); - }).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceNonExistingWithUpsertFalse() { - - template.findAndReplace(query(where("name").is("Walter")), new MyPerson("Heisenberg")) // - .as(StepVerifier::create) // - .verifyComplete(); - - template.findAll(MyPerson.class).as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceNonExistingWithUpsertTrue() { - - template - .findAndReplace(query(where("name").is("Walter")), new MyPerson("Heisenberg"), - FindAndReplaceOptions.options().upsert()) // - .as(StepVerifier::create) // - .verifyComplete(); - - template.findAll(MyPerson.class).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceShouldProjectReturnedObjectCorrectly() { - - MyPerson person = new MyPerson("Walter"); - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template - .findAndReplace(query(where("name").is("Walter")), new MyPerson("Heisenberg"), FindAndReplaceOptions.empty(), - MyPerson.class, MyPersonProjection.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual.getName()).isEqualTo("Walter"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceShouldReplaceObjectReturingNew() { - - MyPerson person = new MyPerson("Walter"); - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template - .findAndReplace(query(where("name").is("Walter")), new MyPerson("Heisenberg"), - FindAndReplaceOptions.options().returnNew()) - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual.getName()).isEqualTo("Heisenberg"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void testFindAllAndRemoveFullyReturnsAndRemovesDocuments() { - - Sample spring = new Sample("100", "spring"); - Sample data = new Sample("200", "data"); - Sample mongodb = new Sample("300", "mongodb"); - - template.insert(Arrays.asList(spring, data, mongodb), Sample.class) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Query qry = query(where("field").in("spring", "mongodb")); - - template.findAllAndRemove(qry, Sample.class) // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - - template.findOne(new Query(), Sample.class) // - .as(StepVerifier::create) // - .expectNext(data) // - .verifyComplete(); - } - - @Test // DATAMONGO-2219 - void testFindAllAndRemoveReturnsEmptyWithoutMatches() { - - Query qry = query(where("field").in("spring", "mongodb")); - template.findAllAndRemove(qry, Sample.class) // - .as(StepVerifier::create) // - .verifyComplete(); - - template.count(new Query(), Sample.class) // - .as(StepVerifier::create) // - .expectNext(0L).verifyComplete(); - } - - @Test // DATAMONGO-1774 - void testFindAllAndRemoveByCollectionReturnsAndRemovesDocuments() { - - Sample spring = new Sample("100", "spring"); - Sample data = new Sample("200", "data"); - Sample mongodb = new Sample("300", "mongodb"); - - template.insert(Arrays.asList(spring, data, mongodb), Sample.class) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Query qry = query(where("field").in("spring", "mongodb")); - - template.findAllAndRemove(qry, "sample") // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - - template.findOne(new Query(), Sample.class) // - .as(StepVerifier::create) // - .expectNext(data) // - .verifyComplete(); - } - - @Test // DATAMONGO-1774 - void removeWithNullShouldThrowError() { - assertThatIllegalArgumentException().isThrownBy(() -> template.remove((Object) null).subscribe()); - } - - @Test // DATAMONGO-1774 - void removeWithEmptyMonoShouldDoNothing() { - - Sample spring = new Sample("100", "spring"); - Sample data = new Sample("200", "data"); - Sample mongodb = new Sample("300", "mongodb"); - - template.insert(Arrays.asList(spring, data, mongodb), Sample.class) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - template.remove(Mono.empty()).as(StepVerifier::create).verifyComplete(); - template.count(new Query(), Sample.class) // - .as(StepVerifier::create) // - .expectNext(3L) // - .verifyComplete(); - } - - @Test // DATAMONGO-1774 - void removeWithMonoShouldDeleteElement() { - - Sample spring = new Sample("100", "spring"); - Sample data = new Sample("200", "data"); - Sample mongodb = new Sample("300", "mongodb"); - - template.insert(Arrays.asList(spring, data, mongodb), Sample.class) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - template.remove(Mono.just(spring)).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - template.count(new Query(), Sample.class).as(StepVerifier::create).expectNext(2L).verifyComplete(); - } - - @Test // DATAMONGO-1774 - void removeWithMonoAndCollectionShouldDeleteElement() { - - Sample spring = new Sample("100", "spring"); - Sample data = new Sample("200", "data"); - Sample mongodb = new Sample("300", "mongodb"); - - template.insert(Arrays.asList(spring, data, mongodb), Sample.class) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - template.remove(Mono.just(spring), template.getCollectionName(Sample.class)) // - .as(StepVerifier::create) // - .expectNextCount(1).verifyComplete(); - template.count(new Query(), Sample.class).as(StepVerifier::create).expectNext(2L).verifyComplete(); - } - - @Test // DATAMONGO-2195 - void removeVersionedEntityConsidersVersion() { - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.insert(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - assertThat(person.version).isZero(); - - template.update(PersonWithVersionPropertyOfTypeInteger.class).matching(query(where("id").is(person.id))) - .apply(new Update().set("firstName", "Walter")).first() // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.remove(person).as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.wasAcknowledged()).isTrue(); - assertThat(actual.getDeletedCount()).isZero(); - }).verifyComplete(); - template.count(new Query(), PersonWithVersionPropertyOfTypeInteger.class) // - .as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void optimisticLockingHandling() { - - // Init version - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.age = 29; - person.firstName = "Patryk"; - - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.findAll(PersonWithVersionPropertyOfTypeInteger.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.version).isZero(); - }).verifyComplete(); - - template.findAll(PersonWithVersionPropertyOfTypeInteger.class).flatMap(p -> { - - // Version change - person.firstName = "Patryk2"; - return template.save(person); - }) // - .as(StepVerifier::create) // - .expectNextCount(1).verifyComplete(); - - assertThat(person.version).isOne(); - - template.findAll(PersonWithVersionPropertyOfTypeInteger.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.version).isOne(); - }).verifyComplete(); - - // Optimistic lock exception - person.version = 0; - person.firstName = "Patryk3"; - - template.save(person).as(StepVerifier::create).expectError(OptimisticLockingFailureException.class).verify(); - } - - @Test // DATAMONGO-1444 - void doesNotFailOnVersionInitForUnversionedEntity() { - - Document dbObject = new Document(); - dbObject.put("firstName", "Oliver"); - - template.insert(dbObject, // - template.getCollectionName(PersonWithVersionPropertyOfTypeInteger.class)) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void removesObjectFromExplicitCollection() { - - String collectionName = "explicit"; - template.remove(new Query(), collectionName).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - PersonWithConvertedId person = new PersonWithConvertedId(); - person.name = "Dave"; - - template.save(person, collectionName) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.findAll(PersonWithConvertedId.class, collectionName) // - .as(StepVerifier::create) // - .expectNextCount(1).verifyComplete(); - - template.remove(person, collectionName).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - template.findAll(PersonWithConvertedId.class, collectionName).as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void savesMapCorrectly() { - - Map map = new HashMap<>(); - map.put("key", "value"); - - template.save(map, "maps") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test - // DATAMONGO-1444, DATAMONGO-1730, DATAMONGO-2150 - void savesMongoPrimitiveObjectCorrectly() { - assertThatExceptionOfType(MappingException.class).isThrownBy(() -> template.save(new Object(), "collection")); - } - - @Test // DATAMONGO-1444 - void savesPlainDbObjectCorrectly() { - - Document dbObject = new Document("foo", "bar"); - - template.save(dbObject, "collection") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(dbObject.containsKey("_id")).isTrue(); - } - - @Test // DATAMONGO-1444, DATAMONGO-1730 - void rejectsPlainObjectWithOutExplicitCollection() { - - Document dbObject = new Document("foo", "bar"); - - template.save(dbObject, "collection") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThatExceptionOfType(MappingException.class) - .isThrownBy(() -> template.findById(dbObject.get("_id"), Document.class)); - } - - @Test // DATAMONGO-1444 - void readsPlainDbObjectById() { - - Document dbObject = new Document("foo", "bar"); - template.save(dbObject, "collection") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.findById(dbObject.get("_id"), Document.class, "collection") // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.get("foo")).isEqualTo(dbObject.get("foo")); - assertThat(actual.get("_id")).isEqualTo(dbObject.get("_id")); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void geoNear() { - - List venues = Arrays.asList(TestEntities.geolocation().pennStation(), // - TestEntities.geolocation().tenGenOffice(), // - TestEntities.geolocation().flatironBuilding(), // - TestEntities.geolocation().maplewoodNJ()); - - template.insertAll(venues) // - .as(StepVerifier::create) // - .expectNextCount(4) // - .verifyComplete(); - - IndexOperationsAdapter.blocking(template.indexOps(Venue.class)) - .ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2D)); - - NearQuery geoFar = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).maxDistance(150, Metrics.KILOMETERS); - - template.geoNear(geoFar, Venue.class) // - .as(StepVerifier::create) // - .expectNextCount(4) // - .verifyComplete(); - - NearQuery geoNear = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).maxDistance(120, Metrics.KILOMETERS); - - template.geoNear(geoNear, Venue.class) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void writesPlainString() { - - template.save("{ 'foo' : 'bar' }", "collection") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444, DATAMONGO-2150 - void rejectsNonJsonStringForSave() { - assertThatExceptionOfType(MappingException.class).isThrownBy(() -> template.save("Foobar!", "collection")); - } - - @Test // DATAMONGO-1444 - void initializesVersionOnInsert() { - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.insert(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(person.version).isZero(); - } - - @Test // DATAMONGO-1444 - void initializesVersionOnBatchInsert() { - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.insertAll(Collections.singleton(person)) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(person.version).isZero(); - } - - @Test // DATAMONGO-1992 - void initializesIdAndVersionAndOfImmutableObject() { - - ImmutableVersioned versioned = new ImmutableVersioned(); - - template.insert(versioned) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual).isNotSameAs(versioned); - assertThat(versioned.id).isNull(); - assertThat(versioned.version).isNull(); - - assertThat(actual.id).isNotNull(); - assertThat(actual.version).isEqualTo(0); - - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void queryCanBeNull() { - - template.findAll(PersonWithIdPropertyOfTypeObjectId.class) // - .as(StepVerifier::create) // - .verifyComplete(); - - template.find(null, PersonWithIdPropertyOfTypeObjectId.class) // - .as(StepVerifier::create) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void versionsObjectIntoDedicatedCollection() { - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.save(person, "personX") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - assertThat(person.version).isZero(); - - template.save(person, "personX") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - assertThat(person.version).isOne(); - } - - @Test // DATAMONGO-1444 - void correctlySetsLongVersionProperty() { - - PersonWithVersionPropertyOfTypeLong person = new PersonWithVersionPropertyOfTypeLong(); - person.firstName = "Dave"; - - template.save(person, "personX") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - assertThat(person.version).isZero(); - } - - @Test // DATAMONGO-1444 - void throwsExceptionForIndexViolationIfConfigured() { - - ReactiveMongoTemplate template = new ReactiveMongoTemplate(factory); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - template.indexOps("unique_person") // - .ensureIndex(new Index().on("firstName", Direction.DESC).unique()) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Person person = new Person(new ObjectId(), "Amol"); - person.setAge(28); - - template.save(person, "unique_person") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - person = new Person(new ObjectId(), "Amol"); - person.setAge(28); - - template.save(person, "unique_person") // - .as(StepVerifier::create) // - .verifyError(DataIntegrityViolationException.class); - - // safeguard to clean up previous state - template.dropCollection(Person.class).as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void preventsDuplicateInsert() { - - template.setWriteConcern(WriteConcern.MAJORITY); - - PersonWithVersionPropertyOfTypeInteger person = new PersonWithVersionPropertyOfTypeInteger(); - person.firstName = "Dave"; - - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - assertThat(person.version).isZero(); - - person.version = null; - template.save(person) // - .as(StepVerifier::create) // - .verifyError(DuplicateKeyException.class); - } - - @Test // DATAMONGO-1444 - void countAndFindWithoutTypeInformation() { - - Person person = new Person(); - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Query query = query(where("_id").is(person.getId())); - String collectionName = template.getCollectionName(Person.class); - - template.find(query, HashMap.class, collectionName) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.count(query, collectionName) // - .as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void nullsPropertiesForVersionObjectUpdates() { - - VersionedPerson person = new VersionedPerson(); - person.firstname = "Dave"; - person.lastname = "Matthews"; - - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(person.id).isNotNull(); - - person.lastname = null; - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.findOne(query(where("id").is(person.id)), VersionedPerson.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.lastname).isNull(); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void nullsValuesForUpdatesOfUnversionedEntity() { - - Person person = new Person("Dave"); - template.save(person). // - as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - person.setFirstName(null); - template.save(person) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.findOne(query(where("id").is(person.getId())), Person.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.getFirstName()).isNull(); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void savesJsonStringCorrectly() { - - Document dbObject = new Document().append("first", "first").append("second", "second"); - - template.save(dbObject, "collection") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.findAll(Document.class, "collection") // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.containsKey("first")).isTrue(); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void executesExistsCorrectly() { - - Sample sample = new Sample(); - template.save(sample).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - Query query = query(where("id").is(sample.id)); - - template.exists(query, Sample.class) // - .as(StepVerifier::create) // - .expectNext(true) // - .verifyComplete(); - - template.exists(query(where("_id").is(sample.id)), template.getCollectionName(Sample.class)) // - .as(StepVerifier::create) // - .expectNext(true) // - .verifyComplete(); - - template.exists(query, Sample.class, template.getCollectionName(Sample.class)) // - .as(StepVerifier::create).expectNext(true) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void tailStreamsData() throws InterruptedException { - - template.dropCollection("capped").then(template.createCollection("capped", // - CollectionOptions.empty().size(1000).maxDocuments(10).capped())) - .then(template.insert(new Document("random", Math.random()).append("key", "value"), // - "capped")) // - .as(StepVerifier::create) // - .expectNextCount(1).verifyComplete(); - - BlockingQueue documents = new LinkedBlockingQueue<>(1000); - - Flux capped = template.tail(null, Document.class, "capped"); - - Disposable disposable = capped.doOnNext(documents::add).subscribe(); - - assertThat(documents.poll(5, TimeUnit.SECONDS)).isNotNull(); - assertThat(documents).isEmpty(); - - disposable.dispose(); - } - - @Test // DATAMONGO-1444 - void tailStreamsDataUntilCancellation() throws InterruptedException { - - template.dropCollection("capped").then(template.createCollection("capped", // - CollectionOptions.empty().size(1000).maxDocuments(10).capped())) - .then(template.insert(new Document("random", Math.random()).append("key", "value"), // - "capped")) // - .as(StepVerifier::create) // - .expectNextCount(1).verifyComplete(); - - BlockingQueue documents = new LinkedBlockingQueue<>(1000); - - Flux capped = template.tail(null, Document.class, "capped"); - - Disposable disposable = capped.doOnNext(documents::add).subscribe(); - - assertThat(documents.poll(5, TimeUnit.SECONDS)).isNotNull(); - assertThat(documents).isEmpty(); - - template.insert(new Document("random", Math.random()).append("key", "value"), "capped") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(documents.poll(5, TimeUnit.SECONDS)).isNotNull(); - - disposable.dispose(); - - template.insert(new Document("random", Math.random()).append("key", "value"), "capped") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(documents.poll(1, TimeUnit.SECONDS)).isNull(); - } - - @Test // DATAMONGO-1761 - void testDistinct() { - - Person person1 = new Person("Christoph", 38); - Person person2 = new Person("Christine", 39); - Person person3 = new Person("Christoph", 37); - - template.insertAll(Arrays.asList(person1, person2, person3)) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - template.findDistinct("firstName", Person.class, String.class) // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - } - - @Test // DATAMONGO-1803 - @Disabled("Heavily relying on timing assumptions. Cannot test message resumption properly. Too much race for too little time in between.") - @EnableIfReplicaSetAvailable - void changeStreamEventsShouldBeEmittedCorrectly() throws InterruptedException { - - template.createCollection(Person.class).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - Disposable disposable = template.changeStream("person", ChangeStreamOptions.empty(), Document.class) - .doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 39); - Person person3 = new Person("MongoDB", 37); - - Flux.merge(template.insert(person1), template.insert(person2), template.insert(person3)) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(documents.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())).hasSize(3) - .allMatch(val -> val instanceof Document); - } finally { - disposable.dispose(); - } - } - - @Test // DATAMONGO-1803 - @Disabled("Heavily relying on timing assumptions. Cannot test message resumption properly. Too much race for too little time in between.") - @EnableIfReplicaSetAvailable - void changeStreamEventsShouldBeConvertedCorrectly() throws InterruptedException { - - template.createCollection(Person.class).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - Disposable disposable = template.changeStream("person", ChangeStreamOptions.empty(), Person.class) - .doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 39); - Person person3 = new Person("MongoDB", 37); - - Flux.merge(template.insert(person1), template.insert(person2), template.insert(person3)) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(documents.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())) - .containsExactly(person1, person2, person3); - } finally { - disposable.dispose(); - } - } - - @Test // DATAMONGO-1803 - @Disabled("Heavily relying on timing assumptions. Cannot test message resumption properly. Too much race for too little time in between.") - @EnableIfReplicaSetAvailable - void changeStreamEventsShouldBeFilteredCorrectly() throws InterruptedException { - - template.createCollection(Person.class).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - Disposable disposable = template.changeStream("person", - ChangeStreamOptions.builder().filter(newAggregation(Person.class, match(where("age").gte(38)))).build(), - Person.class).doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 37); - Person person3 = new Person("MongoDB", 39); - - Flux.merge(template.save(person1), template.save(person2), template.save(person3)) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(documents.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())) - .containsExactly(person1, person3); - } finally { - disposable.dispose(); - } - } - - @Test // DATAMONGO-1803 - @EnableIfReplicaSetAvailable - void mapsReservedWordsCorrectly() throws InterruptedException { - - template.dropCollection(Person.class).onErrorResume(it -> Mono.empty()).as(StepVerifier::create).verifyComplete(); - template.createCollection(Person.class).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - Disposable disposable = template - .changeStream("person", - ChangeStreamOptions.builder() - .filter(newAggregation(Person.class, match(where("operationType").is("replace")))).build(), - Person.class) - .doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 37); - - Flux.merge(template.insert(person1), template.insert(person2)) // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - - Person replacement = new Person(person2.getId(), "BDognoM"); - replacement.setAge(person2.getAge()); - - template.save(replacement) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(documents.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())) - .containsExactly(replacement); - } finally { - disposable.dispose(); - } - } - - @Test // DATAMONGO-1803 - @Disabled("Heavily relying on timing assumptions. Cannot test message resumption properly. Too much race for too little time in between.") - @EnableIfReplicaSetAvailable - void changeStreamEventsShouldBeResumedCorrectly() throws InterruptedException { - - template.createCollection(Person.class).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - Disposable disposable = template.changeStream("person", ChangeStreamOptions.empty(), Person.class) - .doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 37); - Person person3 = new Person("MongoDB", 39); - - Flux.merge(template.insert(person1), template.insert(person2), template.insert(person3)) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - disposable.dispose(); - - BsonDocument resumeToken = documents.take().getRaw().getResumeToken(); - - BlockingQueue> resumeDocuments = new LinkedBlockingQueue<>(100); - template.changeStream("person", ChangeStreamOptions.builder().resumeToken(resumeToken).build(), Person.class) - .doOnNext(resumeDocuments::add).subscribe(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(resumeDocuments.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())) - .containsExactly(person2, person3); - } finally { - disposable.dispose(); - } - - } - - @Test // DATAMONGO-1870 - void removeShouldConsiderLimit() { - - List samples = IntStream.range(0, 100) // - .mapToObj(i -> new Sample("id-" + i, i % 2 == 0 ? "stark" : "lannister")) // - .collect(Collectors.toList()); - - template.insertAll(samples) // - .as(StepVerifier::create) // - .expectNextCount(100) // - .verifyComplete(); - - template.remove(query(where("field").is("lannister")).limit(25), Sample.class) // - .as(StepVerifier::create) // - .assertNext(wr -> assertThat(wr.getDeletedCount()).isEqualTo(25L)).verifyComplete(); - } - - @Test // DATAMONGO-1870 - void removeShouldConsiderSkipAndSort() { - - List samples = IntStream.range(0, 100) // - .mapToObj(i -> new Sample("id-" + i, i % 2 == 0 ? "stark" : "lannister")) // - .collect(Collectors.toList()); - - template.insertAll(samples).as(StepVerifier::create).expectNextCount(100).verifyComplete(); - - template.remove(new Query().skip(25).with(Sort.by("field")), Sample.class) // - .as(StepVerifier::create) // - .assertNext(wr -> assertThat(wr.getDeletedCount()).isEqualTo(75L)).verifyComplete(); - - template.count(query(where("field").is("lannister")), Sample.class).as(StepVerifier::create).expectNext(25L) - .verifyComplete(); - template.count(query(where("field").is("stark")), Sample.class).as(StepVerifier::create).expectNext(0L) - .verifyComplete(); - } - - @Test // DATAMONGO-2189 - void afterSaveEventContainsSavedObjectUsingInsert() { - - AtomicReference saved = createAfterSaveReference(); - ImmutableVersioned source = new ImmutableVersioned(); - - template.insert(source) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(saved.get()).isNotNull().isNotSameAs(source); - assertThat(saved.get().id).isNotNull(); - } - - @Test // DATAMONGO-2189 - void afterSaveEventContainsSavedObjectUsingInsertAll() { - - AtomicReference saved = createAfterSaveReference(); - ImmutableVersioned source = new ImmutableVersioned(); - - template.insertAll(Collections.singleton(new ImmutableVersioned())) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(saved.get()).isNotNull().isNotSameAs(source); - assertThat(saved.get().id).isNotNull(); - } - - @Test // DATAMONGO-2012 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - @EnableIfReplicaSetAvailable - void watchesDatabaseCorrectly() throws InterruptedException { - - template.dropCollection(Person.class).onErrorResume(it -> Mono.empty()).as(StepVerifier::create).verifyComplete(); - template.dropCollection("personX").onErrorResume(it -> Mono.empty()).as(StepVerifier::create).verifyComplete(); - template.createCollection(Person.class).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - template.createCollection("personX").as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - Disposable disposable = template.changeStream(ChangeStreamOptions.empty(), Person.class).doOnNext(documents::add) - .subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 37); - Person person3 = new Person("MongoDB", 39); - - template.save(person1) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - template.save(person2) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - template.save(person3, "personX") // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(documents.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())) - .containsExactly(person1, person2, person3); - } finally { - disposable.dispose(); - } - } - - @Test // DATAMONGO-2012, DATAMONGO-2113 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - @EnableIfReplicaSetAvailable - void resumesAtTimestampCorrectly() throws InterruptedException { - - template.dropCollection(Person.class).onErrorResume(it -> Mono.empty()).as(StepVerifier::create).verifyComplete(); - template.createCollection(Person.class).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - Disposable disposable = template.changeStream("person", ChangeStreamOptions.empty(), Person.class) - .doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 37); - Person person3 = new Person("MongoDB", 39); - - template.save(person1).delayElement(Duration.ofSeconds(1)) // - .as(StepVerifier::create) // - .expectNextCount(1) // - - .verifyComplete(); - template.save(person2) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Thread.sleep(500); // just give it some time to link receive all events - - disposable.dispose(); - - documents.take(); // skip first - Instant resumeAt = documents.take().getTimestamp(); // take 2nd - - template.save(person3).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue> resumeDocuments = new LinkedBlockingQueue<>(100); - template.changeStream("person", ChangeStreamOptions.builder().resumeAt(resumeAt).build(), Person.class) - .doOnNext(resumeDocuments::add).subscribe(); - - Thread.sleep(500); // just give it some time to link receive all events - - try { - assertThat(resumeDocuments.stream().map(ChangeStreamEvent::getBody).collect(Collectors.toList())) - .containsExactly(person2, person3); - } finally { - disposable.dispose(); - } - } - - @Test // DATAMONGO-2115 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - @EnableIfReplicaSetAvailable - void resumesAtBsonTimestampCorrectly() throws InterruptedException { - - template.createCollection(Person.class).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue> documents = new LinkedBlockingQueue<>(100); - Disposable disposable = template.changeStream("person", ChangeStreamOptions.empty(), Person.class) - .doOnNext(documents::add).subscribe(); - - Thread.sleep(500); // just give it some time to link to the collection. - - Person person1 = new Person("Spring", 38); - Person person2 = new Person("Data", 37); - Person person3 = new Person("MongoDB", 39); - - template.save(person1).delayElement(Duration.ofSeconds(1)) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - template.save(person2) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - documents.take(); // skip first - BsonTimestamp resumeAt = documents.take().getBsonTimestamp(); // take 2nd - - disposable.dispose(); - - template.save(person3).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - template.changeStream("person", ChangeStreamOptions.builder().resumeAt(resumeAt).build(), Person.class) - .map(ChangeStreamEvent::getBody) // - .buffer(2) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual).containsExactly(person2, person3); - }).thenCancel() // - .verify(); - } - - private PersonWithAList createPersonWithAList(String firstname, int age) { - - PersonWithAList p = new PersonWithAList(); - p.setFirstName(firstname); - p.setAge(age); - - return p; - } - - private AtomicReference createAfterSaveReference() { - - AtomicReference saved = new AtomicReference<>(); - context.addApplicationListener(new AbstractMongoEventListener() { - - @Override - public void onAfterSave(AfterSaveEvent event) { - saved.set(event.getSource()); - } - }); - - return saved; - } - - @AllArgsConstructor - @Wither - static class ImmutableVersioned { - - final @Id String id; - final @Version Long version; - - ImmutableVersioned() { - id = null; - version = null; - } - } - - @Data - static class Sample { - - @Id String id; - String field; - - Sample() {} - - Sample(String id, String field) { - this.id = id; - this.field = field; - } - } - - @Data - @NoArgsConstructor - @AllArgsConstructor - public static class MyPerson { - - String id; - String name; - Address address; - - MyPerson(String name) { - this.name = name; - } - } - - interface MyPersonProjection { - String getName(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTransactionTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTransactionTests.java deleted file mode 100644 index 05f5a7c85c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTransactionTests.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.Arrays; -import java.util.stream.Collectors; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.reactivestreams.Publisher; -import org.springframework.data.domain.Sort; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; - -import com.mongodb.ClientSessionOptions; -import com.mongodb.reactivestreams.client.ClientSession; -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Integration tests for Mongo Transactions using {@link ReactiveMongoTemplate}. - * - * @author Christoph Strobl - * @author Mark Paluch - * @author Mathieu Ouellet - * @currentRead The Core - Peter V. Brett - */ -@ExtendWith(MongoClientExtension.class) -@EnableIfReplicaSetAvailable -@EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") -public class ReactiveMongoTemplateTransactionTests { - - static final String DATABASE_NAME = "reactive-template-tx-tests"; - static final String COLLECTION_NAME = "test"; - static final Document DOCUMENT = new Document("_id", "id-1").append("value", "spring"); - static final Query ID_QUERY = query(where("_id").is("id-1")); - - static final Person AHMANN = new Person("ahmann", 32); - static final Person ARLEN = new Person("arlen", 24); - static final Person LEESHA = new Person("leesha", 22); - static final Person RENNA = new Person("renna", 22); - - static @Client MongoClient client; - ReactiveMongoTemplate template; - - @BeforeEach - public void setUp() { - - template = new ReactiveMongoTemplate(client, DATABASE_NAME); - - MongoTestUtils.createOrReplaceCollection(DATABASE_NAME, COLLECTION_NAME, client).as(StepVerifier::create) // - .verifyComplete(); - - MongoTestUtils.createOrReplaceCollection(DATABASE_NAME, "person", client).as(StepVerifier::create).verifyComplete(); - - MongoTestUtils.createOrReplaceCollection(DATABASE_NAME, "personWithVersionPropertyOfTypeInteger", client) - .as(StepVerifier::create) // - .verifyComplete(); - - template.insert(DOCUMENT, COLLECTION_NAME).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - template.insertAll(Arrays.asList(AHMANN, ARLEN, LEESHA, RENNA)) // - .as(StepVerifier::create) // - .expectNextCount(4) // - .verifyComplete(); - } - - @Test // DATAMONGO-1970 - public void reactiveTransactionWithExplicitTransactionStart() { - - Publisher sessionPublisher = client - .startSession(ClientSessionOptions.builder().causallyConsistent(true).build()); - - ClientSession clientSession = Mono.from(sessionPublisher).block(); - - template.withSession(Mono.just(clientSession)) - .execute(action -> ReactiveMongoContext.getSession().flatMap(session -> { - - session.startTransaction(); - return action.remove(ID_QUERY, Document.class, COLLECTION_NAME); - - })).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - template.exists(ID_QUERY, COLLECTION_NAME) // - .as(StepVerifier::create) // - .expectNext(true) // - .verifyComplete(); - - assertThat(clientSession.hasActiveTransaction()).isTrue(); - StepVerifier.create(clientSession.commitTransaction()).verifyComplete(); - - template.exists(ID_QUERY, COLLECTION_NAME) // - .as(StepVerifier::create) // - .expectNext(false) // - .verifyComplete(); - } - - @Test // DATAMONGO-1970 - public void reactiveTransactionsCommitOnComplete() { - - template.inTransaction().execute(action -> action.remove(ID_QUERY, Document.class, COLLECTION_NAME)) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.exists(ID_QUERY, COLLECTION_NAME) // - .as(StepVerifier::create) // - .expectNext(false) // - .verifyComplete(); - } - - @Test // DATAMONGO-1970 - public void reactiveTransactionsAbortOnError() { - - template.inTransaction().execute(action -> { - return action.remove(ID_QUERY, Document.class, COLLECTION_NAME).flatMap(result -> Mono.fromSupplier(() -> { - throw new RuntimeException("¯\\_(ツ)_/¯"); - })); - }).as(StepVerifier::create) // - .expectError() // - .verify(); - - template.exists(ID_QUERY, COLLECTION_NAME) // - .as(StepVerifier::create) // - .expectNext(true) // - .verifyComplete(); - } - - @Test // DATAMONGO-1970 - public void withSessionDoesNotManageTransactions() { - - Mono.from(client.startSession()).flatMap(session -> { - - session.startTransaction(); - return template.withSession(session).remove(ID_QUERY, Document.class, COLLECTION_NAME); - }).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - template.exists(ID_QUERY, COLLECTION_NAME) // - .as(StepVerifier::create) // - .expectNext(true) // - .verifyComplete(); - } - - @Test // DATAMONGO-1970 - public void inTransactionCommitsProvidedTransactionalSession() { - - ClientSession session = Mono.from(client.startSession()).block(); - - session.startTransaction(); - - template.inTransaction(Mono.just(session)).execute(action -> { - return action.remove(ID_QUERY, Document.class, COLLECTION_NAME); - }) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - assertThat(session.hasActiveTransaction()).isFalse(); - } - - @Test // DATAMONGO-1970 - public void changesNotVisibleOutsideTransaction() { - - template.inTransaction().execute(action -> { - return action.remove(ID_QUERY, Document.class, COLLECTION_NAME).flatMapMany(val -> { - - // once we use the collection directly we're no longer participating in the tx - return template.getCollection(COLLECTION_NAME).flatMapMany(it -> it.find(ID_QUERY.getQueryObject())); - }); - }).as(StepVerifier::create).expectNext(DOCUMENT).verifyComplete(); - - template.exists(ID_QUERY, COLLECTION_NAME) // - .as(StepVerifier::create) // - .expectNext(false) // - .verifyComplete(); - } - - @Test // DATAMONGO-1970 - public void executeCreatesNewTransaction() { - - ReactiveSessionScoped sessionScoped = template.inTransaction(); - - sessionScoped.execute(action -> { - return action.remove(ID_QUERY, Document.class, COLLECTION_NAME); - }) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.exists(ID_QUERY, COLLECTION_NAME) // - .as(StepVerifier::create) // - .expectNext(false) // - .verifyComplete(); - - sessionScoped.execute(action -> { - return action.insert(DOCUMENT, COLLECTION_NAME); - }) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.exists(ID_QUERY, COLLECTION_NAME) // - .as(StepVerifier::create) // - .expectNext(true) // - .verifyComplete(); - } - - @Test // DATAMONGO-1970 - public void takeDoesNotAbortTransaction() { - - template.inTransaction().execute(action -> { - return action.find(query(where("age").exists(true)).with(Sort.by("age")), Person.class).take(3) - .flatMap(action::remove); - }) // - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - - template.count(query(where("age").exists(true)), Person.class) // - .as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - } - - @Test // DATAMONGO-1970 - public void errorInFlowOutsideTransactionDoesNotAbortIt() { - - template.inTransaction().execute(action -> { - - return action.find(query(where("age").is(22)).with(Sort.by("age")), Person.class).buffer(2).flatMap(values -> { - - return action.remove(query(where("id").in(values.stream().map(Person::getId).collect(Collectors.toList()))), - Person.class).then(Mono.just(values)); - }); - }).flatMap(deleted -> { - throw new RuntimeException("error outside the transaction does not influence it."); - }) // - .as(StepVerifier::create) // - .expectError() // - .verify(); - - template.count(query(where("age").exists(true)), Person.class) // - .as(StepVerifier::create) // - .expectNext(2L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2195 - public void deleteWithMatchingVersion() { - - PersonWithVersionPropertyOfTypeInteger rojer = new PersonWithVersionPropertyOfTypeInteger(); - rojer.firstName = "rojer"; - - PersonWithVersionPropertyOfTypeInteger saved = template.insert(rojer).block(); - - template.inTransaction().execute(action -> action.remove(saved)) // - .as(StepVerifier::create) // - .consumeNextWith(result -> assertThat(result.getDeletedCount()).isOne()) // - .verifyComplete(); - } - - @Test // DATAMONGO-2195 - public void deleteWithVersionMismatch() { - - PersonWithVersionPropertyOfTypeInteger rojer = new PersonWithVersionPropertyOfTypeInteger(); - rojer.firstName = "rojer"; - - PersonWithVersionPropertyOfTypeInteger saved = template.insert(rojer).block(); - saved.version = 5; - - template.inTransaction().execute(action -> action.remove(saved)) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.wasAcknowledged()).isTrue(); - assertThat(actual.getDeletedCount()).isZero(); - }).verifyComplete(); - } - - @Test // DATAMONGO-2195 - public void deleteNonExistingWithVersion() { - - PersonWithVersionPropertyOfTypeInteger rojer = new PersonWithVersionPropertyOfTypeInteger(); - rojer.id = "deceased"; - rojer.firstName = "rojer"; - rojer.version = 5; - - template.inTransaction().execute(action -> action.remove(rojer)) // - .as(StepVerifier::create) // - .consumeNextWith(result -> assertThat(result.getDeletedCount()).isZero()) // - .verifyComplete(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java deleted file mode 100644 index eb44de349f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java +++ /dev/null @@ -1,1541 +0,0 @@ -/* - * Copyright 2016-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.test.util.Assertions.assertThat; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import org.assertj.core.api.Assertions; -import org.bson.Document; -import org.bson.conversions.Bson; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationListener; -import org.springframework.context.support.StaticApplicationContext; -import org.springframework.data.annotation.Id; -import org.springframework.data.mapping.callback.ReactiveEntityCallbacks; -import org.springframework.data.mongodb.core.MongoTemplateUnitTests.AutogenerateableId; -import org.springframework.data.mongodb.core.aggregation.AggregationOptions; -import org.springframework.data.mongodb.core.aggregation.AggregationUpdate; -import org.springframework.data.mongodb.core.aggregation.ArithmeticOperators; -import org.springframework.data.mongodb.core.aggregation.ComparisonOperators.Gte; -import org.springframework.data.mongodb.core.aggregation.ConditionalOperators; -import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator; -import org.springframework.data.mongodb.core.aggregation.Fields; -import org.springframework.data.mongodb.core.aggregation.SetOperation; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener; -import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; -import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterConvertCallback; -import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterSaveCallback; -import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeConvertCallback; -import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeSaveCallback; -import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.lang.Nullable; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.CollectionUtils; - -import com.mongodb.MongoClientSettings; -import com.mongodb.ReadPreference; -import com.mongodb.client.model.CountOptions; -import com.mongodb.client.model.CreateCollectionOptions; -import com.mongodb.client.model.DeleteOptions; -import com.mongodb.client.model.FindOneAndDeleteOptions; -import com.mongodb.client.model.FindOneAndReplaceOptions; -import com.mongodb.client.model.FindOneAndUpdateOptions; -import com.mongodb.client.model.ReplaceOptions; -import com.mongodb.client.model.UpdateOptions; -import com.mongodb.client.result.DeleteResult; -import com.mongodb.client.result.InsertManyResult; -import com.mongodb.client.result.InsertOneResult; -import com.mongodb.client.result.UpdateResult; -import com.mongodb.reactivestreams.client.AggregatePublisher; -import com.mongodb.reactivestreams.client.DistinctPublisher; -import com.mongodb.reactivestreams.client.FindPublisher; -import com.mongodb.reactivestreams.client.MapReducePublisher; -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * Unit tests for {@link ReactiveMongoTemplate}. - * - * @author Mark Paluch - * @author Christoph Strobl - * @author Roman Puchkovskiy - * @author Mathieu Ouellet - * @author Yadhukrishna S Pai - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -public class ReactiveMongoTemplateUnitTests { - - private ReactiveMongoTemplate template; - - @Mock SimpleReactiveMongoDatabaseFactory factory; - @Mock MongoClient mongoClient; - @Mock MongoDatabase db; - @Mock MongoCollection collection; - @Mock FindPublisher findPublisher; - @Mock AggregatePublisher aggregatePublisher; - @Mock Publisher runCommandPublisher; - @Mock Publisher updateResultPublisher; - @Mock Publisher findAndUpdatePublisher; - @Mock Publisher successPublisher; - @Mock DistinctPublisher distinctPublisher; - @Mock Publisher deletePublisher; - @Mock MapReducePublisher mapReducePublisher; - - private MongoExceptionTranslator exceptionTranslator = new MongoExceptionTranslator(); - private MappingMongoConverter converter; - private MongoMappingContext mappingContext; - - @BeforeEach - void beforeEach() { - - when(factory.getExceptionTranslator()).thenReturn(exceptionTranslator); - when(factory.getCodecRegistry()).thenReturn(MongoClientSettings.getDefaultCodecRegistry()); - when(factory.getMongoDatabase()).thenReturn(Mono.just(db)); - when(db.getCollection(any())).thenReturn(collection); - when(db.getCollection(any(), any())).thenReturn(collection); - when(db.runCommand(any(), any(Class.class))).thenReturn(runCommandPublisher); - when(db.createCollection(any(), any(CreateCollectionOptions.class))).thenReturn(runCommandPublisher); - when(collection.withReadPreference(any())).thenReturn(collection); - when(collection.find(any(Class.class))).thenReturn(findPublisher); - when(collection.find(any(Document.class), any(Class.class))).thenReturn(findPublisher); - when(collection.aggregate(anyList())).thenReturn(aggregatePublisher); - when(collection.aggregate(anyList(), any(Class.class))).thenReturn(aggregatePublisher); - when(collection.countDocuments(any(), any(CountOptions.class))).thenReturn(Mono.just(0L)); - when(collection.estimatedDocumentCount(any())).thenReturn(Mono.just(0L)); - when(collection.updateOne(any(), any(Bson.class), any(UpdateOptions.class))).thenReturn(updateResultPublisher); - when(collection.updateMany(any(Bson.class), any(Bson.class), any())).thenReturn(updateResultPublisher); - when(collection.updateOne(any(), anyList(), any())).thenReturn(updateResultPublisher); - when(collection.updateMany(any(), anyList(), any())).thenReturn(updateResultPublisher); - when(collection.findOneAndUpdate(any(), any(Bson.class), any(FindOneAndUpdateOptions.class))) - .thenReturn(findAndUpdatePublisher); - when(collection.findOneAndReplace(any(Bson.class), any(), any())).thenReturn(findPublisher); - when(collection.findOneAndDelete(any(), any(FindOneAndDeleteOptions.class))).thenReturn(findPublisher); - when(collection.distinct(anyString(), any(Document.class), any())).thenReturn(distinctPublisher); - when(collection.deleteMany(any(Bson.class), any())).thenReturn(deletePublisher); - when(collection.findOneAndUpdate(any(), any(Bson.class), any(FindOneAndUpdateOptions.class))) - .thenReturn(findAndUpdatePublisher); - when(collection.mapReduce(anyString(), anyString(), any())).thenReturn(mapReducePublisher); - when(collection.replaceOne(any(Bson.class), any(), any(ReplaceOptions.class))).thenReturn(updateResultPublisher); - when(collection.insertOne(any(Bson.class))).thenReturn(successPublisher); - when(collection.insertMany(anyList())).thenReturn(successPublisher); - when(findPublisher.projection(any())).thenReturn(findPublisher); - when(findPublisher.limit(anyInt())).thenReturn(findPublisher); - when(findPublisher.collation(any())).thenReturn(findPublisher); - when(findPublisher.first()).thenReturn(findPublisher); - when(findPublisher.allowDiskUse(anyBoolean())).thenReturn(findPublisher); - when(aggregatePublisher.allowDiskUse(anyBoolean())).thenReturn(aggregatePublisher); - when(aggregatePublisher.collation(any())).thenReturn(aggregatePublisher); - when(aggregatePublisher.maxTime(anyLong(), any())).thenReturn(aggregatePublisher); - when(aggregatePublisher.first()).thenReturn(findPublisher); - - this.mappingContext = new MongoMappingContext(); - this.converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); - this.template = new ReactiveMongoTemplate(factory, converter); - } - - @Test // DATAMONGO-1444 - void rejectsNullDatabaseName() { - assertThatIllegalArgumentException().isThrownBy(() -> new ReactiveMongoTemplate(mongoClient, null)); - } - - @Test // DATAMONGO-1444 - void rejectsNullMongo() { - assertThatIllegalArgumentException().isThrownBy(() -> new ReactiveMongoTemplate(null, "database")); - } - - @Test // DATAMONGO-1444 - void defaultsConverterToMappingMongoConverter() throws Exception { - ReactiveMongoTemplate template = new ReactiveMongoTemplate(mongoClient, "database"); - assertThat(ReflectionTestUtils.getField(template, "mongoConverter") instanceof MappingMongoConverter).isTrue(); - } - - @Test // DATAMONGO-1912 - void autogeneratesIdForMap() { - - ReactiveMongoTemplate template = spy(this.template); - doReturn(Mono.just(new ObjectId())).when(template).saveDocument(any(String.class), any(Document.class), - any(Class.class)); - - Map entity = new LinkedHashMap<>(); - template.save(entity, "foo").as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(entity).containsKey("_id"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1311 - void executeQueryShouldUseBatchSizeWhenPresent() { - - when(findPublisher.batchSize(anyInt())).thenReturn(findPublisher); - - Query query = new Query().cursorBatchSize(1234); - template.find(query, Person.class).subscribe(); - - verify(findPublisher).batchSize(1234); - } - - @Test // DATAMONGO-2659 - void executeQueryShouldUseAllowDiskSizeWhenPresent() { - - when(findPublisher.batchSize(anyInt())).thenReturn(findPublisher); - - Query query = new Query().allowDiskUse(true); - template.find(query, Person.class).subscribe(); - - verify(findPublisher).allowDiskUse(true); - } - - @Test // DATAMONGO-1518 - void findShouldUseCollationWhenPresent() { - - template.find(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class).subscribe(); - - verify(findPublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - // - @Test // DATAMONGO-1518 - void findOneShouldUseCollationWhenPresent() { - - template.findOne(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class).subscribe(); - - verify(findPublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1518 - void existsShouldUseCollationWhenPresent() { - - template.exists(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class).subscribe(); - - verify(findPublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1518 - void findAndModfiyShoudUseCollationWhenPresent() { - - when(collection.findOneAndUpdate(any(Bson.class), any(Bson.class), any())).thenReturn(Mono.empty()); - - template.findAndModify(new BasicQuery("{}").collation(Collation.of("fr")), new Update(), AutogenerateableId.class) - .subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndUpdateOptions.class); - verify(collection).findOneAndUpdate(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518 - void findAndRemoveShouldUseCollationWhenPresent() { - - when(collection.findOneAndDelete(any(Bson.class), any())).thenReturn(Mono.empty()); - - template.findAndRemove(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndDeleteOptions.class); - verify(collection).findOneAndDelete(any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518 - void findAndRemoveManyShouldUseCollationWhenPresent() { - - when(collection.deleteMany(any(Bson.class), any())).thenReturn(Mono.empty()); - - template.doRemove("collection-1", new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class) - .subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(DeleteOptions.class); - verify(collection).deleteMany(any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518 - void updateOneShouldUseCollationWhenPresent() { - - when(collection.updateOne(any(Bson.class), any(Bson.class), any())).thenReturn(Mono.empty()); - - template.updateFirst(new BasicQuery("{}").collation(Collation.of("fr")), new Update().set("foo", "bar"), - AutogenerateableId.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateOne(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518 - void updateManyShouldUseCollationWhenPresent() { - - when(collection.updateMany(any(Bson.class), any(Bson.class), any())).thenReturn(Mono.empty()); - - template.updateMulti(new BasicQuery("{}").collation(Collation.of("fr")), new Update().set("foo", "bar"), - AutogenerateableId.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateMany(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - - } - - @Test // DATAMONGO-1518 - void replaceOneShouldUseCollationWhenPresent() { - - when(collection.replaceOne(any(Bson.class), any(), any(ReplaceOptions.class))).thenReturn(Mono.empty()); - - template.updateFirst(new BasicQuery("{}").collation(Collation.of("fr")), new Update(), AutogenerateableId.class) - .subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(ReplaceOptions.class); - verify(collection).replaceOne(any(Bson.class), any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1518, DATAMONGO-2257 - void mapReduceShouldUseCollationWhenPresent() { - - template.mapReduce(new BasicQuery("{}"), AutogenerateableId.class, AutogenerateableId.class, "", "", - MapReduceOptions.options().collation(Collation.of("fr"))).subscribe(); - - verify(mapReducePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1518, DATAMONGO-2264 - void geoNearShouldUseCollationWhenPresent() { - - NearQuery query = NearQuery.near(0D, 0D).query(new BasicQuery("{}").collation(Collation.of("fr"))); - template.geoNear(query, AutogenerateableId.class).subscribe(); - - verify(aggregatePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1719 - void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, PersonProjection.class, - FindPublisherPreparer.NO_OP_PREPARER).subscribe(); - - verify(findPublisher).projection(eq(new Document("firstname", 1))); - } - - @Test // DATAMONGO-1719 - void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() { - - template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, PersonProjection.class, - FindPublisherPreparer.NO_OP_PREPARER).subscribe(); - - verify(findPublisher).projection(eq(new Document("bar", 1))); - } - - @Test // DATAMONGO-1719 - void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, PersonSpELProjection.class, - FindPublisherPreparer.NO_OP_PREPARER).subscribe(); - - verify(findPublisher, never()).projection(any()); - } - - @Test // DATAMONGO-1719, DATAMONGO-2041 - void appliesFieldsToDtoProjection() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, Jedi.class, - FindPublisherPreparer.NO_OP_PREPARER).subscribe(); - - verify(findPublisher).projection(eq(new Document("firstname", 1))); - } - - @Test // DATAMONGO-1719 - void doesNotApplyFieldsToDtoProjectionWhenQueryDefinesFields() { - - template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, Jedi.class, - FindPublisherPreparer.NO_OP_PREPARER).subscribe(); - - verify(findPublisher).projection(eq(new Document("bar", 1))); - } - - @Test // DATAMONGO-1719 - void doesNotApplyFieldsWhenTargetIsNotAProjection() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, Person.class, - FindPublisherPreparer.NO_OP_PREPARER).subscribe(); - - verify(findPublisher, never()).projection(any()); - } - - @Test // DATAMONGO-1719 - void doesNotApplyFieldsWhenTargetExtendsDomainType() { - - template.doFind("star-wars", new Document(), new Document(), Person.class, PersonExtended.class, - FindPublisherPreparer.NO_OP_PREPARER).subscribe(); - - verify(findPublisher, never()).projection(any()); - } - - @Test // DATAMONGO-1783 - void countShouldUseSkipFromQuery() { - - template.count(new Query().skip(10), Person.class, "star-wars").subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getSkip()).isEqualTo(10); - } - - @Test // DATAMONGO-1783 - void countShouldUseLimitFromQuery() { - - template.count(new Query().limit(100), Person.class, "star-wars").subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getLimit()).isEqualTo(100); - } - - @Test // DATAMONGO-2360 - void countShouldApplyQueryHintIfPresent() { - - Document queryHint = new Document("age", 1); - template.count(new Query().withHint(queryHint), Person.class, "star-wars").subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getHint()).isEqualTo(queryHint); - } - - @Test // DATAMONGO-2365 - void countShouldApplyQueryHintAsIndexNameIfPresent() { - - template.count(new Query().withHint("idx-1"), Person.class, "star-wars").subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(CountOptions.class); - verify(collection).countDocuments(any(), options.capture()); - - assertThat(options.getValue().getHintString()).isEqualTo("idx-1"); - } - - @Test // DATAMONGO-2215 - void updateShouldApplyArrayFilters() { - - template.updateFirst(new BasicQuery("{}"), - new Update().set("grades.$[element]", 100).filterArray(Criteria.where("element").gte(100)), - EntityWithListOfSimple.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateOne(any(), any(Bson.class), options.capture()); - - Assertions.assertThat((List) options.getValue().getArrayFilters()) - .contains(new org.bson.Document("element", new Document("$gte", 100))); - } - - @Test // DATAMONGO-2215 - void findAndModifyShouldApplyArrayFilters() { - - template.findAndModify(new BasicQuery("{}"), - new Update().set("grades.$[element]", 100).filterArray(Criteria.where("element").gte(100)), - EntityWithListOfSimple.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndUpdateOptions.class); - verify(collection).findOneAndUpdate(any(), any(Bson.class), options.capture()); - - Assertions.assertThat((List) options.getValue().getArrayFilters()) - .contains(new org.bson.Document("element", new Document("$gte", 100))); - } - - @Test // DATAMONGO-1854 - void findShouldNotUseCollationWhenNoDefaultPresent() { - - template.find(new BasicQuery("{'foo' : 'bar'}"), Jedi.class).subscribe(); - - verify(findPublisher, never()).collation(any()); - } - - @Test // DATAMONGO-1854 - void findShouldUseDefaultCollationWhenPresent() { - - template.find(new BasicQuery("{'foo' : 'bar'}"), Sith.class).subscribe(); - - verify(findPublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void findOneShouldUseDefaultCollationWhenPresent() { - - template.findOne(new BasicQuery("{'foo' : 'bar'}"), Sith.class).subscribe(); - - verify(findPublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void existsShouldUseDefaultCollationWhenPresent() { - - template.exists(new BasicQuery("{}"), Sith.class).subscribe(); - - verify(findPublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void findAndModfiyShoudUseDefaultCollationWhenPresent() { - - template.findAndModify(new BasicQuery("{}"), new Update(), Sith.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndUpdateOptions.class); - verify(collection).findOneAndUpdate(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void findAndRemoveShouldUseDefaultCollationWhenPresent() { - - template.findAndRemove(new BasicQuery("{}"), Sith.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndDeleteOptions.class); - verify(collection).findOneAndDelete(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void createCollectionShouldNotCollationIfNotPresent() { - - template.createCollection(AutogenerateableId.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(CreateCollectionOptions.class); - verify(db).createCollection(any(), options.capture()); - - Assertions.assertThat(options.getValue().getCollation()).isNull(); - } - - @Test // DATAMONGO-1854 - void createCollectionShouldApplyDefaultCollation() { - - template.createCollection(Sith.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(CreateCollectionOptions.class); - verify(db).createCollection(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void createCollectionShouldFavorExplicitOptionsOverDefaultCollation() { - - template.createCollection(Sith.class, CollectionOptions.just(Collation.of("en_US"))).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(CreateCollectionOptions.class); - verify(db).createCollection(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("en_US").build()); - } - - @Test // DATAMONGO-1854 - void createCollectionShouldUseDefaultCollationIfCollectionOptionsAreNull() { - - template.createCollection(Sith.class, null).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(CreateCollectionOptions.class); - verify(db).createCollection(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void aggreateShouldUseDefaultCollationIfPresent() { - - template.aggregate(newAggregation(Sith.class, project("id")), AutogenerateableId.class, Document.class).subscribe(); - - verify(aggregatePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void aggreateShouldUseCollationFromOptionsEvenIfDefaultCollationIsPresent() { - - template - .aggregate( - newAggregation(Sith.class, project("id")) - .withOptions(newAggregationOptions().collation(Collation.of("fr")).build()), - AutogenerateableId.class, Document.class) - .subscribe(); - - verify(aggregatePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-2153 - void aggregateShouldHonorOptionsComment() { - - AggregationOptions options = AggregationOptions.builder().comment("expensive").build(); - - template.aggregate(newAggregation(Sith.class, project("id")).withOptions(options), AutogenerateableId.class, - Document.class).subscribe(); - - verify(aggregatePublisher).comment("expensive"); - } - - @Test // DATAMONGO-1836 - void aggregateShouldHonorOptionsHint() { - - Document hint = new Document("dummyHint", 1); - AggregationOptions options = AggregationOptions.builder().hint(hint).build(); - - template.aggregate(newAggregation(Sith.class, project("id")).withOptions(options), AutogenerateableId.class, - Document.class).subscribe(); - - verify(aggregatePublisher).hint(hint); - } - - @Test // DATAMONGO-2390 - void aggregateShouldNoApplyZeroOrNegativeMaxTime() { - - template - .aggregate(newAggregation(MongoTemplateUnitTests.Sith.class, project("id")).withOptions( - newAggregationOptions().maxTime(Duration.ZERO).build()), AutogenerateableId.class, Document.class) - .subscribe(); - template - .aggregate( - newAggregation(MongoTemplateUnitTests.Sith.class, project("id")) - .withOptions(newAggregationOptions().maxTime(Duration.ofSeconds(-1)).build()), - AutogenerateableId.class, Document.class) - .subscribe(); - - verify(aggregatePublisher, never()).maxTime(anyLong(), any()); - } - - @Test // DATAMONGO-2390 - void aggregateShouldApplyMaxTimeIfSet() { - - template - .aggregate( - newAggregation(MongoTemplateUnitTests.Sith.class, project("id")) - .withOptions(newAggregationOptions().maxTime(Duration.ofSeconds(10)).build()), - AutogenerateableId.class, Document.class) - .subscribe(); - - verify(aggregatePublisher).maxTime(eq(10000L), eq(TimeUnit.MILLISECONDS)); - } - - @Test // DATAMONGO-1854 - void findAndReplaceShouldUseCollationWhenPresent() { - - template.findAndReplace(new BasicQuery("{}").collation(Collation.of("fr")), new Jedi()).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndReplaceOptions.class); - verify(collection).findOneAndReplace(any(Bson.class), any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1854 - void findAndReplaceShouldUseDefaultCollationWhenPresent() { - - template.findAndReplace(new BasicQuery("{}"), new Sith()).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndReplaceOptions.class); - verify(collection).findOneAndReplace(any(Bson.class), any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("de_AT"); - } - - @Test // DATAMONGO-1854 - void findAndReplaceShouldUseCollationEvenIfDefaultCollationIsPresent() { - - template.findAndReplace(new BasicQuery("{}").collation(Collation.of("fr")), new MongoTemplateUnitTests.Sith()) - .subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(FindOneAndReplaceOptions.class); - verify(collection).findOneAndReplace(any(Bson.class), any(), options.capture()); - - assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); - } - - @Test // DATAMONGO-1854 - void findDistinctShouldUseDefaultCollationWhenPresent() { - - template.findDistinct(new BasicQuery("{}"), "name", Sith.class, String.class).subscribe(); - - verify(distinctPublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build())); - } - - @Test // DATAMONGO-1854 - void findDistinctPreferCollationFromQueryOverDefaultCollation() { - - template.findDistinct(new BasicQuery("{}").collation(Collation.of("fr")), "name", Sith.class, String.class) - .subscribe(); - - verify(distinctPublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build())); - } - - @Test // DATAMONGO-1854 - void updateFirstShouldUseDefaultCollationWhenPresent() { - - template.updateFirst(new BasicQuery("{}"), Update.update("foo", "bar"), Sith.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateOne(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void updateFirstShouldPreferExplicitCollationOverDefaultCollation() { - - template.updateFirst(new BasicQuery("{}").collation(Collation.of("fr")), Update.update("foo", "bar"), Sith.class) - .subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateOne(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); - } - - @Test // DATAMONGO-1854 - void updateMultiShouldUseDefaultCollationWhenPresent() { - - template.updateMulti(new BasicQuery("{}"), Update.update("foo", "bar"), Sith.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateMany(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void updateMultiShouldPreferExplicitCollationOverDefaultCollation() { - - template.updateMulti(new BasicQuery("{}").collation(Collation.of("fr")), Update.update("foo", "bar"), Sith.class) - .subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); - verify(collection).updateMany(any(), any(Bson.class), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); - } - - @Test // DATAMONGO-1854 - void removeShouldUseDefaultCollationWhenPresent() { - - template.remove(new BasicQuery("{}"), Sith.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(DeleteOptions.class); - verify(collection).deleteMany(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); - } - - @Test // DATAMONGO-1854 - void removeShouldPreferExplicitCollationOverDefaultCollation() { - - template.remove(new BasicQuery("{}").collation(Collation.of("fr")), Sith.class).subscribe(); - - ArgumentCaptor options = ArgumentCaptor.forClass(DeleteOptions.class); - verify(collection).deleteMany(any(), options.capture()); - - assertThat(options.getValue().getCollation()) - .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); - } - - @Test // DATAMONGO-2261 - void saveShouldInvokeCallbacks() { - - ValueCapturingBeforeConvertCallback beforeConvertCallback = spy(new ValueCapturingBeforeConvertCallback()); - ValueCapturingBeforeSaveCallback beforeSaveCallback = spy(new ValueCapturingBeforeSaveCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvertCallback, beforeSaveCallback)); - - Person entity = new Person(); - entity.id = "init"; - entity.firstname = "luke"; - - template.save(entity).subscribe(); - - verify(beforeConvertCallback).onBeforeConvert(eq(entity), anyString()); - verify(beforeSaveCallback).onBeforeSave(eq(entity), any(), anyString()); - } - - @Test // DATAMONGO-2261 - void insertShouldInvokeCallbacks() { - - ValueCapturingBeforeConvertCallback beforeConvertCallback = spy(new ValueCapturingBeforeConvertCallback()); - ValueCapturingBeforeSaveCallback beforeSaveCallback = spy(new ValueCapturingBeforeSaveCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvertCallback, beforeSaveCallback)); - - Person entity = new Person(); - entity.id = "init"; - entity.firstname = "luke"; - - template.insert(entity).subscribe(); - - verify(beforeConvertCallback).onBeforeConvert(eq(entity), anyString()); - verify(beforeSaveCallback).onBeforeSave(eq(entity), any(), anyString()); - } - - @Test // DATAMONGO-2261 - void insertAllShouldInvokeCallbacks() { - - ValueCapturingBeforeConvertCallback beforeConvertCallback = spy(new ValueCapturingBeforeConvertCallback()); - ValueCapturingBeforeSaveCallback beforeSaveCallback = spy(new ValueCapturingBeforeSaveCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvertCallback, beforeSaveCallback)); - - Person entity1 = new Person(); - entity1.id = "1"; - entity1.firstname = "luke"; - - Person entity2 = new Person(); - entity1.id = "2"; - entity1.firstname = "luke"; - - template.insertAll(Arrays.asList(entity1, entity2)).subscribe(); - - verify(beforeConvertCallback, times(2)).onBeforeConvert(any(), anyString()); - verify(beforeSaveCallback, times(2)).onBeforeSave(any(), any(), anyString()); - } - - @Test // DATAMONGO-2261 - void findAndReplaceShouldInvokeCallbacks() { - - ValueCapturingBeforeConvertCallback beforeConvertCallback = spy(new ValueCapturingBeforeConvertCallback()); - ValueCapturingBeforeSaveCallback beforeSaveCallback = spy(new ValueCapturingBeforeSaveCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvertCallback, beforeSaveCallback)); - - Person entity = new Person(); - entity.id = "init"; - entity.firstname = "luke"; - - template.findAndReplace(new Query(), entity).subscribe(); - - verify(beforeConvertCallback).onBeforeConvert(eq(entity), anyString()); - verify(beforeSaveCallback).onBeforeSave(eq(entity), any(), anyString()); - } - - @Test // DATAMONGO-2261 - void entityCallbacksAreNotSetByDefault() { - Assertions.assertThat(ReflectionTestUtils.getField(template, "entityCallbacks")).isNull(); - } - - @Test // DATAMONGO-2261 - void entityCallbacksShouldBeInitiatedOnSettingApplicationContext() { - - ApplicationContext ctx = new StaticApplicationContext(); - template.setApplicationContext(ctx); - - Assertions.assertThat(ReflectionTestUtils.getField(template, "entityCallbacks")).isNotNull(); - } - - @Test // DATAMONGO-2261 - void setterForEntityCallbackOverridesContextInitializedOnes() { - - ApplicationContext ctx = new StaticApplicationContext(); - template.setApplicationContext(ctx); - - ReactiveEntityCallbacks callbacks = ReactiveEntityCallbacks.create(); - template.setEntityCallbacks(callbacks); - - Assertions.assertThat(ReflectionTestUtils.getField(template, "entityCallbacks")).isSameAs(callbacks); - } - - @Test // DATAMONGO-2261 - void setterForApplicationContextShouldNotOverrideAlreadySetEntityCallbacks() { - - ReactiveEntityCallbacks callbacks = ReactiveEntityCallbacks.create(); - ApplicationContext ctx = new StaticApplicationContext(); - - template.setEntityCallbacks(callbacks); - template.setApplicationContext(ctx); - - Assertions.assertThat(ReflectionTestUtils.getField(template, "entityCallbacks")).isSameAs(callbacks); - } - - @Test // DATAMONGO-2344, DATAMONGO-2572 - void allowSecondaryReadsQueryOptionShouldApplyPrimaryPreferredReadPreferenceForFind() { - - template.find(new Query().allowSecondaryReads(), AutogenerateableId.class).subscribe(); - - verify(collection).withReadPreference(eq(ReadPreference.primaryPreferred())); - } - - @Test // DATAMONGO-2344, DATAMONGO-2572 - void allowSecondaryReadsQueryOptionShouldApplyPrimaryPreferredReadPreferenceForFindOne() { - - template.findOne(new Query().allowSecondaryReads(), AutogenerateableId.class).subscribe(); - - verify(collection).withReadPreference(eq(ReadPreference.primaryPreferred())); - } - - @Test // DATAMONGO-2344, DATAMONGO-2572 - void allowSecondaryReadsQueryOptionShouldApplyPrimaryPreferredReadPreferenceForFindDistinct() { - - template.findDistinct(new Query().allowSecondaryReads(), "name", AutogenerateableId.class, String.class).subscribe(); - - verify(collection).withReadPreference(eq(ReadPreference.primaryPreferred())); - } - - @Test // DATAMONGO-2331 - void updateShouldAllowAggregationExpressions() { - - AggregationUpdate update = AggregationUpdate.update().set("total") - .toValue(ArithmeticOperators.valueOf("val1").sum().and("val2")); - - template.updateFirst(new BasicQuery("{}"), update, Wrapper.class).subscribe(); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).isEqualTo( - Collections.singletonList(Document.parse("{ $set : { total : { $sum : [ \"$val1\",\"$val2\" ] } } }"))); - } - - @Test // DATAMONGO-2331 - void updateShouldAllowMultipleAggregationExpressions() { - - AggregationUpdate update = AggregationUpdate.update() // - .set("average").toValue(ArithmeticOperators.valueOf("tests").avg()) // - .set("grade").toValue(ConditionalOperators.switchCases( // - CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(90)).then("A"), // - CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(80)).then("B"), // - CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(70)).then("C"), // - CaseOperator.when(Gte.valueOf("average").greaterThanEqualToValue(60)).then("D") // - ) // - .defaultTo("F"));// - - template.updateFirst(new BasicQuery("{}"), update, Wrapper.class).subscribe(); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).containsExactly(Document.parse("{ $set: { average : { $avg: \"$tests\" } } }"), - Document.parse("{ $set: { grade: { $switch: {\n" + " branches: [\n" - + " { case: { $gte: [ \"$average\", 90 ] }, then: \"A\" },\n" - + " { case: { $gte: [ \"$average\", 80 ] }, then: \"B\" },\n" - + " { case: { $gte: [ \"$average\", 70 ] }, then: \"C\" },\n" - + " { case: { $gte: [ \"$average\", 60 ] }, then: \"D\" }\n" - + " ],\n" + " default: \"F\"\n" + " } } } }")); - } - - @Test // DATAMONGO-2331 - void updateShouldMapAggregationExpressionToDomainType() { - - AggregationUpdate update = AggregationUpdate.update().set("name") - .toValue(ArithmeticOperators.valueOf("val1").sum().and("val2")); - - template.updateFirst(new BasicQuery("{}"), update, Jedi.class).subscribe(); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).isEqualTo( - Collections.singletonList(Document.parse("{ $set : { firstname : { $sum:[ \"$val1\",\"$val2\" ] } } }"))); - } - - @Test // DATAMONGO-2331 - void updateShouldPassOnUnsetCorrectly() { - - SetOperation setOperation = SetOperation.builder().set("status").toValue("Modified").and().set("comments") - .toValue(Fields.fields("misc1").and("misc2").asList()); - AggregationUpdate update = AggregationUpdate.update(); - update.set(setOperation); - update.unset("misc1", "misc2"); - - template.updateFirst(new BasicQuery("{}"), update, Wrapper.class).subscribe(); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).isEqualTo( - Arrays.asList(Document.parse("{ $set: { status: \"Modified\", comments: [ \"$misc1\", \"$misc2\" ] } }"), - Document.parse("{ $unset: [ \"misc1\", \"misc2\" ] }"))); - } - - @Test // DATAMONGO-2331 - void updateShouldMapAggregationUnsetToDomainType() { - - AggregationUpdate update = AggregationUpdate.update(); - update.unset("name"); - - template.updateFirst(new BasicQuery("{}"), update, Jedi.class).subscribe(); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - - verify(collection, times(1)).updateOne(any(org.bson.Document.class), captor.capture(), any(UpdateOptions.class)); - - assertThat(captor.getValue()).isEqualTo(Collections.singletonList(Document.parse("{ $unset : \"firstname\" }"))); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendNonDefaultShardKeyIfNotPresentInFilter() { - - when(findPublisher.first()).thenReturn(Mono.empty()); - - template.save(new ShardedEntityWithNonDefaultShardKey("id-1", "AT", 4230)).subscribe(); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - verify(collection).replaceOne(filter.capture(), any(), any()); - - assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("country", "AT").append("userid", 4230)); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendNonDefaultShardKeyFromGivenDocumentIfShardKeyIsImmutable() { - - template.save(new ShardedEntityWithNonDefaultImmutableShardKey("id-1", "AT", 4230)).subscribe(); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor replacement = ArgumentCaptor.forClass(Document.class); - - verify(collection).replaceOne(filter.capture(), replacement.capture(), any()); - - assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("country", "AT").append("userid", 4230)); - assertThat(replacement.getValue()).containsEntry("country", "AT").containsEntry("userid", 4230); - - verifyNoInteractions(findPublisher); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendNonDefaultShardKeyToVersionedEntityIfNotPresentInFilter() { - - when(collection.replaceOne(any(Bson.class), any(Document.class), any(ReplaceOptions.class))) - .thenReturn(Mono.just(UpdateResult.acknowledged(1, 1L, null))); - when(findPublisher.first()).thenReturn(Mono.empty()); - - template.save(new ShardedVersionedEntityWithNonDefaultShardKey("id-1", 1L, "AT", 4230)).subscribe(); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - verify(collection).replaceOne(filter.capture(), any(), any()); - - assertThat(filter.getValue()) - .isEqualTo(new Document("_id", "id-1").append("version", 1L).append("country", "AT").append("userid", 4230)); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendNonDefaultShardKeyFromExistingDocumentIfNotPresentInFilter() { - - when(findPublisher.first()) - .thenReturn(Mono.just(new Document("_id", "id-1").append("country", "US").append("userid", 4230))); - - template.save(new ShardedEntityWithNonDefaultShardKey("id-1", "AT", 4230)).subscribe(); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - ArgumentCaptor replacement = ArgumentCaptor.forClass(Document.class); - - verify(collection).replaceOne(filter.capture(), replacement.capture(), any()); - - assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("country", "US").append("userid", 4230)); - assertThat(replacement.getValue()).containsEntry("country", "AT").containsEntry("userid", 4230); - } - - @Test // DATAMONGO-2341 - void saveShouldAppendDefaultShardKeyIfNotPresentInFilter() { - - template.save(new ShardedEntityWithDefaultShardKey("id-1", "AT", 4230)).subscribe(); - - ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class); - verify(collection).replaceOne(filter.capture(), any(), any()); - - assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1")); - } - - @Test // DATAMONGO-2341 - void saveShouldProjectOnShardKeyWhenLoadingExistingDocument() { - - when(findPublisher.first()) - .thenReturn(Mono.just(new Document("_id", "id-1").append("country", "US").append("userid", 4230))); - - template.save(new ShardedEntityWithNonDefaultShardKey("id-1", "AT", 4230)).subscribe(); - - verify(findPublisher).projection(new Document("country", 1).append("userid", 1)); - } - - @Test // DATAMONGO-2341 - void saveVersionedShouldProjectOnShardKeyWhenLoadingExistingDocument() { - - when(collection.replaceOne(any(Bson.class), any(Document.class), any(ReplaceOptions.class))) - .thenReturn(Mono.just(UpdateResult.acknowledged(1, 1L, null))); - when(findPublisher.first()).thenReturn(Mono.empty()); - - template.save(new ShardedVersionedEntityWithNonDefaultShardKey("id-1", 1L, "AT", 4230)).subscribe(); - - verify(findPublisher).projection(new Document("country", 1).append("userid", 1)); - } - - @Test // DATAMONGO-2479 - void findShouldInvokeAfterConvertCallbacks() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.find(Document.class)).thenReturn(findPublisher); - stubFindSubscribe(document); - - List results = template.find(new Query(), Person.class).timeout(Duration.ofSeconds(1)).toStream() - .collect(Collectors.toList()); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - assertThat(results.get(0).id).isEqualTo("after-convert"); - } - - @Test // DATAMONGO-2479 - void findByIdShouldInvokeAfterConvertCallbacks() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.find(any(Bson.class), eq(Document.class))).thenReturn(findPublisher); - stubFindSubscribe(document); - - Person result = template.findById("init", Person.class).block(Duration.ofSeconds(1)); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - assertThat(result.id).isEqualTo("after-convert"); - } - - @Test // DATAMONGO-2479 - void findOneShouldInvokeAfterConvertCallbacks() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.find(any(Bson.class), eq(Document.class))).thenReturn(findPublisher); - stubFindSubscribe(document); - - Person result = template.findOne(new Query(), Person.class).block(Duration.ofSeconds(1)); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - assertThat(result.id).isEqualTo("after-convert"); - } - - @Test // DATAMONGO-2479 - void findAllShouldInvokeAfterConvertCallbacks() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.find(Document.class)).thenReturn(findPublisher); - stubFindSubscribe(document); - - List results = template.findAll(Person.class).timeout(Duration.ofSeconds(1)).toStream() - .collect(Collectors.toList()); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - assertThat(results.get(0).id).isEqualTo("after-convert"); - } - - @Test // DATAMONGO-2479 - void findAndModifyShouldInvokeAfterConvertCallbacks() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.findOneAndUpdate(any(Bson.class), any(Bson.class), any())).thenReturn(findPublisher); - stubFindSubscribe(document); - - Person result = template.findAndModify(new Query(), new Update(), Person.class).block(Duration.ofSeconds(1)); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - assertThat(result.id).isEqualTo("after-convert"); - } - - @Test // DATAMONGO-2479 - void findAndRemoveShouldInvokeAfterConvertCallbacks() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.findOneAndDelete(any(Bson.class), any())).thenReturn(findPublisher); - stubFindSubscribe(document); - - Person result = template.findAndRemove(new Query(), Person.class).block(Duration.ofSeconds(1)); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - assertThat(result.id).isEqualTo("after-convert"); - } - - @Test // DATAMONGO-2479 - void findAllAndRemoveShouldInvokeAfterConvertCallbacks() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback)); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.find(Document.class)).thenReturn(findPublisher); - stubFindSubscribe(document); - when(collection.deleteMany(any(Bson.class), any(DeleteOptions.class))) - .thenReturn(Mono.just(spy(DeleteResult.class))); - - List results = template.findAllAndRemove(new Query(), Person.class).timeout(Duration.ofSeconds(1)) - .toStream().collect(Collectors.toList()); - - verify(afterConvertCallback).onAfterConvert(eq(new Person("init", "luke")), eq(document), anyString()); - assertThat(results.get(0).id).isEqualTo("after-convert"); - } - - @Test // DATAMONGO-2479 - void findAndReplaceShouldInvokeAfterConvertCallbacks() { - - ValueCapturingAfterConvertCallback afterConvertCallback = spy(new ValueCapturingAfterConvertCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterConvertCallback)); - - when(collection.findOneAndReplace(any(Bson.class), any(Document.class), any())).thenReturn(findPublisher); - stubFindSubscribe(new Document("_id", "init").append("firstname", "luke")); - - Person entity = new Person(); - entity.id = "init"; - entity.firstname = "luke"; - - Person saved = template.findAndReplace(new Query(), entity).block(Duration.ofSeconds(1)); - - verify(afterConvertCallback).onAfterConvert(eq(entity), any(), anyString()); - assertThat(saved.id).isEqualTo("after-convert"); - } - - @Test // DATAMONGO-2479 - void saveShouldInvokeAfterSaveCallbacks() { - - ValueCapturingAfterSaveCallback afterSaveCallback = spy(new ValueCapturingAfterSaveCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback)); - - when(collection.replaceOne(any(Bson.class), any(Document.class), any(ReplaceOptions.class))) - .thenReturn(Mono.just(mock(UpdateResult.class))); - - Person entity = new Person("init", "luke"); - - Person saved = template.save(entity).block(Duration.ofSeconds(1)); - - verify(afterSaveCallback).onAfterSave(eq(entity), any(), anyString()); - assertThat(saved.id).isEqualTo("after-save"); - } - - @Test // DATAMONGO-2479 - void insertShouldInvokeAfterSaveCallbacks() { - - ValueCapturingAfterSaveCallback afterSaveCallback = spy(new ValueCapturingAfterSaveCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback)); - - when(collection.insertOne(any())).thenReturn(Mono.just(mock(InsertOneResult.class))); - - Person entity = new Person("init", "luke"); - - Person saved = template.insert(entity).block(Duration.ofSeconds(1)); - - verify(afterSaveCallback).onAfterSave(eq(entity), any(), anyString()); - assertThat(saved.id).isEqualTo("after-save"); - } - - @Test // DATAMONGO-2479 - void insertAllShouldInvokeAfterSaveCallbacks() { - - ValueCapturingAfterSaveCallback afterSaveCallback = spy(new ValueCapturingAfterSaveCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback)); - - Person entity1 = new Person(); - entity1.id = "1"; - entity1.firstname = "luke"; - - Person entity2 = new Person(); - entity1.id = "2"; - entity1.firstname = "luke"; - - when(collection.insertMany(anyList())).then(invocation -> { - List list = invocation.getArgument(0); - return Flux.fromIterable(list).map(i -> mock(InsertManyResult.class)); - }); - - List saved = template.insertAll(Arrays.asList(entity1, entity2)).timeout(Duration.ofSeconds(1)).toStream() - .collect(Collectors.toList()); - - verify(afterSaveCallback, times(2)).onAfterSave(any(), any(), anyString()); - assertThat(saved.get(0).id).isEqualTo("after-save"); - assertThat(saved.get(1).id).isEqualTo("after-save"); - } - - @Test // DATAMONGO-2479 - void findAndReplaceShouldInvokeAfterSaveCallbacks() { - - ValueCapturingAfterSaveCallback afterSaveCallback = spy(new ValueCapturingAfterSaveCallback()); - - template.setEntityCallbacks(ReactiveEntityCallbacks.create(afterSaveCallback)); - - when(collection.findOneAndReplace(any(Bson.class), any(Document.class), any())).thenReturn(findPublisher); - stubFindSubscribe(new Document("_id", "init").append("firstname", "luke")); - - Person entity = new Person("init", "luke"); - - Person saved = template.findAndReplace(new Query(), entity).block(Duration.ofSeconds(1)); - - verify(afterSaveCallback).onAfterSave(eq(entity), any(), anyString()); - assertThat(saved.id).isEqualTo("after-save"); - } - - @Test // DATAMONGO-2479 - void findAndReplaceShouldEmitAfterSaveEvent() { - - AbstractMongoEventListener eventListener = new AbstractMongoEventListener() { - - @Override - public void onAfterSave(AfterSaveEvent event) { - - assertThat(event.getSource().id).isEqualTo("init"); - event.getSource().id = "after-save-event"; - } - }; - - StaticApplicationContext ctx = new StaticApplicationContext(); - ctx.registerBean(ApplicationListener.class, () -> eventListener); - ctx.refresh(); - - template.setApplicationContext(ctx); - - Person entity = new Person("init", "luke"); - - Document document = new Document("_id", "init").append("firstname", "luke"); - when(collection.findOneAndReplace(any(Bson.class), any(Document.class), any())).thenReturn(Mono.just(document)); - - Person saved = template.findAndReplace(new Query(), entity).block(Duration.ofSeconds(1)); - - assertThat(saved.id).isEqualTo("after-save-event"); - } - - @Test // DATAMONGO-2556 - void esitmatedCountShouldBeDelegatedCorrectly() { - - template.estimatedCount(Person.class).subscribe(); - - verify(db).getCollection("star-wars", Document.class); - verify(collection).estimatedDocumentCount(any()); - } - - @Test // GH-2911 - void insertErrorsOnPublisher() { - - Publisher publisher = Mono.just("data"); - - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> template.insert(publisher)); - } - - private void stubFindSubscribe(Document document) { - - Publisher realPublisher = Flux.just(document); - - doAnswer(invocation -> { - Subscriber subscriber = invocation.getArgument(0); - realPublisher.subscribe(subscriber); - return null; - }).when(findPublisher).subscribe(any()); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = "star-wars") - @AllArgsConstructor - @NoArgsConstructor - static class Person { - - @Id String id; - String firstname; - } - - class Wrapper { - - AutogenerateableId foo; - } - - static class PersonExtended extends Person { - - String lastname; - } - - interface PersonProjection { - String getFirstname(); - } - - public interface PersonSpELProjection { - - @Value("#{target.firstname}") - String getName(); - } - - @Data - static class Jedi { - - @Field("firstname") String name; - } - - @org.springframework.data.mongodb.core.mapping.Document(collation = "de_AT") - static class Sith { - - @Field("firstname") String name; - } - - static class EntityWithListOfSimple { - List grades; - } - - static class ValueCapturingEntityCallback { - - private final List values = new ArrayList<>(1); - - protected void capture(T value) { - values.add(value); - } - - public List getValues() { - return values; - } - - @Nullable - public T getValue() { - return CollectionUtils.lastElement(values); - } - } - - static class ValueCapturingBeforeConvertCallback extends ValueCapturingEntityCallback - implements ReactiveBeforeConvertCallback { - - @Override - public Mono onBeforeConvert(Person entity, String collection) { - - capture(entity); - return Mono.just(entity); - } - } - - static class ValueCapturingBeforeSaveCallback extends ValueCapturingEntityCallback - implements ReactiveBeforeSaveCallback { - - @Override - public Mono onBeforeSave(Person entity, Document document, String collection) { - - capture(entity); - return Mono.just(entity); - } - } - - static class ValueCapturingAfterConvertCallback extends ValueCapturingEntityCallback - implements ReactiveAfterConvertCallback { - - @Override - public Mono onAfterConvert(Person entity, Document document, String collection) { - - capture(entity); - return Mono.just(new Person() { - { - id = "after-convert"; - firstname = entity.firstname; - } - }); - } - } - - static class ValueCapturingAfterSaveCallback extends ValueCapturingEntityCallback - implements ReactiveAfterSaveCallback { - - @Override - public Mono onAfterSave(Person entity, Document document, String collection) { - - capture(entity); - return Mono.just(new Person() { - { - id = "after-save"; - firstname = entity.firstname; - } - }); - } - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUpdateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUpdateTests.java deleted file mode 100644 index 61dc9b526f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUpdateTests.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2019-2021 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 static org.assertj.core.api.Assertions.*; - -import lombok.EqualsAndHashCode; -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.Version; -import org.springframework.data.mongodb.core.aggregation.AggregationUpdate; -import org.springframework.data.mongodb.core.aggregation.ArithmeticOperators; -import org.springframework.data.mongodb.core.aggregation.ReplaceWithOperation; -import org.springframework.data.mongodb.core.aggregation.SetOperation; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; - -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoCollection; - -/** - * @author Christoph Strobl - */ -@ExtendWith(MongoClientExtension.class) -@EnableIfMongoServerVersion(isGreaterThanEqual = "4.2") -public class ReactiveMongoTemplateUpdateTests { - - static final String DB_NAME = "reactive-update-test"; - - static @Client MongoClient client; - ReactiveMongoTemplate template; - - @BeforeEach - void beforeEach() { - - template = new ReactiveMongoTemplate(new SimpleReactiveMongoDatabaseFactory(client, DB_NAME)); - - MongoTestUtils.createOrReplaceCollection(DB_NAME, template.getCollectionName(Score.class), client).then() - .as(StepVerifier::create).verifyComplete(); - MongoTestUtils.createOrReplaceCollection(DB_NAME, template.getCollectionName(Versioned.class), client).then() - .as(StepVerifier::create).verifyComplete(); - MongoTestUtils.createOrReplaceCollection(DB_NAME, template.getCollectionName(Book.class), client).then() - .as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-2331 - public void aggregateUpdateWithSet() { - - Score score1 = new Score(1, "Maya", Arrays.asList(10, 5, 10), Arrays.asList(10, 8), 0); - Score score2 = new Score(2, "Ryan", Arrays.asList(5, 6, 5), Arrays.asList(8, 8), 8); - - template.insertAll(Arrays.asList(score1, score2)).then().as(StepVerifier::create).verifyComplete(); - - AggregationUpdate update = AggregationUpdate.update().set(SetOperation.builder() // - .set("totalHomework").toValueOf(ArithmeticOperators.valueOf("homework").sum()).and() // - .set("totalQuiz").toValueOf(ArithmeticOperators.valueOf("quiz").sum())) // - .set(SetOperation.builder() // - .set("totalScore") - .toValueOf(ArithmeticOperators.valueOf("totalHomework").add("totalQuiz").add("extraCredit"))); - - template.update(Score.class).apply(update).all().then().as(StepVerifier::create).verifyComplete(); - - Flux.from(collection(Score.class).find(new org.bson.Document())).collectList().as(StepVerifier::create) - .consumeNextWith(it -> { - - assertThat(it).containsExactlyInAnyOrder( // - org.bson.Document.parse( - "{\"_id\" : 1, \"student\" : \"Maya\", \"homework\" : [ 10, 5, 10 ], \"quiz\" : [ 10, 8 ], \"extraCredit\" : 0, \"totalHomework\" : 25, \"totalQuiz\" : 18, \"totalScore\" : 43, \"_class\" : \"org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Score\"}"), - org.bson.Document.parse( - "{ \"_id\" : 2, \"student\" : \"Ryan\", \"homework\" : [ 5, 6, 5 ], \"quiz\" : [ 8, 8 ], \"extraCredit\" : 8, \"totalHomework\" : 16, \"totalQuiz\" : 16, \"totalScore\" : 40, \"_class\" : \"org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Score\"}")); - }).verifyComplete(); - } - - @Test // DATAMONGO-2331 - public void versionedAggregateUpdateWithSet() { - - Versioned source = new Versioned("id-1", "value-0"); - template.insert(Versioned.class).one(source).then().as(StepVerifier::create).verifyComplete(); - - AggregationUpdate update = AggregationUpdate.update().set("value").toValue("changed"); - template.update(Versioned.class).matching(Query.query(Criteria.where("id").is(source.id))).apply(update).first() - .then().as(StepVerifier::create).verifyComplete(); - - Flux.from(collection(Versioned.class).find(new org.bson.Document("_id", source.id)).limit(1)).collectList() - .as(StepVerifier::create).consumeNextWith(it -> { - assertThat(it).containsExactly( - new org.bson.Document("_id", source.id).append("version", 1L).append("value", "changed").append("_class", - "org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Versioned")); - }).verifyComplete(); - } - - @Test // DATAMONGO-2331 - public void versionedAggregateUpdateTouchingVersionProperty() { - - Versioned source = new Versioned("id-1", "value-0"); - template.insert(Versioned.class).one(source).then().as(StepVerifier::create).verifyComplete(); - - AggregationUpdate update = AggregationUpdate.update() - .set(SetOperation.builder().set("value").toValue("changed").and().set("version").toValue(10L)); - - template.update(Versioned.class).matching(Query.query(Criteria.where("id").is(source.id))).apply(update).first() - .then().as(StepVerifier::create).verifyComplete(); - - Flux.from(collection(Versioned.class).find(new org.bson.Document("_id", source.id)).limit(1)).collectList() - .as(StepVerifier::create).consumeNextWith(it -> { - assertThat(it).containsExactly( - new org.bson.Document("_id", source.id).append("version", 10L).append("value", "changed").append("_class", - "org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Versioned")); - }).verifyComplete(); - } - - @Test // DATAMONGO-2331 - public void aggregateUpdateWithUnset() { - - Book antelopeAntics = new Book(); - antelopeAntics.id = 1; - antelopeAntics.title = "Antelope Antics"; - antelopeAntics.isbn = "0001122223334"; - antelopeAntics.author = new Author("Auntie", "An"); - antelopeAntics.stock = new ArrayList<>(); - antelopeAntics.stock.add(new Warehouse("A", 5)); - antelopeAntics.stock.add(new Warehouse("B", 15)); - - Book beesBabble = new Book(); - beesBabble.id = 2; - beesBabble.title = "Bees Babble"; - beesBabble.isbn = "999999999333"; - beesBabble.author = new Author("Bee", "Bumble"); - beesBabble.stock = new ArrayList<>(); - beesBabble.stock.add(new Warehouse("A", 2)); - beesBabble.stock.add(new Warehouse("B", 5)); - - template.insertAll(Arrays.asList(antelopeAntics, beesBabble)).then().as(StepVerifier::create).verifyComplete(); - - AggregationUpdate update = AggregationUpdate.update().unset("isbn", "stock"); - template.update(Book.class).apply(update).all().then().as(StepVerifier::create).verifyComplete(); - - all(Book.class).collectList().as(StepVerifier::create).consumeNextWith(it -> { - - assertThat(it).containsExactlyInAnyOrder( // - org.bson.Document.parse( - "{ \"_id\" : 1, \"title\" : \"Antelope Antics\", \"author\" : { \"last\" : \"An\", \"first\" : \"Auntie\" }, \"_class\" : \"org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Book\" }"), - org.bson.Document.parse( - "{ \"_id\" : 2, \"title\" : \"Bees Babble\", \"author\" : { \"last\" : \"Bumble\", \"first\" : \"Bee\" }, \"_class\" : \"org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Book\" }")); - }).verifyComplete(); - } - - @Test // DATAMONGO-2331 - public void aggregateUpdateWithReplaceWith() { - - Book one = new Book(); - one.id = 1; - one.author = new Author("John", "Backus"); - - Book two = new Book(); - two.id = 2; - two.author = new Author("Grace", "Hopper"); - - template.insertAll(Arrays.asList(one, two)).then().as(StepVerifier::create).verifyComplete(); - ; - - AggregationUpdate update = AggregationUpdate.update() - .replaceWith(ReplaceWithOperation.replaceWithValueOf("author")); - - template.update(Book.class).apply(update).all().then().as(StepVerifier::create).verifyComplete(); - - all(Book.class).collectList().as(StepVerifier::create).consumeNextWith(it -> { - - assertThat(it).containsExactlyInAnyOrder( - org.bson.Document.parse("{\"_id\" : 1, \"first\" : \"John\", \"last\" : \"Backus\"}"), - org.bson.Document.parse("{\"_id\" : 2, \"first\" : \"Grace\", \"last\" : \"Hopper\"}")); - }).verifyComplete(); - } - - @Test // DATAMONGO-2331 - public void aggregationUpdateUpsertsCorrectly() { - - AggregationUpdate update = AggregationUpdate.update().set("title").toValue("The Burning White"); - - template.update(Book.class).matching(Query.query(Criteria.where("id").is(1))).apply(update).upsert().then() - .as(StepVerifier::create).verifyComplete(); - - all(Book.class).collectList().as(StepVerifier::create).consumeNextWith(it -> { - assertThat(it).containsExactly(org.bson.Document.parse("{\"_id\" : 1, \"title\" : \"The Burning White\" }")); - }).verifyComplete(); - } - - @Test // DATAMONGO-2331 - public void aggregateUpdateFirstMatch() { - - Book one = new Book(); - one.id = 1; - one.title = "The Blood Mirror"; - - Book two = new Book(); - two.id = 2; - two.title = "The Broken Eye"; - - template.insertAll(Arrays.asList(one, two)).then().as(StepVerifier::create).verifyComplete(); - - template.update(Book.class).apply(AggregationUpdate.update().set("title").toValue("The Blinding Knife")).first() - .then().as(StepVerifier::create).verifyComplete(); - - all(Book.class).collectList().as(StepVerifier::create).consumeNextWith(it -> { - - assertThat(it).containsExactly(org.bson.Document.parse( - "{\"_id\" : 1, \"title\" : \"The Blinding Knife\", \"_class\" : \"org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Book\"}"), - org.bson.Document.parse( - "{\"_id\" : 2, \"title\" : \"The Broken Eye\", \"_class\" : \"org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Book\"}")); - }).verifyComplete(); - } - - @Test // DATAMONGO-2331 - public void findAndModifyAppliesAggregationUpdateCorrectly() { - - Book one = new Book(); - one.id = 1; - one.title = "The Blood Mirror"; - - Book two = new Book(); - two.id = 2; - two.title = "The Broken Eye"; - - template.insertAll(Arrays.asList(one, two)).then().as(StepVerifier::create).verifyComplete(); - - template.update(Book.class) // - .matching(Query.query(Criteria.where("id").is(one.id))) // - .apply(AggregationUpdate.update().set("title").toValue("The Blinding Knife")) // - .findAndModify() // - .as(StepVerifier::create) // - .expectNext(one) // - .verifyComplete(); - - all(Book.class).collectList().as(StepVerifier::create).consumeNextWith(it -> { - - assertThat(it).containsExactly(org.bson.Document.parse( - "{\"_id\" : 1, \"title\" : \"The Blinding Knife\", \"_class\" : \"org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Book\"}"), - org.bson.Document.parse( - "{\"_id\" : 2, \"title\" : \"The Broken Eye\", \"_class\" : \"org.springframework.data.mongodb.core.ReactiveMongoTemplateUpdateTests$Book\"}")); - }).verifyComplete(); - - } - - private Flux all(Class type) { - return Flux.from(collection(type).find(new org.bson.Document())); - } - - private MongoCollection collection(Class type) { - return client.getDatabase(DB_NAME).getCollection(template.getCollectionName(type)); - } - - @Document("scores") - static class Score { - - Integer id; - String student; - List homework; - List quiz; - Integer extraCredit; - - public Score(Integer id, String student, List homework, List quiz, Integer extraCredit) { - - this.id = id; - this.student = student; - this.homework = homework; - this.quiz = quiz; - this.extraCredit = extraCredit; - } - } - - static class Versioned { - - String id; - @Version Long version; - String value; - - public Versioned(String id, String value) { - this.id = id; - this.value = value; - } - } - - @EqualsAndHashCode - static class Book { - - @Id Integer id; - String title; - String isbn; - Author author; - @Field("copies") Collection stock; - } - - static class Author { - - @Field("first") String firstname; - @Field("last") String lastname; - - public Author(String firstname, String lastname) { - this.firstname = firstname; - this.lastname = lastname; - } - } - - static class Warehouse { - - public Warehouse(String location, Integer qty) { - this.location = location; - this.qty = qty; - } - - @Field("warehouse") String location; - Integer qty; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupportTests.java deleted file mode 100644 index df4ea6de6f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupportTests.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; -import reactor.test.StepVerifier; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; - -import com.mongodb.client.MongoClient; - -/** - * Integration tests for {@link ReactiveRemoveOperationSupport}. - * - * @author Mark Paluch - */ -@ExtendWith(MongoClientExtension.class) -class ReactiveRemoveOperationSupportTests { - - private static final String STAR_WARS = "star-wars"; - private static @Client MongoClient client; - private static @Client com.mongodb.reactivestreams.client.MongoClient reactiveClient; - - private MongoTemplate blocking; - private ReactiveMongoTemplate template; - - private Person han; - private Person luke; - - @BeforeEach - void setUp() { - - blocking = new MongoTemplate(new SimpleMongoClientDatabaseFactory(client, "ExecutableRemoveOperationSupportTests")); - blocking.dropCollection(STAR_WARS); - - han = new Person(); - han.firstname = "han"; - han.id = "id-1"; - - luke = new Person(); - luke.firstname = "luke"; - luke.id = "id-2"; - - blocking.save(han); - blocking.save(luke); - - template = new ReactiveMongoTemplate(reactiveClient, "ExecutableRemoveOperationSupportTests"); - } - - @Test // DATAMONGO-1719 - void removeAll() { - - template.remove(Person.class).all().as(StepVerifier::create).consumeNextWith(actual -> { - assertThat(actual.getDeletedCount()).isEqualTo(2L); - }).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void removeAllMatching() { - - template.remove(Person.class).matching(query(where("firstname").is("han"))).all().as(StepVerifier::create) - .consumeNextWith(actual -> assertThat(actual.getDeletedCount()).isEqualTo(1L)).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void removeAllMatchingCriteria() { - - template.remove(Person.class).matching(where("firstname").is("han")).all().as(StepVerifier::create) - .consumeNextWith(actual -> assertThat(actual.getDeletedCount()).isEqualTo(1L)).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void removeAllMatchingWithAlternateDomainTypeAndCollection() { - - template.remove(Jedi.class).inCollection(STAR_WARS).matching(query(where("name").is("luke"))).all() - .as(StepVerifier::create).consumeNextWith(actual -> assertThat(actual.getDeletedCount()).isEqualTo(1L)) - .verifyComplete(); - } - - @Test // DATAMONGO-1719 - void removeAndReturnAllMatching() { - - template.remove(Person.class).matching(query(where("firstname").is("han"))).findAndRemove().as(StepVerifier::create) - .expectNext(han).verifyComplete(); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person { - @Id String id; - String firstname; - } - - @Data - static class Jedi { - - @Field("firstname") String name; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveSessionBoundMongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveSessionBoundMongoTemplateUnitTests.java deleted file mode 100644 index dae5347a04..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveSessionBoundMongoTemplateUnitTests.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyList; -import static org.mockito.Mockito.anyString; - -import java.lang.reflect.Proxy; - -import org.bson.Document; -import org.bson.codecs.BsonValueCodec; -import org.bson.codecs.configuration.CodecRegistry; -import org.bson.conversions.Bson; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.reactivestreams.Publisher; - -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate.ReactiveSessionBoundMongoTemplate; -import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; - -import com.mongodb.client.model.CountOptions; -import com.mongodb.client.model.DeleteOptions; -import com.mongodb.client.model.FindOneAndUpdateOptions; -import com.mongodb.client.model.UpdateOptions; -import com.mongodb.reactivestreams.client.AggregatePublisher; -import com.mongodb.reactivestreams.client.ClientSession; -import com.mongodb.reactivestreams.client.DistinctPublisher; -import com.mongodb.reactivestreams.client.FindPublisher; -import com.mongodb.reactivestreams.client.MapReducePublisher; -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * Unit tests for {@link ReactiveSessionBoundMongoTemplate}. - * - * @author Christoph Strobl - * @author Mark Paluch - * @author Mathieu Ouellet - */ -@SuppressWarnings("unchecked") -@RunWith(MockitoJUnitRunner.Silent.class) -public class ReactiveSessionBoundMongoTemplateUnitTests { - - private static final String COLLECTION_NAME = "collection-1"; - - ReactiveSessionBoundMongoTemplate template; - MongoMappingContext mappingContext; - MappingMongoConverter converter; - - ReactiveMongoDatabaseFactory factory; - - @Mock MongoCollection collection; - @Mock MongoDatabase database; - @Mock ClientSession clientSession; - @Mock FindPublisher findPublisher; - @Mock AggregatePublisher aggregatePublisher; - @Mock DistinctPublisher distinctPublisher; - @Mock Publisher resultPublisher; - @Mock MapReducePublisher mapReducePublisher; - @Mock MongoClient client; - @Mock CodecRegistry codecRegistry; - - @Before - public void setUp() { - - when(client.getDatabase(anyString())).thenReturn(database); - when(codecRegistry.get(any(Class.class))).thenReturn(new BsonValueCodec()); - when(database.getCodecRegistry()).thenReturn(codecRegistry); - when(database.getCollection(anyString())).thenReturn(collection); - when(database.getCollection(anyString(), any())).thenReturn(collection); - when(database.listCollectionNames(any(ClientSession.class))).thenReturn(findPublisher); - when(database.createCollection(any(ClientSession.class), any(), any())).thenReturn(resultPublisher); - when(database.runCommand(any(ClientSession.class), any(), any(Class.class))).thenReturn(resultPublisher); - when(collection.find(any(ClientSession.class))).thenReturn(findPublisher); - when(collection.find(any(ClientSession.class), any(Document.class))).thenReturn(findPublisher); - when(collection.find(any(ClientSession.class), any(Class.class))).thenReturn(findPublisher); - when(collection.find(any(ClientSession.class), any(), any())).thenReturn(findPublisher); - when(collection.deleteMany(any(ClientSession.class), any(), any())).thenReturn(resultPublisher); - when(collection.insertOne(any(ClientSession.class), any(Document.class))).thenReturn(resultPublisher); - when(collection.aggregate(any(ClientSession.class), anyList(), any(Class.class))).thenReturn(aggregatePublisher); - when(collection.countDocuments(any(ClientSession.class), any(), any(CountOptions.class))) - .thenReturn(resultPublisher); - when(collection.drop(any(ClientSession.class))).thenReturn(resultPublisher); - when(collection.findOneAndUpdate(any(ClientSession.class), any(), any(Bson.class), any())) - .thenReturn(resultPublisher); - when(collection.distinct(any(ClientSession.class), any(), any(Bson.class), any())).thenReturn(distinctPublisher); - when(collection.updateOne(any(ClientSession.class), any(), any(Bson.class), any(UpdateOptions.class))) - .thenReturn(resultPublisher); - when(collection.updateMany(any(ClientSession.class), any(), any(Bson.class), any(UpdateOptions.class))) - .thenReturn(resultPublisher); - when(collection.dropIndex(any(ClientSession.class), anyString())).thenReturn(resultPublisher); - when(collection.mapReduce(any(ClientSession.class), any(), any(), any())).thenReturn(mapReducePublisher); - when(findPublisher.projection(any())).thenReturn(findPublisher); - when(findPublisher.limit(anyInt())).thenReturn(findPublisher); - when(findPublisher.collation(any())).thenReturn(findPublisher); - when(findPublisher.first()).thenReturn(resultPublisher); - when(aggregatePublisher.allowDiskUse(anyBoolean())).thenReturn(aggregatePublisher); - - factory = new SimpleReactiveMongoDatabaseFactory(client, "foo"); - - this.mappingContext = new MongoMappingContext(); - this.converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); - this.template = new ReactiveSessionBoundMongoTemplate(clientSession, new ReactiveMongoTemplate(factory, converter)); - } - - @Test // DATAMONGO-1880 - public void executeUsesProxiedCollectionInCallback() { - - template.execute("collection", MongoCollection::find).subscribe(); - - verify(collection, never()).find(); - verify(collection).find(eq(clientSession)); - } - - @Test // DATAMONGO-1880 - public void executeUsesProxiedDatabaseInCallback() { - - template.execute(MongoDatabase::listCollectionNames).subscribe(); - - verify(database, never()).listCollectionNames(); - verify(database).listCollectionNames(eq(clientSession)); - } - - @Test // DATAMONGO-1880 - public void findOneUsesProxiedCollection() { - - template.findOne(new Query(), Person.class).subscribe(); - - verify(collection).find(eq(clientSession), any(), any()); - } - - @Test // DATAMONGO-1880 - public void findShouldUseProxiedCollection() { - - template.find(new Query(), Person.class).subscribe(); - - verify(collection).find(eq(clientSession), any(Class.class)); - } - - @Test // DATAMONGO-1880 - public void findAllShouldUseProxiedCollection() { - - template.findAll(Person.class).subscribe(); - - verify(collection).find(eq(clientSession), eq(Document.class)); - } - - @Test // DATAMONGO-1880 - public void executeCommandShouldUseProxiedDatabase() { - - template.executeCommand("{}").subscribe(); - - verify(database).runCommand(eq(clientSession), any(), any(Class.class)); - } - - @Test // DATAMONGO-1880 - public void removeShouldUseProxiedCollection() { - - template.remove(new Query(), Person.class).subscribe(); - - verify(collection).deleteMany(eq(clientSession), any(), any(DeleteOptions.class)); - } - - @Test // DATAMONGO-1880 - public void insertShouldUseProxiedCollection() { - - template.insert(new Person()).subscribe(); - - verify(collection).insertOne(eq(clientSession), any(Document.class)); - } - - @Test // DATAMONGO-1880 - public void aggregateShouldUseProxiedCollection() { - - template.aggregate(Aggregation.newAggregation(Aggregation.project("foo")), COLLECTION_NAME, Person.class) - .subscribe(); - - verify(collection).aggregate(eq(clientSession), anyList(), eq(Document.class)); - } - - @Test // DATAMONGO-1880 - public void collectionExistsShouldUseProxiedDatabase() { - - template.collectionExists(Person.class).subscribe(); - - verify(database).listCollectionNames(eq(clientSession)); - } - - @Test // DATAMONGO-1880 - public void countShouldUseProxiedCollection() { - - template.count(new Query(), Person.class).subscribe(); - - verify(collection).countDocuments(eq(clientSession), any(), any(CountOptions.class)); - } - - @Test // DATAMONGO-1880 - public void createCollectionShouldUseProxiedDatabase() { - - template.createCollection(Person.class).subscribe(); - - verify(database).createCollection(eq(clientSession), anyString(), any()); - } - - @Test // DATAMONGO-1880 - public void dropShouldUseProxiedCollection() { - - template.dropCollection(Person.class).subscribe(); - - verify(collection).drop(eq(clientSession)); - } - - @Test // DATAMONGO-1880 - public void findAndModifyShouldUseProxiedCollection() { - - template.findAndModify(new Query(), new Update().set("foo", "bar"), Person.class).subscribe(); - - verify(collection).findOneAndUpdate(eq(clientSession), any(), any(Bson.class), any(FindOneAndUpdateOptions.class)); - } - - @Test // DATAMONGO-1880 - public void findDistinctShouldUseProxiedCollection() { - - template.findDistinct(new Query(), "firstName", Person.class, String.class).subscribe(); - - verify(collection).distinct(eq(clientSession), anyString(), any(), any()); - } - - @Test // DATAMONGO-1880, DATAMONGO-2264 - public void geoNearShouldUseProxiedDatabase() { - - template.geoNear(NearQuery.near(new Point(0, 0), Metrics.NEUTRAL), Person.class).subscribe(); - - verify(collection).aggregate(eq(clientSession), anyList(), eq(Document.class)); - } - - @Test // DATAMONGO-1880, DATAMONGO-1890, DATAMONGO-257 - public void mapReduceShouldUseProxiedCollection() { - - template.mapReduce(new BasicQuery("{}"), Person.class, COLLECTION_NAME, Person.class, "foo", "bar", - MapReduceOptions.options()).subscribe(); - - verify(collection).mapReduce(eq(clientSession), anyString(), anyString(), eq(Document.class)); - } - - @Test // DATAMONGO-1880 - public void updateFirstShouldUseProxiedCollection() { - - template.updateFirst(new Query(), Update.update("foo", "bar"), Person.class).subscribe(); - - verify(collection).updateOne(eq(clientSession), any(), any(Bson.class), any(UpdateOptions.class)); - } - - @Test // DATAMONGO-1880 - public void updateMultiShouldUseProxiedCollection() { - - template.updateMulti(new Query(), Update.update("foo", "bar"), Person.class).subscribe(); - - verify(collection).updateMany(eq(clientSession), any(), any(Bson.class), any(UpdateOptions.class)); - } - - @Test // DATAMONGO-1880 - public void upsertShouldUseProxiedCollection() { - - template.upsert(new Query(), Update.update("foo", "bar"), Person.class).subscribe(); - - verify(collection).updateOne(eq(clientSession), any(), any(Bson.class), any(UpdateOptions.class)); - } - - @Test // DATAMONGO-1880 - public void getCollectionShouldShouldJustReturnTheCollection/*No ClientSession binding*/() { - assertThat(template.getCollection(COLLECTION_NAME).block()).isNotInstanceOf(Proxy.class) - .isInstanceOf(MongoCollection.class); - } - - @Test // DATAMONGO-1880 - public void getDbShouldJustReturnTheDatabase/*No ClientSession binding*/() { - assertThat(template.getMongoDatabase().block()).isNotInstanceOf(Proxy.class).isInstanceOf(MongoDatabase.class); - } - - @Test // DATAMONGO-1880 - public void indexOpsShouldUseProxiedCollection() { - - template.indexOps(COLLECTION_NAME).dropIndex("index-name").subscribe(); - - verify(collection).dropIndex(eq(clientSession), eq("index-name")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupportTests.java deleted file mode 100644 index 587380bc81..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupportTests.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2017-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; -import reactor.test.StepVerifier; - -import org.bson.BsonString; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; - -import com.mongodb.client.MongoClient; - -/** - * Integration tests for {@link ReactiveUpdateOperationSupport}. - * - * @author Mark Paluch - */ -@ExtendWith(MongoClientExtension.class) -class ReactiveUpdateOperationSupportTests { - - private static final String STAR_WARS = "star-wars"; - private static @Client MongoClient client; - private static @Client com.mongodb.reactivestreams.client.MongoClient reactiveClient; - - private MongoTemplate blocking; - private ReactiveMongoTemplate template; - - private Person han; - private Person luke; - - @BeforeEach - void setUp() { - - blocking = new MongoTemplate(new SimpleMongoClientDatabaseFactory(client, "ExecutableUpdateOperationSupportTests")); - blocking.dropCollection(STAR_WARS); - - han = new Person(); - han.firstname = "han"; - han.id = "id-1"; - - luke = new Person(); - luke.firstname = "luke"; - luke.id = "id-2"; - - blocking.save(han); - blocking.save(luke); - - template = new ReactiveMongoTemplate(reactiveClient, "ExecutableUpdateOperationSupportTests"); - } - - @Test // DATAMONGO-1719 - void domainTypeIsRequired() { - assertThatIllegalArgumentException().isThrownBy(() -> template.update(null)); - } - - @Test // DATAMONGO-1719 - void updateIsRequired() { - assertThatIllegalArgumentException().isThrownBy(() -> template.update(Person.class).apply(null)); - } - - @Test // DATAMONGO-1719 - void collectionIsRequiredOnSet() { - assertThatIllegalArgumentException().isThrownBy(() -> template.update(Person.class).inCollection(null)); - } - - @Test // DATAMONGO-1719 - void findAndModifyOptionsAreRequiredOnSet() { - assertThatIllegalArgumentException() - .isThrownBy(() -> template.update(Person.class).apply(new Update()).withOptions(null)); - } - - @Test // DATAMONGO-1719 - void updateFirst() { - - template.update(Person.class).apply(new Update().set("firstname", "Han")).first().as(StepVerifier::create) - .consumeNextWith(actual -> { - - assertThat(actual.getModifiedCount()).isEqualTo(1L); - assertThat(actual.getUpsertedId()).isNull(); - }).verifyComplete(); - - } - - @Test // DATAMONGO-1719 - void updateAll() { - - template.update(Person.class).apply(new Update().set("firstname", "Han")).all().as(StepVerifier::create) - .consumeNextWith(actual -> { - - assertThat(actual.getModifiedCount()).isEqualTo(2L); - assertThat(actual.getUpsertedId()).isNull(); - }).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void updateAllMatching() { - - template.update(Person.class).matching(queryHan()).apply(new Update().set("firstname", "Han")).all() - .as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual.getModifiedCount()).isEqualTo(1L); - assertThat(actual.getUpsertedId()).isNull(); - }).verifyComplete(); - } - - @Test // DATAMONGO-2416 - void updateAllMatchingCriteria() { - - template.update(Person.class).matching(where("id").is(han.getId())).apply(new Update().set("firstname", "Han")) - .all().as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual.getModifiedCount()).isEqualTo(1L); - assertThat(actual.getUpsertedId()).isNull(); - }).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void updateWithDifferentDomainClassAndCollection() { - - template.update(Jedi.class).inCollection(STAR_WARS).matching(query(where("_id").is(han.getId()))) - .apply(new Update().set("name", "Han")).all().as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual.getModifiedCount()).isEqualTo(1L); - assertThat(actual.getUpsertedId()).isNull(); - }).verifyComplete(); - - assertThat(blocking.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Han"); - } - - @Test // DATAMONGO-1719 - void findAndModify() { - - template.update(Person.class).matching(queryHan()).apply(new Update().set("firstname", "Han")).findAndModify() - .as(StepVerifier::create).expectNext(han).verifyComplete(); - - assertThat(blocking.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Han"); - } - - @Test // DATAMONGO-1719 - void findAndModifyWithDifferentDomainTypeAndCollection() { - - template.update(Jedi.class).inCollection(STAR_WARS).matching(query(where("_id").is(han.getId()))) - .apply(new Update().set("name", "Han")).findAndModify().as(StepVerifier::create) - .consumeNextWith(actual -> assertThat(actual.getName()).isEqualTo("han")).verifyComplete(); - - assertThat(blocking.findOne(queryHan(), Person.class)).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", - "Han"); - } - - @Test // DATAMONGO-1719 - void findAndModifyWithOptions() { - - template.update(Person.class).matching(queryHan()).apply(new Update().set("firstname", "Han")) - .withOptions(FindAndModifyOptions.options().returnNew(true)).findAndModify().as(StepVerifier::create) - .consumeNextWith(actual -> { - - assertThat(actual).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", "Han"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1719 - void upsert() { - - template.update(Person.class).matching(query(where("id").is("id-3"))) - .apply(new Update().set("firstname", "Chewbacca")).upsert().as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual.getModifiedCount()).isEqualTo(0L); - assertThat(actual.getUpsertedId()).isEqualTo(new BsonString("id-3")); - }).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplace() { - - Person luke = new Person(); - luke.firstname = "Luke"; - - template.update(Person.class).matching(queryHan()).replaceWith(luke).findAndReplace() // - .as(StepVerifier::create).expectNext(han).verifyComplete(); - - template.findOne(queryHan(), Person.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", "Luke"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceWithProjection() { - - Person luke = new Person(); - luke.firstname = "Luke"; - - template.update(Person.class).matching(queryHan()).replaceWith(luke).as(Jedi.class).findAndReplace() // - .as(StepVerifier::create).consumeNextWith(it -> { - assertThat(it.getName()).isEqualTo(han.firstname); - }).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceWithCollection() { - - Person luke = new Person(); - luke.firstname = "Luke"; - - template.update(Person.class).inCollection(STAR_WARS).matching(queryHan()).replaceWith(luke).findAndReplace() // - .as(StepVerifier::create).expectNext(han).verifyComplete(); - - template.findOne(queryHan(), Person.class) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", "Luke"); - }).verifyComplete(); - } - - @Test // DATAMONGO-1827 - void findAndReplaceWithOptions() { - - Person luke = new Person(); - luke.firstname = "Luke"; - - template.update(Person.class).matching(queryHan()).replaceWith(luke) - .withOptions(FindAndReplaceOptions.options().returnNew()).findAndReplace() // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual).isNotEqualTo(han).hasFieldOrPropertyWithValue("firstname", "Luke"); - }).verifyComplete(); - } - - private Query queryHan() { - return query(where("id").is(han.getId())); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = STAR_WARS) - static class Person { - @Id String id; - String firstname; - } - - @Data - static class Human { - @Id String id; - } - - @Data - static class Jedi { - - @Field("firstname") String name; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SerializationUtilsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SerializationUtilsUnitTests.java deleted file mode 100644 index e53de3c8db..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SerializationUtilsUnitTests.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2012-2021 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 static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.SerializationUtils.*; - -import java.util.Arrays; -import java.util.Map; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.query.SerializationUtils; - -import com.mongodb.BasicDBList; - -/** - * Unit tests for {@link SerializationUtils}. - * - * @author Oliver Gierke - * @author Christoph Strobl - */ -public class SerializationUtilsUnitTests { - - @Test - public void writesSimpleDocument() { - - Document document = new Document("foo", "bar"); - assertThat(serializeToJsonSafely(document)).isEqualTo("{ \"foo\" : \"bar\"}"); - } - - @Test - public void writesComplexObjectAsPlainToString() { - - Document document = new Document("foo", new Complex()); - assertThat(serializeToJsonSafely(document).startsWith( - "{ \"foo\" : { \"$java\" : org.springframework.data.mongodb.core.SerializationUtilsUnitTests$Complex")); - } - - @Test - public void writesCollection() { - - Document document = new Document("foo", Arrays.asList("bar", new Complex())); - assertThat(serializeToJsonSafely(document)).startsWith( - "{ \"foo\" : [ \"bar\", { \"$java\" : org.springframework.data.mongodb.core.SerializationUtilsUnitTests$Complex") - .endsWith(" } ] }"); - } - - @Test // DATAMONGO-1245 - public void flattenMapShouldFlatOutNestedStructureCorrectly() { - - Document document = new Document(); - document.put("_id", 1); - document.put("nested", new Document("value", "conflux")); - - assertThat(flattenMap(document)).containsEntry("_id", 1).containsEntry("nested.value", "conflux"); - } - - @Test // DATAMONGO-1245 - public void flattenMapShouldFlatOutNestedStructureWithListCorrectly() { - - BasicDBList dbl = new BasicDBList(); - dbl.addAll(Arrays.asList("nightwielder", "calamity")); - - Document document = new Document(); - document.put("_id", 1); - document.put("nested", new Document("value", dbl)); - - assertThat(flattenMap(document)).containsEntry("_id", 1).containsEntry("nested.value", dbl); - } - - @Test // DATAMONGO-1245 - public void flattenMapShouldLeaveKeywordsUntouched() { - - Document document = new Document(); - document.put("_id", 1); - document.put("nested", new Document("$regex", "^conflux$")); - - Map map = flattenMap(document); - - assertThat(map).containsEntry("_id", 1).containsKey("nested"); - assertThat(((Map) map.get("nested")).get("$regex")).isEqualTo("^conflux$"); - } - - @Test // DATAMONGO-1245 - public void flattenMapShouldAppendCommandsCorrectly() { - - Document document = new Document(); - Document nested = new Document(); - nested.put("$regex", "^conflux$"); - nested.put("$options", "i"); - document.put("_id", 1); - document.put("nested", nested); - - Map map = flattenMap(document); - - assertThat(map).containsEntry("_id", 1).containsKey("nested"); - assertThat(((Map) map.get("nested")).get("$regex")).isEqualTo("^conflux$"); - assertThat(((Map) map.get("nested")).get("$options")).isEqualTo("i"); - } - - @Test // DATAMONGO-1245 - public void flattenMapShouldReturnEmptyMapWhenSourceIsNull() { - assertThat(flattenMap(null)).isEmpty(); - } - - static class Complex { - - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateTests.java deleted file mode 100644 index 5861dd5915..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateTests.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.aopalliance.aop.Advice; -import org.bson.Document; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; -import org.springframework.aop.Advisor; -import org.springframework.aop.framework.Advised; -import org.springframework.dao.DataAccessException; -import org.springframework.data.annotation.Id; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.ClientSessionException; -import org.springframework.data.mongodb.LazyLoadingException; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.SessionAwareMethodInterceptor; -import org.springframework.data.mongodb.core.MongoTemplate.SessionBoundMongoTemplate; -import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoVersion; -import org.springframework.data.mongodb.test.util.ReplSetClient; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.ClientSessionOptions; -import com.mongodb.client.ClientSession; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; - -/** - * Integration tests for {@link SessionBoundMongoTemplate} operating up an active {@link ClientSession}. - * - * @author Christoph Strobl - */ -@ExtendWith(MongoClientExtension.class) -@EnableIfReplicaSetAvailable -public class SessionBoundMongoTemplateTests { - - static @ReplSetClient MongoClient client; - - MongoTemplate template; - SessionBoundMongoTemplate sessionBoundTemplate; - ClientSession session; - volatile List> spiedCollections = new ArrayList<>(); - volatile List spiedDatabases = new ArrayList<>(); - - @BeforeEach - public void setUp() { - - MongoDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(client, "session-bound-mongo-template-tests") { - - @Override - public MongoDatabase getMongoDatabase() throws DataAccessException { - - MongoDatabase spiedDatabse = Mockito.spy(super.getMongoDatabase()); - spiedDatabases.add(spiedDatabse); - return spiedDatabse; - } - }; - - session = client.startSession(ClientSessionOptions.builder().build()); - - this.template = new MongoTemplate(factory); - - this.sessionBoundTemplate = new SessionBoundMongoTemplate(session, - new MongoTemplate(factory, getDefaultMongoConverter(factory))) { - - @Override - protected MongoCollection prepareCollection(MongoCollection collection) { - - injectCollectionSpy(collection); - - return super.prepareCollection(collection); - } - - @SuppressWarnings({ "ConstantConditions", "unchecked" }) - private void injectCollectionSpy(MongoCollection collection) { - - InvocationHandler handler = Proxy.getInvocationHandler(collection); - - Advised advised = (Advised) ReflectionTestUtils.getField(handler, "advised"); - - for (Advisor advisor : advised.getAdvisors()) { - Advice advice = advisor.getAdvice(); - if (advice instanceof SessionAwareMethodInterceptor) { - - MongoCollection spiedCollection = Mockito - .spy((MongoCollection) ReflectionTestUtils.getField(advice, "target")); - spiedCollections.add(spiedCollection); - - ReflectionTestUtils.setField(advice, "target", spiedCollection); - } - } - } - }; - } - - @AfterEach - public void tearDown() { - - session.close(); - } - - @Test // DATAMONGO-1880 - public void findDelegatesToMethodWithSession() { - - sessionBoundTemplate.find(new Query(), Person.class); - - verify(operation(0)).find(eq(session), any(), any()); - } - - @Test // DATAMONGO-1880 - public void fluentFindDelegatesToMethodWithSession() { - - sessionBoundTemplate.query(Person.class).all(); - - verify(operation(0)).find(eq(session), any(), any()); - } - - @Test // DATAMONGO-1880 - public void aggregateDelegatesToMethoddWithSession() { - - sessionBoundTemplate.aggregate(Aggregation.newAggregation(Aggregation.project("firstName")), Person.class, - Person.class); - - verify(operation(0)).aggregate(eq(session), any(), any()); - } - - @Test // DATAMONGO-1880 - public void collectionExistsDelegatesToMethodWithSession() { - - sessionBoundTemplate.collectionExists(Person.class); - - verify(command(0)).listCollectionNames(eq(session)); - } - - @Test // DATAMONGO-1880 - public void shouldLoadDbRefWhenSessionIsActive() { - - Person person = new Person("Kylar Stern"); - - template.save(person); - - WithDbRef wdr = new WithDbRef(); - wdr.id = "id-1"; - wdr.personRef = person; - - template.save(wdr); - - WithDbRef result = sessionBoundTemplate.findById(wdr.id, WithDbRef.class); - - assertThat(result.personRef).isEqualTo(person); - } - - @Test // DATAMONGO-1880 - public void shouldErrorOnLoadDbRefWhenSessionIsClosed() { - - Person person = new Person("Kylar Stern"); - - template.save(person); - - WithDbRef wdr = new WithDbRef(); - wdr.id = "id-1"; - wdr.personRef = person; - - template.save(wdr); - - session.close(); - - assertThatExceptionOfType(ClientSessionException.class) - .isThrownBy(() -> sessionBoundTemplate.findById(wdr.id, WithDbRef.class)); - } - - @Test // DATAMONGO-1880 - public void shouldLoadLazyDbRefWhenSessionIsActive() { - - Person person = new Person("Kylar Stern"); - - template.save(person); - - WithLazyDbRef wdr = new WithLazyDbRef(); - wdr.id = "id-1"; - wdr.personRef = person; - - template.save(wdr); - - WithLazyDbRef result = sessionBoundTemplate.findById(wdr.id, WithLazyDbRef.class); - - assertThat(result.getPersonRef()).isEqualTo(person); - } - - @Test // DATAMONGO-1880 - public void shouldErrorOnLoadLazyDbRefWhenSessionIsClosed() { - - Person person = new Person("Kylar Stern"); - - template.save(person); - - WithLazyDbRef wdr = new WithLazyDbRef(); - wdr.id = "id-1"; - wdr.personRef = person; - - template.save(wdr); - - WithLazyDbRef result = sessionBoundTemplate.findById(wdr.id, WithLazyDbRef.class); - - session.close(); // now close the session - - assertThatExceptionOfType(LazyLoadingException.class).isThrownBy(() -> result.getPersonRef().toString()); - } - - @Test // DATAMONGO-2001 - @MongoVersion(asOf = "4.0") - public void countShouldWorkInTransactions() { - - if (!template.collectionExists(Person.class)) { - template.createCollection(Person.class); - } else { - template.remove(Person.class).all(); - } - - ClientSession session = client.startSession(); - session.startTransaction(); - - MongoTemplate sessionBound = template.withSession(session); - - sessionBound.save(new Person("Kylar Stern")); - - assertThat(sessionBound.query(Person.class).matching(query(where("firstName").is("foobar"))).count()).isZero(); - assertThat(sessionBound.query(Person.class).matching(query(where("firstName").is("Kylar Stern"))).count()).isOne(); - assertThat(sessionBound.query(Person.class).count()).isOne(); - - session.commitTransaction(); - session.close(); - } - - @Test // DATAMONGO-2012 - @MongoVersion(asOf = "4.0") - public void countWithGeoInTransaction() { - - if (!template.collectionExists(Person.class)) { - template.createCollection(Person.class); - template.indexOps(Person.class).ensureIndex(new GeospatialIndex("location")); - } else { - template.remove(Person.class).all(); - } - - ClientSession session = client.startSession(); - session.startTransaction(); - - MongoTemplate sessionBound = template.withSession(session); - - sessionBound.save(new Person("Kylar Stern")); - - assertThat(sessionBound.query(Person.class).matching(query(where("location").near(new Point(1, 0)))).count()) - .isZero(); - - session.commitTransaction(); - session.close(); - } - - @Test // DATAMONGO-2001 - @MongoVersion(asOf = "4.0") - public void countShouldReturnIsolatedCount() throws InterruptedException { - - if (!template.collectionExists(Person.class)) { - template.createCollection(Person.class); - } else { - template.remove(Person.class).all(); - } - - int nrThreads = 2; - CountDownLatch savedInTransaction = new CountDownLatch(nrThreads); - CountDownLatch beforeCommit = new CountDownLatch(nrThreads); - List resultList = new CopyOnWriteArrayList<>(); - - Runnable runnable = () -> { - - ClientSession session = client.startSession(); - session.startTransaction(); - - try { - MongoTemplate sessionBound = template.withSession(session); - - try { - sessionBound.save(new Person("Kylar Stern")); - } finally { - savedInTransaction.countDown(); - } - - savedInTransaction.await(1, TimeUnit.SECONDS); - - try { - resultList.add(sessionBound.query(Person.class).count()); - } finally { - beforeCommit.countDown(); - } - - beforeCommit.await(1, TimeUnit.SECONDS); - } catch (Exception e) { - resultList.add(e); - } - - session.commitTransaction(); - session.close(); - }; - - List threads = IntStream.range(0, nrThreads) // - .mapToObj(i -> new Thread(runnable)) // - .peek(Thread::start) // - .collect(Collectors.toList()); - - for (Thread thread : threads) { - thread.join(); - } - - assertThat(template.query(Person.class).count()).isEqualTo(2L); - assertThat(resultList).hasSize(nrThreads).allMatch(it -> it.equals(1L)); - } - - @Data - static class WithDbRef { - - @Id String id; - @DBRef Person personRef; - } - - @Data - static class WithLazyDbRef { - - @Id String id; - @DBRef(lazy = true) Person personRef; - - public Person getPersonRef() { - return personRef; - } - } - - // --> Just some helpers for testing - - MongoCollection operation(int index) { - return spiedCollections.get(index); - } - - MongoDatabase command(int index) { - return spiedDatabases.get(index); - } - - private MongoConverter getDefaultMongoConverter(MongoDatabaseFactory factory) { - - DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory); - MongoCustomConversions conversions = new MongoCustomConversions(Collections.emptyList()); - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); - mappingContext.afterPropertiesSet(); - - MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext); - converter.setCustomConversions(conversions); - converter.setCodecRegistryProvider(factory); - converter.afterPropertiesSet(); - - return converter; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateUnitTests.java deleted file mode 100644 index 4cc5ae28ad..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateUnitTests.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.lang.reflect.Proxy; -import java.util.Collections; - -import org.bson.Document; -import org.bson.codecs.BsonValueCodec; -import org.bson.codecs.configuration.CodecRegistry; -import org.bson.conversions.Bson; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.BulkOperations.BulkMode; -import org.springframework.data.mongodb.core.MongoTemplate.SessionBoundMongoTemplate; -import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapreduce.GroupBy; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; - -import com.mongodb.client.AggregateIterable; -import com.mongodb.client.ClientSession; -import com.mongodb.client.DistinctIterable; -import com.mongodb.client.FindIterable; -import com.mongodb.client.MapReduceIterable; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoCursor; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.MongoIterable; -import com.mongodb.client.model.CountOptions; -import com.mongodb.client.model.DeleteOptions; -import com.mongodb.client.model.FindOneAndUpdateOptions; -import com.mongodb.client.model.UpdateOptions; - -/** - * Unit test for {@link SessionBoundMongoTemplate} making sure a proxied {@link MongoCollection} and - * {@link MongoDatabase} is used for executing high level commands like {@link MongoOperations#find(Query, Class)} - * provided by Spring Data. Those commands simply handing over MongoDB base types for interaction like when obtaining a - * {@link MongoCollection} via {@link MongoOperations#getCollection(String)} shall not be proxied as the user can - * control the behavior by using the methods dedicated for {@link ClientSession} directly. - * - * @author Christoph Strobl - * @author Jens Schauder - */ -@SuppressWarnings("unchecked") -@RunWith(MockitoJUnitRunner.Silent.class) -public class SessionBoundMongoTemplateUnitTests { - - private static final String COLLECTION_NAME = "collection-1"; - - SessionBoundMongoTemplate template; - - MongoDatabaseFactory factory; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) MongoCollection collection; - @Mock MongoDatabase database; - @Mock MongoClient client; - @Mock ClientSession clientSession; - @Mock FindIterable findIterable; - @Mock MongoIterable mongoIterable; - @Mock DistinctIterable distinctIterable; - @Mock AggregateIterable aggregateIterable; - @Mock MapReduceIterable mapReduceIterable; - @Mock MongoCursor cursor; - @Mock CodecRegistry codecRegistry; - - MappingMongoConverter converter; - MongoMappingContext mappingContext; - - @Before - public void setUp() { - - when(client.getDatabase(anyString())).thenReturn(database); - when(codecRegistry.get(any(Class.class))).thenReturn(new BsonValueCodec()); - when(database.getCodecRegistry()).thenReturn(codecRegistry); - when(database.getCollection(anyString(), any())).thenReturn(collection); - when(database.listCollectionNames(any(ClientSession.class))).thenReturn(mongoIterable); - when(collection.find(any(ClientSession.class), any(), any())).thenReturn(findIterable); - when(collection.aggregate(any(ClientSession.class), anyList(), any())).thenReturn(aggregateIterable); - when(collection.distinct(any(ClientSession.class), any(), any(), any())).thenReturn(distinctIterable); - when(collection.mapReduce(any(ClientSession.class), any(), any(), any())).thenReturn(mapReduceIterable); - when(findIterable.iterator()).thenReturn(cursor); - when(aggregateIterable.collation(any())).thenReturn(aggregateIterable); - when(aggregateIterable.allowDiskUse(anyBoolean())).thenReturn(aggregateIterable); - when(aggregateIterable.batchSize(anyInt())).thenReturn(aggregateIterable); - when(aggregateIterable.map(any())).thenReturn(aggregateIterable); - when(aggregateIterable.into(any())).thenReturn(Collections.emptyList()); - when(mongoIterable.iterator()).thenReturn(cursor); - when(distinctIterable.map(any())).thenReturn(distinctIterable); - when(distinctIterable.into(any())).thenReturn(Collections.emptyList()); - when(mapReduceIterable.sort(any())).thenReturn(mapReduceIterable); - when(mapReduceIterable.filter(any())).thenReturn(mapReduceIterable); - when(mapReduceIterable.map(any())).thenReturn(mapReduceIterable); - when(mapReduceIterable.iterator()).thenReturn(cursor); - when(cursor.hasNext()).thenReturn(false); - when(findIterable.projection(any())).thenReturn(findIterable); - - factory = new SimpleMongoClientDatabaseFactory(client, "foo"); - - this.mappingContext = new MongoMappingContext(); - this.converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), mappingContext); - this.template = new SessionBoundMongoTemplate(clientSession, new MongoTemplate(factory, converter)); - } - - @Test // DATAMONGO-1880 - public void executeUsesProxiedCollectionInCallback() { - - template.execute("collection", MongoCollection::find); - - verify(collection, never()).find(); - verify(collection).find(eq(clientSession)); - } - - @Test // DATAMONGO-1880 - public void executeUsesProxiedDatabaseInCallback() { - - template.execute(MongoDatabase::listCollectionNames); - - verify(database, never()).listCollectionNames(); - verify(database).listCollectionNames(eq(clientSession)); - } - - @Test // DATAMONGO-1880 - public void findOneUsesProxiedCollection() { - - template.findOne(new Query(), Person.class); - - verify(collection).find(eq(clientSession), any(), any()); - } - - @Test // DATAMONGO-1880 - public void findShouldUseProxiedCollection() { - - template.find(new Query(), Person.class); - - verify(collection).find(eq(clientSession), any(), any()); - } - - @Test // DATAMONGO-1880 - public void findAllShouldUseProxiedCollection() { - - template.findAll(Person.class); - - verify(collection).find(eq(clientSession), any(), any()); - } - - @Test // DATAMONGO-1880 - public void executeCommandShouldUseProxiedDatabase() { - - template.executeCommand("{}"); - - verify(database).runCommand(eq(clientSession), any(), any(Class.class)); - } - - @Test // DATAMONGO-1880 - public void removeShouldUseProxiedCollection() { - - template.remove(new Query(), Person.class); - - verify(collection).deleteMany(eq(clientSession), any(), any(DeleteOptions.class)); - } - - @Test // DATAMONGO-1880 - public void insertShouldUseProxiedCollection() { - - template.insert(new Person()); - - verify(collection).insertOne(eq(clientSession), any(Document.class)); - } - - @Test // DATAMONGO-1880 - public void aggregateShouldUseProxiedCollection() { - - template.aggregate(Aggregation.newAggregation(Aggregation.project("foo")), COLLECTION_NAME, Person.class); - - verify(collection).aggregate(eq(clientSession), anyList(), eq(Document.class)); - } - - @Test // DATAMONGO-1880 - public void aggregateStreamShouldUseProxiedCollection() { - - template.aggregateStream(Aggregation.newAggregation(Aggregation.project("foo")), COLLECTION_NAME, Person.class); - - verify(collection).aggregate(eq(clientSession), anyList(), eq(Document.class)); - } - - @Test // DATAMONGO-1880 - public void collectionExistsShouldUseProxiedDatabase() { - - template.collectionExists(Person.class); - - verify(database).listCollectionNames(eq(clientSession)); - } - - @Test // DATAMONGO-1880 - public void countShouldUseProxiedCollection() { - - template.count(new Query(), Person.class); - - verify(collection).countDocuments(eq(clientSession), any(), any(CountOptions.class)); - } - - @Test // DATAMONGO-1880 - public void createCollectionShouldUseProxiedDatabase() { - - template.createCollection(Person.class); - - verify(database).createCollection(eq(clientSession), anyString(), any()); - } - - @Test // DATAMONGO-1880 - public void dropShouldUseProxiedCollection() { - - template.dropCollection(Person.class); - - verify(collection).drop(eq(clientSession)); - } - - @Test // DATAMONGO-1880 - public void findAndModifyShouldUseProxiedCollection() { - - template.findAndModify(new Query(), new Update().set("foo", "bar"), Person.class); - - verify(collection).findOneAndUpdate(eq(clientSession), any(), any(Bson.class), any(FindOneAndUpdateOptions.class)); - } - - @Test // DATAMONGO-1880 - public void findDistinctShouldUseProxiedCollection() { - - template.findDistinct(new Query(), "firstName", Person.class, String.class); - - verify(collection).distinct(eq(clientSession), anyString(), any(), any()); - } - - @Test // DATAMONGO-1880, DATAMONGO-2264 - public void geoNearShouldUseProxiedDatabase() { - - when(database.runCommand(any(ClientSession.class), any(), eq(Document.class))) - .thenReturn(new Document("results", Collections.emptyList())); - template.geoNear(NearQuery.near(new Point(0, 0), Metrics.NEUTRAL), Person.class); - - verify(collection).aggregate(eq(clientSession), anyList(), eq(Document.class)); - } - - @Test // DATAMONGO-1880 - public void groupShouldUseProxiedDatabase() { - - when(database.runCommand(any(ClientSession.class), any(), eq(Document.class))) - .thenReturn(new Document("retval", Collections.emptyList())); - - template.group(COLLECTION_NAME, GroupBy.key("firstName"), Person.class); - - verify(database).runCommand(eq(clientSession), any(), eq(Document.class)); - } - - @Test // DATAMONGO-1880 - public void mapReduceShouldUseProxiedCollection() { - - template.mapReduce(COLLECTION_NAME, "foo", "bar", Person.class); - - verify(collection).mapReduce(eq(clientSession), anyString(), anyString(), eq(Document.class)); - } - - @Test // DATAMONGO-1880 - public void streamShouldUseProxiedCollection() { - - template.stream(new Query(), Person.class); - - verify(collection).find(eq(clientSession), any(), eq(Document.class)); - } - - @Test // DATAMONGO-1880 - public void updateFirstShouldUseProxiedCollection() { - - template.updateFirst(new Query(), Update.update("foo", "bar"), Person.class); - - verify(collection).updateOne(eq(clientSession), any(), any(Bson.class), any(UpdateOptions.class)); - } - - @Test // DATAMONGO-1880 - public void updateMultiShouldUseProxiedCollection() { - - template.updateMulti(new Query(), Update.update("foo", "bar"), Person.class); - - verify(collection).updateMany(eq(clientSession), any(), any(Bson.class), any(UpdateOptions.class)); - } - - @Test // DATAMONGO-1880 - public void upsertShouldUseProxiedCollection() { - - template.upsert(new Query(), Update.update("foo", "bar"), Person.class); - - verify(collection).updateOne(eq(clientSession), any(), any(Bson.class), any(UpdateOptions.class)); - } - - @Test // DATAMONGO-1880 - public void getCollectionShouldShouldJustReturnTheCollection/*No ClientSession binding*/() { - assertThat(template.getCollection(COLLECTION_NAME)).isNotInstanceOf(Proxy.class); - } - - @Test // DATAMONGO-1880 - public void getDbShouldJustReturnTheDatabase/*No ClientSession binding*/() { - assertThat(template.getDb()).isNotInstanceOf(Proxy.class); - } - - @Test // DATAMONGO-1880 - public void indexOpsShouldUseProxiedCollection() { - - template.indexOps(COLLECTION_NAME).dropIndex("index-name"); - - verify(collection).dropIndex(eq(clientSession), eq("index-name")); - } - - @Test // DATAMONGO-1880 - public void bulkOpsShouldUseProxiedCollection() { - - BulkOperations bulkOps = template.bulkOps(BulkMode.ORDERED, COLLECTION_NAME); - bulkOps.insert(new Document()); - - bulkOps.execute(); - - verify(collection).bulkWrite(eq(clientSession), anyList(), any()); - } - - @Test // DATAMONGO-1880 - public void scriptOpsShouldUseProxiedDatabase() { - - when(database.runCommand(eq(clientSession), any())).thenReturn(new Document("retval", new Object())); - template.scriptOps().call("W-O-P-R"); - - verify(database).runCommand(eq(clientSession), any()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedEntityWithDefaultShardKey.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedEntityWithDefaultShardKey.java deleted file mode 100644 index 6d7c6c5b22..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedEntityWithDefaultShardKey.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2020-2021 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 lombok.AllArgsConstructor; -import lombok.Data; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.Sharded; - -/** - * @author Christoph Strobl - */ -@Data -@AllArgsConstructor -@Sharded -public class ShardedEntityWithDefaultShardKey { - - private @Id String id; - - private String country; - - @Field("userid") // - private Integer userId; - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedEntityWithNonDefaultImmutableShardKey.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedEntityWithNonDefaultImmutableShardKey.java deleted file mode 100644 index c2ab35b8e2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedEntityWithNonDefaultImmutableShardKey.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2020-2021 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 lombok.AllArgsConstructor; -import lombok.Data; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.Sharded; - -/** - * @author Christoph Strobl - */ -@Data -@AllArgsConstructor -@Sharded(shardKey = { "country", "userId" }, immutableKey = true) -public class ShardedEntityWithNonDefaultImmutableShardKey { - - private @Id String id; - - private String country; - - @Field("userid") // - private Integer userId; - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedEntityWithNonDefaultShardKey.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedEntityWithNonDefaultShardKey.java deleted file mode 100644 index a098cc04dc..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedEntityWithNonDefaultShardKey.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2020-2021 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 lombok.AllArgsConstructor; -import lombok.Data; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.Sharded; - -/** - * @author Christoph Strobl - */ -@Data -@AllArgsConstructor -@Sharded(shardKey = { "country", "userId" }) -public class ShardedEntityWithNonDefaultShardKey { - - private @Id String id; - - private String country; - - @Field("userid") // - private Integer userId; - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedVersionedEntityWithNonDefaultShardKey.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedVersionedEntityWithNonDefaultShardKey.java deleted file mode 100644 index 754fffb722..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ShardedVersionedEntityWithNonDefaultShardKey.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2020-2021 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 lombok.AllArgsConstructor; -import lombok.Data; - -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.Version; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.Sharded; - -/** - * @author Christoph Strobl - */ -@Data -@AllArgsConstructor -@Sharded(shardKey = { "country", "userId" }) -public class ShardedVersionedEntityWithNonDefaultShardKey { - - private @Id String id; - - private @Version Long version; - - private String country; - - @Field("userid") // - private Integer userId; - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoClientDatabaseFactoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoClientDatabaseFactoryUnitTests.java deleted file mode 100644 index 8f5a8cafa3..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoClientDatabaseFactoryUnitTests.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2011-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.aop.framework.AopProxyUtils; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.ConnectionString; -import com.mongodb.client.ClientSession; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoDatabase; - -/** - * Unit tests for {@link SimpleMongoClientDatabaseFactory}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -class SimpleMongoClientDatabaseFactoryUnitTests { - - @Mock MongoClient mongo; - @Mock ClientSession clientSession; - @Mock MongoDatabase database; - - @Test // DATADOC-254, DATAMONGO-1903 - void rejectsIllegalDatabaseNames() { - - rejectsDatabaseName("foo.bar"); - rejectsDatabaseName("foo$bar"); - rejectsDatabaseName("foo\\bar"); - rejectsDatabaseName("foo//bar"); - rejectsDatabaseName("foo bar"); - rejectsDatabaseName("foo\"bar"); - } - - @Test // DATADOC-254 - void allowsDatabaseNames() { - new SimpleMongoClientDatabaseFactory(mongo, "foo-bar"); - new SimpleMongoClientDatabaseFactory(mongo, "foo_bar"); - new SimpleMongoClientDatabaseFactory(mongo, "foo01231bar"); - } - - @Test // DATADOC-295 - void mongoUriConstructor() { - - ConnectionString mongoURI = new ConnectionString( - "mongodb://myUsername:myPassword@localhost/myDatabase.myCollection"); - MongoDatabaseFactory mongoDbFactory = new SimpleMongoClientDatabaseFactory(mongoURI); - - assertThat(mongoDbFactory).hasFieldOrPropertyWithValue("databaseName", "myDatabase"); - } - - @Test // DATAMONGO-1158 - void constructsMongoClientAccordingToMongoUri() { - - ConnectionString uri = new ConnectionString( - "mongodb://myUserName:myPassWord@127.0.0.1:27017/myDataBase.myCollection"); - SimpleMongoClientDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(uri); - - assertThat(factory).hasFieldOrPropertyWithValue("databaseName", "myDataBase"); - } - - @Test // DATAMONGO-1880 - void cascadedWithSessionUsesRootFactory() { - - when(mongo.getDatabase("foo")).thenReturn(database); - - MongoDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(mongo, "foo"); - MongoDatabaseFactory wrapped = factory.withSession(clientSession).withSession(clientSession); - - InvocationHandler invocationHandler = Proxy.getInvocationHandler(wrapped.getMongoDatabase()); - - Object singletonTarget = AopProxyUtils - .getSingletonTarget(ReflectionTestUtils.getField(invocationHandler, "advised")); - - assertThat(singletonTarget).isSameAs(database); - } - - private void rejectsDatabaseName(String databaseName) { - assertThatThrownBy(() -> new SimpleMongoClientDatabaseFactory(mongo, databaseName)) - .isInstanceOf(IllegalArgumentException.class); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactoryUnitTests.java deleted file mode 100644 index 84e6fda5f9..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactoryUnitTests.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2018-2021 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 static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.aop.framework.AopProxyUtils; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.reactivestreams.client.ClientSession; -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * Unit tests for {@link SimpleReactiveMongoDatabaseFactory}. - * - * @author Mark Paluch - * @author Mathieu Ouellet - */ -@ExtendWith(MockitoExtension.class) -class SimpleReactiveMongoDatabaseFactoryUnitTests { - - @Mock MongoClient mongoClient; - @Mock ClientSession clientSession; - @Mock MongoDatabase database; - - @Test // DATAMONGO-1880 - void cascadedWithSessionUsesRootFactory() { - - when(mongoClient.getDatabase("foo")).thenReturn(database); - - ReactiveMongoDatabaseFactory factory = new SimpleReactiveMongoDatabaseFactory(mongoClient, "foo"); - ReactiveMongoDatabaseFactory wrapped = factory.withSession(clientSession).withSession(clientSession); - - InvocationHandler invocationHandler = Proxy.getInvocationHandler(wrapped.getMongoDatabase().block()); - - Object singletonTarget = AopProxyUtils - .getSingletonTarget(ReflectionTestUtils.getField(invocationHandler, "advised")); - - assertThat(singletonTarget).isSameAs(database); - } - - @Test // DATAMONGO-1903 - void rejectsIllegalDatabaseNames() { - - rejectsDatabaseName("foo.bar"); - rejectsDatabaseName("foo$bar"); - rejectsDatabaseName("foo\\bar"); - rejectsDatabaseName("foo//bar"); - rejectsDatabaseName("foo bar"); - rejectsDatabaseName("foo\"bar"); - } - - private void rejectsDatabaseName(String databaseName) { - assertThatThrownBy(() -> new SimpleReactiveMongoDatabaseFactory(mongoClient, databaseName)) - .isInstanceOf(IllegalArgumentException.class); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SpecialDoc.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SpecialDoc.java deleted file mode 100644 index aa9a26ea25..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SpecialDoc.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.springframework.data.mongodb.core; - -public class SpecialDoc extends BaseDoc { - String specialValue; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestEntities.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestEntities.java deleted file mode 100644 index c0de54d7cf..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestEntities.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2018-2021 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.List; - -/** - * A simple collection of grouped test entities used throughout the test suite. - * - * @author Christoph Strobl - */ -public class TestEntities { - - private static final GeoEntities GEO = new GeoEntities(); - - public static GeoEntities geolocation() { - return GEO; - } - - public static class GeoEntities { - - /** - *
-		 * X: -73.99408
-		 * Y: 40.75057
-		 * 
- * - * @return new {@link Venue} - */ - public Venue pennStation() { - return new Venue("Penn Station", -73.99408, 40.75057); - } - - /** - *
-		 * X: -73.99171
-		 * Y: 40.738868
-		 * 
- * - * @return new {@link Venue} - */ - - public Venue tenGenOffice() { - return new Venue("10gen Office", -73.99171, 40.738868); - } - - /** - *
-		 * X: -73.988135
-		 * Y: 40.741404
-		 * 
- * - * @return new {@link Venue} - */ - public Venue flatironBuilding() { - return new Venue("Flatiron Building", -73.988135, 40.741404); - } - - /** - *
-		 * X: -74.2713
-		 * Y: 40.73137
-		 * 
- * - * @return new {@link Venue} - */ - public Venue maplewoodNJ() { - return new Venue("Maplewood, NJ", -74.2713, 40.73137); - } - - public List newYork() { - - List venues = new ArrayList<>(); - - venues.add(pennStation()); - venues.add(tenGenOffice()); - venues.add(flatironBuilding()); - venues.add(new Venue("Players Club", -73.997812, 40.739128)); - venues.add(new Venue("City Bakery ", -73.992491, 40.738673)); - venues.add(new Venue("Splash Bar", -73.992491, 40.738673)); - venues.add(new Venue("Momofuku Milk Bar", -73.985839, 40.731698)); - venues.add(new Venue("Shake Shack", -73.98820, 40.74164)); - venues.add(new Venue("Penn Station", -73.99408, 40.75057)); - venues.add(new Venue("Empire State Building", -73.98602, 40.74894)); - venues.add(new Venue("Ulaanbaatar, Mongolia", 106.9154, 47.9245)); - venues.add(maplewoodNJ()); - - return venues; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestMongoConfiguration.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestMongoConfiguration.java deleted file mode 100644 index a7ab1c99fe..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TestMongoConfiguration.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017-2021 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.Collections; -import java.util.List; -import java.util.Set; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.test.util.MongoTestUtils; - -import com.mongodb.client.MongoClient; - -public class TestMongoConfiguration extends AbstractMongoClientConfiguration { - - @Override - public String getDatabaseName() { - return "database"; - } - - @Primary - @Bean - @Override - public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory databaseFactory, - MongoCustomConversions customConversions, MongoMappingContext mappingContext) { - return super.mappingMongoConverter(databaseFactory, customConversions, mappingContext); - } - - @Override - @Bean - public MongoClient mongoClient() { - return MongoTestUtils.client(); - } - - @Override - public String getMappingBasePackage() { - return MongoMappingContext.class.getPackage().getName(); - } - - @Override - public MongoCustomConversions customConversions() { - - List> converters = new ArrayList<>(2); - converters.add(new org.springframework.data.mongodb.core.PersonReadConverter()); - converters.add(new org.springframework.data.mongodb.core.PersonWriteConverter()); - return new MongoCustomConversions(converters); - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Trade.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Trade.java deleted file mode 100644 index 5696ce17d8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Trade.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2010-2021 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; - -public class Trade { - - private String ticker; - - private long quantity; - - private double price; - - private String orderType; - - public String getOrderType() { - return orderType; - } - - public void setOrderType(String orderType) { - this.orderType = orderType; - } - - public double getPrice() { - return price; - } - - public void setPrice(double price) { - this.price = price; - } - - public long getQuantity() { - return quantity; - } - - public void setQuantity(long quantity) { - this.quantity = quantity; - } - - public String getTicker() { - return ticker; - } - - public void setTicker(String ticker) { - this.ticker = ticker; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TradeBatch.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TradeBatch.java deleted file mode 100644 index 18c7331bb9..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TradeBatch.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.springframework.data.mongodb.core; - -import java.util.List; - -public class TradeBatch { - - private String batchId; - - private Trade[] trades; - - private List tradeList; - - public String getBatchId() { - return batchId; - } - - public void setBatchId(String batchId) { - this.batchId = batchId; - } - - public Trade[] getTrades() { - return trades; - } - - public void setTrades(Trade[] trades) { - this.trades = trades; - } - - public List getTradeList() { - return tradeList; - } - - public void setTradeList(List tradeList) { - this.tradeList = tradeList; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java deleted file mode 100644 index 4d1d5a4264..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2020-2021 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 static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.CodecRegistryProvider; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.convert.UpdateMapper; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; - -import com.mongodb.MongoClientSettings; - -/** - * Unit test for {@link com.mongodb.internal.operation.UpdateOperation}. - * - * @author Christoph Strobl - */ -class UpdateOperationsUnitTests { - - static final Document SHARD_KEY = new Document("country", "AT").append("userid", "4230"); - static final Document SOURCE_DOC = appendShardKey(new Document("_id", "id-1")); - - MongoMappingContext mappingContext = new MongoMappingContext(); - MongoConverter mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); - QueryMapper queryMapper = new QueryMapper(mongoConverter); - UpdateMapper updateMapper = new UpdateMapper(mongoConverter); - EntityOperations entityOperations = new EntityOperations(mappingContext); - PropertyOperations propertyOperations = new PropertyOperations(mappingContext); - - ExtendedQueryOperations queryOperations = new ExtendedQueryOperations(queryMapper, updateMapper, entityOperations, propertyOperations, - MongoClientSettings::getDefaultCodecRegistry); - - @Test // DATAMONGO-2341 - void appliesShardKeyToFilter() { - - Document sourceFilter = new Document("name", "kaladin"); - assertThat(shardedFilter(sourceFilter, ShardedEntityWithNonDefaultShardKey.class, null)) - .isEqualTo(appendShardKey(sourceFilter)); - } - - @Test - void applyShardKeyDoesNotAlterSourceFilter() { - - Document sourceFilter = new Document("name", "kaladin"); - shardedFilter(sourceFilter, ShardedEntityWithNonDefaultShardKey.class, null); - assertThat(sourceFilter).isEqualTo(new Document("name", "kaladin")); - } - - @Test // DATAMONGO-2341 - void appliesExistingShardKeyToFilter() { - - Document sourceFilter = new Document("name", "kaladin"); - Document existing = new Document("country", "GB").append("userid", "007"); - - assertThat(shardedFilter(sourceFilter, ShardedEntityWithNonDefaultShardKey.class, existing)) - .isEqualTo(new Document(existing).append("name", "kaladin")); - } - - @Test // DATAMONGO-2341 - void recognizesExistingShardKeyInFilter() { - - Document sourceFilter = appendShardKey(new Document("name", "kaladin")); - - assertThat(queryOperations.replaceSingleContextFor(SOURCE_DOC).requiresShardKey(sourceFilter, - entityOf(ShardedEntityWithNonDefaultShardKey.class))).isFalse(); - } - - @Test // DATAMONGO-2341 - void recognizesIdPropertyAsShardKey() { - - Document sourceFilter = new Document("_id", "id-1"); - - assertThat(queryOperations.replaceSingleContextFor(SOURCE_DOC).requiresShardKey(sourceFilter, - entityOf(ShardedEntityWithDefaultShardKey.class))).isFalse(); - } - - @Test // DATAMONGO-2341 - void returnsMappedShardKey() { - - queryOperations.replaceSingleContextFor(SOURCE_DOC) - .getMappedShardKeyFields(entityOf(ShardedEntityWithDefaultShardKey.class)) - .containsAll(Arrays.asList("country", "userid")); - } - - @NonNull - private Document shardedFilter(Document sourceFilter, Class entity, Document existing) { - return queryOperations.replaceSingleContextFor(SOURCE_DOC).applyShardKey(entity, sourceFilter, existing); - } - - private static Document appendShardKey(Document source) { - - Document target = new Document(source); - target.putAll(SHARD_KEY); - return target; - } - - MongoPersistentEntity entityOf(Class type) { - return mappingContext.getPersistentEntity(type); - } - - class ExtendedQueryOperations extends QueryOperations { - - ExtendedQueryOperations(QueryMapper queryMapper, UpdateMapper updateMapper, EntityOperations entityOperations, PropertyOperations propertyOperations, - CodecRegistryProvider codecRegistryProvider) { - super(queryMapper, updateMapper, entityOperations, propertyOperations, codecRegistryProvider); - } - - @NonNull - private ExtendedUpdateContext replaceSingleContextFor(Document source) { - return new ExtendedUpdateContext(MappedDocument.of(source), true); - } - - MongoPersistentEntity entityOf(Class type) { - return mappingContext.getPersistentEntity(type); - } - - class ExtendedUpdateContext extends UpdateContext { - - ExtendedUpdateContext(MappedDocument update, boolean upsert) { - super(update, upsert); - } - - Document applyShardKey(Class domainType, Document filter, @Nullable Document existing) { - return applyShardKey(entityOf(domainType), filter, existing); - } - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java deleted file mode 100644 index 1941c505f4..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2010-2021 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; - -public class User { - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((accountName == null) ? 0 : accountName.hashCode()); - result = prime * result + ((userName == null) ? 0 : userName.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - User other = (User) obj; - if (accountName == null) { - if (other.accountName != null) - return false; - } else if (!accountName.equals(other.accountName)) - return false; - if (userName == null) { - if (other.userName != null) - return false; - } else if (!userName.equals(other.userName)) - return false; - return true; - } - - private String accountName; - - private String userName; - - public String getAccountName() { - return accountName; - } - - public void setAccountName(String accountName) { - this.accountName = accountName; - } - - public String getUserName() { - return userName; - } - - public void setUserName(String userName) { - this.userName = userName; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java deleted file mode 100644 index 0ab13b3939..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2010-2021 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.Arrays; - -import org.joda.time.LocalDate; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.mongodb.core.mapping.Document; - -@Document("newyork") -public class Venue { - - @Id private String id; - private String name; - private double[] location; - private LocalDate openingDate; - - @PersistenceConstructor - Venue(String name, double[] location) { - super(); - this.name = name; - this.location = location; - } - - public Venue(String name, double x, double y) { - super(); - this.name = name; - this.location = new double[] { x, y }; - } - - public String getName() { - return name; - } - - public double[] getLocation() { - return location; - } - - public LocalDate getOpeningDate() { - return openingDate; - } - - public void setOpeningDate(LocalDate openingDate) { - this.openingDate = openingDate; - } - - @Override - public String toString() { - return "Venue [id=" + id + ", name=" + name + ", location=" + Arrays.toString(location) + "]"; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/VerySpecialDoc.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/VerySpecialDoc.java deleted file mode 100644 index abf0369fc5..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/VerySpecialDoc.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.springframework.data.mongodb.core; - -public class VerySpecialDoc extends SpecialDoc { - int verySpecialValue; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java deleted file mode 100644 index b2f2bd611d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2020-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; - -/** - * Unit tests for {@link AddFieldsOperation}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -class AddFieldsOperationUnitTests { - - @Test // DATAMONGO-2363 - void raisesErrorOnNullField() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new AddFieldsOperation(null, "value")); - } - - @Test // DATAMONGO-2363 - void rendersFieldReferenceCorrectly() { - - assertThat(new AddFieldsOperation("name", "value").toPipelineStages(contextFor(Scores.class))) - .containsExactly(Document.parse("{\"$addFields\" : {\"name\":\"value\"}}")); - } - - @Test // DATAMONGO-2363 - void rendersMultipleEntriesCorrectly() { - - assertThat(new AddFieldsOperation("name", "value").addField("field-2", "value2") - .toPipelineStages(contextFor(Scores.class))) - .containsExactly(Document.parse("{\"$addFields\" : {\"name\":\"value\", \"field-2\":\"value2\"}}")); - } - - @Test // DATAMONGO-2363 - void rendersMappedFieldReferenceCorrectly() { - - assertThat(new AddFieldsOperation("student", "value").toPipelineStages(contextFor(ScoresWithMappedField.class))) - .containsExactly(Document.parse("{\"$addFields\" : {\"student_name\":\"value\"}}")); - } - - @Test // DATAMONGO-2363 - void rendersNestedMappedFieldReferenceCorrectly() { - - assertThat(new AddFieldsOperation("scoresWithMappedField.student", "value") - .toPipelineStages(contextFor(ScoresWrapper.class))) - .containsExactly(Document.parse("{\"$addFields\" : {\"scoresWithMappedField.student_name\":\"value\"}}")); - } - - @Test // DATAMONGO-2363 - void rendersTargetValueFieldReferenceCorrectly() { - - assertThat(new AddFieldsOperation("name", Fields.field("value")).toPipelineStages(contextFor(Scores.class))) - .containsExactly(Document.parse("{\"$addFields\" : {\"name\":\"$value\"}}")); - } - - @Test // DATAMONGO-2363 - void rendersMappedTargetValueFieldReferenceCorrectly() { - - assertThat(new AddFieldsOperation("student", Fields.field("homework")) - .toPipelineStages(contextFor(ScoresWithMappedField.class))) - .containsExactly(Document.parse("{\"$addFields\" : {\"student_name\":\"$home_work\"}}")); - } - - @Test // DATAMONGO-2363 - void rendersNestedMappedTargetValueFieldReferenceCorrectly() { - - assertThat(new AddFieldsOperation("scoresWithMappedField.student", Fields.field("scoresWithMappedField.homework")) - .toPipelineStages(contextFor(ScoresWrapper.class))) - .containsExactly(Document.parse( - "{\"$addFields\" : {\"scoresWithMappedField.student_name\":\"$scoresWithMappedField.home_work\"}}")); - } - - @Test // DATAMONGO-2363 - void appliesSpelExpressionCorrectly() { - - AddFieldsOperation operation = AddFieldsOperation.builder().addField("totalHomework") - .withValueOfExpression("sum(homework) * [0]", 2) // - .build(); - - assertThat(operation.toPipelineStages(contextFor(ScoresWrapper.class))).contains( - Document.parse("{\"$addFields\" : {\"totalHomework\": { $multiply : [{ \"$sum\" : [\"$homework\"] }, 2] }}}")); - } - - @Test // DATAMONGO-2363 - void rendersTargetValueExpressionCorrectly() { - - assertThat(AddFieldsOperation.builder().addField("totalHomework") - .withValueOf(ArithmeticOperators.valueOf("homework").sum()).build().toPipelineStages(contextFor(Scores.class))) - .containsExactly(Document.parse("{\"$addFields\" : {\"totalHomework\": { \"$sum\" : \"$homework\" }}}")); - } - - @Test // DATAMONGO-2363 - void exposesFieldsCorrectly() { - - ExposedFields fields = AddFieldsOperation.builder().addField("totalHomework").withValue("A+") // - .addField("totalQuiz").withValue("B-") // - .addField("computed").withValueOfExpression("totalHomework").build().getFields(); - - assertThat(fields.getField("totalHomework")).isNotNull(); - assertThat(fields.getField("totalQuiz")).isNotNull(); - assertThat(fields.getField("computed")).isNotNull(); - assertThat(fields.getField("does-not-exist")).isNull(); - } - - private static AggregationOperationContext contextFor(@Nullable Class type) { - - if (type == null) { - return Aggregation.DEFAULT_CONTEXT; - } - - MappingMongoConverter mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, - new MongoMappingContext()); - mongoConverter.afterPropertiesSet(); - - return new TypeBasedAggregationOperationContext(type, mongoConverter.getMappingContext(), - new QueryMapper(mongoConverter)).continueOnMissingFieldReference(); - } - - static class Scores { - - String student; - List homework; - } - - static class ScoresWithMappedField { - - @Field("student_name") String student; - @Field("home_work") List homework; - } - - static class ScoresWrapper { - - Scores scores; - ScoresWithMappedField scoresWithMappedField; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationOptionsTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationOptionsTests.java deleted file mode 100644 index 372f2f1e97..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationOptionsTests.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2014-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link AggregationOptions}. - * - * @author Thomas Darimont - * @author Mark Paluch - * @author Christoph Strobl - * @author Yadhukrishna S Pai - * @since 1.6 - */ -class AggregationOptionsTests { - - private final Document dummyHint = new Document("dummyField", 1); - AggregationOptions aggregationOptions; - - @BeforeEach - void setup() { - aggregationOptions = newAggregationOptions().explain(true) // - .cursorBatchSize(1) // - .allowDiskUse(true) // - .comment("hola!") // - .hint(dummyHint) // - .build(); - } - - @Test // DATAMONGO-960, DATAMONGO-1836 - void aggregationOptionsBuilderShouldSetOptionsAccordingly() { - - assertThat(aggregationOptions.isAllowDiskUse()).isTrue(); - assertThat(aggregationOptions.isExplain()).isTrue(); - assertThat(aggregationOptions.getCursor()).contains(new Document("batchSize", 1)); - assertThat(aggregationOptions.getHint()).contains(dummyHint); - } - - @Test // DATAMONGO-1637, DATAMONGO-2153, DATAMONGO-1836 - void shouldInitializeFromDocument() { - - Document document = new Document(); - document.put("cursor", new Document("batchSize", 1)); - document.put("explain", true); - document.put("allowDiskUse", true); - document.put("comment", "hola!"); - document.put("hint", dummyHint); - - aggregationOptions = AggregationOptions.fromDocument(document); - - assertThat(aggregationOptions.isAllowDiskUse()).isTrue(); - assertThat(aggregationOptions.isExplain()).isTrue(); - assertThat(aggregationOptions.getCursor()).contains(new Document("batchSize", 1)); - assertThat(aggregationOptions.getCursorBatchSize()).isEqualTo(1); - assertThat(aggregationOptions.getComment()).contains("hola!"); - assertThat(aggregationOptions.getHint()).contains(dummyHint); - } - - @Test // DATAMONGO-960, DATAMONGO-2153, DATAMONGO-1836 - void aggregationOptionsToString() { - - assertThat(aggregationOptions.toDocument()).isEqualTo(Document - .parse("{ " + "\"allowDiskUse\" : true , " + "\"explain\" : true , " + "\"cursor\" : { \"batchSize\" : 1}, " - + "\"comment\": \"hola!\", " + "\"hint\" : { \"dummyField\" : 1}" + "}")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java deleted file mode 100644 index 2cf1b3fbb5..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java +++ /dev/null @@ -1,2270 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.springframework.data.domain.Sort.Direction.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.core.aggregation.Fields.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import lombok.Builder; - -import java.io.BufferedInputStream; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Scanner; - -import org.assertj.core.data.Offset; -import org.bson.Document; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.LocalDateTime; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.ClassPathResource; -import org.springframework.dao.DataAccessException; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.geo.Box; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.CollectionCallback; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.TestEntities; -import org.springframework.data.mongodb.core.Venue; -import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry; -import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.Granularities; -import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.MongoVersion; -import org.springframework.data.mongodb.test.util.Template; -import org.springframework.data.util.CloseableIterator; - -import com.mongodb.MongoException; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.InsertOneModel; -import com.mongodb.client.model.WriteModel; - -/** - * Tests for {@link MongoTemplate#aggregate(Aggregation, Class, Class)}. - * - * @author Tobias Trelle - * @author Thomas Darimont - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - * @author Nikolay Bogdanov - * @author Maninder Singh - * @author Sergey Shcherbakov - * @author Minsu Kim - */ -@ExtendWith(MongoTemplateExtension.class) -public class AggregationTests { - - private static final String INPUT_COLLECTION = "aggregation_test_collection"; - private static final Logger LOGGER = LoggerFactory.getLogger(AggregationTests.class); - - private static boolean initialized = false; - - @Template // - private static MongoTestTemplate mongoTemplate; - - @BeforeEach - void setUp() { - - cleanDb(); - initSampleDataIfNecessary(); - } - - @AfterEach - void cleanUp() { - cleanDb(); - } - - private void cleanDb() { - - mongoTemplate.flush(Product.class, UserWithLikes.class, DATAMONGO753.class, Data.class, DATAMONGO788.class, - User.class, Person.class, Reservation.class, Venue.class, MeterData.class, LineItem.class, InventoryItem.class, - Sales.class, Sales2.class, Employee.class, Art.class, Venue.class); - - mongoTemplate.dropCollection(INPUT_COLLECTION); - mongoTemplate.dropCollection("personQueryTemp"); - } - - /** - * Imports the sample dataset (zips.json) if necessary (e.g. if it doesn't exist yet). The dataset can originally be - * found on the mongodb aggregation framework example website: - * - * @see MongoDB Aggregation Examples - */ - private void initSampleDataIfNecessary() { - - if (!initialized) { - - mongoTemplate.dropCollection(ZipInfo.class); - mongoTemplate.execute(ZipInfo.class, new CollectionCallback() { - - @Override - public Void doInCollection(MongoCollection collection) throws MongoException, DataAccessException { - - List> docs = new ArrayList<>(); - Scanner scanner = null; - try { - scanner = new Scanner(new BufferedInputStream(new ClassPathResource("zips.json").getInputStream())); - while (scanner.hasNextLine()) { - String zipInfoRecord = scanner.nextLine(); - docs.add(new InsertOneModel<>(Document.parse(zipInfoRecord))); - } - } catch (Exception e) { - if (scanner != null) { - scanner.close(); - } - throw new RuntimeException("Could not load mongodb sample dataset!", e); - } - - collection.bulkWrite(docs); - return null; - } - }); - - long count = mongoTemplate.count(new Query(), ZipInfo.class); - assertThat(count).isEqualTo(29467L); - - initialized = true; - } - } - - @Test // DATAMONGO-586 - void shouldHandleMissingInputCollection() { - assertThatIllegalArgumentException() - .isThrownBy(() -> mongoTemplate.aggregate(newAggregation(), (String) null, TagCount.class)); - } - - @Test // DATAMONGO-586 - void shouldHandleMissingAggregationPipeline() { - assertThatIllegalArgumentException() - .isThrownBy(() -> mongoTemplate.aggregate(null, INPUT_COLLECTION, TagCount.class)); - } - - @Test // DATAMONGO-586 - void shouldHandleMissingEntityClass() { - assertThatIllegalArgumentException() - .isThrownBy(() -> mongoTemplate.aggregate(newAggregation(), INPUT_COLLECTION, null)); - } - - @Test // DATAMONGO-586 - void shouldAggregate() { - - createTagDocuments(); - - Aggregation agg = newAggregation( // - project("tags"), // - unwind("tags"), // - group("tags") // - .count().as("n"), // - project("n") // - .and("tag").previousOperation(), // - sort(DESC, "n") // - ); - - AggregationResults results = mongoTemplate.aggregate(agg, INPUT_COLLECTION, TagCount.class); - - assertThat(results).isNotNull(); - - List tagCount = results.getMappedResults(); - - assertThat(tagCount).isNotNull(); - assertThat(tagCount.size()).isEqualTo(3); - - assertTagCount("spring", 3, tagCount.get(0)); - assertTagCount("mongodb", 2, tagCount.get(1)); - assertTagCount("nosql", 1, tagCount.get(2)); - } - - @Test // DATAMONGO-1637 - void shouldAggregateAndStream() { - - createTagDocuments(); - - Aggregation agg = newAggregation( // - project("tags"), // - unwind("tags"), // - group("tags") // - .count().as("n"), // - project("n") // - .and("tag").previousOperation(), // - sort(DESC, "n") // - ).withOptions(new AggregationOptions(true, false, 1)); - - CloseableIterator iterator = mongoTemplate.aggregateStream(agg, INPUT_COLLECTION, TagCount.class); - - assertThat(iterator).isNotNull(); - List tagCount = toList(iterator); - iterator.close(); - - assertThat(tagCount).isNotNull(); - assertThat(tagCount.size()).isEqualTo(3); - - assertTagCount("spring", 3, tagCount.get(0)); - assertTagCount("mongodb", 2, tagCount.get(1)); - assertTagCount("nosql", 1, tagCount.get(2)); - } - - @Test // DATAMONGO-586 - void shouldAggregateEmptyCollection() { - - Aggregation aggregation = newAggregation(// - project("tags"), // - unwind("tags"), // - group("tags") // - .count().as("n"), // - project("n") // - .and("tag").previousOperation(), // - sort(DESC, "n") // - ); - - AggregationResults results = mongoTemplate.aggregate(aggregation, INPUT_COLLECTION, TagCount.class); - - assertThat(results).isNotNull(); - - List tagCount = results.getMappedResults(); - - assertThat(tagCount).isNotNull(); - assertThat(tagCount.size()).isEqualTo(0); - } - - @Test // DATAMONGO-1637 - void shouldAggregateEmptyCollectionAndStream() { - - Aggregation aggregation = newAggregation(// - project("tags"), // - unwind("tags"), // - group("tags") // - .count().as("n"), // - project("n") // - .and("tag").previousOperation(), // - sort(DESC, "n") // - ); - - CloseableIterator results = mongoTemplate.aggregateStream(aggregation, INPUT_COLLECTION, TagCount.class); - - assertThat(results).isNotNull(); - - List tagCount = toList(results); - results.close(); - - assertThat(tagCount.size()).isEqualTo(0); - } - - @Test // DATAMONGO-1391 - void shouldUnwindWithIndex() { - - MongoCollection coll = mongoTemplate.getCollection(INPUT_COLLECTION); - - coll.insertOne(createDocument("Doc1", "spring", "mongodb", "nosql")); - coll.insertOne(createDocument("Doc2")); - - Aggregation agg = newAggregation( // - project("tags"), // - unwind("tags", "n"), // - project("n") // - .and("tag").previousOperation(), // - sort(DESC, "n") // - ); - - AggregationResults results = mongoTemplate.aggregate(agg, INPUT_COLLECTION, TagCount.class); - - assertThat(results).isNotNull(); - - List tagCount = results.getMappedResults(); - - assertThat(tagCount).isNotNull(); - assertThat(tagCount.size()).isEqualTo(3); - } - - @Test // DATAMONGO-1391 - void shouldUnwindPreserveEmpty() { - - MongoCollection coll = mongoTemplate.getCollection(INPUT_COLLECTION); - - coll.insertOne(createDocument("Doc1", "spring", "mongodb", "nosql")); - coll.insertOne(createDocument("Doc2")); - - Aggregation agg = newAggregation( // - project("tags"), // - unwind("tags", "n", true), // - sort(DESC, "n") // - ); - - AggregationResults results = mongoTemplate.aggregate(agg, INPUT_COLLECTION, Document.class); - - assertThat(results).isNotNull(); - - List tagCount = results.getMappedResults(); - - assertThat(tagCount).isNotNull(); - assertThat(tagCount.size()).isEqualTo(4); - assertThat(tagCount.get(0)).containsEntry("n", 2L); - assertThat(tagCount.get(3)).containsEntry("n", null); - } - - @Test // DATAMONGO-586 - void shouldDetectResultMismatch() { - - createTagDocuments(); - - Aggregation aggregation = newAggregation( // - project("tags"), // - unwind("tags"), // - group("tags") // - .count().as("count"), // count field not present - limit(2) // - ); - - AggregationResults results = mongoTemplate.aggregate(aggregation, INPUT_COLLECTION, TagCount.class); - - assertThat(results).isNotNull(); - - List tagCount = results.getMappedResults(); - - assertThat(tagCount).isNotNull(); - assertThat(tagCount.size()).isEqualTo(2); - assertTagCount(null, 0, tagCount.get(0)); - assertTagCount(null, 0, tagCount.get(1)); - } - - @Test // DATAMONGO-1637 - void shouldDetectResultMismatchWhileStreaming() { - - createTagDocuments(); - - Aggregation aggregation = newAggregation( // - project("tags"), // - unwind("tags"), // - group("tags") // - .count().as("count"), // count field not present - limit(2) // - ); - - CloseableIterator results = mongoTemplate.aggregateStream(aggregation, INPUT_COLLECTION, TagCount.class); - - assertThat(results).isNotNull(); - - List tagCount = toList(results); - results.close(); - - assertThat(tagCount.size()).isEqualTo(2); - assertTagCount(null, 0, tagCount.get(0)); - assertTagCount(null, 0, tagCount.get(1)); - } - - @Test // DATAMONGO-586 - void complexAggregationFrameworkUsageLargestAndSmallestCitiesByState() { - /* - //complex mongodb aggregation framework example from https://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state - db.zipInfo.aggregate( - { - $group: { - _id: { - state: '$state', - city: '$city' - }, - pop: { - $sum: '$pop' - } - } - }, - { - $sort: { - pop: 1, - '_id.state': 1, - '_id.city': 1 - } - }, - { - $group: { - _id: '$_id.state', - biggestCity: { - $last: '$_id.city' - }, - biggestPop: { - $last: '$pop' - }, - smallestCity: { - $first: '$_id.city' - }, - smallestPop: { - $first: '$pop' - } - } - }, - { - $project: { - _id: 0, - state: '$_id', - biggestCity: { - name: '$biggestCity', - pop: '$biggestPop' - }, - smallestCity: { - name: '$smallestCity', - pop: '$smallestPop' - } - } - }, - { - $sort: { - state: 1 - } - } - ) - */ - - TypedAggregation aggregation = newAggregation(ZipInfo.class, // - group("state", "city").sum("population").as("pop"), // - sort(ASC, "pop", "state", "city"), // - group("state") // - .last("city").as("biggestCity") // - .last("pop").as("biggestPop") // - .first("city").as("smallestCity") // - .first("pop").as("smallestPop"), // - project() // - .and("state").previousOperation() // - .and("biggestCity").nested(bind("name", "biggestCity").and("population", "biggestPop")) // - .and("smallestCity").nested(bind("name", "smallestCity").and("population", "smallestPop")), // - sort(ASC, "state") // - ); - - assertThat(aggregation).isNotNull(); - assertThat(aggregation.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(aggregation, ZipInfoStats.class); - assertThat(result).isNotNull(); - assertThat(result.getMappedResults()).isNotNull(); - assertThat(result.getMappedResults().size()).isEqualTo(51); - - ZipInfoStats firstZipInfoStats = result.getMappedResults().get(0); - assertThat(firstZipInfoStats).isNotNull(); - assertThat(firstZipInfoStats.id).isNull(); - assertThat(firstZipInfoStats.state).isEqualTo("AK"); - assertThat(firstZipInfoStats.smallestCity).isNotNull(); - assertThat(firstZipInfoStats.smallestCity.name).isEqualTo("CHEVAK"); - assertThat(firstZipInfoStats.smallestCity.population).isEqualTo(0); - assertThat(firstZipInfoStats.biggestCity).isNotNull(); - assertThat(firstZipInfoStats.biggestCity.name).isEqualTo("ANCHORAGE"); - assertThat(firstZipInfoStats.biggestCity.population).isEqualTo(183987); - - ZipInfoStats lastZipInfoStats = result.getMappedResults().get(50); - assertThat(lastZipInfoStats).isNotNull(); - assertThat(lastZipInfoStats.id).isNull(); - assertThat(lastZipInfoStats.state).isEqualTo("WY"); - assertThat(lastZipInfoStats.smallestCity).isNotNull(); - assertThat(lastZipInfoStats.smallestCity.name).isEqualTo("LOST SPRINGS"); - assertThat(lastZipInfoStats.smallestCity.population).isEqualTo(6); - assertThat(lastZipInfoStats.biggestCity).isNotNull(); - assertThat(lastZipInfoStats.biggestCity.name).isEqualTo("CHEYENNE"); - assertThat(lastZipInfoStats.biggestCity.population).isEqualTo(70185); - } - - @Test // DATAMONGO-586 - void findStatesWithPopulationOver10MillionAggregationExample() { - /* - //complex mongodb aggregation framework example from - https://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state - - db.zipcodes.aggregate( - { - $group: { - _id:"$state", - totalPop:{ $sum:"$pop"} - } - }, - { - $sort: { _id: 1, "totalPop": 1 } - }, - { - $match: { - totalPop: { $gte:10*1000*1000 } - } - } - ) - */ - - TypedAggregation agg = newAggregation(ZipInfo.class, // - group("state") // - .sum("population").as("totalPop"), // - sort(ASC, previousOperation(), "totalPop"), // - match(where("totalPop").gte(10 * 1000 * 1000)) // - ); - - assertThat(agg).isNotNull(); - assertThat(agg.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(agg, StateStats.class); - assertThat(result).isNotNull(); - assertThat(result.getMappedResults()).isNotNull(); - assertThat(result.getMappedResults().size()).isEqualTo(7); - - StateStats stateStats = result.getMappedResults().get(0); - assertThat(stateStats).isNotNull(); - assertThat(stateStats.id).isEqualTo("CA"); - assertThat(stateStats.state).isNull(); - assertThat(stateStats.totalPopulation).isEqualTo(29760021); - } - - /** - * @see MongoDB Aggregation - * Framework: $cond - */ - @Test // DATAMONGO-861 - void aggregationUsingConditionalProjectionToCalculateDiscount() { - - /* - db.inventory.aggregate( - [ - { - $project: - { - item: 1, - discount: - { - $cond: { if: { $gte: [ "$qty", 250 ] }, then: 30, else: 20 } - } - } - } - ] - ) - */ - - mongoTemplate.insert(new InventoryItem(1, "abc1", 300)); - mongoTemplate.insert(new InventoryItem(2, "abc2", 200)); - mongoTemplate.insert(new InventoryItem(3, "xyz1", 250)); - - TypedAggregation aggregation = newAggregation(InventoryItem.class, // - project("item") // - .and("discount")// - .applyCondition(ConditionalOperators.Cond.newBuilder().when(Criteria.where("qty").gte(250)) // - .then(30) // - .otherwise(20))); - - assertThat(aggregation.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(aggregation, Document.class); - assertThat(result.getMappedResults().size()).isEqualTo(3); - - Document first = result.getMappedResults().get(0); - assertThat(first.get("_id")).isEqualTo((Object) 1); - assertThat(first.get("discount")).isEqualTo((Object) 30); - - Document second = result.getMappedResults().get(1); - assertThat(second.get("_id")).isEqualTo((Object) 2); - assertThat(second.get("discount")).isEqualTo((Object) 20); - - Document third = result.getMappedResults().get(2); - assertThat(third.get("_id")).isEqualTo((Object) 3); - assertThat(third.get("discount")).isEqualTo((Object) 30); - } - - /** - * @see MongoDB Aggregation - * Framework: $ifNull - */ - @Test // DATAMONGO-861 - void aggregationUsingIfNullToProjectSaneDefaults() { - - /* - db.inventory.aggregate( - [ - { - $project: { - item: 1, - description: { $ifNull: [ "$description", "Unspecified" ] } - } - } - ] - ) - */ - - mongoTemplate.insert(new InventoryItem(1, "abc1", "product 1", 300)); - mongoTemplate.insert(new InventoryItem(2, "abc2", 200)); - mongoTemplate.insert(new InventoryItem(3, "xyz1", 250)); - - TypedAggregation aggregation = newAggregation(InventoryItem.class, // - project("item") // - .and(ConditionalOperators.ifNull("description").then("Unspecified")) // - .as("description")// - ); - - assertThat(aggregation.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(aggregation, Document.class); - assertThat(result.getMappedResults().size()).isEqualTo(3); - - Document first = result.getMappedResults().get(0); - assertThat(first.get("_id")).isEqualTo((Object) 1); - assertThat(first.get("description")).isEqualTo((Object) "product 1"); - - Document second = result.getMappedResults().get(1); - assertThat(second.get("_id")).isEqualTo((Object) 2); - assertThat(second.get("description")).isEqualTo((Object) "Unspecified"); - } - - @Test // DATAMONGO-861 - void aggregationUsingConditionalProjection() { - - TypedAggregation aggregation = newAggregation(ZipInfo.class, // - project() // - .and("largePopulation")// - .applyCondition(ConditionalOperators.when(Criteria.where("population").gte(20000)) // - .then(true) // - .otherwise(false)) // - .and("population").as("population")); - - assertThat(aggregation).isNotNull(); - assertThat(aggregation.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(aggregation, Document.class); - assertThat(result.getMappedResults().size()).isEqualTo(29467); - - Document firstZipInfoStats = result.getMappedResults().get(0); - assertThat(firstZipInfoStats.get("largePopulation")).isEqualTo((Object) false); - assertThat(firstZipInfoStats.get("population")).isEqualTo((Object) 6055); - } - - @Test // DATAMONGO-861 - void aggregationUsingNestedConditionalProjection() { - - TypedAggregation aggregation = newAggregation(ZipInfo.class, // - project() // - .and("size")// - .applyCondition(ConditionalOperators.when(Criteria.where("population").gte(20000)) // - .then( - ConditionalOperators.when(Criteria.where("population").gte(200000)).then("huge").otherwise("small")) // - .otherwise("small")) // - .and("population").as("population")); - - assertThat(aggregation).isNotNull(); - assertThat(aggregation.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(aggregation, Document.class); - assertThat(result.getMappedResults().size()).isEqualTo(29467); - - Document firstZipInfoStats = result.getMappedResults().get(0); - assertThat(firstZipInfoStats.get("size")).isEqualTo((Object) "small"); - assertThat(firstZipInfoStats.get("population")).isEqualTo((Object) 6055); - } - - @Test // DATAMONGO-861 - void aggregationUsingIfNullProjection() { - - mongoTemplate.insert(new LineItem("id", "caption", 0)); - mongoTemplate.insert(new LineItem("idonly", null, 0)); - - TypedAggregation aggregation = newAggregation(LineItem.class, // - project("id") // - .and("caption")// - .applyCondition(ConditionalOperators.ifNull("caption").then("unknown")), - sort(ASC, "id")); - - assertThat(aggregation.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(aggregation, Document.class); - assertThat(result.getMappedResults().size()).isEqualTo(2); - - Document id = result.getMappedResults().get(0); - assertThat((String) id.get("caption")).isEqualTo("caption"); - - Document idonly = result.getMappedResults().get(1); - assertThat((String) idonly.get("caption")).isEqualTo("unknown"); - } - - @Test // DATAMONGO-861 - void aggregationUsingIfNullReplaceWithFieldReferenceProjection() { - - mongoTemplate.insert(new LineItem("id", "caption", 0)); - mongoTemplate.insert(new LineItem("idonly", null, 0)); - - TypedAggregation aggregation = newAggregation(LineItem.class, // - project("id") // - .and("caption")// - .applyCondition(ConditionalOperators.ifNull("caption").thenValueOf("id")), - sort(ASC, "id")); - - assertThat(aggregation.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(aggregation, Document.class); - assertThat(result.getMappedResults().size()).isEqualTo(2); - - Document id = result.getMappedResults().get(0); - assertThat((String) id.get("caption")).isEqualTo("caption"); - - Document idonly = result.getMappedResults().get(1); - assertThat((String) idonly.get("caption")).isEqualTo("idonly"); - } - - @Test // DATAMONGO-861 - void shouldAllowGroupingUsingConditionalExpressions() { - - mongoTemplate.dropCollection(CarPerson.class); - - CarPerson person1 = new CarPerson("first1", "last1", new CarDescriptor.Entry("MAKE1", "MODEL1", 2000), - new CarDescriptor.Entry("MAKE1", "MODEL2", 2001)); - - CarPerson person2 = new CarPerson("first2", "last2", new CarDescriptor.Entry("MAKE3", "MODEL4", 2014)); - CarPerson person3 = new CarPerson("first3", "last3", new CarDescriptor.Entry("MAKE2", "MODEL5", 2015)); - - mongoTemplate.save(person1); - mongoTemplate.save(person2); - mongoTemplate.save(person3); - - TypedAggregation agg = Aggregation.newAggregation(CarPerson.class, - unwind("descriptors.carDescriptor.entries"), // - project() // - .and(ConditionalOperators // - .when(Criteria.where("descriptors.carDescriptor.entries.make").is("MAKE1")).then("good") - .otherwise("meh")) - .as("make") // - .and("descriptors.carDescriptor.entries.model").as("model") // - .and("descriptors.carDescriptor.entries.year").as("year"), // - group("make").avg(ConditionalOperators // - .when(Criteria.where("year").gte(2012)) // - .then(1) // - .otherwise(9000)) // - .as("score"), - sort(ASC, "score")); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - - assertThat(result.getMappedResults()).hasSize(2); - - Document meh = result.getMappedResults().get(0); - assertThat((String) meh.get("_id")).isEqualTo("meh"); - assertThat(((Number) meh.get("score")).longValue()).isEqualTo(1L); - - Document good = result.getMappedResults().get(1); - assertThat((String) good.get("_id")).isEqualTo("good"); - assertThat(((Number) good.get("score")).longValue()).isEqualTo(9000L); - } - - @Test // DATAMONGO-1784, DATAMONGO-2264 - void shouldAllowSumUsingConditionalExpressions() { - - mongoTemplate.dropCollection(CarPerson.class); - - CarPerson person1 = new CarPerson("first1", "last1", new CarDescriptor.Entry("MAKE1", "MODEL1", 2000), - new CarDescriptor.Entry("MAKE1", "MODEL2", 2001)); - - CarPerson person2 = new CarPerson("first2", "last2", new CarDescriptor.Entry("MAKE3", "MODEL4", 2014)); - CarPerson person3 = new CarPerson("first3", "last3", new CarDescriptor.Entry("MAKE2", "MODEL5", 2015)); - - mongoTemplate.save(person1); - mongoTemplate.save(person2); - mongoTemplate.save(person3); - - TypedAggregation agg = Aggregation.newAggregation(CarPerson.class, - unwind("descriptors.carDescriptor.entries"), // - project() // - .and(ConditionalOperators // - .when(Criteria.where("descriptors.carDescriptor.entries.make").is("MAKE1")).then("good") - .otherwise("meh")) - .as("make") // - .and("descriptors.carDescriptor.entries.model").as("model") // - .and("descriptors.carDescriptor.entries.year").as("year"), // - group("make").sum(ConditionalOperators // - .when(Criteria.where("year").gte(2012)) // - .then(1) // - .otherwise(9000)).as("score"), - sort(ASC, "score")); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - - assertThat(result.getMappedResults()).hasSize(2); - - Document meh = result.getMappedResults().get(0); - assertThat(meh.get("_id")).isEqualTo("meh"); - assertThat(((Number) meh.get("score")).longValue()).isEqualTo(2L); - - Document good = result.getMappedResults().get(1); - assertThat(good.get("_id")).isEqualTo("good"); - assertThat(((Number) good.get("score")).longValue()).isEqualTo(18000L); - } - - /** - * @see Return - * the Five Most Common “Likes” - */ - @Test // DATAMONGO-586 - void returnFiveMostCommonLikesAggregationFrameworkExample() { - - createUserWithLikesDocuments(); - - TypedAggregation agg = createUsersWithCommonLikesAggregation(); - - assertThat(agg).isNotNull(); - assertThat(agg.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(agg, LikeStats.class); - assertThat(result).isNotNull(); - assertThat(result.getMappedResults()).isNotNull(); - assertThat(result.getMappedResults().size()).isEqualTo(5); - - assertLikeStats(result.getMappedResults().get(0), "a", 4); - assertLikeStats(result.getMappedResults().get(1), "b", 2); - assertLikeStats(result.getMappedResults().get(2), "c", 4); - assertLikeStats(result.getMappedResults().get(3), "d", 2); - assertLikeStats(result.getMappedResults().get(4), "e", 3); - } - - TypedAggregation createUsersWithCommonLikesAggregation() { - return newAggregation(UserWithLikes.class, // - unwind("likes"), // - group("likes").count().as("number"), // - sort(DESC, "number"), // - limit(5), // - sort(ASC, previousOperation()) // - ); - } - - @Test // DATAMONGO-586 - void arithmenticOperatorsInProjectionExample() { - - Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); - mongoTemplate.insert(product); - - TypedAggregation agg = newAggregation(Product.class, // - project("name", "netPrice") // - .and("netPrice").plus(1).as("netPricePlus1") // - .and("netPrice").minus(1).as("netPriceMinus1") // - .and("netPrice").multiply(2).as("netPriceMul2") // - .and("netPrice").divide(1.19).as("netPriceDiv119") // - .and("spaceUnits").mod(2).as("spaceUnitsMod2") // - .and("spaceUnits").plus("spaceUnits").as("spaceUnitsPlusSpaceUnits") // - .and("spaceUnits").minus("spaceUnits").as("spaceUnitsMinusSpaceUnits") // - .and("spaceUnits").multiply("spaceUnits").as("spaceUnitsMultiplySpaceUnits") // - .and("spaceUnits").divide("spaceUnits").as("spaceUnitsDivideSpaceUnits") // - .and("spaceUnits").mod("spaceUnits").as("spaceUnitsModSpaceUnits") // - ); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - List resultList = result.getMappedResults(); - - assertThat(resultList).isNotNull(); - assertThat((String) resultList.get(0).get("_id")).isEqualTo(product.id); - assertThat((String) resultList.get(0).get("name")).isEqualTo(product.name); - assertThat((Double) resultList.get(0).get("netPricePlus1")).isEqualTo(product.netPrice + 1); - assertThat((Double) resultList.get(0).get("netPriceMinus1")).isEqualTo(product.netPrice - 1); - assertThat((Double) resultList.get(0).get("netPriceMul2")).isEqualTo(product.netPrice * 2); - assertThat((Double) resultList.get(0).get("netPriceDiv119")).isEqualTo(product.netPrice / 1.19); - assertThat((Integer) resultList.get(0).get("spaceUnitsMod2")).isEqualTo(product.spaceUnits % 2); - assertThat((Integer) resultList.get(0).get("spaceUnitsPlusSpaceUnits")) - .isEqualTo(product.spaceUnits + product.spaceUnits); - assertThat((Integer) resultList.get(0).get("spaceUnitsMinusSpaceUnits")) - .isEqualTo(product.spaceUnits - product.spaceUnits); - assertThat((Integer) resultList.get(0).get("spaceUnitsMultiplySpaceUnits")) - .isEqualTo(product.spaceUnits * product.spaceUnits); - assertThat((Double) resultList.get(0).get("spaceUnitsDivideSpaceUnits")) - .isEqualTo((double) (product.spaceUnits / product.spaceUnits)); - assertThat((Integer) resultList.get(0).get("spaceUnitsModSpaceUnits")) - .isEqualTo(product.spaceUnits % product.spaceUnits); - } - - @Test // DATAMONGO-774 - void expressionsInProjectionExample() { - - Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); - mongoTemplate.insert(product); - - TypedAggregation agg = newAggregation(Product.class, // - project("name", "netPrice") // - .andExpression("netPrice + 1").as("netPricePlus1") // - .andExpression("netPrice - 1").as("netPriceMinus1") // - .andExpression("netPrice / 2").as("netPriceDiv2") // - .andExpression("netPrice * 1.19").as("grossPrice") // - .andExpression("spaceUnits % 2").as("spaceUnitsMod2") // - .andExpression("(netPrice * 0.8 + 1.2) * 1.19").as("grossPriceIncludingDiscountAndCharge") // - - ); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - List resultList = result.getMappedResults(); - - assertThat(resultList).isNotNull(); - assertThat((String) resultList.get(0).get("_id")).isEqualTo(product.id); - assertThat((String) resultList.get(0).get("name")).isEqualTo(product.name); - assertThat((Double) resultList.get(0).get("netPricePlus1")).isEqualTo(product.netPrice + 1); - assertThat((Double) resultList.get(0).get("netPriceMinus1")).isEqualTo(product.netPrice - 1); - assertThat((Double) resultList.get(0).get("netPriceDiv2")).isEqualTo(product.netPrice / 2); - assertThat((Double) resultList.get(0).get("grossPrice")).isEqualTo(product.netPrice * 1.19); - assertThat((Integer) resultList.get(0).get("spaceUnitsMod2")).isEqualTo(product.spaceUnits % 2); - assertThat((Double) resultList.get(0).get("grossPriceIncludingDiscountAndCharge")) - .isEqualTo((product.netPrice * 0.8 + 1.2) * 1.19); - } - - @Test // DATAMONGO-774 - void stringExpressionsInProjectionExample() { - - Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); - mongoTemplate.insert(product); - - TypedAggregation agg = newAggregation(Product.class, // - project("name", "netPrice") // - .andExpression("concat(name, '_bubu')").as("name_bubu") // - ); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - List resultList = result.getMappedResults(); - - assertThat(resultList).isNotNull(); - assertThat((String) resultList.get(0).get("_id")).isEqualTo(product.id); - assertThat((String) resultList.get(0).get("name")).isEqualTo(product.name); - assertThat((String) resultList.get(0).get("name_bubu")).isEqualTo(product.name + "_bubu"); - } - - @Test // DATAMONGO-774 - void expressionsInProjectionExampleShowcase() { - - Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); - mongoTemplate.insert(product); - - double shippingCosts = 1.2; - - TypedAggregation agg = newAggregation(Product.class, // - project("name", "netPrice") // - .andExpression("(netPrice * (1-discountRate) + [0]) * (1+taxRate)", shippingCosts).as("salesPrice") // - ); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - List resultList = result.getMappedResults(); - - assertThat(resultList).isNotNull(); - Document firstItem = resultList.get(0); - assertThat((String) firstItem.get("_id")).isEqualTo(product.id); - assertThat((String) firstItem.get("name")).isEqualTo(product.name); - assertThat((Double) firstItem.get("salesPrice")) - .isEqualTo((product.netPrice * (1 - product.discountRate) + shippingCosts) * (1 + product.taxRate)); - } - - /** - * @see Spring - * Data MongoDB - Aggregation Framework - invalid reference in group Operation - */ - @Test // DATAMONGO-753 - void allowsNestedFieldReferencesAsGroupIdsInGroupExpressions() { - - mongoTemplate.insert(new DATAMONGO753().withPDs(new PD("A", 1), new PD("B", 1), new PD("C", 1))); - mongoTemplate.insert(new DATAMONGO753().withPDs(new PD("B", 1), new PD("B", 1), new PD("C", 1))); - - TypedAggregation agg = newAggregation(DATAMONGO753.class, // - unwind("pd"), // - group("pd.pDch") // the nested field expression - .sum("pd.up").as("uplift"), // - project("_id", "uplift"), // - sort(Sort.by("uplift"))); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - List stats = result.getMappedResults(); - - assertThat(stats.size()).isEqualTo(3); - assertThat(stats.get(0).get("_id").toString()).isEqualTo("A"); - assertThat((Integer) stats.get(0).get("uplift")).isEqualTo(1); - assertThat(stats.get(1).get("_id").toString()).isEqualTo("C"); - assertThat((Integer) stats.get(1).get("uplift")).isEqualTo(2); - assertThat(stats.get(2).get("_id").toString()).isEqualTo("B"); - assertThat((Integer) stats.get(2).get("uplift")).isEqualTo(3); - } - - /** - * @see Spring - * Data MongoDB - Aggregation Framework - invalid reference in group Operation - */ - @Test // DATAMONGO-753 - void aliasesNestedFieldInProjectionImmediately() { - - mongoTemplate.insert(new DATAMONGO753().withPDs(new PD("A", 1), new PD("B", 1), new PD("C", 1))); - mongoTemplate.insert(new DATAMONGO753().withPDs(new PD("B", 1), new PD("B", 1), new PD("C", 1))); - - TypedAggregation agg = newAggregation(DATAMONGO753.class, // - unwind("pd"), // - project().and("pd.up").as("up")); - - AggregationResults results = mongoTemplate.aggregate(agg, Document.class); - List mappedResults = results.getMappedResults(); - - assertThat(mappedResults).hasSize(6); - for (Document element : mappedResults) { - assertThat(element.get("up")).isEqualTo((Object) 1); - } - } - - @Test // DATAMONGO-774 - void shouldPerformDateProjectionOperatorsCorrectly() throws ParseException { - - Data data = new Data(); - data.stringValue = "ABC"; - mongoTemplate.insert(data); - - TypedAggregation agg = newAggregation(Data.class, project() // - .andExpression("concat(stringValue, 'DE')").as("concat") // - .andExpression("strcasecmp(stringValue,'XYZ')").as("strcasecmp") // - .andExpression("substr(stringValue,1,1)").as("substr") // - .andExpression("toLower(stringValue)").as("toLower") // - .andExpression("toUpper(toLower(stringValue))").as("toUpper") // - ); - - AggregationResults results = mongoTemplate.aggregate(agg, Document.class); - Document document = results.getUniqueMappedResult(); - - assertThat(document).isNotNull(); - assertThat((String) document.get("concat")).isEqualTo("ABCDE"); - assertThat((Integer) document.get("strcasecmp")).isEqualTo(-1); - assertThat((String) document.get("substr")).isEqualTo("B"); - assertThat((String) document.get("toLower")).isEqualTo("abc"); - assertThat((String) document.get("toUpper")).isEqualTo("ABC"); - } - - @Test // DATAMONGO-774 - void shouldPerformStringProjectionOperatorsCorrectly() throws ParseException { - - Data data = new Data(); - data.dateValue = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SSSZ").parse("29.08.1983 12:34:56.789+0000"); - mongoTemplate.insert(data); - - TypedAggregation agg = newAggregation(Data.class, project() // - .andExpression("dayOfYear(dateValue)").as("dayOfYear") // - .andExpression("dayOfMonth(dateValue)").as("dayOfMonth") // - .andExpression("dayOfWeek(dateValue)").as("dayOfWeek") // - .andExpression("year(dateValue)").as("year") // - .andExpression("month(dateValue)").as("month") // - .andExpression("week(dateValue)").as("week") // - .andExpression("hour(dateValue)").as("hour") // - .andExpression("minute(dateValue)").as("minute") // - .andExpression("second(dateValue)").as("second") // - .andExpression("millisecond(dateValue)").as("millisecond") // - ); - - AggregationResults results = mongoTemplate.aggregate(agg, Document.class); - Document document = results.getUniqueMappedResult(); - - assertThat(document).isNotNull(); - assertThat((Integer) document.get("dayOfYear")).isEqualTo(241); - assertThat((Integer) document.get("dayOfMonth")).isEqualTo(29); - assertThat((Integer) document.get("dayOfWeek")).isEqualTo(2); - assertThat((Integer) document.get("year")).isEqualTo(1983); - assertThat((Integer) document.get("month")).isEqualTo(8); - assertThat((Integer) document.get("week")).isEqualTo(35); - assertThat((Integer) document.get("hour")).isEqualTo(12); - assertThat((Integer) document.get("minute")).isEqualTo(34); - assertThat((Integer) document.get("second")).isEqualTo(56); - assertThat((Integer) document.get("millisecond")).isEqualTo(789); - } - - @Test // DATAMONGO-1550 - void shouldPerformReplaceRootOperatorCorrectly() throws ParseException { - - Data data = new Data(); - DataItem dataItem = new DataItem(); - dataItem.primitiveIntValue = 42; - data.item = dataItem; - mongoTemplate.insert(data); - - TypedAggregation agg = newAggregation(Data.class, project("item"), // - replaceRoot("item"), // - project().and("primitiveIntValue").as("my_primitiveIntValue")); - - AggregationResults results = mongoTemplate.aggregate(agg, Document.class); - Document resultDocument = results.getUniqueMappedResult(); - - assertThat(resultDocument).isNotNull(); - assertThat((Integer) resultDocument.get("my_primitiveIntValue")).isEqualTo(42); - assertThat((Integer) resultDocument.keySet().size()).isEqualTo(1); - } - - @Test // DATAMONGO-788, DATAMONGO-2264 - void referencesToGroupIdsShouldBeRenderedProperly() { - - mongoTemplate.insert(new DATAMONGO788(1, 1)); - mongoTemplate.insert(new DATAMONGO788(1, 1)); - mongoTemplate.insert(new DATAMONGO788(1, 1)); - mongoTemplate.insert(new DATAMONGO788(2, 1)); - mongoTemplate.insert(new DATAMONGO788(2, 1)); - - AggregationOperation projectFirst = Aggregation.project("x", "y").and("xField").as("x").and("yField").as("y"); - AggregationOperation group = Aggregation.group("x", "y").count().as("xPerY"); - AggregationOperation project = Aggregation.project("xPerY", "x", "y").andExclude("_id"); - - TypedAggregation aggregation = Aggregation.newAggregation(DATAMONGO788.class, projectFirst, group, - project, Aggregation.sort(Sort.by("xPerY"))); - AggregationResults aggResults = mongoTemplate.aggregate(aggregation, Document.class); - List items = aggResults.getMappedResults(); - - assertThat(items.size()).isEqualTo(2); - assertThat((Integer) items.get(0).get("xPerY")).isEqualTo(2); - assertThat((Integer) items.get(0).get("x")).isEqualTo(2); - assertThat((Integer) items.get(0).get("y")).isEqualTo(1); - assertThat((Integer) items.get(1).get("xPerY")).isEqualTo(3); - assertThat((Integer) items.get(1).get("x")).isEqualTo(1); - assertThat((Integer) items.get(1).get("y")).isEqualTo(1); - } - - @Test // DATAMONGO-806 - void shouldAllowGroupByIdFields() { - - mongoTemplate.dropCollection(User.class); - - LocalDateTime now = new LocalDateTime(); - - User user1 = new User("u1", new PushMessage("1", "aaa", now.toDate())); - User user2 = new User("u2", new PushMessage("2", "bbb", now.minusDays(2).toDate())); - User user3 = new User("u3", new PushMessage("3", "ccc", now.minusDays(1).toDate())); - - mongoTemplate.save(user1); - mongoTemplate.save(user2); - mongoTemplate.save(user3); - - Aggregation agg = newAggregation( // - project("id", "msgs"), // - unwind("msgs"), // - match(where("msgs.createDate").gt(now.minusDays(1).toDate())), // - group("id").push("msgs").as("msgs") // - ); - - AggregationResults results = mongoTemplate.aggregate(agg, User.class, Document.class); - - List mappedResults = results.getMappedResults(); - - Document firstItem = mappedResults.get(0); - assertThat(firstItem.get("_id")).isNotNull(); - assertThat(String.valueOf(firstItem.get("_id"))).isEqualTo("u1"); - } - - @Test // DATAMONGO-840 - void shouldAggregateOrderDataToAnInvoice() { - - mongoTemplate.dropCollection(Order.class); - - double taxRate = 0.19; - - LineItem product1 = new LineItem("1", "p1", 1.23); - LineItem product2 = new LineItem("2", "p2", 0.87, 2); - LineItem product3 = new LineItem("3", "p3", 5.33); - - Order order = new Order("o4711", "c42", new Date()).addItem(product1).addItem(product2).addItem(product3); - - mongoTemplate.save(order); - - AggregationResults results = mongoTemplate.aggregate(newAggregation(Order.class, // - match(where("id").is(order.getId())), unwind("items"), // - project("id", "customerId", "items") // - .andExpression("items.price * items.quantity").as("lineTotal"), // - group("id") // - .sum("lineTotal").as("netAmount") // - .addToSet("items").as("items"), // - project("id", "items", "netAmount") // - .and("orderId").previousOperation() // - .andExpression("netAmount * [0]", taxRate).as("taxAmount") // - .andExpression("netAmount * (1 + [0])", taxRate).as("totalAmount") // - ), Invoice.class); - - Invoice invoice = results.getUniqueMappedResult(); - - assertThat(invoice).isNotNull(); - assertThat(invoice.getOrderId()).isEqualTo(order.getId()); - assertThat(invoice.getNetAmount()).isCloseTo(8.3, Offset.offset(000001D)); - assertThat(invoice.getTaxAmount()).isCloseTo(1.577, Offset.offset(000001D)); - assertThat(invoice.getTotalAmount()).isCloseTo(9.877, Offset.offset(000001D)); - } - - @Test // DATAMONGO-924 - void shouldAllowGroupingByAliasedFieldDefinedInFormerAggregationStage() { - - mongoTemplate.dropCollection(CarPerson.class); - - CarPerson person1 = new CarPerson("first1", "last1", new CarDescriptor.Entry("MAKE1", "MODEL1", 2000), - new CarDescriptor.Entry("MAKE1", "MODEL2", 2001), new CarDescriptor.Entry("MAKE2", "MODEL3", 2010), - new CarDescriptor.Entry("MAKE3", "MODEL4", 2014)); - - CarPerson person2 = new CarPerson("first2", "last2", new CarDescriptor.Entry("MAKE3", "MODEL4", 2014)); - - CarPerson person3 = new CarPerson("first3", "last3", new CarDescriptor.Entry("MAKE2", "MODEL5", 2011)); - - mongoTemplate.save(person1); - mongoTemplate.save(person2); - mongoTemplate.save(person3); - - TypedAggregation agg = Aggregation.newAggregation(CarPerson.class, - unwind("descriptors.carDescriptor.entries"), // - project() // - .and("descriptors.carDescriptor.entries.make").as("make") // - .and("descriptors.carDescriptor.entries.model").as("model") // - .and("firstName").as("firstName") // - .and("lastName").as("lastName"), // - group("make")); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - - assertThat(result.getMappedResults()).hasSize(3); - } - - @Test // DATAMONGO-960 - void returnFiveMostCommonLikesAggregationFrameworkExampleWithSortOnDiskOptionEnabled() { - - createUserWithLikesDocuments(); - - TypedAggregation agg = createUsersWithCommonLikesAggregation() // - .withOptions(newAggregationOptions().allowDiskUse(true).build()); - - assertThat(agg).isNotNull(); - assertThat(agg.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(agg, LikeStats.class); - assertThat(result).isNotNull(); - assertThat(result.getMappedResults()).isNotNull(); - assertThat(result.getMappedResults().size()).isEqualTo(5); - - assertLikeStats(result.getMappedResults().get(0), "a", 4); - assertLikeStats(result.getMappedResults().get(1), "b", 2); - assertLikeStats(result.getMappedResults().get(2), "c", 4); - assertLikeStats(result.getMappedResults().get(3), "d", 2); - assertLikeStats(result.getMappedResults().get(4), "e", 3); - } - - @Test // DATAMONGO-1637 - void returnFiveMostCommonLikesAggregationFrameworkExampleWithSortOnDiskOptionEnabledWhileStreaming() { - - createUserWithLikesDocuments(); - - TypedAggregation agg = createUsersWithCommonLikesAggregation() // - .withOptions(newAggregationOptions().allowDiskUse(true).build()); - - assertThat(agg).isNotNull(); - assertThat(agg.toString()).isNotNull(); - - CloseableIterator iterator = mongoTemplate.aggregateStream(agg, LikeStats.class); - List result = toList(iterator); - iterator.close(); - - assertThat(result).isNotNull(); - assertThat(result).isNotNull(); - assertThat(result.size()).isEqualTo(5); - - assertLikeStats(result.get(0), "a", 4); - assertLikeStats(result.get(1), "b", 2); - assertLikeStats(result.get(2), "c", 4); - assertLikeStats(result.get(3), "d", 2); - assertLikeStats(result.get(4), "e", 3); - } - - @Test // DATAMONGO-960 - void returnFiveMostCommonLikesShouldReturnStageExecutionInformationWithExplainOptionEnabled() { - - createUserWithLikesDocuments(); - - TypedAggregation agg = createUsersWithCommonLikesAggregation() // - .withOptions(newAggregationOptions().explain(true).build()); - - AggregationResults result = mongoTemplate.aggregate(agg, LikeStats.class); - - assertThat(result.getMappedResults()).isEmpty(); - - Document rawResult = result.getRawResults(); - - assertThat(rawResult).isNotNull(); - assertThat(rawResult.containsKey("stages")).isEqualTo(true); - } - - @Test // DATAMONGO-954, DATAMONGO-2264 - void shouldSupportReturningCurrentAggregationRoot() { - - mongoTemplate.save(new Person("p1_first", "p1_last", 25)); - mongoTemplate.save(new Person("p2_first", "p2_last", 32)); - mongoTemplate.save(new Person("p3_first", "p3_last", 25)); - mongoTemplate.save(new Person("p4_first", "p4_last", 15)); - - List personsWithAge25 = mongoTemplate.find(Query.query(where("age").is(25)), Document.class, - mongoTemplate.getCollectionName(Person.class)); - - Aggregation agg = newAggregation(group("age").push(Aggregation.ROOT).as("users"), sort(Sort.by("_id"))); - AggregationResults result = mongoTemplate.aggregate(agg, Person.class, Document.class); - - assertThat(result.getMappedResults()).hasSize(3); - Document o = result.getMappedResults().get(1); - - assertThat(o.get("_id")).isEqualTo((Object) 25); - assertThat((List) o.get("users")).hasSize(2); - assertThat((List) o.get("users")).contains(personsWithAge25.toArray()); - } - - /** - * {@link https://stackoverflow.com/questions/24185987/using-root-inside-spring-data-mongodb-for-retrieving-whole-document} - */ - @Test // DATAMONGO-954, DATAMONGO-2264 - void shouldSupportReturningCurrentAggregationRootInReference() { - - mongoTemplate.save(new Reservation("0123", "42", 100)); - mongoTemplate.save(new Reservation("0360", "43", 200)); - mongoTemplate.save(new Reservation("0360", "44", 300)); - - Aggregation agg = newAggregation( // - match(where("hotelCode").is("0360")), // - sort(Direction.DESC, "confirmationNumber", "timestamp"), // - group("confirmationNumber") // - .first("timestamp").as("timestamp") // - .first(Aggregation.ROOT).as("reservationImage") // - ); - AggregationResults result = mongoTemplate.aggregate(agg, Reservation.class, Document.class); - - assertThat(result.getMappedResults()).hasSize(2); - } - - @Test // DATAMONGO-1549 - void shouldApplyCountCorrectly() { - - mongoTemplate.save(new Reservation("0123", "42", 100)); - mongoTemplate.save(new Reservation("0360", "43", 200)); - mongoTemplate.save(new Reservation("0360", "44", 300)); - - Aggregation agg = newAggregation( // - count().as("documents"), // - project("documents") // - .andExpression("documents * 2").as("twice")); - AggregationResults result = mongoTemplate.aggregate(agg, Reservation.class, Document.class); - - assertThat(result.getMappedResults()).hasSize(1); - - Document document = result.getMappedResults().get(0); - assertThat(document).containsEntry("documents", 3).containsEntry("twice", 6); - } - - @Test // DATAMONGO-975 - void shouldRetrieveDateTimeFragementsCorrectly() throws Exception { - - mongoTemplate.dropCollection(ObjectWithDate.class); - - DateTime dateTime = new DateTime() // - .withZone(DateTimeZone.UTC) // - .withYear(2014) // - .withMonthOfYear(2) // - .withDayOfMonth(7) // - .withTime(3, 4, 5, 6).toDateTimeISO(); - - ObjectWithDate owd = new ObjectWithDate(dateTime.toDate()); - mongoTemplate.insert(owd); - - ProjectionOperation dateProjection = Aggregation.project() // - .and("dateValue").extractHour().as("hour") // - .and("dateValue").extractMinute().as("min") // - .and("dateValue").extractSecond().as("second") // - .and("dateValue").extractMillisecond().as("millis") // - .and("dateValue").extractYear().as("year") // - .and("dateValue").extractMonth().as("month") // - .and("dateValue").extractWeek().as("week") // - .and("dateValue").extractDayOfYear().as("dayOfYear") // - .and("dateValue").extractDayOfMonth().as("dayOfMonth") // - .and("dateValue").extractDayOfWeek().as("dayOfWeek") // - .andExpression("dateValue + 86400000").extractDayOfYear().as("dayOfYearPlus1Day") // - .andExpression("dateValue + 86400000").project("dayOfYear").as("dayOfYearPlus1DayManually") // - ; - - Aggregation agg = newAggregation(dateProjection); - AggregationResults result = mongoTemplate.aggregate(agg, ObjectWithDate.class, Document.class); - - assertThat(result.getMappedResults()).hasSize(1); - Document document = result.getMappedResults().get(0); - - assertThat(document.get("hour")).isEqualTo((Object) dateTime.getHourOfDay()); - assertThat(document.get("min")).isEqualTo((Object) dateTime.getMinuteOfHour()); - assertThat(document.get("second")).isEqualTo((Object) dateTime.getSecondOfMinute()); - assertThat(document.get("millis")).isEqualTo((Object) dateTime.getMillisOfSecond()); - assertThat(document.get("year")).isEqualTo((Object) dateTime.getYear()); - assertThat(document.get("month")).isEqualTo((Object) dateTime.getMonthOfYear()); - // dateTime.getWeekOfWeekyear()) returns 6 since for MongoDB the week starts on sunday and not on monday. - assertThat(document.get("week")).isEqualTo((Object) 5); - assertThat(document.get("dayOfYear")).isEqualTo((Object) dateTime.getDayOfYear()); - assertThat(document.get("dayOfMonth")).isEqualTo((Object) dateTime.getDayOfMonth()); - - // dateTime.getDayOfWeek() - assertThat(document.get("dayOfWeek")).isEqualTo((Object) 6); - assertThat(document.get("dayOfYearPlus1Day")).isEqualTo((Object) dateTime.plusDays(1).getDayOfYear()); - assertThat(document.get("dayOfYearPlus1DayManually")).isEqualTo((Object) dateTime.plusDays(1).getDayOfYear()); - } - - @Test // DATAMONGO-1127 - void shouldSupportGeoNearQueriesForAggregationWithDistanceField() { - - mongoTemplate.insertAll(Arrays.asList(TestEntities.geolocation().pennStation(), - TestEntities.geolocation().tenGenOffice(), TestEntities.geolocation().flatironBuilding())); - - mongoTemplate.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location")); - - NearQuery geoNear = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).maxDistance(150); - - Aggregation agg = newAggregation(Aggregation.geoNear(geoNear, "distance")); - AggregationResults result = mongoTemplate.aggregate(agg, Venue.class, Document.class); - - assertThat(result.getMappedResults()).hasSize(3); - - Document firstResult = result.getMappedResults().get(0); - assertThat(firstResult.containsKey("distance")).isEqualTo(true); - assertThat((Double) firstResult.get("distance")).isCloseTo(117.620092203928, Offset.offset(0.00001D)); - } - - @Test // DATAMONGO-1348 - void shouldSupportGeoJsonInGeoNearQueriesForAggregationWithDistanceField() { - - mongoTemplate.insert(new Venue("Penn Station", -73.99408, 40.75057)); - mongoTemplate.insert(new Venue("10gen Office", -73.99171, 40.738868)); - mongoTemplate.insert(new Venue("Flatiron Building", -73.988135, 40.741404)); - - mongoTemplate.indexOps(Venue.class) - .ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE)); - - NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.KILOMETERS).num(10).maxDistance(150); - - Aggregation agg = newAggregation(Aggregation.geoNear(geoNear, "distance")); - AggregationResults result = mongoTemplate.aggregate(agg, Venue.class, Document.class); - - assertThat(result.getMappedResults()).hasSize(3); - - Document firstResult = result.getMappedResults().get(0); - assertThat(firstResult.containsKey("distance")).isEqualTo(true); - assertThat((Double) firstResult.get("distance")).isCloseTo(117.61940988193759, Offset.offset(0.00001D)); - } - - @Test // DATAMONGO-1348 - void shouldSupportGeoJsonInGeoNearQueriesForAggregationWithDistanceFieldInMiles() { - - mongoTemplate.insert(new Venue("Penn Station", -73.99408, 40.75057)); - mongoTemplate.insert(new Venue("10gen Office", -73.99171, 40.738868)); - mongoTemplate.insert(new Venue("Flatiron Building", -73.988135, 40.741404)); - - mongoTemplate.indexOps(Venue.class) - .ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE)); - - NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.KILOMETERS).num(10).maxDistance(150) - .inMiles(); - - Aggregation agg = newAggregation(Aggregation.geoNear(geoNear, "distance")); - AggregationResults result = mongoTemplate.aggregate(agg, Venue.class, Document.class); - - assertThat(result.getMappedResults()).hasSize(3); - - Document firstResult = result.getMappedResults().get(0); - assertThat(firstResult.containsKey("distance")).isEqualTo(true); - assertThat((Double) firstResult.get("distance")).isCloseTo(73.08517, Offset.offset(0.00001D)); - } - - @Test // DATAMONGO-1133 - void shouldHonorFieldAliasesForFieldReferences() { - - mongoTemplate.insert(new MeterData("m1", "counter1", 42)); - mongoTemplate.insert(new MeterData("m1", "counter1", 13)); - mongoTemplate.insert(new MeterData("m1", "counter1", 45)); - - TypedAggregation agg = newAggregation(MeterData.class, // - match(where("resourceId").is("m1")), // - group("counterName").sum("counterVolume").as("totalValue")); - - AggregationResults results = mongoTemplate.aggregate(agg, Document.class); - - assertThat(results.getMappedResults()).hasSize(1); - Document result = results.getMappedResults().get(0); - - assertThat(result.get("_id")).isEqualTo("counter1"); - assertThat(result.get("totalValue")).isEqualTo(100.0); - } - - @Test // DATAMONGO-1326 - void shouldLookupPeopleCorectly() { - - createUsersWithReferencedPersons(); - - TypedAggregation agg = newAggregation(User.class, // - lookup("person", "_id", "firstname", "linkedPerson"), // - sort(ASC, "id")); - - AggregationResults results = mongoTemplate.aggregate(agg, User.class, Document.class); - - List mappedResults = results.getMappedResults(); - - Document firstItem = mappedResults.get(0); - - assertThat(firstItem).containsEntry("_id", "u1"); - assertThat(firstItem).containsEntry("linkedPerson.[0].firstname", "u1"); - } - - @Test // DATAMONGO-1326 - void shouldGroupByAndLookupPeopleCorectly() { - - createUsersWithReferencedPersons(); - - TypedAggregation agg = newAggregation(User.class, // - group().min("id").as("foreignKey"), // - lookup("person", "foreignKey", "firstname", "linkedPerson"), // - sort(ASC, "foreignKey", "linkedPerson.firstname")); - - AggregationResults results = mongoTemplate.aggregate(agg, User.class, Document.class); - - List mappedResults = results.getMappedResults(); - - Document firstItem = mappedResults.get(0); - - assertThat(firstItem).containsEntry("foreignKey", "u1"); - assertThat(firstItem).containsEntry("linkedPerson.[0].firstname", "u1"); - } - - @Test // DATAMONGO-1418, DATAMONGO-1824 - @MongoVersion(asOf = "2.6") - void shouldCreateOutputCollection() { - - createPersonDocuments(); - - String tempOutCollection = "personQueryTemp"; - TypedAggregation agg = newAggregation(Person.class, // - group("sex").count().as("count"), // - sort(DESC, "count"), // - out(tempOutCollection)); - - AggregationResults results = mongoTemplate.aggregate(agg, Document.class); - - assertThat(results.getMappedResults()).hasSize(2); - - List list = mongoTemplate.findAll(Document.class, tempOutCollection); - - assertThat(list).hasSize(2); - assertThat(list.get(0)).containsEntry("_id", "MALE").containsEntry("count", 3); - assertThat(list.get(1)).containsEntry("_id", "FEMALE").containsEntry("count", 2); - - mongoTemplate.dropCollection(tempOutCollection); - } - - @Test // DATAMONGO-1637 - void shouldCreateOutputCollectionWhileStreaming() { - - createPersonDocuments(); - - String tempOutCollection = "personQueryTemp"; - TypedAggregation agg = newAggregation(Person.class, // - group("sex").count().as("count"), // - sort(DESC, "count"), // - out(tempOutCollection)); - - mongoTemplate.aggregateStream(agg, Document.class).close(); - - List list = mongoTemplate.findAll(Document.class, tempOutCollection); - - assertThat(list).hasSize(2); - assertThat(list.get(0)).containsEntry("_id", "MALE").containsEntry("count", 3); - assertThat(list.get(1)).containsEntry("_id", "FEMALE").containsEntry("count", 2); - - mongoTemplate.dropCollection(tempOutCollection); - } - - @Test // DATAMONGO-1637 - void shouldReturnDocumentsWithOutputCollectionWhileStreaming() { - - createPersonDocuments(); - - String tempOutCollection = "personQueryTemp"; - TypedAggregation agg = newAggregation(Person.class, // - group("sex").count().as("count"), // - sort(DESC, "count"), // - out(tempOutCollection)); - - CloseableIterator iterator = mongoTemplate.aggregateStream(agg, Document.class); - - List result = toList(iterator); - - assertThat(result).hasSize(2); - assertThat(result.get(0)).containsEntry("_id", "MALE").containsEntry("count", 3); - assertThat(result.get(1)).containsEntry("_id", "FEMALE").containsEntry("count", 2); - - mongoTemplate.dropCollection(tempOutCollection); - } - - private void createPersonDocuments() { - - mongoTemplate.save(new Person("Anna", "Ivanova", 21, Person.Sex.FEMALE)); - mongoTemplate.save(new Person("Pavel", "Sidorov", 36, Person.Sex.MALE)); - mongoTemplate.save(new Person("Anastasia", "Volochkova", 29, Person.Sex.FEMALE)); - mongoTemplate.save(new Person("Igor", "Stepanov", 31, Person.Sex.MALE)); - mongoTemplate.save(new Person("Leoniv", "Yakubov", 55, Person.Sex.MALE)); - } - - @Test // DATAMONGO-1418, DATAMONGO-2536 - void outShouldOutBeTheLastOperation() { - assertThatIllegalArgumentException().isThrownBy(() -> newAggregation(match(new Criteria()), // - group("field1").count().as("totalCount"), // - out("collection1"), // - skip(100L)).toPipeline(DEFAULT_CONTEXT)); - } - - @Test // DATAMONGO-1325 - void shouldApplySampleCorrectly() { - - createUserWithLikesDocuments(); - - TypedAggregation agg = newAggregation(UserWithLikes.class, // - unwind("likes"), // - sample(3) // - ); - - assertThat(agg.toString()).isNotNull(); - - AggregationResults result = mongoTemplate.aggregate(agg, LikeStats.class); - assertThat(result.getMappedResults().size()).isEqualTo(3); - } - - @Test // DATAMONGO-1457 - @MongoVersion(asOf = "3.2") - void sliceShouldBeAppliedCorrectly() { - - createUserWithLikesDocuments(); - - TypedAggregation agg = newAggregation(UserWithLikes.class, match(new Criteria()), - project().and("likes").slice(2)); - - AggregationResults result = mongoTemplate.aggregate(agg, UserWithLikes.class); - - assertThat(result.getMappedResults()).hasSize(9); - for (UserWithLikes user : result) { - assertThat(user.likes.size() <= 2).isEqualTo(true); - } - } - - @Test // DATAMONGO-1491 - void filterShouldBeAppliedCorrectly() { - - Item item43 = Item.builder().itemId("43").quantity(2).price(2L).build(); - Item item2 = Item.builder().itemId("2").quantity(1).price(240L).build(); - Sales sales1 = Sales.builder().id("0").items(Arrays.asList( // - item43, item2)) // - .build(); - - Item item23 = Item.builder().itemId("23").quantity(3).price(110L).build(); - Item item103 = Item.builder().itemId("103").quantity(4).price(5L).build(); - Item item38 = Item.builder().itemId("38").quantity(1).price(300L).build(); - Sales sales2 = Sales.builder().id("1").items(Arrays.asList( // - item23, item103, item38)).build(); - - Item item4 = Item.builder().itemId("4").quantity(1).price(23L).build(); - Sales sales3 = Sales.builder().id("2").items(Arrays.asList( // - item4)).build(); - - mongoTemplate.insert(Arrays.asList(sales1, sales2, sales3), Sales.class); - - TypedAggregation agg = newAggregation(Sales.class, project().and("items") - .filter("item", AggregationFunctionExpressions.GTE.of(field("item.price"), 100)).as("items")); - - assertThat(mongoTemplate.aggregate(agg, Sales.class).getMappedResults()).contains( - Sales.builder().id("0").items(Collections.singletonList(item2)).build(), - Sales.builder().id("1").items(Arrays.asList(item23, item38)).build(), - Sales.builder().id("2").items(Collections. emptyList()).build()); - } - - @Test // DATAMONGO-1538 - void letShouldBeAppliedCorrectly() { - - Sales2 sales1 = Sales2.builder().id("1").price(10).tax(0.5F).applyDiscount(true).build(); - Sales2 sales2 = Sales2.builder().id("2").price(10).tax(0.25F).applyDiscount(false).build(); - - mongoTemplate.insert(Arrays.asList(sales1, sales2), Sales2.class); - - ExpressionVariable total = ExpressionVariable.newVariable("total") - .forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax"))); - ExpressionVariable discounted = ExpressionVariable.newVariable("discounted") - .forExpression(ConditionalOperators.Cond.when("applyDiscount").then(0.9D).otherwise(1.0D)); - - TypedAggregation agg = Aggregation.newAggregation(Sales2.class, - Aggregation.project() - .and(VariableOperators.Let.define(total, discounted).andApply( - AggregationFunctionExpressions.MULTIPLY.of(Fields.field("total"), Fields.field("discounted")))) - .as("finalTotal")); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - assertThat(result.getMappedResults()).contains(new Document("_id", "1").append("finalTotal", 9.450000000000001D), - new Document("_id", "2").append("finalTotal", 10.25D)); - } - - @Test // DATAMONGO-1551, DATAMONGO-2264 - void graphLookupShouldBeAppliedCorrectly() { - - Employee em1 = Employee.builder().id(1).name("Dev").build(); - Employee em2 = Employee.builder().id(2).name("Eliot").reportsTo("Dev").build(); - Employee em4 = Employee.builder().id(4).name("Andrew").reportsTo("Eliot").build(); - - mongoTemplate.insert(Arrays.asList(em1, em2, em4), Employee.class); - - TypedAggregation agg = Aggregation.newAggregation(Employee.class, - match(Criteria.where("name").is("Andrew")), // - Aggregation.graphLookup("employee") // - .startWith("reportsTo") // - .connectFrom("reportsTo") // - .connectTo("name") // - .depthField("depth") // - .maxDepth(5) // - .as("reportingHierarchy"), // - project("id", "depth", "name", "reportsTo", "reportingHierarchy")); - - AggregationResults result = mongoTemplate.aggregate(agg, Document.class); - - Document object = result.getUniqueMappedResult(); - List list = (List) object.get("reportingHierarchy"); - - assertThat(object).containsEntry("name", "Andrew").containsEntry("reportsTo", "Eliot"); - assertThat(list).containsOnly( - new Document("_id", 2).append("name", "Eliot").append("reportsTo", "Dev").append("depth", 0L).append("_class", - Employee.class.getName()), - new Document("_id", 1).append("name", "Dev").append("depth", 1L).append("_class", Employee.class.getName())); - } - - @Test // DATAMONGO-1552 - void bucketShouldCollectDocumentsIntoABucket() { - - Art a1 = Art.builder().id(1).title("The Pillars of Society").artist("Grosz").year(1926).price(199.99).build(); - Art a2 = Art.builder().id(2).title("Melancholy III").artist("Munch").year(1902).price(280.00).build(); - Art a3 = Art.builder().id(3).title("Dancer").artist("Miro").year(1925).price(76.04).build(); - Art a4 = Art.builder().id(4).title("The Great Wave off Kanagawa").artist("Hokusai").price(167.30).build(); - - mongoTemplate.insert(Arrays.asList(a1, a2, a3, a4), Art.class); - - TypedAggregation aggregation = newAggregation(Art.class, // - bucket("price") // - .withBoundaries(0, 100, 200) // - .withDefaultBucket("other") // - .andOutputCount().as("count") // - .andOutput("title").push().as("titles") // - .andOutputExpression("price * 10").sum().as("sum")); - - AggregationResults result = mongoTemplate.aggregate(aggregation, Document.class); - assertThat(result.getMappedResults().size()).isEqualTo(3); - - // { "_id" : 0 , "count" : 1 , "titles" : [ "Dancer"] , "sum" : 760.4000000000001} - Document bound0 = result.getMappedResults().get(0); - assertThat(bound0).containsEntry("count", 1).containsEntry("titles.[0]", "Dancer"); - assertThat((Double) bound0.get("sum")).isCloseTo(760.40, Offset.offset(0.1D)); - - // { "_id" : 100 , "count" : 2 , "titles" : [ "The Pillars of Society" , "The Great Wave off Kanagawa"] , "sum" : - // 3672.9} - Document bound100 = result.getMappedResults().get(1); - assertThat(bound100).containsEntry("count", 2).containsEntry("_id", 100); - assertThat((List) bound100.get("titles")).contains("The Pillars of Society", "The Great Wave off Kanagawa"); - assertThat((Double) bound100.get("sum")).isCloseTo(3672.9, Offset.offset(0.1D)); - } - - @Test // DATAMONGO-1552, DATAMONGO-2437 - void bucketAutoShouldCollectDocumentsIntoABucket() { - - Art a1 = Art.builder().id(1).title("The Pillars of Society").artist("Grosz").year(1926).price(199.99).build(); - Art a2 = Art.builder().id(2).title("Melancholy III").artist("Munch").year(1902).price(280.00).build(); - Art a3 = Art.builder().id(3).title("Dancer").artist("Miro").year(1925).price(76.04).build(); - Art a4 = Art.builder().id(4).title("The Great Wave off Kanagawa").artist("Hokusai").price(167.30).build(); - - mongoTemplate.insert(Arrays.asList(a1, a2, a3, a4), Art.class); - - TypedAggregation aggregation = newAggregation(Art.class, // - bucketAuto(ArithmeticOperators.Multiply.valueOf("price").multiplyBy(10), 3) // - .withGranularity(Granularities.E12) // - .andOutputCount().as("count") // - .andOutput("title").push().as("titles") // - .andOutputExpression("price * 10").sum().as("sum")); - - AggregationResults result = mongoTemplate.aggregate(aggregation, Document.class); - assertThat(result.getMappedResults().size()).isEqualTo(3); - - // { "_id" : { "min" : 680.0 , "max" : 820.0 }, "count" : 1 , "titles" : [ "Dancer"] , "sum" : 760.4000000000001} - Document bound0 = result.getMappedResults().get(0); - assertThat(bound0).containsEntry("count", 1).containsEntry("titles.[0]", "Dancer").containsEntry("_id.min", 680.0) - .containsKey("_id.max"); - - // { "_id" : { "min" : 820.0 , "max" : 1800.0 }, "count" : 1 , "titles" : [ "The Great Wave off Kanagawa"] , "sum" : - // 1673.0} - Document bound1 = result.getMappedResults().get(1); - assertThat(bound1).containsEntry("count", 1).containsEntry("_id.min", 820.0); - assertThat((List) bound1.get("titles")).contains("The Great Wave off Kanagawa"); - assertThat((Double) bound1.get("sum")).isCloseTo(1673.0, Offset.offset(0.1D)); - } - - @Test // DATAMONGO-1552 - void facetShouldCreateFacets() { - - Art a1 = Art.builder().id(1).title("The Pillars of Society").artist("Grosz").year(1926).price(199.99).build(); - Art a2 = Art.builder().id(2).title("Melancholy III").artist("Munch").year(1902).price(280.00).build(); - Art a3 = Art.builder().id(3).title("Dancer").artist("Miro").year(1925).price(76.04).build(); - Art a4 = Art.builder().id(4).title("The Great Wave off Kanagawa").artist("Hokusai").price(167.30).build(); - - mongoTemplate.insert(Arrays.asList(a1, a2, a3, a4), Art.class); - - BucketAutoOperation bucketPrice = bucketAuto(ArithmeticOperators.Multiply.valueOf("price").multiplyBy(10), 3) // - .withGranularity(Granularities.E12) // - .andOutputCount().as("count") // - .andOutput("title").push().as("titles") // - .andOutputExpression("price * 10") // - .sum().as("sum"); - - TypedAggregation aggregation = newAggregation(Art.class, // - project("title", "artist", "year", "price"), // - facet(bucketPrice).as("categorizeByPrice") // - .and(bucketAuto("year", 3)).as("categorizeByYear")); - - AggregationResults result = mongoTemplate.aggregate(aggregation, Document.class); - assertThat(result.getMappedResults().size()).isEqualTo(1); - - Document mappedResult = result.getUniqueMappedResult(); - - // [ { "_id" : { "min" : 680.0 , "max" : 820.0} , "count" : 1 , "titles" : [ "Dancer"] , "sum" : 760.4000000000001} - // , - // { "_id" : { "min" : 820.0 , "max" : 1800.0} , "count" : 1 , "titles" : [ "The Great Wave off Kanagawa"] , "sum" : - // 1673.0} , - // { "_id" : { "min" : 1800.0 , "max" : 3300.0} , "count" : 2 , "titles" : [ "The Pillars of Society" , "Melancholy - // III"] , "sum" : 4799.9}] - List categorizeByPrice = (List) mappedResult.get("categorizeByPrice"); - assertThat(categorizeByPrice).hasSize(3); - - // [ { "_id" : { "min" : null , "max" : 1902} , "count" : 1} , - // { "_id" : { "min" : 1902-2018 , "max" : 1925} , "count" : 1} , - // { "_id" : { "min" : 1925-2018 , "max" : 1926} , "count" : 2}] - List categorizeByYear = (List) mappedResult.get("categorizeByYear"); - assertThat(categorizeByYear).hasSize(3); - } - - @Test // DATAMONGO-1986 - void runMatchOperationCriteriaThroughQueryMapperForTypedAggregation() { - - mongoTemplate.insertAll(TestEntities.geolocation().newYork()); - - Aggregation aggregation = newAggregation(Venue.class, - match(Criteria.where("location") - .within(new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)))), - project("id", "location", "name")); - - AggregationResults groupResults = mongoTemplate.aggregate(aggregation, "newyork", Document.class); - - assertThat(groupResults.getMappedResults().size()).isEqualTo(4); - } - - @Test // DATAMONGO-1986 - void runMatchOperationCriteriaThroughQueryMapperForUntypedAggregation() { - - mongoTemplate.insertAll(TestEntities.geolocation().newYork()); - - Aggregation aggregation = newAggregation( - match(Criteria.where("location") - .within(new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)))), - project("id", "location", "name")); - - AggregationResults groupResults = mongoTemplate.aggregate(aggregation, "newyork", Document.class); - - assertThat(groupResults.getMappedResults().size()).isEqualTo(4); - } - - @Test // DATAMONGO-2437 - void shouldReadComplexIdValueCorrectly() { - - WithComplexId source = new WithComplexId(); - source.id = new ComplexId(); - source.id.p1 = "v1"; - source.id.p2 = "v2"; - - mongoTemplate.save(source); - - AggregationResults result = mongoTemplate.aggregate(newAggregation(project("id")), - WithComplexId.class, WithComplexId.class); - assertThat(result.getMappedResults()).containsOnly(source); - } - - @Test // DATAMONGO-2536 - void skipOutputDoesNotReadBackAggregationResults() { - - createTagDocuments(); - - Aggregation agg = newAggregation( // - project("tags"), // - unwind("tags"), // - group("tags") // - .count().as("n"), // - project("n") // - .and("tag").previousOperation(), // - sort(DESC, "n") // - ).withOptions(AggregationOptions.builder().skipOutput().build()); - - AggregationResults results = mongoTemplate.aggregate(agg, INPUT_COLLECTION, TagCount.class); - - assertThat(results.getMappedResults()).isEmpty(); - assertThat(results.getRawResults()).isEmpty(); - } - - @Test // DATAMONGO-2635 - void mapsEnumsInMatchClauseUsingInCriteriaCorrectly() { - - WithEnum source = new WithEnum(); - source.enumValue = MyEnum.TWO; - source.id = "id-1"; - - mongoTemplate.save(source); - - Aggregation agg = newAggregation(match(where("enumValue").in(Collections.singletonList(MyEnum.TWO)))); - - AggregationResults results = mongoTemplate.aggregate(agg, mongoTemplate.getCollectionName(WithEnum.class), - Document.class); - assertThat(results.getMappedResults()).hasSize(1); - } - - private void createUsersWithReferencedPersons() { - - mongoTemplate.dropCollection(User.class); - mongoTemplate.dropCollection(Person.class); - - User user1 = new User("u1"); - User user2 = new User("u2"); - User user3 = new User("u3"); - - mongoTemplate.save(user1); - mongoTemplate.save(user2); - mongoTemplate.save(user3); - - Person person1 = new Person("u1", "User 1"); - Person person2 = new Person("u2", "User 2"); - - mongoTemplate.save(person1); - mongoTemplate.save(person2); - mongoTemplate.save(user3); - } - - private void assertLikeStats(LikeStats like, String id, long count) { - - assertThat(like).isNotNull(); - assertThat(like.id).isEqualTo(id); - assertThat(like.count).isEqualTo(count); - } - - private void createUserWithLikesDocuments() { - mongoTemplate.insert(new UserWithLikes("u1", new Date(), "a", "b", "c")); - mongoTemplate.insert(new UserWithLikes("u2", new Date(), "a")); - mongoTemplate.insert(new UserWithLikes("u3", new Date(), "b", "c")); - mongoTemplate.insert(new UserWithLikes("u4", new Date(), "c", "d", "e")); - mongoTemplate.insert(new UserWithLikes("u5", new Date(), "a", "e", "c")); - mongoTemplate.insert(new UserWithLikes("u6", new Date())); - mongoTemplate.insert(new UserWithLikes("u7", new Date(), "a")); - mongoTemplate.insert(new UserWithLikes("u8", new Date(), "x", "e")); - mongoTemplate.insert(new UserWithLikes("u9", new Date(), "y", "d")); - } - - private void createTagDocuments() { - - MongoCollection coll = mongoTemplate.getCollection(INPUT_COLLECTION); - - coll.insertOne(createDocument("Doc1", "spring", "mongodb", "nosql")); - coll.insertOne(createDocument("Doc2", "spring", "mongodb")); - coll.insertOne(createDocument("Doc3", "spring")); - } - - private static Document createDocument(String title, String... tags) { - - Document doc = new Document("title", title); - List tagList = new ArrayList(); - - for (String tag : tags) { - tagList.add(tag); - } - - doc.put("tags", tagList); - return doc; - } - - private static void assertTagCount(String tag, int n, TagCount tagCount) { - - assertThat(tagCount.getTag()).isEqualTo(tag); - assertThat(tagCount.getN()).isEqualTo(n); - } - - private static List toList(CloseableIterator results) { - - List result = new ArrayList(); - while (results.hasNext()) { - result.add(results.next()); - } - - return result; - } - - static class DATAMONGO753 { - - PD[] pd; - - DATAMONGO753 withPDs(PD... pds) { - this.pd = pds; - return this; - } - } - - static class PD { - - String pDch; - @org.springframework.data.mongodb.core.mapping.Field("alias") int up; - - PD(String pDch, int up) { - this.pDch = pDch; - this.up = up; - } - } - - static class DATAMONGO788 { - - int x; - int y; - int xField; - int yField; - - public DATAMONGO788() {} - - DATAMONGO788(int x, int y) { - this.x = x; - this.xField = x; - this.y = y; - this.yField = y; - } - } - - // DATAMONGO-806 - static class User { - - @Id String id; - List msgs; - - public User() {} - - User(String id, PushMessage... msgs) { - this.id = id; - this.msgs = Arrays.asList(msgs); - } - } - - // DATAMONGO-806 - static class PushMessage { - - @Id String id; - String content; - Date createDate; - - public PushMessage() {} - - PushMessage(String id, String content, Date createDate) { - this.id = id; - this.content = content; - this.createDate = createDate; - } - } - - @org.springframework.data.mongodb.core.mapping.Document - static class CarPerson { - - @Id private String id; - private String firstName; - private String lastName; - private Descriptors descriptors; - - CarPerson(String firstname, String lastname, Entry... entries) { - this.firstName = firstname; - this.lastName = lastname; - - this.descriptors = new Descriptors(); - - this.descriptors.carDescriptor = new CarDescriptor(entries); - } - } - - @SuppressWarnings("unused") - static class Descriptors { - - private CarDescriptor carDescriptor; - } - - static class CarDescriptor { - - private List entries = new ArrayList(); - - CarDescriptor(Entry... entries) { - - for (Entry entry : entries) { - this.entries.add(entry); - } - } - - @SuppressWarnings("unused") - static class Entry { - - private String make; - private String model; - private int year; - - public Entry() {} - - Entry(String make, String model, int year) { - this.make = make; - this.model = model; - this.year = year; - } - } - } - - static class Reservation { - - String hotelCode; - String confirmationNumber; - int timestamp; - - public Reservation() {} - - Reservation(String hotelCode, String confirmationNumber, int timestamp) { - this.hotelCode = hotelCode; - this.confirmationNumber = confirmationNumber; - this.timestamp = timestamp; - } - } - - static class ObjectWithDate { - - Date dateValue; - - ObjectWithDate(Date dateValue) { - this.dateValue = dateValue; - } - } - - // DATAMONGO-861 - @org.springframework.data.mongodb.core.mapping.Document(collection = "inventory") - static class InventoryItem { - - int id; - String item; - String description; - int qty; - - public InventoryItem() {} - - InventoryItem(int id, String item, int qty) { - - this.id = id; - this.item = item; - this.qty = qty; - } - - InventoryItem(int id, String item, String description, int qty) { - - this.id = id; - this.item = item; - this.description = description; - this.qty = qty; - } - } - - // DATAMONGO-1491 - @lombok.Data - @Builder - static class Sales { - - @Id String id; - List items; - } - - // DATAMONGO-1491 - @lombok.Data - @Builder - static class Item { - - @org.springframework.data.mongodb.core.mapping.Field("item_id") // - String itemId; - Integer quantity; - Long price; - } - - // DATAMONGO-1538 - @lombok.Data - @Builder - static class Sales2 { - - String id; - Integer price; - Float tax; - boolean applyDiscount; - } - - // DATAMONGO-1551 - @lombok.Data - @Builder - static class Employee { - - int id; - String name; - String reportsTo; - } - - // DATAMONGO-1552 - @lombok.Data - @Builder - static class Art { - - int id; - String title; - String artist; - Integer year; - double price; - } - - @lombok.Data - static class WithComplexId { - @Id ComplexId id; - } - - @lombok.Data - static class ComplexId { - String p1; - String p2; - } - - static enum MyEnum { - ONE, TWO - } - - @lombok.Data - static class WithEnum { - - @Id String id; - MyEnum enumValue; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java deleted file mode 100755 index 37aeb5b2cf..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.springframework.data.mongodb.core.DocumentTestUtils.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond; -import org.springframework.data.mongodb.core.aggregation.ProjectionOperationUnitTests.BookWithFieldAnnotation; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.Criteria; - -/** - * Unit tests for {@link Aggregation}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - */ -public class AggregationUnitTests { - - @Test - void rejectsNullAggregationOperation() { - assertThatIllegalArgumentException().isThrownBy(() -> newAggregation((AggregationOperation[]) null)); - } - - @Test - void rejectsNullTypedAggregationOperation() { - assertThatIllegalArgumentException().isThrownBy(() -> newAggregation(String.class, (AggregationOperation[]) null)); - } - - @Test - void rejectsNoAggregationOperation() { - assertThatIllegalArgumentException().isThrownBy(() -> newAggregation(new AggregationOperation[0])); - } - - @Test - void rejectsNoTypedAggregationOperation() { - assertThatIllegalArgumentException().isThrownBy(() -> newAggregation(String.class, new AggregationOperation[0])); - } - - @Test // DATAMONGO-753 - void checkForCorrectFieldScopeTransfer() { - - assertThatIllegalArgumentException().isThrownBy(() -> { - newAggregation( // - project("a", "b"), // - group("a").count().as("cnt"), // a was introduced to the context by the project operation - project("cnt", "b") // b was removed from the context by the group operation - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); // -> triggers IllegalArgumentException - }); - } - - @Test // DATAMONGO-753 - void unwindOperationShouldNotChangeAvailableFields() { - - newAggregation( // - project("a", "b"), // - unwind("a"), // - project("a", "b") // b should still be available - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - } - - @Test // DATAMONGO-1391 - void unwindOperationWithIndexShouldPreserveFields() { - - newAggregation( // - project("a", "b"), // - unwind("a", "x"), // - project("a", "b") // b should still be available - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - } - - @Test // DATAMONGO-1391 - void unwindOperationWithIndexShouldAddIndexField() { - - newAggregation( // - project("a", "b"), // - unwind("a", "x"), // - project("a", "x") // b should still be available - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - } - - @Test // DATAMONGO-1391 - void fullUnwindOperationShouldBuildCorrectClause() { - - Document agg = newAggregation( // - unwind("a", "x", true)).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - @SuppressWarnings("unchecked") - Document unwind = ((List) agg.get("pipeline")).get(0); - assertThat(unwind.get("$unwind", Document.class)). // - containsEntry("includeArrayIndex", "x"). // - containsEntry("preserveNullAndEmptyArrays", true); - } - - @Test // DATAMONGO-1391 - void unwindOperationWithPreserveNullShouldBuildCorrectClause() { - - Document agg = newAggregation( // - unwind("a", true)).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - @SuppressWarnings("unchecked") - Document unwind = ((List) agg.get("pipeline")).get(0); - assertThat(unwind) // - .doesNotContainKey("$unwind.includeArrayIndex") // - .containsEntry("$unwind.preserveNullAndEmptyArrays", true); - } - - @Test // DATAMONGO-1550 - void replaceRootOperationShouldBuildCorrectClause() { - - Document agg = newAggregation( // - replaceRoot().withDocument().andValue("value").as("field")) // - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - @SuppressWarnings("unchecked") - Document unwind = ((List) agg.get("pipeline")).get(0); - assertThat(unwind).containsEntry("$replaceRoot.newRoot", new Document("field", "value")); - } - - @Test // DATAMONGO-753 - void matchOperationShouldNotChangeAvailableFields() { - - newAggregation( // - project("a", "b"), // - match(where("a").gte(1)), // - project("a", "b") // b should still be available - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - } - - @Test // DATAMONGO-788 - void referencesToGroupIdsShouldBeRenderedAsReferences() { - - Document agg = newAggregation( // - project("a"), // - group("a").count().as("aCnt"), // - project("aCnt", "a") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - @SuppressWarnings("unchecked") - Document secondProjection = ((List) agg.get("pipeline")).get(2); - Document fields = getAsDocument(secondProjection, "$project"); - assertThat(fields.get("aCnt")).isEqualTo(1); - assertThat(fields.get("a")).isEqualTo("$_id.a"); - } - - @Test // DATAMONGO-791 - void allowAggregationOperationsToBePassedAsIterable() { - - List ops = new ArrayList(); - ops.add(project("a")); - ops.add(group("a").count().as("aCnt")); - ops.add(project("aCnt", "a")); - - Document agg = newAggregation(ops).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - @SuppressWarnings("unchecked") - Document secondProjection = ((List) agg.get("pipeline")).get(2); - Document fields = getAsDocument(secondProjection, "$project"); - assertThat(fields.get("aCnt")).isEqualTo(1); - assertThat(fields.get("a")).isEqualTo("$_id.a"); - } - - @Test // DATAMONGO-791 - void allowTypedAggregationOperationsToBePassedAsIterable() { - - List ops = new ArrayList<>(); - ops.add(project("a")); - ops.add(group("a").count().as("aCnt")); - ops.add(project("aCnt", "a")); - - Document agg = newAggregation(Document.class, ops).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - @SuppressWarnings("unchecked") - Document secondProjection = ((List) agg.get("pipeline")).get(2); - Document fields = getAsDocument(secondProjection, "$project"); - assertThat(fields.get("aCnt")).isEqualTo((Object) 1); - assertThat(fields.get("a")).isEqualTo((Object) "$_id.a"); - } - - @Test // DATAMONGO-838 - void expressionBasedFieldsShouldBeReferencableInFollowingOperations() { - - Document agg = newAggregation( // - project("a").andExpression("b+c").as("foo"), // - group("a").sum("foo").as("foosum") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - @SuppressWarnings("unchecked") - Document secondProjection = ((List) agg.get("pipeline")).get(1); - Document fields = getAsDocument(secondProjection, "$group"); - assertThat(fields.get("foosum")).isEqualTo(new Document("$sum", "$foo")); - } - - @Test // DATAMONGO-908 - void shouldSupportReferingToNestedPropertiesInGroupOperation() { - - Document agg = newAggregation( // - project("cmsParameterId", "rules"), // - unwind("rules"), // - group("cmsParameterId", "rules.ruleType").count().as("totol") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isNotNull(); - - Document group = ((List) agg.get("pipeline")).get(2); - Document fields = getAsDocument(group, "$group"); - Document id = getAsDocument(fields, "_id"); - - assertThat(id.get("ruleType")).isEqualTo("$rules.ruleType"); - } - - @Test // DATAMONGO-1585 - void shouldSupportSortingBySyntheticAndExposedGroupFields() { - - Document agg = newAggregation( // - group("cmsParameterId").addToSet("title").as("titles"), // - sort(Direction.ASC, "cmsParameterId", "titles") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isNotNull(); - - Document sort = ((List) agg.get("pipeline")).get(1); - - assertThat(getAsDocument(sort, "$sort")) - .isEqualTo(Document.parse("{ \"_id.cmsParameterId\" : 1 , \"titles\" : 1}")); - } - - @Test // DATAMONGO-1585 - void shouldSupportSortingByProjectedFields() { - - Document agg = newAggregation( // - project("cmsParameterId") // - .and(SystemVariable.CURRENT + ".titles").as("titles") // - .and("field").as("alias"), // - sort(Direction.ASC, "cmsParameterId", "titles", "alias") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isNotNull(); - - Document sort = ((List) agg.get("pipeline")).get(1); - - assertThat(getAsDocument(sort, "$sort")).containsEntry("cmsParameterId", 1) // - .containsEntry("titles", 1) // - .containsEntry("alias", 1); - } - - @Test // DATAMONGO-924 - void referencingProjectionAliasesFromPreviousStepShouldReferToTheSameFieldTarget() { - - Document agg = newAggregation( // - project().and("foo.bar").as("ba") // - , project().and("ba").as("b") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document projection0 = extractPipelineElement(agg, 0, "$project"); - assertThat(projection0).isEqualTo(new Document("ba", "$foo.bar")); - - Document projection1 = extractPipelineElement(agg, 1, "$project"); - assertThat(projection1).isEqualTo(new Document("b", "$ba")); - } - - @Test // DATAMONGO-960 - void shouldRenderAggregationWithDefaultOptionsCorrectly() { - - Document agg = newAggregation( // - project().and("a").as("aa") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ \"aggregate\" : \"foo\" , \"pipeline\" : [ { \"$project\" : { \"aa\" : \"$a\"}}]}")); - } - - @Test // DATAMONGO-960 - void shouldRenderAggregationWithCustomOptionsCorrectly() { - - AggregationOptions aggregationOptions = newAggregationOptions().explain(true).cursor(new Document("foo", 1)) - .allowDiskUse(true).build(); - - Document agg = newAggregation( // - project().and("a").as("aa") // - ) // - .withOptions(aggregationOptions) // - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ \"aggregate\" : \"foo\" , " // - + "\"pipeline\" : [ { \"$project\" : { \"aa\" : \"$a\"}}] , " // - + "\"allowDiskUse\" : true , " // - + "\"explain\" : true , " // - + "\"cursor\" : { \"foo\" : 1}}") // - ); - } - - @Test // DATAMONGO-954, DATAMONGO-1585 - void shouldSupportReferencingSystemVariables() { - - Document agg = newAggregation( // - project("someKey") // - .and("a").as("a1") // - .and(Aggregation.CURRENT + ".a").as("a2") // - , sort(Direction.DESC, "a1") // - , group("someKey").first(Aggregation.ROOT).as("doc") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document projection0 = extractPipelineElement(agg, 0, "$project"); - assertThat(projection0).isEqualTo(new Document("someKey", 1).append("a1", "$a").append("a2", "$$CURRENT.a")); - - Document sort = extractPipelineElement(agg, 1, "$sort"); - assertThat(sort).isEqualTo(new Document("a1", -1)); - - Document group = extractPipelineElement(agg, 2, "$group"); - assertThat(group).isEqualTo(new Document("_id", "$someKey").append("doc", new Document("$first", "$$ROOT"))); - } - - @Test // DATAMONGO-1254 - void shouldExposeAliasedFieldnameForProjectionsIncludingOperationsDownThePipeline() { - - Document agg = Aggregation.newAggregation(// - project("date") // - .and("tags").minus(10).as("tags_count")// - , group("date")// - .sum("tags_count").as("count")// - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document group = extractPipelineElement(agg, 1, "$group"); - assertThat(getAsDocument(group, "count")).isEqualTo(new Document().append("$sum", "$tags_count")); - } - - @Test // DATAMONGO-1254 - void shouldUseAliasedFieldnameForProjectionsIncludingOperationsDownThePipelineWhenUsingSpEL() { - - Document agg = Aggregation.newAggregation(// - project("date") // - .andExpression("tags-10")// - , group("date")// - .sum("tags_count").as("count")// - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document group = extractPipelineElement(agg, 1, "$group"); - assertThat(getAsDocument(group, "count")).isEqualTo(new Document().append("$sum", "$tags_count")); - } - - @Test // DATAMONGO-861 - void conditionExpressionBasedFieldsShouldBeReferencableInFollowingOperations() { - - Document agg = newAggregation( // - project("a", "answer"), // - group("a") - .first(Cond.newBuilder().when(Criteria.where("a").gte(42)).thenValueOf("answer").otherwise("no-answer")) - .as("foosum") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - @SuppressWarnings("unchecked") - Document secondProjection = ((List) agg.get("pipeline")).get(1); - Document fields = getAsDocument(secondProjection, "$group"); - assertThat(getAsDocument(fields, "foosum")).containsKey("$first"); - assertThat(getAsDocument(fields, "foosum")).containsEntry("$first.$cond.then", "$answer"); - assertThat(getAsDocument(fields, "foosum")).containsEntry("$first.$cond.else", "no-answer"); - } - - @Test // DATAMONGO-861 - void shouldRenderProjectionConditionalExpressionCorrectly() { - - Document agg = Aggregation.newAggregation(// - project().and(ConditionalOperators.Cond.newBuilder() // - .when("isYellow") // - .then("bright") // - .otherwise("dark")).as("color")) - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document project = extractPipelineElement(agg, 0, "$project"); - Document expectedCondition = new Document() // - .append("if", "$isYellow") // - .append("then", "bright") // - .append("else", "dark"); - - assertThat(getAsDocument(project, "color")).containsEntry("$cond", expectedCondition); - } - - @Test // DATAMONGO-861 - void shouldRenderProjectionConditionalCorrectly() { - - Document agg = Aggregation.newAggregation(// - project().and("color").applyCondition(ConditionalOperators.Cond.newBuilder() // - .when("isYellow") // - .then("bright") // - .otherwise("dark"))) - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document project = extractPipelineElement(agg, 0, "$project"); - Document expectedCondition = new Document() // - .append("if", "$isYellow") // - .append("then", "bright") // - .append("else", "dark"); - - assertThat(getAsDocument(project, "color")).containsEntry("$cond", expectedCondition); - } - - @Test // DATAMONGO-861 - void shouldRenderProjectionConditionalWithCriteriaCorrectly() { - - Document agg = Aggregation.newAggregation(project()// - .and("color")// - .applyCondition(ConditionalOperators.Cond.newBuilder().when(Criteria.where("key").gt(5)) // - .then("bright").otherwise("dark"))) // - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document project = extractPipelineElement(agg, 0, "$project"); - Document expectedCondition = new Document() // - .append("if", new Document("$gt", Arrays. asList("$key", 5))) // - .append("then", "bright") // - .append("else", "dark"); - - assertThat(getAsDocument(project, "color")).containsEntry("$cond", expectedCondition); - } - - @Test // DATAMONGO-861, DATAMONGO-2242 - void referencingProjectionAliasesShouldRenderProjectionConditionalWithFieldReferenceCorrectly() { - - Document agg = Aggregation.newAggregation(// - project().and("color").as("chroma"), project().and("luminosity") // - .applyCondition(ConditionalOperators // - .when("chroma") // - .then("bright") // - .otherwise("dark"))) // - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document project = extractPipelineElement(agg, 1, "$project"); - Document expectedCondition = new Document() // - .append("if", "$chroma") // - .append("then", "bright") // - .append("else", "dark"); - - assertThat(getAsDocument(project, "luminosity")).containsEntry("$cond", expectedCondition); - } - - @Test // DATAMONGO-861 - void referencingProjectionAliasesShouldRenderProjectionConditionalWithCriteriaReferenceCorrectly() { - - Document agg = Aggregation.newAggregation(// - project().and("color").as("chroma"), project().and("luminosity") // - .applyCondition(ConditionalOperators.Cond.newBuilder().when(Criteria.where("chroma") // - .is(100)) // - .then("bright").otherwise("dark"))) // - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document project = extractPipelineElement(agg, 1, "$project"); - Document expectedCondition = new Document() // - .append("if", new Document("$eq", Arrays. asList("$chroma", 100))) // - .append("then", "bright") // - .append("else", "dark"); - - assertThat(getAsDocument(project, "luminosity")).containsEntry("$cond", expectedCondition); - } - - @Test // DATAMONGO-861 - void shouldRenderProjectionIfNullWithFieldReferenceCorrectly() { - - Document agg = Aggregation.newAggregation(// - project().and("color"), // - project().and("luminosity") // - .applyCondition(ConditionalOperators // - .ifNull("chroma") // - .then("unknown"))) // - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document project = extractPipelineElement(agg, 1, "$project"); - - assertThat(getAsDocument(project, "luminosity")).containsEntry("$ifNull", Arrays.asList("$chroma", "unknown")); - } - - @Test // DATAMONGO-861 - void shouldRenderProjectionIfNullWithFallbackFieldReferenceCorrectly() { - - Document agg = Aggregation.newAggregation(// - project("fallback").and("color").as("chroma"), project().and("luminosity") // - .applyCondition(ConditionalOperators.ifNull("chroma") // - .thenValueOf("fallback"))) // - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document project = extractPipelineElement(agg, 1, "$project"); - - assertThat(getAsDocument(project, "luminosity")).containsEntry("$ifNull", Arrays.asList("$chroma", "$fallback")); - } - - @Test // DATAMONGO-1552 - void shouldHonorDefaultCountField() { - - Document agg = Aggregation.newAggregation(// - bucket("year"), // - project("count")) // - .toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - Document project = extractPipelineElement(agg, 1, "$project"); - - assertThat(project).containsEntry("count", 1); - } - - @Test // DATAMONGO-1533 - void groupOperationShouldAllowUsageOfDerivedSpELAggregationExpression() { - - Document agg = newAggregation( // - project("a"), // - group("a").first(AggregationSpELExpression.expressionOf("cond(a >= 42, 'answer', 'no-answer')")).as("foosum") // - ).toDocument("foo", Aggregation.DEFAULT_CONTEXT); - - @SuppressWarnings("unchecked") - Document secondProjection = ((List) agg.get("pipeline")).get(1); - Document fields = getAsDocument(secondProjection, "$group"); - assertThat(getAsDocument(fields, "foosum")).containsKey("$first"); - assertThat(getAsDocument(fields, "foosum")).containsEntry("$first.$cond.if", - new Document("$gte", Arrays.asList("$a", 42))); - assertThat(getAsDocument(fields, "foosum")).containsEntry("$first.$cond.then", "answer"); - assertThat(getAsDocument(fields, "foosum")).containsEntry("$first.$cond.else", "no-answer"); - } - - @Test // DATAMONGO-1756 - void projectOperationShouldRenderNestedFieldNamesCorrectly() { - - Document agg = newAggregation(project().and("value1.value").plus("value2.value").as("val")).toDocument("collection", - Aggregation.DEFAULT_CONTEXT); - - Document expected = new Document("val", new Document("$add", Arrays.asList("$value1.value", "$value2.value"))); - assertThat(extractPipelineElement(agg, 0, "$project")).isEqualTo(expected); - } - - @Test // DATAMONGO-1871 - void providedAliasShouldAllowNestingExpressionWithAliasCorrectly() { - - Document condition = new Document("$and", - Arrays.asList(new Document("$gte", Arrays.asList("$$est.dt", "2015-12-29")), // - new Document("$lte", Arrays.asList("$$est.dt", "2017-12-29")) // - )); - - Aggregation agg = newAggregation(project("_id", "dId", "aId", "cty", "cat", "plts.plt") - .and(ArrayOperators.arrayOf("plts.ests").filter().as("est").by(condition)).as("plts.ests")); - - Document $project = extractPipelineElement(agg.toDocument("collection-1", Aggregation.DEFAULT_CONTEXT), 0, - "$project"); - - assertThat($project).containsKey("plts.ests"); - } - - @Test // DATAMONGO-2377 - void shouldAllowInternalThisAndValueReferences() { - - Document untyped = newAggregation( // - Arrays.asList( // - (group("uid", "data.sourceId") // - .push("data.attributeRecords").as("attributeRecordArrays")), // - (project() // - .and(ArrayOperators.arrayOf("attributeRecordArrays") // - .reduce(ArrayOperators.arrayOf("$$value").concat("$$this")) // - .startingWith(Collections.emptyList())) // - .as("attributeRecordArrays")) // - )).toDocument("collection-1", DEFAULT_CONTEXT); - - assertThat(extractPipelineElement(untyped, 1, "$project")).isEqualTo(Document.parse( - "{\"attributeRecordArrays\": {\"$reduce\": {\"input\": \"$attributeRecordArrays\", \"initialValue\": [], \"in\": {\"$concatArrays\": [\"$$value\", \"$$this\"]}}}}")); - } - - @Test // DATAMONGO-2644 - void projectOnIdIsAlwaysValid() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - Document target = new Aggregation(bucket("start"), project("_id")).toDocument("collection-1", - new RelaxedTypeBasedAggregationOperationContext(BookWithFieldAnnotation.class, mappingContext, - new QueryMapper(new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext)))); - - assertThat(extractPipelineElement(target, 1, "$project")).isEqualTo(Document.parse(" { \"_id\" : \"$_id\" }")); - } - - private Document extractPipelineElement(Document agg, int index, String operation) { - - List pipeline = (List) agg.get("pipeline"); - return (Document) pipeline.get(index).get(operation); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdateUnitTests.java deleted file mode 100644 index 86c62ff91b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdateUnitTests.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2019-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link AggregationUpdate}. - * - * @author Christoph Strobl - */ -public class AggregationUpdateUnitTests { - - @Test // DATAMONGO-2331 - public void createPipelineWithMultipleStages() { - - assertThat(AggregationUpdate.update() // - .set("stage-1").toValue("value-1") // - .unset("stage-2") // - .set("stage-3").toValue("value-3") // - .toPipeline(Aggregation.DEFAULT_CONTEXT)) // - .containsExactly(new Document("$set", new Document("stage-1", "value-1")), - new Document("$unset", "stage-2"), new Document("$set", new Document("stage-3", "value-3"))); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java deleted file mode 100644 index 940a315239..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2019-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.ArithmeticOperators.*; - -import java.util.Arrays; -import java.util.Collections; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link Round}. - * - * @author Christoph Strobl - */ -public class ArithmeticOperatorsUnitTests { - - @Test // DATAMONGO-2370 - void roundShouldWithoutPlace() { - - assertThat(valueOf("field").round().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(new Document("$round", Collections.singletonList("$field"))); - } - - @Test // DATAMONGO-2370 - void roundShouldWithPlace() { - - assertThat(valueOf("field").roundToPlace(3).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(new Document("$round", Arrays.asList("$field", 3))); - } - - @Test // DATAMONGO-2370 - void roundShouldWithPlaceFromField() { - - assertThat(valueOf("field").round().placeOf("my-field").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(new Document("$round", Arrays.asList("$field", "$my-field"))); - } - - @Test // DATAMONGO-2370 - void roundShouldWithPlaceFromExpression() { - - assertThat(valueOf("field").round().placeOf((ctx -> new Document("$first", "$source"))) - .toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(new Document("$round", Arrays.asList("$field", new Document("$first", "$source")))); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArrayOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArrayOperatorsUnitTests.java deleted file mode 100644 index 8844b606de..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArrayOperatorsUnitTests.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2018-2021 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.aggregation; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.aggregation.ArrayOperators.ArrayToObject; - -/** - * Unit tests for {@link ArrayOperators} - * - * @author Christoph Strobl - * @author Shashank Sharma - * @currentRead Royal Assassin - Robin Hobb - */ -public class ArrayOperatorsUnitTests { - - static final List VALUE_LIST = Arrays.asList(1, "2", new Document("_id", 3)); - static final String VALUE_LIST_STRING = "[1, \"2\", { \"_id\" : 3 }]"; - static final String EXPRESSION_STRING = "{ \"$stablemaster\" : \"burrich\" }"; - static final Document EXPRESSION_DOC = Document.parse(EXPRESSION_STRING); - static final AggregationExpression EXPRESSION = context -> EXPRESSION_DOC; - - @Test // DATAMONGO-2052 - public void toArrayWithFieldReference() { - - assertThat(ArrayOperators.arrayOf("regal").toObject().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $arrayToObject: \"$regal\" } "); - } - - @Test // DATAMONGO-2052 - public void toArrayWithExpression() { - - assertThat(ArrayOperators.arrayOf(EXPRESSION).toObject().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $arrayToObject: " + EXPRESSION_STRING + "} "); - } - - @Test // DATAMONGO-2052 - public void toArrayWithArgumentList() { - - List> source = new ArrayList<>(); - source.add(Arrays.asList("king", "shrewd")); - source.add(Arrays.asList("prince", "verity")); - - assertThat(ArrayToObject.arrayToObject(source).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $arrayToObject: [ [ \"king\", \"shrewd\"], [ \"prince\", \"verity\" ] ] } "); - } - - @Test // DATAMONGO-2287 - public void arrayElementAtWithValueList() { - - assertThat(ArrayOperators.arrayOf(VALUE_LIST).elementAt(1).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $arrayElemAt: [ " + VALUE_LIST_STRING + ", 1] } "); - } - - @Test // DATAMONGO-2287 - public void concatWithValueList() { - - assertThat(ArrayOperators.arrayOf(VALUE_LIST).concat("field").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $concatArrays: [ " + VALUE_LIST_STRING + ", \"$field\"] } "); - } - - @Test // DATAMONGO-2287 - public void filterWithValueList() { - - assertThat(ArrayOperators.arrayOf(VALUE_LIST).filter().as("var").by(new Document()) - .toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $filter: { \"input\" : " + VALUE_LIST_STRING + ", \"as\" : \"var\", \"cond\" : {} } } "); - } - - @Test // DATAMONGO-2287 - public void lengthWithValueList() { - - assertThat(ArrayOperators.arrayOf(VALUE_LIST).length().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $size: [ " + VALUE_LIST_STRING + "] } "); - } - - @Test // DATAMONGO-2287 - public void sliceWithValueList() { - - assertThat(ArrayOperators.arrayOf(VALUE_LIST).slice().itemCount(3).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $slice: [ " + VALUE_LIST_STRING + ", 3] } "); - } - - @Test // DATAMONGO-2287 - public void indexOfWithValueList() { - - assertThat(ArrayOperators.arrayOf(VALUE_LIST).indexOf("s1p").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $indexOfArray: [ " + VALUE_LIST_STRING + ", \"s1p\"] } "); - } - - @Test // DATAMONGO-2287 - public void reverseWithValueList() { - - assertThat(ArrayOperators.arrayOf(VALUE_LIST).reverse().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $reverseArray: [ " + VALUE_LIST_STRING + "] } "); - } - - @Test // DATAMONGO-2287 - public void zipWithValueList() { - - assertThat(ArrayOperators.arrayOf(VALUE_LIST).zipWith("field").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ $zip: { \"inputs\": [" + VALUE_LIST_STRING + ", \"$field\"]} } "); - } - - @Test // DATAMONGO-2287 - public void inWithValueList() { - - assertThat(ArrayOperators.arrayOf(VALUE_LIST).containsValue("$userName").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo("{ \"$in\" : [\"$userName\", " + VALUE_LIST_STRING + "] }"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperationUnitTests.java deleted file mode 100644 index 0e47a8000e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperationUnitTests.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.DocumentTestUtils.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.Granularities; - -/** - * Unit tests for {@link BucketAutoOperation}. - * - * @author Mark Paluch - */ -public class BucketAutoOperationUnitTests { - - @Test // DATAMONGO-1552 - public void rejectsNullFields() { - assertThatIllegalArgumentException().isThrownBy(() -> new BucketAutoOperation((Field) null, 0)); - } - - @Test // DATAMONGO-1552 - public void rejectsNonPositiveIntegerNullFields() { - assertThatIllegalArgumentException().isThrownBy(() -> new BucketAutoOperation(Fields.field("field"), 0)); - } - - @Test // DATAMONGO-1552 - public void shouldRenderBucketOutputExpressions() { - - BucketAutoOperation operation = Aggregation.bucketAuto("field", 5) // - .andOutputExpression("(netPrice + surCharge) * taxrate * [0]", 2).as("grossSalesPrice") // - .andOutput("title").push().as("titles"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse( - "{ \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"titles\" : { $push: \"$title\" } }}")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderEmptyAggregationExpression() { - assertThatIllegalStateException().isThrownBy(() -> bucket("groupby").andOutput("field").as("alias")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderBucketOutputOperators() { - - BucketAutoOperation operation = Aggregation.bucketAuto("field", 5) // - .andOutputCount().as("titles"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ titles : { $sum: 1 } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderCorrectly() { - - Document agg = bucketAuto("field", 1).withBuckets(5).toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $bucketAuto: { groupBy: \"$field\", buckets: 5 } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderGranulariy() { - - Document agg = bucketAuto("field", 1) // - .withGranularity(Granularities.E24) // - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $bucketAuto: { buckets: 1, granularity: \"E24\", groupBy: \"$field\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderSumOperator() { - - BucketAutoOperation operation = bucketAuto("field", 5) // - .andOutput("score").sum().as("cummulated_score"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ cummulated_score : { $sum: \"$score\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderSumWithOwnOutputExpression() { - - BucketAutoOperation operation = bucketAuto("field", 5) // - .andOutputExpression("netPrice + tax").apply("$multiply", 5).as("total"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)) - .isEqualTo(Document.parse("{ total : { $multiply: [ {$add : [\"$netPrice\", \"$tax\"]}, 5] } }")); - } - - private static Document extractOutput(Document fromBucketClause) { - return getAsDocument(getAsDocument(fromBucketClause, "$bucketAuto"), "output"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketOperationUnitTests.java deleted file mode 100644 index 3a7475798a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketOperationUnitTests.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link BucketOperation}. - * - * @author Mark Paluch - */ -public class BucketOperationUnitTests { - - @Test // DATAMONGO-1552 - public void rejectsNullFields() { - assertThatIllegalArgumentException().isThrownBy(() -> new BucketOperation((Field) null)); - } - - @Test // DATAMONGO-1552 - public void shouldRenderBucketOutputExpressions() { - - BucketOperation operation = Aggregation.bucket("field") // - .andOutputExpression("(netPrice + surCharge) * taxrate * [0]", 2).as("grossSalesPrice") // - .andOutput("title").push().as("titles"); - - Document dbObject = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(dbObject)).isEqualTo(Document.parse( - "{ \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"titles\" : { $push: \"$title\" } }}")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderEmptyAggregationExpression() { - assertThatIllegalStateException().isThrownBy(() -> bucket("groupby").andOutput("field").as("alias")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderBucketOutputOperators() { - - BucketOperation operation = Aggregation.bucket("field") // - .andOutputCount().as("titles"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ titles : { $sum: 1 } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderSumAggregationExpression() { - - Document agg = bucket("field") // - .andOutput(ArithmeticOperators.valueOf("quizzes").sum()).as("quizTotal") // - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $bucket: { groupBy: \"$field\", boundaries: [], output : { quizTotal: { $sum: \"$quizzes\"} } } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderDefault() { - - Document agg = bucket("field").withDefaultBucket("default bucket").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $bucket: { groupBy: \"$field\", boundaries: [], default: \"default bucket\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderBoundaries() { - - Document agg = bucket("field") // - .withDefaultBucket("default bucket") // - .withBoundaries(0) // - .withBoundaries(10, 20).toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $bucket: { boundaries: [0, 10, 20], default: \"default bucket\", groupBy: \"$field\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderSumOperator() { - - BucketOperation operation = bucket("field") // - .andOutput("score").sum().as("cummulated_score"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ cummulated_score : { $sum: \"$score\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderSumWithValueOperator() { - - BucketOperation operation = bucket("field") // - .andOutput("score").sum(4).as("cummulated_score"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ cummulated_score : { $sum: 4 } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderAvgOperator() { - - BucketOperation operation = bucket("field") // - .andOutput("score").avg().as("average"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ average : { $avg: \"$score\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderFirstOperator() { - - BucketOperation operation = bucket("field") // - .andOutput("title").first().as("first_title"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ first_title : { $first: \"$title\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderLastOperator() { - - BucketOperation operation = bucket("field") // - .andOutput("title").last().as("last_title"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ last_title : { $last: \"$title\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderMinOperator() { - - BucketOperation operation = bucket("field") // - .andOutput("score").min().as("min_score"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ min_score : { $min: \"$score\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderPushOperator() { - - BucketOperation operation = bucket("field") // - .andOutput("title").push().as("titles"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ titles : { $push: \"$title\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderAddToSetOperator() { - - BucketOperation operation = bucket("field") // - .andOutput("title").addToSet().as("titles"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)).isEqualTo(Document.parse("{ titles : { $addToSet: \"$title\" } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderSumWithExpression() { - - BucketOperation operation = bucket("field") // - .andOutputExpression("netPrice + tax").sum().as("total"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)) - .isEqualTo(Document.parse("{ total : { $sum: { $add : [\"$netPrice\", \"$tax\"]} } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderSumWithOwnOutputExpression() { - - BucketOperation operation = bucket("field") // - .andOutputExpression("netPrice + tax").apply("$multiply", 5).as("total"); - - Document agg = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(extractOutput(agg)) - .isEqualTo(Document.parse("{ total : { $multiply: [ {$add : [\"$netPrice\", \"$tax\"]}, 5] } }")); - } - - @Test // DATAMONGO-1552 - public void shouldExposeDefaultCountField() { - - BucketOperation operation = bucket("field"); - - assertThat(operation.getFields().exposesSingleFieldOnly()).isTrue(); - assertThat(operation.getFields().getField("count")).isNotNull(); - } - - private static Document extractOutput(Document fromBucketClause) { - return (Document) ((Document) fromBucketClause.get("$bucket")).get("output"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/City.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/City.java deleted file mode 100644 index 509594cacf..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/City.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017-2021 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.aggregation; - -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; - -/** - * @author Thomas Darimont - * @author Mark Paluch - */ -@lombok.Data -@AllArgsConstructor -@NoArgsConstructor -class City { - - String name; - int population; - - public String toString() { - return "City [name=" + name + ", population=" + population + "]"; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CondExpressionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CondExpressionUnitTests.java deleted file mode 100644 index 691100c7fb..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CondExpressionUnitTests.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond; -import org.springframework.data.mongodb.core.query.Criteria; - -/** - * Unit tests for {@link Cond}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -public class CondExpressionUnitTests { - - @Test // DATAMONGO-861 - void builderRejectsEmptyFieldName() { - assertThatIllegalArgumentException().isThrownBy(() -> newBuilder().when("")); - } - - @Test // DATAMONGO-861 - void builderRejectsNullFieldName() { - assertThatIllegalArgumentException().isThrownBy(() -> newBuilder().when((Document) null)); - } - - @Test // DATAMONGO-861 - void builderRejectsNullCriteriaName() { - assertThatIllegalArgumentException().isThrownBy(() -> newBuilder().when((Criteria) null)); - } - - @Test // DATAMONGO-861 - void builderRejectsBuilderAsThenValue() { - assertThatIllegalArgumentException().isThrownBy( - () -> newBuilder().when("isYellow").then(newBuilder().when("field").then("then-value")).otherwise("otherwise")); - } - - @Test // DATAMONGO-861, DATAMONGO-1542, DATAMONGO-2242 - void simpleBuilderShouldRenderCorrectly() { - - Cond operator = ConditionalOperators.when("isYellow").thenValueOf("bright").otherwise("dark"); - Document document = operator.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document expectedCondition = new Document() // - .append("if", "$isYellow") // - .append("then", "$bright") // - .append("else", "dark"); - - assertThat(document).containsEntry("$cond", expectedCondition); - } - - @Test // DATAMONGO-861, DATAMONGO-1542, DATAMONGO-2242 - void simpleCriteriaShouldRenderCorrectly() { - - Cond operator = ConditionalOperators.when(Criteria.where("luminosity").gte(100)).thenValueOf("bright") - .otherwise("dark"); - Document document = operator.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document expectedCondition = new Document() // - .append("if", new Document("$gte", Arrays. asList("$luminosity", 100))) // - .append("then", "$bright") // - .append("else", "dark"); - - assertThat(document).containsEntry("$cond", expectedCondition); - } - - @Test // DATAMONGO-861, DATAMONGO-2242 - void andCriteriaShouldRenderCorrectly() { - - Cond operator = ConditionalOperators.when(Criteria.where("luminosity").gte(100) // - .andOperator(Criteria.where("hue").is(50), // - Criteria.where("saturation").lt(11))) - .thenValueOf("bright").otherwiseValueOf("dark-field"); - - Document document = operator.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document luminosity = new Document("$gte", Arrays. asList("$luminosity", 100)); - Document hue = new Document("$eq", Arrays. asList("$hue", 50)); - Document saturation = new Document("$lt", Arrays. asList("$saturation", 11)); - - Document expectedCondition = new Document() // - .append("if", Arrays. asList(luminosity, new Document("$and", Arrays.asList(hue, saturation)))) // - .append("then", "$bright") // - .append("else", "$dark-field"); - - assertThat(document).containsEntry("$cond", expectedCondition); - } - - @Test // DATAMONGO-861, DATAMONGO-1542, DATAMONGO-2242 - void twoArgsCriteriaShouldRenderCorrectly() { - - Criteria criteria = Criteria.where("luminosity").gte(100) // - .and("saturation").and("chroma").is(200); - Cond operator = ConditionalOperators.when(criteria).thenValueOf("bright").otherwise("dark"); - - Document document = operator.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document gte = new Document("$gte", Arrays. asList("$luminosity", 100)); - Document is = new Document("$eq", Arrays. asList("$chroma", 200)); - - Document expectedCondition = new Document() // - .append("if", Arrays.asList(gte, is)) // - .append("then", "$bright") // - .append("else", "dark"); - - assertThat(document).containsEntry("$cond", expectedCondition); - } - - @Test // DATAMONGO-861, DATAMONGO-1542 - void nestedCriteriaShouldRenderCorrectly() { - - Cond operator = ConditionalOperators.when(Criteria.where("luminosity").gte(100)) // - .thenValueOf(newBuilder() // - .when(Criteria.where("luminosity").gte(200)) // - .then("verybright") // - .otherwise("not-so-bright")) // - .otherwise(newBuilder() // - .when(Criteria.where("luminosity").lt(50)) // - .then("very-dark") // - .otherwise("not-so-dark")); - - Document document = operator.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document trueCondition = new Document() // - .append("if", new Document("$gte", Arrays. asList("$luminosity", 200))) // - .append("then", "verybright") // - .append("else", "not-so-bright"); - - Document falseCondition = new Document() // - .append("if", new Document("$lt", Arrays. asList("$luminosity", 50))) // - .append("then", "very-dark") // - .append("else", "not-so-dark"); - - assertThat(document).containsEntry("$cond.then.$cond", trueCondition); - assertThat(document).containsEntry("$cond.else.$cond", falseCondition); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ConvertOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ConvertOperatorsUnitTests.java deleted file mode 100644 index a44c932723..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ConvertOperatorsUnitTests.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2018-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link ConvertOperators}. - * - * @author Christoph Strobl - * @currentRead Royal Assassin - Robin Hobb - */ -public class ConvertOperatorsUnitTests { - - static final String EXPRESSION_STRING = "{ \"$molly\" : \"chandler\" }"; - static final Document EXPRESSION_DOC = Document.parse(EXPRESSION_STRING); - static final AggregationExpression EXPRESSION = context -> EXPRESSION_DOC; - - @Test // DATAMONGO-2048 - public void convertToUsingStringIdentifier() { - - assertThat(ConvertOperators.valueOf("shrewd").convertTo("double").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $convert: { \"input\" : \"$shrewd\", \"to\" : \"double\" } } ")); - } - - @Test // DATAMONGO-2048 - public void convertToUsingIntIdentifier() { - - assertThat(ConvertOperators.valueOf("shrewd").convertTo(1).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $convert: { \"input\" : \"$shrewd\", \"to\" : 1 } } ")); - } - - @Test // DATAMONGO-2048 - public void convertToUsingFieldReference() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToTypeOf("fitz").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $convert: { \"input\" : \"$shrewd\", \"to\" : \"$fitz\" } } ")); - } - - @Test // DATAMONGO-2048 - public void convertToUsingExpression() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToTypeOf(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $convert: { \"input\" : \"$shrewd\", \"to\" : " + EXPRESSION_STRING + " } } ")); - } - - @Test // DATAMONGO-2048 - public void convertToWithOnErrorValue() { - - assertThat(ConvertOperators.valueOf("shrewd").convertTo("double").onErrorReturn("foo") - .toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo( - Document.parse("{ $convert: { \"input\" : \"$shrewd\", \"to\" : \"double\", \"onError\" : \"foo\" } } ")); - } - - @Test // DATAMONGO-2048 - public void convertToWithOnErrorValueOfField() { - - assertThat(ConvertOperators.valueOf("shrewd").convertTo("double").onErrorReturnValueOf("verity") - .toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document - .parse("{ $convert: { \"input\" : \"$shrewd\", \"to\" : \"double\", \"onError\" : \"$verity\" } } ")); - } - - @Test // DATAMONGO-2048 - public void convertToWithOnErrorValueOfExpression() { - - assertThat(ConvertOperators.valueOf("shrewd").convertTo("double").onErrorReturnValueOf(EXPRESSION) - .toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $convert: { \"input\" : \"$shrewd\", \"to\" : \"double\", \"onError\" : " - + EXPRESSION_STRING + " } } ")); - } - - @Test // DATAMONGO-2048 - public void convertToWithOnNullValue() { - - assertThat(ConvertOperators.valueOf("shrewd").convertTo("double").onNullReturn("foo") - .toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo( - Document.parse("{ $convert: { \"input\" : \"$shrewd\", \"to\" : \"double\", \"onNull\" : \"foo\" } } ")); - } - - @Test // DATAMONGO-2048 - public void convertToWithOnNullValueOfField() { - - assertThat(ConvertOperators.valueOf("shrewd").convertTo("double").onNullReturnValueOf("verity") - .toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document - .parse("{ $convert: { \"input\" : \"$shrewd\", \"to\" : \"double\", \"onNull\" : \"$verity\" } } ")); - } - - @Test // DATAMONGO-2048 - public void convertToWithOnNullValueOfExpression() { - - assertThat(ConvertOperators.valueOf("shrewd").convertTo("double").onNullReturnValueOf(EXPRESSION) - .toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse( - "{ $convert: { \"input\" : \"$shrewd\", \"to\" : \"double\", \"onNull\" : " + EXPRESSION_STRING + " } } ")); - } - - @Test // DATAMONGO-2048 - public void toBoolUsingFieldReference() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToBoolean().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toBool: \"$shrewd\" } ")); - } - - @Test // DATAMONGO-2048 - public void toBoolUsingExpression() { - - assertThat(ConvertOperators.valueOf(EXPRESSION).convertToBoolean().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toBool: " + EXPRESSION_STRING + " } ")); - } - - @Test // DATAMONGO-2048 - public void toDateUsingFieldReference() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToDate().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toDate: \"$shrewd\" } ")); - } - - @Test // DATAMONGO-2048 - public void toDateUsingExpression() { - - assertThat(ConvertOperators.valueOf(EXPRESSION).convertToDate().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toDate: " + EXPRESSION_STRING + " } ")); - } - - @Test // DATAMONGO-2048 - public void toDecimalUsingFieldReference() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToDecimal().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toDecimal: \"$shrewd\" } ")); - } - - @Test // DATAMONGO-2048 - public void toDecimalUsingExpression() { - - assertThat(ConvertOperators.valueOf(EXPRESSION).convertToDecimal().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toDecimal: " + EXPRESSION_STRING + " } ")); - } - - @Test // DATAMONGO-2048 - public void toDoubleUsingFieldReference() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToDouble().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toDouble: \"$shrewd\" } ")); - } - - @Test // DATAMONGO-2048 - public void toDoubleUsingExpression() { - - assertThat(ConvertOperators.valueOf(EXPRESSION).convertToDouble().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toDouble: " + EXPRESSION_STRING + " } ")); - } - - @Test // DATAMONGO-2048 - public void toIntUsingFieldReference() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToInt().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toInt: \"$shrewd\" } ")); - } - - @Test // DATAMONGO-2048 - public void toIntUsingExpression() { - - assertThat(ConvertOperators.valueOf(EXPRESSION).convertToInt().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toInt: " + EXPRESSION_STRING + " } ")); - } - - @Test // DATAMONGO-2048 - public void toLongUsingFieldReference() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToLong().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toLong: \"$shrewd\" } ")); - } - - @Test // DATAMONGO-2048 - public void toLongUsingExpression() { - - assertThat(ConvertOperators.valueOf(EXPRESSION).convertToLong().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toLong: " + EXPRESSION_STRING + " } ")); - } - - @Test // DATAMONGO-2048 - public void toObjectIdUsingFieldReference() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToObjectId().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toObjectId: \"$shrewd\" } ")); - } - - @Test // DATAMONGO-2048 - public void toObjectIdUsingExpression() { - - assertThat(ConvertOperators.valueOf(EXPRESSION).convertToObjectId().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toObjectId: " + EXPRESSION_STRING + " } ")); - } - - @Test // DATAMONGO-2048 - public void toStringUsingFieldReference() { - - assertThat(ConvertOperators.valueOf("shrewd").convertToString().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toString: \"$shrewd\" } ")); - } - - @Test // DATAMONGO-2048 - public void toStringUsingExpression() { - - assertThat(ConvertOperators.valueOf(EXPRESSION).convertToString().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $toString: " + EXPRESSION_STRING + " } ")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CountOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CountOperationUnitTests.java deleted file mode 100644 index bc4c9b1f9d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CountOperationUnitTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link CountOperation}. - * - * @author Mark Paluch - */ -public class CountOperationUnitTests { - - @Test // DATAMONGO-1549 - public void rejectsEmptyFieldName() { - assertThatIllegalArgumentException().isThrownBy(() -> new CountOperation("")); - } - - @Test // DATAMONGO-1549 - public void shouldRenderCorrectly() { - - CountOperation countOperation = new CountOperation("field"); - assertThat(countOperation.toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{$count : \"field\" }")); - } - - @Test // DATAMONGO-1549 - public void countExposesFields() { - - CountOperation countOperation = new CountOperation("field"); - - assertThat(countOperation.getFields().exposesNoFields()).isFalse(); - assertThat(countOperation.getFields().exposesSingleFieldOnly()).isTrue(); - assertThat(countOperation.getFields().getField("field")).isNotNull(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Data.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Data.java deleted file mode 100644 index 9d95ba86a9..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Data.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import java.util.Date; - -/** - * @author Thomas Darimont - */ -class Data { - public long primitiveLongValue; - public double primitiveDoubleValue; - public Double doubleValue; - public Date dateValue; - public String stringValue; - - public DataItem item; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DataItem.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DataItem.java deleted file mode 100644 index 16b1651965..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DataItem.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -/** - * @author Thomas Darimont - */ -class DataItem { - int primitiveIntValue; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsUnitTests.java deleted file mode 100644 index ff9b031876..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsUnitTests.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; - -/** - * Unit tests for {@link ExposedFields}. - * - * @author Oliver Gierke - * @author Thomas Darimont - */ -public class ExposedFieldsUnitTests { - - @Test - public void rejectsNullFields() { - assertThatIllegalArgumentException().isThrownBy(() -> ExposedFields.from((ExposedField) null)); - } - - @Test - public void rejectsNullFieldsForSynthetics() { - assertThatIllegalArgumentException().isThrownBy(() -> ExposedFields.synthetic(null)); - } - - @Test - public void rejectsNullFieldsForNonSynthetics() { - assertThatIllegalArgumentException().isThrownBy(() -> ExposedFields.nonSynthetic(null)); - } - - @Test - public void exposesSingleField() { - - ExposedFields fields = ExposedFields.synthetic(Fields.fields("foo")); - assertThat(fields.exposesSingleFieldOnly()).isTrue(); - - fields = fields.and(new ExposedField("bar", true)); - assertThat(fields.exposesSingleFieldOnly()).isFalse(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FacetOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FacetOperationUnitTests.java deleted file mode 100644 index a45e2e88fa..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FacetOperationUnitTests.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import org.bson.Document; -import org.junit.Test; - -import org.springframework.data.mongodb.core.query.Criteria; - -/** - * Unit tests for {@link FacetOperation}. - * - * @author Mark Paluch - * @author Jérôme Guyon - * @soundtrack Stanley Foort - You Make Me Believe In Magic (Extended Mix) - */ -public class FacetOperationUnitTests { - - @Test // DATAMONGO-1552 - public void shouldRenderCorrectly() { - - FacetOperation facetOperation = new FacetOperation().and(match(Criteria.where("price").exists(true)), // - bucket("price") // - .withBoundaries(0, 150, 200, 300, 400) // - .withDefaultBucket("Other") // - .andOutputCount().as("count") // - .andOutput("title").push().as("titles")) // - .as("categorizedByPrice") // - .and(bucketAuto("year", 5)).as("categorizedByYears"); - - Document agg = facetOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $facet: { categorizedByPrice: [" + "{ $match: { price: { $exists: true } } }, " - + "{ $bucket: { boundaries: [ 0, 150, 200, 300, 400 ], groupBy: \"$price\", default: \"Other\", " - + "output: { count: { $sum: 1 }, titles: { $push: \"$title\" } } } } ]," - + "categorizedByYears: [ { $bucketAuto: { buckets: 5, groupBy: \"$year\" } } ] } }")); - } - - @Test // DATAMONGO-1552 - public void shouldRenderEmpty() { - - FacetOperation facetOperation = facet(); - - Document agg = facetOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $facet: { } }")); - } - - @Test(expected = IllegalArgumentException.class) // DATAMONGO-1552 - public void shouldRejectNonExistingFields() { - - FacetOperation facetOperation = new FacetOperation().and(project("price"), // - bucket("price") // - .withBoundaries(0, 150, 200, 300, 400) // - .withDefaultBucket("Other") // - .andOutputCount().as("count") // - .andOutput("title").push().as("titles")) // - .as("categorizedByPrice"); - - Document agg = facetOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $facet: { categorizedByPrice: [" + "{ $match: { price: { $exists: true } } }, " - + "{ $bucket: {boundaries: [ 0, 150, 200, 300, 400 ], groupBy: \"$price\", default: \"Other\", " - + "output: { count: { $sum: 1 }, titles: { $push: \"$title\" } } } } ]," - + "categorizedByYears: [ { $bucketAuto: { buckets: 5, groupBy: \"$year\" } } ] } }")); - } - - @Test // DATAMONGO-1552 - public void shouldHonorProjectedFields() { - - FacetOperation facetOperation = new FacetOperation().and(project("price").and("title").as("name"), // - bucketAuto("price", 5) // - .andOutput("name").push().as("titles")) // - .as("categorizedByPrice"); - - Document agg = facetOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $facet: { categorizedByPrice: [" - + "{ $project: { price: 1, name: \"$title\" } }, " + "{ $bucketAuto: { buckets: 5, groupBy: \"$price\", " - + "output: { titles: { $push: \"$name\" } } } } ] } }")); - } - - @Test // DATAMONGO-1553 - public void shouldRenderSortByCountCorrectly() { - - FacetOperation facetOperation = new FacetOperation() // - .and(sortByCount("country")) // - .as("categorizedByCountry"); - - Document agg = facetOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).containsEntry("$facet.categorizedByCountry.[0].$sortByCount", "$country"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FieldsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FieldsUnitTests.java deleted file mode 100644 index 13cc769c28..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FieldsUnitTests.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.Fields.*; - -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.aggregation.Fields.*; - -/** - * Unit tests for {@link Fields}. - * - * @author Oliver Gierke - * @author Thomas Darimont - */ -public class FieldsUnitTests { - - @Test - public void rejectsNullFieldVarArgs() { - assertThatIllegalArgumentException().isThrownBy(() -> Fields.from((Field[]) null)); - } - - @Test - public void rejectsNullFieldNameVarArgs() { - assertThatIllegalArgumentException().isThrownBy(() -> Fields.fields((String[]) null)); - } - - @Test - public void createsFieldFromNameOnly() { - verify(Fields.field("foo"), "foo", null); - } - - @Test - public void createsFieldFromNameAndTarget() { - verify(Fields.field("foo", "bar"), "foo", "bar"); - } - - @Test - public void rejectsNullFieldName() { - assertThatIllegalArgumentException().isThrownBy(() -> Fields.field(null)); - } - - @Test - public void rejectsNullFieldNameIfTargetGiven() { - assertThatIllegalArgumentException().isThrownBy(() -> Fields.field(null, "foo")); - } - - @Test - public void rejectsEmptyFieldName() { - assertThatIllegalArgumentException().isThrownBy(() -> Fields.field("")); - } - - @Test - public void createsFieldsFromFieldInstances() { - - AggregationField reference = new AggregationField("foo"); - Fields fields = Fields.from(reference); - - assertThat(fields).hasSize(1); - assertThat(fields).contains(reference); - } - - @Test - public void aliasesPathExpressionsIntoLeafForImplicits() { - verify(Fields.field("foo.bar"), "bar", "foo.bar"); - } - - @Test - public void fieldsFactoryMethod() { - - Fields fields = fields("a", "b").and("c").and("d", "e"); - - assertThat(fields).hasSize(4); - - verify(fields.getField("a"), "a", null); - verify(fields.getField("b"), "b", null); - verify(fields.getField("c"), "c", null); - verify(fields.getField("d"), "d", "e"); - } - - @Test - public void rejectsAmbiguousFieldNames() { - assertThatIllegalArgumentException().isThrownBy(() -> fields("b", "a.b")); - } - - @Test // DATAMONGO-774 - public void stripsLeadingDollarsFromName() { - - assertThat(Fields.field("$name").getName()).isEqualTo("name"); - assertThat(Fields.field("$$$$name").getName()).isEqualTo("name"); - } - - @Test // DATAMONGO-774 - public void rejectsNameConsistingOfDollarOnly() { - assertThatIllegalArgumentException().isThrownBy(() -> Fields.field("$")); - } - - @Test // DATAMONGO-774 - public void stripsLeadingDollarsFromTarget() { - - assertThat(Fields.field("$target").getTarget()).isEqualTo("target"); - assertThat(Fields.field("$$$$target").getTarget()).isEqualTo("target"); - } - - private static void verify(Field field, String name, String target) { - - assertThat(field).isNotNull(); - assertThat(field.getName()).isEqualTo(name); - assertThat(field.getTarget()).isEqualTo(target != null ? target : name); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java deleted file mode 100644 index 1580b4efb0..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 - * - * 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.ArrayOperators.Filter.filter; - -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -/** - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -class FilterExpressionUnitTests { - - @Mock MongoDatabaseFactory mongoDbFactory; - - private AggregationOperationContext aggregationContext; - private MongoMappingContext mappingContext; - - @BeforeEach - void setUp() { - - mappingContext = new MongoMappingContext(); - aggregationContext = new TypeBasedAggregationOperationContext(Sales.class, mappingContext, - new QueryMapper(new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), mappingContext))); - } - - @Test // DATAMONGO-1491 - void shouldConstructFilterExpressionCorrectly() { - - TypedAggregation agg = Aggregation.newAggregation(Sales.class, - Aggregation.project() - .and(filter("items").as("item").by(AggregationFunctionExpressions.GTE.of(Fields.field("item.price"), 100))) - .as("items")); - - Document $filter = extractFilterOperatorFromDocument(agg.toDocument("sales", aggregationContext)); - Document expected = Document.parse("{" + // - "input: \"$items\"," + // - "as: \"item\"," + // - "cond: { $gte: [ \"$$item.price\", 100 ] }" + // - "}"); - - assertThat($filter).isEqualTo(new Document(expected)); - } - - @Test // DATAMONGO-1491 - void shouldConstructFilterExpressionCorrectlyWhenUsingFilterOnProjectionBuilder() { - - TypedAggregation agg = Aggregation.newAggregation(Sales.class, Aggregation.project().and("items") - .filter("item", AggregationFunctionExpressions.GTE.of(Fields.field("item.price"), 100)).as("items")); - - Document $filter = extractFilterOperatorFromDocument(agg.toDocument("sales", aggregationContext)); - Document expected = Document.parse("{" + // - "input: \"$items\"," + // - "as: \"item\"," + // - "cond: { $gte: [ \"$$item.price\", 100 ] }" + // - "}"); - - assertThat($filter).isEqualTo(expected); - } - - @Test // DATAMONGO-1491 - void shouldConstructFilterExpressionCorrectlyWhenInputMapToArray() { - - TypedAggregation agg = Aggregation.newAggregation(Sales.class, - Aggregation.project().and(filter(Arrays. asList(1, "a", 2, null, 3.1D, 4, "5")).as("num") - .by(AggregationFunctionExpressions.GTE.of(Fields.field("num"), 3))).as("items")); - - Document $filter = extractFilterOperatorFromDocument(agg.toDocument("sales", aggregationContext)); - Document expected = Document.parse("{" + // - "input: [ 1, \"a\", 2, null, 3.1, 4, \"5\" ]," + // - "as: \"num\"," + // - "cond: { $gte: [ \"$$num\", 3 ] }" + // - "}"); - - assertThat($filter).isEqualTo(expected); - } - - @Test // DATAMONGO-2320 - void shouldConstructFilterExpressionCorrectlyWhenConditionContainsFieldReference() { - - Aggregation agg = Aggregation.newAggregation(Aggregation.project().and((ctx) -> new Document()).as("field-1") - .and(filter("items").as("item").by(ComparisonOperators.valueOf("item.price").greaterThan("field-1"))) - .as("items")); - - Document $filter = extractFilterOperatorFromDocument(agg.toDocument("sales", Aggregation.DEFAULT_CONTEXT)); - Document expected = Document.parse("{" + // - "input: \"$items\"," + // - "as: \"item\"," + // - "cond: { $gt: [ \"$$item.price\", \"$field-1\" ] }" + // - "}"); - - assertThat($filter).isEqualTo(new Document(expected)); - } - - private Document extractFilterOperatorFromDocument(Document source) { - - List pipeline = DocumentTestUtils.getAsDBList(source, "pipeline"); - Document $project = DocumentTestUtils.getAsDocument((Document) pipeline.get(0), "$project"); - Document items = DocumentTestUtils.getAsDocument($project, "items"); - return DocumentTestUtils.getAsDocument(items, "$filter"); - } - - static class Sales { - - List items; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java deleted file mode 100644 index 48a2fe2c6f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.annotation.Id; -import org.springframework.data.geo.Distance; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.lang.Nullable; - -/** - * Unit tests for {@link GeoNearOperation}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - */ -public class GeoNearOperationUnitTests { - - @Test // DATAMONGO-1127 - public void rendersNearQueryAsAggregationOperation() { - - NearQuery query = NearQuery.near(10.0, 10.0); - GeoNearOperation operation = new GeoNearOperation(query, "distance"); - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document nearClause = DocumentTestUtils.getAsDocument(document, "$geoNear"); - - Document expected = new Document(query.toDocument()).append("distanceField", "distance"); - assertThat(nearClause).isEqualTo(expected); - } - - @Test // DATAMONGO-2050 - public void rendersNearQueryWithKeyCorrectly() { - - NearQuery query = NearQuery.near(10.0, 10.0); - GeoNearOperation operation = new GeoNearOperation(query, "distance").useIndex("geo-index-1"); - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(DocumentTestUtils.getAsDocument(document, "$geoNear")).containsEntry("key", "geo-index-1"); - } - - @Test // DATAMONGO-2264 - public void rendersMaxDistanceCorrectly() { - - NearQuery query = NearQuery.near(10.0, 20.0).maxDistance(new Distance(30.0)); - - assertThat(new GeoNearOperation(query, "distance").toPipelineStages(Aggregation.DEFAULT_CONTEXT)) - .containsExactly($geoNear().near(10.0, 20.0).maxDistance(30.0).doc()); - } - - @Test // DATAMONGO-2264 - public void rendersMinDistanceCorrectly() { - - NearQuery query = NearQuery.near(10.0, 20.0).minDistance(new Distance(30.0)); - - assertThat(new GeoNearOperation(query, "distance").toPipelineStages(Aggregation.DEFAULT_CONTEXT)) - .containsExactly($geoNear().near(10.0, 20.0).minDistance(30.0).doc()); - } - - @Test // DATAMONGO-2264 - public void rendersSphericalCorrectly() { - - NearQuery query = NearQuery.near(10.0, 20.0).spherical(true); - - assertThat(new GeoNearOperation(query, "distance").toPipelineStages(Aggregation.DEFAULT_CONTEXT)) - .containsExactly($geoNear().near(10.0, 20.0).spherical(true).doc()); - } - - @Test // DATAMONGO-2264 - public void rendersDistanceMultiplier() { - - NearQuery query = NearQuery.near(10.0, 20.0).inKilometers(); - - assertThat(new GeoNearOperation(query, "distance").toPipelineStages(Aggregation.DEFAULT_CONTEXT)) - .containsExactly($geoNear().near(10.0, 20.0).spherical(true).distanceMultiplier(6378.137).doc()); - } - - @Test // DATAMONGO-2264 - public void rendersIndexKey() { - - NearQuery query = NearQuery.near(10.0, 20.0); - - assertThat( - new GeoNearOperation(query, "distance").useIndex("index-1").toPipelineStages(Aggregation.DEFAULT_CONTEXT)) - .containsExactly($geoNear().near(10.0, 20.0).key("index-1").doc()); - } - - @Test // DATAMONGO-2264 - public void rendersQuery() { - - NearQuery query = NearQuery.near(10.0, 20.0).query(Query.query(Criteria.where("city").is("Austin"))); - - assertThat(new GeoNearOperation(query, "distance").toPipelineStages(Aggregation.DEFAULT_CONTEXT)) - .containsExactly($geoNear().near(10.0, 20.0).query(new Document("city", "Austin")).doc()); - } - - @Test // DATAMONGO-2264 - public void rendersMappedQuery() { - - NearQuery query = NearQuery.near(10.0, 20.0).query(Query.query(Criteria.where("city").is("Austin"))); - - assertThat( - new GeoNearOperation(query, "distance").toPipelineStages(typedAggregationOperationContext(GeoDocument.class))) - .containsExactly($geoNear().near(10.0, 20.0).query(new Document("ci-ty", "Austin")).doc()); - } - - @Test // DATAMONGO-2264 - public void appliesSkipFromNearQuery() { - - NearQuery query = NearQuery.near(10.0, 20.0).skip(10L); - - assertThat(new GeoNearOperation(query, "distance").toPipelineStages(Aggregation.DEFAULT_CONTEXT)) - .containsExactly($geoNear().near(10.0, 20.0).doc(), new Document("$skip", 10L)); - } - - @Test // DATAMONGO-2264 - public void appliesLimitFromNearQuery() { - - NearQuery query = NearQuery.near(10.0, 20.0).limit(10L); - - assertThat(new GeoNearOperation(query, "distance").toPipelineStages(Aggregation.DEFAULT_CONTEXT)) - .containsExactly($geoNear().near(10.0, 20.0).doc(), new Document("$limit", 10L)); - } - - @Test // DATAMONGO-2264 - public void appliesSkipAndLimitInOrder() { - - NearQuery query = NearQuery.near(10.0, 20.0).limit(10L).skip(3L); - - assertThat(new GeoNearOperation(query, "distance").toPipelineStages(Aggregation.DEFAULT_CONTEXT)) - .containsExactly($geoNear().near(10.0, 20.0).doc(), new Document("$skip", 3L), new Document("$limit", 10L)); - } - - private TypeBasedAggregationOperationContext typedAggregationOperationContext(Class type) { - - MongoMappingContext mappingContext = new MongoMappingContext(); - MongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); - return new TypeBasedAggregationOperationContext(type, mappingContext, new QueryMapper(converter)); - } - - GeoNearDocumentBuilder $geoNear() { - return new GeoNearDocumentBuilder(); - } - - static class GeoDocument { - - @Id String id; - @Field("ci-ty") String city; - } - - static class GeoNearDocumentBuilder { - - Document target = new Document("distanceField", "distance").append("distanceMultiplier", 1.0D).append("spherical", - false); - - GeoNearDocumentBuilder maxDistance(@Nullable Number value) { - - if (value != null) { - target.put("maxDistance", value); - } else { - target.remove("maxDistance"); - } - return this; - } - - GeoNearDocumentBuilder minDistance(@Nullable Number value) { - - if (value != null) { - target.put("minDistance", value); - } else { - target.remove("minDistance"); - } - return this; - } - - GeoNearDocumentBuilder near(Number... coordinates) { - - target.put("near", Arrays.asList(coordinates)); - return this; - } - - GeoNearDocumentBuilder spherical(@Nullable Boolean value) { - - if (value != null) { - target.put("spherical", value); - } else { - target.remove("spherical"); - } - return this; - } - - GeoNearDocumentBuilder distanceField(@Nullable String value) { - - if (value != null) { - target.put("distanceField", value); - } else { - target.remove("distanceField"); - } - return this; - } - - GeoNearDocumentBuilder distanceMultiplier(Number value) { - - if (value != null) { - target.put("distanceMultiplier", value); - } else { - target.remove("distanceMultiplier"); - } - return this; - } - - GeoNearDocumentBuilder key(String value) { - - if (value != null) { - target.put("key", value); - } else { - target.remove("key"); - } - return this; - } - - GeoNearDocumentBuilder query(Document value) { - - if (value != null) { - target.put("query", value); - } else { - target.remove("query"); - } - return this; - } - - Document doc() { - return new Document("$geoNear", new Document(target)); - } - - } - - // TODO: we need to test this to the full extend -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperationUnitTests.java deleted file mode 100644 index 53e6accec5..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperationUnitTests.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.Person; -import org.springframework.data.mongodb.core.query.Criteria; - -/** - * Unit tests for {@link GraphLookupOperation}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -public class GraphLookupOperationUnitTests { - - @Test // DATAMONGO-1551 - public void rejectsNullFromCollection() { - assertThatIllegalArgumentException().isThrownBy(() -> GraphLookupOperation.builder().from(null)); - } - - @Test // DATAMONGO-1551 - public void shouldRenderCorrectly() { - - GraphLookupOperation graphLookupOperation = GraphLookupOperation.builder() // - .from("employees") // - .startWith("reportsTo") // - .connectFrom("reportsTo") // - .connectTo("name") // - .depthField("depth") // - .maxDepth(42) // - .as("reportingHierarchy"); - - Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(document).containsEntry("$graphLookup.depthField", "depth").containsEntry("$graphLookup.maxDepth", 42L); - } - - @Test // DATAMONGO-1551 - public void shouldRenderCriteriaCorrectly() { - - GraphLookupOperation graphLookupOperation = GraphLookupOperation.builder() // - .from("employees") // - .startWith("reportsTo") // - .connectFrom("reportsTo") // - .connectTo("name") // - .restrict(Criteria.where("key").is("value")) // - .as("reportingHierarchy"); - - Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(document).containsEntry("$graphLookup.restrictSearchWithMatch", new Document("key", "value")); - } - - @Test // DATAMONGO-1551 - public void shouldRenderArrayOfStartsWithCorrectly() { - - GraphLookupOperation graphLookupOperation = GraphLookupOperation.builder() // - .from("employees") // - .startWith("reportsTo", "boss") // - .connectFrom("reportsTo") // - .connectTo("name") // - .as("reportingHierarchy"); - - Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(document) - .isEqualTo(Document.parse("{ $graphLookup : { from: \"employees\", startWith: [\"$reportsTo\", \"$boss\"], " - + "connectFromField: \"reportsTo\", connectToField: \"name\", as: \"reportingHierarchy\" } }")); - } - - @Test // DATAMONGO-1551 - public void shouldRenderMixedArrayOfStartsWithCorrectly() { - - GraphLookupOperation graphLookupOperation = GraphLookupOperation.builder() // - .from("employees") // - .startWith("reportsTo", LiteralOperators.Literal.asLiteral("$boss")) // - .connectFrom("reportsTo") // - .connectTo("name") // - .as("reportingHierarchy"); - - Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(document).containsEntry("$graphLookup.startWith", - Arrays.asList("$reportsTo", new Document("$literal", "$boss"))); - } - - @Test // DATAMONGO-1551 - public void shouldRejectUnknownTypeInMixedArrayOfStartsWithCorrectly() { - assertThatIllegalArgumentException().isThrownBy(() -> GraphLookupOperation.builder() // - .from("employees") // - .startWith("reportsTo", new Person()) // - .connectFrom("reportsTo") // - .connectTo("name") // - .as("reportingHierarchy")); - } - - @Test // DATAMONGO-1551 - public void shouldRenderStartWithAggregationExpressions() { - - GraphLookupOperation graphLookupOperation = GraphLookupOperation.builder() // - .from("employees") // - .startWith(LiteralOperators.Literal.asLiteral("hello")) // - .connectFrom("reportsTo") // - .connectTo("name") // - .as("reportingHierarchy"); - - Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(document).containsEntry("$graphLookup.startWith", new Document("$literal", "hello")); - } - - @Test // DATAMONGO-2096 - public void connectFromShouldUseTargetFieldInsteadOfAlias() { - - AggregationOperation graphLookupOperation = Aggregation.graphLookup("user").startWith("contacts.userId") - .connectFrom("contacts.userId").connectTo("_id").depthField("numConnections").as("connections"); - - Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(document).containsEntry("$graphLookup.startWith", "$contacts.userId"); - } - - @Test // DATAMONGO-2096 - public void connectToShouldUseTargetFieldInsteadOfAlias() { - - AggregationOperation graphLookupOperation = Aggregation.graphLookup("user").startWith("contacts.userId") - .connectFrom("userId").connectTo("connectto.field").depthField("numConnections").as("connections"); - - Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(document).containsEntry("$graphLookup.connectToField", "connectto.field"); - } - - @Test // DATAMONGO-2096 - public void depthFieldShouldUseTargetFieldInsteadOfAlias() { - - AggregationOperation graphLookupOperation = Aggregation.graphLookup("user").startWith("contacts.userId") - .connectFrom("contacts.userId").connectTo("_id").depthField("foo.bar").as("connections"); - - Document document = graphLookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(document).containsEntry("$graphLookup.depthField", "foo.bar"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GroupOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GroupOperationUnitTests.java deleted file mode 100644 index e63aa9ca80..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GroupOperationUnitTests.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.AggregationFunctionExpressions.*; -import static org.springframework.data.mongodb.core.aggregation.Fields.*; - -import java.util.Arrays; -import java.util.Collections; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.query.Criteria; - -/** - * Unit tests for {@link GroupOperation}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Gustavo de Geus - */ -class GroupOperationUnitTests { - - @Test - void rejectsNullFields() { - assertThatIllegalArgumentException().isThrownBy(() -> new GroupOperation((Fields) null)); - } - - @Test // DATAMONGO-759 - void groupOperationWithNoGroupIdFieldsShouldGenerateNullAsGroupId() { - - GroupOperation operation = new GroupOperation(Fields.from()); - ExposedFields fields = operation.getFields(); - Document groupClause = extractDocumentFromGroupOperation(operation); - - assertThat(fields.exposesSingleFieldOnly()).isTrue(); - assertThat(fields.exposesNoFields()).isFalse(); - assertThat(groupClause.get(UNDERSCORE_ID)).isNull(); - } - - @Test // DATAMONGO-759 - void groupOperationWithNoGroupIdFieldsButAdditionalFieldsShouldGenerateNullAsGroupId() { - - GroupOperation operation = new GroupOperation(Fields.from()).count().as("cnt").last("foo").as("foo"); - ExposedFields fields = operation.getFields(); - Document groupClause = extractDocumentFromGroupOperation(operation); - - assertThat(fields.exposesSingleFieldOnly()).isFalse(); - assertThat(fields.exposesNoFields()).isFalse(); - assertThat(groupClause.get(UNDERSCORE_ID)).isNull(); - assertThat((Document) groupClause.get("cnt")).isEqualTo(new Document("$sum", 1)); - assertThat((Document) groupClause.get("foo")).isEqualTo(new Document("$last", "$foo")); - } - - @Test - void createsGroupOperationWithSingleField() { - - GroupOperation operation = new GroupOperation(fields("a")); - - Document groupClause = extractDocumentFromGroupOperation(operation); - - assertThat(groupClause).containsEntry(UNDERSCORE_ID, "$a"); - } - - @Test - void createsGroupOperationWithMultipleFields() { - - GroupOperation operation = new GroupOperation(fields("a").and("b", "c")); - - Document groupClause = extractDocumentFromGroupOperation(operation); - Document idClause = DocumentTestUtils.getAsDocument(groupClause, UNDERSCORE_ID); - - assertThat(idClause).containsEntry("a", "$a") - .containsEntry("b", "$c"); - } - - @Test - void groupFactoryMethodWithMultipleFieldsAndSumOperation() { - - GroupOperation groupOperation = Aggregation.group(fields("a", "b").and("c")) // - .sum("e").as("e"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document eOp = DocumentTestUtils.getAsDocument(groupClause, "e"); - assertThat(eOp).isEqualTo(new Document("$sum", "$e")); - } - - @Test - void groupFactoryMethodWithMultipleFieldsAndSumOperationWithAlias() { - - GroupOperation groupOperation = Aggregation.group(fields("a", "b").and("c")) // - .sum("e").as("ee"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document eOp = DocumentTestUtils.getAsDocument(groupClause, "ee"); - assertThat(eOp).isEqualTo(new Document("$sum", "$e")); - } - - @Test - void groupFactoryMethodWithMultipleFieldsAndCountOperationWithout() { - - GroupOperation groupOperation = Aggregation.group(fields("a", "b").and("c")) // - .count().as("count"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document eOp = DocumentTestUtils.getAsDocument(groupClause, "count"); - assertThat(eOp).isEqualTo(new Document("$sum", 1)); - } - - @Test - void groupFactoryMethodWithMultipleFieldsAndMultipleAggregateOperationsWithAlias() { - - GroupOperation groupOperation = Aggregation.group(fields("a", "b").and("c")) // - .sum("e").as("sum") // - .min("e").as("min"); // - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document sum = DocumentTestUtils.getAsDocument(groupClause, "sum"); - assertThat(sum).isEqualTo(new Document("$sum", "$e")); - - Document min = DocumentTestUtils.getAsDocument(groupClause, "min"); - assertThat(min).isEqualTo(new Document("$min", "$e")); - } - - @Test - void groupOperationPushWithValue() { - - GroupOperation groupOperation = Aggregation.group("a", "b").push(1).as("x"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document push = DocumentTestUtils.getAsDocument(groupClause, "x"); - - assertThat(push).isEqualTo(new Document("$push", 1)); - } - - @Test - void groupOperationPushWithReference() { - - GroupOperation groupOperation = Aggregation.group("a", "b").push("ref").as("x"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document push = DocumentTestUtils.getAsDocument(groupClause, "x"); - - assertThat(push).isEqualTo(new Document("$push", "$ref")); - } - - @Test - void groupOperationAddToSetWithReference() { - - GroupOperation groupOperation = Aggregation.group("a", "b").addToSet("ref").as("x"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document push = DocumentTestUtils.getAsDocument(groupClause, "x"); - - assertThat(push).isEqualTo(new Document("$addToSet", "$ref")); - } - - @Test - void groupOperationAddToSetWithValue() { - - GroupOperation groupOperation = Aggregation.group("a", "b").addToSet(42).as("x"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document push = DocumentTestUtils.getAsDocument(groupClause, "x"); - - assertThat(push).isEqualTo(new Document("$addToSet", 42)); - } - - @Test // DATAMONGO-979 - void shouldRenderSizeExpressionInGroup() { - - GroupOperation groupOperation = Aggregation // - .group("username") // - .first(SIZE.of(field("tags"))) // - .as("tags_count"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document tagsCount = DocumentTestUtils.getAsDocument(groupClause, "tags_count"); - - assertThat(tagsCount) - .containsEntry("$first", new Document("$size", Collections - .singletonList("$tags"))); - } - - @Test // DATAMONGO-1327 - void groupOperationStdDevSampWithValue() { - - GroupOperation groupOperation = Aggregation.group("a", "b").stdDevSamp("field").as("fieldStdDevSamp"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document push = DocumentTestUtils.getAsDocument(groupClause, "fieldStdDevSamp"); - - assertThat(push).isEqualTo(new Document("$stdDevSamp", "$field")); - } - - @Test // DATAMONGO-1327 - void groupOperationStdDevPopWithValue() { - - GroupOperation groupOperation = Aggregation.group("a", "b").stdDevPop("field").as("fieldStdDevPop"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document push = DocumentTestUtils.getAsDocument(groupClause, "fieldStdDevPop"); - - assertThat(push).isEqualTo(new Document("$stdDevPop", "$field")); - } - - @Test // DATAMONGO-1784 - void shouldRenderSumWithExpressionInGroup() { - - GroupOperation groupOperation = Aggregation // - .group("username") // - .sum(ConditionalOperators // - .when(Criteria.where("foo").is("bar")) // - .then(1) // - .otherwise(-1)) // - .as("foobar"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document foobar = DocumentTestUtils.getAsDocument(groupClause, "foobar"); - - assertThat(foobar).containsEntry("$sum", new Document("$cond", - new Document("if", new Document("$eq", Arrays.asList("$foo", "bar"))).append("then", 1).append("else", -1))); - } - - @Test // DATAMONGO-1784 - void sumWithNullExpressionShouldThrowException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> Aggregation.group("username").sum((AggregationExpression) null)); - } - - @Test // DATAMONGO-2651 - void accumulatorShouldBeAllowedOnGroupOperation() { - - GroupOperation groupOperation = Aggregation.group("id") - .accumulate( - ScriptOperators.accumulatorBuilder().init("inti").accumulate("acc").merge("merge").finalize("finalize")) - .as("accumulated-value"); - - Document groupClause = extractDocumentFromGroupOperation(groupOperation); - Document accumulatedValue = DocumentTestUtils.getAsDocument(groupClause, "accumulated-value"); - - assertThat(accumulatedValue).containsKey("$accumulator"); - } - - private Document extractDocumentFromGroupOperation(GroupOperation groupOperation) { - Document document = groupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document groupClause = DocumentTestUtils.getAsDocument(document, "$group"); - return groupClause; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Invoice.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Invoice.java deleted file mode 100644 index 1821b28e84..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Invoice.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2014-2021 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.aggregation; - -import java.util.List; - -/** - * @author Thomas Darimont - */ -public class Invoice { - - String orderId; - - double taxAmount; - - double netAmount; - - double totalAmount; - - List items; - - public String getOrderId() { - return orderId; - } - - public void setOrderId(String orderId) { - this.orderId = orderId; - } - - public double getTaxAmount() { - return taxAmount; - } - - public void setTaxAmount(double taxAmount) { - this.taxAmount = taxAmount; - } - - public double getNetAmount() { - return netAmount; - } - - public void setNetAmount(double netAmount) { - this.netAmount = netAmount; - } - - public double getTotalAmount() { - return totalAmount; - } - - public void setTotalAmount(double totalAmount) { - this.totalAmount = totalAmount; - } - - public List getItems() { - return items; - } - - public void setItems(List items) { - this.items = items; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/LikeStats.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/LikeStats.java deleted file mode 100644 index bb1917727b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/LikeStats.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import org.springframework.data.mongodb.core.mapping.Field; - -/** - * @author Thomas Darimont - */ -public class LikeStats { - - String id; - @Field("number") long count; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/LineItem.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/LineItem.java deleted file mode 100644 index 447ec6d72d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/LineItem.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2021 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.aggregation; - -/** - * @author Thomas Darimont - */ -public class LineItem { - - String id; - - String caption; - - double price; - - int quantity = 1; - - @SuppressWarnings("unused") - private LineItem() { - this(null, null, 0.0, 0); - } - - public LineItem(String id, String caption, double price) { - this.id = id; - this.caption = caption; - this.price = price; - } - - public LineItem(String id, String caption, double price, int quantity) { - this(id, caption, price); - this.quantity = quantity; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/LookupOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/LookupOperationUnitTests.java deleted file mode 100644 index ce43ac1bc1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/LookupOperationUnitTests.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.DocumentTestUtils; - -/** - * Unit tests for {@link LookupOperation}. - * - * @author Alessio Fachechi - * @author Christoph Strobl - * @author Mark Paluch - */ -public class LookupOperationUnitTests { - - @Test // DATAMONGO-1326 - public void rejectsNullForFrom() { - assertThatIllegalArgumentException().isThrownBy( - () -> new LookupOperation(null, Fields.field("localField"), Fields.field("foreignField"), Fields.field("as"))); - } - - @Test // DATAMONGO-1326 - public void rejectsNullLocalFieldField() { - assertThatIllegalArgumentException().isThrownBy( - () -> new LookupOperation(Fields.field("from"), null, Fields.field("foreignField"), Fields.field("as"))); - } - - @Test // DATAMONGO-1326 - public void rejectsNullForeignField() { - assertThatIllegalArgumentException().isThrownBy( - () -> new LookupOperation(Fields.field("from"), Fields.field("localField"), null, Fields.field("as"))); - } - - @Test // DATAMONGO-1326 - public void rejectsNullForAs() { - assertThatIllegalArgumentException().isThrownBy(() -> new LookupOperation(Fields.field("from"), - Fields.field("localField"), Fields.field("foreignField"), null)); - } - - @Test // DATAMONGO-1326 - public void lookupOperationWithValues() { - - LookupOperation lookupOperation = Aggregation.lookup("a", "b", "c", "d"); - - Document lookupClause = extractDocumentFromLookupOperation(lookupOperation); - - assertThat(lookupClause).containsEntry("from", "a") // - .containsEntry("localField", "b") // - .containsEntry("foreignField", "c") // - .containsEntry("as", "d"); - } - - @Test // DATAMONGO-1326 - public void lookupOperationExposesAsField() { - - LookupOperation lookupOperation = Aggregation.lookup("a", "b", "c", "d"); - - assertThat(lookupOperation.getFields().exposesNoFields()).isFalse(); - assertThat(lookupOperation.getFields().exposesSingleFieldOnly()).isTrue(); - assertThat(lookupOperation.getFields().getField("d")).isNotNull(); - } - - private Document extractDocumentFromLookupOperation(LookupOperation lookupOperation) { - - Document document = lookupOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document lookupClause = DocumentTestUtils.getAsDocument(document, "$lookup"); - return lookupClause; - } - - @Test // DATAMONGO-1326 - public void builderRejectsNullFromField() { - assertThatIllegalArgumentException().isThrownBy(() -> LookupOperation.newLookup().from(null)); - } - - @Test // DATAMONGO-1326 - public void builderRejectsNullLocalField() { - assertThatIllegalArgumentException().isThrownBy(() -> LookupOperation.newLookup().from("a").localField(null)); - } - - @Test // DATAMONGO-1326 - public void builderRejectsNullForeignField() { - assertThatIllegalArgumentException() - .isThrownBy(() -> LookupOperation.newLookup().from("a").localField("b").foreignField(null)); - } - - @Test // DATAMONGO-1326 - public void builderRejectsNullAsField() { - assertThatIllegalArgumentException() - .isThrownBy(() -> LookupOperation.newLookup().from("a").localField("b").foreignField("c").as(null)); - } - - @Test // DATAMONGO-1326 - public void lookupBuilderBuildsCorrectClause() { - - LookupOperation lookupOperation = LookupOperation.newLookup().from("a").localField("b").foreignField("c").as("d"); - - Document lookupClause = extractDocumentFromLookupOperation(lookupOperation); - - assertThat(lookupClause).containsEntry("from", "a") // - .containsEntry("localField", "b") // - .containsEntry("foreignField", "c") // - .containsEntry("as", "d"); - } - - @Test // DATAMONGO-1326 - public void lookupBuilderExposesFields() { - - LookupOperation lookupOperation = LookupOperation.newLookup().from("a").localField("b").foreignField("c").as("d"); - - assertThat(lookupOperation.getFields().exposesNoFields()).isFalse(); - assertThat(lookupOperation.getFields().exposesSingleFieldOnly()).isTrue(); - assertThat(lookupOperation.getFields().getField("d")).isNotNull(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java deleted file mode 100644 index 35260d43c5..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2020-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.core.aggregation.MergeOperation.*; -import static org.springframework.data.mongodb.core.aggregation.MergeOperation.WhenDocumentsMatch.*; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; - -/** - * Unit tests for {@link MergeOperation}. - * - * @author Christoph Strobl - */ -class MergeOperationUnitTests { - - private static final String OUT_COLLECTION = "target-collection"; - private static final String OUT_DB = "target-db"; - - private static final Document OUT = new Document("db", OUT_DB).append("coll", OUT_COLLECTION); - - @Test // DATAMONGO-2363 - void justCollection() { - - assertThat(mergeInto(OUT_COLLECTION).toDocument(DEFAULT_CONTEXT)).isEqualTo(new Document("$merge", OUT_COLLECTION)); - } - - @Test // DATAMONGO-2363 - void collectionInDatabase() { - - assertThat(merge().intoCollection(OUT_COLLECTION).inDatabase("target-db").build().toDocument(DEFAULT_CONTEXT)) - .isEqualTo(new Document("$merge", new Document("into", OUT))); - } - - @Test // DATAMONGO-2363 - void singleOn() { - - assertThat(merge().intoCollection(OUT_COLLECTION).on("id-field").build().toDocument(DEFAULT_CONTEXT)) - .isEqualTo(new Document("$merge", new Document("into", OUT_COLLECTION).append("on", "id-field"))); - } - - @Test // DATAMONGO-2363 - void multipleOn() { - - assertThat(merge().intoCollection(OUT_COLLECTION).on("field-1", "field-2").build().toDocument(DEFAULT_CONTEXT)) - .isEqualTo(new Document("$merge", - new Document("into", OUT_COLLECTION).append("on", Arrays.asList("field-1", "field-2")))); - } - - @Test // DATAMONGO-2363 - void collectionAndSimpleArgs() { - - assertThat(merge().intoCollection(OUT_COLLECTION).on("_id").whenMatched(replaceDocument()) - .whenNotMatched(WhenDocumentsDontMatch.insertNewDocument()).build().toDocument(DEFAULT_CONTEXT)) - .isEqualTo(new Document("$merge", new Document("into", OUT_COLLECTION).append("on", "_id") - .append("whenMatched", "replace").append("whenNotMatched", "insert"))); - } - - @Test // DATAMONGO-2363 - void whenMatchedWithAggregation() { - - String expected = "{ \"$merge\" : {\"into\": \"" + OUT_COLLECTION + "\", \"whenMatched\": [" - + "{ \"$addFields\" : {" // - + "\"thumbsup\": { \"$sum\":[ \"$thumbsup\", \"$$new.thumbsup\" ] }," - + "\"thumbsdown\": { \"$sum\": [ \"$thumbsdown\", \"$$new.thumbsdown\" ] } } } ]" // - + "} }"; - - Aggregation update = Aggregation - .newAggregation(AddFieldsOperation.addField("thumbsup").withValueOf(Sum.sumOf("thumbsup").and("$$new.thumbsup")) - .addField("thumbsdown").withValueOf(Sum.sumOf("thumbsdown").and("$$new.thumbsdown")).build()); - - assertThat( - merge().intoCollection(OUT_COLLECTION).whenDocumentsMatchApply(update).build().toDocument(DEFAULT_CONTEXT)) - .isEqualTo(Document.parse(expected)); - } - - @Test // DATAMONGO-2363 - void mapsFieldNames() { - - assertThat(merge().intoCollection("newrestaurants").on("date", "postCode").build() - .toDocument(contextFor(Restaurant.class))).isEqualTo( - Document.parse("{ \"$merge\": { \"into\": \"newrestaurants\", \"on\": [ \"date\", \"post_code\" ] } }")); - } - - private static AggregationOperationContext contextFor(@Nullable Class type) { - - if (type == null) { - return Aggregation.DEFAULT_CONTEXT; - } - - MappingMongoConverter mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, - new MongoMappingContext()); - mongoConverter.afterPropertiesSet(); - - return new TypeBasedAggregationOperationContext(type, mongoConverter.getMappingContext(), - new QueryMapper(mongoConverter)).continueOnMissingFieldReference(); - } - - static class Restaurant { - - @Field("post_code") String postCode; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MeterData.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MeterData.java deleted file mode 100644 index e805608e57..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MeterData.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2015-2021 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.aggregation; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; - -/** - * @author Thomas Darimont - */ -public class MeterData { - - @Id String id; - String resourceId; - @Field("counter_name") String counterName; - double counterVolume; - - public MeterData(String resourceId, String counterName, double counterVolume) { - - this.resourceId = resourceId; - this.counterName = counterName; - this.counterVolume = counterVolume; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ObjectOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ObjectOperatorsUnitTests.java deleted file mode 100644 index 5069d200b0..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ObjectOperatorsUnitTests.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2018-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.aggregation.Aggregation.SystemVariable; -import org.springframework.data.mongodb.core.aggregation.ObjectOperators.MergeObjects; - -/** - * Unit tests for {@link ObjectOperators}. - * - * @author Christoph Strobl - * @author Mark Paluch - * @currentRead Royal Assassin - Robin Hobb - */ -public class ObjectOperatorsUnitTests { - - static final String EXPRESSION_STRING = "{ \"$king-in-waiting\" : \"verity\" }"; - static final Document EXPRESSION_DOC = Document.parse(EXPRESSION_STRING); - static final AggregationExpression EXPRESSION = context -> EXPRESSION_DOC; - - @Test // DATAMONGO-2053 - public void mergeSingleFieldReference() { - - assertThat(ObjectOperators.valueOf("kettricken").merge().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $mergeObjects: \"$kettricken\" } ")); - } - - @Test // DATAMONGO-2053 - public void mergeSingleExpression() { - - assertThat(ObjectOperators.valueOf(EXPRESSION).merge().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $mergeObjects: " + EXPRESSION_STRING + " } ")); - } - - @Test // DATAMONGO-2053 - public void mergeEmpty() { - - assertThat(MergeObjects.merge().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $mergeObjects: [] } ")); - } - - @Test // DATAMONGO-2053 - public void mergeMuliFieldReference() { - - assertThat( - ObjectOperators.valueOf("kettricken").mergeWithValuesOf("verity").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $mergeObjects: [ \"$kettricken\", \"$verity\" ] } ")); - } - - @Test // DATAMONGO-2053 - public void mergeMixed() { - - assertThat(ObjectOperators.valueOf("kettricken").mergeWithValuesOf(EXPRESSION).mergeWithValuesOf("verity") - .toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo( - Document.parse("{ $mergeObjects: [ \"$kettricken\", " + EXPRESSION_STRING + ", \"$verity\" ] } ")); - } - - @Test // DATAMONGO-2053 - public void mergeWithSystemVariable() { - - assertThat( - ObjectOperators.valueOf(EXPRESSION).mergeWith(SystemVariable.ROOT).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $mergeObjects: [ " + EXPRESSION_STRING + ", \"$$ROOT\" ] } ")); - } - - @Test // DATAMONGO-2053 - public void mergeMany() { - - assertThat(ObjectOperators.valueOf("kettricken").mergeWithValuesOf(EXPRESSION) - .mergeWith(new Document("fitz", "chivalry")).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse( - "{ $mergeObjects: [ \"$kettricken\", " + EXPRESSION_STRING + ", { \"fitz\" : \"chivalry\" } ] } ")); - } - - @Test // DATAMONGO-2052 - public void toArrayWithFieldReference() { - - assertThat(ObjectOperators.valueOf("verity").toArray().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $objectToArray : \"$verity\" }")); - } - - @Test // DATAMONGO-2052 - public void toArrayWithExpression() { - - assertThat(ObjectOperators.valueOf(EXPRESSION).toArray().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $objectToArray : " + EXPRESSION_STRING + " }")); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java deleted file mode 100644 index a8b052e5db..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2021 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.aggregation; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -/** - * @author Thomas Darimont - */ -public class Order { - - final String id; - - final String customerId; - - final Date orderDate; - - final List items; - - public Order(String id, String customerId, Date orderDate) { - this(id, customerId, orderDate, new ArrayList()); - } - - public Order(String id, String customerId, Date orderDate, List items) { - this.id = id; - this.customerId = customerId; - this.orderDate = orderDate; - this.items = items; - } - - public Order addItem(LineItem item) { - - List newItems = new ArrayList(items != null ? items : Collections. emptyList()); - newItems.add(item); - - return new Order(id, customerId, orderDate, newItems); - } - - public String getId() { - return id; - } - - public String getCustomerId() { - return customerId; - } - - public Date getOrderDate() { - return orderDate; - } - - public List getItems() { - return items; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/OutOperationUnitTest.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/OutOperationUnitTest.java deleted file mode 100644 index 6e83dcf437..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/OutOperationUnitTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link OutOperation}. - * - * @author Nikolay Bogdanov - * @author Christoph Strobl - * @author Mark Paluch - */ -public class OutOperationUnitTest { - - @Test // DATAMONGO-1418 - public void shouldCheckNPEInCreation() { - assertThatIllegalArgumentException().isThrownBy(() -> new OutOperation(null)); - } - - @Test // DATAMONGO-2259 - public void shouldUsePreMongoDB42FormatWhenOnlyCollectionIsPresent() { - assertThat(out("out-col").toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(new Document("$out", "out-col")); - } - - @Test // DATAMONGO-2259 - public void shouldUseMongoDB42ExtendedFormatWhenAdditionalParametersPresent() { - - assertThat(out("out-col").insertDocuments().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(new Document("$out", new Document("to", "out-col").append("mode", "insertDocuments"))); - } - - @Test // DATAMONGO-2259 - public void shouldRenderExtendedFormatWithJsonStringKey() { - - assertThat(out("out-col").insertDocuments() // - .in("database-2") // - .uniqueKey("{ 'field-1' : 1, 'field-2' : 1}") // - .toDocument(Aggregation.DEFAULT_CONTEXT)) // - .containsEntry("$out.to", "out-col") // - .containsEntry("$out.mode", "insertDocuments") // - .containsEntry("$out.db", "database-2") // - .containsEntry("$out.uniqueKey", new Document("field-1", 1).append("field-2", 1)); - } - - @Test // DATAMONGO-2259 - public void shouldRenderExtendedFormatWithSingleFieldKey() { - - assertThat(out("out-col").insertDocuments().in("database-2") // - .uniqueKey("field-1").toDocument(Aggregation.DEFAULT_CONTEXT)) // - .containsEntry("$out.to", "out-col") // - .containsEntry("$out.mode", "insertDocuments") // - .containsEntry("$out.db", "database-2") // - .containsEntry("$out.uniqueKey", new Document("field-1", 1)); - } - - @Test // DATAMONGO-2259 - public void shouldRenderExtendedFormatWithMultiFieldKey() { - - assertThat(out("out-col").insertDocuments().in("database-2") // - .uniqueKeyOf(Arrays.asList("field-1", "field-2")) // - .toDocument(Aggregation.DEFAULT_CONTEXT)).containsEntry("$out.to", "out-col") // - .containsEntry("$out.mode", "insertDocuments") // - .containsEntry("$out.db", "database-2") // - .containsEntry("$out.uniqueKey", new Document("field-1", 1).append("field-2", 1)); - } - - @Test // DATAMONGO-2259 - public void shouldErrorOnExtendedFormatWithoutMode() { - - assertThatThrownBy(() -> out("out-col").in("database-2").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isInstanceOf(IllegalStateException.class); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Product.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Product.java deleted file mode 100644 index 08ebc1e204..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Product.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -/** - * @author Thomas Darimont - */ -public class Product { - String id; - String name; - double netPrice; - int spaceUnits; - double discountRate; - double taxRate; - - public Product() {} - - public Product(String id, String name, double netPrice, int spaceUnits, double discountRate, double taxRate) { - this.id = id; - this.name = name; - this.netPrice = netPrice; - this.spaceUnits = spaceUnits; - this.discountRate = discountRate; - this.taxRate = taxRate; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java deleted file mode 100755 index 805fc10f38..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java +++ /dev/null @@ -1,2269 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.core.aggregation.AggregationFunctionExpressions.*; -import static org.springframework.data.mongodb.core.aggregation.Fields.*; -import static org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable.*; -import static org.springframework.data.mongodb.test.util.Assertions.assertThat; - -import lombok.Data; - -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.domain.Range; -import org.springframework.data.domain.Range.Bound; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce; -import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.PropertyExpression; -import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.Variable; -import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Slice; -import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator; -import org.springframework.data.mongodb.core.aggregation.DateOperators.Timezone; -import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder; -import org.springframework.data.mongodb.core.aggregation.StringOperators.Concat; -import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -/** - * Unit tests for {@link ProjectionOperation}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - */ -public class ProjectionOperationUnitTests { - - static final String MOD = "$mod"; - static final String ADD = "$add"; - static final String SUBTRACT = "$subtract"; - static final String MULTIPLY = "$multiply"; - static final String DIVIDE = "$divide"; - static final String PROJECT = "$project"; - - @Test // DATAMONGO-586 - public void rejectsNullFields() { - assertThatIllegalArgumentException().isThrownBy(() -> new ProjectionOperation((Fields) null)); - } - - @Test // DATAMONGO-586 - public void declaresBackReferenceCorrectly() { - - ProjectionOperation operation = new ProjectionOperation(); - operation = operation.and("prop").previousOperation(); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - assertThat(projectClause.get("prop")).isEqualTo(Fields.UNDERSCORE_ID_REF); - } - - @Test // DATAMONGO-586 - public void alwaysUsesExplicitReference() { - - ProjectionOperation operation = new ProjectionOperation(Fields.fields("foo").and("bar", "foobar")); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - assertThat(projectClause.get("foo")).isEqualTo(1); - assertThat(projectClause.get("bar")).isEqualTo("$foobar"); - } - - @Test // DATAMONGO-586 - public void aliasesSimpleFieldProjection() { - - ProjectionOperation operation = new ProjectionOperation(); - - Document document = operation.and("foo").as("bar").toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - assertThat(projectClause.get("bar")).isEqualTo("$foo"); - } - - @Test // DATAMONGO-586 - public void aliasesArithmeticProjection() { - - ProjectionOperation operation = new ProjectionOperation(); - - Document document = operation.and("foo").plus(41).as("bar").toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - Document barClause = DocumentTestUtils.getAsDocument(projectClause, "bar"); - List addClause = (List) barClause.get("$add"); - - assertThat(addClause).hasSize(2); - assertThat(addClause.get(0)).isEqualTo("$foo"); - assertThat(addClause.get(1)).isEqualTo(41); - } - - @Test // DATAMONGO-586 - public void arithmeticProjectionOperationWithoutAlias() { - - String fieldName = "a"; - ProjectionOperationBuilder operation = new ProjectionOperation().and(fieldName).plus(1); - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - Document oper = extractOperation(fieldName, projectClause); - - assertThat(oper.containsKey(ADD)).isTrue(); - assertThat(oper.get(ADD)).isEqualTo(Arrays. asList("$a", 1)); - } - - @Test // DATAMONGO-586 - public void arithmeticProjectionOperationPlus() { - - String fieldName = "a"; - String fieldAlias = "b"; - ProjectionOperation operation = new ProjectionOperation().and(fieldName).plus(1).as(fieldAlias); - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - Document oper = extractOperation(fieldAlias, projectClause); - assertThat(oper.containsKey(ADD)).isTrue(); - assertThat(oper.get(ADD)).isEqualTo(Arrays. asList("$a", 1)); - } - - @Test // DATAMONGO-586 - public void arithmeticProjectionOperationMinus() { - - String fieldName = "a"; - String fieldAlias = "b"; - ProjectionOperation operation = new ProjectionOperation().and(fieldName).minus(1).as(fieldAlias); - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - Document oper = extractOperation(fieldAlias, projectClause); - - assertThat(oper.containsKey(SUBTRACT)).isTrue(); - assertThat(oper.get(SUBTRACT)).isEqualTo(Arrays. asList("$a", 1)); - } - - @Test // DATAMONGO-586 - public void arithmeticProjectionOperationMultiply() { - - String fieldName = "a"; - String fieldAlias = "b"; - ProjectionOperation operation = new ProjectionOperation().and(fieldName).multiply(1).as(fieldAlias); - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - Document oper = extractOperation(fieldAlias, projectClause); - - assertThat(oper.containsKey(MULTIPLY)).isTrue(); - assertThat(oper.get(MULTIPLY)).isEqualTo(Arrays. asList("$a", 1)); - } - - @Test // DATAMONGO-586 - public void arithmeticProjectionOperationDivide() { - - String fieldName = "a"; - String fieldAlias = "b"; - ProjectionOperation operation = new ProjectionOperation().and(fieldName).divide(1).as(fieldAlias); - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - Document oper = extractOperation(fieldAlias, projectClause); - - assertThat(oper.containsKey(DIVIDE)).isTrue(); - assertThat(oper.get(DIVIDE)).isEqualTo(Arrays. asList("$a", 1)); - } - - @Test // DATAMONGO-586 - public void arithmeticProjectionOperationDivideByZeroException() { - assertThatIllegalArgumentException().isThrownBy(() -> new ProjectionOperation().and("a").divide(0)); - } - - @Test // DATAMONGO-586 - public void arithmeticProjectionOperationMod() { - - String fieldName = "a"; - String fieldAlias = "b"; - ProjectionOperation operation = new ProjectionOperation().and(fieldName).mod(3).as(fieldAlias); - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - Document oper = extractOperation(fieldAlias, projectClause); - - assertThat(oper.containsKey(MOD)).isTrue(); - assertThat(oper.get(MOD)).isEqualTo(Arrays. asList("$a", 3)); - } - - @Test // DATAMONGO-758, DATAMONGO-1893 - public void excludeShouldAllowExclusionOfFieldsOtherThanUnderscoreId/* since MongoDB 3.4 */() { - - ProjectionOperation projectionOp = new ProjectionOperation().andExclude("foo"); - Document document = projectionOp.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - assertThat(projectionOp.inheritsFields()).isTrue(); - assertThat((Integer) projectClause.get("foo")).isEqualTo(0); - } - - @Test // DATAMONGO-1893 - public void includeShouldNotInheritFields() { - - ProjectionOperation projectionOp = new ProjectionOperation().andInclude("foo"); - - assertThat(projectionOp.inheritsFields()).isFalse(); - } - - @Test // DATAMONGO-758 - public void excludeShouldAllowExclusionOfUnderscoreId() { - - ProjectionOperation projectionOp = new ProjectionOperation().andExclude(Fields.UNDERSCORE_ID); - Document document = projectionOp.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - assertThat((Integer) projectClause.get(Fields.UNDERSCORE_ID)).isEqualTo(0); - } - - @Test // DATAMONGO-1906 - public void rendersConditionalProjectionCorrectly() { - - TypedAggregation aggregation = Aggregation.newAggregation(Book.class, - Aggregation.project("title") - .and(ConditionalOperators.when(ComparisonOperators.valueOf("author.middle").equalToValue("")) - .then("$$REMOVE").otherwiseValueOf("author.middle")) - .as("author.middle")); - - Document document = aggregation.toDocument("books", Aggregation.DEFAULT_CONTEXT); - - assertThat(document).isEqualTo(Document.parse( - "{\"aggregate\" : \"books\", \"pipeline\" : [{\"$project\" : {\"title\" : 1, \"author.middle\" : {\"$cond\" : {\"if\" : {\"$eq\" : [\"$author.middle\", \"\"]}, \"then\" : \"$$REMOVE\",\"else\" : \"$author.middle\"} }}}]}")); - } - - @Test // DATAMONGO-757 - public void usesImplictAndExplicitFieldAliasAndIncludeExclude() { - - ProjectionOperation operation = Aggregation.project("foo").and("foobar").as("bar").andInclude("inc1", "inc2") - .andExclude("_id"); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - assertThat(projectClause.get("foo")).isEqualTo(1); // implicit - assertThat(projectClause.get("bar")).isEqualTo("$foobar"); // explicit - assertThat(projectClause.get("inc1")).isEqualTo(1); // include shortcut - assertThat(projectClause.get("inc2")).isEqualTo(1); - assertThat(projectClause.get("_id")).isEqualTo(0); - } - - @Test - public void arithmeticProjectionOperationModByZeroException() { - assertThatIllegalArgumentException().isThrownBy(() -> new ProjectionOperation().and("a").mod(0)); - } - - @Test // DATAMONGO-769 - public void allowArithmeticOperationsWithFieldReferences() { - - ProjectionOperation operation = Aggregation.project() // - .and("foo").plus("bar").as("fooPlusBar") // - .and("foo").minus("bar").as("fooMinusBar") // - .and("foo").multiply("bar").as("fooMultiplyBar") // - .and("foo").divide("bar").as("fooDivideBar") // - .and("foo").mod("bar").as("fooModBar"); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - assertThat(projectClause.get("fooPlusBar")). // - isEqualTo(new Document("$add", Arrays.asList("$foo", "$bar"))); - assertThat(projectClause.get("fooMinusBar")). // - isEqualTo(new Document("$subtract", Arrays.asList("$foo", "$bar"))); - assertThat(projectClause.get("fooMultiplyBar")). // - isEqualTo(new Document("$multiply", Arrays.asList("$foo", "$bar"))); - assertThat(projectClause.get("fooDivideBar")). // - isEqualTo(new Document("$divide", Arrays.asList("$foo", "$bar"))); - assertThat(projectClause.get("fooModBar")). // - isEqualTo(new Document("$mod", Arrays.asList("$foo", "$bar"))); - } - - @Test // DATAMONGO-774 - public void projectionExpressions() { - - ProjectionOperation operation = Aggregation.project() // - .andExpression("(netPrice + surCharge) * taxrate * [0]", 2).as("grossSalesPrice") // - .and("foo").as("bar"); // - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(document).isEqualTo(Document.parse( - "{ \"$project\" : { \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"bar\" : \"$foo\"}}")); - } - - @Test // DATAMONGO-975 - public void shouldRenderDateTimeFragmentExtractionsForSimpleFieldProjectionsCorrectly() { - - ProjectionOperation operation = Aggregation.project() // - .and("date").extractHour().as("hour") // - .and("date").extractMinute().as("min") // - .and("date").extractSecond().as("second") // - .and("date").extractMillisecond().as("millis") // - .and("date").extractYear().as("year") // - .and("date").extractMonth().as("month") // - .and("date").extractWeek().as("week") // - .and("date").extractDayOfYear().as("dayOfYear") // - .and("date").extractDayOfMonth().as("dayOfMonth") // - .and("date").extractDayOfWeek().as("dayOfWeek") // - ; - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(document).isNotNull(); - - Document projected = extractOperation("$project", document); - - assertThat(projected.get("hour")).isEqualTo(new Document("$hour", Arrays.asList("$date"))); - assertThat(projected.get("min")).isEqualTo(new Document("$minute", Arrays.asList("$date"))); - assertThat(projected.get("second")).isEqualTo(new Document("$second", Arrays.asList("$date"))); - assertThat(projected.get("millis")).isEqualTo(new Document("$millisecond", Arrays.asList("$date"))); - assertThat(projected.get("year")).isEqualTo(new Document("$year", Arrays.asList("$date"))); - assertThat(projected.get("month")).isEqualTo(new Document("$month", Arrays.asList("$date"))); - assertThat(projected.get("week")).isEqualTo(new Document("$week", Arrays.asList("$date"))); - assertThat(projected.get("dayOfYear")).isEqualTo(new Document("$dayOfYear", Arrays.asList("$date"))); - assertThat(projected.get("dayOfMonth")).isEqualTo(new Document("$dayOfMonth", Arrays.asList("$date"))); - assertThat(projected.get("dayOfWeek")).isEqualTo(new Document("$dayOfWeek", Arrays.asList("$date"))); - } - - @Test // DATAMONGO-975 - public void shouldRenderDateTimeFragmentExtractionsForExpressionProjectionsCorrectly() throws Exception { - - ProjectionOperation operation = Aggregation.project() // - .andExpression("date + 86400000") // - .extractDayOfYear() // - .as("dayOfYearPlus1Day") // - ; - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - assertThat(document).isNotNull(); - - Document projected = extractOperation("$project", document); - assertThat(projected.get("dayOfYearPlus1Day")).isEqualTo( - new Document("$dayOfYear", Arrays.asList(new Document("$add", Arrays. asList("$date", 86400000))))); - } - - @Test // DATAMONGO-979 - public void shouldRenderSizeExpressionInProjection() { - - ProjectionOperation operation = Aggregation // - .project() // - .and("tags") // - .size()// - .as("tags_count"); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document projected = extractOperation("$project", document); - assertThat(projected.get("tags_count")).isEqualTo(new Document("$size", Arrays.asList("$tags"))); - } - - @Test // DATAMONGO-979 - public void shouldRenderGenericSizeExpressionInProjection() { - - ProjectionOperation operation = Aggregation // - .project() // - .and(SIZE.of(field("tags"))) // - .as("tags_count"); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document projected = extractOperation("$project", document); - assertThat(projected.get("tags_count")).isEqualTo(new Document("$size", Arrays.asList("$tags"))); - } - - @Test // DATAMONGO-1457 - public void shouldRenderSliceCorrectly() throws Exception { - - ProjectionOperation operation = Aggregation.project().and("field").slice(10).as("renamed"); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projected = extractOperation("$project", document); - - assertThat(projected.get("renamed")).isEqualTo(new Document("$slice", Arrays. asList("$field", 10))); - } - - @Test // DATAMONGO-1457 - public void shouldRenderSliceWithPositionCorrectly() throws Exception { - - ProjectionOperation operation = Aggregation.project().and("field").slice(10, 5).as("renamed"); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projected = extractOperation("$project", document); - - assertThat(projected.get("renamed")).isEqualTo(new Document("$slice", Arrays. asList("$field", 5, 10))); - } - - @Test // DATAMONGO-784 - public void shouldRenderCmpCorrectly() { - - ProjectionOperation operation = Aggregation.project().and("field").cmp(10).as("cmp10"); - - assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).containsEntry("$project.cmp10.$cmp.[0]", "$field") - .containsEntry("$project.cmp10.$cmp.[1]", 10); - } - - @Test // DATAMONGO-784 - public void shouldRenderEqCorrectly() { - - ProjectionOperation operation = Aggregation.project().and("field").eq(10).as("eq10"); - - assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).containsEntry("$project.eq10.$eq.[0]", "$field") - .containsEntry("$project.eq10.$eq.[1]", 10); - } - - @Test // DATAMONGO-784 - public void shouldRenderGtCorrectly() { - - ProjectionOperation operation = Aggregation.project().and("field").gt(10).as("gt10"); - - assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).containsEntry("$project.gt10.$gt.[0]", "$field") - .containsEntry("$project.gt10.$gt.[1]", 10); - } - - @Test // DATAMONGO-784 - public void shouldRenderGteCorrectly() { - - ProjectionOperation operation = Aggregation.project().and("field").gte(10).as("gte10"); - - assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).containsEntry("$project.gte10.$gte.[0]", "$field") - .containsEntry("$project.gte10.$gte.[1]", 10); - } - - @Test // DATAMONGO-784 - public void shouldRenderLtCorrectly() { - - ProjectionOperation operation = Aggregation.project().and("field").lt(10).as("lt10"); - - assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).containsEntry("$project.lt10.$lt.[0]", "$field") - .containsEntry("$project.lt10.$lt.[1]", 10); - } - - @Test // DATAMONGO-784 - public void shouldRenderLteCorrectly() { - - ProjectionOperation operation = Aggregation.project().and("field").lte(10).as("lte10"); - - assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).containsEntry("$project.lte10.$lte.[0]", "$field") - .containsEntry("$project.lte10.$lte.[1]", 10); - } - - @Test // DATAMONGO-784 - public void shouldRenderNeCorrectly() { - - ProjectionOperation operation = Aggregation.project().and("field").ne(10).as("ne10"); - - assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).containsEntry("$project.ne10.$ne.[0]", "$field") - .containsEntry("$project.ne10.$ne.[1]", 10); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetEquals() { - - Document agg = project("A", "B").and("A").equalsArrays("B").as("sameElements") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { A: 1, B: 1, sameElements: { $setEquals: [ \"$A\", \"$B\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetEqualsAggregationExpresssion() { - - Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").isEqualTo("B")).as("sameElements") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { A: 1, B: 1, sameElements: { $setEquals: [ \"$A\", \"$B\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetIntersection() { - - Document agg = project("A", "B").and("A").intersectsArrays("B").as("commonToBoth") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { A: 1, B: 1, commonToBoth: { $setIntersection: [ \"$A\", \"$B\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetIntersectionAggregationExpresssion() { - - Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").intersects("B")).as("commonToBoth") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { A: 1, B: 1, commonToBoth: { $setIntersection: [ \"$A\", \"$B\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetUnion() { - - Document agg = project("A", "B").and("A").unionArrays("B").as("allValues").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { A: 1, B: 1, allValues: { $setUnion: [ \"$A\", \"$B\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetUnionAggregationExpresssion() { - - Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").union("B")).as("allValues") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { A: 1, B: 1, allValues: { $setUnion: [ \"$A\", \"$B\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetDifference() { - - Document agg = project("A", "B").and("B").differenceToArray("A").as("inBOnly") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { A: 1, B: 1, inBOnly: { $setDifference: [ \"$B\", \"$A\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetDifferenceAggregationExpresssion() { - - Document agg = project("A", "B").and(SetOperators.arrayAsSet("B").differenceTo("A")).as("inBOnly") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { A: 1, B: 1, inBOnly: { $setDifference: [ \"$B\", \"$A\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetIsSubset() { - - Document agg = project("A", "B").and("A").subsetOfArray("B").as("aIsSubsetOfB") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { A: 1, B: 1, aIsSubsetOfB: { $setIsSubset: [ \"$A\", \"$B\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSetIsSubsetAggregationExpresssion() { - - Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").isSubsetOf("B")).as("aIsSubsetOfB") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { A: 1, B: 1, aIsSubsetOfB: { $setIsSubset: [ \"$A\", \"$B\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderAnyElementTrue() { - - Document agg = project("responses").and("responses").anyElementInArrayTrue().as("isAnyTrue") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { responses: 1, isAnyTrue: { $anyElementTrue: [ \"$responses\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderAnyElementTrueAggregationExpresssion() { - - Document agg = project("responses").and(SetOperators.arrayAsSet("responses").anyElementTrue()).as("isAnyTrue") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { responses: 1, isAnyTrue: { $anyElementTrue: [ \"$responses\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderAllElementsTrue() { - - Document agg = project("responses").and("responses").allElementsInArrayTrue().as("isAllTrue") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { responses: 1, isAllTrue: { $allElementsTrue: [ \"$responses\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderAllElementsTrueAggregationExpresssion() { - - Document agg = project("responses").and(SetOperators.arrayAsSet("responses").allElementsTrue()).as("isAllTrue") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { responses: 1, isAllTrue: { $allElementsTrue: [ \"$responses\" ] }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderAbs() { - - Document agg = project().and("anyNumber").absoluteValue().as("absoluteValue") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { absoluteValue : { $abs: \"$anyNumber\" }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderAbsAggregationExpresssion() { - - Document agg = project() - .and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).abs()) - .as("delta").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { delta: { $abs: { $subtract: [ \"$start\", \"$end\" ] } } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderAddAggregationExpresssion() { - - Document agg = project().and(ArithmeticOperators.valueOf("price").add("fee")).as("total") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse(" { $project: { total: { $add: [ \"$price\", \"$fee\" ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderCeil() { - - Document agg = project().and("anyNumber").ceil().as("ceilValue").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { ceilValue : { $ceil: \"$anyNumber\" }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderCeilAggregationExpresssion() { - - Document agg = project().and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).ceil()) - .as("delta").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { delta: { $ceil: { $subtract: [ \"$start\", \"$end\" ] } } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderDivide() { - - Document agg = project().and("value") - .divide(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).as("result") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { result: { $divide: [ \"$value\", { $subtract: [ \"$start\", \"$end\" ] }] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderDivideAggregationExpresssion() { - - Document agg = project() - .and(ArithmeticOperators.valueOf("anyNumber") - .divideBy(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end")))) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { result: { $divide: [ \"$anyNumber\", { $subtract: [ \"$start\", \"$end\" ] }] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderExp() { - - Document agg = project().and("value").exp().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { result: { $exp: \"$value\" } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderExpAggregationExpresssion() { - - Document agg = project() - .and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).exp()) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $exp: { $subtract: [ \"$start\", \"$end\" ] } } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderFloor() { - - Document agg = project().and("value").floor().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { result: { $floor: \"$value\" } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderFloorAggregationExpresssion() { - - Document agg = project().and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).floor()) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $floor: { $subtract: [ \"$start\", \"$end\" ] } } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLn() { - - Document agg = project().and("value").ln().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { result: { $ln: \"$value\"} }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLnAggregationExpresssion() { - - Document agg = project() - .and(ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).ln()) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $ln: { $subtract: [ \"$start\", \"$end\" ] } } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLog() { - - Document agg = project().and("value").log(2).as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { result: { $log: [ \"$value\", 2] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLogAggregationExpresssion() { - - Document agg = project().and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).log(2)) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $log: [ { $subtract: [ \"$start\", \"$end\" ] }, 2] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLog10() { - - Document agg = project().and("value").log10().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { result: { $log10: \"$value\" } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLog10AggregationExpresssion() { - - Document agg = project().and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).log10()) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $log10: { $subtract: [ \"$start\", \"$end\" ] } } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMod() { - - Document agg = project().and("value").mod(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { result: { $mod: [\"$value\", { $subtract: [ \"$start\", \"$end\" ] }] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderModAggregationExpresssion() { - - Document agg = project().and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).mod(2)) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $mod: [{ $subtract: [ \"$start\", \"$end\" ] }, 2] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMultiply() { - - Document agg = project().and("value") - .multiply(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).as("result") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { result: { $multiply: [\"$value\", { $subtract: [ \"$start\", \"$end\" ] }] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMultiplyAggregationExpresssion() { - - Document agg = project() - .and(ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))) - .multiplyBy(2).multiplyBy("refToAnotherNumber")) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { result: { $multiply: [{ $subtract: [ \"$start\", \"$end\" ] }, 2, \"$refToAnotherNumber\"] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderPow() { - - Document agg = project().and("value").pow(2).as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { result: { $pow: [\"$value\", 2] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderPowAggregationExpresssion() { - - Document agg = project().and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).pow(2)) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $pow: [{ $subtract: [ \"$start\", \"$end\" ] }, 2] } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSqrt() { - - Document agg = project().and("value").sqrt().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { result: { $sqrt: \"$value\" } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSqrtAggregationExpresssion() { - - Document agg = project().and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).sqrt()) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $sqrt: { $subtract: [ \"$start\", \"$end\" ] } } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSubtract() { - - Document agg = project().and("numericField").minus(AggregationFunctionExpressions.SIZE.of(field("someArray"))) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { result: { $subtract: [ \"$numericField\", { $size : [\"$someArray\"]}] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSubtractAggregationExpresssion() { - - Document agg = project() - .and(ArithmeticOperators.valueOf("numericField") - .subtract(AggregationFunctionExpressions.SIZE.of(field("someArray")))) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { result: { $subtract: [ \"$numericField\", { $size : [\"$someArray\"]}] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderTrunc() { - - Document agg = project().and("value").trunc().as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { result : { $trunc: \"$value\" }}}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderTruncAggregationExpresssion() { - - Document agg = project().and( - ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).trunc()) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $trunc: { $subtract: [ \"$start\", \"$end\" ] } } }}")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderConcat() { - - Document agg = project().and("item").concat(" - ", field("description")).as("itemDescription") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { itemDescription: { $concat: [ \"$item\", \" - \", \"$description\" ] } } }")); - - } - - @Test // DATAMONGO-1536 - public void shouldRenderConcatAggregationExpression() { - - Document agg = project().and(StringOperators.valueOf("item").concat(" - ").concatValueOf("description")) - .as("itemDescription").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { itemDescription: { $concat: [ \"$item\", \" - \", \"$description\" ] } } }")); - - } - - @Test // DATAMONGO-1536 - public void shouldRenderSubstr() { - - Document agg = project().and("quarter").substring(0, 2).as("yearSubstring").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { yearSubstring: { $substr: [ \"$quarter\", 0, 2 ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSubstrAggregationExpression() { - - Document agg = project().and(StringOperators.valueOf("quarter").substring(0, 2)).as("yearSubstring") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { yearSubstring: { $substr: [ \"$quarter\", 0, 2 ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderToLower() { - - Document agg = project().and("item").toLower().as("item").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { item: { $toLower: \"$item\" } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderToLowerAggregationExpression() { - - Document agg = project().and(StringOperators.valueOf("item").toLower()).as("item") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { item: { $toLower: \"$item\" } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderToUpper() { - - Document agg = project().and("item").toUpper().as("item").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { item: { $toUpper: \"$item\" } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderToUpperAggregationExpression() { - - Document agg = project().and(StringOperators.valueOf("item").toUpper()).as("item") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { item: { $toUpper: \"$item\" } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderStrCaseCmp() { - - Document agg = project().and("quarter").strCaseCmp("13q4").as("comparisonResult") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { comparisonResult: { $strcasecmp: [ \"$quarter\", \"13q4\" ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderStrCaseCmpAggregationExpression() { - - Document agg = project().and(StringOperators.valueOf("quarter").strCaseCmp("13q4")).as("comparisonResult") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { comparisonResult: { $strcasecmp: [ \"$quarter\", \"13q4\" ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderArrayElementAt() { - - Document agg = project().and("favorites").arrayElementAt(0).as("first").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { first: { $arrayElemAt: [ \"$favorites\", 0 ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderArrayElementAtAggregationExpression() { - - Document agg = project().and(ArrayOperators.arrayOf("favorites").elementAt(0)).as("first") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { first: { $arrayElemAt: [ \"$favorites\", 0 ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderConcatArrays() { - - Document agg = project().and("instock").concatArrays("ordered").as("items").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { items: { $concatArrays: [ \"$instock\", \"$ordered\" ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderConcatArraysAggregationExpression() { - - Document agg = project().and(ArrayOperators.arrayOf("instock").concat("ordered")).as("items") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { items: { $concatArrays: [ \"$instock\", \"$ordered\" ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderIsArray() { - - Document agg = project().and("instock").isArray().as("isAnArray").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { isAnArray: { $isArray: \"$instock\" } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderIsArrayAggregationExpression() { - - Document agg = project().and(ArrayOperators.arrayOf("instock").isArray()).as("isAnArray") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { isAnArray: { $isArray: \"$instock\" } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSizeAggregationExpression() { - - Document agg = project().and(ArrayOperators.arrayOf("instock").length()).as("arraySize") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { arraySize: { $size: \"$instock\" } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSliceAggregationExpression() { - - Document agg = project().and(ArrayOperators.arrayOf("favorites").slice().itemCount(3)).as("threeFavorites") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { threeFavorites: { $slice: [ \"$favorites\", 3 ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSliceWithPositionAggregationExpression() { - - Document agg = project().and(ArrayOperators.arrayOf("favorites").slice().offset(2).itemCount(3)) - .as("threeFavorites").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { threeFavorites: { $slice: [ \"$favorites\", 2, 3 ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLiteral() { - - Document agg = project().and("$1").asLiteral().as("literalOnly").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { literalOnly: { $literal: \"$1\"} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLiteralAggregationExpression() { - - Document agg = project().and(LiteralOperators.valueOf("$1").asLiteral()).as("literalOnly") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { literalOnly: { $literal: \"$1\"} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderDayOfYearAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").dayOfYear()).as("dayOfYear") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { dayOfYear: { $dayOfYear: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDayOfYearAggregationExpressionWithTimezone() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).dayOfYear()).as("dayOfYear") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { dayOfYear: { $dayOfYear: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderTimeZoneFromField() { - - Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.ofField("tz")).dayOfYear()) - .as("dayOfYear").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { dayOfYear: { $dayOfYear: { \"date\" : \"$date\", \"timezone\" : \"$tz\" } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderTimeZoneFromExpression() { - - Document agg = project() - .and(DateOperators.dateOf("date") - .withTimezone(Timezone.ofExpression(LiteralOperators.valueOf("America/Chicago").asLiteral())).dayOfYear()) - .as("dayOfYear").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { dayOfYear: { $dayOfYear: { \"date\" : \"$date\", \"timezone\" : { $literal: \"America/Chicago\"} } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderDayOfMonthAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").dayOfMonth()).as("day") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { day: { $dayOfMonth: \"$date\" }} }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDayOfMonthAggregationExpressionWithTimezone() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).dayOfMonth()).as("day") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { day: { $dayOfMonth: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderDayOfWeekAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").dayOfWeek()).as("dayOfWeek") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { dayOfWeek: { $dayOfWeek: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDayOfWeekAggregationExpressionWithTimezone() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).dayOfWeek()).as("dayOfWeek") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { dayOfWeek: { $dayOfWeek: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderYearAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").year()).as("year") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { year: { $year: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderYearAggregationExpressionWithTimezone() { - - Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).year()) - .as("year").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { year: { $year: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMonthAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").month()).as("month") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { month: { $month: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderMonthAggregationExpressionWithTimezone() { - - Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).month()) - .as("month").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { month: { $month: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderWeekAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").week()).as("week") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { week: { $week: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderWeekAggregationExpressionWithTimezone() { - - Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).week()) - .as("week").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { week: { $week: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderHourAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").hour()).as("hour") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { hour: { $hour: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderHourAggregationExpressionWithTimezone() { - - Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).hour()) - .as("hour").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { hour: { $hour: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMinuteAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").minute()).as("minute") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { minute: { $minute: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderMinuteAggregationExpressionWithTimezone() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).minute()).as("minute") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { minute: { $minute: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSecondAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").second()).as("second") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { second: { $second: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderSecondAggregationExpressionWithTimezone() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).second()).as("second") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { second: { $second: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMillisecondAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").millisecond()).as("msec") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { msec: { $millisecond: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderMillisecondAggregationExpressionWithTimezone() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).millisecond()).as("msec") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { msec: { $millisecond: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderDateToString() { - - Document agg = project().and("date").dateAsFormattedString("%H:%M:%S:%L").as("time") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\" } } } }")); - } - - @Test // DATAMONGO-2047 - public void shouldRenderDateToStringWithoutFormatOption() { - - Document agg = project().and("date").dateAsFormattedString().as("time").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { time: { $dateToString: { date: \"$date\" } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderDateToStringAggregationExpression() { - - Document agg = project().and(DateOperators.dateOf("date").toString("%H:%M:%S:%L")).as("time") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\" } } } }")); - } - - @Test // DATAMONGO-1834, DATAMONGO-2047 - public void shouldRenderDateToStringAggregationExpressionWithTimezone() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).toString("%H:%M:%S:%L")) - .as("time").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\", \"timezone\" : \"America/Chicago\" } } } } } }")); - - Document removedTimezone = project().and(DateOperators.dateOf("date") - .withTimezone(Timezone.valueOf("America/Chicago")).toString("%H:%M:%S:%L").withTimezone(Timezone.none())) - .as("time").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(removedTimezone).isEqualTo( - Document.parse("{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\" } } } } } }")); - } - - @Test // DATAMONGO-2047 - public void shouldRenderDateToStringWithOnNull() { - - Document agg = project() - .and(DateOperators.dateOf("date").toStringWithDefaultFormat().onNullReturnValueOf("fallback-field")).as("time") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { time: { $dateToString: { date: \"$date\", \"onNull\" : \"$fallback-field\" } } } }")); - } - - @Test // DATAMONGO-2047 - public void shouldRenderDateToStringWithOnNullExpression() { - - Document agg = project() - .and(DateOperators.dateOf("date").toStringWithDefaultFormat() - .onNullReturnValueOf(LiteralOperators.valueOf("my-literal").asLiteral())) - .as("time").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { time: { $dateToString: { date: \"$date\", \"onNull\" : { \"$literal\": \"my-literal\"} } } } }")); - } - - @Test // DATAMONGO-2047 - public void shouldRenderDateToStringWithOnNullAndTimezone() { - - Document agg = project().and(DateOperators.dateOf("date").toStringWithDefaultFormat() - .onNullReturnValueOf("fallback-field").withTimezone(Timezone.ofField("foo"))).as("time") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { time: { $dateToString: { date: \"$date\", \"onNull\" : \"$fallback-field\", \"timezone\": \"$foo\" } } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSumAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("quizzes").sum()).as("quizTotal") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { quizTotal: { $sum: \"$quizzes\"} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderSumWithMultipleArgsAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("final").sum().and("midterm")).as("examTotal") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { examTotal: { $sum: [ \"$final\", \"$midterm\" ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderAvgAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("quizzes").avg()).as("quizAvg") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { quizAvg: { $avg: \"$quizzes\"} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderAvgWithMultipleArgsAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("final").avg().and("midterm")).as("examAvg") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { examAvg: { $avg: [ \"$final\", \"$midterm\" ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMaxAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("quizzes").max()).as("quizMax") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { quizMax: { $max: \"$quizzes\"} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMaxWithMultipleArgsAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("final").max().and("midterm")).as("examMax") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { examMax: { $max: [ \"$final\", \"$midterm\" ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMinAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("quizzes").min()).as("quizMin") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { quizMin: { $min: \"$quizzes\"} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderMinWithMultipleArgsAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("final").min().and("midterm")).as("examMin") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { examMin: { $min: [ \"$final\", \"$midterm\" ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderStdDevPopAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("scores").stdDevPop()).as("stdDev") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { stdDev: { $stdDevPop: \"$scores\"} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderStdDevSampAggregationExpression() { - - Document agg = project().and(ArithmeticOperators.valueOf("scores").stdDevSamp()).as("stdDev") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { stdDev: { $stdDevSamp: \"$scores\"} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderCmpAggregationExpression() { - - Document agg = project().and(ComparisonOperators.valueOf("qty").compareToValue(250)).as("cmp250") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { cmp250: { $cmp: [\"$qty\", 250]} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderEqAggregationExpression() { - - Document agg = project().and(ComparisonOperators.valueOf("qty").equalToValue(250)).as("eq250") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { eq250: { $eq: [\"$qty\", 250]} } }")); - } - - @Test // DATAMONGO-2513 - public void shouldRenderEqAggregationExpressionWithListComparison() { - - Document agg = project().and(ComparisonOperators.valueOf("qty").equalToValue(Arrays.asList(250))).as("eq250") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { eq250: { $eq: [\"$qty\", [250]]} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderGtAggregationExpression() { - - Document agg = project().and(ComparisonOperators.valueOf("qty").greaterThanValue(250)).as("gt250") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { gt250: { $gt: [\"$qty\", 250]} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderGteAggregationExpression() { - - Document agg = project().and(ComparisonOperators.valueOf("qty").greaterThanEqualToValue(250)).as("gte250") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { gte250: { $gte: [\"$qty\", 250]} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLtAggregationExpression() { - - Document agg = project().and(ComparisonOperators.valueOf("qty").lessThanValue(250)).as("lt250") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { lt250: { $lt: [\"$qty\", 250]} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLteAggregationExpression() { - - Document agg = project().and(ComparisonOperators.valueOf("qty").lessThanEqualToValue(250)).as("lte250") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { lte250: { $lte: [\"$qty\", 250]} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderNeAggregationExpression() { - - Document agg = project().and(ComparisonOperators.valueOf("qty").notEqualToValue(250)).as("ne250") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { ne250: { $ne: [\"$qty\", 250]} } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLogicAndAggregationExpression() { - - Document agg = project() - .and(BooleanOperators.valueOf(ComparisonOperators.valueOf("qty").greaterThanValue(100)) - .and(ComparisonOperators.valueOf("qty").lessThanValue(250))) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { result: { $and: [ { $gt: [ \"$qty\", 100 ] }, { $lt: [ \"$qty\", 250 ] } ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderLogicOrAggregationExpression() { - - Document agg = project() - .and(BooleanOperators.valueOf(ComparisonOperators.valueOf("qty").greaterThanValue(250)) - .or(ComparisonOperators.valueOf("qty").lessThanValue(200))) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project: { result: { $or: [ { $gt: [ \"$qty\", 250 ] }, { $lt: [ \"$qty\", 200 ] } ] } } }")); - } - - @Test // DATAMONGO-1536 - public void shouldRenderNotAggregationExpression() { - - Document agg = project().and(BooleanOperators.not(ComparisonOperators.valueOf("qty").greaterThanValue(250))) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { result: { $not: [ { $gt: [ \"$qty\", 250 ] } ] } } }")); - } - - @Test // DATAMONGO-1540 - public void shouldRenderMapAggregationExpression() { - - Document agg = Aggregation.project() - .and(VariableOperators.mapItemsOf("quizzes").as("grade") - .andApply(AggregationFunctionExpressions.ADD.of(field("grade"), 2))) - .as("adjustedGrades").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project:{ adjustedGrades:{ $map: { input: \"$quizzes\", as: \"grade\",in: { $add: [ \"$$grade\", 2 ] }}}}}")); - } - - @Test // DATAMONGO-1540 - public void shouldRenderMapAggregationExpressionOnExpression() { - - Document agg = Aggregation.project() - .and(VariableOperators.mapItemsOf(AggregationFunctionExpressions.SIZE.of("foo")).as("grade") - .andApply(AggregationFunctionExpressions.ADD.of(field("grade"), 2))) - .as("adjustedGrades").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project:{ adjustedGrades:{ $map: { input: { $size : [\"foo\"]}, as: \"grade\",in: { $add: [ \"$$grade\", 2 ] }}}}}")); - } - - @Test // DATAMONGO-861, DATAMONGO-1542 - public void shouldRenderIfNullConditionAggregationExpression() { - - Document agg = project().and( - ConditionalOperators.ifNull(ArrayOperators.arrayOf("array").elementAt(1)).then("a more sophisticated value")) - .as("result").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project: { result: { $ifNull: [ { $arrayElemAt: [\"$array\", 1] }, \"a more sophisticated value\" ] } } }")); - } - - @Test // DATAMONGO-1542 - public void shouldRenderIfNullValueAggregationExpression() { - - Document agg = project() - .and(ConditionalOperators.ifNull("field").then(ArrayOperators.arrayOf("array").elementAt(1))).as("result") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project: { result: { $ifNull: [ \"$field\", { $arrayElemAt: [\"$array\", 1] } ] } } }")); - } - - @Test // DATAMONGO-861, DATAMONGO-1542 - public void fieldReplacementIfNullShouldRenderCorrectly() { - - Document agg = project().and(ConditionalOperators.ifNull("optional").thenValueOf("$never-null")).as("result") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { result: { $ifNull: [ \"$optional\", \"$never-null\" ] } } }")); - } - - @Test // DATAMONGO-1538 - public void shouldRenderLetExpressionCorrectly() { - - Document agg = Aggregation.project() - .and(VariableOperators - .define( - newVariable("total") - .forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax"))), - newVariable("discounted") - .forExpression(ConditionalOperators.Cond.when("applyDiscount").then(0.9D).otherwise(1.0D))) - .andApply(AggregationFunctionExpressions.MULTIPLY.of(Fields.field("total"), Fields.field("discounted")))) // - .as("finalTotal").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project:{ \"finalTotal\" : { \"$let\": {" + // - "\"vars\": {" + // - "\"total\": { \"$add\": [ \"$price\", \"$tax\" ] }," + // - "\"discounted\": { \"$cond\": { \"if\": \"$applyDiscount\", \"then\": 0.9, \"else\": 1.0 } }" + // - "}," + // - "\"in\": { \"$multiply\": [ \"$$total\", \"$$discounted\" ] }" + // - "}}}}")); - } - - @Test // DATAMONGO-1538 - public void shouldRenderLetExpressionCorrectlyWhenUsingLetOnProjectionBuilder() { - - ExpressionVariable var1 = newVariable("total") - .forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax"))); - - ExpressionVariable var2 = newVariable("discounted") - .forExpression(ConditionalOperators.Cond.when("applyDiscount").then(0.9D).otherwise(1.0D)); - - Document agg = Aggregation.project().and("foo") - .let(Arrays.asList(var1, var2), - AggregationFunctionExpressions.MULTIPLY.of(Fields.field("total"), Fields.field("discounted"))) - .as("finalTotal").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project:{ \"finalTotal\" : { \"$let\": {" + // - "\"vars\": {" + // - "\"total\": { \"$add\": [ \"$price\", \"$tax\" ] }," + // - "\"discounted\": { \"$cond\": { \"if\": \"$applyDiscount\", \"then\": 0.9, \"else\": 1.0 } }" + // - "}," + // - "\"in\": { \"$multiply\": [ \"$$total\", \"$$discounted\" ] }" + // - "}}}}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderIndexOfBytesCorrectly() { - - Document agg = project().and(StringOperators.valueOf("item").indexOf("foo")).as("byteLocation") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project: { byteLocation: { $indexOfBytes: [ \"$item\", \"foo\" ] } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderIndexOfBytesWithRangeCorrectly() { - - Document agg = project() - .and(StringOperators.valueOf("item").indexOf("foo") - .within(Range.from(Bound.inclusive(5L)).to(Bound.exclusive(9L)))) - .as("byteLocation").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).containsEntry("$project.byteLocation.$indexOfBytes.[2]", 5L) - .containsEntry("$project.byteLocation.$indexOfBytes.[3]", 9L); - } - - @Test // DATAMONGO-1548 - public void shouldRenderIndexOfCPCorrectly() { - - Document agg = project().and(StringOperators.valueOf("item").indexOfCP("foo")).as("cpLocation") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project: { cpLocation: { $indexOfCP: [ \"$item\", \"foo\" ] } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderIndexOfCPWithRangeCorrectly() { - - Document agg = project() - .and(StringOperators.valueOf("item").indexOfCP("foo") - .within(Range.from(Bound.inclusive(5L)).to(Bound.exclusive(9L)))) - .as("cpLocation").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).containsEntry("$project.cpLocation.$indexOfCP.[2]", 5L) - .containsEntry("$project.cpLocation.$indexOfCP.[3]", 9L); - } - - @Test // DATAMONGO-1548 - public void shouldRenderSplitCorrectly() { - - Document agg = project().and(StringOperators.valueOf("city").split(", ")).as("city_state") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { city_state : { $split: [\"$city\", \", \"] }} }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderStrLenBytesCorrectly() { - - Document agg = project().and(StringOperators.valueOf("name").length()).as("length") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { \"length\": { $strLenBytes: \"$name\" } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderStrLenCPCorrectly() { - - Document agg = project().and(StringOperators.valueOf("name").lengthCP()).as("length") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { \"length\": { $strLenCP: \"$name\" } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderSubstrCPCorrectly() { - - Document agg = project().and(StringOperators.valueOf("quarter").substringCP(0, 2)).as("yearSubstring") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project : { yearSubstring: { $substrCP: [ \"$quarter\", 0, 2 ] } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderIndexOfArrayCorrectly() { - - Document agg = project().and(ArrayOperators.arrayOf("items").indexOf(2)).as("index") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { index: { $indexOfArray: [ \"$items\", 2 ] } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderRangeCorrectly() { - - Document agg = project().and(ArrayOperators.RangeOperator.rangeStartingAt(0L).to("distance").withStepSize(25L)) - .as("rest_stops").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).containsEntry("$project.rest_stops.$range.[0]", 0L) - .containsEntry("$project.rest_stops.$range.[1]", "$distance") - .containsEntry("$project.rest_stops.$range.[2]", 25L); - } - - @Test // DATAMONGO-1548 - public void shouldRenderReverseArrayCorrectly() { - - Document agg = project().and(ArrayOperators.arrayOf("favorites").reverse()).as("reverseFavorites") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { reverseFavorites: { $reverseArray: \"$favorites\" } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderReduceWithSimpleObjectCorrectly() { - - Document agg = project() - .and(ArrayOperators.arrayOf("probabilityArr") - .reduce(ArithmeticOperators.valueOf("$$value").multiplyBy("$$this")).startingWith(1)) - .as("results").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { \"results\": { $reduce: { input: \"$probabilityArr\", initialValue: 1, in: { $multiply: [ \"$$value\", \"$$this\" ] } } } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderReduceWithComplexObjectCorrectly() { - - PropertyExpression sum = PropertyExpression.property("sum").definedAs( - ArithmeticOperators.valueOf(Variable.VALUE.referringTo("sum").getName()).add(Variable.THIS.getName())); - PropertyExpression product = PropertyExpression.property("product").definedAs(ArithmeticOperators - .valueOf(Variable.VALUE.referringTo("product").getName()).multiplyBy(Variable.THIS.getName())); - - Document agg = project() - .and(ArrayOperators.arrayOf("probabilityArr").reduce(sum, product) - .startingWith(new Document().append("sum", 5).append("product", 2))) - .as("results").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { \"results\": { $reduce: { input: \"$probabilityArr\", initialValue: { \"sum\" : 5 , \"product\" : 2} , in: { \"sum\": { $add : [\"$$value.sum\", \"$$this\"] }, \"product\": { $multiply: [ \"$$value.product\", \"$$this\" ] } } } } } }")); - } - - @Test // DATAMONGO-1843 - public void shouldRenderReduceWithInputAndInExpressionsCorrectly() { - - Document expected = Document.parse( - "{ \"$project\" : { \"results\" : { \"$reduce\" : { \"input\" : { \"$slice\" : [\"$array\", 5] }, \"initialValue\" : \"\", \"in\" : { \"$concat\" : [\"$$value\", \"/\", \"$$this\"] } } } } }"); - - Reduce reduceEntryPoint = Reduce.arrayOf(Slice.sliceArrayOf("array").itemCount(5)) // - .withInitialValue("") // - .reduce(Concat.valueOf("$$value").concat("/").concatValueOf("$$this")); - - Reduce arrayEntryPoint = ArrayOperators.arrayOf(Slice.sliceArrayOf("array").itemCount(5)) // - .reduce(Concat.valueOf("$$value").concat("/").concatValueOf("$$this")) // - .startingWith(""); - - assertThat(project().and(reduceEntryPoint).as("results").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(expected); - - assertThat(project().and(arrayEntryPoint).as("results").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(expected); - } - - @Test // DATAMONGO-1548 - public void shouldRenderZipCorrectly() { - - AggregationExpression elemAt0 = ArrayOperators.arrayOf("matrix").elementAt(0); - AggregationExpression elemAt1 = ArrayOperators.arrayOf("matrix").elementAt(1); - AggregationExpression elemAt2 = ArrayOperators.arrayOf("matrix").elementAt(2); - - Document agg = project().and( - ArrayOperators.arrayOf(elemAt0).zipWith(elemAt1, elemAt2).useLongestLength().defaultTo(new Object[] { 1, 2 })) - .as("transposed").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { transposed: { $zip: { inputs: [ { $arrayElemAt: [ \"$matrix\", 0 ] }, { $arrayElemAt: [ \"$matrix\", 1 ] }, { $arrayElemAt: [ \"$matrix\", 2 ] } ], useLongestLength : true, defaults: [1,2] } } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderInCorrectly() { - - Document agg = project().and(ArrayOperators.arrayOf("in_stock").containsValue("bananas")).as("has_bananas") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project : { has_bananas : { $in : [\"bananas\", \"$in_stock\" ] } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderIsoDayOfWeekCorrectly() { - - Document agg = project().and(DateOperators.dateOf("birthday").isoDayOfWeek()).as("dayOfWeek") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { dayOfWeek: { $isoDayOfWeek: \"$birthday\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderIsoDayOfWeekWithTimezoneCorrectly() { - - Document agg = project() - .and(DateOperators.dateOf("birthday").withTimezone(Timezone.valueOf("America/Chicago")).isoDayOfWeek()) - .as("dayOfWeek").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { dayOfWeek: { $isoDayOfWeek: { \"date\" : \"$birthday\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderIsoWeekCorrectly() { - - Document agg = project().and(DateOperators.dateOf("date").isoWeek()).as("weekNumber") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { weekNumber: { $isoWeek: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderIsoWeekWithTimezoneCorrectly() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).isoWeek()).as("weekNumber") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { weekNumber: { $isoWeek: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderIsoWeekYearCorrectly() { - - Document agg = project().and(DateOperators.dateOf("date").isoWeekYear()).as("yearNumber") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { yearNumber: { $isoWeekYear: \"$date\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderIsoWeekYearWithTimezoneCorrectly() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).isoWeekYear()) - .as("yearNumber").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { yearNumber: { $isoWeekYear: { \"date\" : \"$date\", \"timezone\" : \"America/Chicago\" } } } } }")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderSwitchCorrectly() { - - String expected = "$switch:\n" + // - "{\n" + // - " branches: [\n" + // - " {\n" + // - " case: { $gte : [ { $avg : \"$scores\" }, 90 ] },\n" + // - " then: \"Doing great!\"\n" + // - " },\n" + // - " {\n" + // - " case: { $and : [ { $gte : [ { $avg : \"$scores\" }, 80 ] },\n" + // - " { $lt : [ { $avg : \"$scores\" }, 90 ] } ] },\n" + // - " then: \"Doing pretty well.\"\n" + // - " },\n" + // - " {\n" + // - " case: { $lt : [ { $avg : \"$scores\" }, 80 ] },\n" + // - " then: \"Needs improvement.\"\n" + // - " }\n" + // - " ],\n" + // - " default: \"No scores found.\"\n" + // - " }\n" + // - "}"; - - CaseOperator cond1 = CaseOperator - .when(ComparisonOperators.Gte.valueOf(AccumulatorOperators.Avg.avgOf("scores")).greaterThanEqualToValue(90)) - .then("Doing great!"); - CaseOperator cond2 = CaseOperator - .when(BooleanOperators.And.and( - ComparisonOperators.Gte.valueOf(AccumulatorOperators.Avg.avgOf("scores")).greaterThanEqualToValue(80), - ComparisonOperators.Lt.valueOf(AccumulatorOperators.Avg.avgOf("scores")).lessThanValue(90))) - .then("Doing pretty well."); - CaseOperator cond3 = CaseOperator - .when(ComparisonOperators.Lt.valueOf(AccumulatorOperators.Avg.avgOf("scores")).lessThanValue(80)) - .then("Needs improvement."); - - Document agg = project().and(ConditionalOperators.switchCases(cond1, cond2, cond3).defaultTo("No scores found.")) - .as("summary").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { summary: {" + expected + "} } }")); - } - - @Test // DATAMONGO-1548 - public void shouldTypeCorrectly() { - - Document agg = project().and(DataTypeOperators.Type.typeOf("a")).as("a").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { a: { $type: \"$a\" } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDateFromPartsWithJustTheYear() { - - Document agg = project().and(DateOperators.dateFromParts().year(2018)).as("newDate") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { newDate: { $dateFromParts: { year : 2018 } } } }")); - } - - @Test // DATAMONGO-1834, DATAMONGO-2671 - public void shouldRenderDateFromParts() { - - Document agg = project() - .and(DateOperators.dateFromParts().year(2018).month(3).day(23).hour(14).minute(25).second(10).millisecond(2)) - .as("newDate").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { newDate: { $dateFromParts: { year : 2018, month : 3, day : 23, hour : 14, minute : 25, second : 10, millisecond : 2 } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDateFromPartsWithTimezone() { - - Document agg = project() - .and(DateOperators.dateFromParts().withTimezone(Timezone.valueOf("America/Chicago")).year(2018)).as("newDate") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project : { newDate: { $dateFromParts: { year : 2018, timezone : \"America/Chicago\" } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderIsoDateFromPartsWithJustTheYear() { - - Document agg = project().and(DateOperators.dateFromParts().isoWeekYear(2018)).as("newDate") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { newDate: { $dateFromParts: { isoWeekYear : 2018 } } } }")); - } - - @Test // DATAMONGO-1834, DATAMONGO-2671 - public void shouldRenderIsoDateFromParts() { - - Document agg = project().and(DateOperators.dateFromParts().isoWeekYear(2018).isoWeek(12).isoDayOfWeek(5).hour(14) - .minute(30).second(42).millisecond(2)).as("newDate").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { newDate: { $dateFromParts: { isoWeekYear : 2018, isoWeek : 12, isoDayOfWeek : 5, hour : 14, minute : 30, second : 42, millisecond : 2 } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderIsoDateFromPartsWithTimezone() { - - Document agg = project() - .and(DateOperators.dateFromParts().withTimezone(Timezone.valueOf("America/Chicago")).isoWeekYear(2018)) - .as("newDate").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { newDate: { $dateFromParts: { isoWeekYear : 2018, timezone : \"America/Chicago\" } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDateToParts() { - - Document agg = project().and(DateOperators.dateOf("date").toParts()).as("newDate") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse("{ $project : { newDate: { $dateToParts: { date : \"$date\" } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDateToIsoParts() { - - Document agg = project().and(DateOperators.dateOf("date").toParts().iso8601()).as("newDate") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo( - Document.parse("{ $project : { newDate: { $dateToParts: { date : \"$date\", iso8601 : true } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDateToPartsWithTimezone() { - - Document agg = project() - .and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).toParts()).as("newDate") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project : { newDate: { $dateToParts: { date : \"$date\", timezone : \"America/Chicago\" } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDateFromString() { - - Document agg = project().and(DateOperators.dateFromString("2017-02-08T12:10:40.787")).as("newDate") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document - .parse("{ $project : { newDate: { $dateFromString: { dateString : \"2017-02-08T12:10:40.787\" } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDateFromStringWithFieldReference() { - - Document agg = project().and(DateOperators.dateOf("date").fromString()).as("newDate") - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg) - .isEqualTo(Document.parse("{ $project : { newDate: { $dateFromString: { dateString : \"$date\" } } } }")); - } - - @Test // DATAMONGO-1834 - public void shouldRenderDateFromStringWithTimezone() { - - Document agg = project() - .and(DateOperators.dateFromString("2017-02-08T12:10:40.787").withTimezone(Timezone.valueOf("America/Chicago"))) - .as("newDate").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { newDate: { $dateFromString: { dateString : \"2017-02-08T12:10:40.787\", timezone : \"America/Chicago\" } } } }")); - } - - @Test // DATAMONGO-2047 - public void shouldRenderDateFromStringWithFormat() { - - Document agg = project().and(DateOperators.dateFromString("2017-02-08T12:10:40.787").withFormat("dd/mm/yyyy")) - .as("newDate").toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(agg).isEqualTo(Document.parse( - "{ $project : { newDate: { $dateFromString: { dateString : \"2017-02-08T12:10:40.787\", format : \"dd/mm/yyyy\" } } } }")); - } - - @Test // DATAMONGO-2200 - public void typeProjectionShouldIncludeTopLevelFieldsOfType() { - - ProjectionOperation operation = Aggregation.project(Book.class); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - assertThat(projectClause) // - .hasSize(2) // - .containsEntry("title", 1) // - .containsEntry("author", 1); - } - - @Test // DATAMONGO-2200 - public void typeProjectionShouldMapFieldNames() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - MongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); - - Document document = Aggregation.project(BookRenamed.class) - .toDocument(new TypeBasedAggregationOperationContext(Book.class, mappingContext, new QueryMapper(converter))); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - assertThat(projectClause) // - .hasSize(2) // - .containsEntry("ti_tl_e", 1) // - .containsEntry("author", 1); - } - - @Test // DATAMONGO-2200 - public void typeProjectionShouldIncludeInterfaceProjectionValues() { - - ProjectionOperation operation = Aggregation.project(ProjectionInterface.class); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - assertThat(projectClause) // - .hasSize(1) // - .containsEntry("title", 1); - } - - @Test // DATAMONGO-2200 - public void typeProjectionShouldBeEmptyIfNoPropertiesFound() { - - ProjectionOperation operation = Aggregation.project(EmptyType.class); - - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT); - - assertThat(projectClause).isEmpty(); - } - - @Test // DATAMONGO-2312 - public void simpleFieldReferenceAsArray() { - - org.bson.Document doc = Aggregation.newAggregation(project("x", "y", "someField").asArray("myArray")) - .toDocument("coll", Aggregation.DEFAULT_CONTEXT); - - assertThat(doc).isEqualTo(Document.parse( - "{\"aggregate\":\"coll\", \"pipeline\":[ { $project: { myArray: [ \"$x\", \"$y\", \"$someField\" ] } } ] }")); - } - - @Test // DATAMONGO-2312 - public void mappedFieldReferenceAsArray() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - - org.bson.Document doc = Aggregation - .newAggregation(BookWithFieldAnnotation.class, project("title", "author").asArray("myArray")) - .toDocument("coll", new TypeBasedAggregationOperationContext(BookWithFieldAnnotation.class, mappingContext, - new QueryMapper(new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext)))); - - assertThat(doc).isEqualTo(Document - .parse("{\"aggregate\":\"coll\", \"pipeline\":[ { $project: { myArray: [ \"$ti_t_le\", \"$author\" ] } } ] }")); - } - - @Test // DATAMONGO-2312 - public void arrayWithNullValue() { - - Document doc = project() // - .andArrayOf(Fields.field("field-1"), null, "value").as("myArray") // - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(doc).isEqualTo(Document.parse("{ $project: { \"myArray\" : [ \"$field-1\", null, \"value\" ] } }")); - } - - @Test // DATAMONGO-2312 - public void nestedArrayField() { - - Document doc = project("_id", "value") // - .andArrayOf(Fields.field("field-1"), "plain - string", ArithmeticOperators.valueOf("field-1").sum().and(10)) - .as("myArray") // - .toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(doc).isEqualTo(Document.parse( - "{ $project: { \"_id\" : 1, \"value\" : 1, \"myArray\" : [ \"$field-1\", \"plain - string\", { \"$sum\" : [\"$field-1\", 10] } ] } } ] }")); - } - - @Test // DATAMONGO-2312 - public void nestedMappedFieldReferenceInArrayField() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - - Document doc = project("author") // - .andArrayOf(Fields.field("title"), "plain - string", ArithmeticOperators.valueOf("title").sum().and(10)) - .as("myArray") // - .toDocument(new TypeBasedAggregationOperationContext(BookWithFieldAnnotation.class, mappingContext, - new QueryMapper(new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext)))); - - assertThat(doc).isEqualTo(Document.parse( - "{ $project: { \"author\" : 1, \"myArray\" : [ \"$ti_t_le\", \"plain - string\", { \"$sum\" : [\"$ti_t_le\", 10] } ] } } ] }")); - } - - private static Document extractOperation(String field, Document fromProjectClause) { - return (Document) fromProjectClause.get(field); - } - - @Data - static class Book { - String title; - Author author; - } - - @Data - static class BookWithFieldAnnotation { - - @Field("ti_t_le") String title; - Author author; - } - - @Data - static class BookRenamed { - @Field("ti_tl_e") String title; - Author author; - } - - @Data - static class Author { - String first; - String last; - String middle; - } - - interface ProjectionInterface { - String getTitle(); - } - - static class EmptyType { - - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReactiveAggregationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReactiveAggregationTests.java deleted file mode 100644 index b6691626d2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReactiveAggregationTests.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2017-2021 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.aggregation; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import reactor.test.StepVerifier; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.geo.Box; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.TestEntities; -import org.springframework.data.mongodb.core.Venue; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration test for aggregation via {@link org.springframework.data.mongodb.core.ReactiveMongoTemplate}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:reactive-infrastructure.xml") -public class ReactiveAggregationTests { - - private static final String INPUT_COLLECTION = "aggregation_test_collection"; - private static final String OUTPUT_COLLECTION = "aggregation_test_out"; - - @Autowired ReactiveMongoTemplate reactiveMongoTemplate; - - @Before - public void setUp() { - cleanDb(); - } - - @After - public void cleanUp() { - cleanDb(); - } - - private void cleanDb() { - - reactiveMongoTemplate.dropCollection(INPUT_COLLECTION) // - .then(reactiveMongoTemplate.dropCollection(OUTPUT_COLLECTION)) // - .then(reactiveMongoTemplate.dropCollection(Product.class)) // - .then(reactiveMongoTemplate.dropCollection(City.class)) // - .then(reactiveMongoTemplate.dropCollection(Venue.class)).as(StepVerifier::create) // - .verifyComplete(); - } - - @Test // DATAMONGO-1646 - public void expressionsInProjectionExampleShowcase() { - - Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); - reactiveMongoTemplate.insert(product).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - double shippingCosts = 1.2; - - TypedAggregation agg = newAggregation(Product.class, // - project("name", "netPrice") // - .andExpression("netPrice * 10", shippingCosts).as("salesPrice") // - ); - - reactiveMongoTemplate.aggregate(agg, Document.class).as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual).containsEntry("_id", product.id); - assertThat(actual).containsEntry("name", product.name); - assertThat(actual).containsEntry("salesPrice", product.netPrice * 10); - }).verifyComplete(); - } - - @Test // DATAMONGO-1646 - public void shouldProjectMultipleDocuments() { - - City dresden = new City("Dresden", 100); - City linz = new City("Linz", 101); - City braunschweig = new City("Braunschweig", 102); - City weinheim = new City("Weinheim", 103); - - reactiveMongoTemplate.insertAll(Arrays.asList(dresden, linz, braunschweig, weinheim)).as(StepVerifier::create) - .expectNextCount(4).verifyComplete(); - - Aggregation agg = newAggregation( // - match(where("population").lt(103))); - - reactiveMongoTemplate.aggregate(agg, "city", City.class).collectList().as(StepVerifier::create) - .consumeNextWith(actual -> { - assertThat(actual).hasSize(3).contains(dresden, linz, braunschweig); - }).verifyComplete(); - } - - @Test // DATAMONGO-1646 - public void shouldAggregateToOutCollection() { - - City dresden = new City("Dresden", 100); - City linz = new City("Linz", 101); - City braunschweig = new City("Braunschweig", 102); - City weinheim = new City("Weinheim", 103); - - reactiveMongoTemplate.insertAll(Arrays.asList(dresden, linz, braunschweig, weinheim)).as(StepVerifier::create) - .expectNextCount(4).verifyComplete(); - - Aggregation agg = newAggregation( // - out(OUTPUT_COLLECTION)); - - reactiveMongoTemplate.aggregate(agg, "city", City.class).as(StepVerifier::create).expectNextCount(4) - .verifyComplete(); - reactiveMongoTemplate.find(new Query(), City.class, OUTPUT_COLLECTION).as(StepVerifier::create).expectNextCount(4) - .verifyComplete(); - } - - @Test // DATAMONGO-1986 - public void runMatchOperationCriteriaThroughQueryMapperForTypedAggregation() { - - reactiveMongoTemplate.insertAll(TestEntities.geolocation().newYork()).as(StepVerifier::create).expectNextCount(12) - .verifyComplete(); - - Aggregation aggregation = newAggregation(Venue.class, - match(Criteria.where("location") - .within(new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)))), - project("id", "location", "name")); - - reactiveMongoTemplate.aggregate(aggregation, "newyork", Document.class).as(StepVerifier::create).expectNextCount(4) - .verifyComplete(); - } - - @Test // DATAMONGO-1986 - public void runMatchOperationCriteriaThroughQueryMapperForUntypedAggregation() { - - reactiveMongoTemplate.insertAll(TestEntities.geolocation().newYork()).as(StepVerifier::create).expectNextCount(12) - .verifyComplete(); - - Aggregation aggregation = newAggregation( - match(Criteria.where("location") - .within(new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)))), - project("id", "location", "name")); - - reactiveMongoTemplate.aggregate(aggregation, "newyork", Document.class).as(StepVerifier::create).expectNextCount(4) - .verifyComplete(); - } - - @Test // DATAMONGO-2356 - public void skipOutputDoesNotReadBackAggregationResults() { - - Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); - reactiveMongoTemplate.insert(product).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - double shippingCosts = 1.2; - - TypedAggregation agg = newAggregation(Product.class, // - project("name", "netPrice") // - .andExpression("netPrice * 10", shippingCosts).as("salesPrice") // - ).withOptions(AggregationOptions.builder().skipOutput().build()); - - reactiveMongoTemplate.aggregate(agg, Document.class).as(StepVerifier::create).verifyComplete(); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReactiveAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReactiveAggregationUnitTests.java deleted file mode 100644 index 609591273b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReactiveAggregationUnitTests.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2017-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyList; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.query.Collation; - -import com.mongodb.reactivestreams.client.AggregatePublisher; -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class ReactiveAggregationUnitTests { - - private static final String INPUT_COLLECTION = "collection-1"; - - private ReactiveMongoTemplate template; - private ReactiveMongoDatabaseFactory factory; - @Mock MongoClient mongoClient; - @Mock MongoDatabase db; - @Mock MongoCollection collection; - @Mock AggregatePublisher publisher; - - @BeforeEach - void setUp() { - - factory = new SimpleReactiveMongoDatabaseFactory(mongoClient, "db"); - template = new ReactiveMongoTemplate(factory); - - when(mongoClient.getDatabase("db")).thenReturn(db); - when(db.getCollection(eq(INPUT_COLLECTION), any(Class.class))).thenReturn(collection); - when(collection.aggregate(anyList(), any(Class.class))).thenReturn(publisher); - when(publisher.allowDiskUse(any())).thenReturn(publisher); - when(publisher.collation(any())).thenReturn(publisher); - } - - @Test // DATAMONGO-1646 - void shouldHandleMissingInputCollection() { - assertThatIllegalArgumentException() - .isThrownBy(() -> template.aggregate(newAggregation(), (String) null, TagCount.class)); - } - - @Test // DATAMONGO-1646 - void shouldHandleMissingAggregationPipeline() { - assertThatIllegalArgumentException().isThrownBy(() -> template.aggregate(null, INPUT_COLLECTION, TagCount.class)); - } - - @Test // DATAMONGO-1646 - void shouldHandleMissingEntityClass() { - assertThatIllegalArgumentException().isThrownBy(() -> template.aggregate(newAggregation(), INPUT_COLLECTION, null)); - } - - @Test // DATAMONGO-1646 - void errorsOnExplainUsage() { - assertThatIllegalArgumentException().isThrownBy(() -> template.aggregate(newAggregation(Product.class, // - project("name", "netPrice")) // - .withOptions(AggregationOptions.builder().explain(true).build()), - INPUT_COLLECTION, TagCount.class).subscribe()); - } - - @Test // DATAMONGO-1646, DATAMONGO-1311 - void appliesBatchSizeWhenPresent() { - - when(publisher.batchSize(anyInt())).thenReturn(publisher); - - AggregationOptions options = AggregationOptions.builder().cursorBatchSize(1234).build(); - template.aggregate(newAggregation(Product.class, // - project("name", "netPrice")) // - .withOptions(options), - INPUT_COLLECTION, TagCount.class).subscribe(); - - verify(publisher).batchSize(1234); - } - - @Test // DATAMONGO-1646 - void appliesCollationCorrectlyWhenPresent() { - - template.aggregate(newAggregation(Product.class, // - project("name", "netPrice")) // - .withOptions(AggregationOptions.builder().collation(Collation.of("en_US")).build()), - INPUT_COLLECTION, TagCount.class).subscribe(); - - verify(publisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("en_US").build())); - } - - @Test // DATAMONGO-1646 - void doesNotSetCollationWhenNotPresent() { - - template.aggregate(newAggregation(Product.class, // - project("name", "netPrice")) // - .withOptions(AggregationOptions.builder().build()), - INPUT_COLLECTION, TagCount.class).subscribe(); - - verify(publisher, never()).collation(any()); - } - - @Test // DATAMONGO-1646 - void appliesDiskUsageCorrectly() { - - template.aggregate(newAggregation(Product.class, // - project("name", "netPrice")) // - .withOptions(AggregationOptions.builder().allowDiskUse(true).build()), - INPUT_COLLECTION, TagCount.class).subscribe(); - - verify(publisher).allowDiskUse(eq(true)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java deleted file mode 100644 index 5bee57709a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2020-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import lombok.Data; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.lang.Nullable; - -/** - * Unit tests for {@link RedactOperation}. - * - * @author Christoph Strobl - */ -class RedactOperationUnitTests { - - Document expected = new Document("$redact", - new Document("$cond", new Document("if", new Document("$eq", Arrays.asList("$level", 5))) - .append("then", "$$PRUNE").append("else", "$$DESCEND"))); - Document expectedMapped = new Document("$redact", - new Document("$cond", new Document("if", new Document("$eq", Arrays.asList("$le_v_el", 5))) - .append("then", "$$PRUNE").append("else", "$$DESCEND"))); - - @Test // DATAMONGO-931 - void errorsOnNullExpression() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new RedactOperation(null)); - } - - @Test // DATAMONGO-931 - void mapsAggregationExpressionCorrectly() { - - assertThat(new RedactOperation(ConditionalOperators.when(Criteria.where("level").is(5)) // - .then(RedactOperation.PRUNE) // - .otherwise(RedactOperation.DESCEND)).toDocument(contextFor(null))).isEqualTo(expected); - } - - @Test // DATAMONGO-931 - void mapsAggregationExpressionViaBuilderCorrectly() { - - assertThat(RedactOperation.builder().when(Criteria.where("level").is(5)) // - .thenPrune() // - .otherwiseDescend().build().toDocument(contextFor(null))).isEqualTo(expected); - } - - @Test // DATAMONGO-931 - void mapsTypedAggregationExpressionCorrectly() { - - assertThat(new RedactOperation(ConditionalOperators.when(Criteria.where("level").is(5)) // - .then(RedactOperation.PRUNE) // - .otherwise(RedactOperation.DESCEND)).toDocument(contextFor(DomainType.class))).isEqualTo(expectedMapped); - } - - @Data - static class DomainType { - - @Field("le_v_el") String level; - } - - private static AggregationOperationContext contextFor(@Nullable Class type) { - - if (type == null) { - return Aggregation.DEFAULT_CONTEXT; - } - - MappingMongoConverter mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, - new MongoMappingContext()); - mongoConverter.afterPropertiesSet(); - - return new TypeBasedAggregationOperationContext(type, mongoConverter.getMappingContext(), - new QueryMapper(mongoConverter)).continueOnMissingFieldReference(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java deleted file mode 100644 index e97e1ff018..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootDocumentOperation; - -/** - * Unit tests for {@link ReplaceRootOperation}. - * - * @author Mark Paluch - */ -public class ReplaceRootOperationUnitTests { - - @Test // DATAMONGO-1550 - public void rejectsNullField() { - assertThatIllegalArgumentException().isThrownBy(() -> new ReplaceRootOperation((Field) null)); - } - - @Test // DATAMONGO-1550 - public void rejectsNullExpression() { - assertThatIllegalArgumentException().isThrownBy(() -> new ReplaceRootOperation((AggregationExpression) null)); - } - - @Test // DATAMONGO-1550 - public void shouldRenderCorrectly() { - - ReplaceRootOperation operation = ReplaceRootDocumentOperation.builder() - .withDocument(new Document("hello", "world")); - Document dbObject = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(dbObject).isEqualTo(Document.parse("{ $replaceRoot : { newRoot: { hello: \"world\" } } }")); - } - - @Test // DATAMONGO-1550 - public void shouldRenderExpressionCorrectly() { - - ReplaceRootOperation operation = new ReplaceRootOperation(VariableOperators // - .mapItemsOf("array") // - .as("element") // - .andApply(AggregationFunctionExpressions.MULTIPLY.of("$$element", 10))); - - Document dbObject = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(dbObject).isEqualTo(Document.parse("{ $replaceRoot : { newRoot : { " - + "$map : { input : \"$array\" , as : \"element\" , in : { $multiply : [ \"$$element\" , 10]} } " + "} } }")); - } - - @Test // DATAMONGO-1550 - public void shouldComposeDocument() { - - ReplaceRootOperation operation = ReplaceRootDocumentOperation.builder().withDocument() // - .andValue("value").as("key") // - .and(AggregationFunctionExpressions.MULTIPLY.of("$$element", 10)).as("multiply"); - - Document dbObject = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(dbObject).isEqualTo(Document - .parse("{ $replaceRoot : { newRoot: { key: \"value\", multiply: { $multiply : [ \"$$element\" , 10]} } } }")); - } - - @Test // DATAMONGO-1550 - public void shouldComposeSubDocument() { - - Document partialReplacement = new Document("key", "override").append("key2", "value2"); - - ReplaceRootOperation operation = ReplaceRootDocumentOperation.builder().withDocument() // - .andValue("value").as("key") // - .andValuesOf(partialReplacement); - - Document dbObject = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(dbObject) - .isEqualTo(Document.parse("{ $replaceRoot : { newRoot: { key: \"override\", key2: \"value2\"} } } }")); - } - - @Test // DATAMONGO-1550 - public void shouldNotExposeFields() { - - ReplaceRootOperation operation = new ReplaceRootOperation(Fields.field("field")); - - assertThat(operation.getFields().exposesNoFields()).isTrue(); - assertThat(operation.getFields().exposesSingleFieldOnly()).isFalse(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceWithOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceWithOperationUnitTests.java deleted file mode 100644 index 8f8b5c9dd1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceWithOperationUnitTests.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2019-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link ReplaceRootOperation}. - * - * @author Christoph Strobl - */ -public class ReplaceWithOperationUnitTests { - - @Test // DATAMONGO-2331 - public void rejectsNullField() { - assertThatIllegalArgumentException().isThrownBy(() -> new ReplaceWithOperation(null)); - } - - @Test // DATAMONGO-2331 - public void shouldRenderValueCorrectly() { - - ReplaceWithOperation operation = ReplaceWithOperation.replaceWithValue(new Document("hello", "world")); - Document dbObject = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(dbObject).isEqualTo(Document.parse("{ $replaceWith : { hello: \"world\" } }")); - } - - @Test // DATAMONGO-2331 - public void shouldRenderExpressionCorrectly() { - - ReplaceWithOperation operation = ReplaceWithOperation.replaceWithValueOf(VariableOperators // - .mapItemsOf("array") // - .as("element") // - .andApply(AggregationFunctionExpressions.MULTIPLY.of("$$element", 10))); - - Document dbObject = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(dbObject).isEqualTo(Document.parse("{ $replaceWith : { " - + "$map : { input : \"$array\" , as : \"element\" , in : { $multiply : [ \"$$element\" , 10]} } " + "} }")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SampleOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SampleOperationUnitTests.java deleted file mode 100644 index 5a98aed7f6..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SampleOperationUnitTests.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link SampleOperation}. - * - * @author Gustavo de Geus - */ -public class SampleOperationUnitTests { - - private static final String SIZE = "size"; - private static final String OP = "$sample"; - - @Test // DATAMONGO-1325 - public void rejectsNegativeSample() { - assertThatIllegalArgumentException().isThrownBy(() -> new SampleOperation(-1L)); - } - - @Test // DATAMONGO-1325 - public void rejectsZeroSample() { - assertThatIllegalArgumentException().isThrownBy(() -> new SampleOperation(0L)); - } - - @Test // DATAMONGO-1325 - public void rendersSampleOperation() { - - long sampleSize = 5L; - - SampleOperation sampleOperation = Aggregation.sample(sampleSize); - - Document sampleOperationDocument = sampleOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(sampleOperationDocument.get(OP)).isNotNull(); - assertThat(sampleOperationDocument.get(OP)).isInstanceOf(Document.class); - - Document sampleSizeDocument = sampleOperationDocument.get(OP, Document.class); - assertThat(sampleSizeDocument.get(SIZE)).isEqualTo(sampleSize); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ScriptOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ScriptOperatorsUnitTests.java deleted file mode 100644 index ed88902422..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ScriptOperatorsUnitTests.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2020-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.ScriptOperators.*; - -import java.util.Collections; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link ScriptOperators}. - * - * @author Christoph Strobl - */ -class ScriptOperatorsUnitTests { - - private static final String FUNCTION_BODY = "function(name) { return hex_md5(name) == \"15b0a220baa16331e8d80e15367677ad\" }"; - private static final Document EMPTY_ARGS_FUNCTION_DOCUMENT = new Document("body", FUNCTION_BODY) - .append("args", Collections.emptyList()).append("lang", "js"); - - private static final String INIT_FUNCTION = "function() { return { count: 0, sum: 0 } }"; - private static final String ACC_FUNCTION = "function(state, numCopies) { return { count: state.count + 1, sum: state.sum + numCopies } }"; - private static final String MERGE_FUNCTION = "function(state1, state2) { return { count: state1.count + state2.count, sum: state1.sum + state2.sum } }"; - private static final String FINALIZE_FUNCTION = "function(state) { return (state.sum / state.count) }"; - - private static final Document $ACCUMULATOR = Document.parse("{" + // - " $accumulator:" + // - " {" + // - " init: '" + INIT_FUNCTION + "'," + // - " accumulate: '" + ACC_FUNCTION + "'," + // - " accumulateArgs: [\"$copies\"]," + // - " merge: '" + MERGE_FUNCTION + "'," + // - " finalize: '" + FINALIZE_FUNCTION + "'," + // - " lang: \"js\"" + // - " }" + // - " }" + // - " }"); - - @Test // DATAMONGO-2623 - void functionWithoutArgsShouldBeRenderedCorrectly() { - - assertThat(function(FUNCTION_BODY).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo($function(EMPTY_ARGS_FUNCTION_DOCUMENT)); - } - - @Test // DATAMONGO-2623 - void functionWithArgsShouldBeRenderedCorrectly() { - - assertThat(function(FUNCTION_BODY).args("$name").toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo( - $function(new Document(EMPTY_ARGS_FUNCTION_DOCUMENT).append("args", Collections.singletonList("$name")))); - } - - @Test // DATAMONGO-2623 - void accumulatorWithStringInput() { - - Accumulator accumulator = accumulatorBuilder() // - .init(INIT_FUNCTION) // - .accumulate(ACC_FUNCTION).accumulateArgs("$copies") // - .merge(MERGE_FUNCTION) // - .finalize(FINALIZE_FUNCTION); - - assertThat(accumulator.toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo($ACCUMULATOR); - } - - @Test // DATAMONGO-2623 - void accumulatorWithFunctionInput() { - - Accumulator accumulator = accumulatorBuilder() // - .init(function(INIT_FUNCTION)) // - .accumulate(function(ACC_FUNCTION).args("$copies")) // - .merge(MERGE_FUNCTION) // - .finalize(FINALIZE_FUNCTION); - - assertThat(accumulator.toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo($ACCUMULATOR); - } - - static Document $function(Document source) { - return new Document("$function", source); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java deleted file mode 100644 index b90b049da1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2019-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; - -/** - * Unit tests for {@link SetOperation}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -class SetOperationUnitTests { - - @Test // DATAMONGO-2331 - void raisesErrorOnNullField() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new SetOperation(null, "value")); - } - - @Test // DATAMONGO-2331 - void rendersFieldReferenceCorrectly() { - - assertThat(new SetOperation("name", "value").toPipelineStages(contextFor(Scores.class))) - .containsExactly(Document.parse("{\"$set\" : {\"name\":\"value\"}}")); - } - - @Test // DATAMONGO-2331 - void rendersMappedFieldReferenceCorrectly() { - - assertThat(new SetOperation("student", "value").toPipelineStages(contextFor(ScoresWithMappedField.class))) - .containsExactly(Document.parse("{\"$set\" : {\"student_name\":\"value\"}}")); - } - - @Test // DATAMONGO-2331 - void rendersNestedMappedFieldReferenceCorrectly() { - - assertThat( - new SetOperation("scoresWithMappedField.student", "value").toPipelineStages(contextFor(ScoresWrapper.class))) - .containsExactly(Document.parse("{\"$set\" : {\"scoresWithMappedField.student_name\":\"value\"}}")); - } - - @Test // DATAMONGO-2331 - void rendersTargetValueFieldReferenceCorrectly() { - - assertThat(new SetOperation("name", Fields.field("value")).toPipelineStages(contextFor(Scores.class))) - .containsExactly(Document.parse("{\"$set\" : {\"name\":\"$value\"}}")); - } - - @Test // DATAMONGO-2331 - void rendersMappedTargetValueFieldReferenceCorrectly() { - - assertThat( - new SetOperation("student", Fields.field("homework")).toPipelineStages(contextFor(ScoresWithMappedField.class))) - .containsExactly(Document.parse("{\"$set\" : {\"student_name\":\"$home_work\"}}")); - } - - @Test // DATAMONGO-2331 - void rendersNestedMappedTargetValueFieldReferenceCorrectly() { - - assertThat(new SetOperation("scoresWithMappedField.student", Fields.field("scoresWithMappedField.homework")) - .toPipelineStages(contextFor(ScoresWrapper.class))) - .containsExactly(Document - .parse("{\"$set\" : {\"scoresWithMappedField.student_name\":\"$scoresWithMappedField.home_work\"}}")); - } - - @Test // DATAMONGO-2363 - void appliesSpelExpressionCorrectly() { - - SetOperation operation = SetOperation.builder().set("totalHomework").withValueOfExpression("sum(homework) * [0]", - 2); - - assertThat(operation.toPipelineStages(contextFor(AddFieldsOperationUnitTests.ScoresWrapper.class))).contains( - Document.parse("{\"$set\" : {\"totalHomework\": { $multiply : [{ \"$sum\" : [\"$homework\"] }, 2] }}}")); - } - - @Test // DATAMONGO-2331 - void rendersTargetValueExpressionCorrectly() { - - assertThat(SetOperation.builder().set("totalHomework").toValueOf(ArithmeticOperators.valueOf("homework").sum()) - .toPipelineStages(contextFor(Scores.class))) - .containsExactly(Document.parse("{\"$set\" : {\"totalHomework\": { \"$sum\" : \"$homework\" }}}")); - } - - @Test // DATAMONGO-2331 - void exposesFieldsCorrectly() { - - ExposedFields fields = SetOperation.builder().set("totalHomework").toValue("A+") // - .and() // - .set("totalQuiz").toValue("B-") // - .getFields(); - - assertThat(fields.getField("totalHomework")).isNotNull(); - assertThat(fields.getField("totalQuiz")).isNotNull(); - assertThat(fields.getField("does-not-exist")).isNull(); - } - - private static AggregationOperationContext contextFor(@Nullable Class type) { - - if (type == null) { - return Aggregation.DEFAULT_CONTEXT; - } - - MappingMongoConverter mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, - new MongoMappingContext()); - mongoConverter.afterPropertiesSet(); - - return new RelaxedTypeBasedAggregationOperationContext(type, mongoConverter.getMappingContext(), - new QueryMapper(mongoConverter)); - } - - static class Scores { - - String student; - List homework; - } - - static class ScoresWithMappedField { - - @Field("student_name") String student; - @Field("home_work") List homework; - } - - static class ScoresWrapper { - - Scores scores; - ScoresWithMappedField scoresWithMappedField; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SkipOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SkipOperationUnitTests.java deleted file mode 100644 index 20ba21aac8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SkipOperationUnitTests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link SkipOperation}. - * - * @author Oliver Gierke - */ -public class SkipOperationUnitTests { - - static final String OP = "$skip"; - - @Test - public void rejectsNegativeSkip() { - assertThatIllegalArgumentException().isThrownBy(() -> new SkipOperation(-1L)); - } - - @Test - public void rendersSkipOperation() { - - SkipOperation operation = new SkipOperation(10L); - Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(document.get(OP)).isEqualTo((Object) 10L); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperationUnitTests.java deleted file mode 100644 index f9d39d98a8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperationUnitTests.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2018-2021 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.aggregation; - -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link SortByCountOperation}. - * - * @author Mark Paluch - */ -public class SortByCountOperationUnitTests { - - @Test // DATAMONGO-1553 - public void shouldRenderFieldCorrectly() { - - SortByCountOperation operation = sortByCount("country"); - Document result = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(result).containsEntry("$sortByCount", "$country"); - } - - @Test // DATAMONGO-1553 - public void shouldRenderExpressionCorrectly() { - - SortByCountOperation operation = sortByCount(StringOperators.valueOf("foo").substring(5)); - Document result = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(result).containsEntry("$sortByCount.$substr", Arrays.asList("$foo", 5, -1)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SortOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SortOperationUnitTests.java deleted file mode 100644 index 1986c5c82a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SortOperationUnitTests.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.DocumentTestUtils.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; - -/** - * Unit tests for {@link SortOperation}. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -public class SortOperationUnitTests { - - @Test - public void createsDocumentForAscendingSortCorrectly() { - - SortOperation operation = new SortOperation(Sort.by(Direction.ASC, "foobar")); - Document result = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document sortValue = getAsDocument(result, "$sort"); - assertThat(sortValue).isNotNull(); - assertThat(sortValue.get("foobar")).isEqualTo((Object) 1); - } - - @Test - public void createsDocumentForDescendingSortCorrectly() { - - SortOperation operation = new SortOperation(Sort.by(Direction.DESC, "foobar")); - Document result = operation.toDocument(Aggregation.DEFAULT_CONTEXT); - - Document sortValue = getAsDocument(result, "$sort"); - assertThat(sortValue).isNotNull(); - assertThat(sortValue.get("foobar")).isEqualTo((Object) (0 - 1)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerIntegrationTests.java deleted file mode 100644 index f00a4bd4c9..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerIntegrationTests.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mapping.context.InvalidPersistentPropertyPath; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration tests for {@link SpelExpressionTransformer}. - * - * @author Thomas Darimont - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:infrastructure.xml") -public class SpelExpressionTransformerIntegrationTests { - - @Autowired MongoDatabaseFactory mongoDbFactory; - - SpelExpressionTransformer transformer; - DbRefResolver dbRefResolver; - - @Before - public void setUp() { - this.transformer = new SpelExpressionTransformer(); - this.dbRefResolver = new DefaultDbRefResolver(mongoDbFactory); - } - - @Test // DATAMONGO-774 - public void shouldConvertCompoundExpressionToPropertyPath() { - - MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext()); - TypeBasedAggregationOperationContext ctxt = new TypeBasedAggregationOperationContext(Data.class, - new MongoMappingContext(), new QueryMapper(converter)); - assertThat(transformer.transform("item.primitiveIntValue", ctxt, new Object[0]).toString()) - .isEqualTo("$item.primitiveIntValue"); - } - - @Test // DATAMONGO-774 - public void shouldThrowExceptionIfNestedPropertyCannotBeFound() { - - MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext()); - TypeBasedAggregationOperationContext ctxt = new TypeBasedAggregationOperationContext(Data.class, - new MongoMappingContext(), new QueryMapper(converter)); - - assertThatExceptionOfType(InvalidPersistentPropertyPath.class).isThrownBy(() -> { - transformer.transform("item.value2", ctxt, new Object[0]).toString(); - }); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java deleted file mode 100644 index b67beed126..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java +++ /dev/null @@ -1,953 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.Person; - -/** - * Unit tests for {@link SpelExpressionTransformer}. - * - * @author Thomas Darimont - * @author Oliver Gierke - * @author Christoph Strobl - */ -public class SpelExpressionTransformerUnitTests { - - SpelExpressionTransformer transformer = new SpelExpressionTransformer(); - - Data data; - - @BeforeEach - public void beforeEach() { - - this.data = new Data(); - this.data.primitiveLongValue = 42; - this.data.primitiveDoubleValue = 1.2345; - this.data.doubleValue = 23.0; - this.data.item = new DataItem(); - this.data.item.primitiveIntValue = 21; - } - - @Test // DATAMONGO-774 - public void shouldRenderConstantExpression() { - - assertThat(transform("1")).isEqualTo((Object) "1"); - assertThat(transform("-1")).isEqualTo((Object) "-1"); - assertThat(transform("1.0")).isEqualTo((Object) "1.0"); - assertThat(transform("-1.0")).isEqualTo((Object) "-1.0"); - assertThat(transform("null")).isNull(); - } - - @Test // DATAMONGO-774 - public void shouldSupportKnownOperands() { - - assertThat(transform("a + b")).isEqualTo((Object) Document.parse("{ \"$add\" : [ \"$a\" , \"$b\"]}")); - assertThat(transform("a - b")).isEqualTo((Object) Document.parse("{ \"$subtract\" : [ \"$a\" , \"$b\"]}")); - assertThat(transform("a * b")).isEqualTo((Object) Document.parse("{ \"$multiply\" : [ \"$a\" , \"$b\"]}")); - assertThat(transform("a / b")).isEqualTo((Object) Document.parse("{ \"$divide\" : [ \"$a\" , \"$b\"]}")); - assertThat(transform("a % b")).isEqualTo((Object) Document.parse("{ \"$mod\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-774 - public void shouldThrowExceptionOnUnknownOperand() { - assertThatIllegalArgumentException().isThrownBy(() -> transform("a++")); - } - - @Test // DATAMONGO-774 - public void shouldRenderSumExpression() { - assertThat(transform("a + 1")).isEqualTo((Object) Document.parse("{ \"$add\" : [ \"$a\" , 1]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderFormula() { - - assertThat(transform("(netPrice + surCharge) * taxrate + 42")).isEqualTo((Object) Document.parse( - "{ \"$add\" : [ { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\"]} , 42]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderFormulaInCurlyBrackets() { - - assertThat(transform("{(netPrice + surCharge) * taxrate + 42}")).isEqualTo((Object) Document.parse( - "{ \"$add\" : [ { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\"]} , 42]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderFieldReference() { - - assertThat(transform("foo")).isEqualTo((Object) "$foo"); - assertThat(transform("$foo")).isEqualTo((Object) "$foo"); - } - - @Test // DATAMONGO-774 - public void shouldRenderNestedFieldReference() { - - assertThat(transform("foo.bar")).isEqualTo((Object) "$foo.bar"); - assertThat(transform("$foo.bar")).isEqualTo((Object) "$foo.bar"); - } - - @Test // DATAMONGO-774 - @Disabled - public void shouldRenderNestedIndexedFieldReference() { - - // TODO add support for rendering nested indexed field references - assertThat(transform("foo[3].bar")).isEqualTo((Object) "$foo[3].bar"); - } - - @Test // DATAMONGO-774 - public void shouldRenderConsecutiveOperation() { - assertThat(transform("1 + 1 + 1")).isEqualTo((Object) Document.parse("{ \"$add\" : [ 1 , 1 , 1]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderComplexExpression0() { - - assertThat(transform("-(1 + q)")) - .isEqualTo((Object) Document.parse("{ \"$multiply\" : [ -1 , { \"$add\" : [ 1 , \"$q\"]}]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderComplexExpression1() { - - assertThat(transform("1 + (q + 1) / (q - 1)")).isEqualTo((Object) Document.parse( - "{ \"$add\" : [ 1 , { \"$divide\" : [ { \"$add\" : [ \"$q\" , 1]} , { \"$subtract\" : [ \"$q\" , 1]}]}]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderComplexExpression2() { - - assertThat(transform("(q + 1 + 4 - 5) / (q + 1 + 3 + 4)")).isEqualTo((Object) Document.parse( - "{ \"$divide\" : [ { \"$subtract\" : [ { \"$add\" : [ \"$q\" , 1 , 4]} , 5]} , { \"$add\" : [ \"$q\" , 1 , 3 , 4]}]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderBinaryExpressionWithMixedSignsCorrectly() { - - assertThat(transform("-4 + 1")).isEqualTo((Object) Document.parse("{ \"$add\" : [ -4 , 1]}")); - assertThat(transform("1 + -4")).isEqualTo((Object) Document.parse("{ \"$add\" : [ 1 , -4]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderConsecutiveOperationsInComplexExpression() { - - assertThat(transform("1 + 1 + (1 + 1 + 1) / q")).isEqualTo( - (Object) Document.parse("{ \"$add\" : [ 1 , 1 , { \"$divide\" : [ { \"$add\" : [ 1 , 1 , 1]} , \"$q\"]}]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderParameterExpressionResults() { - assertThat(transform("[0] + [1] + [2]", 1, 2, 3)).isEqualTo((Object) Document.parse("{ \"$add\" : [ 1 , 2 , 3]}")); - } - - @Test // DATAMONGO-774 - public void shouldRenderNestedParameterExpressionResults() { - - assertThat( - ((Document) transform("[0].primitiveLongValue + [0].primitiveDoubleValue + [0].doubleValue.longValue()", data)) - .toJson()) - .isEqualTo(Document - .parse("{ \"$add\" : [ { $numberLong : \"42\"} , 1.2345 , { $numberLong : \"23\" } ]}").toJson()); - } - - @Test // DATAMONGO-774 - public void shouldRenderNestedParameterExpressionResultsInNestedExpressions() { - - Document target = ((Document) transform( - "((1 + [0].primitiveLongValue) + [0].primitiveDoubleValue) * [0].doubleValue.longValue()", data)); - - assertThat( - ((Document) transform("((1 + [0].primitiveLongValue) + [0].primitiveDoubleValue) * [0].doubleValue.longValue()", - data))) - .isEqualTo(new Document("$multiply", - Arrays. asList(new Document("$add", Arrays. asList(1, 42L, 1.2345D)), 23L))); - } - - @Test // DATAMONGO-840 - public void shouldRenderCompoundExpressionsWithIndexerAndFieldReference() { - - Person person = new Person(); - person.setAge(10); - assertThat(transform("[0].age + a.c", person)) - .isEqualTo((Object) Document.parse("{ \"$add\" : [ 10 , \"$a.c\"] }")); - } - - @Test // DATAMONGO-840 - public void shouldRenderCompoundExpressionsWithOnlyFieldReferences() { - - assertThat(transform("a.b + a.c")).isEqualTo((Object) Document.parse("{ \"$add\" : [ \"$a.b\" , \"$a.c\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeAnd() { - assertThat(transform("and(a, b)")).isEqualTo((Object) Document.parse("{ \"$and\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeOr() { - assertThat(transform("or(a, b)")).isEqualTo((Object) Document.parse("{ \"$or\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeNot() { - assertThat(transform("not(a)")).isEqualTo((Object) Document.parse("{ \"$not\" : [ \"$a\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeSetEquals() { - assertThat(transform("setEquals(a, b)")) - .isEqualTo((Object) Document.parse("{ \"$setEquals\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeSetEqualsForArrays() { - assertThat(transform("setEquals(new int[]{1,2,3}, new int[]{4,5,6})")) - .isEqualTo((Object) Document.parse("{ \"$setEquals\" : [ [ 1 , 2 , 3] , [ 4 , 5 , 6]]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeSetEqualsMixedArrays() { - assertThat(transform("setEquals(a, new int[]{4,5,6})")) - .isEqualTo((Object) Document.parse("{ \"$setEquals\" : [ \"$a\" , [ 4 , 5 , 6]]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceSetIntersection() { - assertThat(transform("setIntersection(a, new int[]{4,5,6})")) - .isEqualTo((Object) Document.parse("{ \"$setIntersection\" : [ \"$a\" , [ 4 , 5 , 6]]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceSetUnion() { - assertThat(transform("setUnion(a, new int[]{4,5,6})")) - .isEqualTo((Object) Document.parse("{ \"$setUnion\" : [ \"$a\" , [ 4 , 5 , 6]]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceSeDifference() { - assertThat(transform("setDifference(a, new int[]{4,5,6})")) - .isEqualTo((Object) Document.parse("{ \"$setDifference\" : [ \"$a\" , [ 4 , 5 , 6]]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceSetIsSubset() { - assertThat(transform("setIsSubset(a, new int[]{4,5,6})")) - .isEqualTo((Object) Document.parse("{ \"$setIsSubset\" : [ \"$a\" , [ 4 , 5 , 6]]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceAnyElementTrue() { - assertThat(transform("anyElementTrue(a)")).isEqualTo((Object) Document.parse("{ \"$anyElementTrue\" : [ \"$a\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceAllElementsTrue() { - assertThat(transform("allElementsTrue(a, new int[]{4,5,6})")) - .isEqualTo((Object) Document.parse("{ \"$allElementsTrue\" : [ \"$a\" , [ 4 , 5 , 6]]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceCmp() { - assertThat(transform("cmp(a, 250)")).isEqualTo((Object) Document.parse("{ \"$cmp\" : [ \"$a\" , 250]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceEq() { - assertThat(transform("eq(a, 250)")).isEqualTo((Object) Document.parse("{ \"$eq\" : [ \"$a\" , 250]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceGt() { - assertThat(transform("gt(a, 250)")).isEqualTo((Object) Document.parse("{ \"$gt\" : [ \"$a\" , 250]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceGte() { - assertThat(transform("gte(a, 250)")).isEqualTo((Object) Document.parse("{ \"$gte\" : [ \"$a\" , 250]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceLt() { - assertThat(transform("lt(a, 250)")).isEqualTo((Object) Document.parse("{ \"$lt\" : [ \"$a\" , 250]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceLte() { - assertThat(transform("lte(a, 250)")).isEqualTo((Object) Document.parse("{ \"$lte\" : [ \"$a\" , 250]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNe() { - assertThat(transform("ne(a, 250)")).isEqualTo((Object) Document.parse("{ \"$ne\" : [ \"$a\" , 250]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceAbs() { - assertThat(transform("abs(1)")).isEqualTo((Object) Document.parse("{ \"$abs\" : 1}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceAdd() { - assertThat(transform("add(a, 250)")).isEqualTo((Object) Document.parse("{ \"$add\" : [ \"$a\" , 250]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceCeil() { - assertThat(transform("ceil(7.8)")).isEqualTo((Object) Document.parse("{ \"$ceil\" : 7.8}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceDivide() { - assertThat(transform("divide(a, 250)")).isEqualTo((Object) Document.parse("{ \"$divide\" : [ \"$a\" , 250]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceExp() { - assertThat(transform("exp(2)")).isEqualTo((Object) Document.parse("{ \"$exp\" : 2}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceFloor() { - assertThat(transform("floor(2)")).isEqualTo((Object) Document.parse("{ \"$floor\" : 2}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceLn() { - assertThat(transform("ln(2)")).isEqualTo((Object) Document.parse("{ \"$ln\" : 2}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceLog() { - assertThat(transform("log(100, 10)")).isEqualTo((Object) Document.parse("{ \"$log\" : [ 100 , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceLog10() { - assertThat(transform("log10(100)")).isEqualTo((Object) Document.parse("{ \"$log10\" : 100}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeMod() { - assertThat(transform("mod(a, b)")).isEqualTo((Object) Document.parse("{ \"$mod\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeMultiply() { - assertThat(transform("multiply(a, b)")).isEqualTo((Object) Document.parse("{ \"$multiply\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodePow() { - assertThat(transform("pow(a, 2)")).isEqualTo((Object) Document.parse("{ \"$pow\" : [ \"$a\" , 2]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceSqrt() { - assertThat(transform("sqrt(2)")).isEqualTo((Object) Document.parse("{ \"$sqrt\" : 2}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeSubtract() { - assertThat(transform("subtract(a, b)")).isEqualTo((Object) Document.parse("{ \"$subtract\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceTrunc() { - assertThat(transform("trunc(2.1)")).isEqualTo((Object) Document.parse("{ \"$trunc\" : 2.1}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeConcat() { - assertThat(transform("concat(a, b, 'c')")) - .isEqualTo((Object) Document.parse("{ \"$concat\" : [ \"$a\" , \"$b\" , \"c\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeSubstrc() { - assertThat(transform("substr(a, 0, 1)")).isEqualTo((Object) Document.parse("{ \"$substr\" : [ \"$a\" , 0 , 1]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceToLower() { - assertThat(transform("toLower(a)")).isEqualTo((Object) Document.parse("{ \"$toLower\" : \"$a\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceToUpper() { - assertThat(transform("toUpper(a)")).isEqualTo((Object) Document.parse("{ \"$toUpper\" : \"$a\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeStrCaseCmp() { - assertThat(transform("strcasecmp(a, b)")) - .isEqualTo((Object) Document.parse("{ \"$strcasecmp\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceMeta() { - assertThat(transform("meta('textScore')")).isEqualTo((Object) Document.parse("{ \"$meta\" : \"textScore\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeArrayElemAt() { - assertThat(transform("arrayElemAt(a, 10)")) - .isEqualTo((Object) Document.parse("{ \"$arrayElemAt\" : [ \"$a\" , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeConcatArrays() { - assertThat(transform("concatArrays(a, b, c)")) - .isEqualTo((Object) Document.parse("{ \"$concatArrays\" : [ \"$a\" , \"$b\" , \"$c\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeFilter() { - assertThat(transform("filter(a, 'num', '$$num' > 10)")).isEqualTo((Object) Document.parse( - "{ \"$filter\" : { \"input\" : \"$a\" , \"as\" : \"num\" , \"cond\" : { \"$gt\" : [ \"$$num\" , 10]}}}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceIsArray() { - assertThat(transform("isArray(a)")).isEqualTo((Object) Document.parse("{ \"$isArray\" : \"$a\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceIsSize() { - assertThat(transform("size(a)")).isEqualTo((Object) Document.parse("{ \"$size\" : \"$a\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeSlice() { - assertThat(transform("slice(a, 10)")).isEqualTo((Object) Document.parse("{ \"$slice\" : [ \"$a\" , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeMap() { - assertThat(transform("map(quizzes, 'grade', '$$grade' + 2)")).isEqualTo((Object) Document.parse( - "{ \"$map\" : { \"input\" : \"$quizzes\" , \"as\" : \"grade\" , \"in\" : { \"$add\" : [ \"$$grade\" , 2]}}}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeLet() { - assertThat(transform("let({low:1, high:'$$low'}, gt('$$low', '$$high'))")).isEqualTo((Object) Document.parse( - "{ \"$let\" : { \"vars\" : { \"low\" : 1 , \"high\" : \"$$low\"} , \"in\" : { \"$gt\" : [ \"$$low\" , \"$$high\"]}}}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceLiteral() { - assertThat(transform("literal($1)")).isEqualTo((Object) Document.parse("{ \"$literal\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceDayOfYear() { - assertThat(transform("dayOfYear($1)")).isEqualTo((Object) Document.parse("{ \"$dayOfYear\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceDayOfMonth() { - assertThat(transform("dayOfMonth($1)")).isEqualTo((Object) Document.parse("{ \"$dayOfMonth\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceDayOfWeek() { - assertThat(transform("dayOfWeek($1)")).isEqualTo((Object) Document.parse("{ \"$dayOfWeek\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceYear() { - assertThat(transform("year($1)")).isEqualTo((Object) Document.parse("{ \"$year\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceMonth() { - assertThat(transform("month($1)")).isEqualTo((Object) Document.parse("{ \"$month\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceWeek() { - assertThat(transform("week($1)")).isEqualTo((Object) Document.parse("{ \"$week\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceHour() { - assertThat(transform("hour($1)")).isEqualTo((Object) Document.parse("{ \"$hour\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceMinute() { - assertThat(transform("minute($1)")).isEqualTo((Object) Document.parse("{ \"$minute\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceSecond() { - assertThat(transform("second($1)")).isEqualTo((Object) Document.parse("{ \"$second\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceMillisecond() { - assertThat(transform("millisecond($1)")).isEqualTo((Object) Document.parse("{ \"$millisecond\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceDateToString() { - assertThat(transform("dateToString('%Y-%m-%d', $date)")).isEqualTo( - (Object) Document.parse("{ \"$dateToString\" : { \"format\" : \"%Y-%m-%d\" , \"date\" : \"$date\"}}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceCond() { - assertThat(transform("cond(qty > 250, 30, 20)")).isEqualTo((Object) Document - .parse("{ \"$cond\" : { \"if\" : { \"$gt\" : [ \"$qty\" , 250]} , \"then\" : 30 , \"else\" : 20}}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeIfNull() { - assertThat(transform("ifNull(a, 10)")).isEqualTo((Object) Document.parse("{ \"$ifNull\" : [ \"$a\" , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeSum() { - assertThat(transform("sum(a, b)")).isEqualTo((Object) Document.parse("{ \"$sum\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeAvg() { - assertThat(transform("avg(a, b)")).isEqualTo((Object) Document.parse("{ \"$avg\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceFirst() { - assertThat(transform("first($1)")).isEqualTo((Object) Document.parse("{ \"$first\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceLast() { - assertThat(transform("last($1)")).isEqualTo((Object) Document.parse("{ \"$last\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeMax() { - assertThat(transform("max(a, b)")).isEqualTo((Object) Document.parse("{ \"$max\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeMin() { - assertThat(transform("min(a, b)")).isEqualTo((Object) Document.parse("{ \"$min\" : [ \"$a\" , \"$b\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodePush() { - assertThat(transform("push({'item':'$item', 'quantity':'$qty'})")) - .isEqualTo((Object) Document.parse("{ \"$push\" : { \"item\" : \"$item\" , \"quantity\" : \"$qty\"}}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceAddToSet() { - assertThat(transform("addToSet($1)")).isEqualTo((Object) Document.parse("{ \"$addToSet\" : \"$1\"}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeStdDevPop() { - assertThat(transform("stdDevPop(scores.score)")) - .isEqualTo((Object) Document.parse("{ \"$stdDevPop\" : [ \"$scores.score\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderMethodReferenceNodeStdDevSamp() { - assertThat(transform("stdDevSamp(age)")).isEqualTo((Object) Document.parse("{ \"$stdDevSamp\" : [ \"$age\"]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderOperationNodeEq() { - assertThat(transform("foo == 10")).isEqualTo((Object) Document.parse("{ \"$eq\" : [ \"$foo\" , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderOperationNodeNe() { - assertThat(transform("foo != 10")).isEqualTo((Object) Document.parse("{ \"$ne\" : [ \"$foo\" , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderOperationNodeGt() { - assertThat(transform("foo > 10")).isEqualTo((Object) Document.parse("{ \"$gt\" : [ \"$foo\" , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderOperationNodeGte() { - assertThat(transform("foo >= 10")).isEqualTo((Object) Document.parse("{ \"$gte\" : [ \"$foo\" , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderOperationNodeLt() { - assertThat(transform("foo < 10")).isEqualTo((Object) Document.parse("{ \"$lt\" : [ \"$foo\" , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderOperationNodeLte() { - assertThat(transform("foo <= 10")).isEqualTo((Object) Document.parse("{ \"$lte\" : [ \"$foo\" , 10]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderOperationNodePow() { - assertThat(transform("foo^2")).isEqualTo((Object) Document.parse("{ \"$pow\" : [ \"$foo\" , 2]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderOperationNodeOr() { - assertThat(transform("true || false")).isEqualTo((Object) Document.parse("{ \"$or\" : [ true , false]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderComplexOperationNodeOr() { - assertThat(transform("1+2 || concat(a, b) || true")).isEqualTo( - (Object) Document.parse("{ \"$or\" : [ { \"$add\" : [ 1 , 2]} , { \"$concat\" : [ \"$a\" , \"$b\"]} , true]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderOperationNodeAnd() { - assertThat(transform("true && false")).isEqualTo((Object) Document.parse("{ \"$and\" : [ true , false]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderComplexOperationNodeAnd() { - assertThat(transform("1+2 && concat(a, b) && true")).isEqualTo((Object) Document - .parse("{ \"$and\" : [ { \"$add\" : [ 1 , 2]} , { \"$concat\" : [ \"$a\" , \"$b\"]} , true]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderNotCorrectly() { - assertThat(transform("!true")).isEqualTo((Object) Document.parse("{ \"$not\" : [ true]}")); - } - - @Test // DATAMONGO-1530 - public void shouldRenderComplexNotCorrectly() { - assertThat(transform("!(foo > 10)")) - .isEqualTo((Object) Document.parse("{ \"$not\" : [ { \"$gt\" : [ \"$foo\" , 10]}]}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodReferenceIndexOfBytes() { - assertThat(transform("indexOfBytes(item, 'foo')")) - .isEqualTo(Document.parse("{ \"$indexOfBytes\" : [ \"$item\" , \"foo\"]}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodReferenceIndexOfCP() { - assertThat(transform("indexOfCP(item, 'foo')")) - .isEqualTo(Document.parse("{ \"$indexOfCP\" : [ \"$item\" , \"foo\"]}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodReferenceSplit() { - assertThat(transform("split(item, ',')")).isEqualTo(Document.parse("{ \"$split\" : [ \"$item\" , \",\"]}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodReferenceStrLenBytes() { - assertThat(transform("strLenBytes(item)")).isEqualTo(Document.parse("{ \"$strLenBytes\" : \"$item\"}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodReferenceStrLenCP() { - assertThat(transform("strLenCP(item)")).isEqualTo(Document.parse("{ \"$strLenCP\" : \"$item\"}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodSubstrCP() { - assertThat(transform("substrCP(item, 0, 5)")).isEqualTo(Document.parse("{ \"$substrCP\" : [ \"$item\" , 0 , 5]}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodReferenceReverseArray() { - assertThat(transform("reverseArray(array)")).isEqualTo(Document.parse("{ \"$reverseArray\" : \"$array\"}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodReferenceReduce() { - assertThat(transform("reduce(field, '', {'$concat':{'$$value','$$this'}})")).isEqualTo(Document.parse( - "{ \"$reduce\" : { \"input\" : \"$field\" , \"initialValue\" : \"\" , \"in\" : { \"$concat\" : [ \"$$value\" , \"$$this\"]}}}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodReferenceZip() { - assertThat(transform("zip(new String[]{'$array1', '$array2'})")) - .isEqualTo(Document.parse("{ \"$zip\" : { \"inputs\" : [ \"$array1\" , \"$array2\"]}}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodReferenceZipWithOptionalArgs() { - assertThat(transform("zip(new String[]{'$array1', '$array2'}, true, new int[]{1,2})")).isEqualTo(Document.parse( - "{ \"$zip\" : { \"inputs\" : [ \"$array1\" , \"$array2\"] , \"useLongestLength\" : true , \"defaults\" : [ 1 , 2]}}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodIn() { - assertThat(transform("in('item', array)")).isEqualTo(Document.parse("{ \"$in\" : [ \"item\" , \"$array\"]}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodRefereneIsoDayOfWeek() { - assertThat(transform("isoDayOfWeek(date)")).isEqualTo(Document.parse("{ \"$isoDayOfWeek\" : \"$date\"}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodRefereneIsoWeek() { - assertThat(transform("isoWeek(date)")).isEqualTo(Document.parse("{ \"$isoWeek\" : \"$date\"}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodRefereneIsoWeekYear() { - assertThat(transform("isoWeekYear(date)")).isEqualTo(Document.parse("{ \"$isoWeekYear\" : \"$date\"}")); - } - - @Test // DATAMONGO-1548 - public void shouldRenderMethodRefereneType() { - assertThat(transform("type(a)")).isEqualTo(Document.parse("{ \"$type\" : \"$a\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderArrayToObjectWithFieldReference() { - assertThat(transform("arrayToObject(field)")).isEqualTo(Document.parse("{ \"$arrayToObject\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderArrayToObjectWithArray() { - - assertThat(transform("arrayToObject(new String[]{'key', 'value'})")) - .isEqualTo(Document.parse("{ \"$arrayToObject\" : [\"key\", \"value\"]}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderObjectToArrayWithFieldReference() { - assertThat(transform("objectToArray(field)")).isEqualTo(Document.parse("{ \"$objectToArray\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderMergeObjects() { - - assertThat(transform("mergeObjects(field1, $$ROOT)")) - .isEqualTo(Document.parse("{ \"$mergeObjects\" : [\"$field1\", \"$$ROOT\"]}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderTrimWithoutChars() { - assertThat(transform("trim(field)")).isEqualTo(Document.parse("{ \"$trim\" : {\"input\" : \"$field\"}}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderTrimWithChars() { - - assertThat(transform("trim(field, 'ie')")) - .isEqualTo(Document.parse("{ \"$trim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderTrimWithCharsFromFieldReference() { - - assertThat(transform("trim(field1, field2)")) - .isEqualTo(Document.parse("{ \"$trim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderLtrimWithoutChars() { - assertThat(transform("ltrim(field)")).isEqualTo(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field\"}}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderLtrimWithChars() { - - assertThat(transform("ltrim(field, 'ie')")) - .isEqualTo(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderLtrimWithCharsFromFieldReference() { - - assertThat(transform("ltrim(field1, field2)")) - .isEqualTo(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderRtrimWithoutChars() { - assertThat(transform("rtrim(field)")).isEqualTo(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field\"}}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderRtrimWithChars() { - - assertThat(transform("rtrim(field, 'ie')")) - .isEqualTo(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderRtrimWithCharsFromFieldReference() { - - assertThat(transform("rtrim(field1, field2)")) - .isEqualTo(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderConvertWithoutOptionalParameters() { - - assertThat(transform("convert(field, 'string')")) - .isEqualTo(Document.parse("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"string\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderConvertWithOnError() { - - assertThat(transform("convert(field, 'int', 'Not an integer.')")).isEqualTo(Document - .parse("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderConvertWithOnErrorOnNull() { - - assertThat(transform("convert(field, 'int', 'Not an integer.', -1)")).isEqualTo(Document.parse( - "{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\", \"onNull\" : -1 }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderToBool() { - assertThat(transform("toBool(field)")).isEqualTo(Document.parse("{ \"$toBool\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderToDate() { - assertThat(transform("toDate(field)")).isEqualTo(Document.parse("{ \"$toDate\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderToDecimal() { - assertThat(transform("toDecimal(field)")).isEqualTo(Document.parse("{ \"$toDecimal\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderToDouble() { - assertThat(transform("toDouble(field)")).isEqualTo(Document.parse("{ \"$toDouble\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderToInt() { - assertThat(transform("toInt(field)")).isEqualTo(Document.parse("{ \"$toInt\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderToLong() { - assertThat(transform("toLong(field)")).isEqualTo(Document.parse("{ \"$toLong\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderToObjectId() { - assertThat(transform("toObjectId(field)")).isEqualTo(Document.parse("{ \"$toObjectId\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderToString() { - assertThat(transform("toString(field)")).isEqualTo(Document.parse("{ \"$toString\" : \"$field\"}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderDateFromStringWithoutOptionalParameters() { - - assertThat(transform("dateFromString(field)")) - .isEqualTo(Document.parse("{ \"$dateFromString\" : {\"dateString\" : \"$field\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderDateFromStringWithFormat() { - - assertThat(transform("dateFromString(field, 'DD-MM-YYYY')")).isEqualTo( - Document.parse("{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderDateFromStringWithFormatAndTimezone() { - - assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC')")).isEqualTo(Document.parse( - "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\" }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderDateFromStringWithFormatTimezoneAndOnError() { - - assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1)")).isEqualTo(Document.parse( - "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1 }}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderDateFromStringWithFormatTimezoneOnErrorAndOnNull() { - - assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1, -2)")).isEqualTo(Document.parse( - "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1, \"onNull\" : -2}}")); - } - - @Test // DATAMONGO-2077, DATAMONGO-2671 - public void shouldRenderDateFromParts() { - - assertThat(transform("dateFromParts(y, m, d, h, mm, s, ms, 'UTC')")).isEqualTo(Document.parse( - "{ \"$dateFromParts\" : {\"year\" : \"$y\", \"month\" : \"$m\", \"day\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"millisecond\" : \"$ms\", \"timezone\" : \"UTC\"}}")); - } - - @Test // DATAMONGO-2077, DATAMONGO-2671 - public void shouldRenderIsoDateFromParts() { - - assertThat(transform("isoDateFromParts(y, m, d, h, mm, s, ms, 'UTC')")).isEqualTo(Document.parse( - "{ \"$dateFromParts\" : {\"isoWeekYear\" : \"$y\", \"isoWeek\" : \"$m\", \"isoDayOfWeek\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"millisecond\" : \"$ms\", \"timezone\" : \"UTC\"}}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderDateToParts() { - - assertThat(transform("dateToParts(field, 'UTC', false)")).isEqualTo( - Document.parse("{ \"$dateToParts\" : {\"date\" : \"$field\", \"timezone\" : \"UTC\", \"iso8601\" : false}}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderIndexOfArray() { - - assertThat(transform("indexOfArray(field, 2)")) - .isEqualTo(Document.parse("{ \"$indexOfArray\" : [\"$field\", 2 ]}")); - } - - @Test // DATAMONGO-2077 - public void shouldRenderRange() { - - assertThat(transform("range(0, 10, 2)")).isEqualTo(Document.parse("{ \"$range\" : [0, 10, 2 ]}")); - } - - @Test // DATAMONGO-2370 - public void shouldRenderRound() { - assertThat(transform("round(field)")).isEqualTo(Document.parse("{ \"$round\" : [\"$field\"]}")); - } - - @Test // DATAMONGO-2370 - public void shouldRenderRoundWithPlace() { - assertThat(transform("round(field, 2)")).isEqualTo(Document.parse("{ \"$round\" : [\"$field\", 2]}")); - } - - private Object transform(String expression, Object... params) { - Object result = transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params); - return result == null ? null : (!(result instanceof org.bson.Document) ? result.toString() : result); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StateStats.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StateStats.java deleted file mode 100644 index a1f184208c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StateStats.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Field; - -/** - * @author Thomas Darimont - */ -class StateStats { - @Id String id; - String state; - @Field("totalPop") int totalPopulation; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java deleted file mode 100644 index 0dbe362ae4..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2018-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit test for {@link StringOperators}. - * - * @author Christoph Strobl - * @author Mark Paluch - * @currentRead Royal Assassin - Robin Hobb - */ -public class StringOperatorsUnitTests { - - static final String EXPRESSION_STRING = "{ \"$fitz\" : \"chivalry\" }"; - static final Document EXPRESSION_DOC = Document.parse(EXPRESSION_STRING); - static final AggregationExpression EXPRESSION = context -> EXPRESSION_DOC; - - @Test // DATAMONGO-2049 - public void shouldRenderTrim() { - - assertThat(StringOperators.valueOf("shrewd").trim().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $trim: { \"input\" : \"$shrewd\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderTrimForExpression() { - - assertThat(StringOperators.valueOf(EXPRESSION).trim().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $trim: { \"input\" : " + EXPRESSION_STRING + " } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderTrimWithChars() { - - assertThat(StringOperators.valueOf("shrewd").trim("sh").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $trim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderTrimWithCharsExpression() { - - assertThat(StringOperators.valueOf("shrewd").trim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $trim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderTrimLeft() { - - assertThat(StringOperators.valueOf("shrewd").trim().left().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderTrimLeftWithChars() { - - assertThat(StringOperators.valueOf("shrewd").trim("sh").left().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderTrimRight() { - - assertThat(StringOperators.valueOf("shrewd").trim().right().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderTrimRightWithChars() { - - assertThat(StringOperators.valueOf("shrewd").trim("sh").right().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderLTrim() { - - assertThat(StringOperators.valueOf("shrewd").ltrim().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderLTrimForExpression() { - - assertThat(StringOperators.valueOf(EXPRESSION).ltrim().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $ltrim: { \"input\" : " + EXPRESSION_STRING + " } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderLTrimWithChars() { - - assertThat(StringOperators.valueOf("shrewd").ltrim("sh").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderLTrimWithCharsExpression() { - - assertThat(StringOperators.valueOf("shrewd").ltrim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderRTrim() { - - assertThat(StringOperators.valueOf("shrewd").rtrim().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderRTrimForExpression() { - - assertThat(StringOperators.valueOf(EXPRESSION).rtrim().toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $rtrim: { \"input\" : " + EXPRESSION_STRING + " } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderRTrimWithChars() { - - assertThat(StringOperators.valueOf("shrewd").rtrim("sh").toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); - } - - @Test // DATAMONGO-2049 - public void shouldRenderRTrimWithCharsExpression() { - - assertThat(StringOperators.valueOf("shrewd").rtrim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT)) - .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } ")); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TagCount.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TagCount.java deleted file mode 100644 index 2bfdb2271e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TagCount.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.springframework.data.mongodb.core.aggregation; - -/** - * Simple value object holding the aggregation result. - * - * @author Tobias Trelle - */ -public class TagCount { - - private String tag; - - private int n; - - public String getTag() { - return tag; - } - - public void setTag(String tag) { - this.tag = tag; - } - - public int getN() { - return n; - } - - public void setN(int n) { - this.n = n; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java deleted file mode 100644 index fe28097d89..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import static org.springframework.data.mongodb.core.DocumentTestUtils.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.core.aggregation.Fields.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import lombok.AllArgsConstructor; - -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.support.GenericConversionService; -import org.springframework.data.annotation.Id; -import org.springframework.data.convert.CustomConversions; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference; -import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.data.mongodb.core.query.Criteria; - -/** - * Unit tests for {@link TypeBasedAggregationOperationContext}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Mark Paluch - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -public class TypeBasedAggregationOperationContextUnitTests { - - MongoMappingContext context; - MappingMongoConverter converter; - QueryMapper mapper; - - @Mock DbRefResolver dbRefResolver; - - @BeforeEach - public void setUp() { - - this.context = new MongoMappingContext(); - this.converter = new MappingMongoConverter(dbRefResolver, context); - this.mapper = new QueryMapper(converter); - } - - @Test - public void findsSimpleReference() { - assertThat(getContext(Foo.class).getReference("bar")).isNotNull(); - } - - @Test - public void rejectsInvalidFieldReference() { - assertThatExceptionOfType(MappingException.class).isThrownBy(() -> getContext(Foo.class).getReference("foo")); - } - - @Test // DATAMONGO-741 - public void returnsReferencesToNestedFieldsCorrectly() { - - AggregationOperationContext context = getContext(Foo.class); - - Field field = field("bar.name"); - - assertThat(context.getReference("bar.name")).isNotNull(); - assertThat(context.getReference(field)).isNotNull(); - assertThat(context.getReference(field)).isEqualTo(context.getReference("bar.name")); - } - - @Test // DATAMONGO-806 - public void aliasesIdFieldCorrectly() { - - AggregationOperationContext context = getContext(Foo.class); - assertThat(context.getReference("id")) - .isEqualTo(new DirectFieldReference(new ExposedField(field("id", "_id"), true))); - } - - @Test // DATAMONGO-912 - public void shouldUseCustomConversionIfPresentAndConversionIsRequiredInFirstStage() { - - CustomConversions customConversions = customAgeConversions(); - converter.setCustomConversions(customConversions); - customConversions.registerConvertersIn((GenericConversionService) converter.getConversionService()); - - AggregationOperationContext context = getContext(FooPerson.class); - - MatchOperation matchStage = match(Criteria.where("age").is(new Age(10))); - ProjectionOperation projectStage = project("age", "name"); - - org.bson.Document agg = newAggregation(matchStage, projectStage).toDocument("test", context); - - org.bson.Document age = getValue(getValue(getPipelineElementFromAggregationAt(agg, 0), "$match"), "age"); - assertThat(age).isEqualTo(new Document("v", 10)); - } - - @Test // DATAMONGO-912 - public void shouldUseCustomConversionIfPresentAndConversionIsRequiredInLaterStage() { - - CustomConversions customConversions = customAgeConversions(); - converter.setCustomConversions(customConversions); - customConversions.registerConvertersIn((GenericConversionService) converter.getConversionService()); - - AggregationOperationContext context = getContext(FooPerson.class); - - MatchOperation matchStage = match(Criteria.where("age").is(new Age(10))); - ProjectionOperation projectStage = project("age", "name"); - - org.bson.Document agg = newAggregation(projectStage, matchStage).toDocument("test", context); - - org.bson.Document age = getValue(getValue(getPipelineElementFromAggregationAt(agg, 1), "$match"), "age"); - assertThat(age).isEqualTo(new Document("v", 10)); - } - - @Test // DATAMONGO-960 - public void rendersAggregationOptionsInTypedAggregationContextCorrectly() { - - AggregationOperationContext context = getContext(FooPerson.class); - TypedAggregation agg = newAggregation(FooPerson.class, project("name", "age")) // - .withOptions( - newAggregationOptions().allowDiskUse(true).explain(true).cursor(new org.bson.Document("foo", 1)).build()); - - org.bson.Document document = agg.toDocument("person", context); - - org.bson.Document projection = getPipelineElementFromAggregationAt(document, 0); - assertThat(projection.containsKey("$project")).isTrue(); - - assertThat(projection.get("$project")).isEqualTo(new Document("name", 1).append("age", 1)); - - assertThat(document.get("allowDiskUse")).isEqualTo(true); - assertThat(document.get("explain")).isEqualTo(true); - assertThat(document.get("cursor")).isEqualTo(new Document("foo", 1)); - } - - @Test // DATAMONGO-1585 - public void rendersSortOfProjectedFieldCorrectly() { - - TypeBasedAggregationOperationContext context = getContext(MeterData.class); - TypedAggregation agg = newAggregation(MeterData.class, project().and("counterName").as("counter"), // - sort(Direction.ASC, "counter")); - - Document dbo = agg.toDocument("meterData", context); - Document sort = getPipelineElementFromAggregationAt(dbo, 1); - - Document definition = (Document) sort.get("$sort"); - assertThat(definition.get("counter")).isEqualTo(1); - } - - @Test // DATAMONGO-1586 - public void rendersFieldAliasingProjectionCorrectly() { - - AggregationOperationContext context = getContext(FooPerson.class); - TypedAggregation agg = newAggregation(FooPerson.class, project() // - .and("name").as("person_name") // - .and("age.value").as("age")); - - Document dbo = agg.toDocument("person", context); - - Document projection = getPipelineElementFromAggregationAt(dbo, 0); - assertThat(getAsDocument(projection, "$project")).containsEntry("person_name", "$name") // - .containsEntry("age", "$age.value"); - } - - @Test // DATAMONGO-1893 - public void considersIncludedFieldsFromSingleExclusionsCorrectly() { - - AggregationOperationContext context = getContext(FooPerson.class); - TypedAggregation agg = newAggregation(FooPerson.class, project() // - .andExclude("name"), sort(Sort.by("age.value", "lastName"))); - - Document dbo = agg.toDocument("person", context); - - Document sort = getPipelineElementFromAggregationAt(dbo, 1); - assertThat(getAsDocument(sort, "$sort")).isEqualTo(new Document("age.value", 1).append("last_name", 1)); - } - - @Test // DATAMONGO-1133 - public void shouldHonorAliasedFieldsInGroupExpressions() { - - TypeBasedAggregationOperationContext context = getContext(MeterData.class); - TypedAggregation agg = newAggregation(MeterData.class, - group("counterName").sum("counterVolume").as("totalCounterVolume")); - - org.bson.Document document = agg.toDocument("meterData", context); - org.bson.Document group = getPipelineElementFromAggregationAt(document, 0); - - org.bson.Document definition = (org.bson.Document) group.get("$group"); - - assertThat(definition.get("_id")).isEqualTo("$counter_name"); - } - - @Test // DATAMONGO-1326, DATAMONGO-1585 - public void lookupShouldInheritFieldsFromInheritingAggregationOperation() { - - TypeBasedAggregationOperationContext context = getContext(MeterData.class); - TypedAggregation agg = newAggregation(MeterData.class, - lookup("OtherCollection", "resourceId", "otherId", "lookup"), // - sort(Direction.ASC, "resourceId", "counterName")); - - org.bson.Document document = agg.toDocument("meterData", context); - org.bson.Document sort = getPipelineElementFromAggregationAt(document, 1); - - org.bson.Document definition = (org.bson.Document) sort.get("$sort"); - - assertThat(definition.get("resourceId")).isEqualTo(1); - assertThat(definition.get("counter_name")).isEqualTo(1); - } - - @Test // DATAMONGO-1326 - public void groupLookupShouldInheritFieldsFromPreviousAggregationOperation() { - - TypeBasedAggregationOperationContext context = getContext(MeterData.class); - TypedAggregation agg = newAggregation(MeterData.class, group().min("resourceId").as("foreignKey"), - lookup("OtherCollection", "foreignKey", "otherId", "lookup"), sort(Direction.ASC, "foreignKey")); - - org.bson.Document document = agg.toDocument("meterData", context); - org.bson.Document sort = getPipelineElementFromAggregationAt(document, 2); - - org.bson.Document definition = (org.bson.Document) sort.get("$sort"); - - assertThat(definition.get("foreignKey")).isEqualTo(1); - } - - @Test // DATAMONGO-1326 - public void lookupGroupAggregationShouldUseCorrectGroupField() { - - TypeBasedAggregationOperationContext context = getContext(MeterData.class); - TypedAggregation agg = newAggregation(MeterData.class, - lookup("OtherCollection", "resourceId", "otherId", "lookup"), - group().min("lookup.otherkey").as("something_totally_different")); - - org.bson.Document document = agg.toDocument("meterData", context); - org.bson.Document group = getPipelineElementFromAggregationAt(document, 1); - - org.bson.Document definition = (org.bson.Document) group.get("$group"); - org.bson.Document field = (org.bson.Document) definition.get("something_totally_different"); - - assertThat(field.get("$min")).isEqualTo("$lookup.otherkey"); - } - - @Test // DATAMONGO-1326 - public void lookupGroupAggregationShouldOverwriteExposedFields() { - - TypeBasedAggregationOperationContext context = getContext(MeterData.class); - TypedAggregation agg = newAggregation(MeterData.class, - lookup("OtherCollection", "resourceId", "otherId", "lookup"), - group().min("lookup.otherkey").as("something_totally_different"), - sort(Direction.ASC, "something_totally_different")); - - org.bson.Document document = agg.toDocument("meterData", context); - org.bson.Document sort = getPipelineElementFromAggregationAt(document, 2); - - org.bson.Document definition = (org.bson.Document) sort.get("$sort"); - - assertThat(definition.get("something_totally_different")).isEqualTo(1); - } - - @Test // DATAMONGO-1326 - public void lookupGroupAggregationShouldFailInvalidFieldReference() { - - TypeBasedAggregationOperationContext context = getContext(MeterData.class); - TypedAggregation agg = newAggregation(MeterData.class, - lookup("OtherCollection", "resourceId", "otherId", "lookup"), - group().min("lookup.otherkey").as("something_totally_different"), sort(Direction.ASC, "resourceId")); - - assertThatIllegalArgumentException().isThrownBy(() -> agg.toDocument("meterData", context)); - } - - @Test // DATAMONGO-861 - public void rendersAggregationConditionalInTypedAggregationContextCorrectly() { - - AggregationOperationContext context = getContext(FooPerson.class); - TypedAggregation agg = newAggregation(FooPerson.class, project("name") // - .and("age") // - .applyCondition( - ConditionalOperators.when(Criteria.where("age.value").lt(10)).then(new Age(0)).otherwiseValueOf("age")) // - ); - - Document document = agg.toDocument("person", context); - - Document projection = getPipelineElementFromAggregationAt(document, 0); - assertThat(projection.containsKey("$project")).isTrue(); - - Document project = getValue(projection, "$project"); - Document age = getValue(project, "age"); - - assertThat((Document) getValue(age, "$cond")).containsEntry("then.value", 0); - assertThat((Document) getValue(age, "$cond")).containsEntry("then._class", Age.class.getName()); - assertThat((Document) getValue(age, "$cond")).containsEntry("else", "$age"); - } - - /** - * .AggregationUnitTests - */ - @Test // DATAMONGO-861, DATAMONGO-1542 - public void rendersAggregationIfNullInTypedAggregationContextCorrectly() { - - AggregationOperationContext context = getContext(FooPerson.class); - TypedAggregation agg = newAggregation(FooPerson.class, project("name") // - .and("age") // - .applyCondition(ConditionalOperators.ifNull("age").then(new Age(0))) // - ); - - Document document = agg.toDocument("person", context); - - Document projection = getPipelineElementFromAggregationAt(document, 0); - assertThat(projection.containsKey("$project")).isTrue(); - - Document project = getValue(projection, "$project"); - Document age = getValue(project, "age"); - - assertThat(age).isEqualTo(Document.parse( - "{ $ifNull: [ \"$age\", { \"_class\":\"org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContextUnitTests$Age\", \"value\": 0} ] }")); - - assertThat(age).containsEntry("$ifNull.[0]", "$age"); - assertThat(age).containsEntry("$ifNull.[1].value", 0); - assertThat(age).containsEntry("$ifNull.[1]._class", Age.class.getName()); - } - - @Test // DATAMONGO-1756 - public void projectOperationShouldRenderNestedFieldNamesCorrectlyForTypedAggregation() { - - AggregationOperationContext context = getContext(Wrapper.class); - - Document agg = newAggregation(Wrapper.class, project().and("nested1.value1").plus("nested2.value2").as("val")) - .toDocument("collection", context); - - assertThat(getPipelineElementFromAggregationAt(agg, 0).get("$project")) - .isEqualTo(new Document("val", new Document("$add", Arrays.asList("$nested1.value1", "$field2.nestedValue2")))); - } - - @Test // DATAMONGO-1902 - void rendersProjectOnUnwrappableTypeFieldCorrectly() { - - AggregationOperationContext context = getContext(WithUnwrapped.class); - - Document agg = newAggregation(WithUnwrapped.class, project().and("unwrappedValue.stringValue").as("val")) - .toDocument("collection", context); - - assertThat(getPipelineElementFromAggregationAt(agg, 0).get("$project")) - .isEqualTo(new Document("val", "$stringValue")); - } - - @Test // DATAMONGO-1902 - void rendersProjectOnUnwrappedFieldWithAtFieldAnnotationCorrectly() { - - AggregationOperationContext context = getContext(WithUnwrapped.class); - - Document agg = newAggregation(WithUnwrapped.class, project().and("unwrappedValue.atFieldAnnotatedValue").as("val")) - .toDocument("collection", context); - - assertThat(getPipelineElementFromAggregationAt(agg, 0).get("$project")) - .isEqualTo(new Document("val", "$with-at-field-annotation")); - } - - @Test // DATAMONGO-1902 - void rendersProjectOnPrefixedUnwrappedFieldCorrectly() { - - AggregationOperationContext context = getContext(WithUnwrapped.class); - - Document agg = newAggregation(WithUnwrapped.class, project().and("prefixedUnwrappedValue.stringValue").as("val")) - .toDocument("collection", context); - - assertThat(getPipelineElementFromAggregationAt(agg, 0).get("$project")) - .isEqualTo(new Document("val", "$prefix-stringValue")); - } - - @Test // DATAMONGO-1902 - void rendersProjectOnPrefixedUnwrappedFieldWithAtFieldAnnotationCorrectly() { - - AggregationOperationContext context = getContext(WithUnwrapped.class); - - Document agg = newAggregation(WithUnwrapped.class, - project().and("prefixedUnwrappedValue.atFieldAnnotatedValue").as("val")).toDocument("collection", context); - - assertThat(getPipelineElementFromAggregationAt(agg, 0).get("$project")) - .isEqualTo(new Document("val", "$prefix-with-at-field-annotation")); - } - - @Test // DATAMONGO-1902 - void rendersProjectOnNestedUnwrappedFieldCorrectly() { - - AggregationOperationContext context = getContext(WrapperAroundWithUnwrapped.class); - - Document agg = newAggregation(WrapperAroundWithUnwrapped.class, - project().and("withUnwrapped.unwrappedValue.stringValue").as("val")).toDocument("collection", context); - - assertThat(getPipelineElementFromAggregationAt(agg, 0).get("$project")) - .isEqualTo(new Document("val", "$withUnwrapped.stringValue")); - } - - @Test // DATAMONGO-1902 - void rendersProjectOnNestedUnwrappedFieldWithAtFieldAnnotationCorrectly() { - - AggregationOperationContext context = getContext(WrapperAroundWithUnwrapped.class); - - Document agg = newAggregation(WrapperAroundWithUnwrapped.class, - project().and("withUnwrapped.unwrappedValue.atFieldAnnotatedValue").as("val")).toDocument("collection", - context); - - assertThat(getPipelineElementFromAggregationAt(agg, 0).get("$project")) - .isEqualTo(new Document("val", "$withUnwrapped.with-at-field-annotation")); - } - - @Test // DATAMONGO-1902 - void rendersProjectOnNestedPrefixedUnwrappedFieldCorrectly() { - - AggregationOperationContext context = getContext(WrapperAroundWithUnwrapped.class); - - Document agg = newAggregation(WrapperAroundWithUnwrapped.class, - project().and("withUnwrapped.prefixedUnwrappedValue.stringValue").as("val")).toDocument("collection", context); - - assertThat(getPipelineElementFromAggregationAt(agg, 0).get("$project")) - .isEqualTo(new Document("val", "$withUnwrapped.prefix-stringValue")); - } - - @Test // DATAMONGO-1902 - void rendersProjectOnNestedPrefixedUnwrappedFieldWithAtFieldAnnotationCorrectly() { - - AggregationOperationContext context = getContext(WrapperAroundWithUnwrapped.class); - - Document agg = newAggregation(WrapperAroundWithUnwrapped.class, - project().and("withUnwrapped.prefixedUnwrappedValue.atFieldAnnotatedValue").as("val")).toDocument("collection", - context); - - assertThat(getPipelineElementFromAggregationAt(agg, 0).get("$project")) - .isEqualTo(new Document("val", "$withUnwrapped.prefix-with-at-field-annotation")); - } - - @org.springframework.data.mongodb.core.mapping.Document(collection = "person") - @AllArgsConstructor - public static class FooPerson { - - final ObjectId id; - final String name; - @org.springframework.data.mongodb.core.mapping.Field("last_name") final String lastName; - final Age age; - } - - public static class Age { - - final int value; - - Age(int value) { - this.value = value; - } - } - - public CustomConversions customAgeConversions() { - return new MongoCustomConversions(Arrays.asList(ageWriteConverter(), ageReadConverter())); - } - - Converter ageWriteConverter() { - return new Converter() { - @Override - public org.bson.Document convert(Age age) { - return new org.bson.Document("v", age.value); - } - }; - } - - Converter ageReadConverter() { - return new Converter() { - @Override - public Age convert(org.bson.Document document) { - return new Age(((Integer) document.get("v"))); - } - }; - } - - @SuppressWarnings("unchecked") - static org.bson.Document getPipelineElementFromAggregationAt(org.bson.Document agg, int index) { - return ((List) agg.get("pipeline")).get(index); - } - - @SuppressWarnings("unchecked") - static T getValue(org.bson.Document o, String key) { - return (T) o.get(key); - } - - private TypeBasedAggregationOperationContext getContext(Class type) { - return new TypeBasedAggregationOperationContext(type, context, mapper); - } - - static class Foo { - - @Id String id; - Bar bar; - } - - static class Bar { - - String name; - } - - static class Wrapper { - - Nested nested1; - @org.springframework.data.mongodb.core.mapping.Field("field2") Nested nested2; - } - - static class Nested { - String value1; - @org.springframework.data.mongodb.core.mapping.Field("nestedValue2") String value2; - } - - static class WrapperAroundWithUnwrapped { - - String id; - WithUnwrapped withUnwrapped; - } - - static class WithUnwrapped { - - String id; - - @Unwrapped.Nullable UnwrappableType unwrappedValue; - @Unwrapped.Nullable("prefix-") UnwrappableType prefixedUnwrappedValue; - } - - static class UnwrappableType { - - String stringValue; - - @org.springframework.data.mongodb.core.mapping.Field("with-at-field-annotation") // - String atFieldAnnotatedValue; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java deleted file mode 100644 index cef23de937..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2020-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; - -/** - * Unit tests for {@link UnionWithOperation}. - * - * @author Christoph Strobl - */ -class UnionWithOperationUnitTests { - - @Test // DATAMONGO-2622 - void throwsErrorWhenNoCollectionPresent() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> UnionWithOperation.unionWith(null)); - } - - @Test // DATAMONGO-2622 - void rendersJustCollectionCorrectly() { - - assertThat(UnionWithOperation.unionWith("coll-1").toPipelineStages(contextFor(Warehouse.class))) - .containsExactly(new Document("$unionWith", new Document("coll", "coll-1"))); - } - - @Test // DATAMONGO-2622 - void rendersPipelineCorrectly() { - - assertThat(UnionWithOperation.unionWith("coll-1").mapFieldsTo(Warehouse.class) - .pipeline(Aggregation.project().and("location").as("region")).toPipelineStages(contextFor(Warehouse.class))) - .containsExactly(new Document("$unionWith", new Document("coll", "coll-1").append("pipeline", - Arrays.asList(new Document("$project", new Document("region", 1)))))); - } - - @Test // DATAMONGO-2622 - void rendersPipelineCorrectlyForDifferentDomainType() { - - assertThat(UnionWithOperation.unionWith("coll-1").pipeline(Aggregation.project().and("name").as("name")) - .mapFieldsTo(Supplier.class).toPipelineStages(contextFor(Warehouse.class))) - .containsExactly(new Document("$unionWith", new Document("coll", "coll-1").append("pipeline", - Arrays.asList(new Document("$project", new Document("name", "$supplier")))))); - } - - @Test // DATAMONGO-2622 - void rendersPipelineCorrectlyForUntypedContext() { - - assertThat(UnionWithOperation.unionWith("coll-1").pipeline(Aggregation.project("region")) - .toPipelineStages(contextFor(null))) - .containsExactly(new Document("$unionWith", new Document("coll", "coll-1").append("pipeline", - Arrays.asList(new Document("$project", new Document("region", 1)))))); - } - - @Test // DATAMONGO-2622 - void doesNotMapAgainstFieldsFromAPreviousStage() { - - TypedAggregation agg = TypedAggregation.newAggregation(Supplier.class, - Aggregation.project().and("name").as("supplier"), - UnionWithOperation.unionWith("coll-1").pipeline(Aggregation.project().and("name").as("name"))); - - List pipeline = agg.toPipeline(contextFor(Supplier.class)); - assertThat(pipeline).containsExactly(new Document("$project", new Document("supplier", 1)), // - new Document("$unionWith", new Document("coll", "coll-1").append("pipeline", - Arrays.asList(new Document("$project", new Document("name", 1)))))); - } - - @Test // DATAMONGO-2622 - void mapAgainstUnionWithDomainTypeEvenWhenInsideTypedAggregation() { - - TypedAggregation agg = TypedAggregation.newAggregation(Supplier.class, - Aggregation.project().and("name").as("supplier"), UnionWithOperation.unionWith("coll-1") - .mapFieldsTo(Warehouse.class).pipeline(Aggregation.project().and("location").as("location"))); - - List pipeline = agg.toPipeline(contextFor(Supplier.class)); - assertThat(pipeline).containsExactly(new Document("$project", new Document("supplier", 1)), // - new Document("$unionWith", new Document("coll", "coll-1").append("pipeline", - Arrays.asList(new Document("$project", new Document("location", "$region")))))); - } - - private static AggregationOperationContext contextFor(@Nullable Class type) { - - if (type == null) { - return Aggregation.DEFAULT_CONTEXT; - } - - MappingMongoConverter mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, - new MongoMappingContext()); - mongoConverter.afterPropertiesSet(); - - return new TypeBasedAggregationOperationContext(type, mongoConverter.getMappingContext(), - new QueryMapper(mongoConverter)); - } - - static class Warehouse { - - String name; - @Field("region") String location; - String state; - } - - static class Supplier { - - @Field("supplier") String name; - String state; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java deleted file mode 100644 index 5f9e7e7247..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2019-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; - -/** - * Unit tests for {@link UnsetOperation}. - * - * @author Christoph Strobl - */ -public class UnsetOperationUnitTests { - - @Test // DATAMONGO-2331 - public void raisesErrorOnNullField() { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new UnsetOperation(null)); - } - - @Test // DATAMONGO-2331 - public void rendersSingleFieldReferenceCorrectly() { - - assertThat(new UnsetOperation(Collections.singletonList("title")).toPipelineStages(contextFor(Book.class))) - .containsExactly(Document.parse("{\"$unset\" : \"title\" }")); - } - - @Test // DATAMONGO-2331 - public void rendersSingleMappedFieldReferenceCorrectly() { - - assertThat(new UnsetOperation(Collections.singletonList("stock")).toPipelineStages(contextFor(Book.class))) - .containsExactly(Document.parse("{\"$unset\" : \"copies\" }")); - } - - @Test // DATAMONGO-2331 - public void rendersSingleNestedMappedFieldReferenceCorrectly() { - - assertThat( - new UnsetOperation(Collections.singletonList("author.firstname")).toPipelineStages(contextFor(Book.class))) - .containsExactly(Document.parse("{\"$unset\" : \"author.first\"}")); - } - - @Test // DATAMONGO-2331 - public void rendersMultipleFieldReferencesCorrectly() { - - assertThat(new UnsetOperation(Arrays.asList("title", "author.firstname", "stock.location")) - .toPipelineStages(contextFor(Book.class))) - .containsExactly(Document.parse("{\"$unset\" : [\"title\", \"author.first\", \"copies.warehouse\"] }")); - } - - @Test // DATAMONGO-2331 - public void exposesFieldsCorrectly() { - assertThat(UnsetOperation.unset("title").and("isbn").getFields()).isEqualTo(ExposedFields.from()); - } - - private static AggregationOperationContext contextFor(@Nullable Class type) { - - if (type == null) { - return Aggregation.DEFAULT_CONTEXT; - } - - MappingMongoConverter mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, - new MongoMappingContext()); - mongoConverter.afterPropertiesSet(); - - return new TypeBasedAggregationOperationContext(type, mongoConverter.getMappingContext(), - new QueryMapper(mongoConverter)); - } - - static class Book { - - @Id Integer id; - String title; - String isbn; - Author author; - @Field("copies") Collection stock; - } - - static class Author { - - @Field("first") String firstname; - @Field("last") String lastname; - } - - static class Warehouse { - - @Field("warehouse") String location; - Integer qty; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnwindOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnwindOperationUnitTests.java deleted file mode 100644 index fab157af90..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnwindOperationUnitTests.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2016-2021 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.aggregation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.DocumentTestUtils; - -/** - * Unit tests for {@link UnwindOperation}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -public class UnwindOperationUnitTests { - - @Test // DATAMONGO-1391 - public void unwindWithPathOnlyShouldUsePreMongo32Syntax() { - - UnwindOperation unwindOperation = Aggregation.unwind("a"); - - Document pipeline = unwindOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(pipeline).containsEntry("$unwind", "$a"); - } - - @Test // DATAMONGO-1391 - public void unwindWithArrayIndexShouldUseMongo32Syntax() { - - UnwindOperation unwindOperation = Aggregation.unwind("a", "index"); - - Document unwindClause = extractDocumentFromUnwindOperation(unwindOperation); - - assertThat(unwindClause).containsEntry("path", "$a").// - containsEntry("preserveNullAndEmptyArrays", false).// - containsEntry("includeArrayIndex", "index"); - } - - @Test // DATAMONGO-1391 - public void unwindWithArrayIndexShouldExposeArrayIndex() { - - UnwindOperation unwindOperation = Aggregation.unwind("a", "index"); - - assertThat(unwindOperation.getFields().getField("index")).isNotNull(); - } - - @Test // DATAMONGO-1391 - public void plainUnwindShouldNotExposeIndex() { - - UnwindOperation unwindOperation = Aggregation.unwind("a"); - - assertThat(unwindOperation.getFields().exposesNoFields()).isTrue(); - } - - @Test // DATAMONGO-1391 - public void unwindWithPreserveNullShouldUseMongo32Syntax() { - - UnwindOperation unwindOperation = Aggregation.unwind("a", true); - - Document unwindClause = extractDocumentFromUnwindOperation(unwindOperation); - - assertThat(unwindClause).containsEntry("path", "$a").// - containsEntry("preserveNullAndEmptyArrays", true).// - doesNotContainKey("includeArrayIndex"); - } - - @Test // DATAMONGO-1391 - public void lookupBuilderBuildsCorrectClause() { - - UnwindOperation unwindOperation = UnwindOperation.newUnwind().path("$foo").noArrayIndex().skipNullAndEmptyArrays(); - Document pipeline = unwindOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - - assertThat(pipeline).containsEntry("$unwind", "$foo"); - } - - @Test // DATAMONGO-1391 - public void lookupBuilderBuildsCorrectClauseForMongo32() { - - UnwindOperation unwindOperation = UnwindOperation.newUnwind().path("$foo").arrayIndex("myindex") - .preserveNullAndEmptyArrays(); - - Document unwindClause = extractDocumentFromUnwindOperation(unwindOperation); - - assertThat(unwindClause).containsEntry("path", "$foo").// - containsEntry("preserveNullAndEmptyArrays", true).// - containsEntry("includeArrayIndex", "myindex"); - } - - private Document extractDocumentFromUnwindOperation(UnwindOperation unwindOperation) { - - Document document = unwindOperation.toDocument(Aggregation.DEFAULT_CONTEXT); - Document unwindClause = DocumentTestUtils.getAsDocument(document, "$unwind"); - return unwindClause; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UserWithLikes.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UserWithLikes.java deleted file mode 100644 index fc75716c03..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UserWithLikes.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2013-2021 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.aggregation; - -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.Arrays; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -/** - * @author Thomas Darimont - * @author Christoph Strobl - */ -@Data -@NoArgsConstructor -public class UserWithLikes { - - String id; - Date joined; - Set likes = new HashSet(); - - public UserWithLikes(String id, Date joined, String... likes) { - - this.id = id; - this.joined = joined; - this.likes = new HashSet(Arrays.asList(likes)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ZipInfo.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ZipInfo.java deleted file mode 100644 index 7cf01122f2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ZipInfo.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.springframework.data.mongodb.core.aggregation; - -import java.util.Arrays; - -import org.springframework.data.mongodb.core.mapping.Field; - -/** - * Data model from mongodb reference data set - * - * @see Aggregation Examples - * @see > getInitialEntitySet() throws ClassNotFoundException { - return new HashSet<>(Arrays.asList(ImmutableAuditableEntityWithVersion.class, KAuditableVersionedEntity.class)); - } - } - - @Autowired MongoTemplate template; - - @Test // DATAMONGO-2346 - public void auditingSetsLastModifiedDateCorrectlyForImmutableVersionedEntityOnSave() throws InterruptedException { - - template.remove(new Query(), ImmutableAuditableEntityWithVersion.class); - - ImmutableAuditableEntityWithVersion entity = new ImmutableAuditableEntityWithVersion("id-1", "value", null, null); - ImmutableAuditableEntityWithVersion inserted = template.save(entity); - - TimeUnit.MILLISECONDS.sleep(500); - - ImmutableAuditableEntityWithVersion modified = inserted.withValue("changed-value"); - ImmutableAuditableEntityWithVersion updated = template.save(modified); - - ImmutableAuditableEntityWithVersion fetched = template.findOne(Query.query(Criteria.where("id").is(entity.id)), - ImmutableAuditableEntityWithVersion.class); - - assertThat(updated.modificationDate).isAfter(inserted.modificationDate); - assertThat(fetched.modificationDate).isAfter(inserted.modificationDate); - assertThat(fetched.modificationDate).isEqualTo(updated.modificationDate.truncatedTo(ChronoUnit.MILLIS)); - } - - @Test // DATAMONGO-2346 - public void auditingSetsLastModifiedDateCorrectlyForImmutableVersionedKotlinEntityOnSave() - throws InterruptedException { - - template.remove(new Query(), KAuditableVersionedEntity.class); - - KAuditableVersionedEntity entity = new KAuditableVersionedEntity("kId-1", "value", null, null); - KAuditableVersionedEntity inserted = template.save(entity); - - TimeUnit.MILLISECONDS.sleep(500); - - KAuditableVersionedEntity updated = template.save(inserted.withValue("changed-value")); - - KAuditableVersionedEntity fetched = template.findOne(Query.query(Criteria.where("id").is(entity.getId())), - KAuditableVersionedEntity.class); - - assertThat(updated.getModificationDate()).isAfter(inserted.getModificationDate()); - assertThat(fetched.getModificationDate()).isAfter(inserted.getModificationDate()); - assertThat(fetched.getModificationDate()).isEqualTo(updated.getModificationDate().truncatedTo(ChronoUnit.MILLIS)); - } - - static class ImmutableAuditableEntityWithVersion { - - final @Id String id; - final String value; - final @Version Integer version; - final @LastModifiedDate Instant modificationDate; - - ImmutableAuditableEntityWithVersion(String id, String value, Integer version, Instant modificationDate) { - - this.id = id; - this.value = value; - this.version = version; - this.modificationDate = modificationDate; - } - - ImmutableAuditableEntityWithVersion withValue(String value) { - return new ImmutableAuditableEntityWithVersion(id, value, version, modificationDate); - } - - ImmutableAuditableEntityWithVersion withModificationDate(Instant modificationDate) { - return new ImmutableAuditableEntityWithVersion(id, value, version, modificationDate); - } - - ImmutableAuditableEntityWithVersion withVersion(Integer version) { - return new ImmutableAuditableEntityWithVersion(id, value, version, modificationDate); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/auditing/ReactiveMongoTemplateAuditingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/auditing/ReactiveMongoTemplateAuditingTests.java deleted file mode 100644 index 165c8d8901..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/auditing/ReactiveMongoTemplateAuditingTests.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2019-2021 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.auditing; - -import static org.assertj.core.api.Assertions.*; - -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; -import reactor.util.function.Tuples; - -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.annotation.Version; -import org.springframework.data.domain.ReactiveAuditorAware; -import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; -import org.springframework.data.mongodb.config.EnableReactiveMongoAuditing; -import org.springframework.data.mongodb.core.KAuditableVersionedEntity; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Integration tests for {@link EnableReactiveMongoAuditing} through {@link ReactiveMongoTemplate}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -class ReactiveMongoTemplateAuditingTests { - - static final String DB_NAME = "mongo-template-audit-tests"; - - static @Client MongoClient mongoClient; - - @Configuration - @EnableReactiveMongoAuditing - static class Conf extends AbstractReactiveMongoConfiguration { - - @Bean - @Override - public MongoClient reactiveMongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return DB_NAME; - } - - @Override - protected Set> getInitialEntitySet() { - return Collections.emptySet(); - } - } - - @Autowired ReactiveMongoTemplate template; - @Autowired MongoClient client; - - @BeforeEach - void setUp() { - - MongoTestUtils.flushCollection(DB_NAME, template.getCollectionName(ImmutableAuditableEntityWithVersion.class), - client); - MongoTestUtils.flushCollection(DB_NAME, template.getCollectionName(KAuditableVersionedEntity.class), client); - } - - @Test // DATAMONGO-2346 - void auditingSetsLastModifiedDateCorrectlyForImmutableVersionedEntityOnSave() { - - ImmutableAuditableEntityWithVersion entity = new ImmutableAuditableEntityWithVersion(null, "value", null, null); - - template.save(entity).delayElement(Duration.ofMillis(500)) // - .flatMap(inserted -> template.save(inserted.withValue("changed-value")) // - .map(updated -> Tuples.of(inserted, updated))) // - .flatMap(tuple2 -> template - .findOne(Query.query(Criteria.where("id").is(tuple2.getT1().id)), ImmutableAuditableEntityWithVersion.class) - .map(fetched -> Tuples.of(tuple2.getT1(), tuple2.getT2(), fetched))) // - .as(StepVerifier::create) // - .consumeNextWith(tuple3 -> { - - assertThat(tuple3.getT2().modificationDate).isAfter(tuple3.getT1().modificationDate); - assertThat(tuple3.getT3().modificationDate).isAfter(tuple3.getT1().modificationDate); - assertThat(tuple3.getT3().modificationDate) - .isEqualTo(tuple3.getT2().modificationDate.truncatedTo(ChronoUnit.MILLIS)); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-2346 - void auditingSetsLastModifiedDateCorrectlyForImmutableVersionedKotlinEntityOnSave() { - - KAuditableVersionedEntity entity = new KAuditableVersionedEntity(null, "value", null, null); - - template.save(entity).delayElement(Duration.ofMillis(500)) // - .flatMap(inserted -> template.save(inserted.withValue("changed-value")) // - .map(updated -> Tuples.of(inserted, updated))) // - .flatMap(tuple2 -> template - .findOne(Query.query(Criteria.where("id").is(tuple2.getT1().getId())), KAuditableVersionedEntity.class) - .map(fetched -> Tuples.of(tuple2.getT1(), tuple2.getT2(), fetched))) // - .as(StepVerifier::create) // - .consumeNextWith(tuple3 -> { - - assertThat(tuple3.getT2().getModificationDate()).isAfter(tuple3.getT1().getModificationDate()); - assertThat(tuple3.getT3().getModificationDate()).isAfter(tuple3.getT1().getModificationDate()); - assertThat(tuple3.getT3().getModificationDate()) - .isEqualTo(tuple3.getT2().getModificationDate().truncatedTo(ChronoUnit.MILLIS)); - }) // - .verifyComplete(); - } - - @Document("versioned-auditable") - static class ImmutableAuditableEntityWithVersion { - - final @Id String id; - final String value; - final @Version Integer version; - final @LastModifiedDate Instant modificationDate; - - ImmutableAuditableEntityWithVersion(String id, String value, Integer version, Instant modificationDate) { - - this.id = id; - this.value = value; - this.version = version; - this.modificationDate = modificationDate; - } - - ImmutableAuditableEntityWithVersion withId(String id) { - return new ImmutableAuditableEntityWithVersion(id, value, version, modificationDate); - } - - ImmutableAuditableEntityWithVersion withValue(String value) { - return new ImmutableAuditableEntityWithVersion(id, value, version, modificationDate); - } - - ImmutableAuditableEntityWithVersion withModificationDate(Instant modificationDate) { - return new ImmutableAuditableEntityWithVersion(id, value, version, modificationDate); - } - - ImmutableAuditableEntityWithVersion withVersion(Integer version) { - return new ImmutableAuditableEntityWithVersion(id, value, version, modificationDate); - } - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverterUnitTests.java deleted file mode 100644 index b1d4a61204..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverterUnitTests.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2015-2021 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.convert; - -import static org.mockito.Mockito.*; - -import org.bson.conversions.Bson; -import org.junit.jupiter.api.Test; -import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.core.convert.support.GenericConversionService; -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToStringConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.StringToObjectIdConverter; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.util.TypeInformation; - -import com.mongodb.DBRef; - -/** - * Unit tests for {@link AbstractMongoConverter}. - * - * @author Oliver Gierke - */ -public class AbstractMongoConverterUnitTests { - - @Test // DATAMONGO-1324 - public void registersObjectIdConvertersExplicitly() { - - DefaultConversionService conversionService = spy(new DefaultConversionService()); - - new SampleMongoConverter(conversionService).afterPropertiesSet(); - - verify(conversionService).addConverter(StringToObjectIdConverter.INSTANCE); - verify(conversionService).addConverter(ObjectIdToStringConverter.INSTANCE); - } - - static class SampleMongoConverter extends AbstractMongoConverter { - - public SampleMongoConverter(GenericConversionService conversionService) { - super(conversionService); - } - - @Override - public MongoTypeMapper getTypeMapper() { - throw new UnsupportedOperationException(); - } - - @Override - public MappingContext, MongoPersistentProperty> getMappingContext() { - throw new UnsupportedOperationException(); - } - - @Override - public R read(Class type, Bson source) { - throw new UnsupportedOperationException(); - } - - @Override - public void write(Object source, Bson sink) { - throw new UnsupportedOperationException(); - } - - @Override - public Object convertToMongoType(Object obj, TypeInformation typeInformation) { - throw new UnsupportedOperationException(); - } - - @Override - public DBRef toDBRef(Object object, MongoPersistentProperty referingProperty) { - throw new UnsupportedOperationException(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/CustomConvertersUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/CustomConvertersUnitTests.java deleted file mode 100644 index 1c1ba0715d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/CustomConvertersUnitTests.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2011-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.HashSet; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.annotation.Id; -import org.springframework.data.convert.CustomConversions; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -/** - * Test case to verify correct usage of custom {@link Converter} implementations to be used. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class CustomConvertersUnitTests { - - private MappingMongoConverter converter; - - @Mock BarToDocumentConverter barToDocumentConverter; - @Mock DocumentToBarConverter documentToBarConverter; - @Mock MongoDatabaseFactory mongoDbFactory; - - private MongoMappingContext context; - - @BeforeEach - void setUp() { - - when(barToDocumentConverter.convert(any(Bar.class))).thenReturn(new Document()); - when(documentToBarConverter.convert(any(Document.class))).thenReturn(new Bar()); - - CustomConversions conversions = new MongoCustomConversions( - Arrays.asList(barToDocumentConverter, documentToBarConverter)); - - context = new MongoMappingContext(); - context.setInitialEntitySet(new HashSet<>(Arrays.asList(Foo.class, Bar.class))); - context.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); - context.initialize(); - - converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), context); - converter.setCustomConversions(conversions); - converter.afterPropertiesSet(); - } - - @Test // DATADOC-101 - void nestedToDocumentConverterGetsInvoked() { - - Foo foo = new Foo(); - foo.bar = new Bar(); - - converter.write(foo, new Document()); - verify(barToDocumentConverter).convert(any(Bar.class)); - } - - @Test // DATADOC-101 - void nestedFromDocumentConverterGetsInvoked() { - - Document document = new Document(); - document.put("bar", new Document()); - - converter.read(Foo.class, document); - verify(documentToBarConverter).convert(any(Document.class)); - } - - @Test // DATADOC-101 - void toDocumentConverterGetsInvoked() { - - converter.write(new Bar(), new Document()); - verify(barToDocumentConverter).convert(any(Bar.class)); - } - - @Test // DATADOC-101 - void fromDocumentConverterGetsInvoked() { - - converter.read(Bar.class, new Document()); - verify(documentToBarConverter).convert(any(Document.class)); - } - - @Test // DATADOC-101 - void foo() { - Document document = new Document(); - document.put("foo", null); - - assertThat(document).containsKey("foo"); - } - - public static class Foo { - @Id public String id; - public Bar bar; - } - - public static class Bar { - @Id public String id; - public String foo; - } - - private interface BarToDocumentConverter extends Converter {} - - private interface DocumentToBarConverter extends Converter {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DataMongo273Tests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DataMongo273Tests.java deleted file mode 100644 index af2db106d2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DataMongo273Tests.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2011-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -/** - * Unit test to reproduce DATAMONGO-273. - * - * @author Harlan Iverson - * @author Oliver Gierke - */ -public class DataMongo273Tests { - - MappingMongoConverter converter; - - @BeforeEach - public void setupMongoConverter() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - DbRefResolver factory = mock(DbRefResolver.class); - - converter = new MappingMongoConverter(factory, mappingContext); - converter.afterPropertiesSet(); - } - - @Test // DATAMONGO-273 - public void convertMapOfThings() { - - Plane plane = new Plane("Boeing", 4); - Train train = new Train("Santa Fe", 200); - Automobile automobile = new Automobile("Tesla", "Roadster", 2); - - Map mapOfThings = new HashMap(); - mapOfThings.put("plane", plane); - mapOfThings.put("train", train); - mapOfThings.put("automobile", automobile); - - Document result = new Document(); - converter.write(mapOfThings, result); - - @SuppressWarnings("unchecked") - Map mapOfThings2 = converter.read(Map.class, result); - - assertThat(mapOfThings2.get("plane") instanceof Plane).isTrue(); - assertThat(mapOfThings2.get("train") instanceof Train).isTrue(); - assertThat(mapOfThings2.get("automobile") instanceof Automobile).isTrue(); - } - - @Test // DATAMONGO-294 - @Disabled("TODO: Mongo3 - this is no longer supported as DBList is no Bson type :/") - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void convertListOfThings() { - Plane plane = new Plane("Boeing", 4); - Train train = new Train("Santa Fe", 200); - Automobile automobile = new Automobile("Tesla", "Roadster", 2); - - List listOfThings = new ArrayList(); - listOfThings.add(plane); - listOfThings.add(train); - listOfThings.add(automobile); - - Document result = new Document(); - converter.write(listOfThings, result); - - List listOfThings2 = converter.read(List.class, result); - - assertThat(listOfThings2.get(0) instanceof Plane).isTrue(); - assertThat(listOfThings2.get(1) instanceof Train).isTrue(); - assertThat(listOfThings2.get(2) instanceof Automobile).isTrue(); - } - - @Test // DATAMONGO-294 - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void convertListOfThings_NestedInMap() { - - Plane plane = new Plane("Boeing", 4); - Train train = new Train("Santa Fe", 200); - Automobile automobile = new Automobile("Tesla", "Roadster", 2); - - List listOfThings = new ArrayList(); - listOfThings.add(plane); - listOfThings.add(train); - listOfThings.add(automobile); - - Map box = new HashMap(); - box.put("one", listOfThings); - - Shipment shipment = new Shipment(box); - - Document result = new Document(); - converter.write(shipment, result); - - Shipment shipment2 = converter.read(Shipment.class, result); - - List listOfThings2 = (List) shipment2.getBoxes().get("one"); - - assertThat(listOfThings2.get(0) instanceof Plane).isTrue(); - assertThat(listOfThings2.get(1) instanceof Train).isTrue(); - assertThat(listOfThings2.get(2) instanceof Automobile).isTrue(); - } - - static class Plane { - - String maker; - int numberOfPropellers; - - public Plane(String maker, int numberOfPropellers) { - this.maker = maker; - this.numberOfPropellers = numberOfPropellers; - } - } - - static class Train { - - String railLine; - int numberOfCars; - - public Train(String railLine, int numberOfCars) { - this.railLine = railLine; - this.numberOfCars = numberOfCars; - } - } - - static class Automobile { - - String make; - String model; - int numberOfDoors; - - public Automobile(String make, String model, int numberOfDoors) { - this.make = make; - this.model = model; - this.numberOfDoors = numberOfDoors; - } - } - - @SuppressWarnings("rawtypes") - static class Shipment { - - Map boxes; - - public Shipment(Map boxes) { - this.boxes = boxes; - } - - public Map getBoxes() { - return boxes; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java deleted file mode 100644 index 2c0f8649e2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java +++ /dev/null @@ -1,895 +0,0 @@ -/* - * Copyright 2013-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils.*; - -import java.io.Serializable; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.bson.Document; -import org.bson.conversions.Bson; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.annotation.AccessType; -import org.springframework.data.annotation.AccessType.Type; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.mapping.PersistentPropertyAccessor; -import org.springframework.data.mapping.PropertyPath; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoExceptionTranslator; -import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.Person; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.SerializationUtils; - -import com.mongodb.DBRef; -import com.mongodb.client.FindIterable; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; - -/** - * Unit tests for {@link MappingMongoConverter}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class DbRefMappingMongoConverterUnitTests { - - MappingMongoConverter converter; - MongoMappingContext mappingContext; - - @Mock MongoDatabaseFactory dbFactory; - DefaultDbRefResolver dbRefResolver; - - @BeforeEach - public void setUp() { - - when(dbFactory.getExceptionTranslator()).thenReturn(new MongoExceptionTranslator()); - - this.dbRefResolver = spy(new DefaultDbRefResolver(dbFactory)); - this.mappingContext = new MongoMappingContext(); - this.converter = new MappingMongoConverter(dbRefResolver, mappingContext); - } - - @Test // DATAMONGO-347 - public void createsSimpleDBRefCorrectly() { - - Person person = new Person(); - person.id = "foo"; - - DBRef dbRef = converter.toDBRef(person, null); - assertThat(dbRef.getId()).isEqualTo("foo"); - assertThat(dbRef.getCollectionName()).isEqualTo("person"); - } - - @Test // DATAMONGO-657 - public void convertDocumentWithMapDBRef() { - - Document mapValDocument = new Document(); - mapValDocument.put("_id", BigInteger.ONE); - - DBRef dbRef = mock(DBRef.class); - when(dbRef.getId()).thenReturn(BigInteger.ONE); - when(dbRef.getCollectionName()).thenReturn("collection-1"); - - MongoDatabase dbMock = mock(MongoDatabase.class); - MongoCollection collectionMock = mock(MongoCollection.class); - when(dbFactory.getMongoDatabase()).thenReturn(dbMock); - when(dbMock.getCollection(anyString(), eq(Document.class))).thenReturn(collectionMock); - - FindIterable fi = mock(FindIterable.class); - when(fi.first()).thenReturn(mapValDocument); - when(collectionMock.find(Mockito.any(Bson.class))).thenReturn(fi); - - MapDBRef mapDBRef = new MapDBRef(); - - MapDBRefVal val = new MapDBRefVal(); - val.id = BigInteger.ONE; - - Map mapVal = new HashMap<>(); - mapVal.put("test", val); - - mapDBRef.map = mapVal; - - Document document = new Document(); - converter.write(mapDBRef, document); - - Document map = (Document) document.get("map"); - - assertThat(map.get("test")).isInstanceOf(DBRef.class); - - ((Document) document.get("map")).put("test", dbRef); - - MapDBRef read = converter.read(MapDBRef.class, document); - - assertThat(read.map.get("test").id).isEqualTo(BigInteger.ONE); - } - - @Test // DATAMONGO-347 - public void createsDBRefWithClientSpecCorrectly() { - - PropertyPath path = PropertyPath.from("person", PersonClient.class); - MongoPersistentProperty property = mappingContext.getPersistentPropertyPath(path).getLeafProperty(); - - Person person = new Person(); - person.id = "foo"; - - DBRef dbRef = converter.toDBRef(person, property); - assertThat(dbRef.getId()).isEqualTo("foo"); - assertThat(dbRef.getCollectionName()).isEqualTo("person"); - } - - @Test // DATAMONGO-348 - public void lazyLoadingProxyForLazyDbRefOnInterface() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id).append("value", value)).when(converterSpy).readRef(any()); - - Document document = new Document(); - ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); - lazyDbRefs.dbRefToInterface = new LinkedList<>(Collections.singletonList(new LazyDbRefTarget("1"))); - converterSpy.write(lazyDbRefs, document); - - ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, document); - - assertProxyIsResolved(result.dbRefToInterface, false); - assertThat(result.dbRefToInterface.get(0).getId()).isEqualTo(id); - assertProxyIsResolved(result.dbRefToInterface, true); - assertThat(result.dbRefToInterface.get(0).getValue()).isEqualTo(value); - } - - @Test // DATAMONGO-348 - public void lazyLoadingProxyForLazyDbRefOnConcreteCollection() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id).append("value", value)).when(converterSpy).readRef(any()); - - Document document = new Document(); - ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); - lazyDbRefs.dbRefToConcreteCollection = new ArrayList<>(Collections.singletonList(new LazyDbRefTarget(id, value))); - converterSpy.write(lazyDbRefs, document); - - ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, document); - - assertProxyIsResolved(result.dbRefToConcreteCollection, false); - assertThat(result.dbRefToConcreteCollection.get(0).getId()).isEqualTo(id); - assertProxyIsResolved(result.dbRefToConcreteCollection, true); - assertThat(result.dbRefToConcreteCollection.get(0).getValue()).isEqualTo(value); - } - - @Test // DATAMONGO-348 - public void lazyLoadingProxyForLazyDbRefOnConcreteType() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id).append("value", value)).when(converterSpy).readRef(any()); - - Document document = new Document(); - ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); - lazyDbRefs.dbRefToConcreteType = new LazyDbRefTarget(id, value); - converterSpy.write(lazyDbRefs, document); - - ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, document); - - assertProxyIsResolved(result.dbRefToConcreteType, false); - assertThat(result.dbRefToConcreteType.getId()).isEqualTo(id); - assertProxyIsResolved(result.dbRefToConcreteType, true); - assertThat(result.dbRefToConcreteType.getValue()).isEqualTo(value); - } - - @Test // DATAMONGO-348 - public void lazyLoadingProxyForLazyDbRefOnConcreteTypeWithPersistenceConstructor() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id).append("value", value)).when(converterSpy).readRef(any()); - - Document document = new Document(); - ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); - lazyDbRefs.dbRefToConcreteTypeWithPersistenceConstructor = new LazyDbRefTargetWithPeristenceConstructor(id, value); - converterSpy.write(lazyDbRefs, document); - - ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, document); - - assertProxyIsResolved(result.dbRefToConcreteTypeWithPersistenceConstructor, false); - assertThat(result.dbRefToConcreteTypeWithPersistenceConstructor.getId()).isEqualTo(id); - assertProxyIsResolved(result.dbRefToConcreteTypeWithPersistenceConstructor, true); - assertThat(result.dbRefToConcreteTypeWithPersistenceConstructor.getValue()).isEqualTo(value); - } - - @Test // DATAMONGO-348 - public void lazyLoadingProxyForLazyDbRefOnConcreteTypeWithPersistenceConstructorButWithoutDefaultConstructor() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id).append("value", value)).when(converterSpy).readRef(any()); - - Document document = new Document(); - ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); - lazyDbRefs.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor = new LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor( - id, value); - converterSpy.write(lazyDbRefs, document); - - ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, document); - - assertProxyIsResolved(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor, false); - assertThat(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor.getId()).isEqualTo(id); - assertProxyIsResolved(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor, true); - assertThat(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor.getValue()) - .isEqualTo(value); - } - - @Test // DATAMONGO-348 - public void lazyLoadingProxyForSerializableLazyDbRefOnConcreteType() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id).append("value", value)).when(converterSpy).readRef(any()); - - Document document = new Document(); - SerializableClassWithLazyDbRefs lazyDbRefs = new SerializableClassWithLazyDbRefs(); - lazyDbRefs.dbRefToSerializableTarget = new SerializableLazyDbRefTarget(id, value); - converterSpy.write(lazyDbRefs, document); - - SerializableClassWithLazyDbRefs result = converterSpy.read(SerializableClassWithLazyDbRefs.class, document); - - SerializableClassWithLazyDbRefs deserializedResult = (SerializableClassWithLazyDbRefs) transport(result); - - assertThat(deserializedResult.dbRefToSerializableTarget.getId()).isEqualTo(id); - assertProxyIsResolved(deserializedResult.dbRefToSerializableTarget, true); - assertThat(deserializedResult.dbRefToSerializableTarget.getValue()).isEqualTo(value); - } - - @Test // DATAMONGO-884 - public void lazyLoadingProxyForToStringObjectMethodOverridingDbref() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id).append("value", value)).when(converterSpy).readRef(any()); - - Document document = new Document(); - WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs(); - lazyDbRefs.dbRefToToStringObjectMethodOverride = new ToStringObjectMethodOverrideLazyDbRefTarget(id, value); - converterSpy.write(lazyDbRefs, document); - - WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, document); - - assertThat(result.dbRefToToStringObjectMethodOverride).isNotNull(); - assertProxyIsResolved(result.dbRefToToStringObjectMethodOverride, false); - assertThat(result.dbRefToToStringObjectMethodOverride.toString()).isEqualTo(id + ":" + value); - assertProxyIsResolved(result.dbRefToToStringObjectMethodOverride, true); - } - - @Test // DATAMONGO-884 - public void callingToStringObjectMethodOnLazyLoadingDbrefShouldNotInitializeProxy() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id).append("value", value)).when(converterSpy).readRef(any()); - - Document document = new Document(); - WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs(); - lazyDbRefs.dbRefToPlainObject = new LazyDbRefTarget(id, value); - converterSpy.write(lazyDbRefs, document); - - WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, document); - - assertThat(result.dbRefToPlainObject).isNotNull(); - assertProxyIsResolved(result.dbRefToPlainObject, false); - - // calling Object#toString does not initialize the proxy. - String proxyString = result.dbRefToPlainObject.toString(); - assertThat(proxyString).isEqualTo("lazyDbRefTarget" + ":" + id + "$LazyLoadingProxy"); - assertProxyIsResolved(result.dbRefToPlainObject, false); - - // calling another method not declared on object triggers proxy initialization. - assertThat(result.dbRefToPlainObject.getValue()).isEqualTo(value); - assertProxyIsResolved(result.dbRefToPlainObject, true); - } - - @Test // DATAMONGO-884 - public void equalsObjectMethodOnLazyLoadingDbrefShouldNotInitializeProxy() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - - Document document = new Document(); - WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs(); - lazyDbRefs.dbRefToPlainObject = new LazyDbRefTarget(id, value); - lazyDbRefs.dbRefToToStringObjectMethodOverride = new ToStringObjectMethodOverrideLazyDbRefTarget(id, value); - converterSpy.write(lazyDbRefs, document); - - WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, document); - - assertThat(result.dbRefToPlainObject).isNotNull(); - assertProxyIsResolved(result.dbRefToPlainObject, false); - - assertThat(result.dbRefToPlainObject).isEqualTo(result.dbRefToPlainObject); - assertThat(result.dbRefToPlainObject).isNotEqualTo(null); - assertThat(result.dbRefToPlainObject).isNotEqualTo((Object) lazyDbRefs.dbRefToToStringObjectMethodOverride); - - assertProxyIsResolved(result.dbRefToPlainObject, false); - } - - @Test // DATAMONGO-884 - public void hashcodeObjectMethodOnLazyLoadingDbrefShouldNotInitializeProxy() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - - Document document = new Document(); - WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs(); - lazyDbRefs.dbRefToPlainObject = new LazyDbRefTarget(id, value); - lazyDbRefs.dbRefToToStringObjectMethodOverride = new ToStringObjectMethodOverrideLazyDbRefTarget(id, value); - converterSpy.write(lazyDbRefs, document); - - WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, document); - - assertThat(result.dbRefToPlainObject).isNotNull(); - assertProxyIsResolved(result.dbRefToPlainObject, false); - - assertThat(result.dbRefToPlainObject.hashCode()).isEqualTo(311365444); - - assertProxyIsResolved(result.dbRefToPlainObject, false); - } - - @Test // DATAMONGO-884 - public void lazyLoadingProxyForEqualsAndHashcodeObjectMethodOverridingDbref() { - - String id = "42"; - String value = "bubu"; - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id).append("value", value)).when(converterSpy).readRef(any()); - - Document document = new Document(); - WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs(); - lazyDbRefs.dbRefEqualsAndHashcodeObjectMethodOverride1 = new EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget( - id, value); - lazyDbRefs.dbRefEqualsAndHashcodeObjectMethodOverride2 = new EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget( - id, value); - converterSpy.write(lazyDbRefs, document); - - WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, document); - - assertProxyIsResolved(result.dbRefEqualsAndHashcodeObjectMethodOverride1, false); - assertThat(result.dbRefEqualsAndHashcodeObjectMethodOverride1).isNotNull(); - result.dbRefEqualsAndHashcodeObjectMethodOverride1.equals(null); - assertProxyIsResolved(result.dbRefEqualsAndHashcodeObjectMethodOverride1, true); - - assertProxyIsResolved(result.dbRefEqualsAndHashcodeObjectMethodOverride2, false); - assertThat(result.dbRefEqualsAndHashcodeObjectMethodOverride2).isNotNull(); - result.dbRefEqualsAndHashcodeObjectMethodOverride2.hashCode(); - assertProxyIsResolved(result.dbRefEqualsAndHashcodeObjectMethodOverride2, true); - } - - @Test // DATAMONGO-987 - public void shouldNotGenerateLazyLoadingProxyForNullValues() { - - Document document = new Document(); - ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); - lazyDbRefs.id = "42"; - converter.write(lazyDbRefs, document); - - ClassWithLazyDbRefs result = converter.read(ClassWithLazyDbRefs.class, document); - - assertThat(result.id).isEqualTo(lazyDbRefs.id); - assertThat(result.dbRefToInterface).isNull(); - assertThat(result.dbRefToConcreteCollection).isNull(); - assertThat(result.dbRefToConcreteType).isNull(); - assertThat(result.dbRefToConcreteTypeWithPersistenceConstructor).isNull(); - assertThat(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor).isNull(); - } - - @Test // DATAMONGO-1005 - public void shouldBeAbleToStoreDirectReferencesToSelf() { - - Document document = new Document(); - - ClassWithDbRefField o = new ClassWithDbRefField(); - o.id = "123"; - o.reference = o; - converter.write(o, document); - - ClassWithDbRefField found = converter.read(ClassWithDbRefField.class, document); - - assertThat(found).isNotNull(); - assertThat(found.reference).isEqualTo(found); - } - - @Test // DATAMONGO-1005 - public void shouldBeAbleToStoreNestedReferencesToSelf() { - - Document document = new Document(); - - ClassWithNestedDbRefField o = new ClassWithNestedDbRefField(); - o.id = "123"; - o.nested = new NestedReferenceHolder(); - o.nested.reference = o; - - converter.write(o, document); - - ClassWithNestedDbRefField found = converter.read(ClassWithNestedDbRefField.class, document); - - assertThat(found).isNotNull(); - assertThat(found.nested).isNotNull(); - assertThat(found.nested.reference).isEqualTo(found); - } - - @Test // DATAMONGO-1012 - public void shouldEagerlyResolveIdPropertyWithFieldAccess() { - - MongoPersistentEntity entity = mappingContext.getRequiredPersistentEntity(ClassWithLazyDbRefs.class); - MongoPersistentProperty property = entity.getRequiredPersistentProperty("dbRefToConcreteType"); - MongoPersistentEntity propertyEntity = mappingContext.getRequiredPersistentEntity(property); - - String idValue = new ObjectId().toString(); - DBRef dbRef = converter.toDBRef(new LazyDbRefTarget(idValue), property); - - Document object = new Document("dbRefToConcreteType", dbRef); - - ClassWithLazyDbRefs result = converter.read(ClassWithLazyDbRefs.class, object); - - PersistentPropertyAccessor accessor = propertyEntity.getPropertyAccessor(result.dbRefToConcreteType); - MongoPersistentProperty idProperty = mappingContext.getRequiredPersistentEntity(LazyDbRefTarget.class) - .getIdProperty(); - - assertThat(accessor.getProperty(idProperty)).isNotNull(); - assertProxyIsResolved(result.dbRefToConcreteType, false); - } - - @Test // DATAMONGO-1012 - public void shouldNotEagerlyResolveIdPropertyWithPropertyAccess() { - - MongoPersistentEntity entity = mappingContext.getRequiredPersistentEntity(ClassWithLazyDbRefs.class); - MongoPersistentProperty property = entity.getRequiredPersistentProperty("dbRefToConcreteTypeWithPropertyAccess"); - - String idValue = new ObjectId().toString(); - DBRef dbRef = converter.toDBRef(new LazyDbRefTargetPropertyAccess(idValue), property); - - Document object = new Document("dbRefToConcreteTypeWithPropertyAccess", dbRef); - - ClassWithLazyDbRefs result = converter.read(ClassWithLazyDbRefs.class, object); - - LazyDbRefTargetPropertyAccess proxy = result.dbRefToConcreteTypeWithPropertyAccess; - assertThat(ReflectionTestUtils.getField(proxy, "id")).isNull(); - assertProxyIsResolved(proxy, false); - } - - @Test // DATAMONGO-1076 - public void shouldNotTriggerResolvingOfLazyLoadedProxyWhenFinalizeMethodIsInvoked() throws Exception { - - MongoPersistentEntity entity = mappingContext - .getRequiredPersistentEntity(WithObjectMethodOverrideLazyDbRefs.class); - MongoPersistentProperty property = entity.getRequiredPersistentProperty("dbRefToPlainObject"); - - String idValue = new ObjectId().toString(); - DBRef dbRef = converter.toDBRef(new LazyDbRefTargetPropertyAccess(idValue), property); - - WithObjectMethodOverrideLazyDbRefs result = converter.read(WithObjectMethodOverrideLazyDbRefs.class, - new Document("dbRefToPlainObject", dbRef)); - - ReflectionTestUtils.invokeMethod(result.dbRefToPlainObject, "finalize"); - - assertProxyIsResolved(result.dbRefToPlainObject, false); - } - - @Test // DATAMONGO-1194 - public void shouldBulkFetchListOfReferences() { - - String id1 = "1"; - String id2 = "2"; - String value = "val"; - - MappingMongoConverter converterSpy = spy(converter); - doReturn( - Arrays.asList(new Document("_id", id1).append("value", value), new Document("_id", id2).append("value", value))) - .when(converterSpy).bulkReadRefs(anyList()); - - Document document = new Document(); - ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); - lazyDbRefs.dbRefToConcreteCollection = new ArrayList<>( - Arrays.asList(new LazyDbRefTarget(id1, value), new LazyDbRefTarget(id2, value))); - converterSpy.write(lazyDbRefs, document); - - ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, document); - - assertProxyIsResolved(result.dbRefToConcreteCollection, false); - assertThat(result.dbRefToConcreteCollection.get(0).getId()).isEqualTo(id1); - assertProxyIsResolved(result.dbRefToConcreteCollection, true); - assertThat(result.dbRefToConcreteCollection.get(1).getId()).isEqualTo(id2); - - verify(converterSpy, never()).readRef(Mockito.any(DBRef.class)); - } - - @Test // DATAMONGO-1666 - public void shouldBulkFetchSetOfReferencesForConstructorCreation() { - - String id1 = "1"; - String id2 = "2"; - String value = "val"; - - MappingMongoConverter converterSpy = spy(converter); - doReturn( - Arrays.asList(new Document("_id", id1).append("value", value), new Document("_id", id2).append("value", value))) - .when(converterSpy).bulkReadRefs(anyList()); - - Document document = new Document("dbRefToInterface", - Arrays.asList(new DBRef("lazyDbRefTarget", "1"), new DBRef("lazyDbRefTarget", "2"))); - - ClassWithDbRefSetConstructor result = converterSpy.read(ClassWithDbRefSetConstructor.class, document); - - assertThat(result.dbRefToInterface).isInstanceOf(Set.class); - - verify(converterSpy, never()).readRef(Mockito.any(DBRef.class)); - } - - @Test // DATAMONGO-1194 - public void shouldFallbackToOneByOneFetchingWhenElementsInListOfReferencesPointToDifferentCollections() { - - String id1 = "1"; - String id2 = "2"; - String value = "val"; - - MappingMongoConverter converterSpy = spy(converter); - doReturn(new Document("_id", id1).append("value", value)).doReturn(new Document("_id", id2).append("value", value)) - .when(converterSpy).readRef(Mockito.any(DBRef.class)); - - Document document = new Document(); - ClassWithLazyDbRefs lazyDbRefs = new ClassWithLazyDbRefs(); - lazyDbRefs.dbRefToConcreteCollection = new ArrayList<>( - Arrays.asList(new LazyDbRefTarget(id1, value), new SerializableLazyDbRefTarget(id2, value))); - converterSpy.write(lazyDbRefs, document); - - ClassWithLazyDbRefs result = converterSpy.read(ClassWithLazyDbRefs.class, document); - - assertProxyIsResolved(result.dbRefToConcreteCollection, false); - assertThat(result.dbRefToConcreteCollection.get(0).getId()).isEqualTo(id1); - assertProxyIsResolved(result.dbRefToConcreteCollection, true); - assertThat(result.dbRefToConcreteCollection.get(1).getId()).isEqualTo(id2); - - verify(converterSpy, times(2)).readRef(Mockito.any(DBRef.class)); - verify(converterSpy, never()).bulkReadRefs(anyList()); - } - - @Test // DATAMONGO-1194 - public void shouldBulkFetchMapOfReferences() { - - MapDBRefVal val1 = new MapDBRefVal(); - val1.id = BigInteger.ONE; - - MapDBRefVal val2 = new MapDBRefVal(); - val2.id = BigInteger.ZERO; - - MappingMongoConverter converterSpy = spy(converter); - doReturn(Arrays.asList(new Document("_id", val1.id), new Document("_id", val2.id))).when(converterSpy) - .bulkReadRefs(anyList()); - - Document document = new Document(); - MapDBRef mapDBRef = new MapDBRef(); - mapDBRef.map = new LinkedHashMap<>(); - mapDBRef.map.put("one", val1); - mapDBRef.map.put("two", val2); - - converterSpy.write(mapDBRef, document); - - MapDBRef result = converterSpy.read(MapDBRef.class, document); - - // assertProxyIsResolved(result.map, false); - assertThat(result.map.get("one").id).isEqualTo(val1.id); - // assertProxyIsResolved(result.map, true); - assertThat(result.map.get("two").id).isEqualTo(val2.id); - - verify(converterSpy, times(1)).bulkReadRefs(anyList()); - verify(converterSpy, never()).readRef(Mockito.any(DBRef.class)); - } - - @Test // DATAMONGO-1194 - public void shouldBulkFetchLazyMapOfReferences() { - - MapDBRefVal val1 = new MapDBRefVal(); - val1.id = BigInteger.ONE; - - MapDBRefVal val2 = new MapDBRefVal(); - val2.id = BigInteger.ZERO; - - MappingMongoConverter converterSpy = spy(converter); - doReturn(Arrays.asList(new Document("_id", val1.id), new Document("_id", val2.id))).when(converterSpy) - .bulkReadRefs(anyList()); - - Document document = new Document(); - MapDBRef mapDBRef = new MapDBRef(); - mapDBRef.lazyMap = new LinkedHashMap<>(); - mapDBRef.lazyMap.put("one", val1); - mapDBRef.lazyMap.put("two", val2); - - converterSpy.write(mapDBRef, document); - - MapDBRef result = converterSpy.read(MapDBRef.class, document); - - assertProxyIsResolved(result.lazyMap, false); - assertThat(result.lazyMap.get("one").id).isEqualTo(val1.id); - assertProxyIsResolved(result.lazyMap, true); - assertThat(result.lazyMap.get("two").id).isEqualTo(val2.id); - - verify(converterSpy, times(1)).bulkReadRefs(anyList()); - verify(converterSpy, never()).readRef(any()); - } - - private Object transport(Object result) { - return SerializationUtils.deserialize(SerializationUtils.serialize(result)); - } - - class MapDBRef { - @org.springframework.data.mongodb.core.mapping.DBRef Map map; - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) Map lazyMap; - } - - class MapDBRefVal { - BigInteger id; - } - - class PersonClient { - @org.springframework.data.mongodb.core.mapping.DBRef Person person; - } - - static class ClassWithLazyDbRefs { - - @Id String id; - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) List dbRefToInterface; - @org.springframework.data.mongodb.core.mapping.DBRef( - lazy = true) ArrayList dbRefToConcreteCollection; - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) LazyDbRefTarget dbRefToConcreteType; - @org.springframework.data.mongodb.core.mapping.DBRef( - lazy = true) LazyDbRefTargetPropertyAccess dbRefToConcreteTypeWithPropertyAccess; - @org.springframework.data.mongodb.core.mapping.DBRef( - lazy = true) LazyDbRefTargetWithPeristenceConstructor dbRefToConcreteTypeWithPersistenceConstructor; - @org.springframework.data.mongodb.core.mapping.DBRef( - lazy = true) LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor; - } - - static class ClassWithDbRefSetConstructor { - - final @org.springframework.data.mongodb.core.mapping.DBRef Set dbRefToInterface; - - public ClassWithDbRefSetConstructor(Set dbRefToInterface) { - this.dbRefToInterface = dbRefToInterface; - } - } - - static class SerializableClassWithLazyDbRefs implements Serializable { - - private static final long serialVersionUID = 1L; - - @org.springframework.data.mongodb.core.mapping.DBRef( - lazy = true) SerializableLazyDbRefTarget dbRefToSerializableTarget; - } - - static class LazyDbRefTarget implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id String id; - String value; - - public LazyDbRefTarget() { - this(null); - } - - public LazyDbRefTarget(String id) { - this(id, null); - } - - public LazyDbRefTarget(String id, String value) { - this.id = id; - this.value = value; - } - - public String getId() { - return id; - } - - public String getValue() { - return value; - } - } - - static class LazyDbRefTargetPropertyAccess implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id @AccessType(Type.PROPERTY) String id; - - public LazyDbRefTargetPropertyAccess(String id) { - this.id = id; - } - - public String getId() { - return id; - } - } - - @SuppressWarnings("serial") - static class LazyDbRefTargetWithPeristenceConstructor extends LazyDbRefTarget { - - boolean persistenceConstructorCalled; - - public LazyDbRefTargetWithPeristenceConstructor() {} - - @PersistenceConstructor - public LazyDbRefTargetWithPeristenceConstructor(String id, String value) { - super(id, value); - this.persistenceConstructorCalled = true; - } - - public LazyDbRefTargetWithPeristenceConstructor(Object id, Object value) { - super(id.toString(), value.toString()); - } - } - - @SuppressWarnings("serial") - static class LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor extends LazyDbRefTarget { - - boolean persistenceConstructorCalled; - - @PersistenceConstructor - public LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor(String id, String value) { - super(id, value); - this.persistenceConstructorCalled = true; - } - - public LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor(Object id, Object value) { - super(id.toString(), value.toString()); - } - } - - static class SerializableLazyDbRefTarget extends LazyDbRefTarget implements Serializable { - - public SerializableLazyDbRefTarget() {} - - public SerializableLazyDbRefTarget(String id, String value) { - super(id, value); - } - - private static final long serialVersionUID = 1L; - } - - static class ToStringObjectMethodOverrideLazyDbRefTarget extends LazyDbRefTarget { - - private static final long serialVersionUID = 1L; - - public ToStringObjectMethodOverrideLazyDbRefTarget() {} - - public ToStringObjectMethodOverrideLazyDbRefTarget(String id, String value) { - super(id, value); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return this.id + ":" + this.value; - } - } - - static class EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget extends LazyDbRefTarget { - - private static final long serialVersionUID = 1L; - - public EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget() {} - - public EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget(String id, String value) { - super(id, value); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((id == null) ? 0 : id.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget other = (EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget) obj; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; - if (value == null) { - if (other.value != null) - return false; - } else if (!value.equals(other.value)) - return false; - return true; - } - } - - static class WithObjectMethodOverrideLazyDbRefs { - - @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) LazyDbRefTarget dbRefToPlainObject; - @org.springframework.data.mongodb.core.mapping.DBRef( - lazy = true) ToStringObjectMethodOverrideLazyDbRefTarget dbRefToToStringObjectMethodOverride; - @org.springframework.data.mongodb.core.mapping.DBRef( - lazy = true) EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget dbRefEqualsAndHashcodeObjectMethodOverride2; - @org.springframework.data.mongodb.core.mapping.DBRef( - lazy = true) EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget dbRefEqualsAndHashcodeObjectMethodOverride1; - } - - class ClassWithDbRefField { - - String id; - @org.springframework.data.mongodb.core.mapping.DBRef ClassWithDbRefField reference; - } - - static class NestedReferenceHolder { - - String id; - @org.springframework.data.mongodb.core.mapping.DBRef ClassWithNestedDbRefField reference; - } - - static class ClassWithNestedDbRefField { - - String id; - NestedReferenceHolder nested; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverUnitTests.java deleted file mode 100644 index d7a2870477..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverUnitTests.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2016-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.Collections; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.DocumentTestUtils; - -import com.mongodb.DBRef; -import com.mongodb.client.FindIterable; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; - -/** - * Unit tests for {@link DefaultDbRefResolver}. - * - * @author Christoph Strobl - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class DefaultDbRefResolverUnitTests { - - @Mock MongoDatabaseFactory factoryMock; - @Mock MongoDatabase dbMock; - @Mock MongoCollection collectionMock; - @Mock FindIterable cursorMock; - private DefaultDbRefResolver resolver; - - @BeforeEach - void setUp() { - - when(factoryMock.getMongoDatabase()).thenReturn(dbMock); - when(dbMock.getCollection(anyString(), any(Class.class))).thenReturn(collectionMock); - when(collectionMock.find(any(Document.class))).thenReturn(cursorMock); - - resolver = new DefaultDbRefResolver(factoryMock); - } - - @Test // DATAMONGO-1194 - @SuppressWarnings("unchecked") - void bulkFetchShouldLoadDbRefsCorrectly() { - - DBRef ref1 = new DBRef("collection-1", new ObjectId()); - DBRef ref2 = new DBRef("collection-1", new ObjectId()); - - resolver.bulkFetch(Arrays.asList(ref1, ref2)); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Document.class); - - verify(collectionMock, times(1)).find(captor.capture()); - - Document _id = DocumentTestUtils.getAsDocument(captor.getValue(), "_id"); - Iterable $in = DocumentTestUtils.getTypedValue(_id, "$in", Iterable.class); - - assertThat($in).hasSize(2); - } - - @Test // DATAMONGO-1194 - void bulkFetchShouldThrowExceptionWhenUsingDifferntCollectionsWithinSetOfReferences() { - - DBRef ref1 = new DBRef("collection-1", new ObjectId()); - DBRef ref2 = new DBRef("collection-2", new ObjectId()); - - assertThatThrownBy(() -> resolver.bulkFetch(Arrays.asList(ref1, ref2))) - .isInstanceOf(InvalidDataAccessApiUsageException.class); - } - - @Test // DATAMONGO-1194 - void bulkFetchShouldReturnEarlyForEmptyLists() { - - resolver.bulkFetch(Collections.emptyList()); - - verify(collectionMock, never()).find(Mockito.any(Document.class)); - } - - @Test // DATAMONGO-1194 - void bulkFetchShouldRestoreOriginalOrder() { - - Document o1 = new Document("_id", new ObjectId()); - Document o2 = new Document("_id", new ObjectId()); - - DBRef ref1 = new DBRef("collection-1", o1.get("_id")); - DBRef ref2 = new DBRef("collection-1", o2.get("_id")); - - when(cursorMock.into(any())).then(invocation -> Arrays.asList(o2, o1)); - - assertThat(resolver.bulkFetch(Arrays.asList(ref1, ref2))).containsExactly(o1, o2); - } - - @Test // DATAMONGO-1765 - void bulkFetchContainsDuplicates() { - - Document document = new Document("_id", new ObjectId()); - - DBRef ref1 = new DBRef("collection-1", document.get("_id")); - DBRef ref2 = new DBRef("collection-1", document.get("_id")); - - when(cursorMock.into(any())).then(invocation -> Arrays.asList(document)); - - assertThat(resolver.bulkFetch(Arrays.asList(ref1, ref2))).containsExactly(document, document); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapperUnitTests.java deleted file mode 100644 index 5191ece7ab..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapperUnitTests.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2011-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.data.convert.ConfigurableTypeInformationMapper; -import org.springframework.data.convert.SimpleTypeInformationMapper; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.util.TypeInformation; - -/** - * Unit tests for {@link DefaultMongoTypeMapper}. - * - * @author Oliver Gierke - */ -public class DefaultMongoTypeMapperUnitTests { - - ConfigurableTypeInformationMapper configurableTypeInformationMapper; - SimpleTypeInformationMapper simpleTypeInformationMapper; - - DefaultMongoTypeMapper typeMapper; - - @BeforeEach - public void setUp() { - - configurableTypeInformationMapper = new ConfigurableTypeInformationMapper( - Collections.singletonMap(String.class, "1")); - simpleTypeInformationMapper = new SimpleTypeInformationMapper(); - - typeMapper = new DefaultMongoTypeMapper(); - } - - @Test - public void defaultInstanceWritesClasses() { - - writesTypeToField(new Document(), String.class, String.class.getName()); - } - - @Test - public void defaultInstanceReadsClasses() { - - Document document = new Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, String.class.getName()); - readsTypeFromField(document, String.class); - } - - @Test - public void writesMapKeyForType() { - - typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, - Arrays.asList(configurableTypeInformationMapper)); - - writesTypeToField(new Document(), String.class, "1"); - writesTypeToField(new Document(), Object.class, null); - } - - @Test - public void writesClassNamesForUnmappedValuesIfConfigured() { - - typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, - Arrays.asList(configurableTypeInformationMapper, simpleTypeInformationMapper)); - - writesTypeToField(new Document(), String.class, "1"); - writesTypeToField(new Document(), Object.class, Object.class.getName()); - } - - @Test - public void readsTypeForMapKey() { - - typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, - Arrays.asList(configurableTypeInformationMapper)); - - readsTypeFromField(new Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, "1"), String.class); - readsTypeFromField(new Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, "unmapped"), null); - } - - @Test - public void readsTypeLoadingClassesForUnmappedTypesIfConfigured() { - - typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, - Arrays.asList(configurableTypeInformationMapper, simpleTypeInformationMapper)); - - readsTypeFromField(new Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, "1"), String.class); - readsTypeFromField(new Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, Object.class.getName()), Object.class); - } - - @Test // DATAMONGO-709 - public void writesTypeRestrictionsCorrectly() { - - Document result = new Document(); - - typeMapper = new DefaultMongoTypeMapper(); - typeMapper.writeTypeRestrictions(result, Collections.> singleton(String.class)); - - Document typeInfo = DocumentTestUtils.getAsDocument(result, DefaultMongoTypeMapper.DEFAULT_TYPE_KEY); - List aliases = DocumentTestUtils.getAsDBList(typeInfo, "$in"); - assertThat(aliases).hasSize(1); - assertThat(aliases.get(0)).isEqualTo((Object) String.class.getName()); - } - - @Test - public void addsFullyQualifiedClassNameUnderDefaultKeyByDefault() { - writesTypeToField(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, new Document(), String.class); - } - - @Test - public void writesTypeToCustomFieldIfConfigured() { - typeMapper = new DefaultMongoTypeMapper("_custom"); - writesTypeToField("_custom", new Document(), String.class); - } - - @Test - public void doesNotWriteTypeInformationInCaseKeyIsSetToNull() { - typeMapper = new DefaultMongoTypeMapper(null); - writesTypeToField(null, new Document(), String.class); - } - - @Test - public void readsTypeFromDefaultKeyByDefault() { - readsTypeFromField(new Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, String.class.getName()), String.class); - } - - @Test - public void readsTypeFromCustomFieldConfigured() { - - typeMapper = new DefaultMongoTypeMapper("_custom"); - readsTypeFromField(new Document("_custom", String.class.getName()), String.class); - } - - @Test - public void returnsListForBasicDBLists() { - readsTypeFromField(new Document(), null); - } - - @Test - public void returnsNullIfNoTypeInfoInDocument() { - readsTypeFromField(new Document(), null); - readsTypeFromField(new Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, ""), null); - } - - @Test - public void returnsNullIfClassCannotBeLoaded() { - readsTypeFromField(new Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, "fooBar"), null); - } - - @Test - public void returnsNullIfTypeKeySetToNull() { - typeMapper = new DefaultMongoTypeMapper(null); - readsTypeFromField(new Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, String.class), null); - } - - @Test - public void returnsCorrectTypeKey() { - - assertThat(typeMapper.isTypeKey(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY)).isTrue(); - - typeMapper = new DefaultMongoTypeMapper("_custom"); - assertThat(typeMapper.isTypeKey("_custom")).isTrue(); - assertThat(typeMapper.isTypeKey(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY)).isFalse(); - - typeMapper = new DefaultMongoTypeMapper(null); - assertThat(typeMapper.isTypeKey("_custom")).isFalse(); - assertThat(typeMapper.isTypeKey(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY)).isFalse(); - } - - private void readsTypeFromField(Document document, Class type) { - - TypeInformation typeInfo = typeMapper.readType(document); - - if (type != null) { - assertThat(typeInfo).isNotNull(); - assertThat(typeInfo.getType()).isAssignableFrom(type); - } else { - assertThat(typeInfo).isNull(); - } - } - - private void writesTypeToField(String field, Document document, Class type) { - - typeMapper.writeType(type, document); - - if (field == null) { - assertThat(document.keySet().isEmpty()).isTrue(); - } else { - assertThat(document.containsKey(field)).isTrue(); - assertThat(document.get(field)).isEqualTo((Object) type.getName()); - } - } - - private void writesTypeToField(Document document, Class type, Object value) { - - typeMapper.writeType(type, document); - - if (value == null) { - assertThat(document.keySet().isEmpty()).isTrue(); - } else { - assertThat(document.containsKey(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY)).isTrue(); - assertThat(document.get(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY)).isEqualTo(value); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DocumentAccessorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DocumentAccessorUnitTests.java deleted file mode 100644 index 617bc91048..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DocumentAccessorUnitTests.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2013-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.BsonDocument; -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; - -import com.mongodb.BasicDBObject; - -/** - * Unit tests for {@link DocumentAccessor}. - * - * @author Oliver Gierke - */ -public class DocumentAccessorUnitTests { - - MongoMappingContext context = new MongoMappingContext(); - MongoPersistentEntity projectingTypeEntity = context.getRequiredPersistentEntity(ProjectingType.class); - MongoPersistentProperty fooProperty = projectingTypeEntity.getRequiredPersistentProperty("foo"); - - @Test // DATAMONGO-766 - public void putsNestedFieldCorrectly() { - - Document document = new Document(); - - DocumentAccessor accessor = new DocumentAccessor(document); - accessor.put(fooProperty, "FooBar"); - - Document aDocument = DocumentTestUtils.getAsDocument(document, "a"); - assertThat(aDocument.get("b")).isEqualTo((Object) "FooBar"); - } - - @Test // DATAMONGO-766 - public void getsNestedFieldCorrectly() { - - Document source = new Document("a", new Document("b", "FooBar")); - - DocumentAccessor accessor = new DocumentAccessor(source); - assertThat(accessor.get(fooProperty)).isEqualTo((Object) "FooBar"); - } - - @Test // DATAMONGO-766 - public void returnsNullForNonExistingFieldPath() { - - DocumentAccessor accessor = new DocumentAccessor(new Document()); - assertThat(accessor.get(fooProperty)).isNull(); - } - - @Test // DATAMONGO-766 - public void rejectsNonDocuments() { - assertThatIllegalArgumentException().isThrownBy(() -> new DocumentAccessor(new BsonDocument())); - } - - @Test // DATAMONGO-766 - public void rejectsNullDocument() { - assertThatIllegalArgumentException().isThrownBy(() -> new DocumentAccessor(null)); - } - - @Test // DATAMONGO-1335 - public void writesAllNestingsCorrectly() { - - MongoPersistentEntity entity = context.getRequiredPersistentEntity(TypeWithTwoNestings.class); - - Document target = new Document(); - - DocumentAccessor accessor = new DocumentAccessor(target); - accessor.put(entity.getRequiredPersistentProperty("id"), "id"); - accessor.put(entity.getRequiredPersistentProperty("b"), "b"); - accessor.put(entity.getRequiredPersistentProperty("c"), "c"); - - Document nestedA = DocumentTestUtils.getAsDocument(target, "a"); - - assertThat(nestedA).isNotNull(); - assertThat(nestedA.get("b")).isEqualTo((Object) "b"); - assertThat(nestedA.get("c")).isEqualTo((Object) "c"); - } - - @Test // DATAMONGO-1471 - public void exposesAvailabilityOfFields() { - - DocumentAccessor accessor = new DocumentAccessor(new Document("a", new BasicDBObject("c", "d"))); - MongoPersistentEntity entity = context.getRequiredPersistentEntity(ProjectingType.class); - - assertThat(accessor.hasValue(entity.getRequiredPersistentProperty("foo"))).isFalse(); - assertThat(accessor.hasValue(entity.getRequiredPersistentProperty("a"))).isTrue(); - assertThat(accessor.hasValue(entity.getRequiredPersistentProperty("name"))).isFalse(); - } - - static class ProjectingType { - - String name; - @Field("a.b") String foo; - NestedType a; - } - - static class NestedType { - String b; - String c; - } - - static class TypeWithTwoNestings { - - String id; - @Field("a.b") String b; - @Field("a.c") String c; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/GeoConvertersUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/GeoConvertersUnitTests.java deleted file mode 100644 index ce43febb5b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/GeoConvertersUnitTests.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2014-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.geo.Box; -import org.springframework.data.geo.Circle; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.geo.Polygon; -import org.springframework.data.mongodb.core.convert.GeoConverters.*; -import org.springframework.data.mongodb.core.geo.Sphere; -import org.springframework.data.mongodb.core.query.GeoCommand; - -/** - * Unit tests for {@link GeoConverters}. - * - * @author Thomas Darimont - * @author Oliver Gierke - * @author Christoph Strobl - * @since 1.5 - */ -public class GeoConvertersUnitTests { - - @Test // DATAMONGO-858 - public void convertsBoxToDocumentAndBackCorrectly() { - - Box box = new Box(new Point(1, 2), new Point(3, 4)); - - Document document = BoxToDocumentConverter.INSTANCE.convert(box); - Box result = DocumentToBoxConverter.INSTANCE.convert(document); - - assertThat(result).isEqualTo(box); - assertThat(result.getClass().equals(Box.class)).isTrue(); - } - - @Test // DATAMONGO-858 - public void convertsCircleToDocumentAndBackCorrectlyNeutralDistance() { - - Circle circle = new Circle(new Point(1, 2), 3); - - Document document = CircleToDocumentConverter.INSTANCE.convert(circle); - Circle result = DocumentToCircleConverter.INSTANCE.convert(document); - - assertThat(result).isEqualTo(circle); - } - - @Test // DATAMONGO-858 - public void convertsCircleToDocumentAndBackCorrectlyMilesDistance() { - - Distance radius = new Distance(3, Metrics.MILES); - Circle circle = new Circle(new Point(1, 2), radius); - - Document document = CircleToDocumentConverter.INSTANCE.convert(circle); - Circle result = DocumentToCircleConverter.INSTANCE.convert(document); - - assertThat(result).isEqualTo(circle); - assertThat(result.getRadius()).isEqualTo(radius); - } - - @Test // DATAMONGO-858 - public void convertsPolygonToDocumentAndBackCorrectly() { - - Polygon polygon = new Polygon(new Point(1, 2), new Point(2, 3), new Point(3, 4), new Point(5, 6)); - - Document document = PolygonToDocumentConverter.INSTANCE.convert(polygon); - Polygon result = DocumentToPolygonConverter.INSTANCE.convert(document); - - assertThat(result).isEqualTo(polygon); - assertThat(result.getClass().equals(Polygon.class)).isTrue(); - } - - @Test // DATAMONGO-858 - public void convertsSphereToDocumentAndBackCorrectlyWithNeutralDistance() { - - Sphere sphere = new Sphere(new Point(1, 2), 3); - - Document document = SphereToDocumentConverter.INSTANCE.convert(sphere); - Sphere result = DocumentToSphereConverter.INSTANCE.convert(document); - - assertThat(result).isEqualTo(sphere); - assertThat(result.getClass().equals(Sphere.class)).isTrue(); - } - - @Test // DATAMONGO-858 - public void convertsSphereToDocumentAndBackCorrectlyWithKilometerDistance() { - - Distance radius = new Distance(3, Metrics.KILOMETERS); - Sphere sphere = new Sphere(new Point(1, 2), radius); - - Document document = SphereToDocumentConverter.INSTANCE.convert(sphere); - Sphere result = DocumentToSphereConverter.INSTANCE.convert(document); - - assertThat(result).isEqualTo(sphere); - assertThat(result.getRadius()).isEqualTo(radius); - assertThat(result.getClass().equals(Sphere.class)).isTrue(); - } - - @Test // DATAMONGO-858 - public void convertsPointToListAndBackCorrectly() { - - Point point = new Point(1, 2); - - Document document = PointToDocumentConverter.INSTANCE.convert(point); - Point result = DocumentToPointConverter.INSTANCE.convert(document); - - assertThat(result).isEqualTo(point); - assertThat(result.getClass().equals(Point.class)).isTrue(); - } - - @Test // DATAMONGO-858 - public void convertsGeoCommandToDocumentCorrectly() { - - Box box = new Box(new double[] { 1, 2 }, new double[] { 3, 4 }); - GeoCommand cmd = new GeoCommand(box); - - Document document = GeoCommandToDocumentConverter.INSTANCE.convert(cmd); - - assertThat(document).isNotNull(); - - List boxObject = (List) document.get("$box"); - - assertThat(boxObject) - .isEqualTo((Object) Arrays.asList(GeoConverters.toList(box.getFirst()), GeoConverters.toList(box.getSecond()))); - } - - @Test // DATAMONGO-1607 - public void convertsPointCorrectlyWhenUsingNonDoubleForCoordinates() { - - assertThat(DocumentToPointConverter.INSTANCE.convert(new Document().append("x", 1L).append("y", 2L))) - .isEqualTo(new Point(1, 2)); - } - - @Test // DATAMONGO-1607 - public void convertsCircleCorrectlyWhenUsingNonDoubleForCoordinates() { - - Document circle = new Document(); - circle.put("center", new Document().append("x", 1).append("y", 2)); - circle.put("radius", 3L); - - assertThat(DocumentToCircleConverter.INSTANCE.convert(circle)) - .isEqualTo(new Circle(new Point(1, 2), new Distance(3))); - } - - @Test // DATAMONGO-1607 - public void convertsSphereCorrectlyWhenUsingNonDoubleForCoordinates() { - - Document sphere = new Document(); - sphere.put("center", new Document().append("x", 1).append("y", 2)); - sphere.put("radius", 3L); - - assertThat(DocumentToSphereConverter.INSTANCE.convert(sphere)) - .isEqualTo(new Sphere(new Point(1, 2), new Distance(3))); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/GeoJsonConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/GeoJsonConverterUnitTests.java deleted file mode 100644 index 8e24ee1093..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/GeoJsonConverterUnitTests.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright 2015-2021 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.convert; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.convert.GeoConverters.DocumentToGeoJsonLineStringConverter; -import org.springframework.data.mongodb.core.convert.GeoConverters.DocumentToGeoJsonMultiLineStringConverter; -import org.springframework.data.mongodb.core.convert.GeoConverters.DocumentToGeoJsonMultiPointConverter; -import org.springframework.data.mongodb.core.convert.GeoConverters.DocumentToGeoJsonMultiPolygonConverter; -import org.springframework.data.mongodb.core.convert.GeoConverters.DocumentToGeoJsonPointConverter; -import org.springframework.data.mongodb.core.convert.GeoConverters.DocumentToGeoJsonPolygonConverter; -import org.springframework.data.mongodb.core.convert.GeoConverters.GeoJsonToDocumentConverter; -import org.springframework.data.mongodb.core.geo.GeoJson; -import org.springframework.data.mongodb.core.geo.GeoJsonGeometryCollection; -import org.springframework.data.mongodb.core.geo.GeoJsonLineString; -import org.springframework.data.mongodb.core.geo.GeoJsonMultiLineString; -import org.springframework.data.mongodb.core.geo.GeoJsonMultiPoint; -import org.springframework.data.mongodb.core.geo.GeoJsonMultiPolygon; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.geo.GeoJsonPolygon; -import org.springframework.data.mongodb.test.util.BasicDbListBuilder; - -import com.mongodb.BasicDBList; - -/** - * @author Christoph Strobl - */ -@RunWith(Suite.class) -@SuiteClasses({ GeoJsonConverterUnitTests.GeoJsonToDocumentConverterUnitTests.class, - GeoJsonConverterUnitTests.DocumentToGeoJsonPointConverterUnitTests.class, - GeoJsonConverterUnitTests.DocumentToGeoJsonPolygonConverterUnitTests.class, - GeoJsonConverterUnitTests.DocumentToGeoJsonLineStringConverterUnitTests.class, - GeoJsonConverterUnitTests.DocumentToGeoJsonMultiPolygonConverterUnitTests.class, - GeoJsonConverterUnitTests.DocumentToGeoJsonMultiLineStringConverterUnitTests.class, - GeoJsonConverterUnitTests.DocumentToGeoJsonMultiPointConverterUnitTests.class }) -public class GeoJsonConverterUnitTests { - - /* - * --- GeoJson - */ - static final GeoJsonPoint SINGLE_POINT = new GeoJsonPoint(100, 50); - - static final Point POINT_0 = new Point(0, 0); - static final Point POINT_1 = new Point(100, 0); - static final Point POINT_2 = new Point(100, 100); - static final Point POINT_3 = new Point(0, 100); - - static final Point INNER_POINT_0 = new Point(10, 10); - static final Point INNER_POINT_1 = new Point(90, 10); - static final Point INNER_POINT_2 = new Point(90, 90); - static final Point INNER_POINT_3 = new Point(10, 90); - - static final GeoJsonMultiPoint MULTI_POINT = new GeoJsonMultiPoint(POINT_0, POINT_2, POINT_3); - static final GeoJsonLineString LINE_STRING = new GeoJsonLineString(POINT_0, POINT_1, POINT_2); - @SuppressWarnings("unchecked") static final GeoJsonMultiLineString MULTI_LINE_STRING = new GeoJsonMultiLineString( - Arrays.asList(POINT_0, POINT_1, POINT_2), Arrays.asList(POINT_3, POINT_0)); - static final GeoJsonPolygon POLYGON = new GeoJsonPolygon(POINT_0, POINT_1, POINT_2, POINT_3, POINT_0); - static final GeoJsonPolygon POLYGON_WITH_2_RINGS = POLYGON.withInnerRing(INNER_POINT_0, INNER_POINT_1, INNER_POINT_2, - INNER_POINT_3, INNER_POINT_0); - static final GeoJsonMultiPolygon MULTI_POLYGON = new GeoJsonMultiPolygon(Arrays.asList(POLYGON)); - static final GeoJsonGeometryCollection GEOMETRY_COLLECTION = new GeoJsonGeometryCollection( - Arrays.> asList(SINGLE_POINT, POLYGON)); - /* - * -- GeoJson Documents - */ - - // Point - static final BasicDBList SINGE_POINT_CORDS = new BasicDbListBuilder() // - .add(SINGLE_POINT.getX()) // - .add(SINGLE_POINT.getY()) // - .get(); // - static final Document SINGLE_POINT_DOC = new Document() // - .append("type", "Point") // - .append("coordinates", SINGE_POINT_CORDS);// - - // MultiPoint - static final BasicDBList MULTI_POINT_CORDS = new BasicDbListBuilder() // - .add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) // - .add(new BasicDbListBuilder().add(POINT_2.getX()).add(POINT_2.getY()).get()) // - .add(new BasicDbListBuilder().add(POINT_3.getX()).add(POINT_3.getY()).get()) // - .get(); - static final Document MULTI_POINT_DOC = new Document() // - .append("type", "MultiPoint")// - .append("coordinates", MULTI_POINT_CORDS);// - - // Polygon - static final BasicDBList POLYGON_OUTER_CORDS = new BasicDbListBuilder() // - .add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) // - .add(new BasicDbListBuilder().add(POINT_1.getX()).add(POINT_1.getY()).get()) // - .add(new BasicDbListBuilder().add(POINT_2.getX()).add(POINT_2.getY()).get()) // - .add(new BasicDbListBuilder().add(POINT_3.getX()).add(POINT_3.getY()).get()) // - .add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) // - .get(); - - static final BasicDBList POLYGON_INNER_CORDS = new BasicDbListBuilder() // - .add(new BasicDbListBuilder().add(INNER_POINT_0.getX()).add(INNER_POINT_0.getY()).get()) // - .add(new BasicDbListBuilder().add(INNER_POINT_1.getX()).add(INNER_POINT_1.getY()).get()) // - .add(new BasicDbListBuilder().add(INNER_POINT_2.getX()).add(INNER_POINT_2.getY()).get()) // - .add(new BasicDbListBuilder().add(INNER_POINT_3.getX()).add(INNER_POINT_3.getY()).get()) // - .add(new BasicDbListBuilder().add(INNER_POINT_0.getX()).add(INNER_POINT_0.getY()).get()) // - .get(); - - static final BasicDBList POLYGON_CORDS = new BasicDbListBuilder().add(POLYGON_OUTER_CORDS).get(); - static final Document POLYGON_DOC = new Document() // - .append("type", "Polygon") // - .append("coordinates", POLYGON_CORDS); // - - static final BasicDBList POLYGON_WITH_2_RINGS_CORDS = new BasicDbListBuilder().add(POLYGON_OUTER_CORDS) - .add(POLYGON_INNER_CORDS).get(); - static final Document POLYGON_WITH_2_RINGS_DOC = new Document() // - .append("type", "Polygon") // - .append("coordinates", POLYGON_WITH_2_RINGS_CORDS); - - // LineString - static final BasicDBList LINE_STRING_CORDS_0 = new BasicDbListBuilder() // - .add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) // - .add(new BasicDbListBuilder().add(POINT_1.getX()).add(POINT_1.getY()).get()) // - .add(new BasicDbListBuilder().add(POINT_2.getX()).add(POINT_2.getY()).get()) // - .get(); - static final BasicDBList LINE_STRING_CORDS_1 = new BasicDbListBuilder() // - .add(new BasicDbListBuilder().add(POINT_3.getX()).add(POINT_3.getY()).get()) // - .add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) // - .get(); - static final Document LINE_STRING_DOC = new Document().append("type", "LineString").append("coordinates", - LINE_STRING_CORDS_0); - - // MultiLineString - static final BasicDBList MUILT_LINE_STRING_CORDS = new BasicDbListBuilder() // - .add(LINE_STRING_CORDS_0) // - .add(LINE_STRING_CORDS_1) // - .get(); - static final Document MULTI_LINE_STRING_DOC = new Document().append("type", "MultiLineString").append("coordinates", - MUILT_LINE_STRING_CORDS); - - // MultiPolygoin - static final BasicDBList MULTI_POLYGON_CORDS = new BasicDbListBuilder().add(POLYGON_CORDS).get(); - static final Document MULTI_POLYGON_DOC = new Document().append("type", "MultiPolygon").append("coordinates", - MULTI_POLYGON_CORDS); - - // GeometryCollection - static final BasicDBList GEOMETRY_COLLECTION_GEOMETRIES = new BasicDbListBuilder() // - .add(SINGLE_POINT_DOC)// - .add(POLYGON_DOC)// - .get(); - static final Document GEOMETRY_COLLECTION_DOC = new Document().append("type", "GeometryCollection") - .append("geometries", GEOMETRY_COLLECTION_GEOMETRIES); - - /** - * @author Christoph Strobl - */ - public static class DocumentToGeoJsonPolygonConverterUnitTests { - - DocumentToGeoJsonPolygonConverter converter = DocumentToGeoJsonPolygonConverter.INSTANCE; - - @Test // DATAMONGO-1137 - public void shouldConvertDboCorrectly() { - assertThat(converter.convert(POLYGON_DOC)).isEqualTo(POLYGON); - } - - @Test // DATAMONGO-1137 - public void shouldReturnNullWhenConvertIsGivenNull() { - assertThat(converter.convert(null)).isNull(); - } - - @Test // DATAMONGO-1137 - public void shouldThrowExceptionWhenTypeDoesNotMatchPolygon() { - assertThatIllegalArgumentException().isThrownBy(() -> converter.convert(new Document("type", "YouDontKonwMe"))); - } - - @Test // DATAMONGO-1399 - public void shouldConvertDboWithMultipleRingsCorrectly() { - assertThat(converter.convert(POLYGON_WITH_2_RINGS_DOC)).isEqualTo(POLYGON_WITH_2_RINGS); - } - - } - - /** - * @author Christoph Strobl - */ - public static class DocumentToGeoJsonPointConverterUnitTests { - - DocumentToGeoJsonPointConverter converter = DocumentToGeoJsonPointConverter.INSTANCE; - - @Test // DATAMONGO-1137 - public void shouldConvertDboCorrectly() { - assertThat(converter.convert(SINGLE_POINT_DOC)).isEqualTo(SINGLE_POINT); - } - - @Test // DATAMONGO-1137 - public void shouldReturnNullWhenConvertIsGivenNull() { - assertThat(converter.convert(null)).isNull(); - } - - @Test // DATAMONGO-1137 - public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() { - - assertThatIllegalArgumentException().isThrownBy(() -> converter.convert(new Document("type", "YouDontKonwMe"))); - } - } - - /** - * @author Christoph Strobl - */ - public static class DocumentToGeoJsonLineStringConverterUnitTests { - - DocumentToGeoJsonLineStringConverter converter = DocumentToGeoJsonLineStringConverter.INSTANCE; - - @Test // DATAMONGO-1137 - public void shouldConvertDboCorrectly() { - assertThat(converter.convert(LINE_STRING_DOC)).isEqualTo(LINE_STRING); - } - - @Test // DATAMONGO-1137 - public void shouldReturnNullWhenConvertIsGivenNull() { - assertThat(converter.convert(null)).isNull(); - } - - @Test // DATAMONGO-1137 - public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() { - assertThatIllegalArgumentException().isThrownBy(() -> converter.convert(new Document("type", "YouDontKonwMe"))); - } - } - - /** - * @author Christoph Strobl - */ - public static class DocumentToGeoJsonMultiLineStringConverterUnitTests { - - DocumentToGeoJsonMultiLineStringConverter converter = DocumentToGeoJsonMultiLineStringConverter.INSTANCE; - - @Test // DATAMONGO-1137 - public void shouldConvertDboCorrectly() { - assertThat(converter.convert(MULTI_LINE_STRING_DOC)).isEqualTo(MULTI_LINE_STRING); - } - - @Test // DATAMONGO-1137 - public void shouldReturnNullWhenConvertIsGivenNull() { - assertThat(converter.convert(null)).isNull(); - } - - @Test // DATAMONGO-1137 - public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() { - assertThatIllegalArgumentException().isThrownBy(() -> converter.convert(new Document("type", "YouDontKonwMe"))); - } - } - - /** - * @author Christoph Strobl - */ - public static class DocumentToGeoJsonMultiPointConverterUnitTests { - - DocumentToGeoJsonMultiPointConverter converter = DocumentToGeoJsonMultiPointConverter.INSTANCE; - - @Test // DATAMONGO-1137 - public void shouldConvertDboCorrectly() { - assertThat(converter.convert(MULTI_POINT_DOC)).isEqualTo(MULTI_POINT); - } - - @Test // DATAMONGO-1137 - public void shouldReturnNullWhenConvertIsGivenNull() { - assertThat(converter.convert(null)).isNull(); - } - - @Test // DATAMONGO-1137 - public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() { - assertThatIllegalArgumentException().isThrownBy(() -> converter.convert(new Document("type", "YouDontKonwMe"))); - } - } - - /** - * @author Christoph Strobl - */ - public static class DocumentToGeoJsonMultiPolygonConverterUnitTests { - - DocumentToGeoJsonMultiPolygonConverter converter = DocumentToGeoJsonMultiPolygonConverter.INSTANCE; - - @Test // DATAMONGO-1137 - public void shouldConvertDboCorrectly() { - assertThat(converter.convert(MULTI_POLYGON_DOC)).isEqualTo(MULTI_POLYGON); - } - - @Test // DATAMONGO-1137 - public void shouldReturnNullWhenConvertIsGivenNull() { - assertThat(converter.convert(null)).isNull(); - } - - @Test // DATAMONGO-1137 - public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() { - assertThatIllegalArgumentException().isThrownBy(() -> converter.convert(new Document("type", "YouDontKonwMe"))); - } - } - - /** - * @author Christoph Strobl - */ - public static class GeoJsonToDocumentConverterUnitTests { - - GeoJsonToDocumentConverter converter = GeoJsonToDocumentConverter.INSTANCE; - - // DATAMONGO-1135 - public void convertShouldReturnNullWhenGivenNull() { - assertThat(converter.convert(null)).isNull(); - } - - @Test // DATAMONGO-1135 - public void shouldConvertGeoJsonPointCorrectly() { - assertThat(converter.convert(SINGLE_POINT)).isEqualTo(SINGLE_POINT_DOC); - } - - @Test // DATAMONGO-1135 - public void shouldConvertGeoJsonPolygonCorrectly() { - assertThat(converter.convert(POLYGON)).isEqualTo(POLYGON_DOC); - } - - @Test // DATAMONGO-1137 - public void shouldConvertGeoJsonLineStringCorrectly() { - assertThat(converter.convert(LINE_STRING)).isEqualTo(LINE_STRING_DOC); - } - - @Test // DATAMONGO-1137 - public void shouldConvertGeoJsonMultiLineStringCorrectly() { - assertThat(converter.convert(MULTI_LINE_STRING)).isEqualTo(MULTI_LINE_STRING_DOC); - } - - @Test // DATAMONGO-1137 - public void shouldConvertGeoJsonMultiPointCorrectly() { - assertThat(converter.convert(MULTI_POINT)).isEqualTo(MULTI_POINT_DOC); - } - - @Test // DATAMONGO-1137 - public void shouldConvertGeoJsonMultiPolygonCorrectly() { - assertThat(converter.convert(MULTI_POLYGON)).isEqualTo(MULTI_POLYGON_DOC); - } - - @Test // DATAMONGO-1137 - public void shouldConvertGeometryCollectionCorrectly() { - assertThat(converter.convert(GEOMETRY_COLLECTION)).isEqualTo(GEOMETRY_COLLECTION_DOC); - } - - @Test // DATAMONGO-1399 - public void shouldConvertGeoJsonPolygonWithMultipleRingsCorrectly() { - assertThat(converter.convert(POLYGON_WITH_2_RINGS)).isEqualTo(POLYGON_WITH_2_RINGS_DOC); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingInterceptorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingInterceptorUnitTests.java deleted file mode 100644 index 5b758136e4..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingInterceptorUnitTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2016-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.dao.DataAccessException; -import org.springframework.dao.support.PersistenceExceptionTranslator; -import org.springframework.data.mongodb.LazyLoadingException; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.LazyLoadingInterceptor; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; - -import com.mongodb.DBRef; - -/** - * Unit tests for {@link LazyLoadingInterceptor}. - * - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -public class LazyLoadingInterceptorUnitTests { - - @Mock MongoPersistentProperty propertyMock; - @Mock DBRef dbrefMock; - @Mock DbRefResolverCallback callbackMock; - - @Test // DATAMONGO-1437 - public void shouldPreserveCauseForNonTranslatableExceptions() throws Throwable { - - NullPointerException npe = new NullPointerException("Some Exception we did not think about."); - when(callbackMock.resolve(propertyMock)).thenThrow(npe); - - assertThatExceptionOfType(LazyLoadingException.class).isThrownBy(() -> { - new LazyLoadingInterceptor(propertyMock, dbrefMock, new NullExceptionTranslator(), callbackMock).intercept(null, - LazyLoadingProxy.class.getMethod("getTarget"), null, null); - }).withCause(npe); - } - - static class NullExceptionTranslator implements PersistenceExceptionTranslator { - - @Override - public DataAccessException translateExceptionIfPossible(RuntimeException ex) { - return null; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingTestUtils.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingTestUtils.java deleted file mode 100644 index 5006459fc8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingTestUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2013-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; - -import org.springframework.aop.framework.Advised; -import org.springframework.cglib.proxy.Factory; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.LazyLoadingInterceptor; -import org.springframework.test.util.ReflectionTestUtils; - -/** - * Utility class to test proxy handling for lazy loading. - * - * @author Oliver Gierke - */ -public class LazyLoadingTestUtils { - - /** - * Asserts that the given repository is resolved (expected is {@literal true}) and the value is non-{@literal null} or - * unresolved (expected is {@literal false}) and the value is {@literal null}. - * - * @param target - * @param expected - */ - public static void assertProxyIsResolved(Object target, boolean expected) { - - LazyLoadingInterceptor interceptor = extractInterceptor(target); - assertThat(ReflectionTestUtils.getField(interceptor, "resolved")).isEqualTo((Object) expected); - - if (expected) { - assertThat(ReflectionTestUtils.getField(interceptor, "result")).isNotNull(); - } else { - assertThat(ReflectionTestUtils.getField(interceptor, "result")).isNull(); - - } - } - - private static LazyLoadingInterceptor extractInterceptor(Object proxy) { - return (LazyLoadingInterceptor) (proxy instanceof Advised ? ((Advised) proxy).getAdvisors()[0].getAdvice() - : ((Factory) proxy).getCallback(0)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterTests.java deleted file mode 100644 index 66c2cc9822..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterTests.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2018-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.any; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; - -/** - * Integration tests for {@link MappingMongoConverter}. - * - * @author Christoph Strobl - */ -@ExtendWith(MongoClientExtension.class) -public class MappingMongoConverterTests { - - public static final String DATABASE = "mapping-converter-tests"; - - static @Client MongoClient client; - - MongoDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(client, DATABASE); - - MappingMongoConverter converter; - MongoMappingContext mappingContext; - DbRefResolver dbRefResolver; - - @BeforeEach - public void setUp() { - - MongoDatabase database = client.getDatabase(DATABASE); - - database.getCollection("samples").deleteMany(new Document()); - database.getCollection("java-time-types").deleteMany(new Document()); - - dbRefResolver = spy(new DefaultDbRefResolver(factory)); - - mappingContext = new MongoMappingContext(); - mappingContext.setInitialEntitySet(new HashSet<>( - Arrays.asList(WithLazyDBRefAsConstructorArg.class, WithLazyDBRef.class, WithJavaTimeTypes.class))); - mappingContext.setAutoIndexCreation(false); - mappingContext.afterPropertiesSet(); - - converter = new MappingMongoConverter(dbRefResolver, mappingContext); - converter.afterPropertiesSet(); - } - - @Test // DATAMONGO-2004 - public void resolvesLazyDBRefOnAccess() { - - client.getDatabase(DATABASE).getCollection("samples") - .insertMany(Arrays.asList(new Document("_id", "sample-1").append("value", "one"), - new Document("_id", "sample-2").append("value", "two"))); - - Document source = new Document("_id", "id-1").append("lazyList", - Arrays.asList(new com.mongodb.DBRef("samples", "sample-1"), new com.mongodb.DBRef("samples", "sample-2"))); - - WithLazyDBRef target = converter.read(WithLazyDBRef.class, source); - - verify(dbRefResolver).resolveDbRef(any(), isNull(), any(), any()); - verifyNoMoreInteractions(dbRefResolver); - - assertThat(target.lazyList).isInstanceOf(LazyLoadingProxy.class); - assertThat(target.getLazyList()).contains(new Sample("sample-1", "one"), new Sample("sample-2", "two")); - - verify(dbRefResolver).bulkFetch(any()); - } - - @Test // DATAMONGO-2004 - public void resolvesLazyDBRefConstructorArgOnAccess() { - - client.getDatabase(DATABASE).getCollection("samples") - .insertMany(Arrays.asList(new Document("_id", "sample-1").append("value", "one"), - new Document("_id", "sample-2").append("value", "two"))); - - Document source = new Document("_id", "id-1").append("lazyList", - Arrays.asList(new com.mongodb.DBRef("samples", "sample-1"), new com.mongodb.DBRef("samples", "sample-2"))); - - WithLazyDBRefAsConstructorArg target = converter.read(WithLazyDBRefAsConstructorArg.class, source); - - verify(dbRefResolver).resolveDbRef(any(), isNull(), any(), any()); - verifyNoMoreInteractions(dbRefResolver); - - assertThat(target.lazyList).isInstanceOf(LazyLoadingProxy.class); - assertThat(target.getLazyList()).contains(new Sample("sample-1", "one"), new Sample("sample-2", "two")); - - verify(dbRefResolver).bulkFetch(any()); - } - - @Test // DATAMONGO-2400 - public void readJavaTimeValuesWrittenViaCodec() { - - configureConverterWithNativeJavaTimeCodec(); - MongoCollection mongoCollection = client.getDatabase(DATABASE).getCollection("java-time-types"); - - Instant now = Instant.now().truncatedTo(ChronoUnit.MILLIS); - WithJavaTimeTypes source = WithJavaTimeTypes.withJavaTimeTypes(now); - source.id = "id-1"; - - mongoCollection.insertOne(source.toDocument()); - - assertThat(converter.read(WithJavaTimeTypes.class, mongoCollection.find(new Document("_id", source.id)).first())) - .isEqualTo(source); - } - - void configureConverterWithNativeJavaTimeCodec() { - - converter = new MappingMongoConverter(dbRefResolver, mappingContext); - converter.setCustomConversions( - MongoCustomConversions.create(MongoConverterConfigurationAdapter::useNativeDriverJavaTimeCodecs)); - converter.afterPropertiesSet(); - } - - public static class WithLazyDBRef { - - @Id String id; - @DBRef(lazy = true) List lazyList; - - public List getLazyList() { - return lazyList; - } - } - - public static class WithLazyDBRefAsConstructorArg { - - @Id String id; - @DBRef(lazy = true) List lazyList; - - public WithLazyDBRefAsConstructorArg(String id, List lazyList) { - - this.id = id; - this.lazyList = lazyList; - } - - public List getLazyList() { - return lazyList; - } - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - static class Sample { - - @Id String id; - String value; - } - - @Data - static class WithJavaTimeTypes { - - @Id String id; - LocalDate localDate; - LocalTime localTime; - LocalDateTime localDateTime; - - static WithJavaTimeTypes withJavaTimeTypes(Instant instant) { - - WithJavaTimeTypes instance = new WithJavaTimeTypes(); - - instance.localDate = LocalDate.from(instant.atZone(ZoneId.of("CET"))); - instance.localTime = LocalTime.from(instant.atZone(ZoneId.of("CET"))); - instance.localDateTime = LocalDateTime.from(instant.atZone(ZoneId.of("CET"))); - - return instance; - } - - Document toDocument() { - return new Document("_id", id).append("localDate", localDate).append("localTime", localTime) - .append("localDateTime", localDateTime); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java deleted file mode 100644 index a3836fd8b3..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ /dev/null @@ -1,2974 +0,0 @@ -/* - * Copyright 2011-2021 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.convert; - -import static java.time.ZoneId.*; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.DocumentTestUtils.*; - -import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.net.URL; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.*; - -import org.assertj.core.api.Assertions; -import org.bson.types.Code; -import org.bson.types.Decimal128; -import org.bson.types.ObjectId; -import org.joda.time.LocalDate; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.beans.ConversionNotSupportedException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.StaticApplicationContext; -import org.springframework.core.convert.ConverterNotFoundException; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.annotation.Transient; -import org.springframework.data.annotation.TypeAlias; -import org.springframework.data.convert.CustomConversions; -import org.springframework.data.convert.ReadingConverter; -import org.springframework.data.convert.WritingConverter; -import org.springframework.data.geo.Box; -import org.springframework.data.geo.Circle; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.geo.Polygon; -import org.springframework.data.geo.Shape; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.mapping.callback.EntityCallbacks; -import org.springframework.data.mapping.model.MappingInstantiationException; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.convert.DocumentAccessorUnitTests.NestedType; -import org.springframework.data.mongodb.core.convert.DocumentAccessorUnitTests.ProjectingType; -import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.ClassWithMapUsingEnumAsKey.FooBarEnum; -import org.springframework.data.mongodb.core.geo.Sphere; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.FieldType; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.mongodb.core.mapping.PersonPojoStringId; -import org.springframework.data.mongodb.core.mapping.TextScore; -import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback; -import org.springframework.data.util.ClassTypeInformation; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mongodb.BasicDBList; -import com.mongodb.BasicDBObject; -import com.mongodb.DBObject; -import com.mongodb.DBRef; - -/** - * Unit tests for {@link MappingMongoConverter}. - * - * @author Oliver Gierke - * @author Patrik Wasik - * @author Christoph Strobl - * @author Mark Paluch - * @author Roman Puchkovskiy - * @author Heesu Jung - */ -@ExtendWith(MockitoExtension.class) -class MappingMongoConverterUnitTests { - - private MappingMongoConverter converter; - private MongoMappingContext mappingContext; - @Mock ApplicationContext context; - @Mock DbRefResolver resolver; - - @BeforeEach - void beforeEach() { - - MongoCustomConversions conversions = new MongoCustomConversions(); - - mappingContext = new MongoMappingContext(); - mappingContext.setApplicationContext(context); - mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); - mappingContext.afterPropertiesSet(); - - mappingContext.getPersistentEntity(Address.class); - - converter = new MappingMongoConverter(resolver, mappingContext); - converter.setCustomConversions(conversions); - converter.afterPropertiesSet(); - } - - @Test - void convertsAddressCorrectly() { - - Address address = new Address(); - address.city = "New York"; - address.street = "Broadway"; - - org.bson.Document document = new org.bson.Document(); - - converter.write(address, document); - - assertThat(document.get("city").toString()).isEqualTo("New York"); - assertThat(document.get("street").toString()).isEqualTo("Broadway"); - } - - @Test - void convertsJodaTimeTypesCorrectly() { - - converter = new MappingMongoConverter(resolver, mappingContext); - converter.afterPropertiesSet(); - - Person person = new Person(); - person.birthDate = new LocalDate(); - - org.bson.Document document = new org.bson.Document(); - converter.write(person, document); - - assertThat(document.get("birthDate")).isInstanceOf(Date.class); - - Person result = converter.read(Person.class, document); - assertThat(result.birthDate).isNotNull(); - } - - @Test - void convertsCustomTypeOnConvertToMongoType() { - - converter = new MappingMongoConverter(resolver, mappingContext); - converter.afterPropertiesSet(); - - LocalDate date = new LocalDate(); - converter.convertToMongoType(date); - } - - @Test // DATAMONGO-130 - void writesMapTypeCorrectly() { - - Map map = Collections.singletonMap(Locale.US, "Foo"); - - org.bson.Document document = new org.bson.Document(); - converter.write(map, document); - - assertThat(document.get(Locale.US.toString()).toString()).isEqualTo("Foo"); - } - - @Test // DATAMONGO-130 - void readsMapWithCustomKeyTypeCorrectly() { - - org.bson.Document mapObject = new org.bson.Document(Locale.US.toString(), "Value"); - org.bson.Document document = new org.bson.Document("map", mapObject); - - ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, document); - assertThat(result.map.get(Locale.US)).isEqualTo("Value"); - } - - @Test // DATAMONGO-128 - void usesDocumentsStoredTypeIfSubtypeOfRequest() { - - org.bson.Document document = new org.bson.Document(); - document.put("birthDate", new LocalDate()); - document.put(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, Person.class.getName()); - - assertThat(converter.read(Contact.class, document)).isInstanceOf(Person.class); - } - - @Test // DATAMONGO-128 - void ignoresDocumentsStoredTypeIfCompletelyDifferentTypeRequested() { - - org.bson.Document document = new org.bson.Document(); - document.put("birthDate", new LocalDate()); - document.put(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, Person.class.getName()); - - assertThat(converter.read(BirthDateContainer.class, document)).isInstanceOf(BirthDateContainer.class); - } - - @Test - void writesTypeDiscriminatorIntoRootObject() { - - Person person = new Person(); - - org.bson.Document result = new org.bson.Document(); - converter.write(person, result); - - assertThat(result.containsKey(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY)).isTrue(); - assertThat(result.get(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY).toString()).isEqualTo(Person.class.getName()); - } - - @Test // DATAMONGO-136 - void writesEnumsCorrectly() { - - ClassWithEnumProperty value = new ClassWithEnumProperty(); - value.sampleEnum = SampleEnum.FIRST; - - org.bson.Document result = new org.bson.Document(); - converter.write(value, result); - - assertThat(result.get("sampleEnum")).isInstanceOf(String.class); - assertThat(result.get("sampleEnum").toString()).isEqualTo("FIRST"); - } - - @Test // DATAMONGO-209 - void writesEnumCollectionCorrectly() { - - ClassWithEnumProperty value = new ClassWithEnumProperty(); - value.enums = Arrays.asList(SampleEnum.FIRST); - - org.bson.Document result = new org.bson.Document(); - converter.write(value, result); - - assertThat(result.get("enums")).isInstanceOf(List.class); - - List enums = (List) result.get("enums"); - assertThat(enums.size()).isEqualTo(1); - assertThat(enums.get(0)).isEqualTo("FIRST"); - } - - @Test // DATAMONGO-136 - void readsEnumsCorrectly() { - org.bson.Document document = new org.bson.Document("sampleEnum", "FIRST"); - ClassWithEnumProperty result = converter.read(ClassWithEnumProperty.class, document); - - assertThat(result.sampleEnum).isEqualTo(SampleEnum.FIRST); - } - - @Test // DATAMONGO-209 - void readsEnumCollectionsCorrectly() { - - BasicDBList enums = new BasicDBList(); - enums.add("FIRST"); - org.bson.Document document = new org.bson.Document("enums", enums); - - ClassWithEnumProperty result = converter.read(ClassWithEnumProperty.class, document); - - assertThat(result.enums).isInstanceOf(List.class); - assertThat(result.enums.size()).isEqualTo(1); - assertThat(result.enums).contains(SampleEnum.FIRST); - } - - @Test // DATAMONGO-144 - void considersFieldNameWhenWriting() { - - Person person = new Person(); - person.firstname = "Oliver"; - - org.bson.Document result = new org.bson.Document(); - converter.write(person, result); - - assertThat(result.containsKey("foo")).isTrue(); - assertThat(result.containsKey("firstname")).isFalse(); - } - - @Test // DATAMONGO-144 - void considersFieldNameWhenReading() { - - org.bson.Document document = new org.bson.Document("foo", "Oliver"); - Person result = converter.read(Person.class, document); - - assertThat(result.firstname).isEqualTo("Oliver"); - } - - @Test - void resolvesNestedComplexTypeForConstructorCorrectly() { - - org.bson.Document address = new org.bson.Document("street", "110 Southwark Street"); - address.put("city", "London"); - - BasicDBList addresses = new BasicDBList(); - addresses.add(address); - - org.bson.Document person = new org.bson.Document("firstname", "Oliver"); - person.put("addresses", addresses); - - Person result = converter.read(Person.class, person); - assertThat(result.addresses).isNotNull(); - } - - @Test // DATAMONGO-145 - void writesCollectionWithInterfaceCorrectly() { - - Person person = new Person(); - person.firstname = "Oliver"; - - CollectionWrapper wrapper = new CollectionWrapper(); - wrapper.contacts = Arrays.asList((Contact) person); - - org.bson.Document document = new org.bson.Document(); - converter.write(wrapper, document); - - Object result = document.get("contacts"); - assertThat(result).isInstanceOf(List.class); - List contacts = (List) result; - org.bson.Document personDocument = (org.bson.Document) contacts.get(0); - assertThat(personDocument.get("foo").toString()).isEqualTo("Oliver"); - assertThat((String) personDocument.get(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY)).isEqualTo(Person.class.getName()); - } - - @Test // DATAMONGO-145 - void readsCollectionWithInterfaceCorrectly() { - - org.bson.Document person = new org.bson.Document(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, Person.class.getName()); - person.put("foo", "Oliver"); - - BasicDBList contacts = new BasicDBList(); - contacts.add(person); - - CollectionWrapper result = converter.read(CollectionWrapper.class, new org.bson.Document("contacts", contacts)); - assertThat(result.contacts).isNotNull(); - assertThat(result.contacts.size()).isEqualTo(1); - Contact contact = result.contacts.get(0); - assertThat(contact).isInstanceOf(Person.class); - assertThat(((Person) contact).firstname).isEqualTo("Oliver"); - } - - @Test - void convertsLocalesOutOfTheBox() { - LocaleWrapper wrapper = new LocaleWrapper(); - wrapper.locale = Locale.US; - - org.bson.Document document = new org.bson.Document(); - converter.write(wrapper, document); - - Object localeField = document.get("locale"); - assertThat(localeField).isInstanceOf(String.class); - assertThat(localeField).isEqualTo("en_US"); - - LocaleWrapper read = converter.read(LocaleWrapper.class, document); - assertThat(read.locale).isEqualTo(Locale.US); - } - - @Test // DATAMONGO-161 - void readsNestedMapsCorrectly() { - - Map secondLevel = new HashMap(); - secondLevel.put("key1", "value1"); - secondLevel.put("key2", "value2"); - - Map> firstLevel = new HashMap>(); - firstLevel.put("level1", secondLevel); - firstLevel.put("level2", secondLevel); - - ClassWithNestedMaps maps = new ClassWithNestedMaps(); - maps.nestedMaps = new LinkedHashMap>>(); - maps.nestedMaps.put("afield", firstLevel); - - org.bson.Document document = new org.bson.Document(); - converter.write(maps, document); - - ClassWithNestedMaps result = converter.read(ClassWithNestedMaps.class, document); - Map>> nestedMap = result.nestedMaps; - assertThat(nestedMap).isNotNull(); - assertThat(nestedMap.get("afield")).isEqualTo(firstLevel); - } - - @Test // DATACMNS-42, DATAMONGO-171 - void writesClassWithBigDecimal() { - - BigDecimalContainer container = new BigDecimalContainer(); - container.value = BigDecimal.valueOf(2.5d); - container.map = Collections.singletonMap("foo", container.value); - - org.bson.Document document = new org.bson.Document(); - converter.write(container, document); - - assertThat(document.get("value")).isInstanceOf(String.class); - assertThat((String) document.get("value")).isEqualTo("2.5"); - assertThat(((org.bson.Document) document.get("map")).get("foo")).isInstanceOf(String.class); - } - - @Test // DATACMNS-42, DATAMONGO-171 - void readsClassWithBigDecimal() { - - org.bson.Document document = new org.bson.Document("value", "2.5"); - document.put("map", new org.bson.Document("foo", "2.5")); - - BasicDBList list = new BasicDBList(); - list.add("2.5"); - document.put("collection", list); - BigDecimalContainer result = converter.read(BigDecimalContainer.class, document); - - assertThat(result.value).isEqualTo(BigDecimal.valueOf(2.5d)); - assertThat(result.map.get("foo")).isEqualTo(BigDecimal.valueOf(2.5d)); - assertThat(result.collection.get(0)).isEqualTo(BigDecimal.valueOf(2.5d)); - } - - @Test - void writesNestedCollectionsCorrectly() { - - CollectionWrapper wrapper = new CollectionWrapper(); - wrapper.strings = Arrays.asList(Arrays.asList("Foo")); - - org.bson.Document document = new org.bson.Document(); - converter.write(wrapper, document); - - Object outerStrings = document.get("strings"); - assertThat(outerStrings).isInstanceOf(List.class); - - List typedOuterString = (List) outerStrings; - assertThat(typedOuterString.size()).isEqualTo(1); - } - - @Test // DATAMONGO-192 - void readsEmptySetsCorrectly() { - - Person person = new Person(); - person.addresses = Collections.emptySet(); - - org.bson.Document document = new org.bson.Document(); - converter.write(person, document); - converter.read(Person.class, document); - } - - @Test - void convertsObjectIdStringsToObjectIdCorrectly() { - PersonPojoStringId p1 = new PersonPojoStringId("1234567890", "Text-1"); - org.bson.Document doc1 = new org.bson.Document(); - - converter.write(p1, doc1); - assertThat(doc1.get("_id")).isInstanceOf(String.class); - - PersonPojoStringId p2 = new PersonPojoStringId(new ObjectId().toString(), "Text-1"); - org.bson.Document doc2 = new org.bson.Document(); - - converter.write(p2, doc2); - assertThat(doc2.get("_id")).isInstanceOf(ObjectId.class); - } - - @Test // DATAMONGO-207 - void convertsCustomEmptyMapCorrectly() { - - org.bson.Document map = new org.bson.Document(); - org.bson.Document wrapper = new org.bson.Document("map", map); - - ClassWithSortedMap result = converter.read(ClassWithSortedMap.class, wrapper); - - assertThat(result).isInstanceOf(ClassWithSortedMap.class); - assertThat(result.map).isInstanceOf(SortedMap.class); - } - - @Test // DATAMONGO-211 - void maybeConvertHandlesNullValuesCorrectly() { - assertThat(converter.convertToMongoType(null)).isNull(); - } - - @Test // DATAMONGO-1509 - void writesGenericTypeCorrectly() { - - GenericType
type = new GenericType
(); - type.content = new Address(); - type.content.city = "London"; - - org.bson.Document result = new org.bson.Document(); - converter.write(type, result); - - org.bson.Document content = (org.bson.Document) result.get("content"); - assertTypeHint(content, Address.class); - assertThat(content.get("city")).isNotNull(); - } - - @Test - void readsGenericTypeCorrectly() { - - org.bson.Document address = new org.bson.Document("_class", Address.class.getName()); - address.put("city", "London"); - - GenericType result = converter.read(GenericType.class, new org.bson.Document("content", address)); - assertThat(result.content).isInstanceOf(Address.class); - } - - @Test // DATAMONGO-228 - void writesNullValuesForMaps() { - - ClassWithMapProperty foo = new ClassWithMapProperty(); - foo.map = Collections.singletonMap(Locale.US, null); - - org.bson.Document result = new org.bson.Document(); - converter.write(foo, result); - - Object map = result.get("map"); - assertThat(map).isInstanceOf(org.bson.Document.class); - assertThat(((org.bson.Document) map).keySet()).contains("en_US"); - } - - @Test - void writesBigIntegerIdCorrectly() { - - ClassWithBigIntegerId foo = new ClassWithBigIntegerId(); - foo.id = BigInteger.valueOf(23L); - - org.bson.Document result = new org.bson.Document(); - converter.write(foo, result); - - assertThat(result.get("_id")).isInstanceOf(String.class); - } - - @Test - public void convertsObjectsIfNecessary() { - - ObjectId id = new ObjectId(); - assertThat(converter.convertToMongoType(id)).isEqualTo(id); - } - - @Test // DATAMONGO-235 - void writesMapOfListsCorrectly() { - - ClassWithMapProperty input = new ClassWithMapProperty(); - input.mapOfLists = Collections.singletonMap("Foo", Arrays.asList("Bar")); - - org.bson.Document result = new org.bson.Document(); - converter.write(input, result); - - Object field = result.get("mapOfLists"); - assertThat(field).isInstanceOf(org.bson.Document.class); - - org.bson.Document map = (org.bson.Document) field; - Object foo = map.get("Foo"); - assertThat(foo).isInstanceOf(List.class); - - List value = (List) foo; - assertThat(value.size()).isEqualTo(1); - assertThat(value.get(0)).isEqualTo("Bar"); - } - - @Test // DATAMONGO-235 - void readsMapListValuesCorrectly() { - - BasicDBList list = new BasicDBList(); - list.add("Bar"); - org.bson.Document source = new org.bson.Document("mapOfLists", new org.bson.Document("Foo", list)); - - ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, source); - assertThat(result.mapOfLists).isNotNull(); - } - - @Test // DATAMONGO-235 - void writesMapsOfObjectsCorrectly() { - - ClassWithMapProperty input = new ClassWithMapProperty(); - input.mapOfObjects = new HashMap(); - input.mapOfObjects.put("Foo", Arrays.asList("Bar")); - - org.bson.Document result = new org.bson.Document(); - converter.write(input, result); - - Object field = result.get("mapOfObjects"); - assertThat(field).isInstanceOf(org.bson.Document.class); - - org.bson.Document map = (org.bson.Document) field; - Object foo = map.get("Foo"); - assertThat(foo).isInstanceOf(BasicDBList.class); - - BasicDBList value = (BasicDBList) foo; - assertThat(value.size()).isEqualTo(1); - assertThat(value.get(0)).isEqualTo("Bar"); - } - - @Test // DATAMONGO-235 - void readsMapOfObjectsListValuesCorrectly() { - - BasicDBList list = new BasicDBList(); - list.add("Bar"); - org.bson.Document source = new org.bson.Document("mapOfObjects", new org.bson.Document("Foo", list)); - - ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, source); - assertThat(result.mapOfObjects).isNotNull(); - } - - @Test // DATAMONGO-245 - void readsMapListNestedValuesCorrectly() { - - BasicDBList list = new BasicDBList(); - list.add(new org.bson.Document("Hello", "World")); - org.bson.Document source = new org.bson.Document("mapOfObjects", new org.bson.Document("Foo", list)); - - ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, source); - Object firstObjectInFoo = ((List) result.mapOfObjects.get("Foo")).get(0); - assertThat(firstObjectInFoo).isInstanceOf(Map.class); - assertThat(((Map) firstObjectInFoo).get("Hello")).isEqualTo("World"); - } - - @Test // DATAMONGO-245 - void readsMapDoublyNestedValuesCorrectly() { - - org.bson.Document nested = new org.bson.Document(); - org.bson.Document doubly = new org.bson.Document(); - doubly.append("Hello", "World"); - nested.append("nested", doubly); - org.bson.Document source = new org.bson.Document("mapOfObjects", new org.bson.Document("Foo", nested)); - - ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, source); - Object foo = result.mapOfObjects.get("Foo"); - assertThat(foo).isInstanceOf(Map.class); - Object doublyNestedObject = ((Map) foo).get("nested"); - assertThat(doublyNestedObject).isInstanceOf(Map.class); - assertThat(((Map) doublyNestedObject).get("Hello")).isEqualTo("World"); - } - - @Test // DATAMONGO-245 - void readsMapListDoublyNestedValuesCorrectly() { - - BasicDBList list = new BasicDBList(); - org.bson.Document nested = new org.bson.Document(); - org.bson.Document doubly = new org.bson.Document(); - doubly.append("Hello", "World"); - nested.append("nested", doubly); - list.add(nested); - org.bson.Document source = new org.bson.Document("mapOfObjects", new org.bson.Document("Foo", list)); - - ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, source); - Object firstObjectInFoo = ((List) result.mapOfObjects.get("Foo")).get(0); - assertThat(firstObjectInFoo).isInstanceOf(Map.class); - Object doublyNestedObject = ((Map) firstObjectInFoo).get("nested"); - assertThat(doublyNestedObject).isInstanceOf(Map.class); - assertThat(((Map) doublyNestedObject).get("Hello")).isEqualTo("World"); - } - - @Test // DATAMONGO-259 - void writesListOfMapsCorrectly() { - - Map map = Collections.singletonMap("Foo", Locale.ENGLISH); - - CollectionWrapper wrapper = new CollectionWrapper(); - wrapper.listOfMaps = new ArrayList>(); - wrapper.listOfMaps.add(map); - - org.bson.Document result = new org.bson.Document(); - converter.write(wrapper, result); - - List list = (List) result.get("listOfMaps"); - assertThat(list).isNotNull(); - assertThat(list.size()).isEqualTo(1); - - org.bson.Document document = (org.bson.Document) list.get(0); - assertThat(document.containsKey("Foo")).isTrue(); - assertThat((String) document.get("Foo")).isEqualTo(Locale.ENGLISH.toString()); - } - - @Test // DATAMONGO-259 - void readsListOfMapsCorrectly() { - - org.bson.Document map = new org.bson.Document("Foo", "en"); - - BasicDBList list = new BasicDBList(); - list.add(map); - - org.bson.Document wrapperSource = new org.bson.Document("listOfMaps", list); - - CollectionWrapper wrapper = converter.read(CollectionWrapper.class, wrapperSource); - - assertThat(wrapper.listOfMaps).isNotNull(); - assertThat(wrapper.listOfMaps.size()).isEqualTo(1); - assertThat(wrapper.listOfMaps.get(0)).isNotNull(); - assertThat(wrapper.listOfMaps.get(0).get("Foo")).isEqualTo(Locale.ENGLISH); - } - - @Test // DATAMONGO-259 - void writesPlainMapOfCollectionsCorrectly() { - - Map> map = Collections.singletonMap("Foo", Arrays.asList(Locale.US)); - org.bson.Document result = new org.bson.Document(); - converter.write(map, result); - - assertThat(result.containsKey("Foo")).isTrue(); - assertThat(result.get("Foo")).isNotNull(); - assertThat(result.get("Foo")).isInstanceOf(BasicDBList.class); - - BasicDBList list = (BasicDBList) result.get("Foo"); - - assertThat(list.size()).isEqualTo(1); - assertThat(list.get(0)).isEqualTo(Locale.US.toString()); - } - - @Test // DATAMONGO-285 - @SuppressWarnings({ "unchecked", "rawtypes" }) - void testSaveMapWithACollectionAsValue() { - - Map keyValues = new HashMap(); - keyValues.put("string", "hello"); - List list = new ArrayList(); - list.add("ping"); - list.add("pong"); - keyValues.put("list", list); - - org.bson.Document document = new org.bson.Document(); - converter.write(keyValues, document); - - Map keyValuesFromMongo = converter.read(Map.class, document); - - assertThat(keyValuesFromMongo.size()).isEqualTo(keyValues.size()); - assertThat(keyValuesFromMongo.get("string")).isEqualTo(keyValues.get("string")); - assertThat(List.class.isAssignableFrom(keyValuesFromMongo.get("list").getClass())).isTrue(); - List listFromMongo = (List) keyValuesFromMongo.get("list"); - assertThat(listFromMongo.size()).isEqualTo(list.size()); - assertThat(listFromMongo.get(0)).isEqualTo(list.get(0)); - assertThat(listFromMongo.get(1)).isEqualTo(list.get(1)); - } - - @Test // DATAMONGO-309 - @SuppressWarnings({ "unchecked" }) - void writesArraysAsMapValuesCorrectly() { - - ClassWithMapProperty wrapper = new ClassWithMapProperty(); - wrapper.mapOfObjects = new HashMap(); - wrapper.mapOfObjects.put("foo", new String[] { "bar" }); - - org.bson.Document result = new org.bson.Document(); - converter.write(wrapper, result); - - Object mapObject = result.get("mapOfObjects"); - assertThat(mapObject).isInstanceOf(org.bson.Document.class); - - org.bson.Document map = (org.bson.Document) mapObject; - Object valueObject = map.get("foo"); - assertThat(valueObject).isInstanceOf(BasicDBList.class); - - List list = (List) valueObject; - assertThat(list.size()).isEqualTo(1); - assertThat(list).contains((Object) "bar"); - } - - @Test // DATAMONGO-324 - void writesDocumentCorrectly() { - - org.bson.Document document = new org.bson.Document(); - document.put("foo", "bar"); - - org.bson.Document result = new org.bson.Document(); - - converter.write(document, result); - - result.remove(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY); - assertThat(document).isEqualTo(result); - } - - @Test // DATAMONGO-324 - void readsDocumentCorrectly() { - - org.bson.Document document = new org.bson.Document(); - document.put("foo", "bar"); - - org.bson.Document result = converter.read(org.bson.Document.class, document); - - assertThat(result).isEqualTo(document); - } - - @Test // DATAMONGO-329 - void writesMapAsGenericFieldCorrectly() { - - Map> objectToSave = new HashMap>(); - objectToSave.put("test", new A("testValue")); - - A>> a = new A>>(objectToSave); - org.bson.Document result = new org.bson.Document(); - - converter.write(a, result); - - assertThat(result.get(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY)).isEqualTo(A.class.getName()); - assertThat(result.get("valueType")).isEqualTo(HashMap.class.getName()); - - org.bson.Document object = (org.bson.Document) result.get("value"); - assertThat(object).isNotNull(); - - org.bson.Document inner = (org.bson.Document) object.get("test"); - assertThat(inner).isNotNull(); - assertThat(inner.get(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY)).isEqualTo(A.class.getName()); - assertThat(inner.get("valueType")).isEqualTo(String.class.getName()); - assertThat(inner.get("value")).isEqualTo("testValue"); - } - - @Test - void writesIntIdCorrectly() { - - ClassWithIntId value = new ClassWithIntId(); - value.id = 5; - - org.bson.Document result = new org.bson.Document(); - converter.write(value, result); - - assertThat(result.get("_id")).isEqualTo(5); - } - - @Test // DATAMONGO-368 - @SuppressWarnings("unchecked") - void writesNullValuesForCollection() { - - CollectionWrapper wrapper = new CollectionWrapper(); - wrapper.contacts = Arrays.asList(new Person(), null); - - org.bson.Document result = new org.bson.Document(); - converter.write(wrapper, result); - - Object contacts = result.get("contacts"); - assertThat(contacts).isInstanceOf(Collection.class); - assertThat(((Collection) contacts).size()).isEqualTo(2); - assertThat((Collection) contacts).containsNull(); - } - - @Test // DATAMONGO-379 - void considersDefaultingExpressionsAtConstructorArguments() { - - org.bson.Document document = new org.bson.Document("foo", "bar"); - document.put("foobar", 2.5); - - DefaultedConstructorArgument result = converter.read(DefaultedConstructorArgument.class, document); - assertThat(result.bar).isEqualTo(-1); - } - - @Test // DATAMONGO-379 - void usesDocumentFieldIfReferencedInAtValue() { - - org.bson.Document document = new org.bson.Document("foo", "bar"); - document.put("something", 37); - document.put("foobar", 2.5); - - DefaultedConstructorArgument result = converter.read(DefaultedConstructorArgument.class, document); - assertThat(result.bar).isEqualTo(37); - } - - @Test // DATAMONGO-379 - void rejectsNotFoundConstructorParameterForPrimitiveType() { - - org.bson.Document document = new org.bson.Document("foo", "bar"); - - assertThatThrownBy(() -> converter.read(DefaultedConstructorArgument.class, document)) - .isInstanceOf(MappingInstantiationException.class); - } - - @Test // DATAMONGO-358 - void writesListForObjectPropertyCorrectly() { - - Attribute attribute = new Attribute(); - attribute.key = "key"; - attribute.value = Arrays.asList("1", "2"); - - Item item = new Item(); - item.attributes = Arrays.asList(attribute); - - org.bson.Document result = new org.bson.Document(); - - converter.write(item, result); - - Item read = converter.read(Item.class, result); - assertThat(read.attributes.size()).isEqualTo(1); - assertThat(read.attributes.get(0).key).isEqualTo(attribute.key); - assertThat(read.attributes.get(0).value).isInstanceOf(Collection.class); - - @SuppressWarnings("unchecked") - Collection values = (Collection) read.attributes.get(0).value; - - assertThat(values.size()).isEqualTo(2); - assertThat(values).contains("1", "2"); - } - - @Test // DATAMONGO-380 - void rejectsMapWithKeyContainingDotsByDefault() { - assertThatExceptionOfType(MappingException.class) - .isThrownBy(() -> converter.write(Collections.singletonMap("foo.bar", "foobar"), new org.bson.Document())); - } - - @Test // DATAMONGO-380 - void escapesDotInMapKeysIfReplacementConfigured() { - - converter.setMapKeyDotReplacement("~"); - - org.bson.Document document = new org.bson.Document(); - converter.write(Collections.singletonMap("foo.bar", "foobar"), document); - - assertThat((String) document.get("foo~bar")).isEqualTo("foobar"); - assertThat(document.containsKey("foo.bar")).isFalse(); - } - - @Test // DATAMONGO-380 - @SuppressWarnings("unchecked") - void unescapesDotInMapKeysIfReplacementConfigured() { - - converter.setMapKeyDotReplacement("~"); - - org.bson.Document document = new org.bson.Document("foo~bar", "foobar"); - Map result = converter.read(Map.class, document); - - assertThat(result.get("foo.bar")).isEqualTo("foobar"); - assertThat(result.containsKey("foobar")).isFalse(); - } - - @Test // DATAMONGO-382 - @Disabled("mongo3 - no longer supported") - void convertsSetToBasicDBList() { - - Address address = new Address(); - address.city = "London"; - address.street = "Foo"; - - Object result = converter.convertToMongoType(Collections.singleton(address), ClassTypeInformation.OBJECT); - assertThat(result).isInstanceOf(List.class); - - Set readResult = converter.read(Set.class, (org.bson.Document) result); - assertThat(readResult.size()).isEqualTo(1); - assertThat(readResult.iterator().next()).isInstanceOf(Address.class); - } - - @Test // DATAMONGO-402 - void readsMemberClassCorrectly() { - - org.bson.Document document = new org.bson.Document("inner", new org.bson.Document("value", "FOO!")); - - Outer outer = converter.read(Outer.class, document); - assertThat(outer.inner).isNotNull(); - assertThat(outer.inner.value).isEqualTo("FOO!"); - assertSyntheticFieldValueOf(outer.inner, outer); - } - - @Test // DATAMONGO-458 - void readEmptyCollectionIsModifiable() { - - org.bson.Document document = new org.bson.Document("contactsSet", new BasicDBList()); - CollectionWrapper wrapper = converter.read(CollectionWrapper.class, document); - - assertThat(wrapper.contactsSet).isNotNull(); - wrapper.contactsSet.add(new Contact() {}); - } - - @Test // DATAMONGO-424 - void readsPlainDBRefObject() { - - DBRef dbRef = new DBRef("foo", 2); - org.bson.Document document = new org.bson.Document("ref", dbRef); - - DBRefWrapper result = converter.read(DBRefWrapper.class, document); - assertThat(result.ref).isEqualTo(dbRef); - } - - @Test // DATAMONGO-424 - void readsCollectionOfDBRefs() { - - DBRef dbRef = new DBRef("foo", 2); - BasicDBList refs = new BasicDBList(); - refs.add(dbRef); - - org.bson.Document document = new org.bson.Document("refs", refs); - - DBRefWrapper result = converter.read(DBRefWrapper.class, document); - assertThat(result.refs).hasSize(1); - assertThat(result.refs).contains(dbRef); - } - - @Test // DATAMONGO-424 - void readsDBRefMap() { - - DBRef dbRef = mock(DBRef.class); - org.bson.Document refMap = new org.bson.Document("foo", dbRef); - org.bson.Document document = new org.bson.Document("refMap", refMap); - - DBRefWrapper result = converter.read(DBRefWrapper.class, document); - - assertThat(result.refMap.entrySet()).hasSize(1); - assertThat(result.refMap.values()).contains(dbRef); - } - - @Test // DATAMONGO-424 - @SuppressWarnings({ "rawtypes", "unchecked" }) - void resolvesDBRefMapValue() { - - when(resolver.fetch(Mockito.any(DBRef.class))).thenReturn(new org.bson.Document()); - DBRef dbRef = mock(DBRef.class); - - org.bson.Document refMap = new org.bson.Document("foo", dbRef); - org.bson.Document document = new org.bson.Document("personMap", refMap); - - DBRefWrapper result = converter.read(DBRefWrapper.class, document); - - assertThat(result.personMap.entrySet()).hasSize(1); - assertThat(result.personMap.values()).anyMatch(Person.class::isInstance); - } - - @Test // DATAMONGO-462 - void writesURLsAsStringOutOfTheBox() throws Exception { - - URLWrapper wrapper = new URLWrapper(); - wrapper.url = new URL("https://springsource.org"); - org.bson.Document sink = new org.bson.Document(); - - converter.write(wrapper, sink); - - assertThat(sink.get("url")).isEqualTo("https://springsource.org"); - } - - @Test // DATAMONGO-462 - void readsURLFromStringOutOfTheBox() throws Exception { - org.bson.Document document = new org.bson.Document("url", "https://springsource.org"); - URLWrapper result = converter.read(URLWrapper.class, document); - assertThat(result.url).isEqualTo(new URL("https://springsource.org")); - } - - @Test // DATAMONGO-485 - void writesComplexIdCorrectly() { - - ComplexId id = new ComplexId(); - id.innerId = 4711L; - - ClassWithComplexId entity = new ClassWithComplexId(); - entity.complexId = id; - - org.bson.Document document = new org.bson.Document(); - converter.write(entity, document); - - Object idField = document.get("_id"); - assertThat(idField).isNotNull(); - assertThat(idField).isInstanceOf(org.bson.Document.class); - assertThat(((org.bson.Document) idField).get("innerId")).isEqualTo(4711L); - } - - @Test // DATAMONGO-485 - void readsComplexIdCorrectly() { - - org.bson.Document innerId = new org.bson.Document("innerId", 4711L); - org.bson.Document entity = new org.bson.Document("_id", innerId); - - ClassWithComplexId result = converter.read(ClassWithComplexId.class, entity); - - assertThat(result.complexId).isNotNull(); - assertThat(result.complexId.innerId).isEqualTo(4711L); - } - - @Test // DATAMONGO-489 - void readsArraysAsMapValuesCorrectly() { - - BasicDBList list = new BasicDBList(); - list.add("Foo"); - list.add("Bar"); - - org.bson.Document map = new org.bson.Document("key", list); - org.bson.Document wrapper = new org.bson.Document("mapOfStrings", map); - - ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, wrapper); - assertThat(result.mapOfStrings).isNotNull(); - - String[] values = result.mapOfStrings.get("key"); - assertThat(values).isNotNull(); - assertThat(values).hasSize(2); - } - - @Test // DATAMONGO-497 - void readsEmptyCollectionIntoConstructorCorrectly() { - - org.bson.Document source = new org.bson.Document("attributes", new BasicDBList()); - - TypWithCollectionConstructor result = converter.read(TypWithCollectionConstructor.class, source); - assertThat(result.attributes).isNotNull(); - } - - @Test // DATAMONGO-2400 - void writeJavaTimeValuesViaCodec() { - - configureConverterWithNativeJavaTimeCodec(); - TypeWithLocalDateTime source = new TypeWithLocalDateTime(); - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target).containsEntry("date", source.date); - } - - void configureConverterWithNativeJavaTimeCodec() { - - converter = new MappingMongoConverter(resolver, mappingContext); - - converter.setCustomConversions(MongoCustomConversions - .create(MongoCustomConversions.MongoConverterConfigurationAdapter::useNativeDriverJavaTimeCodecs)); - converter.afterPropertiesSet(); - } - - private static void assertSyntheticFieldValueOf(Object target, Object expected) { - - for (int i = 0; i < 10; i++) { - try { - assertThat(ReflectionTestUtils.getField(target, "this$" + i)).isEqualTo(expected); - return; - } catch (IllegalArgumentException e) { - // Suppress and try next - } - } - - fail(String.format("Didn't find synthetic field on %s!", target)); - } - - @Test // DATAMGONGO-508 - void eagerlyReturnsDBRefObjectIfTargetAlreadyIsOne() { - - DBRef dbRef = new DBRef("collection", "id"); - - MongoPersistentProperty property = mock(MongoPersistentProperty.class); - - assertThat(converter.createDBRef(dbRef, property)).isEqualTo(dbRef); - } - - @Test // DATAMONGO-523, DATAMONGO-1509 - void considersTypeAliasAnnotation() { - - Aliased aliased = new Aliased(); - aliased.name = "foo"; - - org.bson.Document result = new org.bson.Document(); - converter.write(aliased, result); - - assertTypeHint(result, "_"); - } - - @Test // DATAMONGO-533 - void marshalsThrowableCorrectly() { - - ThrowableWrapper wrapper = new ThrowableWrapper(); - wrapper.throwable = new Exception(); - - org.bson.Document document = new org.bson.Document(); - converter.write(wrapper, document); - } - - @Test // DATAMONGO-592 - void recursivelyConvertsSpELReadValue() { - - org.bson.Document input = org.bson.Document.parse( - "{ \"_id\" : { \"$oid\" : \"50ca271c4566a2b08f2d667a\" }, \"_class\" : \"com.recorder.TestRecorder2$ObjectContainer\", \"property\" : { \"property\" : 100 } }"); - - converter.read(ObjectContainer.class, input); - } - - @Test // DATAMONGO-724 - void mappingConsidersCustomConvertersNotWritingTypeInformation() { - - Person person = new Person(); - person.firstname = "Dave"; - - ClassWithMapProperty entity = new ClassWithMapProperty(); - entity.mapOfPersons = new HashMap(); - entity.mapOfPersons.put("foo", person); - entity.mapOfObjects = new HashMap(); - entity.mapOfObjects.put("foo", person); - - CustomConversions conversions = new MongoCustomConversions( - Arrays.asList(new Converter() { - - @Override - public org.bson.Document convert(Person source) { - return new org.bson.Document().append("firstname", source.firstname)// - .append("_class", Person.class.getName()); - } - - }, new Converter() { - - @Override - public Person convert(org.bson.Document source) { - Person person = new Person(); - person.firstname = source.get("firstname").toString(); - person.lastname = "converter"; - return person; - } - })); - - MongoMappingContext context = new MongoMappingContext(); - context.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); - context.afterPropertiesSet(); - - MappingMongoConverter mongoConverter = new MappingMongoConverter(resolver, context); - mongoConverter.setCustomConversions(conversions); - mongoConverter.afterPropertiesSet(); - - org.bson.Document document = new org.bson.Document(); - mongoConverter.write(entity, document); - - ClassWithMapProperty result = mongoConverter.read(ClassWithMapProperty.class, document); - - assertThat(result.mapOfPersons).isNotNull(); - Person personCandidate = result.mapOfPersons.get("foo"); - assertThat(personCandidate).isNotNull(); - assertThat(personCandidate.firstname).isEqualTo("Dave"); - - assertThat(result.mapOfObjects).isNotNull(); - Object value = result.mapOfObjects.get("foo"); - assertThat(value).isNotNull(); - assertThat(value).isInstanceOf(Person.class); - assertThat(((Person) value).firstname).isEqualTo("Dave"); - assertThat(((Person) value).lastname).isEqualTo("converter"); - } - - @Test // DATAMONGO-743, DATAMONGO-2198 - void readsIntoStringsOutOfTheBox() { - - String target = converter.read(String.class, new org.bson.Document("firstname", "Dave")); - - assertThat(target).startsWith("{"); - assertThat(target).endsWith("}"); - assertThat(target).contains("\"firstname\""); - assertThat(target).contains("\"Dave\""); - } - - @Test // DATAMONGO-766 - void writesProjectingTypeCorrectly() { - - NestedType nested = new NestedType(); - nested.c = "C"; - - ProjectingType type = new ProjectingType(); - type.name = "name"; - type.foo = "bar"; - type.a = nested; - - org.bson.Document result = new org.bson.Document(); - converter.write(type, result); - - assertThat(result.get("name")).isEqualTo((Object) "name"); - org.bson.Document aValue = DocumentTestUtils.getAsDocument(result, "a"); - assertThat(aValue.get("b")).isEqualTo((Object) "bar"); - assertThat(aValue.get("c")).isEqualTo((Object) "C"); - } - - @Test // DATAMONGO-812, DATAMONGO-893, DATAMONGO-1509 - void convertsListToBasicDBListAndRetainsTypeInformationForComplexObjects() { - - Address address = new Address(); - address.city = "London"; - address.street = "Foo"; - - Object result = converter.convertToMongoType(Collections.singletonList(address), - ClassTypeInformation.from(InterfaceType.class)); - - assertThat(result).isInstanceOf(List.class); - - List dbList = (List) result; - assertThat(dbList).hasSize(1); - assertTypeHint(getAsDocument(dbList, 0), Address.class); - } - - @Test // DATAMONGO-812 - void convertsListToBasicDBListWithoutTypeInformationForSimpleTypes() { - - Object result = converter.convertToMongoType(Collections.singletonList("foo")); - - assertThat(result).isInstanceOf(List.class); - - List dbList = (List) result; - assertThat(dbList).hasSize(1); - assertThat(dbList.get(0)).isInstanceOf(String.class); - } - - @Test // DATAMONGO-812, DATAMONGO-1509 - void convertsArrayToBasicDBListAndRetainsTypeInformationForComplexObjects() { - - Address address = new Address(); - address.city = "London"; - address.street = "Foo"; - - Object result = converter.convertToMongoType(new Address[] { address }, ClassTypeInformation.OBJECT); - - assertThat(result).isInstanceOf(List.class); - - List dbList = (List) result; - assertThat(dbList).hasSize(1); - assertTypeHint(getAsDocument(dbList, 0), Address.class); - } - - @Test // DATAMONGO-812 - void convertsArrayToBasicDBListWithoutTypeInformationForSimpleTypes() { - - Object result = converter.convertToMongoType(new String[] { "foo" }); - - assertThat(result).isInstanceOf(List.class); - - List dbList = (List) result; - assertThat(dbList).hasSize(1); - assertThat(dbList.get(0)).isInstanceOf(String.class); - } - - @Test // DATAMONGO-833 - void readsEnumSetCorrectly() { - - BasicDBList enumSet = new BasicDBList(); - enumSet.add("SECOND"); - org.bson.Document document = new org.bson.Document("enumSet", enumSet); - - ClassWithEnumProperty result = converter.read(ClassWithEnumProperty.class, document); - - assertThat(result.enumSet).isInstanceOf(EnumSet.class); - assertThat(result.enumSet.size()).isEqualTo(1); - assertThat(result.enumSet).contains(SampleEnum.SECOND); - } - - @Test // DATAMONGO-833 - void readsEnumMapCorrectly() { - - org.bson.Document enumMap = new org.bson.Document("FIRST", "Dave"); - ClassWithEnumProperty result = converter.read(ClassWithEnumProperty.class, - new org.bson.Document("enumMap", enumMap)); - - assertThat(result.enumMap).isInstanceOf(EnumMap.class); - assertThat(result.enumMap.size()).isEqualTo(1); - assertThat(result.enumMap.get(SampleEnum.FIRST)).isEqualTo("Dave"); - } - - @Test // DATAMONGO-887 - void readsTreeMapCorrectly() { - - org.bson.Document person = new org.bson.Document("foo", "Dave"); - org.bson.Document treeMapOfPerson = new org.bson.Document("key", person); - org.bson.Document document = new org.bson.Document("treeMapOfPersons", treeMapOfPerson); - - ClassWithMapProperty result = converter.read(ClassWithMapProperty.class, document); - - assertThat(result.treeMapOfPersons).isNotNull(); - assertThat(result.treeMapOfPersons.get("key")).isNotNull(); - assertThat(result.treeMapOfPersons.get("key").firstname).isEqualTo("Dave"); - } - - @Test // DATAMONGO-887 - void writesTreeMapCorrectly() { - - Person person = new Person(); - person.firstname = "Dave"; - - ClassWithMapProperty source = new ClassWithMapProperty(); - source.treeMapOfPersons = new TreeMap(); - source.treeMapOfPersons.put("key", person); - - org.bson.Document result = new org.bson.Document(); - - converter.write(source, result); - - org.bson.Document map = getAsDocument(result, "treeMapOfPersons"); - org.bson.Document entry = getAsDocument(map, "key"); - assertThat(entry.get("foo")).isEqualTo("Dave"); - } - - @Test // DATAMONGO-858 - void shouldWriteEntityWithGeoBoxCorrectly() { - - ClassWithGeoBox object = new ClassWithGeoBox(); - object.box = new Box(new Point(1, 2), new Point(3, 4)); - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - assertThat(document).isNotNull(); - assertThat(document.get("box")).isInstanceOf(org.bson.Document.class); - assertThat(document.get("box")).isEqualTo((Object) new org.bson.Document() - .append("first", toDocument(object.box.getFirst())).append("second", toDocument(object.box.getSecond()))); - } - - private static org.bson.Document toDocument(Point point) { - return new org.bson.Document("x", point.getX()).append("y", point.getY()); - } - - @Test // DATAMONGO-858 - void shouldReadEntityWithGeoBoxCorrectly() { - - ClassWithGeoBox object = new ClassWithGeoBox(); - object.box = new Box(new Point(1, 2), new Point(3, 4)); - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - ClassWithGeoBox result = converter.read(ClassWithGeoBox.class, document); - - assertThat(result).isNotNull(); - assertThat(result.box).isEqualTo(object.box); - } - - @Test // DATAMONGO-858 - void shouldWriteEntityWithGeoPolygonCorrectly() { - - ClassWithGeoPolygon object = new ClassWithGeoPolygon(); - object.polygon = new Polygon(new Point(1, 2), new Point(3, 4), new Point(4, 5)); - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - assertThat(document).isNotNull(); - - assertThat(document.get("polygon")).isInstanceOf(org.bson.Document.class); - org.bson.Document polygonDoc = (org.bson.Document) document.get("polygon"); - - @SuppressWarnings("unchecked") - List points = (List) polygonDoc.get("points"); - - assertThat(points).hasSize(3); - assertThat(points).contains(toDocument(object.polygon.getPoints().get(0)), - toDocument(object.polygon.getPoints().get(1)), toDocument(object.polygon.getPoints().get(2))); - } - - @Test // DATAMONGO-858 - void shouldReadEntityWithGeoPolygonCorrectly() { - - ClassWithGeoPolygon object = new ClassWithGeoPolygon(); - object.polygon = new Polygon(new Point(1, 2), new Point(3, 4), new Point(4, 5)); - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - ClassWithGeoPolygon result = converter.read(ClassWithGeoPolygon.class, document); - - assertThat(result).isNotNull(); - assertThat(result.polygon).isEqualTo(object.polygon); - } - - @Test // DATAMONGO-858 - void shouldWriteEntityWithGeoCircleCorrectly() { - - ClassWithGeoCircle object = new ClassWithGeoCircle(); - Circle circle = new Circle(new Point(1, 2), 3); - Distance radius = circle.getRadius(); - object.circle = circle; - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - assertThat(document).isNotNull(); - assertThat(document.get("circle")).isInstanceOf(org.bson.Document.class); - assertThat(document.get("circle")).isEqualTo((Object) new org.bson.Document("center", - new org.bson.Document("x", circle.getCenter().getX()).append("y", circle.getCenter().getY())) - .append("radius", radius.getNormalizedValue()).append("metric", radius.getMetric().toString())); - } - - @Test // DATAMONGO-858 - void shouldReadEntityWithGeoCircleCorrectly() { - - ClassWithGeoCircle object = new ClassWithGeoCircle(); - object.circle = new Circle(new Point(1, 2), 3); - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - ClassWithGeoCircle result = converter.read(ClassWithGeoCircle.class, document); - - assertThat(result).isNotNull(); - assertThat(result.circle).isEqualTo(result.circle); - } - - @Test // DATAMONGO-858 - void shouldWriteEntityWithGeoSphereCorrectly() { - - ClassWithGeoSphere object = new ClassWithGeoSphere(); - Sphere sphere = new Sphere(new Point(1, 2), 3); - Distance radius = sphere.getRadius(); - object.sphere = sphere; - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - assertThat(document).isNotNull(); - assertThat(document.get("sphere")).isInstanceOf(org.bson.Document.class); - assertThat(document.get("sphere")).isEqualTo((Object) new org.bson.Document("center", - new org.bson.Document("x", sphere.getCenter().getX()).append("y", sphere.getCenter().getY())) - .append("radius", radius.getNormalizedValue()).append("metric", radius.getMetric().toString())); - } - - @Test // DATAMONGO-858 - void shouldWriteEntityWithGeoSphereWithMetricDistanceCorrectly() { - - ClassWithGeoSphere object = new ClassWithGeoSphere(); - Sphere sphere = new Sphere(new Point(1, 2), new Distance(3, Metrics.KILOMETERS)); - Distance radius = sphere.getRadius(); - object.sphere = sphere; - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - assertThat(document).isNotNull(); - assertThat(document.get("sphere")).isInstanceOf(org.bson.Document.class); - assertThat(document.get("sphere")).isEqualTo((Object) new org.bson.Document("center", - new org.bson.Document("x", sphere.getCenter().getX()).append("y", sphere.getCenter().getY())) - .append("radius", radius.getNormalizedValue()).append("metric", radius.getMetric().toString())); - } - - @Test // DATAMONGO-858 - void shouldReadEntityWithGeoSphereCorrectly() { - - ClassWithGeoSphere object = new ClassWithGeoSphere(); - object.sphere = new Sphere(new Point(1, 2), 3); - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - ClassWithGeoSphere result = converter.read(ClassWithGeoSphere.class, document); - - assertThat(result).isNotNull(); - assertThat(result.sphere).isEqualTo(object.sphere); - } - - @Test // DATAMONGO-858 - void shouldWriteEntityWithGeoShapeCorrectly() { - - ClassWithGeoShape object = new ClassWithGeoShape(); - Sphere sphere = new Sphere(new Point(1, 2), 3); - Distance radius = sphere.getRadius(); - object.shape = sphere; - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - assertThat(document).isNotNull(); - assertThat(document.get("shape")).isInstanceOf(org.bson.Document.class); - assertThat(document.get("shape")).isEqualTo((Object) new org.bson.Document("center", - new org.bson.Document("x", sphere.getCenter().getX()).append("y", sphere.getCenter().getY())) - .append("radius", radius.getNormalizedValue()).append("metric", radius.getMetric().toString())); - } - - @Test // DATAMONGO-858 - @Disabled - void shouldReadEntityWithGeoShapeCorrectly() { - - ClassWithGeoShape object = new ClassWithGeoShape(); - Sphere sphere = new Sphere(new Point(1, 2), 3); - object.shape = sphere; - - org.bson.Document document = new org.bson.Document(); - converter.write(object, document); - - ClassWithGeoShape result = converter.read(ClassWithGeoShape.class, document); - - assertThat(result).isNotNull(); - assertThat(result.shape).isEqualTo(sphere); - } - - @Test // DATAMONGO-976 - void shouldIgnoreTextScorePropertyWhenWriting() { - - ClassWithTextScoreProperty source = new ClassWithTextScoreProperty(); - source.score = Float.MAX_VALUE; - - org.bson.Document document = new org.bson.Document(); - converter.write(source, document); - - assertThat(document.get("score")).isNull(); - } - - @Test // DATAMONGO-976 - void shouldIncludeTextScorePropertyWhenReading() { - - ClassWithTextScoreProperty entity = converter.read(ClassWithTextScoreProperty.class, - new org.bson.Document("score", 5F)); - assertThat(entity.score).isEqualTo(5F); - } - - @Test // DATAMONGO-1001, DATAMONGO-1509 - void shouldWriteCglibProxiedClassTypeInformationCorrectly() { - - ProxyFactory factory = new ProxyFactory(); - factory.setTargetClass(GenericType.class); - factory.setProxyTargetClass(true); - - GenericType proxied = (GenericType) factory.getProxy(); - org.bson.Document document = new org.bson.Document(); - converter.write(proxied, document); - - assertTypeHint(document, GenericType.class); - } - - @Test // DATAMONGO-1001 - void shouldUseTargetObjectOfLazyLoadingProxyWhenWriting() { - - LazyLoadingProxy mock = mock(LazyLoadingProxy.class); - - org.bson.Document document = new org.bson.Document(); - converter.write(mock, document); - - verify(mock, times(1)).getTarget(); - } - - @Test // DATAMONGO-1034 - void rejectsBasicDbListToBeConvertedIntoComplexType() { - - List inner = new ArrayList(); - inner.add("key"); - inner.add("value"); - - List outer = new ArrayList(); - outer.add(inner); - outer.add(inner); - - org.bson.Document source = new org.bson.Document("attributes", outer); - - assertThatExceptionOfType(MappingException.class).isThrownBy(() -> converter.read(Item.class, source)); - } - - @Test // DATAMONGO-1058 - void readShouldRespectExplicitFieldNameForDbRef() { - - org.bson.Document source = new org.bson.Document(); - source.append("explict-name-for-db-ref", new DBRef("foo", "1")); - - converter.read(ClassWithExplicitlyNamedDBRefProperty.class, source); - - verify(resolver, times(1)).resolveDbRef(Mockito.any(MongoPersistentProperty.class), Mockito.any(DBRef.class), - Mockito.any(DbRefResolverCallback.class), Mockito.any(DbRefProxyHandler.class)); - } - - @Test // DATAMONGO-1050 - void writeShouldUseExplicitFieldnameForIdPropertyWhenAnnotated() { - - RootForClassWithExplicitlyRenamedIdField source = new RootForClassWithExplicitlyRenamedIdField(); - source.id = "rootId"; - source.nested = new ClassWithExplicitlyRenamedField(); - source.nested.id = "nestedId"; - - org.bson.Document sink = new org.bson.Document(); - converter.write(source, sink); - - assertThat(sink.get("_id")).isEqualTo("rootId"); - assertThat(sink.get("nested")).isEqualTo(new org.bson.Document().append("id", "nestedId")); - } - - @Test // DATAMONGO-1050 - void readShouldUseExplicitFieldnameForIdPropertyWhenAnnotated() { - - org.bson.Document source = new org.bson.Document().append("_id", "rootId").append("nested", - new org.bson.Document("id", "nestedId")); - - RootForClassWithExplicitlyRenamedIdField sink = converter.read(RootForClassWithExplicitlyRenamedIdField.class, - source); - - assertThat(sink.id).isEqualTo("rootId"); - assertThat(sink.nested).isNotNull(); - assertThat(sink.nested.id).isEqualTo("nestedId"); - } - - @Test // DATAMONGO-1050 - void namedIdFieldShouldExtractValueFromUnderscoreIdField() { - - org.bson.Document document = new org.bson.Document().append("_id", "A").append("id", "B"); - - ClassWithNamedIdField withNamedIdField = converter.read(ClassWithNamedIdField.class, document); - - assertThat(withNamedIdField.id).isEqualTo("A"); - } - - @Test // DATAMONGO-1050 - void explicitlyRenamedIfFieldShouldExtractValueFromIdField() { - - org.bson.Document document = new org.bson.Document().append("_id", "A").append("id", "B"); - - ClassWithExplicitlyRenamedField withExplicitlyRenamedField = converter.read(ClassWithExplicitlyRenamedField.class, - document); - - assertThat(withExplicitlyRenamedField.id).isEqualTo("B"); - } - - @Test // DATAMONGO-1050 - void annotatedIdFieldShouldExtractValueFromUnderscoreIdField() { - - org.bson.Document document = new org.bson.Document().append("_id", "A").append("id", "B"); - - ClassWithAnnotatedIdField withAnnotatedIdField = converter.read(ClassWithAnnotatedIdField.class, document); - - assertThat(withAnnotatedIdField.key).isEqualTo("A"); - } - - @Test // DATAMONGO-1102 - void convertsJava8DateTimeTypesToDateAndBack() { - - TypeWithLocalDateTime source = new TypeWithLocalDateTime(); - LocalDateTime reference = source.date; - org.bson.Document result = new org.bson.Document(); - - converter.write(source, result); - - assertThat(result.get("date")).isInstanceOf(Date.class); - assertThat(converter.read(TypeWithLocalDateTime.class, result).date) - .isEqualTo(reference.truncatedTo(ChronoUnit.MILLIS)); - } - - @Test // DATAMONGO-1128 - void writesOptionalsCorrectly() { - - TypeWithOptional type = new TypeWithOptional(); - type.localDateTime = Optional.of(LocalDateTime.now()); - - org.bson.Document result = new org.bson.Document(); - - converter.write(type, result); - - assertThat(getAsDocument(result, "string")).isEqualTo(new org.bson.Document()); - - org.bson.Document localDateTime = getAsDocument(result, "localDateTime"); - assertThat(localDateTime.get("value")).isInstanceOf(Date.class); - } - - @Test // DATAMONGO-1128 - @Disabled("Broken by DATAMONGO-1992 - In fact, storing Optional fields seems an anti-pattern.") - void readsOptionalsCorrectly() { - - LocalDateTime now = LocalDateTime.now(); - Date reference = Date.from(now.atZone(systemDefault()).toInstant()); - - org.bson.Document optionalOfLocalDateTime = new org.bson.Document("value", reference); - org.bson.Document result = new org.bson.Document("localDateTime", optionalOfLocalDateTime); - - TypeWithOptional read = converter.read(TypeWithOptional.class, result); - - assertThat(read.string).isEmpty(); - assertThat(read.localDateTime).isEqualTo(Optional.of(now)); - } - - @Test // DATAMONGO-1118 - void convertsMapKeyUsingCustomConverterForAndBackwards() { - - MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext); - converter.setCustomConversions( - new MongoCustomConversions(Arrays.asList(new FooBarEnumToStringConverter(), new StringToFooNumConverter()))); - converter.afterPropertiesSet(); - - ClassWithMapUsingEnumAsKey source = new ClassWithMapUsingEnumAsKey(); - source.map = new HashMap(); - source.map.put(FooBarEnum.FOO, "wohoo"); - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(converter.read(ClassWithMapUsingEnumAsKey.class, target).map).isEqualTo(source.map); - } - - @Test // DATAMONGO-1118 - void writesMapKeyUsingCustomConverter() { - - MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext); - converter.setCustomConversions(new MongoCustomConversions(Arrays.asList(new FooBarEnumToStringConverter()))); - converter.afterPropertiesSet(); - - ClassWithMapUsingEnumAsKey source = new ClassWithMapUsingEnumAsKey(); - source.map = new HashMap(); - source.map.put(FooBarEnum.FOO, "spring"); - source.map.put(FooBarEnum.BAR, "data"); - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - org.bson.Document map = DocumentTestUtils.getAsDocument(target, "map"); - - assertThat(map.containsKey("foo-enum-value")).isTrue(); - assertThat(map.containsKey("bar-enum-value")).isTrue(); - } - - @Test // DATAMONGO-1118 - void readsMapKeyUsingCustomConverter() { - - MappingMongoConverter converter = new MappingMongoConverter(resolver, mappingContext); - converter.setCustomConversions(new MongoCustomConversions(Arrays.asList(new StringToFooNumConverter()))); - converter.afterPropertiesSet(); - - org.bson.Document source = new org.bson.Document("map", new org.bson.Document("foo-enum-value", "spring")); - - ClassWithMapUsingEnumAsKey target = converter.read(ClassWithMapUsingEnumAsKey.class, source); - - assertThat(target.map.get(FooBarEnum.FOO)).isEqualTo("spring"); - } - - @Test // DATAMONGO-1471 - void readsDocumentWithPrimitiveIdButNoValue() { - assertThat(converter.read(ClassWithIntId.class, new org.bson.Document())).isNotNull(); - } - - @Test // DATAMONGO-1497 - void readsPropertyFromNestedFieldCorrectly() { - - org.bson.Document source = new org.bson.Document("nested", new org.bson.Document("sample", "value")); - TypeWithPropertyInNestedField result = converter.read(TypeWithPropertyInNestedField.class, source); - - assertThat(result.sample).isEqualTo("value"); - } - - @Test // DATAMONGO-1525 - void readsEmptyEnumSet() { - - org.bson.Document source = new org.bson.Document("enumSet", Collections.emptyList()); - - assertThat(converter.read(ClassWithEnumProperty.class, source).enumSet).isEqualTo(EnumSet.noneOf(SampleEnum.class)); - } - - @Test // DATAMONGO-1757 - void failsReadingDocumentIntoSimpleType() { - - org.bson.Document nested = new org.bson.Document("key", "value"); - org.bson.Document source = new org.bson.Document("map", new org.bson.Document("key", nested)); - - assertThatExceptionOfType(MappingException.class) - .isThrownBy(() -> converter.read(TypeWithMapOfLongValues.class, source)); - } - - @Test // DATAMONGO-1831 - void shouldConvertArrayInConstructorCorrectly() { - - org.bson.Document source = new org.bson.Document("array", Collections.emptyList()); - - assertThat(converter.read(WithArrayInConstructor.class, source).array).isEmpty(); - } - - @Test // DATAMONGO-1831 - void shouldConvertNullForArrayInConstructorCorrectly() { - - org.bson.Document source = new org.bson.Document(); - - assertThat(converter.read(WithArrayInConstructor.class, source).array).isNull(); - } - - @Test // DATAMONGO-1898 - void writesInterfaceBackedEnumsToSimpleNameByDefault() { - - org.bson.Document document = new org.bson.Document(); - - DocWithInterfacedEnum source = new DocWithInterfacedEnum(); - source.property = InterfacedEnum.INSTANCE; - - converter.write(source, document); - - assertThat(document) // - .hasSize(2) // - .hasEntrySatisfying("_class", __ -> {}) // - .hasEntrySatisfying("property", value -> InterfacedEnum.INSTANCE.name().equals(value)); - } - - @Test // DATAMONGO-1898 - void rejectsConversionFromStringToEnumBackedInterface() { - - org.bson.Document document = new org.bson.Document("property", InterfacedEnum.INSTANCE.name()); - - assertThatExceptionOfType(ConverterNotFoundException.class) // - .isThrownBy(() -> converter.read(DocWithInterfacedEnum.class, document)); - } - - @Test // DATAMONGO-1898 - void readsInterfacedEnumIfConverterIsRegistered() { - - org.bson.Document document = new org.bson.Document("property", InterfacedEnum.INSTANCE.name()); - - Converter enumConverter = new Converter() { - - @Override - public SomeInterface convert(String source) { - return InterfacedEnum.valueOf(source); - } - }; - - converter.setCustomConversions(new MongoCustomConversions(Collections.singletonList(enumConverter))); - converter.afterPropertiesSet(); - - DocWithInterfacedEnum result = converter.read(DocWithInterfacedEnum.class, document); - - assertThat(result.property).isEqualTo(InterfacedEnum.INSTANCE); - } - - @Test // DATAMONGO-1904 - void readsNestedArraysCorrectly() { - - List>> floats = Collections.singletonList(Collections.singletonList(Arrays.asList(1.0f, 2.0f))); - - org.bson.Document document = new org.bson.Document("nestedFloats", floats); - - WithNestedLists result = converter.read(WithNestedLists.class, document); - - assertThat(result.nestedFloats).hasDimensions(1, 1).isEqualTo(new float[][][] { { { 1.0f, 2.0f } } }); - } - - @Test // DATAMONGO-1992 - void readsImmutableObjectCorrectly() { - - org.bson.Document document = new org.bson.Document("_id", "foo"); - - ImmutableObject result = converter.read(ImmutableObject.class, document); - - assertThat(result.id).isEqualTo("foo"); - assertThat(result.witherUsed).isTrue(); - } - - @Test // DATAMONGO-2026 - void readsImmutableObjectWithConstructorIdPropertyCorrectly() { - - org.bson.Document source = new org.bson.Document("_id", "spring").append("value", "data"); - - ImmutableObjectWithIdConstructorPropertyAndNoIdWitherMethod target = converter - .read(ImmutableObjectWithIdConstructorPropertyAndNoIdWitherMethod.class, source); - - assertThat(target.id).isEqualTo("spring"); - assertThat(target.value).isEqualTo("data"); - } - - @Test // DATAMONGO-2011 - void readsNestedListsToObjectCorrectly() { - - List values = Arrays.asList("ONE", "TWO"); - org.bson.Document source = new org.bson.Document("value", Collections.singletonList(values)); - - assertThat(converter.read(Attribute.class, source).value).isInstanceOf(List.class); - } - - @Test // DATAMONGO-2043 - void omitsTypeHintWhenWritingSimpleTypes() { - - org.bson.Document target = new org.bson.Document(); - converter.write(new org.bson.Document("value", "FitzChivalry"), target); - - assertThat(target).doesNotContainKeys("_class"); - } - - @Test // DATAMONGO-1798 - void convertStringIdThatIsAnObjectIdHexToObjectIdIfTargetIsObjectId() { - - ObjectId source = new ObjectId(); - assertThat(converter.convertId(source.toHexString(), ObjectId.class)).isEqualTo(source); - } - - @Test // DATAMONGO-1798 - void donNotConvertStringIdThatIsAnObjectIdHexToObjectIdIfTargetIsString() { - - ObjectId source = new ObjectId(); - assertThat(converter.convertId(source.toHexString(), String.class)).isEqualTo(source.toHexString()); - } - - @Test // DATAMONGO-1798 - void donNotConvertStringIdThatIsAnObjectIdHexToObjectIdIfTargetIsObject() { - - ObjectId source = new ObjectId(); - assertThat(converter.convertId(source.toHexString(), Object.class)).isEqualTo(source.toHexString()); - } - - @Test // DATAMONGO-2135 - void addsEqualObjectsToCollection() { - - org.bson.Document itemDocument = new org.bson.Document("itemKey", "123"); - org.bson.Document orderDocument = new org.bson.Document("items", - Arrays.asList(itemDocument, itemDocument, itemDocument)); - - Order order = converter.read(Order.class, orderDocument); - - assertThat(order.items).hasSize(3); - } - - @Test // DATAMONGO-1849 - void mapsValueToExplicitTargetType() { - - WithExplicitTargetTypes source = new WithExplicitTargetTypes(); - source.script = "if (a > b) a else b"; - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target.get("script")).isEqualTo(new Code(source.script)); - } - - @Test // DATAMONGO-2328 - void readsScriptAsStringWhenAnnotatedWithFieldTargetType() { - - String reference = "if (a > b) a else b"; - WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, - new org.bson.Document("script", new Code(reference))); - - assertThat(target.script).isEqualTo(reference); - } - - @Test // DATAMONGO-1849 - void mapsCollectionValueToExplicitTargetType() { - - String script = "if (a > b) a else b"; - WithExplicitTargetTypes source = new WithExplicitTargetTypes(); - source.scripts = Collections.singletonList(script); - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target.get("scripts", List.class)).containsExactly(new Code(script)); - } - - @Test // DATAMONGO-1849 - void mapsBigDecimalToDecimal128WhenAnnotatedWithFieldTargetType() { - - WithExplicitTargetTypes source = new WithExplicitTargetTypes(); - source.bigDecimal = BigDecimal.valueOf(3.14159D); - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target.get("bigDecimal")).isEqualTo(new Decimal128(source.bigDecimal)); - } - - @Test // DATAMONGO-2328 - void mapsDateToLongWhenAnnotatedWithFieldTargetType() { - - WithExplicitTargetTypes source = new WithExplicitTargetTypes(); - source.dateAsLong = new Date(); - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target.get("dateAsLong")).isEqualTo(source.dateAsLong.getTime()); - } - - @Test // DATAMONGO-2328 - void readsLongAsDateWhenAnnotatedWithFieldTargetType() { - - Date reference = new Date(); - WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, - new org.bson.Document("dateAsLong", reference.getTime())); - - assertThat(target.dateAsLong).isEqualTo(reference); - } - - @Test // DATAMONGO-2328 - void mapsLongToDateWhenAnnotatedWithFieldTargetType() { - - Date date = new Date(); - WithExplicitTargetTypes source = new WithExplicitTargetTypes(); - source.longAsDate = date.getTime(); - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target.get("longAsDate")).isEqualTo(date); - } - - @Test // DATAMONGO-2328 - void readsDateAsLongWhenAnnotatedWithFieldTargetType() { - - Date reference = new Date(); - WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, - new org.bson.Document("longAsDate", reference)); - - assertThat(target.longAsDate).isEqualTo(reference.getTime()); - } - - @Test // DATAMONGO-2328 - void mapsStringAsBooleanWhenAnnotatedWithFieldTargetType() { - - WithExplicitTargetTypes source = new WithExplicitTargetTypes(); - source.stringAsBoolean = "true"; - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target.get("stringAsBoolean")).isEqualTo(true); - } - - @Test // DATAMONGO-2328 - void readsBooleanAsStringWhenAnnotatedWithFieldTargetType() { - - WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, - new org.bson.Document("stringAsBoolean", true)); - - assertThat(target.stringAsBoolean).isEqualTo("true"); - } - - @Test // DATAMONGO-2328 - void mapsDateAsObjectIdWhenAnnotatedWithFieldTargetType() { - - WithExplicitTargetTypes source = new WithExplicitTargetTypes(); - source.dateAsObjectId = new Date(); - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - // need to compare the the timestamp as ObjectId has an internal counter - assertThat(target.get("dateAsObjectId", ObjectId.class).getTimestamp()) - .isEqualTo(new ObjectId(source.dateAsObjectId).getTimestamp()); - } - - @Test // DATAMONGO-2328 - void readsObjectIdAsDateWhenAnnotatedWithFieldTargetType() { - - ObjectId reference = new ObjectId(); - WithExplicitTargetTypes target = converter.read(WithExplicitTargetTypes.class, - new org.bson.Document("dateAsObjectId", reference)); - - assertThat(target.dateAsObjectId).isEqualTo(new Date(reference.getTimestamp())); - } - - @Test // DATAMONGO-2410 - void shouldAllowReadingBackDbObject() { - - assertThat(converter.read(BasicDBObject.class, new org.bson.Document("property", "value"))) - .isEqualTo(new BasicDBObject("property", "value")); - assertThat(converter.read(DBObject.class, new org.bson.Document("property", "value"))) - .isEqualTo(new BasicDBObject("property", "value")); - } - - @Test // DATAMONGO-2479 - public void entityCallbacksAreNotSetByDefault() { - Assertions.assertThat(ReflectionTestUtils.getField(converter, "entityCallbacks")).isNull(); - } - - @Test // DATAMONGO-2479 - public void entityCallbacksShouldBeInitiatedOnSettingApplicationContext() { - - ApplicationContext ctx = new StaticApplicationContext(); - converter.setApplicationContext(ctx); - - Assertions.assertThat(ReflectionTestUtils.getField(converter, "entityCallbacks")).isNotNull(); - } - - @Test // DATAMONGO-2479 - public void setterForEntityCallbackOverridesContextInitializedOnes() { - - ApplicationContext ctx = new StaticApplicationContext(); - converter.setApplicationContext(ctx); - - EntityCallbacks callbacks = EntityCallbacks.create(); - converter.setEntityCallbacks(callbacks); - - Assertions.assertThat(ReflectionTestUtils.getField(converter, "entityCallbacks")).isSameAs(callbacks); - } - - @Test // DATAMONGO-2479 - public void setterForApplicationContextShouldNotOverrideAlreadySetEntityCallbacks() { - - EntityCallbacks callbacks = EntityCallbacks.create(); - ApplicationContext ctx = new StaticApplicationContext(); - - converter.setEntityCallbacks(callbacks); - converter.setApplicationContext(ctx); - - Assertions.assertThat(ReflectionTestUtils.getField(converter, "entityCallbacks")).isSameAs(callbacks); - } - - @Test // DATAMONGO-2479 - public void resolveDBRefMapValueShouldInvokeCallbacks() { - - AfterConvertCallback afterConvertCallback = spy(new ReturningAfterConvertCallback()); - converter.setEntityCallbacks(EntityCallbacks.create(afterConvertCallback)); - - when(resolver.fetch(Mockito.any(DBRef.class))).thenReturn(new org.bson.Document()); - DBRef dbRef = mock(DBRef.class); - - org.bson.Document refMap = new org.bson.Document("foo", dbRef); - org.bson.Document document = new org.bson.Document("personMap", refMap); - - DBRefWrapper result = converter.read(DBRefWrapper.class, document); - - verify(afterConvertCallback).onAfterConvert(eq(result.personMap.get("foo")), eq(new org.bson.Document()), any()); - } - - @Test // DATAMONGO-2300 - public void readAndConvertDBRefNestedByMapCorrectly() { - - org.bson.Document cluster = new org.bson.Document("_id", 100L); - DBRef dbRef = new DBRef("clusters", 100L); - - org.bson.Document data = new org.bson.Document("_id", 3L); - data.append("cluster", dbRef); - - MappingMongoConverter spyConverter = spy(converter); - Mockito.doReturn(cluster).when(spyConverter).readRef(dbRef); - - Map result = spyConverter.readMap(spyConverter.getConversionContext(ObjectPath.ROOT), data, - ClassTypeInformation.MAP); - - assertThat(((Map) result.get("cluster")).get("_id")).isEqualTo(100L); - } - - @Test // GH-3546 - void readFlattensNestedDocumentToStringIfNecessary() { - - org.bson.Document source = new org.bson.Document("street", new org.bson.Document("json", "string").append("_id", UUID.randomUUID())); - - Address target = converter.read(Address.class, source); - assertThat(target.street).isNotNull(); - } - - @Test // DATAMONGO-1902 - void writeFlattensUnwrappedType() { - - WithNullableUnwrapped source = new WithNullableUnwrapped(); - source.id = "id-1"; - source.embeddableValue = new EmbeddableType(); - source.embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - source.embeddableValue.stringValue = "string-val"; - source.embeddableValue.transientValue = "must-not-be-written"; - source.embeddableValue.atFieldAnnotatedValue = "@Field"; - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target).containsEntry("_id", "id-1") // - .containsEntry("stringValue", "string-val") // - .containsEntry("listValue", Arrays.asList("list-val-1", "list-val-2")) // - .containsEntry("with-at-field-annotation", "@Field") // - .doesNotContainKey("embeddableValue") // - .doesNotContainKey("transientValue"); - } - - @Test // DATAMONGO-1902 - void writePrefixesUnwrappedType() { - - WithPrefixedNullableUnwrapped source = new WithPrefixedNullableUnwrapped(); - source.id = "id-1"; - source.embeddableValue = new EmbeddableType(); - source.embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - source.embeddableValue.stringValue = "string-val"; - source.embeddableValue.transientValue = "must-not-be-written"; - source.embeddableValue.atFieldAnnotatedValue = "@Field"; - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target).containsEntry("_id", "id-1") // - .containsEntry("prefix-stringValue", "string-val") // - .containsEntry("prefix-listValue", Arrays.asList("list-val-1", "list-val-2")) // - .containsEntry("prefix-with-at-field-annotation", "@Field") // - .doesNotContainKey("embeddableValue") // - .doesNotContainKey("transientValue") // - .doesNotContainKey("prefix-transientValue"); - } - - @Test // DATAMONGO-1902 - void writeNullUnwrappedType() { - - WithNullableUnwrapped source = new WithNullableUnwrapped(); - source.id = "id-1"; - source.embeddableValue = null; - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target) // - .doesNotContainKey("prefix-stringValue").doesNotContainKey("prefix-listValue") - .doesNotContainKey("embeddableValue"); - } - - @Test // DATAMONGO-1902 - void writeDeepNestedUnwrappedType() { - - WrapperAroundWithUnwrapped source = new WrapperAroundWithUnwrapped(); - source.someValue = "root-level-value"; - source.nullableEmbedded = new WithNullableUnwrapped(); - source.nullableEmbedded.id = "id-1"; - source.nullableEmbedded.embeddableValue = new EmbeddableType(); - source.nullableEmbedded.embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - source.nullableEmbedded.embeddableValue.stringValue = "string-val"; - source.nullableEmbedded.embeddableValue.transientValue = "must-not-be-written"; - source.nullableEmbedded.embeddableValue.atFieldAnnotatedValue = "@Field"; - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target).containsEntry("someValue", "root-level-value") // - .containsEntry("nullableEmbedded", new org.bson.Document("_id", "id-1").append("stringValue", "string-val") // - .append("listValue", Arrays.asList("list-val-1", "list-val-2")) // - .append("with-at-field-annotation", "@Field")); // - } - - @Test // DATAMONGO-1902 - void readUnwrappedType() { - - org.bson.Document source = new org.bson.Document("_id", "id-1") // - .append("stringValue", "string-val") // - .append("listValue", Arrays.asList("list-val-1", "list-val-2")) // - .append("with-at-field-annotation", "@Field"); - - EmbeddableType embeddableValue = new EmbeddableType(); - embeddableValue.stringValue = "string-val"; - embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - embeddableValue.atFieldAnnotatedValue = "@Field"; - - WithNullableUnwrapped target = converter.read(WithNullableUnwrapped.class, source); - assertThat(target.embeddableValue).isEqualTo(embeddableValue); - } - - @Test // DATAMONGO-1902 - void readPrefixedUnwrappedType() { - - org.bson.Document source = new org.bson.Document("_id", "id-1") // - .append("prefix-stringValue", "string-val") // - .append("prefix-listValue", Arrays.asList("list-val-1", "list-val-2")) // - .append("prefix-with-at-field-annotation", "@Field"); - - EmbeddableType embeddableValue = new EmbeddableType(); - embeddableValue.stringValue = "string-val"; - embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - embeddableValue.atFieldAnnotatedValue = "@Field"; - - WithPrefixedNullableUnwrapped target = converter.read(WithPrefixedNullableUnwrapped.class, source); - assertThat(target.embeddableValue).isEqualTo(embeddableValue); - } - - @Test // DATAMONGO-1902 - void readNullableUnwrappedTypeWhenSourceDoesNotContainValues() { - - org.bson.Document source = new org.bson.Document("_id", "id-1"); - - WithNullableUnwrapped target = converter.read(WithNullableUnwrapped.class, source); - assertThat(target.embeddableValue).isNull(); - } - - @Test // DATAMONGO-1902 - void readEmptyUnwrappedTypeWhenSourceDoesNotContainValues() { - - org.bson.Document source = new org.bson.Document("_id", "id-1"); - - WithEmptyUnwrappedType target = converter.read(WithEmptyUnwrappedType.class, source); - assertThat(target.embeddableValue).isNotNull(); - } - - @Test // DATAMONGO-1902 - void readDeepNestedUnwrappedType() { - - org.bson.Document source = new org.bson.Document("someValue", "root-level-value").append("nullableEmbedded", - new org.bson.Document("_id", "id-1").append("stringValue", "string-val") // - .append("listValue", Arrays.asList("list-val-1", "list-val-2")) // - .append("with-at-field-annotation", "@Field")); - - WrapperAroundWithUnwrapped target = converter.read(WrapperAroundWithUnwrapped.class, source); - - EmbeddableType embeddableValue = new EmbeddableType(); - embeddableValue.stringValue = "string-val"; - embeddableValue.listValue = Arrays.asList("list-val-1", "list-val-2"); - embeddableValue.atFieldAnnotatedValue = "@Field"; - - assertThat(target.someValue).isEqualTo("root-level-value"); - assertThat(target.nullableEmbedded).isNotNull(); - assertThat(target.nullableEmbedded.embeddableValue).isEqualTo(embeddableValue); - } - - @Test // DATAMONGO-1902 - void readUnwrappedTypeWithComplexValue() { - - org.bson.Document source = new org.bson.Document("_id", "id-1").append("address", - new org.bson.Document("street", "1007 Mountain Drive").append("city", "Gotham")); - - WithNullableUnwrapped target = converter.read(WithNullableUnwrapped.class, source); - - Address expected = new Address(); - expected.city = "Gotham"; - expected.street = "1007 Mountain Drive"; - - assertThat(target.embeddableValue.address) // - .isEqualTo(expected); - } - - @Test // DATAMONGO-1902 - void writeUnwrappedTypeWithComplexValue() { - - WithNullableUnwrapped source = new WithNullableUnwrapped(); - source.id = "id-1"; - source.embeddableValue = new EmbeddableType(); - source.embeddableValue.address = new Address(); - source.embeddableValue.address.city = "Gotham"; - source.embeddableValue.address.street = "1007 Mountain Drive"; - - org.bson.Document target = new org.bson.Document(); - converter.write(source, target); - - assertThat(target) // - .containsEntry("address", new org.bson.Document("street", "1007 Mountain Drive").append("city", "Gotham")) // - .doesNotContainKey("street") // - .doesNotContainKey("address.street") // - .doesNotContainKey("city") // - .doesNotContainKey("address.city"); - } - - @Test // GH-3580 - void shouldFallbackToConfiguredCustomConversionTargetOnRead() { - - GenericTypeConverter genericTypeConverter = spy(new GenericTypeConverter()); - - converter = new MappingMongoConverter(resolver, mappingContext); - converter.setCustomConversions(MongoCustomConversions.create(it -> { - it.registerConverter(genericTypeConverter); - })); - converter.afterPropertiesSet(); - - org.bson.Document source = new org.bson.Document("_class", SubTypeOfGenericType.class.getName()).append("value", - "v1"); - GenericType target = converter.read(GenericType.class, source); - - assertThat(target).isInstanceOf(GenericType.class); - assertThat(target.content).isEqualTo("v1"); - - verify(genericTypeConverter).convert(eq(source)); - } - - @Test // GH-3580 - void shouldUseMostConcreteCustomConversionTargetOnRead() { - - GenericTypeConverter genericTypeConverter = spy(new GenericTypeConverter()); - SubTypeOfGenericTypeConverter subTypeOfGenericTypeConverter = spy(new SubTypeOfGenericTypeConverter()); - - converter = new MappingMongoConverter(resolver, mappingContext); - converter.setCustomConversions(MongoCustomConversions.create(it -> { - it.registerConverter(genericTypeConverter); - it.registerConverter(subTypeOfGenericTypeConverter); - })); - converter.afterPropertiesSet(); - - org.bson.Document source = new org.bson.Document("_class", SubTypeOfGenericType.class.getName()).append("value", - "v1"); - GenericType target = converter.read(GenericType.class, source); - - assertThat(target).isInstanceOf(SubTypeOfGenericType.class); - assertThat(target.content).isEqualTo("v1_s"); - - verify(genericTypeConverter, never()).convert(any()); - verify(subTypeOfGenericTypeConverter).convert(eq(source)); - } - - static class GenericType { - T content; - } - - static class ClassWithEnumProperty { - - SampleEnum sampleEnum; - List enums; - EnumSet enumSet; - EnumMap enumMap; - } - - enum SampleEnum { - FIRST { - @Override - void method() {} - }, - SECOND { - @Override - void method() {} - }; - - abstract void method(); - } - - interface InterfaceType { - - } - - @EqualsAndHashCode - static class Address implements InterfaceType { - String street; - String city; - } - - interface Contact { - - } - - static class Person implements Contact { - - @Id String id; - - LocalDate birthDate; - - @Field("foo") String firstname; - String lastname; - - Set
addresses; - - Person() { - - } - - @PersistenceConstructor - public Person(Set
addresses) { - this.addresses = addresses; - } - } - - static class ClassWithSortedMap { - SortedMap map; - } - - static class ClassWithMapProperty { - Map map; - Map> mapOfLists; - Map mapOfObjects; - Map mapOfStrings; - Map mapOfPersons; - TreeMap treeMapOfPersons; - } - - static class ClassWithNestedMaps { - Map>> nestedMaps; - } - - static class BirthDateContainer { - LocalDate birthDate; - } - - static class BigDecimalContainer { - BigDecimal value; - Map map; - List collection; - } - - static class CollectionWrapper { - List contacts; - List> strings; - List> listOfMaps; - Set contactsSet; - } - - static class LocaleWrapper { - Locale locale; - } - - static class ClassWithBigIntegerId { - @Id BigInteger id; - } - - static class A { - - String valueType; - T value; - - A(T value) { - this.valueType = value.getClass().getName(); - this.value = value; - } - } - - static class ClassWithIntId { - - @Id int id; - } - - static class DefaultedConstructorArgument { - - String foo; - int bar; - double foobar; - - DefaultedConstructorArgument(String foo, @Value("#root.something ?: -1") int bar, double foobar) { - this.foo = foo; - this.bar = bar; - this.foobar = foobar; - } - } - - static class Item { - List attributes; - } - - static class Attribute { - String key; - Object value; - } - - static class Outer { - - class Inner { - String value; - } - - Inner inner; - } - - static class DBRefWrapper { - - DBRef ref; - List refs; - Map refMap; - Map personMap; - } - - static class URLWrapper { - URL url; - } - - static class ClassWithComplexId { - - @Id ComplexId complexId; - } - - static class ComplexId { - Long innerId; - } - - static class TypWithCollectionConstructor { - - List attributes; - - public TypWithCollectionConstructor(List attributes) { - this.attributes = attributes; - } - } - - @TypeAlias("_") - static class Aliased { - String name; - } - - static class ThrowableWrapper { - - Throwable throwable; - } - - @Document - static class PrimitiveContainer { - - @Field("property") private int m_property; - - @PersistenceConstructor - public PrimitiveContainer(@Value("#root.property") int a_property) { - m_property = a_property; - } - - public int property() { - return m_property; - } - } - - @Document - static class ObjectContainer { - - @Field("property") private PrimitiveContainer m_property; - - @PersistenceConstructor - public ObjectContainer(@Value("#root.property") PrimitiveContainer a_property) { - m_property = a_property; - } - - public PrimitiveContainer property() { - return m_property; - } - } - - class ClassWithGeoBox { - - Box box; - } - - class ClassWithGeoCircle { - - Circle circle; - } - - class ClassWithGeoSphere { - - Sphere sphere; - } - - class ClassWithGeoPolygon { - - Polygon polygon; - } - - class ClassWithGeoShape { - - Shape shape; - } - - class ClassWithTextScoreProperty { - - @TextScore Float score; - } - - class ClassWithExplicitlyNamedDBRefProperty { - - @Field("explict-name-for-db-ref") // - @org.springframework.data.mongodb.core.mapping.DBRef // - ClassWithIntId dbRefProperty; - - public ClassWithIntId getDbRefProperty() { - return dbRefProperty; - } - } - - static class RootForClassWithExplicitlyRenamedIdField { - - @Id String id; - ClassWithExplicitlyRenamedField nested; - } - - static class ClassWithExplicitlyRenamedField { - - @Field("id") String id; - } - - static class RootForClassWithNamedIdField { - - String id; - ClassWithNamedIdField nested; - } - - static class ClassWithNamedIdField { - - String id; - } - - static class ClassWithAnnotatedIdField { - - @Id String key; - } - - static class TypeWithLocalDateTime { - - LocalDateTime date; - - TypeWithLocalDateTime() { - this.date = LocalDateTime.now(); - } - } - - static class TypeWithOptional { - - Optional string = Optional.empty(); - Optional localDateTime = Optional.empty(); - } - - static class ClassWithMapUsingEnumAsKey { - - enum FooBarEnum { - FOO, BAR - } - - Map map; - } - - @WritingConverter - static class FooBarEnumToStringConverter implements Converter { - - @Override - public String convert(FooBarEnum source) { - - if (source == null) { - return null; - } - - return FooBarEnum.FOO.equals(source) ? "foo-enum-value" : "bar-enum-value"; - } - } - - @ReadingConverter - static class StringToFooNumConverter implements Converter { - - @Override - public FooBarEnum convert(String source) { - - if (source == null) { - return null; - } - - if ("foo-enum-value".equals(source)) { - return FooBarEnum.FOO; - } - if ("bar-enum-value".equals(source)) { - return FooBarEnum.BAR; - } - - throw new ConversionNotSupportedException(source, String.class, null); - } - } - - static class TypeWithPropertyInNestedField { - @Field("nested.sample") String sample; - } - - static class TypeWithMapOfLongValues { - Map map; - } - - @RequiredArgsConstructor - static class WithArrayInConstructor { - - final String[] array; - - } - - // DATAMONGO-1898 - - // DATACMNS-1278 - static interface SomeInterface {} - - static enum InterfacedEnum implements SomeInterface { - INSTANCE; - } - - static class DocWithInterfacedEnum { - SomeInterface property; - } - - // DATAMONGO-1904 - - static class WithNestedLists { - float[][][] nestedFloats; - } - - static class ImmutableObject { - - final String id; - final String name; - final boolean witherUsed; - - private ImmutableObject(String id) { - this.id = id; - this.name = null; - this.witherUsed = false; - } - - private ImmutableObject(String id, String name, boolean witherUsed) { - this.id = id; - this.name = name; - this.witherUsed = witherUsed; - } - - public ImmutableObject() { - this.id = null; - this.name = null; - witherUsed = false; - } - - public ImmutableObject withId(String id) { - return new ImmutableObject(id, name, true); - } - - public String getId() { - return id; - } - - public String getName() { - return name; - } - - public boolean isWitherUsed() { - return witherUsed; - } - } - - @RequiredArgsConstructor - static class ImmutableObjectWithIdConstructorPropertyAndNoIdWitherMethod { - - final @Id String id; - String value; - } - - // DATAMONGO-2135 - - @EqualsAndHashCode // equality check by fields - static class SomeItem { - String itemKey; - } - - static class Order { - Collection items = new ArrayList<>(); - } - - static class WithExplicitTargetTypes { - - @Field(targetType = FieldType.SCRIPT) // - String script; - - @Field(targetType = FieldType.SCRIPT) // - List scripts; - - @Field(targetType = FieldType.DECIMAL128) // - BigDecimal bigDecimal; - - @Field(targetType = FieldType.INT64) // - Date dateAsLong; - - @Field(targetType = FieldType.DATE_TIME) // - Long longAsDate; - - @Field(targetType = FieldType.BOOLEAN) // - String stringAsBoolean; - - @Field(targetType = FieldType.OBJECT_ID) // - Date dateAsObjectId; - } - - static class WrapperAroundWithUnwrapped { - - String someValue; - WithNullableUnwrapped nullableEmbedded; - WithEmptyUnwrappedType emptyEmbedded; - WithPrefixedNullableUnwrapped prefixedEmbedded; - } - - static class WithNullableUnwrapped { - - String id; - - @Unwrapped.Nullable EmbeddableType embeddableValue; - } - - static class WithPrefixedNullableUnwrapped { - - String id; - - @Unwrapped.Nullable("prefix-") EmbeddableType embeddableValue; - } - - static class WithEmptyUnwrappedType { - - String id; - - @Unwrapped.Empty EmbeddableType embeddableValue; - } - - @EqualsAndHashCode - static class EmbeddableType { - - String stringValue; - List listValue; - - @Field("with-at-field-annotation") // - String atFieldAnnotatedValue; - - @Transient // - String transientValue; - - Address address; - } - - static class ReturningAfterConvertCallback implements AfterConvertCallback { - - @Override - public Person onAfterConvert(Person entity, org.bson.Document document, String collection) { - - return entity; - } - } - - static class SubTypeOfGenericType extends GenericType { - - } - - @ReadingConverter - static class GenericTypeConverter implements Converter> { - - @Override - public GenericType convert(org.bson.Document source) { - - GenericType target = new GenericType<>(); - target.content = source.get("value"); - return target; - } - } - - @ReadingConverter - static class SubTypeOfGenericTypeConverter implements Converter { - - @Override - public SubTypeOfGenericType convert(org.bson.Document source) { - - SubTypeOfGenericType target = new SubTypeOfGenericType(); - target.content = source.getString("value") + "_s"; - return target; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoConvertersIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoConvertersIntegrationTests.java deleted file mode 100644 index b350545fcb..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoConvertersIntegrationTests.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2012-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; - -import java.util.UUID; - -import org.bson.types.Binary; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -/** - * Integration tests for {@link MongoConverters}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -public class MongoConvertersIntegrationTests { - - static final String COLLECTION = "converter-tests"; - - @Template // - static MongoTestTemplate template; - - @BeforeEach - public void setUp() { - template.flush(COLLECTION); - } - - @Test // DATAMONGO-422 - public void writesUUIDBinaryCorrectly() { - - Wrapper wrapper = new Wrapper(); - wrapper.uuid = UUID.randomUUID(); - template.save(wrapper); - - assertThat(wrapper.id).isNotNull(); - - Wrapper result = template.findOne(Query.query(Criteria.where("id").is(wrapper.id)), Wrapper.class); - assertThat(result.uuid).isEqualTo(wrapper.uuid); - } - - @Test // DATAMONGO-1802 - public void shouldConvertBinaryDataOnRead() { - - WithBinaryDataInArray wbd = new WithBinaryDataInArray(); - wbd.data = "calliope-mini".getBytes(); - - template.save(wbd); - - assertThat(template.findOne(query(where("id").is(wbd.id)), WithBinaryDataInArray.class)).isEqualTo(wbd); - } - - @Test // DATAMONGO-1802 - public void shouldConvertEmptyBinaryDataOnRead() { - - WithBinaryDataInArray wbd = new WithBinaryDataInArray(); - wbd.data = new byte[0]; - - template.save(wbd); - - assertThat(template.findOne(query(where("id").is(wbd.id)), WithBinaryDataInArray.class)).isEqualTo(wbd); - } - - @Test // DATAMONGO-1802 - public void shouldReadBinaryType() { - - WithBinaryDataType wbd = new WithBinaryDataType(); - wbd.data = new Binary("calliope-mini".getBytes()); - - template.save(wbd); - - assertThat(template.findOne(query(where("id").is(wbd.id)), WithBinaryDataType.class)).isEqualTo(wbd); - } - - @Document(COLLECTION) - static class Wrapper { - - String id; - UUID uuid; - } - - @Data - @Document(COLLECTION) - static class WithBinaryDataInArray { - - @Id String id; - byte[] data; - } - - @Data - @Document(COLLECTION) - static class WithBinaryDataType { - - @Id String id; - Binary data; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoConvertersUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoConvertersUnitTests.java deleted file mode 100644 index f7c67a09f9..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoConvertersUnitTests.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2011-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; - -import java.math.BigDecimal; -import java.net.URI; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Currency; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -import org.assertj.core.data.TemporalUnitLessThanOffset; -import org.bson.BsonTimestamp; -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.core.convert.support.ConfigurableConversionService; -import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.data.geo.Box; -import org.springframework.data.geo.Circle; -import org.springframework.data.geo.Point; -import org.springframework.data.geo.Polygon; -import org.springframework.data.geo.Shape; -import org.springframework.data.mongodb.core.convert.MongoConverters.AtomicIntegerToIntegerConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.AtomicLongToLongConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.BigDecimalToStringConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.BsonTimestampToInstantConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.CurrencyToStringConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.DocumentToStringConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.IntegerToAtomicIntegerConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.LongToAtomicLongConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigDecimalConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.StringToCurrencyConverter; -import org.springframework.data.mongodb.core.geo.Sphere; - -/** - * Unit tests for {@link MongoConverters}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - */ -class MongoConvertersUnitTests { - - @Test - void convertsBigDecimalToStringAndBackCorrectly() { - - BigDecimal bigDecimal = BigDecimal.valueOf(254, 1); - String value = BigDecimalToStringConverter.INSTANCE.convert(bigDecimal); - assertThat(value).isEqualTo("25.4"); - - BigDecimal reference = StringToBigDecimalConverter.INSTANCE.convert(value); - assertThat(reference).isEqualTo(bigDecimal); - } - - @Test // DATAMONGO-858 - void convertsBoxToDocumentAndBackCorrectly() { - - Box box = new Box(new Point(1, 2), new Point(3, 4)); - - Document document = GeoConverters.BoxToDocumentConverter.INSTANCE.convert(box); - Shape shape = GeoConverters.DocumentToBoxConverter.INSTANCE.convert(document); - - assertThat(shape).isEqualTo(box); - } - - @Test // DATAMONGO-858 - void convertsCircleToDocumentAndBackCorrectly() { - - Circle circle = new Circle(new Point(1, 2), 3); - - Document document = GeoConverters.CircleToDocumentConverter.INSTANCE.convert(circle); - Shape shape = GeoConverters.DocumentToCircleConverter.INSTANCE.convert(document); - - assertThat(shape).isEqualTo(circle); - } - - @Test // DATAMONGO-858 - void convertsPolygonToDocumentAndBackCorrectly() { - - Polygon polygon = new Polygon(new Point(1, 2), new Point(2, 3), new Point(3, 4), new Point(5, 6)); - - Document document = GeoConverters.PolygonToDocumentConverter.INSTANCE.convert(polygon); - Shape shape = GeoConverters.DocumentToPolygonConverter.INSTANCE.convert(document); - - assertThat(shape).isEqualTo(polygon); - } - - @Test // DATAMONGO-858 - void convertsSphereToDocumentAndBackCorrectly() { - - Sphere sphere = new Sphere(new Point(1, 2), 3); - - Document document = GeoConverters.SphereToDocumentConverter.INSTANCE.convert(sphere); - org.springframework.data.geo.Shape shape = GeoConverters.DocumentToSphereConverter.INSTANCE.convert(document); - - assertThat(shape).isEqualTo(sphere); - } - - @Test // DATAMONGO-858 - void convertsPointToListAndBackCorrectly() { - - Point point = new Point(1, 2); - - Document document = GeoConverters.PointToDocumentConverter.INSTANCE.convert(point); - org.springframework.data.geo.Point converted = GeoConverters.DocumentToPointConverter.INSTANCE.convert(document); - - assertThat(converted).isEqualTo(point); - } - - @Test // DATAMONGO-1372 - void convertsCurrencyToStringCorrectly() { - assertThat(CurrencyToStringConverter.INSTANCE.convert(Currency.getInstance("USD"))).isEqualTo("USD"); - } - - @Test // DATAMONGO-1372 - void convertsStringToCurrencyCorrectly() { - assertThat(StringToCurrencyConverter.INSTANCE.convert("USD")).isEqualTo(Currency.getInstance("USD")); - } - - @Test // DATAMONGO-1416 - void convertsAtomicLongToLongCorrectly() { - assertThat(AtomicLongToLongConverter.INSTANCE.convert(new AtomicLong(100L))).isEqualTo(100L); - } - - @Test // DATAMONGO-1416 - void convertsAtomicIntegerToIntegerCorrectly() { - assertThat(AtomicIntegerToIntegerConverter.INSTANCE.convert(new AtomicInteger(100))).isEqualTo(100); - } - - @Test // DATAMONGO-1416 - void convertsLongToAtomicLongCorrectly() { - assertThat(LongToAtomicLongConverter.INSTANCE.convert(100L)).isInstanceOf(AtomicLong.class); - } - - @Test // DATAMONGO-1416 - void convertsIntegerToAtomicIntegerCorrectly() { - assertThat(IntegerToAtomicIntegerConverter.INSTANCE.convert(100)).isInstanceOf(AtomicInteger.class); - } - - @Test // DATAMONGO-2113 - void convertsBsonTimestampToInstantCorrectly() { - - assertThat(BsonTimestampToInstantConverter.INSTANCE.convert(new BsonTimestamp(6615900307735969796L))) - .isCloseTo(Instant.ofEpochSecond(1540384327), new TemporalUnitLessThanOffset(100, ChronoUnit.MILLIS)); - } - - @Test // DATAMONGO-2210 - void convertsUrisToString() { - - MongoCustomConversions conversions = new MongoCustomConversions(); - - assertThat(conversions.getSimpleTypeHolder().isSimpleType(URI.class)).isTrue(); - - ConfigurableConversionService conversionService = new DefaultConversionService(); - conversions.registerConvertersIn(conversionService); - - assertThat(conversionService.convert(URI.create("/segment"), String.class)).isEqualTo("/segment"); - assertThat(conversionService.convert("/segment", URI.class)).isEqualTo(URI.create("/segment")); - } - - @Test // GH-3546 - void convertsDocumentWithUUidToString() { - - UUID uuid = UUID.randomUUID(); - assertThat(DocumentToStringConverter.INSTANCE.convert(new Document("_id", uuid))) - .isEqualTo("{\"_id\": \"" + uuid.toString() + "\"}"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoCustomConversionsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoCustomConversionsUnitTests.java deleted file mode 100644 index c97cbc1422..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoCustomConversionsUnitTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2019-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; - -import java.time.ZonedDateTime; -import java.util.Collections; -import java.util.Date; - -import org.junit.jupiter.api.Test; - -import org.springframework.core.convert.converter.Converter; - -/** - * Unit tests for {@link MongoCustomConversions}. - * - * @author Christoph Strobl - */ -class MongoCustomConversionsUnitTests { - - @Test // DATAMONGO-2349 - void nonAnnotatedConverterForJavaTimeTypeShouldOnlyBeRegisteredAsReadingConverter() { - - MongoCustomConversions conversions = new MongoCustomConversions( - Collections.singletonList(new DateToZonedDateTimeConverter())); - - assertThat(conversions.hasCustomReadTarget(Date.class, ZonedDateTime.class)).isTrue(); - assertThat(conversions.hasCustomWriteTarget(Date.class)).isFalse(); - } - - static class DateToZonedDateTimeConverter implements Converter { - - @Override - public ZonedDateTime convert(Date source) { - return ZonedDateTime.now(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java deleted file mode 100644 index ef92b8ff0c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoExampleMapperUnitTests.java +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright 2015-2021 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.convert; - -import static org.springframework.data.domain.Example.*; -import static org.springframework.data.domain.ExampleMatcher.*; -import static org.springframework.data.mongodb.core.DocumentTestUtils.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; - -import org.bson.conversions.Bson; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.ExampleMatcher; -import org.springframework.data.domain.ExampleMatcher.*; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.QueryMapperUnitTests.ClassWithGeoTypes; -import org.springframework.data.mongodb.core.convert.QueryMapperUnitTests.WithDBRef; -import org.springframework.data.mongodb.core.index.Indexed; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.data.mongodb.core.query.UntypedExampleMatcher; -import org.springframework.data.util.TypeInformation; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class MongoExampleMapperUnitTests { - - MongoExampleMapper mapper; - MongoMappingContext context; - MappingMongoConverter converter; - - @Mock MongoDatabaseFactory factory; - - @BeforeEach - public void setUp() { - - this.context = new MongoMappingContext(); - - this.converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context); - this.converter.afterPropertiesSet(); - - this.mapper = new MongoExampleMapper(converter); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenIdIsSet() { - - FlatDocument probe = new FlatDocument(); - probe.id = "steelheart"; - - assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("_id", "steelheart"); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenMultipleValuesSet() { - - FlatDocument probe = new FlatDocument(); - probe.id = "steelheart"; - probe.stringValue = "firefight"; - probe.intValue = 100; - - assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("_id", "steelheart") // - .containsEntry("stringValue", "firefight") // - .containsEntry("intValue", 100); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenIdIsNotSet() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "firefight"; - probe.intValue = 100; - - assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class))) // - .containsEntry("stringValue", "firefight") // - .containsEntry("intValue", 100); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenListHasValues() { - - FlatDocument probe = new FlatDocument(); - probe.listOfString = Arrays.asList("Prof", "Tia", "David"); - - List list = (Arrays.asList("Prof", "Tia", "David")); - - assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("listOfString", list); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenFieldNameIsCustomized() { - - FlatDocument probe = new FlatDocument(); - probe.customNamedField = "Mitosis"; - - assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("custom_field_name", "Mitosis"); - } - - @Test // DATAMONGO-1245 - public void typedExampleShouldContainTypeRestriction() { - - WrapperDocument probe = new WrapperDocument(); - probe.flatDoc = new FlatDocument(); - probe.flatDoc.stringValue = "conflux"; - - org.bson.Document document = mapper.getMappedExample(Example.of(probe), - context.getRequiredPersistentEntity(WrapperDocument.class)); - - assertThat(document).containsEntry("_class", - new org.bson.Document("$in", Collections.singletonList(probe.getClass().getName()))); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedAsFlatMapWhenGivenNestedElementsWithLenientMatchMode() { - - WrapperDocument probe = new WrapperDocument(); - probe.flatDoc = new FlatDocument(); - probe.flatDoc.stringValue = "conflux"; - - assertThat(mapper.getMappedExample(of(probe), context.getRequiredPersistentEntity(WrapperDocument.class))) - .containsEntry("flatDoc\\.stringValue", "conflux"); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedAsExactObjectWhenGivenNestedElementsWithStrictMatchMode() { - - WrapperDocument probe = new WrapperDocument(); - probe.flatDoc = new FlatDocument(); - probe.flatDoc.stringValue = "conflux"; - - Example example = Example.of(probe, matching().withIncludeNullValues()); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(WrapperDocument.class))) - .containsEntry("flatDoc.stringValue", "conflux"); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenStringMatchModeIsStarting() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "firefight"; - probe.intValue = 100; - - Example example = Example.of(probe, matching().withStringMatcher(StringMatcher.STARTING)); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("stringValue.$regex", "^firefight")// - .containsEntry("intValue", 100); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeContainingDotsWhenStringMatchModeIsStarting() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "fire.ight"; - probe.intValue = 100; - - Example example = Example.of(probe, matching().withStringMatcher(StringMatcher.STARTING)); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("stringValue.$regex", "^" + Pattern.quote("fire.ight"))// - .containsEntry("intValue", 100); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenStringMatchModeIsEnding() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "firefight"; - probe.intValue = 100; - - Example example = Example.of(probe, matching().withStringMatcher(StringMatcher.ENDING)); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("stringValue.$regex", "firefight$") // - .containsEntry("intValue", 100); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenStringMatchModeRegex() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "firefight"; - probe.customNamedField = "^(cat|dog).*shelter\\d?"; - - Example example = Example.of(probe, matching().withStringMatcher(StringMatcher.REGEX)); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("stringValue.$regex", "firefight") // - .containsEntry("custom_field_name.$regex", "^(cat|dog).*shelter\\d?"); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenIgnoreCaseEnabledAndMatchModeSet() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "firefight"; - probe.intValue = 100; - - Example example = Example.of(probe, matching().withStringMatcher(StringMatcher.ENDING).withIgnoreCase()); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("stringValue", new org.bson.Document("$regex", "firefight$").append("$options", "i")) // - .containsEntry("intValue", 100); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyForFlatTypeWhenIgnoreCaseEnabled() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "firefight"; - probe.intValue = 100; - - Example example = Example.of(probe, matching().withIgnoreCase()); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("stringValue", - new org.bson.Document("$regex", Pattern.quote("firefight")).append("$options", "i")) // - .containsEntry("intValue", 100); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedWhenContainingDBRef() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "steelheart"; - probe.referenceDocument = new ReferenceDocument(); - probe.referenceDocument.id = "200"; - - org.bson.Document document = mapper.getMappedExample(of(probe), - context.getRequiredPersistentEntity(WithDBRef.class)); - com.mongodb.DBRef reference = getTypedValue(document, "referenceDocument", com.mongodb.DBRef.class); - - assertThat(reference.getId()).isEqualTo("200"); - assertThat(reference.getCollectionName()).isEqualTo("refDoc"); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedWhenDBRefIsNull() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "steelheart"; - - org.bson.Document document = mapper.getMappedExample(of(probe), - context.getRequiredPersistentEntity(FlatDocument.class)); - - assertThat(document).containsEntry("stringValue", "steelheart"); - } - - @Test // DATAMONGO-1245 - public void exampleShouldBeMappedCorrectlyWhenContainingLegacyPoint() { - - ClassWithGeoTypes probe = new ClassWithGeoTypes(); - probe.legacyPoint = new Point(10D, 20D); - - org.bson.Document document = mapper.getMappedExample(of(probe), - context.getRequiredPersistentEntity(WithDBRef.class)); - - assertThat(document.get("legacyPoint.x")).isEqualTo(10D); - assertThat(document.get("legacyPoint.y")).isEqualTo(20D); - } - - @Test // DATAMONGO-1245 - public void mappingShouldExcludeFieldWithCustomNameCorrectly() { - - FlatDocument probe = new FlatDocument(); - probe.customNamedField = "foo"; - probe.intValue = 10; - probe.stringValue = "string"; - - Example example = Example.of(probe, matching().withIgnorePaths("customNamedField")); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("stringValue", "string") // - .containsEntry("intValue", 10); - } - - @Test // DATAMONGO-1245 - public void mappingShouldExcludeFieldCorrectly() { - - FlatDocument probe = new FlatDocument(); - probe.customNamedField = "foo"; - probe.intValue = 10; - probe.stringValue = "string"; - - Example example = Example.of(probe, matching().withIgnorePaths("stringValue")); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("custom_field_name", "foo") // - .containsEntry("intValue", 10); - } - - @Test // DATAMONGO-1245 - public void mappingShouldExcludeNestedFieldCorrectly() { - - WrapperDocument probe = new WrapperDocument(); - probe.flatDoc = new FlatDocument(); - probe.flatDoc.customNamedField = "foo"; - probe.flatDoc.intValue = 10; - probe.flatDoc.stringValue = "string"; - - Example example = Example.of(probe, matching().withIgnorePaths("flatDoc.stringValue")); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(WrapperDocument.class))) - .containsEntry("flatDoc\\.custom_field_name", "foo")// - .containsEntry("flatDoc\\.intValue", 10); - } - - @Test // DATAMONGO-1245 - public void mappingShouldExcludeNestedFieldWithCustomNameCorrectly() { - - WrapperDocument probe = new WrapperDocument(); - probe.flatDoc = new FlatDocument(); - probe.flatDoc.customNamedField = "foo"; - probe.flatDoc.intValue = 10; - probe.flatDoc.stringValue = "string"; - - Example example = Example.of(probe, matching().withIgnorePaths("flatDoc.customNamedField")); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(WrapperDocument.class))) - .containsEntry("flatDoc\\.stringValue", "string") // - .containsEntry("flatDoc\\.intValue", 10); - } - - @Test // DATAMONGO-1245 - public void mappingShouldFavorFieldSpecificationStringMatcherOverDefaultStringMatcher() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "firefight"; - probe.customNamedField = "steelheart"; - - Example example = Example.of(probe, matching().withMatcher("stringValue", GenericPropertyMatchers.contains())); - - assertThat(mapper.getMappedExample(example, context.getRequiredPersistentEntity(FlatDocument.class))) - .containsEntry("stringValue.$regex", ".*firefight.*") // - .containsEntry("custom_field_name", "steelheart"); - } - - @Test // DATAMONGO-1245 - public void mappingShouldIncludePropertiesFromHierarchicalDocument() { - - HierachicalDocument probe = new HierachicalDocument(); - probe.stringValue = "firefight"; - probe.customNamedField = "steelheart"; - probe.anotherStringValue = "calamity"; - - org.bson.Document document = mapper.getMappedExample(of(probe), - context.getRequiredPersistentEntity(FlatDocument.class)); - - assertThat(document).containsEntry("anotherStringValue", "calamity"); - } - - @Test // DATAMONGO-1459 - public void mapsAnyMatchingExampleCorrectly() { - - FlatDocument probe = new FlatDocument(); - probe.stringValue = "firefight"; - probe.customNamedField = "steelheart"; - - Example example = Example.of(probe, ExampleMatcher.matchingAny()); - - assertThat(mapper.getMappedExample(example)).containsKeys("$or", "_class"); - } - - @Test // DATAMONGO-1768 - public void allowIgnoringTypeRestrictionBySettingUpTypeKeyAsAnIgnoredPath() { - - WrapperDocument probe = new WrapperDocument(); - probe.flatDoc = new FlatDocument(); - probe.flatDoc.stringValue = "conflux"; - - org.bson.Document document = mapper - .getMappedExample(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_class"))); - - assertThat(document).doesNotContainKey("_class"); - } - - @Test // DATAMONGO-1768 - public void allowIgnoringTypeRestrictionBySettingUpTypeKeyAsAnIgnoredPathWhenUsingCustomTypeMapper() { - - WrapperDocument probe = new WrapperDocument(); - probe.flatDoc = new FlatDocument(); - probe.flatDoc.stringValue = "conflux"; - - MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context); - mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper() { - - @Override - public boolean isTypeKey(String key) { - return "_foo".equals(key); - } - - @Override - public void writeTypeRestrictions(org.bson.Document result, Set> restrictedTypes) { - result.put("_foo", "bar"); - } - - @Override - public void writeType(TypeInformation info, Bson sink) { - ((org.bson.Document) sink).put("_foo", "bar"); - - } - }); - mappingMongoConverter.afterPropertiesSet(); - - org.bson.Document document = new MongoExampleMapper(mappingMongoConverter) - .getMappedExample(Example.of(probe, ExampleMatcher.matching().withIgnorePaths("_foo"))); - - assertThat(document).doesNotContainKeys("_class", "_foo"); - } - - @Test // DATAMONGO-1768 - public void untypedExampleShouldNotInferTypeRestriction() { - - WrapperDocument probe = new WrapperDocument(); - probe.flatDoc = new FlatDocument(); - probe.flatDoc.stringValue = "conflux"; - - org.bson.Document document = mapper.getMappedExample(Example.of(probe, UntypedExampleMatcher.matching())); - assertThat(document).doesNotContainKey("_class"); - } - - @Test // DATAMONGO-1902 - void mapsUnwrappedType() { - - WithUnwrapped probe = new WithUnwrapped(); - probe.unwrappedValue = new UnwrappableType(); - probe.unwrappedValue.atFieldAnnotatedValue = "@Field"; - probe.unwrappedValue.stringValue = "string-value"; - - org.bson.Document document = mapper.getMappedExample(Example.of(probe, UntypedExampleMatcher.matching())); - assertThat(document).containsEntry("stringValue", "string-value").containsEntry("with-at-field-annotation", - "@Field"); - } - - @Test // DATAMONGO-1902 - void mapsPrefixedUnwrappedType() { - - WithUnwrapped probe = new WithUnwrapped(); - probe.prefixedUnwrappedValue = new UnwrappableType(); - probe.prefixedUnwrappedValue.atFieldAnnotatedValue = "@Field"; - probe.prefixedUnwrappedValue.stringValue = "string-value"; - - org.bson.Document document = mapper.getMappedExample(Example.of(probe, UntypedExampleMatcher.matching())); - assertThat(document).containsEntry("prefix-stringValue", "string-value") - .containsEntry("prefix-with-at-field-annotation", "@Field"); - } - - @Test // DATAMONGO-1902 - void mapsNestedUnwrappedType() { - - WrapperAroundWithUnwrapped probe = new WrapperAroundWithUnwrapped(); - probe.withUnwrapped = new WithUnwrapped(); - probe.withUnwrapped.unwrappedValue = new UnwrappableType(); - probe.withUnwrapped.unwrappedValue.atFieldAnnotatedValue = "@Field"; - probe.withUnwrapped.unwrappedValue.stringValue = "string-value"; - - org.bson.Document document = mapper.getMappedExample(Example.of(probe, UntypedExampleMatcher.matching())); - assertThat(document).containsEntry("withUnwrapped.stringValue", "string-value") - .containsEntry("withUnwrapped.with-at-field-annotation", "@Field"); - } - - @Test // DATAMONGO-1902 - void mapsNestedPrefixedUnwrappedType() { - - WrapperAroundWithUnwrapped probe = new WrapperAroundWithUnwrapped(); - probe.withUnwrapped = new WithUnwrapped(); - probe.withUnwrapped.prefixedUnwrappedValue = new UnwrappableType(); - probe.withUnwrapped.prefixedUnwrappedValue.atFieldAnnotatedValue = "@Field"; - probe.withUnwrapped.prefixedUnwrappedValue.stringValue = "string-value"; - - org.bson.Document document = mapper.getMappedExample(Example.of(probe, UntypedExampleMatcher.matching())); - assertThat(document).containsEntry("withUnwrapped.prefix-stringValue", "string-value") - .containsEntry("withUnwrapped.prefix-with-at-field-annotation", "@Field"); - } - - static class FlatDocument { - - @Id String id; - String stringValue; - @Field("custom_field_name") String customNamedField; - Integer intValue; - List listOfString; - @DBRef ReferenceDocument referenceDocument; - } - - static class HierachicalDocument extends FlatDocument { - - String anotherStringValue; - } - - static class WrapperDocument { - - @Id String id; - FlatDocument flatDoc; - } - - @Document("refDoc") - static class ReferenceDocument { - - @Id String id; - String value; - } - - @Document - static class WrapperAroundWithUnwrapped { - - String id; - WithUnwrapped withUnwrapped; - } - - @Document - static class WithUnwrapped { - - String id; - - @Unwrapped.Nullable UnwrappableType unwrappedValue; - @Unwrapped.Nullable("prefix-") UnwrappableType prefixedUnwrappedValue; - } - - static class UnwrappableType { - - @Indexed String stringValue; - - @Indexed // - @Field("with-at-field-annotation") // - String atFieldAnnotatedValue; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapperUnitTests.java deleted file mode 100644 index 8630ac0ddf..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapperUnitTests.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2018-2021 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.convert; - -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.schema.MongoJsonSchema; - -/** - * Unit tests for {@link MongoJsonSchemaMapper}. - * - * @author Christoph Strobl - */ -public class MongoJsonSchemaMapperUnitTests { - - MongoJsonSchemaMapper mapper; - - Document addressProperty = new Document("type", "object").append("required", Arrays.asList("street", "postCode")) - .append("properties", - new Document("street", new Document("type", "string")).append("postCode", new Document("type", "string"))); - - Document mappedAddressProperty = new Document("type", "object") - .append("required", Arrays.asList("street", "post_code")).append("properties", - new Document("street", new Document("type", "string")).append("post_code", new Document("type", "string"))); - - Document nameProperty = new Document("type", "string"); - Document gradePointAverageProperty = new Document("bsonType", "double"); - Document yearProperty = new Document("bsonType", "int").append("minimum", 2017).append("maximum", 3017) - .append("exclusiveMaximum", true); - - Document properties = new Document("name", nameProperty) // - .append("gradePointAverage", gradePointAverageProperty) // - .append("year", yearProperty); - - Document mappedProperties = new Document("name", new Document(nameProperty)) // - .append("gpa", new Document(gradePointAverageProperty)) // - .append("year", new Document(yearProperty)); - - List requiredProperties = Arrays.asList("name", "gradePointAverage"); - List mappedRequiredProperties = Arrays.asList("name", "gpa"); - - Document $jsonSchema = new Document("type", "object") // - .append("required", requiredProperties) // - .append("properties", properties); - - Document mapped$jsonSchema = new Document("type", "object") // - .append("required", mappedRequiredProperties) // - .append("properties", mappedProperties); - - Document sourceSchemaDocument = new Document("$jsonSchema", $jsonSchema); - Document mappedSchemaDocument = new Document("$jsonSchema", mapped$jsonSchema); - - String complexSchemaJsonString = "{ $jsonSchema: {" + // - " type: \"object\"," + // - " required: [ \"name\", \"year\", \"major\", \"gpa\" ]," + // - " properties: {" + // - " name: {" + // - " type: \"string\"," + // - " description: \"must be a string and is required\"" + // - " }," + // - " gender: {" + // - " type: \"string\"," + // - " description: \"must be a string and is not required\"" + // - " }," + // - " year: {" + // - " bsonType: \"int\"," + // - " minimum: 2017," + // - " maximum: 3017," + // - " exclusiveMaximum: true," + // - " description: \"must be an integer in [ 2017, 3017 ] and is required\"" + // - " }," + // - " major: {" + // - " type: \"string\"," + // - " enum: [ \"Math\", \"English\", \"Computer Science\", \"History\", null ]," + // - " description: \"can only be one of the enum values and is required\"" + // - " }," + // - " gpa: {" + // - " bsonType: \"double\"," + // - " description: \"must be a double and is required\"" + // - " }" + // - " }" + // - " } }"; - - @BeforeEach - public void setUp() { - mapper = new MongoJsonSchemaMapper(new MappingMongoConverter(mock(DbRefResolver.class), new MongoMappingContext())); - } - - @Test // DATAMONGO-1835 - public void noNullSchemaAllowed() { - - assertThatIllegalArgumentException().isThrownBy(() -> mapper.mapSchema(null, Object.class)); - } - - @Test // DATAMONGO-1835 - public void noNullDomainTypeAllowed() { - - assertThatIllegalArgumentException() - .isThrownBy(() -> mapper.mapSchema(new Document("$jsonSchema", new Document()), null)); - } - - @Test // DATAMONGO-1835 - public void schemaDocumentMustContain$jsonSchemaField() { - assertThatIllegalArgumentException() - .isThrownBy(() -> mapper.mapSchema(new Document("foo", new Document()), Object.class)); - } - - @Test // DATAMONGO-1835 - public void objectTypeSkipsFieldMapping() { - assertThat(mapper.mapSchema(sourceSchemaDocument, Object.class)).isEqualTo(sourceSchemaDocument); - } - - @Test // DATAMONGO-1835 - public void mapSchemaProducesNewDocument() { - assertThat(mapper.mapSchema(sourceSchemaDocument, Object.class)).isNotSameAs(sourceSchemaDocument); - } - - @Test // DATAMONGO-1835 - public void mapSchemaMapsPropertiesToFieldNames() { - assertThat(mapper.mapSchema(sourceSchemaDocument, Student.class)).isEqualTo(mappedSchemaDocument); - } - - @Test // DATAMONGO-1835 - public void mapSchemaLeavesSourceDocumentUntouched() { - - Document source = Document.parse(complexSchemaJsonString); - mapper.mapSchema(source, Student.class); - - assertThat(source).isEqualTo(Document.parse(complexSchemaJsonString)); - } - - @Test // DATAMONGO-1835 - public void mapsNestedPropertiesCorrectly() { - - Document schema = new Document("$jsonSchema", new Document("type", "object") // - .append("properties", new Document(properties).append("address", addressProperty))); - - Document expectedSchema = new Document("$jsonSchema", new Document("type", "object") // - .append("properties", new Document(mappedProperties).append("address", mappedAddressProperty))); - - assertThat(mapper.mapSchema(schema, Student.class)).isEqualTo(expectedSchema); - } - - @Test // DATAMONGO-1835 - public void constructReferenceSchemaCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder() // - .required("name", "year", "major", "gradePointAverage").description("") // - .properties(string("name").description("must be a string and is required"), // - string("gender").description("must be a string and is not required"), // - int32("year").description("must be an integer in [ 2017, 3017 ] and is required").gte(2017).lt(3017), // - string("major").description("can only be one of the enum values and is required").possibleValues("Math", - "English", "Computer Science", "History", null), // - float64("gradePointAverage").description("must be a double and is required") // - ).build(); - - assertThat(mapper.mapSchema(schema.toDocument(), Student.class)).isEqualTo(Document.parse(complexSchemaJsonString)); - } - - static class Student { - - String name; - Gender gender; - Integer year; - String major; - - @Field("gpa") // - Double gradePointAverage; - Address address; - } - - static class Address { - - String city; - String street; - - @Field("post_code") // - String postCode; - } - - static enum Gender { - M, F - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/NamedMongoScriptConvertsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/NamedMongoScriptConvertsUnitTests.java deleted file mode 100644 index 982437a09f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/NamedMongoScriptConvertsUnitTests.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2014-2021 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.convert; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import org.bson.Document; -import org.bson.types.Code; -import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.mongodb.core.convert.MongoConverters.DocumentToNamedMongoScriptConverter; -import org.springframework.data.mongodb.core.convert.MongoConverters.NamedMongoScriptToDocumentConverter; -import org.springframework.data.mongodb.core.convert.NamedMongoScriptConvertsUnitTests.DocumentToNamedMongoScriptConverterUnitTests; -import org.springframework.data.mongodb.core.convert.NamedMongoScriptConvertsUnitTests.NamedMongoScriptToDocumentConverterUnitTests; -import org.springframework.data.mongodb.core.script.NamedMongoScript; - -/** - * Unit tests for {@link Converter} implementations for {@link NamedMongoScript}. - * - * @author Christoph Strobl - * @author Oliver Gierke - * @since 1.7 - */ -@RunWith(Suite.class) -@SuiteClasses({ NamedMongoScriptToDocumentConverterUnitTests.class, - DocumentToNamedMongoScriptConverterUnitTests.class }) -public class NamedMongoScriptConvertsUnitTests { - - static final String FUNCTION_NAME = "echo"; - static final String JS_FUNCTION = "function(x) { return x; }"; - static final NamedMongoScript ECHO_SCRIPT = new NamedMongoScript(FUNCTION_NAME, JS_FUNCTION); - static final Document FUNCTION = new org.bson.Document().append("_id", FUNCTION_NAME).append("value", - new Code(JS_FUNCTION)); - - /** - * @author Christoph Strobl - */ - public static class NamedMongoScriptToDocumentConverterUnitTests { - - NamedMongoScriptToDocumentConverter converter = NamedMongoScriptToDocumentConverter.INSTANCE; - - @Test // DATAMONGO-479 - public void convertShouldConvertScriptNameCorreclty() { - - Document document = converter.convert(ECHO_SCRIPT); - - Object id = document.get("_id"); - assertThat(id).isInstanceOf(String.class).isEqualTo(FUNCTION_NAME); - } - - @Test // DATAMONGO-479 - public void convertShouldConvertScriptCodeCorreclty() { - - Document document = converter.convert(ECHO_SCRIPT); - - Object code = document.get("value"); - assertThat(code).isInstanceOf(Code.class).isEqualTo(new Code(JS_FUNCTION)); - } - } - - /** - * @author Christoph Strobl - */ - public static class DocumentToNamedMongoScriptConverterUnitTests { - - DocumentToNamedMongoScriptConverter converter = DocumentToNamedMongoScriptConverter.INSTANCE; - - @Test // DATAMONGO-479, DATAMONGO-2385 - public void convertShouldReturnNullIfSourceIsEmpty() { - assertThat(converter.convert(new Document())).isNull(); - } - - @Test // DATAMONGO-479 - public void convertShouldConvertIdCorreclty() { - - NamedMongoScript script = converter.convert(FUNCTION); - - assertThat(script.getName()).isEqualTo(FUNCTION_NAME); - } - - @Test // DATAMONGO-479 - public void convertShouldConvertScriptValueCorreclty() { - - NamedMongoScript script = converter.convert(FUNCTION); - - assertThat(script.getCode()).isEqualTo(JS_FUNCTION); - } - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/NumberToNumberConverterFactoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/NumberToNumberConverterFactoryUnitTests.java deleted file mode 100644 index 2a34b0a2cb..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/NumberToNumberConverterFactoryUnitTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2015-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; - -import org.springframework.data.mongodb.core.convert.MongoConverters.NumberToNumberConverterFactory; - -/** - * @author Christoph Strobl - */ -@RunWith(Parameterized.class) -public class NumberToNumberConverterFactoryUnitTests { - - public @Parameter(0) Number source; - - public @Parameter(1) Number expected; - - @Parameters - public static Collection parameters() { - - Number[] longToInt = new Number[] { new Long(10), new Integer(10) }; - Number[] atomicIntToInt = new Number[] { new AtomicInteger(10), new Integer(10) }; - Number[] atomicIntToDouble = new Number[] { new AtomicInteger(10), new Double(10) }; - Number[] atomicLongToInt = new Number[] { new AtomicLong(10), new Integer(10) }; - Number[] atomicLongToLong = new Number[] { new AtomicLong(10), new Long(10) }; - - return Arrays. asList(longToInt, atomicIntToInt, atomicIntToDouble, atomicLongToInt, atomicLongToLong); - } - - @Test // DATAMONGO-1288 - public void convertsToTargetTypeCorrectly() { - assertThat(NumberToNumberConverterFactory.INSTANCE.getConverter(expected.getClass()).convert(source)) - .isEqualTo(expected); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java deleted file mode 100644 index a3621a8fc2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2017-2021 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.convert; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.util.ClassTypeInformation; - -/** - * Unit tests for {@link ObjectPath}. - * - * @author Christoph Strobl - */ -public class ObjectPathUnitTests { - - MongoPersistentEntity one; - MongoPersistentEntity two; - MongoPersistentEntity three; - - @BeforeEach - public void setUp() { - - one = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(EntityOne.class)); - two = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(EntityTwo.class)); - three = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(EntityThree.class)); - } - - @Test // DATAMONGO-1703 - public void getPathItemShouldReturnMatch() { - - ObjectPath path = ObjectPath.ROOT.push(new EntityOne(), one, "id-1"); - - assertThat(path.getPathItem("id-1", "one", EntityOne.class)).isNotNull(); - } - - @Test // DATAMONGO-1703 - public void getPathItemShouldReturnNullWhenNoTypeMatchFound() { - - ObjectPath path = ObjectPath.ROOT.push(new EntityOne(), one, "id-1"); - - assertThat(path.getPathItem("id-1", "one", EntityThree.class)).isNull(); - } - - @Test // DATAMONGO-1703 - public void getPathItemShouldReturnCachedItemWhenIdAndCollectionMatchAndIsAssignable() { - - ObjectPath path = ObjectPath.ROOT.push(new EntityTwo(), one, "id-1"); - - assertThat(path.getPathItem("id-1", "one", EntityOne.class)).isNotNull(); - } - - @Test // DATAMONGO-1703 - public void getPathItemShouldReturnNullWhenIdAndCollectionMatchButNotAssignable() { - - ObjectPath path = ObjectPath.ROOT.push(new EntityOne(), one, "id-1"); - - assertThat(path.getPathItem("id-1", "one", EntityTwo.class)).isNull(); - } - - @Test // DATAMONGO-1703 - public void getPathItemShouldReturnNullWhenIdAndCollectionMatchAndAssignableToInterface() { - - ObjectPath path = ObjectPath.ROOT.push(new EntityThree(), one, "id-1"); - - assertThat(path.getPathItem("id-1", "one", ValueInterface.class)).isNotNull(); - } - - @Test // DATAMONGO-2267 - public void collectionLookupShouldBeLazy/* because we may need to resolve SpEL which can be pretty expensive */() { - - MongoPersistentEntity spied = spy(one); - ObjectPath path = ObjectPath.ROOT.push(new EntityThree(), spied, "id-1"); - - verify(spied, never()).getCollection(); - - path.getPathItem("id-1", "foo", EntityTwo.class); - - verify(spied).getCollection(); - } - - @Document("one") - static class EntityOne {} - - static class EntityTwo extends EntityOne {} - - interface ValueInterface {} - - @Document("three") - static class EntityThree implements ValueInterface {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java deleted file mode 100755 index e2f69260b1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java +++ /dev/null @@ -1,1490 +0,0 @@ -/* - * Copyright 2011-2021 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.convert; - -import static org.springframework.data.mongodb.core.DocumentTestUtils.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.bson.conversions.Bson; -import org.bson.types.Code; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.Transient; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.Person; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.geo.GeoJsonPolygon; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.FieldType; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.TextScore; -import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.TextQuery; - -import com.mongodb.BasicDBObject; -import com.mongodb.MongoClientSettings; -import com.mongodb.client.model.Filters; - -/** - * Unit tests for {@link QueryMapper}. - * - * @author Oliver Gierke - * @author Patryk Wasik - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class QueryMapperUnitTests { - - private QueryMapper mapper; - private MongoMappingContext context; - private MappingMongoConverter converter; - - @Mock MongoDatabaseFactory factory; - - @BeforeEach - void beforeEach() { - - this.context = new MongoMappingContext(); - - this.converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context); - this.converter.afterPropertiesSet(); - - this.mapper = new QueryMapper(converter); - } - - @Test - void translatesIdPropertyIntoIdKey() { - - org.bson.Document query = new org.bson.Document("foo", "value"); - MongoPersistentEntity entity = context.getRequiredPersistentEntity(Sample.class); - - org.bson.Document result = mapper.getMappedObject(query, entity); - assertThat(result).containsKey("_id"); - assertThat(result).doesNotContainKey("foo"); - } - - @Test - void convertsStringIntoObjectId() { - - org.bson.Document query = new org.bson.Document("_id", new ObjectId().toString()); - org.bson.Document result = mapper.getMappedObject(query, context.getPersistentEntity(IdWrapper.class)); - assertThat(result.get("_id")).isInstanceOf(ObjectId.class); - } - - @Test - void handlesBigIntegerIdsCorrectly() { - - org.bson.Document document = new org.bson.Document("id", new BigInteger("1")); - org.bson.Document result = mapper.getMappedObject(document, context.getPersistentEntity(IdWrapper.class)); - assertThat(result).containsEntry("_id", "1"); - } - - @Test - void handlesObjectIdCapableBigIntegerIdsCorrectly() { - - ObjectId id = new ObjectId(); - org.bson.Document document = new org.bson.Document("id", new BigInteger(id.toString(), 16)); - org.bson.Document result = mapper.getMappedObject(document, context.getPersistentEntity(IdWrapper.class)); - assertThat(result).containsEntry("_id", id); - } - - @Test // DATAMONGO-278 - void translates$NeCorrectly() { - - Criteria criteria = where("foo").ne(new ObjectId().toString()); - - org.bson.Document result = mapper.getMappedObject(criteria.getCriteriaObject(), - context.getPersistentEntity(Sample.class)); - Object object = result.get("_id"); - assertThat(object).isInstanceOf(org.bson.Document.class); - org.bson.Document document = (org.bson.Document) object; - assertThat(document.get("$ne")).isInstanceOf(ObjectId.class); - } - - @Test // DATAMONGO-326 - void handlesEnumsCorrectly() { - Query query = query(where("foo").is(Enum.INSTANCE)); - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), Optional.empty()); - - Object object = result.get("foo"); - assertThat(object).isInstanceOf(String.class); - } - - @Test - void handlesEnumsInNotEqualCorrectly() { - Query query = query(where("foo").ne(Enum.INSTANCE)); - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), Optional.empty()); - - Object object = result.get("foo"); - assertThat(object).isInstanceOf(org.bson.Document.class); - - Object ne = ((org.bson.Document) object).get("$ne"); - assertThat(ne).isInstanceOf(String.class).hasToString(Enum.INSTANCE.name()); - } - - @Test - void handlesEnumsIn$InCorrectly() { - - Query query = query(where("foo").in(Enum.INSTANCE)); - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), Optional.empty()); - - Object object = result.get("foo"); - assertThat(object).isInstanceOf(org.bson.Document.class); - - Object in = ((org.bson.Document) object).get("$in"); - assertThat(in).isInstanceOf(List.class); - - List list = (List) in; - assertThat(list).hasSize(1); - assertThat(list.get(0)).isInstanceOf(String.class).hasToString(Enum.INSTANCE.name()); - } - - @Test // DATAMONGO-373 - void handlesNativelyBuiltQueryCorrectly() { - - Bson query = new BasicDBObject(Filters.or(new BasicDBObject("foo", "bar")).toBsonDocument(org.bson.Document.class, - MongoClientSettings.getDefaultCodecRegistry())); - mapper.getMappedObject(query, Optional.empty()); - } - - @Test // DATAMONGO-369 - void handlesAllPropertiesIfDocument() { - - org.bson.Document query = new org.bson.Document(); - query.put("foo", new org.bson.Document("$in", Arrays.asList(1, 2))); - query.put("bar", new Person()); - - org.bson.Document result = mapper.getMappedObject(query, Optional.empty()); - assertThat(result).containsKey("bar"); - } - - @Test // DATAMONGO-429 - void transformsArraysCorrectly() { - - Query query = new BasicQuery("{ 'tags' : { '$all' : [ 'green', 'orange']}}"); - - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), Optional.empty()); - assertThat(result.toJson()).isEqualTo(query.getQueryObject().toJson()); - } - - @Test - void doesHandleNestedFieldsWithDefaultIdNames() { - - org.bson.Document document = new org.bson.Document("id", new ObjectId().toString()); - document.put("nested", new org.bson.Document("id", new ObjectId().toString())); - - MongoPersistentEntity entity = context.getRequiredPersistentEntity(ClassWithDefaultId.class); - - org.bson.Document result = mapper.getMappedObject(document, entity); - assertThat(result.get("_id")).isInstanceOf(ObjectId.class); - assertThat(((org.bson.Document) result.get("nested")).get("_id")).isInstanceOf(ObjectId.class); - } - - @Test // DATAMONGO-493 - void doesNotTranslateNonIdPropertiesFor$NeCriteria() { - - ObjectId accidentallyAnObjectId = new ObjectId(); - - Query query = Query - .query(Criteria.where("id").is("id_value").and("publishers").ne(accidentallyAnObjectId.toString())); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(UserEntity.class)); - assertThat(document.get("publishers")).isInstanceOf(org.bson.Document.class); - - org.bson.Document publishers = (org.bson.Document) document.get("publishers"); - assertThat(publishers).containsKey("$ne"); - assertThat(publishers.get("$ne")).isInstanceOf(String.class); - } - - @Test // DATAMONGO-494 - void usesEntityMetadataInOr() { - - Query query = query(new Criteria().orOperator(where("foo").is("bar"))); - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(Sample.class)); - - assertThat(result.keySet()).hasSize(1).containsOnly("$or"); - - List ors = getAsDBList(result, "$or"); - assertThat(ors).hasSize(1); - org.bson.Document criterias = getAsDocument(ors, 0); - assertThat(criterias.keySet()).hasSize(1).doesNotContain("foo"); - assertThat(criterias).containsKey("_id"); - } - - @Test - void translatesPropertyReferenceCorrectly() { - - Query query = query(where("field").is(new CustomizedField())); - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(CustomizedField.class)); - - assertThat(result).containsKey("foo").hasSize(1); - } - - @Test - void translatesNestedPropertyReferenceCorrectly() { - - Query query = query(where("field.field").is(new CustomizedField())); - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(CustomizedField.class)); - - assertThat(result).containsKey("foo.foo"); - assertThat(result.keySet()).hasSize(1); - } - - @Test - void returnsOriginalKeyIfNoPropertyReference() { - - Query query = query(where("bar").is(new CustomizedField())); - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(CustomizedField.class)); - - assertThat(result).containsKey("bar"); - assertThat(result.keySet()).hasSize(1); - } - - @Test - void convertsAssociationCorrectly() { - - Reference reference = new Reference(); - reference.id = 5L; - - Query query = query(where("reference").is(reference)); - org.bson.Document object = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WithDBRef.class)); - - Object referenceObject = object.get("reference"); - - assertThat(referenceObject).isInstanceOf(com.mongodb.DBRef.class); - } - - @Test - void convertsNestedAssociationCorrectly() { - - Reference reference = new Reference(); - reference.id = 5L; - - Query query = query(where("withDbRef.reference").is(reference)); - org.bson.Document object = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WithDBRefWrapper.class)); - - Object referenceObject = object.get("withDbRef.reference"); - - assertThat(referenceObject).isInstanceOf(com.mongodb.DBRef.class); - } - - @Test - void convertsInKeywordCorrectly() { - - Reference first = new Reference(); - first.id = 5L; - - Reference second = new Reference(); - second.id = 6L; - - Query query = query(where("reference").in(first, second)); - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WithDBRef.class)); - - org.bson.Document reference = DocumentTestUtils.getAsDocument(result, "reference"); - - List inClause = getAsDBList(reference, "$in"); - assertThat(inClause).hasSize(2); - assertThat(inClause.get(0)).isInstanceOf(com.mongodb.DBRef.class); - assertThat(inClause.get(1)).isInstanceOf(com.mongodb.DBRef.class); - } - - @Test // DATAMONGO-570 - void correctlyConvertsNullReference() { - - Query query = query(where("reference").is(null)); - org.bson.Document object = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WithDBRef.class)); - - assertThat(object.get("reference")).isNull(); - } - - @Test // DATAMONGO-629 - void doesNotMapIdIfNoEntityMetadataAvailable() { - - String id = new ObjectId().toString(); - Query query = query(where("id").is(id)); - - org.bson.Document object = mapper.getMappedObject(query.getQueryObject(), Optional.empty()); - - assertThat(object).containsKey("id"); - assertThat(object).containsEntry("id", id); - assertThat(object).doesNotContainKey("_id"); - } - - @Test // DATAMONGO-677 - void handleMapWithDBRefCorrectly() { - - org.bson.Document mapDocument = new org.bson.Document(); - mapDocument.put("test", new com.mongodb.DBRef("test", "test")); - org.bson.Document document = new org.bson.Document(); - document.put("mapWithDBRef", mapDocument); - - org.bson.Document mapped = mapper.getMappedObject(document, context.getPersistentEntity(WithMapDBRef.class)); - - assertThat(mapped).containsKey("mapWithDBRef"); - assertThat(mapped.get("mapWithDBRef")).isInstanceOf(org.bson.Document.class); - assertThat(((org.bson.Document) mapped.get("mapWithDBRef"))).containsKey("test"); - assertThat(((org.bson.Document) mapped.get("mapWithDBRef")).get("test")).isInstanceOf(com.mongodb.DBRef.class); - } - - @Test - void convertsUnderscoreIdValueWithoutMetadata() { - - org.bson.Document document = new org.bson.Document().append("_id", new ObjectId().toString()); - - org.bson.Document mapped = mapper.getMappedObject(document, Optional.empty()); - assertThat(mapped).containsKey("_id"); - assertThat(mapped.get("_id")).isInstanceOf(ObjectId.class); - } - - @Test // DATAMONGO-705 - void convertsDBRefWithExistsQuery() { - - Query query = query(where("reference").exists(false)); - - MongoPersistentEntity entity = context.getRequiredPersistentEntity(WithDBRef.class); - org.bson.Document mappedObject = mapper.getMappedObject(query.getQueryObject(), entity); - - org.bson.Document reference = getAsDocument(mappedObject, "reference"); - assertThat(reference).containsKey("$exists"); - assertThat(reference).containsEntry("$exists", false); - } - - @Test // DATAMONGO-706 - void convertsNestedDBRefsCorrectly() { - - Reference reference = new Reference(); - reference.id = 5L; - - Query query = query(where("someString").is("foo").andOperator(where("reference").in(reference))); - - MongoPersistentEntity entity = context.getRequiredPersistentEntity(WithDBRef.class); - org.bson.Document mappedObject = mapper.getMappedObject(query.getQueryObject(), entity); - - assertThat(mappedObject).containsEntry("someString", "foo"); - - List andClause = getAsDBList(mappedObject, "$and"); - assertThat(andClause).hasSize(1); - - List inClause = getAsDBList(getAsDocument(getAsDocument(andClause, 0), "reference"), "$in"); - assertThat(inClause).hasSize(1); - assertThat(inClause.get(0)).isInstanceOf(com.mongodb.DBRef.class); - } - - @Test // DATAMONGO-752 - void mapsSimpleValuesStartingWith$Correctly() { - - Query query = query(where("myvalue").is("$334")); - - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), Optional.empty()); - - assertThat(result.keySet()).hasSize(1); - assertThat(result).containsEntry("myvalue", "$334"); - } - - @Test // DATAMONGO-752 - void mapsKeywordAsSimpleValuesCorrectly() { - - Query query = query(where("myvalue").is("$center")); - - org.bson.Document result = mapper.getMappedObject(query.getQueryObject(), Optional.empty()); - - assertThat(result.keySet()).hasSize(1); - assertThat(result).containsEntry("myvalue", "$center"); - } - - @Test // DATAMONGO-805 - void shouldExcludeDBRefAssociation() { - - Query query = query(where("someString").is("foo")); - query.fields().exclude("reference"); - - MongoPersistentEntity entity = context.getRequiredPersistentEntity(WithDBRef.class); - org.bson.Document queryResult = mapper.getMappedObject(query.getQueryObject(), entity); - org.bson.Document fieldsResult = mapper.getMappedObject(query.getFieldsObject(), entity); - - assertThat(queryResult).containsEntry("someString", "foo"); - assertThat(fieldsResult).containsEntry("reference", 0); - } - - @Test // DATAMONGO-686 - void queryMapperShouldNotChangeStateInGivenQueryObjectWhenIdConstrainedByInList() { - - MongoPersistentEntity persistentEntity = context.getRequiredPersistentEntity(Sample.class); - String idPropertyName = persistentEntity.getIdProperty().getName(); - org.bson.Document queryObject = query(where(idPropertyName).in("42")).getQueryObject(); - - Object idValuesBefore = getAsDocument(queryObject, idPropertyName).get("$in"); - mapper.getMappedObject(queryObject, persistentEntity); - Object idValuesAfter = getAsDocument(queryObject, idPropertyName).get("$in"); - - assertThat(idValuesAfter).isEqualTo(idValuesBefore); - } - - @Test // DATAMONGO-821 - void queryMapperShouldNotTryToMapDBRefListPropertyIfNestedInsideDocumentWithinDocument() { - - org.bson.Document queryObject = query( - where("referenceList").is(new org.bson.Document("$nested", new org.bson.Document("$keys", 0L)))) - .getQueryObject(); - - org.bson.Document mappedObject = mapper.getMappedObject(queryObject, - context.getPersistentEntity(WithDBRefList.class)); - org.bson.Document referenceObject = getAsDocument(mappedObject, "referenceList"); - org.bson.Document nestedObject = getAsDocument(referenceObject, "$nested"); - - assertThat(nestedObject).isEqualTo(new org.bson.Document("$keys", 0L)); - } - - @Test // DATAMONGO-821 - void queryMapperShouldNotTryToMapDBRefPropertyIfNestedInsideDocumentWithinDocument() { - - org.bson.Document queryObject = query( - where("reference").is(new org.bson.Document("$nested", new org.bson.Document("$keys", 0L)))).getQueryObject(); - - org.bson.Document mappedObject = mapper.getMappedObject(queryObject, context.getPersistentEntity(WithDBRef.class)); - org.bson.Document referenceObject = getAsDocument(mappedObject, "reference"); - org.bson.Document nestedObject = getAsDocument(referenceObject, "$nested"); - - assertThat(nestedObject).isEqualTo(new org.bson.Document("$keys", 0L)); - } - - @Test // DATAMONGO-821 - void queryMapperShouldMapDBRefPropertyIfNestedInDocument() { - - Reference sample = new Reference(); - sample.id = 321L; - org.bson.Document queryObject = query( - where("reference").is(new org.bson.Document("$in", Collections.singletonList(sample)))).getQueryObject(); - - org.bson.Document mappedObject = mapper.getMappedObject(queryObject, context.getPersistentEntity(WithDBRef.class)); - - org.bson.Document referenceObject = getAsDocument(mappedObject, "reference"); - List inObject = getAsDBList(referenceObject, "$in"); - - assertThat(inObject.get(0)).isInstanceOf(com.mongodb.DBRef.class); - } - - @Test // DATAMONGO-773 - void queryMapperShouldBeAbleToProcessQueriesThatIncludeDbRefFields() { - - MongoPersistentEntity persistentEntity = context.getRequiredPersistentEntity(WithDBRef.class); - - Query qry = query(where("someString").is("abc")); - qry.fields().include("reference"); - - org.bson.Document mappedFields = mapper.getMappedObject(qry.getFieldsObject(), persistentEntity); - assertThat(mappedFields).isNotNull(); - } - - @Test // DATAMONGO-893 - void classInformationShouldNotBePresentInDocumentUsedInFinderMethods() { - - EmbeddedClass embedded = new EmbeddedClass(); - embedded.id = "1"; - - EmbeddedClass embedded2 = new EmbeddedClass(); - embedded2.id = "2"; - Query query = query(where("embedded").in(Arrays.asList(embedded, embedded2))); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Foo.class)); - assertThat(document).isEqualTo("{ \"embedded\" : { \"$in\" : [ { \"_id\" : \"1\"} , { \"_id\" : \"2\"}]}}"); - } - - @Test // DATAMONGO-1406 - void shouldMapQueryForNestedCustomizedPropertiesUsingConfiguredFieldNames() { - - EmbeddedClass embeddedClass = new EmbeddedClass(); - embeddedClass.customizedField = "hello"; - - Foo foo = new Foo(); - foo.listOfItems = Collections.singletonList(embeddedClass); - - Query query = new Query(Criteria.where("listOfItems") // - .elemMatch(new Criteria(). // - andOperator(Criteria.where("customizedField").is(embeddedClass.customizedField)))); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Foo.class)); - - assertThat(document).containsEntry("my_items.$elemMatch.$and", - Collections.singletonList(new org.bson.Document("fancy_custom_name", embeddedClass.customizedField))); - } - - @Test // DATAMONGO-647 - void customizedFieldNameShouldBeMappedCorrectlyWhenApplyingSort() { - - Query query = query(where("field").is("bar")).with(Sort.by(Direction.DESC, "field")); - org.bson.Document document = mapper.getMappedObject(query.getSortObject(), - context.getPersistentEntity(CustomizedField.class)); - assertThat(document).isEqualTo(new org.bson.Document().append("foo", -1)); - } - - @Test // DATAMONGO-973 - void getMappedFieldsAppendsTextScoreFieldProperlyCorrectlyWhenNotPresent() { - - Query query = new Query(); - - org.bson.Document document = mapper.getMappedFields(query.getFieldsObject(), - context.getPersistentEntity(WithTextScoreProperty.class)); - - assertThat(document) - .isEqualTo(new org.bson.Document().append("score", new org.bson.Document("$meta", "textScore"))); - } - - @Test // DATAMONGO-973 - void getMappedFieldsReplacesTextScoreFieldProperlyCorrectlyWhenPresent() { - - Query query = new Query(); - query.fields().include("textScore"); - - org.bson.Document document = mapper.getMappedFields(query.getFieldsObject(), - context.getPersistentEntity(WithTextScoreProperty.class)); - - assertThat(document) - .isEqualTo(new org.bson.Document().append("score", new org.bson.Document("$meta", "textScore"))); - } - - @Test // DATAMONGO-973 - void getMappedSortAppendsTextScoreProperlyWhenSortedByScore() { - - Query query = new Query().with(Sort.by("textScore")); - - org.bson.Document document = mapper.getMappedSort(query.getSortObject(), - context.getPersistentEntity(WithTextScoreProperty.class)); - - assertThat(document) - .isEqualTo(new org.bson.Document().append("score", new org.bson.Document("$meta", "textScore"))); - } - - @Test // DATAMONGO-973 - void getMappedSortIgnoresTextScoreWhenNotSortedByScore() { - - Query query = new Query().with(Sort.by("id")); - - org.bson.Document document = mapper.getMappedSort(query.getSortObject(), - context.getPersistentEntity(WithTextScoreProperty.class)); - - assertThat(document).isEqualTo(new org.bson.Document().append("_id", 1)); - } - - @Test // DATAMONGO-1070, DATAMONGO-1798 - void mapsIdReferenceToDBRefCorrectly() { - - ObjectId id = new ObjectId(); - - org.bson.Document query = new org.bson.Document("reference.id", new com.mongodb.DBRef("reference", id)); - org.bson.Document result = mapper.getMappedObject(query, context.getPersistentEntity(WithDBRef.class)); - - assertThat(result).containsKey("reference"); - com.mongodb.DBRef reference = getTypedValue(result, "reference", com.mongodb.DBRef.class); - assertThat(reference.getId()).isInstanceOf(ObjectId.class); - } - - @Test // DATAMONGO-1050 - void shouldUseExplicitlySetFieldnameForIdPropertyCandidates() { - - Query query = query(where("nested.id").is("bar")); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class)); - - assertThat(document).isEqualTo(new org.bson.Document().append("nested.id", "bar")); - } - - @Test // DATAMONGO-1050 - void shouldUseExplicitlySetFieldnameForIdPropertyCandidatesUsedInSortClause() { - - Query query = new Query().with(Sort.by("nested.id")); - - org.bson.Document document = mapper.getMappedSort(query.getSortObject(), - context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class)); - - assertThat(document).isEqualTo(new org.bson.Document().append("nested.id", 1)); - } - - @Test // DATAMONGO-1135 - void nearShouldUseGeoJsonRepresentationOnUnmappedProperty() { - - Query query = query(where("foo").near(new GeoJsonPoint(100, 50))); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithGeoTypes.class)); - - assertThat(document).containsEntry("foo.$near.$geometry.type", "Point"); - assertThat(document).containsEntry("foo.$near.$geometry.coordinates.[0]", 100D); - assertThat(document).containsEntry("foo.$near.$geometry.coordinates.[1]", 50D); - } - - @Test // DATAMONGO-1135 - void nearShouldUseGeoJsonRepresentationWhenMappingToGoJsonType() { - - Query query = query(where("geoJsonPoint").near(new GeoJsonPoint(100, 50))); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithGeoTypes.class)); - - assertThat(document).containsEntry("geoJsonPoint.$near.$geometry.type", "Point"); - } - - @Test // DATAMONGO-1135 - void nearSphereShouldUseGeoJsonRepresentationWhenMappingToGoJsonType() { - - Query query = query(where("geoJsonPoint").nearSphere(new GeoJsonPoint(100, 50))); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithGeoTypes.class)); - - assertThat(document).containsEntry("geoJsonPoint.$nearSphere.$geometry.type", "Point"); - } - - @Test // DATAMONGO-1135 - void shouldMapNameCorrectlyForGeoJsonType() { - - Query query = query(where("namedGeoJsonPoint").nearSphere(new GeoJsonPoint(100, 50))); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithGeoTypes.class)); - - assertThat(document).containsEntry("geoJsonPointWithNameViaFieldAnnotation.$nearSphere.$geometry.type", "Point"); - } - - @Test // DATAMONGO-1135 - void withinShouldUseGeoJsonPolygonWhenMappingPolygonOn2DSphereIndex() { - - Query query = query(where("geoJsonPoint") - .within(new GeoJsonPolygon(new Point(0, 0), new Point(100, 100), new Point(100, 0), new Point(0, 0)))); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithGeoTypes.class)); - - assertThat(document).containsEntry("geoJsonPoint.$geoWithin.$geometry.type", "Polygon"); - } - - @Test // DATAMONGO-1134 - void intersectsShouldUseGeoJsonRepresentationCorrectly() { - - Query query = query(where("geoJsonPoint") - .intersects(new GeoJsonPolygon(new Point(0, 0), new Point(100, 100), new Point(100, 0), new Point(0, 0)))); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithGeoTypes.class)); - - assertThat(document).containsEntry("geoJsonPoint.$geoIntersects.$geometry.type", "Polygon"); - assertThat(document).containsKey("geoJsonPoint.$geoIntersects.$geometry.coordinates"); - } - - @Test // DATAMONGO-1269 - void mappingShouldRetainNumericMapKey() { - - Query query = query(where("map.1.stringProperty").is("ba'alzamon")); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(EntityWithComplexValueTypeMap.class)); - - assertThat(document).containsKey("map.1.stringProperty"); - } - - @Test // DATAMONGO-1269 - void mappingShouldRetainNumericPositionInList() { - - Query query = query(where("list.1.stringProperty").is("ba'alzamon")); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(EntityWithComplexValueTypeList.class)); - - assertThat(document).containsKey("list.1.stringProperty"); - } - - @Test // DATAMONGO-1245 - void exampleShouldBeMappedCorrectly() { - - Foo probe = new Foo(); - probe.embedded = new EmbeddedClass(); - probe.embedded.id = "conflux"; - - Query query = query(byExample(probe)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Foo.class)); - - assertThat(document).containsEntry("embedded\\._id", "conflux"); - } - - @Test // DATAMONGO-1245 - void exampleShouldBeMappedCorrectlyWhenContainingLegacyPoint() { - - ClassWithGeoTypes probe = new ClassWithGeoTypes(); - probe.legacyPoint = new Point(10D, 20D); - - Query query = query(byExample(probe)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WithDBRef.class)); - - assertThat(document).containsEntry("legacyPoint.x", 10D); - assertThat(document).containsEntry("legacyPoint.y", 20D); - } - - @Test // GH-3544 - void exampleWithCombinedCriteriaShouldBeMappedCorrectly() { - - Foo probe = new Foo(); - probe.embedded = new EmbeddedClass(); - probe.embedded.id = "conflux"; - - Query query = query(byExample(probe).and("listOfItems").exists(true)); - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Foo.class)); - - assertThat(document).containsEntry("embedded\\._id", "conflux").containsEntry("my_items", - new org.bson.Document("$exists", true)); - } - - @Test // DATAMONGO-1988 - void mapsStringObjectIdRepresentationToObjectIdWhenReferencingIdProperty() { - - Query query = query(where("sample.foo").is(new ObjectId().toHexString())); - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithEmbedded.class)); - - assertThat(document.get("sample._id")).isInstanceOf(ObjectId.class); - } - - @Test // DATAMONGO-1988 - void matchesExactFieldNameToIdProperty() { - - Query query = query(where("sample.iid").is(new ObjectId().toHexString())); - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithEmbedded.class)); - - assertThat(document.get("sample.iid")).isInstanceOf(String.class); - } - - @Test // DATAMONGO-1988 - void leavesNonObjectIdStringIdRepresentationUntouchedWhenReferencingIdProperty() { - - Query query = query(where("sample.foo").is("id-1")); - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithEmbedded.class)); - - assertThat(document.get("sample._id")).isInstanceOf(String.class); - } - - @Test // DATAMONGO-2168 - void getMappedObjectShouldNotMapTypeHint() { - - converter.setTypeMapper(new DefaultMongoTypeMapper("className")); - - org.bson.Document update = new org.bson.Document("className", "foo"); - org.bson.Document mappedObject = mapper.getMappedObject(update, context.getPersistentEntity(UserEntity.class)); - - assertThat(mappedObject).containsEntry("className", "foo"); - } - - @Test // DATAMONGO-2168 - void getMappedObjectShouldIgnorePathsLeadingToJavaLangClassProperties/* like Class#getName() */() { - - org.bson.Document update = new org.bson.Document("className", "foo"); - org.bson.Document mappedObject = mapper.getMappedObject(update, context.getPersistentEntity(UserEntity.class)); - - assertThat(mappedObject).containsEntry("className", "foo"); - } - - @Test // DATAMONGO-2193 - void shouldNotConvertHexStringToObjectIdForRenamedNestedIdField() { - - String idHex = new ObjectId().toHexString(); - Query query = new Query(where("nested.id").is(idHex)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class)); - - assertThat(document).isEqualTo(new org.bson.Document("nested.id", idHex)); - } - - @Test // DATAMONGO-2221 - void shouldNotConvertHexStringToObjectIdForRenamedDeeplyNestedIdField() { - - String idHex = new ObjectId().toHexString(); - Query query = new Query(where("nested.deeplyNested.id").is(idHex)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class)); - - assertThat(document).isEqualTo(new org.bson.Document("nested.deeplyNested.id", idHex)); - } - - @Test // DATAMONGO-2221 - void shouldNotConvertHexStringToObjectIdForUnresolvablePath() { - - String idHex = new ObjectId().toHexString(); - Query query = new Query(where("nested.unresolvablePath.id").is(idHex)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(RootForClassWithExplicitlyRenamedIdField.class)); - - assertThat(document).isEqualTo(new org.bson.Document("nested.unresolvablePath.id", idHex)); - } - - @Test // DATAMONGO-1849 - void shouldConvertPropertyWithExplicitTargetType() { - - String script = "if (a > b) a else b"; - Query query = new Query(where("script").is(script)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WithExplicitTargetTypes.class)); - - assertThat(document).isEqualTo(new org.bson.Document("script", new Code(script))); - } - - @Test // DATAMONGO-1849 - void shouldConvertCollectionPropertyWithExplicitTargetType() { - - String script = "if (a > b) a else b"; - Query query = new Query(where("scripts").is(script)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WithExplicitTargetTypes.class)); - - assertThat(document).isEqualTo(new org.bson.Document("scripts", new Code(script))); - } - - @Test // DATAMONGO-2339 - void findByIdUsesMappedIdFieldNameWithUnderscoreCorrectly() { - - org.bson.Document target = mapper.getMappedObject(new org.bson.Document("with_underscore", "id-1"), - context.getPersistentEntity(WithIdPropertyContainingUnderscore.class)); - - assertThat(target).isEqualTo(new org.bson.Document("_id", "id-1")); - } - - @Test // DATAMONGO-2394 - void leavesDistanceUntouchedWhenUsingGeoJson() { - - Query query = query(where("geoJsonPoint").near(new GeoJsonPoint(27.987901, 86.9165379)).maxDistance(1000)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(ClassWithGeoTypes.class)); - assertThat(document).containsEntry("geoJsonPoint.$near.$geometry.type", "Point"); - assertThat(document).containsEntry("geoJsonPoint.$near.$maxDistance", 1000.0D); - } - - @Test // DATAMONGO-2440 - void convertsInWithNonIdFieldAndObjectIdTypeHintCorrectly() { - - String id = new ObjectId().toHexString(); - NonIdFieldWithObjectIdTargetType source = new NonIdFieldWithObjectIdTargetType(); - - source.stringAsOid = id; - - org.bson.Document target = mapper.getMappedObject(query(where("stringAsOid").in(id)).getQueryObject(), - context.getPersistentEntity(NonIdFieldWithObjectIdTargetType.class)); - assertThat(target).isEqualTo(org.bson.Document.parse("{\"stringAsOid\": {\"$in\": [{\"$oid\": \"" + id + "\"}]}}")); - } - - @Test // DATAMONGO-2440 - void convertsInWithIdFieldAndObjectIdTypeHintCorrectly() { - - String id = new ObjectId().toHexString(); - NonIdFieldWithObjectIdTargetType source = new NonIdFieldWithObjectIdTargetType(); - - source.id = id; - - org.bson.Document target = mapper.getMappedObject(query(where("id").in(id)).getQueryObject(), - context.getPersistentEntity(NonIdFieldWithObjectIdTargetType.class)); - assertThat(target).isEqualTo(org.bson.Document.parse("{\"_id\": {\"$in\": [{\"$oid\": \"" + id + "\"}]}}")); - } - - @Test // DATAMONGO-2488 - void mapsNestedArrayPathCorrectlyForNonMatchingPath() { - - org.bson.Document target = mapper.getMappedObject( - query(where("array.$[some_item].nested.$[other_item]").is("value")).getQueryObject(), - context.getPersistentEntity(Foo.class)); - - assertThat(target).isEqualTo(new org.bson.Document("array.$[some_item].nested.$[other_item]", "value")); - } - - @Test // DATAMONGO-2488 - void mapsNestedArrayPathCorrectlyForObjectTargetArray() { - - org.bson.Document target = mapper.getMappedObject( - query(where("arrayObj.$[some_item].nested.$[other_item]").is("value")).getQueryObject(), - context.getPersistentEntity(WithNestedArray.class)); - - assertThat(target).isEqualTo(new org.bson.Document("arrayObj.$[some_item].nested.$[other_item]", "value")); - } - - @Test // DATAMONGO-2488 - void mapsNestedArrayPathCorrectlyForStringTargetArray() { - - org.bson.Document target = mapper.getMappedObject( - query(where("arrayString.$[some_item].nested.$[other_item]").is("value")).getQueryObject(), - context.getPersistentEntity(WithNestedArray.class)); - - assertThat(target).isEqualTo(new org.bson.Document("arrayString.$[some_item].nested.$[other_item]", "value")); - } - - @Test // DATAMONGO-2488 - void mapsCustomFieldNamesForNestedArrayPathCorrectly() { - - org.bson.Document target = mapper.getMappedObject( - query(where("arrayCustomName.$[some_item].nested.$[other_item]").is("value")).getQueryObject(), - context.getPersistentEntity(WithNestedArray.class)); - - assertThat(target).isEqualTo(new org.bson.Document("arrayCustomName.$[some_item].nes-ted.$[other_item]", "value")); - } - - @Test // DATAMONGO-2502 - void shouldAllowDeeplyNestedPlaceholders() { - - org.bson.Document target = mapper.getMappedObject( - query(where("level0.$[some_item].arrayObj.$[other_item].nested").is("value")).getQueryObject(), - context.getPersistentEntity(WithDeepArrayNesting.class)); - - assertThat(target).isEqualTo(new org.bson.Document("level0.$[some_item].arrayObj.$[other_item].nested", "value")); - } - - @Test // DATAMONGO-2502 - void shouldAllowDeeplyNestedPlaceholdersWithCustomName() { - - org.bson.Document target = mapper.getMappedObject( - query(where("level0.$[some_item].arrayCustomName.$[other_item].nested").is("value")).getQueryObject(), - context.getPersistentEntity(WithDeepArrayNesting.class)); - - assertThat(target) - .isEqualTo(new org.bson.Document("level0.$[some_item].arrayCustomName.$[other_item].nes-ted", "value")); - } - - @Test // DATAMONGO-2517 - void shouldParseNestedKeywordWithArgumentMatchingTheSourceEntitiesConstructorCorrectly() { - - TextQuery source = new TextQuery("test"); - - org.bson.Document target = mapper.getMappedObject(source.getQueryObject(), - context.getPersistentEntity(WithSingleStringArgConstructor.class)); - assertThat(target).isEqualTo(org.bson.Document.parse("{\"$text\" : { \"$search\" : \"test\" }}")); - } - - @Test // DATAMONGO-1902 - void rendersQueryOnUnwrappedObjectCorrectly() { - - UnwrappableType unwrappableType = new UnwrappableType(); - unwrappableType.stringValue = "test"; - - Query source = query(Criteria.where("unwrappedValue").is(unwrappableType)); - - org.bson.Document target = mapper.getMappedObject(source.getQueryObject(), - context.getPersistentEntity(WithUnwrapped.class)); - - assertThat(target).isEqualTo(new org.bson.Document("stringValue", "test")); - } - - @Test // DATAMONGO-1902 - void rendersQueryOnUnwrappedCorrectly() { - - Query source = query(Criteria.where("unwrappedValue.stringValue").is("test")); - - org.bson.Document target = mapper.getMappedObject(source.getQueryObject(), - context.getPersistentEntity(WithUnwrapped.class)); - - assertThat(target).isEqualTo(new org.bson.Document("stringValue", "test")); - } - - @Test // DATAMONGO-1902 - void rendersQueryOnPrefixedUnwrappedCorrectly() { - - Query source = query(Criteria.where("unwrappedValue.stringValue").is("test")); - - org.bson.Document target = mapper.getMappedObject(source.getQueryObject(), - context.getPersistentEntity(WithPrefixedUnwrapped.class)); - - assertThat(target).isEqualTo(new org.bson.Document("prefix-stringValue", "test")); - } - - @Test // DATAMONGO-1902 - void rendersQueryOnNestedUnwrappedObjectCorrectly() { - - UnwrappableType unwrappableType = new UnwrappableType(); - unwrappableType.stringValue = "test"; - Query source = query(Criteria.where("withUnwrapped.unwrappedValue").is(unwrappableType)); - - org.bson.Document target = mapper.getMappedObject(source.getQueryObject(), - context.getPersistentEntity(WrapperAroundWithUnwrapped.class)); - - assertThat(target).isEqualTo(new org.bson.Document("withUnwrapped", new org.bson.Document("stringValue", "test"))); - } - - @Test // DATAMONGO-1902 - void rendersQueryOnNestedPrefixedUnwrappedObjectCorrectly() { - - UnwrappableType unwrappableType = new UnwrappableType(); - unwrappableType.stringValue = "test"; - Query source = query(Criteria.where("withPrefixedUnwrapped.unwrappedValue").is(unwrappableType)); - - org.bson.Document target = mapper.getMappedObject(source.getQueryObject(), - context.getPersistentEntity(WrapperAroundWithUnwrapped.class)); - - assertThat(target) - .isEqualTo(new org.bson.Document("withPrefixedUnwrapped", new org.bson.Document("prefix-stringValue", "test"))); - } - - @Test // DATAMONGO-1902 - void rendersQueryOnNestedUnwrappedCorrectly() { - - Query source = query(Criteria.where("withUnwrapped.unwrappedValue.stringValue").is("test")); - - org.bson.Document target = mapper.getMappedObject(source.getQueryObject(), - context.getPersistentEntity(WrapperAroundWithUnwrapped.class)); - - assertThat(target).isEqualTo(new org.bson.Document("withUnwrapped.stringValue", "test")); - } - - @Test // DATAMONGO-1902 - void rendersQueryOnNestedPrefixedUnwrappedCorrectly() { - - Query source = query(Criteria.where("withPrefixedUnwrapped.unwrappedValue.stringValue").is("test")); - - org.bson.Document target = mapper.getMappedObject(source.getQueryObject(), - context.getPersistentEntity(WrapperAroundWithUnwrapped.class)); - - assertThat(target).isEqualTo(new org.bson.Document("withPrefixedUnwrapped.prefix-stringValue", "test")); - } - - @Test // DATAMONGO-1902 - void sortByUnwrappedIsEmpty() { - - Query query = new Query().with(Sort.by("unwrappedValue")); - - org.bson.Document document = mapper.getMappedSort(query.getSortObject(), - context.getPersistentEntity(WithUnwrapped.class)); - - assertThat(document).isEqualTo( - new org.bson.Document("stringValue", 1).append("listValue", 1).append("with-at-field-annotation", 1)); - } - - @Test // DATAMONGO-1902 - void sortByUnwrappedValue() { - - // atFieldAnnotatedValue - Query query = new Query().with(Sort.by("unwrappedValue.stringValue")); - - org.bson.Document document = mapper.getMappedSort(query.getSortObject(), - context.getPersistentEntity(WithUnwrapped.class)); - - assertThat(document).isEqualTo(new org.bson.Document("stringValue", 1)); - } - - @Test // DATAMONGO-1902 - void sortByUnwrappedValueWithFieldAnnotation() { - - Query query = new Query().with(Sort.by("unwrappedValue.atFieldAnnotatedValue")); - - org.bson.Document document = mapper.getMappedSort(query.getSortObject(), - context.getPersistentEntity(WithUnwrapped.class)); - - assertThat(document).isEqualTo(new org.bson.Document("with-at-field-annotation", 1)); - } - - @Test // DATAMONGO-1902 - void sortByPrefixedUnwrappedValueWithFieldAnnotation() { - - Query query = new Query().with(Sort.by("unwrappedValue.atFieldAnnotatedValue")); - - org.bson.Document document = mapper.getMappedSort(query.getSortObject(), - context.getPersistentEntity(WithPrefixedUnwrapped.class)); - - assertThat(document).isEqualTo(new org.bson.Document("prefix-with-at-field-annotation", 1)); - } - - @Test // DATAMONGO-1902 - void sortByNestedUnwrappedValueWithFieldAnnotation() { - - Query query = new Query().with(Sort.by("withUnwrapped.unwrappedValue.atFieldAnnotatedValue")); - - org.bson.Document document = mapper.getMappedSort(query.getSortObject(), - context.getPersistentEntity(WrapperAroundWithUnwrapped.class)); - - assertThat(document).isEqualTo(new org.bson.Document("withUnwrapped.with-at-field-annotation", 1)); - } - - @Test // DATAMONGO-1902 - void sortByNestedPrefixedUnwrappedValueWithFieldAnnotation() { - - Query query = new Query().with(Sort.by("withPrefixedUnwrapped.unwrappedValue.atFieldAnnotatedValue")); - - org.bson.Document document = mapper.getMappedSort(query.getSortObject(), - context.getPersistentEntity(WrapperAroundWithUnwrapped.class)); - - assertThat(document).isEqualTo(new org.bson.Document("withPrefixedUnwrapped.prefix-with-at-field-annotation", 1)); - } - - @Test // DATAMONGO-1902 - void projectOnUnwrappedUsesFields() { - - Query query = new Query(); - query.fields().include("unwrappedValue"); - - org.bson.Document document = mapper.getMappedFields(query.getFieldsObject(), - context.getPersistentEntity(WithUnwrapped.class)); - - assertThat(document).isEqualTo( - new org.bson.Document("stringValue", 1).append("listValue", 1).append("with-at-field-annotation", 1)); - } - - @Test // DATAMONGO-1902 - void projectOnUnwrappedValue() { - - Query query = new Query(); - query.fields().include("unwrappedValue.stringValue"); - - org.bson.Document document = mapper.getMappedFields(query.getFieldsObject(), - context.getPersistentEntity(WithUnwrapped.class)); - - assertThat(document).isEqualTo(new org.bson.Document("stringValue", 1)); - } - - @Test // GH-3601 - void resolvesFieldnameWithUnderscoresCorrectly() { - - Query query = query(where("fieldname_with_underscores").exists(true)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WithPropertyUsingUnderscoreInName.class)); - - assertThat(document).isEqualTo(new org.bson.Document("fieldname_with_underscores", new org.bson.Document("$exists", true))); - } - - @Test // GH-3601 - void resolvesMappedFieldnameWithUnderscoresCorrectly() { - - Query query = query(where("renamed_fieldname_with_underscores").exists(true)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WithPropertyUsingUnderscoreInName.class)); - - assertThat(document).isEqualTo(new org.bson.Document("renamed", new org.bson.Document("$exists", true))); - } - - @Test // GH-3601 - void resolvesSimpleNestedFieldnameWithUnderscoresCorrectly() { - - Query query = query(where("simple.fieldname_with_underscores").exists(true)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class)); - - assertThat(document).isEqualTo(new org.bson.Document("simple.fieldname_with_underscores", new org.bson.Document("$exists", true))); - } - - @Test // GH-3601 - void resolvesSimpleNestedMappedFieldnameWithUnderscoresCorrectly() { - - Query query = query(where("simple.renamed_fieldname_with_underscores").exists(true)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class)); - - assertThat(document).isEqualTo(new org.bson.Document("simple.renamed", new org.bson.Document("$exists", true))); - } - - @Test // GH-3601 - void resolvesFieldNameWithUnderscoreOnNestedFieldnameWithUnderscoresCorrectly() { - - Query query = query(where("double_underscore.fieldname_with_underscores").exists(true)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class)); - - assertThat(document).isEqualTo(new org.bson.Document("double_underscore.fieldname_with_underscores", new org.bson.Document("$exists", true))); - } - - @Test // GH-3601 - void resolvesFieldNameWithUnderscoreOnNestedMappedFieldnameWithUnderscoresCorrectly() { - - Query query = query(where("double_underscore.renamed_fieldname_with_underscores").exists(true)); - - org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), - context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class)); - - assertThat(document).isEqualTo(new org.bson.Document("double_underscore.renamed", new org.bson.Document("$exists", true))); - } - - class WithDeepArrayNesting { - - List level0; - } - - class WithNestedArray { - - List arrayObj; - List arrayString; - List arrayCustomName; - } - - class NestedArrayOfObj { - List nested; - } - - class NestedArrayOfObjCustomFieldName { - - @Field("nes-ted") List nested; - } - - class NestedArrayOfString { - List nested; - } - - class ArrayObj { - String foo; - } - - @Document - class Foo { - @Id private ObjectId id; - EmbeddedClass embedded; - - @Field("my_items") List listOfItems; - } - - class EmbeddedClass { - String id; - - @Field("fancy_custom_name") String customizedField; - } - - class IdWrapper { - Object id; - } - - class ClassWithEmbedded { - @Id String id; - Sample sample; - } - - class ClassWithDefaultId { - - String id; - ClassWithDefaultId nested; - } - - class Sample { - - @Id private String foo; - } - - class BigIntegerId { - - @Id private BigInteger id; - } - - enum Enum { - INSTANCE; - } - - class UserEntity { - String id; - List publishers = new ArrayList<>(); - } - - class CustomizedField { - - @Field("foo") CustomizedField field; - } - - class WithDBRef { - - String someString; - @DBRef Reference reference; - } - - class WithDBRefList { - - String someString; - @DBRef List referenceList; - } - - class Reference { - - Long id; - } - - class WithDBRefWrapper { - - WithDBRef withDbRef; - } - - class WithMapDBRef { - - @DBRef Map mapWithDBRef; - } - - class WithTextScoreProperty { - - @Id String id; - @TextScore @Field("score") Float textScore; - } - - static class RootForClassWithExplicitlyRenamedIdField { - - @Id String id; - ClassWithExplicitlyRenamedField nested; - } - - static class ClassWithExplicitlyRenamedField { - - @Field("id") String id; - DeeplyNestedClassWithExplicitlyRenamedField deeplyNested; - } - - static class DeeplyNestedClassWithExplicitlyRenamedField { - @Field("id") String id; - } - - static class ClassWithGeoTypes { - - double[] justAnArray; - Point legacyPoint; - GeoJsonPoint geoJsonPoint; - @Field("geoJsonPointWithNameViaFieldAnnotation") GeoJsonPoint namedGeoJsonPoint; - } - - static class SimpeEntityWithoutId { - - String stringProperty; - Integer integerProperty; - } - - static class EntityWithComplexValueTypeMap { - Map map; - } - - static class EntityWithComplexValueTypeList { - List list; - } - - static class WithExplicitTargetTypes { - - @Field(targetType = FieldType.SCRIPT) // - String script; - - @Field(targetType = FieldType.SCRIPT) // - List scripts; - } - - static class WithIdPropertyContainingUnderscore { - @Id String with_underscore; - } - - static class NonIdFieldWithObjectIdTargetType { - - String id; - @Field(targetType = FieldType.OBJECT_ID) String stringAsOid; - } - - @Document - static class WithSingleStringArgConstructor { - - String value; - - public WithSingleStringArgConstructor() {} - - public WithSingleStringArgConstructor(String value) { - this.value = value; - } - } - - static class WrapperAroundWithUnwrapped { - - String someValue; - WithUnwrapped withUnwrapped; - WithPrefixedUnwrapped withPrefixedUnwrapped; - } - - static class WithUnwrapped { - - String id; - - @Unwrapped.Nullable UnwrappableType unwrappedValue; - } - - static class WithPrefixedUnwrapped { - - String id; - - @Unwrapped.Nullable("prefix-") UnwrappableType unwrappedValue; - } - - static class UnwrappableType { - - String stringValue; - List listValue; - - @Field("with-at-field-annotation") // - String atFieldAnnotatedValue; - - @Transient // - String transientValue; - } - - static class WrapperAroundWithPropertyUsingUnderscoreInName { - - WithPropertyUsingUnderscoreInName simple; - WithPropertyUsingUnderscoreInName double_underscore; - } - - static class WithPropertyUsingUnderscoreInName { - - String fieldname_with_underscores; - - @Field("renamed") - String renamed_fieldname_with_underscores; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/TermToStringConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/TermToStringConverterUnitTests.java deleted file mode 100644 index 2023aa6a32..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/TermToStringConverterUnitTests.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2021 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.convert; - -import static org.mockito.Mockito.*; - -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.convert.MongoConverters.TermToStringConverter; -import org.springframework.data.mongodb.core.query.Term; -import org.springframework.data.mongodb.core.query.Term.Type; - -/** - * @author Christoph Strobl - */ -public class TermToStringConverterUnitTests { - - @Test // DATAMONGO-973 - public void shouldUseFormattedRepresentationForConversion() { - - Term term = spy(new Term("foo", Type.WORD)); - TermToStringConverter.INSTANCE.convert(term); - verify(term, times(1)).getFormatted(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java deleted file mode 100644 index f5b5493327..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java +++ /dev/null @@ -1,1554 +0,0 @@ -/* - * Copyright 2013-2021 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.convert; - -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.DocumentTestUtils.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDate; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.Transient; -import org.springframework.data.convert.CustomConversions; -import org.springframework.data.convert.WritingConverter; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.domain.Sort.Order; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.data.mongodb.core.query.Update.Position; - -import com.mongodb.DBRef; - -/** - * Unit tests for {@link UpdateMapper}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Thomas Darimont - * @author Mark Paluch - * @author Pavel Vodrazka - */ -@ExtendWith(MockitoExtension.class) -class UpdateMapperUnitTests { - - @Mock MongoDatabaseFactory factory; - private MappingMongoConverter converter; - private MongoMappingContext context; - private UpdateMapper mapper; - - private Converter writingConverterSpy; - - @BeforeEach - @SuppressWarnings("unchecked") - void setUp() { - - this.writingConverterSpy = Mockito.spy(new NestedEntityWriteConverter()); - CustomConversions conversions = new MongoCustomConversions(Collections.singletonList(writingConverterSpy)); - - this.context = new MongoMappingContext(); - this.context.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); - this.context.initialize(); - - this.converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), context); - this.converter.setCustomConversions(conversions); - this.converter.afterPropertiesSet(); - - this.mapper = new UpdateMapper(converter); - } - - @Test // DATAMONGO-721 - void updateMapperRetainsTypeInformationForCollectionField() { - - Update update = new Update().push("list", new ConcreteChildClass("2", "BAR")); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document list = getAsDocument(push, "aliased"); - - assertTypeHint(list, ConcreteChildClass.class); - } - - @Test // DATAMONGO-807 - void updateMapperShouldRetainTypeInformationForNestedEntities() { - - Update update = Update.update("model", new ModelImpl(1)); - UpdateMapper mapper = new UpdateMapper(converter); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ModelWrapper.class)); - - Document set = getAsDocument(mappedObject, "$set"); - Document modelDocument = (Document) set.get("model"); - assertTypeHint(modelDocument, ModelImpl.class); - } - - @Test // DATAMONGO-807 - void updateMapperShouldNotPersistTypeInformationForKnownSimpleTypes() { - - Update update = Update.update("model.value", 1); - UpdateMapper mapper = new UpdateMapper(converter); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ModelWrapper.class)); - - Document set = getAsDocument(mappedObject, "$set"); - assertThat(set.get("_class")).isNull(); - } - - @Test // DATAMONGO-807 - void updateMapperShouldNotPersistTypeInformationForNullValues() { - - Update update = Update.update("model", null); - UpdateMapper mapper = new UpdateMapper(converter); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ModelWrapper.class)); - - Document set = getAsDocument(mappedObject, "$set"); - assertThat(set.get("_class")).isNull(); - } - - @Test // DATAMONGO-407 - void updateMapperShouldRetainTypeInformationForNestedCollectionElements() { - - Update update = Update.update("list.$", new ConcreteChildClass("42", "bubu")); - - UpdateMapper mapper = new UpdateMapper(converter); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - Document set = getAsDocument(mappedObject, "$set"); - Document modelDocument = getAsDocument(set, "aliased.$"); - assertTypeHint(modelDocument, ConcreteChildClass.class); - } - - @Test // DATAMONGO-407 - void updateMapperShouldSupportNestedCollectionElementUpdates() { - - Update update = Update.update("list.$.value", "foo").set("list.$.otherValue", "bar"); - - UpdateMapper mapper = new UpdateMapper(converter); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - Document set = getAsDocument(mappedObject, "$set"); - assertThat(set.get("aliased.$.value")).isEqualTo("foo"); - assertThat(set.get("aliased.$.otherValue")).isEqualTo("bar"); - } - - @Test // DATAMONGO-407 - void updateMapperShouldWriteTypeInformationForComplexNestedCollectionElementUpdates() { - - Update update = Update.update("list.$.value", "foo").set("list.$.someObject", new ConcreteChildClass("42", "bubu")); - - UpdateMapper mapper = new UpdateMapper(converter); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - Document document = getAsDocument(mappedObject, "$set"); - assertThat(document.get("aliased.$.value")).isEqualTo("foo"); - - Document someObject = getAsDocument(document, "aliased.$.someObject"); - assertThat(someObject).isNotNull().containsEntry("value", "bubu"); - assertTypeHint(someObject, ConcreteChildClass.class); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test // DATAMONGO-812 - void updateMapperShouldConvertPushCorrectlyWhenCalledWithEachUsingSimpleTypes() { - - Update update = new Update().push("values").each("spring", "data", "mongodb"); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Model.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document values = getAsDocument(push, "values"); - List each = getAsDBList(values, "$each"); - - assertThat(push.get("_class")).isNull(); - assertThat(values.get("_class")).isNull(); - - assertThat(each).containsExactly("spring", "data", "mongodb"); - } - - @Test // DATAMONGO-812 - void updateMapperShouldConvertPushWhithoutAddingClassInformationWhenUsedWithEvery() { - - Update update = new Update().push("values").each("spring", "data", "mongodb"); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Model.class)); - Document push = getAsDocument(mappedObject, "$push"); - Document values = getAsDocument(push, "values"); - - assertThat(push.get("_class")).isNull(); - assertThat(values.get("_class")).isNull(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test // DATAMONGO-812 - void updateMapperShouldConvertPushCorrectlyWhenCalledWithEachUsingCustomTypes() { - - Update update = new Update().push("models").each(new ListModel("spring", "data", "mongodb")); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ModelWrapper.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document model = getAsDocument(push, "models"); - List each = getAsDBList(model, "$each"); - List values = getAsDBList((Document) each.get(0), "values"); - - assertThat(values).containsExactly("spring", "data", "mongodb"); - } - - @Test // DATAMONGO-812 - void updateMapperShouldRetainClassInformationForPushCorrectlyWhenCalledWithEachUsingCustomTypes() { - - Update update = new Update().push("models").each(new ListModel("spring", "data", "mongodb")); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ModelWrapper.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document model = getAsDocument(push, "models"); - List each = getAsDBList(model, "$each"); - - assertTypeHint(each.get(0), ListModel.class); - } - - @Test // DATAMONGO-812 - void testUpdateShouldAllowMultiplePushEachForDifferentFields() { - - Update update = new Update().push("category").each("spring", "data").push("type").each("mongodb"); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); - - Document push = getAsDocument(mappedObject, "$push"); - assertThat(getAsDocument(push, "category")).containsKey("$each"); - assertThat(getAsDocument(push, "type")).containsKey("$each"); - } - - @Test // DATAMONGO-943 - void updatePushEachAtPositionWorksCorrectlyWhenGivenPositiveIndexParameter() { - - Update update = new Update().push("key").atPosition(2).each(Arrays.asList("Arya", "Arry", "Weasel")); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "key"); - - assertThat(key.containsKey("$position")).isTrue(); - assertThat(key.get("$position")).isEqualTo(2); - assertThat(getAsDocument(push, "key")).containsKey("$each"); - } - - @Test // DATAMONGO-943, DATAMONGO-2055 - void updatePushEachAtNegativePositionWorksCorrectly() { - - Update update = new Update().push("key").atPosition(-2).each(Arrays.asList("Arya", "Arry", "Weasel")); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "key"); - - assertThat(key.containsKey("$position")).isTrue(); - assertThat(key.get("$position")).isEqualTo(-2); - } - - @Test // DATAMONGO-943 - void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionFirst() { - - Update update = new Update().push("key").atPosition(Position.FIRST).each(Arrays.asList("Arya", "Arry", "Weasel")); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "key"); - - assertThat(key.containsKey("$position")).isTrue(); - assertThat(key.get("$position")).isEqualTo(0); - assertThat(getAsDocument(push, "key")).containsKey("$each"); - } - - @Test // DATAMONGO-943 - void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionLast() { - - Update update = new Update().push("key").atPosition(Position.LAST).each(Arrays.asList("Arya", "Arry", "Weasel")); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "key"); - - assertThat(key).doesNotContainKey("$position"); - assertThat(getAsDocument(push, "key")).containsKey("$each"); - } - - @Test // DATAMONGO-943 - void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionNull() { - - Update update = new Update().push("key").atPosition(null).each(Arrays.asList("Arya", "Arry", "Weasel")); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "key"); - - assertThat(key).doesNotContainKey("$position"); - assertThat(getAsDocument(push, "key")).containsKey("$each"); - } - - @Test // DATAMONGO-832 - void updatePushEachWithSliceShouldRenderCorrectly() { - - Update update = new Update().push("key").slice(5).each(Arrays.asList("Arya", "Arry", "Weasel")); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "key"); - - assertThat(key).containsKey("$slice").containsEntry("$slice", 5); - assertThat(key).containsKey("$each"); - } - - @Test // DATAMONGO-832 - void updatePushEachWithSliceShouldRenderWhenUsingMultiplePushCorrectly() { - - Update update = new Update().push("key").slice(5).each(Arrays.asList("Arya", "Arry", "Weasel")).push("key-2") - .slice(-2).each("The Beggar King", "Viserys III Targaryen"); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "key"); - - assertThat(key).containsKey("$slice").containsEntry("$slice", 5); - assertThat(key.containsKey("$each")).isTrue(); - - Document key2 = getAsDocument(push, "key-2"); - - assertThat(key2).containsKey("$slice").containsEntry("$slice", -2); - assertThat(key2).containsKey("$each"); - } - - @Test // DATAMONGO-1141 - void updatePushEachWithValueSortShouldRenderCorrectly() { - - Update update = new Update().push("scores").sort(Direction.DESC).each(42, 23, 68); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "scores"); - - assertThat(key).containsKey("$sort"); - assertThat(key).containsEntry("$sort", -1); - assertThat(key).containsKey("$each"); - } - - @Test // DATAMONGO-1141 - void updatePushEachWithDocumentSortShouldRenderCorrectly() { - - Update update = new Update().push("list") - .sort(Sort.by(new Order(Direction.ASC, "value"), new Order(Direction.ASC, "field"))) - .each(Collections.emptyList()); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithList.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "list"); - - assertThat(key).containsKey("$sort"); - assertThat(key.get("$sort")).isEqualTo(new Document("renamed-value", 1).append("field", 1)); - assertThat(key).containsKey("$each"); - } - - @Test // DATAMONGO-1141 - void updatePushEachWithSortShouldRenderCorrectlyWhenUsingMultiplePush() { - - Update update = new Update().push("authors").sort(Direction.ASC).each("Harry").push("chapters") - .sort(Sort.by(Direction.ASC, "order")).each(Collections.emptyList()); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); - - Document push = getAsDocument(mappedObject, "$push"); - Document key1 = getAsDocument(push, "authors"); - - assertThat(key1).containsKey("$sort"); - assertThat(key1).containsEntry("$sort", 1); - assertThat(key1).containsKey("$each"); - - Document key2 = getAsDocument(push, "chapters"); - - assertThat(key2).containsKey("$sort"); - assertThat(key2.get("$sort")).isEqualTo(new Document("order", 1)); - assertThat(key2.containsKey("$each")).isTrue(); - } - - @Test // DATAMONGO-410 - void testUpdateMapperShouldConsiderCustomWriteTarget() { - - List someValues = Arrays.asList(new NestedEntity("spring"), new NestedEntity("data"), - new NestedEntity("mongodb")); - NestedEntity[] array = new NestedEntity[someValues.size()]; - - Update update = new Update().pushAll("collectionOfNestedEntities", someValues.toArray(array)); - mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(DomainEntity.class)); - - verify(writingConverterSpy, times(3)).convert(Mockito.any(NestedEntity.class)); - } - - @Test // DATAMONGO-404 - void createsDbRefForEntityIdOnPulls() { - - Update update = new Update().pull("dbRefAnnotatedList.id", "2"); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DocumentWithDBRefCollection.class)); - - Document pullClause = getAsDocument(mappedObject, "$pull"); - assertThat(pullClause.get("dbRefAnnotatedList")).isEqualTo(new DBRef("entity", "2")); - } - - @Test // DATAMONGO-404 - void createsDbRefForEntityOnPulls() { - - Entity entity = new Entity(); - entity.id = "5"; - - Update update = new Update().pull("dbRefAnnotatedList", entity); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DocumentWithDBRefCollection.class)); - - Document pullClause = getAsDocument(mappedObject, "$pull"); - assertThat(pullClause.get("dbRefAnnotatedList")).isEqualTo(new DBRef("entity", entity.id)); - } - - @Test // DATAMONGO-404 - void rejectsInvalidFieldReferenceForDbRef() { - - Update update = new Update().pull("dbRefAnnotatedList.name", "NAME"); - assertThatThrownBy(() -> mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DocumentWithDBRefCollection.class))).isInstanceOf(MappingException.class); - } - - @Test // DATAMONGO-404 - void rendersNestedDbRefCorrectly() { - - Update update = new Update().pull("nested.dbRefAnnotatedList.id", "2"); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(Wrapper.class)); - - Document pullClause = getAsDocument(mappedObject, "$pull"); - assertThat(pullClause.containsKey("mapped.dbRefAnnotatedList")).isTrue(); - } - - @Test // DATAMONGO-468 - void rendersUpdateOfDbRefPropertyWithDomainObjectCorrectly() { - - Entity entity = new Entity(); - entity.id = "5"; - - Update update = new Update().set("dbRefProperty", entity); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DocumentWithDBRefCollection.class)); - - Document setClause = getAsDocument(mappedObject, "$set"); - assertThat(setClause.get("dbRefProperty")).isEqualTo(new DBRef("entity", entity.id)); - } - - @Test // DATAMONGO-862 - void rendersUpdateAndPreservesKeyForPathsNotPointingToProperty() { - - Update update = new Update().set("listOfInterface.$.value", "expected-value"); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - Document setClause = getAsDocument(mappedObject, "$set"); - assertThat(setClause.containsKey("listOfInterface.$.value")).isTrue(); - } - - @Test // DATAMONGO-863 - void doesNotConvertRawDocuments() { - - Update update = new Update(); - update.pull("options", - new Document("_id", new Document("$in", converter.convertToMongoType(Arrays.asList(1L, 2L))))); - - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - Document setClause = getAsDocument(mappedObject, "$pull"); - Document options = getAsDocument(setClause, "options"); - Document idClause = getAsDocument(options, "_id"); - List inClause = getAsDBList(idClause, "$in"); - - assertThat(inClause).containsExactly(1L, 2L); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Test // DATAMONG0-471 - void testUpdateShouldApply$addToSetCorrectlyWhenUsedWith$each() { - - Update update = new Update().addToSet("values").each("spring", "data", "mongodb"); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ListModel.class)); - - Document addToSet = getAsDocument(mappedObject, "$addToSet"); - Document values = getAsDocument(addToSet, "values"); - List each = getAsDBList(values, "$each"); - - assertThat(each).containsExactly("spring", "data", "mongodb"); - } - - @Test // DATAMONG0-471 - void testUpdateShouldRetainClassTypeInformationWhenUsing$addToSetWith$eachForCustomTypes() { - - Update update = new Update().addToSet("models").each(new ModelImpl(2014), new ModelImpl(1), new ModelImpl(28)); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ModelWrapper.class)); - - Document addToSet = getAsDocument(mappedObject, "$addToSet"); - - Document values = getAsDocument(addToSet, "models"); - List each = getAsDBList(values, "$each"); - - for (Object updateValue : each) { - assertTypeHint((Document) updateValue, ModelImpl.class); - } - } - - @Test // DATAMONGO-897 - void updateOnDbrefPropertyOfInterfaceTypeWithoutExplicitGetterForIdShouldBeMappedCorrectly() { - - Update update = new Update().set("referencedDocument", new InterfaceDocumentDefinitionImpl("1", "Foo")); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DocumentWithReferenceToInterfaceImpl.class)); - - Document $set = DocumentTestUtils.getAsDocument(mappedObject, "$set"); - Object model = $set.get("referencedDocument"); - - DBRef expectedDBRef = new DBRef("interfaceDocumentDefinitionImpl", "1"); - assertThat(model).isInstanceOf(DBRef.class).isEqualTo(expectedDBRef); - } - - @Test // DATAMONGO-847 - void updateMapperConvertsNestedQueryCorrectly() { - - Update update = new Update().pull("list", Query.query(Criteria.where("value").in("foo", "bar"))); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - Document $pull = DocumentTestUtils.getAsDocument(mappedUpdate, "$pull"); - Document list = DocumentTestUtils.getAsDocument($pull, "aliased"); - Document value = DocumentTestUtils.getAsDocument(list, "value"); - List $in = DocumentTestUtils.getAsDBList(value, "$in"); - - assertThat($in).containsExactly("foo", "bar"); - } - - @Test // DATAMONGO-847 - void updateMapperConvertsPullWithNestedQuerfyOnDBRefCorrectly() { - - Update update = new Update().pull("dbRefAnnotatedList", Query.query(Criteria.where("id").is("1"))); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DocumentWithDBRefCollection.class)); - - Document $pull = DocumentTestUtils.getAsDocument(mappedUpdate, "$pull"); - Document list = DocumentTestUtils.getAsDocument($pull, "dbRefAnnotatedList"); - - assertThat(list).isEqualTo(new org.bson.Document().append("_id", "1")); - } - - @Test // DATAMONGO-1077 - void shouldNotRemovePositionalParameter() { - - Update update = new Update(); - update.unset("dbRefAnnotatedList.$"); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DocumentWithDBRefCollection.class)); - - Document $unset = DocumentTestUtils.getAsDocument(mappedUpdate, "$unset"); - - assertThat($unset).isEqualTo(new org.bson.Document().append("dbRefAnnotatedList.$", 1)); - } - - @Test // DATAMONGO-1210 - void mappingEachOperatorShouldNotAddTypeInfoForNonInterfaceNonAbstractTypes() { - - Update update = new Update().addToSet("nestedDocs").each(new NestedDocument("nested-1"), - new NestedDocument("nested-2")); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DocumentWithNestedCollection.class)); - - assertThat(mappedUpdate).doesNotContainKey("$addToSet.nestedDocs.$each.[0]._class") - .doesNotContainKey("$addToSet.nestedDocs.$each.[1]._class"); - } - - @Test // DATAMONGO-1210 - void mappingEachOperatorShouldAddTypeHintForInterfaceTypes() { - - Update update = new Update().addToSet("models").each(new ModelImpl(1), new ModelImpl(2)); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ListModelWrapper.class)); - - assertThat(mappedUpdate).containsEntry("$addToSet.models.$each.[0]._class", ModelImpl.class.getName()); - assertThat(mappedUpdate).containsEntry("$addToSet.models.$each.[1]._class", ModelImpl.class.getName()); - } - - @Test // DATAMONGO-1210 - void mappingEachOperatorShouldAddTypeHintForAbstractTypes() { - - Update update = new Update().addToSet("list").each(new ConcreteChildClass("foo", "one"), - new ConcreteChildClass("bar", "two")); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - assertThat(mappedUpdate).containsEntry("$addToSet.aliased.$each.[0]._class", ConcreteChildClass.class.getName()); - assertThat(mappedUpdate).containsEntry("$addToSet.aliased.$each.[1]._class", ConcreteChildClass.class.getName()); - } - - @Test // DATAMONGO-1210 - void mappingShouldOnlyRemoveTypeHintFromTopLevelTypeInCaseOfNestedDocument() { - - WrapperAroundInterfaceType wait = new WrapperAroundInterfaceType(); - wait.interfaceType = new ModelImpl(1); - - Update update = new Update().addToSet("listHoldingConcretyTypeWithInterfaceTypeAttribute").each(wait); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DomainTypeWithListOfConcreteTypesHavingSingleInterfaceTypeAttribute.class)); - - assertThat(mappedUpdate) - .doesNotContainKey("$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0]._class"); - assertThat(mappedUpdate).containsEntry( - "$addToSet.listHoldingConcretyTypeWithInterfaceTypeAttribute.$each.[0].interfaceType._class", - ModelImpl.class.getName()); - } - - @Test // DATAMONGO-1210 - void mappingShouldRetainTypeInformationOfNestedListWhenUpdatingConcreteyParentType() { - - ListModelWrapper lmw = new ListModelWrapper(); - lmw.models = Collections.singletonList(new ModelImpl(1)); - - Update update = new Update().set("concreteTypeWithListAttributeOfInterfaceType", lmw); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes.class)); - - assertThat(mappedUpdate).doesNotContainKey("$set.concreteTypeWithListAttributeOfInterfaceType._class"); - assertThat(mappedUpdate).containsEntry("$set.concreteTypeWithListAttributeOfInterfaceType.models.[0]._class", - ModelImpl.class.getName()); - } - - @Test // DATAMONGO-1809 - void pathShouldIdentifyPositionalParameterWithMoreThanOneDigit() { - - Document at2digitPosition = mapper.getMappedObject(new Update() - .addToSet("concreteInnerList.10.concreteTypeList", new SomeInterfaceImpl("szeth")).getUpdateObject(), - context.getPersistentEntity(Outer.class)); - - Document at3digitPosition = mapper.getMappedObject(new Update() - .addToSet("concreteInnerList.123.concreteTypeList", new SomeInterfaceImpl("lopen")).getUpdateObject(), - context.getPersistentEntity(Outer.class)); - - assertThat(at2digitPosition).isEqualTo(new Document("$addToSet", - new Document("concreteInnerList.10.concreteTypeList", new Document("value", "szeth")))); - assertThat(at3digitPosition).isEqualTo(new Document("$addToSet", - new Document("concreteInnerList.123.concreteTypeList", new Document("value", "lopen")))); - } - - @Test // DATAMONGO-1236 - void mappingShouldRetainTypeInformationForObjectValues() { - - Update update = new Update().set("value", new NestedDocument("kaladin")); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObject.class)); - - assertThat(mappedUpdate).containsEntry("$set.value.name", "kaladin"); - assertThat(mappedUpdate).containsEntry("$set.value._class", NestedDocument.class.getName()); - } - - @Test // DATAMONGO-1236 - void mappingShouldNotRetainTypeInformationForConcreteValues() { - - Update update = new Update().set("concreteValue", new NestedDocument("shallan")); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObject.class)); - - assertThat(mappedUpdate).containsEntry("$set.concreteValue.name", "shallan"); - assertThat(mappedUpdate).doesNotContainKey("$set.concreteValue._class"); - } - - @Test // DATAMONGO-1236 - void mappingShouldRetainTypeInformationForObjectValuesWithAlias() { - - Update update = new Update().set("value", new NestedDocument("adolin")); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithAliasedObject.class)); - - assertThat(mappedUpdate).containsEntry("$set.renamed-value.name", "adolin"); - assertThat(mappedUpdate).containsEntry("$set.renamed-value._class", NestedDocument.class.getName()); - } - - @Test // DATAMONGO-1236 - void mappingShouldRetrainTypeInformationWhenValueTypeOfMapDoesNotMatchItsDeclaration() { - - Map map = Collections.singletonMap("szeth", new NestedDocument("son-son-vallano")); - - Update update = new Update().set("map", map); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObjectMap.class)); - - assertThat(mappedUpdate).containsEntry("$set.map.szeth.name", "son-son-vallano"); - assertThat(mappedUpdate).containsEntry("$set.map.szeth._class", NestedDocument.class.getName()); - } - - @Test // DATAMONGO-1236 - void mappingShouldNotContainTypeInformationWhenValueTypeOfMapMatchesDeclaration() { - - Map map = Collections.singletonMap("jasnah", new NestedDocument("kholin")); - - Update update = new Update().set("concreteMap", map); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObjectMap.class)); - - assertThat(mappedUpdate).containsEntry("$set.concreteMap.jasnah.name", "kholin"); - assertThat(mappedUpdate).doesNotContainKey("$set.concreteMap.jasnah._class"); - } - - @Test // DATAMONGO-1250 - @SuppressWarnings("unchecked") - void mapsUpdateWithBothReadingAndWritingConverterRegistered() { - - CustomConversions conversions = new MongoCustomConversions(Arrays.asList( - ClassWithEnum.AllocationToStringConverter.INSTANCE, ClassWithEnum.StringToAllocationConverter.INSTANCE)); - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); - mappingContext.afterPropertiesSet(); - - MappingMongoConverter converter = new MappingMongoConverter(mock(DbRefResolver.class), mappingContext); - converter.setCustomConversions(conversions); - converter.afterPropertiesSet(); - - UpdateMapper mapper = new UpdateMapper(converter); - - Update update = new Update().set("allocation", ClassWithEnum.Allocation.AVAILABLE); - Document result = mapper.getMappedObject(update.getUpdateObject(), - mappingContext.getPersistentEntity(ClassWithEnum.class)); - - assertThat(result).containsEntry("$set.allocation", ClassWithEnum.Allocation.AVAILABLE.code); - } - - @Test // DATAMONGO-1251 - void mapsNullValueCorrectlyForSimpleTypes() { - - Update update = new Update().set("value", null); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ConcreteChildClass.class)); - - Document $set = DocumentTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set).containsKey("value").containsEntry("value", null); - } - - @Test // DATAMONGO-1251 - void mapsNullValueCorrectlyForJava8Date() { - - Update update = new Update().set("date", null); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ClassWithJava8Date.class)); - - Document $set = DocumentTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set).containsKey("date").doesNotContainKey("value"); - } - - @Test // DATAMONGO-1251 - void mapsNullValueCorrectlyForCollectionTypes() { - - Update update = new Update().set("values", null); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ListModel.class)); - - Document $set = DocumentTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set).containsKey("values").doesNotContainKey("value"); - } - - @Test // DATAMONGO-1251 - void mapsNullValueCorrectlyForPropertyOfNestedDocument() { - - Update update = new Update().set("concreteValue.name", null); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObject.class)); - - Document $set = DocumentTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set).containsKey("concreteValue.name"); - assertThat($set).containsEntry("concreteValue.name", null); - } - - @Test // DATAMONGO-1288 - void mapsAtomicIntegerToIntegerCorrectly() { - - Update update = new Update().set("intValue", new AtomicInteger(10)); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(SimpleValueHolder.class)); - - Document $set = DocumentTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set.get("intValue")).isEqualTo(10); - } - - @Test // DATAMONGO-1288 - void mapsAtomicIntegerToPrimitiveIntegerCorrectly() { - - Update update = new Update().set("primIntValue", new AtomicInteger(10)); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(SimpleValueHolder.class)); - - Document $set = DocumentTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set.get("primIntValue")).isEqualTo(10); - } - - @Test // DATAMONGO-1404 - void mapsMinCorrectly() { - - Update update = new Update().min("minfield", 10); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(SimpleValueHolder.class)); - - assertThat(mappedUpdate).containsEntry("$min", new Document("minfield", 10)); - } - - @Test // DATAMONGO-1404 - void mapsMaxCorrectly() { - - Update update = new Update().max("maxfield", 999); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(SimpleValueHolder.class)); - - assertThat(mappedUpdate).containsEntry("$max", new Document("maxfield", 999)); - } - - @Test // DATAMONGO-1423, DATAMONGO-2155 - @SuppressWarnings("unchecked") - void mappingShouldConsiderCustomConvertersForEnumMapKeys() { - - CustomConversions conversions = new MongoCustomConversions(Arrays.asList( - ClassWithEnum.AllocationToStringConverter.INSTANCE, ClassWithEnum.StringToAllocationConverter.INSTANCE)); - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); - mappingContext.afterPropertiesSet(); - - MappingMongoConverter converter = new MappingMongoConverter(mock(DbRefResolver.class), mappingContext); - converter.setCustomConversions(conversions); - converter.afterPropertiesSet(); - - UpdateMapper mapper = new UpdateMapper(converter); - - Update update = new Update().set("enumAsMapKey", Collections.singletonMap(ClassWithEnum.Allocation.AVAILABLE, 100)); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - mappingContext.getPersistentEntity(ClassWithEnum.class)); - - Document $set = DocumentTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set.containsKey("enumAsMapKey")).isTrue(); - - Map enumAsMapKey = $set.get("enumAsMapKey", Map.class); - assertThat(enumAsMapKey.get("V")).isEqualTo(100); - } - - @Test // DATAMONGO-1176 - void mappingShouldPrepareUpdateObjectForMixedOperatorsAndFields() { - - Document document = new Document("key", "value").append("$set", new Document("a", "b").append("x", "y")); - - Document mappedObject = mapper.getMappedObject(document, context.getPersistentEntity(SimpleValueHolder.class)); - - assertThat(mappedObject.get("$set")).isEqualTo(new Document("a", "b").append("x", "y").append("key", "value")); - assertThat(mappedObject).hasSize(1); - } - - @Test // DATAMONGO-1176 - void mappingShouldReturnReplaceObject() { - - Document document = new Document("key", "value").append("a", "b").append("x", "y"); - - Document mappedObject = mapper.getMappedObject(document, context.getPersistentEntity(SimpleValueHolder.class)); - - assertThat(mappedObject).containsEntry("key", "value"); - assertThat(mappedObject).containsEntry("a", "b"); - assertThat(mappedObject).containsEntry("x", "y"); - assertThat(mappedObject).hasSize(3); - } - - @Test // DATAMONGO-1176 - void mappingShouldReturnUpdateObject() { - - Document document = new Document("$push", new Document("x", "y")).append("$set", new Document("a", "b")); - - Document mappedObject = mapper.getMappedObject(document, context.getPersistentEntity(SimpleValueHolder.class)); - - assertThat(mappedObject).containsEntry("$push", new Document("x", "y")); - assertThat(mappedObject).containsEntry("$set", new Document("a", "b")); - assertThat(mappedObject).hasSize(2); - } - - @Test // DATAMONGO-1486, DATAMONGO-2155 - void mappingShouldConvertMapKeysToString() { - - Update update = new Update().set("map", Collections.singletonMap(25, "#StarTrek50")); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObjectMap.class)); - - Document $set = DocumentTestUtils.getAsDocument(mappedUpdate, "$set"); - assertThat($set.containsKey("map")).isTrue(); - - Map mapToSet = $set.get("map", Map.class); - for (Object key : mapToSet.keySet()) { - assertThat(key).isInstanceOf(String.class); - } - } - - @Test // DATAMONGO-1772 - void mappingShouldAddTypeKeyInListOfInterfaceTypeContainedInConcreteObjectCorrectly() { - - ConcreteInner inner = new ConcreteInner(); - inner.interfaceTypeList = Collections.singletonList(new SomeInterfaceImpl()); - List list = Collections.singletonList(inner); - - Document mappedUpdate = mapper.getMappedObject(new Update().set("concreteInnerList", list).getUpdateObject(), - context.getPersistentEntity(Outer.class)); - - assertThat(mappedUpdate).containsKey("$set.concreteInnerList.[0].interfaceTypeList.[0]._class") - .doesNotContainKey("$set.concreteInnerList.[0]._class"); - } - - @Test // DATAMONGO-1772 - void mappingShouldAddTypeKeyInListOfAbstractTypeContainedInConcreteObjectCorrectly() { - - ConcreteInner inner = new ConcreteInner(); - inner.abstractTypeList = Collections.singletonList(new SomeInterfaceImpl()); - List list = Collections.singletonList(inner); - - Document mappedUpdate = mapper.getMappedObject(new Update().set("concreteInnerList", list).getUpdateObject(), - context.getPersistentEntity(Outer.class)); - - assertThat(mappedUpdate).containsKey("$set.concreteInnerList.[0].abstractTypeList.[0]._class") - .doesNotContainKey("$set.concreteInnerList.[0]._class"); - } - - @Test // DATAMONGO-2155 - void shouldPreserveFieldNamesOfMapProperties() { - - Update update = Update - .fromDocument(new Document("concreteMap", new Document("Name", new Document("name", "fooo")))); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObjectMap.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("concreteMap", new Document("Name", new Document("name", "fooo")))); - } - - @Test // DATAMONGO-2155 - void shouldPreserveExplicitFieldNamesInsideMapProperties() { - - Update update = Update - .fromDocument(new Document("map", new Document("Value", new Document("renamed-value", "fooo")))); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithMapOfAliased.class)); - - assertThat(mappedUpdate) - .isEqualTo(new Document("map", new Document("Value", new Document("renamed-value", "fooo")))); - } - - @Test // DATAMONGO-2155 - void shouldMapAliasedFieldNamesInMapsCorrectly() { - - Update update = Update - .fromDocument(new Document("map", Collections.singletonMap("Value", new Document("value", "fooo")))); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithMapOfAliased.class)); - - assertThat(mappedUpdate) - .isEqualTo(new Document("map", new Document("Value", new Document("renamed-value", "fooo")))); - } - - @Test // DATAMONGO-2174 - void mappingUpdateDocumentWithExplicitFieldNameShouldBePossible() { - - Document mappedUpdate = mapper.getMappedObject(new Document("AValue", "a value"), - context.getPersistentEntity(TypeWithFieldNameThatCannotBeDecapitalized.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("AValue", "a value")); - } - - @Test // DATAMONGO-2054 - void mappingShouldAllowPositionAllParameter() { - - Update update = new Update().inc("grades.$[]", 10); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithListOfIntegers.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$inc", new Document("grades.$[]", 10))); - } - - @Test // DATAMONGO-2054 - void mappingShouldAllowPositionAllParameterWhenPropertyHasExplicitFieldName() { - - Update update = new Update().inc("list.$[]", 10); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$inc", new Document("aliased.$[]", 10))); - } - - @Test // DATAMONGO-2215 - void mappingShouldAllowPositionParameterWithIdentifier() { - - Update update = new Update().set("grades.$[element]", 10) // - .filterArray(Criteria.where("element").gte(100)); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithListOfIntegers.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$set", new Document("grades.$[element]", 10))); - } - - @Test // DATAMONGO-2215 - void mappingShouldAllowPositionParameterWithIdentifierWhenFieldHasExplicitFieldName() { - - Update update = new Update().set("list.$[element]", 10) // - .filterArray(Criteria.where("element").gte(100)); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$set", new Document("aliased.$[element]", 10))); - } - - @Test // DATAMONGO-2215 - void mappingShouldAllowNestedPositionParameterWithIdentifierWhenFieldHasExplicitFieldName() { - - Update update = new Update().set("list.$[element].value", 10) // - .filterArray(Criteria.where("element").gte(100)); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(ParentClass.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$set", new Document("aliased.$[element].value", 10))); - } - - @Test // DATAMONGO-1902 - void mappingShouldConsiderValueOfUnwrappedType() { - - Update update = new Update().set("unwrappedValue.stringValue", "updated"); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(WithUnwrapped.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$set", new Document("stringValue", "updated"))); - } - - @Test // DATAMONGO-1902 - void mappingShouldConsiderUnwrappedType() { - - UnwrappableType unwrappableType = new UnwrappableType(); - unwrappableType.stringValue = "updated"; - unwrappableType.listValue = Arrays.asList("val-1", "val-2"); - Update update = new Update().set("unwrappedValue", unwrappableType); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(WithUnwrapped.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$set", - new Document("stringValue", "updated").append("listValue", Arrays.asList("val-1", "val-2")))); - } - - @Test // DATAMONGO-1902 - void mappingShouldConsiderValueOfPrefixedUnwrappedType() { - - Update update = new Update().set("unwrappedValue.stringValue", "updated"); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(WithPrefixedUnwrapped.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$set", new Document("prefix-stringValue", "updated"))); - } - - @Test // DATAMONGO-1902 - void mappingShouldConsiderPrefixedUnwrappedType() { - - UnwrappableType unwrappableType = new UnwrappableType(); - unwrappableType.stringValue = "updated"; - unwrappableType.listValue = Arrays.asList("val-1", "val-2"); - - Update update = new Update().set("unwrappedValue", unwrappableType); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(WithPrefixedUnwrapped.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$set", - new Document("prefix-stringValue", "updated").append("prefix-listValue", Arrays.asList("val-1", "val-2")))); - } - - @Test // DATAMONGO-1902 - void mappingShouldConsiderNestedPrefixedUnwrappedType() { - - UnwrappableType unwrappableType = new UnwrappableType(); - unwrappableType.stringValue = "updated"; - unwrappableType.listValue = Arrays.asList("val-1", "val-2"); - - Update update = new Update().set("withPrefixedUnwrapped.unwrappedValue", unwrappableType); - - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(WrapperAroundWithUnwrapped.class)); - - assertThat(mappedUpdate).isEqualTo(new Document("$set", new Document("withPrefixedUnwrapped", - new Document("prefix-stringValue", "updated").append("prefix-listValue", Arrays.asList("val-1", "val-2"))))); - } - - @Test // GH-3552 - void numericKeyForMap() { - - Update update = new Update().set("map.601218778970110001827396", "testing"); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObjectMap.class)); - - assertThat(mappedUpdate).isEqualTo("{\"$set\": {\"map.601218778970110001827396\": \"testing\"}}"); - } - - @Test // GH-3552 - void numericKeyInMapOfNestedPath() { - - Update update = new Update().set("map.601218778970110001827396.value", "testing"); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObjectMap.class)); - - assertThat(mappedUpdate).isEqualTo("{\"$set\": {\"map.601218778970110001827396.value\": \"testing\"}}"); - } - - @Test // GH-3566 - void mapsObjectClassPropertyFieldInMapValueTypeAsKey() { - - Update update = new Update().set("map.class", "value"); - Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), - context.getPersistentEntity(EntityWithObjectMap.class)); - - assertThat(mappedUpdate).isEqualTo("{\"$set\": {\"map.class\": \"value\"}}"); - } - - static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes { - ListModelWrapper concreteTypeWithListAttributeOfInterfaceType; - } - - static class DomainTypeWithListOfConcreteTypesHavingSingleInterfaceTypeAttribute { - List listHoldingConcretyTypeWithInterfaceTypeAttribute; - } - - static class WrapperAroundInterfaceType { - Model interfaceType; - } - - @org.springframework.data.mongodb.core.mapping.Document(collection = "DocumentWithReferenceToInterface") - interface DocumentWithReferenceToInterface { - - String getId(); - - InterfaceDocumentDefinitionWithoutId getReferencedDocument(); - - } - - interface InterfaceDocumentDefinitionWithoutId { - - String getValue(); - } - - static class InterfaceDocumentDefinitionImpl implements InterfaceDocumentDefinitionWithoutId { - - @Id String id; - String value; - - InterfaceDocumentDefinitionImpl(String id, String value) { - - this.id = id; - this.value = value; - } - - @Override - public String getValue() { - return this.value; - } - - } - - static class DocumentWithReferenceToInterfaceImpl implements DocumentWithReferenceToInterface { - - private @Id String id; - - @org.springframework.data.mongodb.core.mapping.DBRef // - private InterfaceDocumentDefinitionWithoutId referencedDocument; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public void setModel(InterfaceDocumentDefinitionWithoutId referencedDocument) { - this.referencedDocument = referencedDocument; - } - - @Override - public InterfaceDocumentDefinitionWithoutId getReferencedDocument() { - return this.referencedDocument; - } - - } - - interface Model {} - - static class ModelImpl implements Model { - public int value; - - ModelImpl(int value) { - this.value = value; - } - - } - - public class ModelWrapper { - Model model; - - public ModelWrapper() {} - - public ModelWrapper(Model model) { - this.model = model; - } - } - - static class ListModelWrapper { - - List models; - } - - static class ListModel { - - List values; - - ListModel(String... values) { - this.values = Arrays.asList(values); - } - } - - static class ParentClass { - - String id; - - @Field("aliased") // - List list; - - @Field // - List listOfInterface; - - public ParentClass(String id, List list) { - this.id = id; - this.list = list; - } - - } - - static abstract class AbstractChildClass { - - String id; - String value; - String otherValue; - AbstractChildClass someObject; - - AbstractChildClass(String id, String value) { - this.id = id; - this.value = value; - this.otherValue = "other_" + value; - } - } - - static class ConcreteChildClass extends AbstractChildClass { - - ConcreteChildClass(String id, String value) { - super(id, value); - } - } - - static class DomainEntity { - List collectionOfNestedEntities; - - public List getCollectionOfNestedEntities() { - return collectionOfNestedEntities; - } - } - - static class NestedEntity { - String name; - - NestedEntity(String name) { - super(); - this.name = name; - } - - } - - @WritingConverter - static class NestedEntityWriteConverter implements Converter { - - @Override - public Document convert(NestedEntity source) { - return new Document(); - } - } - - static class DocumentWithDBRefCollection { - - @Id public String id; - - @org.springframework.data.mongodb.core.mapping.DBRef // - public List dbRefAnnotatedList; - - @org.springframework.data.mongodb.core.mapping.DBRef // - public Entity dbRefProperty; - } - - static class Entity { - - @Id public String id; - String name; - } - - static class Wrapper { - - @Field("mapped") DocumentWithDBRefCollection nested; - } - - static class DocumentWithNestedCollection { - List nestedDocs; - } - - static class NestedDocument { - - String name; - - NestedDocument(String name) { - super(); - this.name = name; - } - } - - static class EntityWithObject { - - Object value; - NestedDocument concreteValue; - } - - static class EntityWithList { - List list; - } - - static class EntityWithListOfIntegers { - List grades; - } - - static class EntityWithAliasedObject { - - @Field("renamed-value") Object value; - Object field; - } - - static class EntityWithMapOfAliased { - Map map; - } - - static class EntityWithObjectMap { - - Map map; - Map concreteMap; - } - - static class ClassWithEnum { - - Allocation allocation; - Map enumAsMapKey; - - enum Allocation { - - AVAILABLE("V"), ALLOCATED("A"); - - String code; - - Allocation(String code) { - this.code = code; - } - - public static Allocation of(String code) { - - for (Allocation value : values()) { - if (value.code.equals(code)) { - return value; - } - } - - throw new IllegalArgumentException(); - } - } - - enum AllocationToStringConverter implements Converter { - - INSTANCE; - - @Override - public String convert(Allocation source) { - return source.code; - } - } - - enum StringToAllocationConverter implements Converter { - - INSTANCE; - - @Override - public Allocation convert(String source) { - return Allocation.of(source); - } - } - } - - static class ClassWithJava8Date { - - LocalDate date; - } - - static class SimpleValueHolder { - - Integer intValue; - int primIntValue; - } - - static class Outer { - List concreteInnerList; - } - - static class ConcreteInner { - List interfaceTypeList; - List abstractTypeList; - List concreteTypeList; - } - - interface SomeInterfaceType { - - } - - static abstract class SomeAbstractType { - - } - - @AllArgsConstructor - @NoArgsConstructor - static class SomeInterfaceImpl extends SomeAbstractType implements SomeInterfaceType { - - String value; - } - - @Data - static class TypeWithFieldNameThatCannotBeDecapitalized { - - @Id protected String id; - - @Field("AValue") private Long aValue = 0L; - - } - - static class WrapperAroundWithUnwrapped { - - String someValue; - WithUnwrapped withUnwrapped; - WithPrefixedUnwrapped withPrefixedUnwrapped; - } - - static class WithUnwrapped { - - String id; - - @Unwrapped.Nullable UnwrappableType unwrappedValue; - } - - static class WithPrefixedUnwrapped { - - String id; - - @Unwrapped.Nullable("prefix-") UnwrappableType unwrappedValue; - } - - static class UnwrappableType { - - String stringValue; - List listValue; - - @Field("with-at-field-annotation") // - String atFieldAnnotatedValue; - - @Transient // - String transientValue; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/AbstractGeoSpatialTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/AbstractGeoSpatialTests.java deleted file mode 100644 index aa674804fe..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/AbstractGeoSpatialTests.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2015-2021 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.geo; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import org.joda.time.LocalDate; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.geo.Box; -import org.springframework.data.geo.Circle; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.geo.Polygon; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.BulkOperations.BulkMode; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.TestEntities; -import org.springframework.data.mongodb.core.Venue; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.WriteConcern; -import com.mongodb.client.MongoClient; - -/** - * @author Christoph Strobl - * @author Oliver Gierke - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public abstract class AbstractGeoSpatialTests { - - @Configuration - static class TestConfig extends AbstractMongoClientConfiguration { - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Override - public MongoClient mongoClient() { - return MongoTestUtils.client(); - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - } - - @Autowired MongoTemplate template; - - @Before - public void setUp() { - - template.setWriteConcern(WriteConcern.JOURNALED); - - createIndex(); - addVenues(); - } - - @After - public void tearDown() { - - dropIndex(); - removeVenues(); - } - - /** - * Create the index required to run the tests. - */ - protected abstract void createIndex(); - - /** - * Remove index - */ - protected abstract void dropIndex(); - - protected void removeVenues() { - template.dropCollection(Venue.class); - } - - protected void addVenues() { - - - template.bulkOps(BulkMode.UNORDERED, Venue.class).insert(TestEntities.geolocation().newYork()).execute(); -// template.insertAll(TestEntities.geolocation().newYork()); - } - - @Test - public void geoNear() { - - NearQuery geoNear = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).maxDistance(150); - - GeoResults result = template.geoNear(geoNear, Venue.class); - - assertThat(result.getContent()).isNotEmpty(); - assertThat(result.getAverageDistance().getMetric()).isEqualTo(Metrics.KILOMETERS); - } - - @Test - public void withinCenter() { - - Circle circle = new Circle(-73.99171, 40.738868, 0.01); - Query query = query(where("location").within(circle)); - List venues = template.find(query, Venue.class); - - assertThat(venues).hasSize(7); - assertThat(template.count(query, Venue.class)).isEqualTo(7); - } - - @Test - public void withinCenterSphere() { - - Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784); - Query query = query(where("location").withinSphere(circle)); - - List venues = template.find(query, Venue.class); - assertThat(venues).hasSize(11); - assertThat(template.count(query, Venue.class)).isEqualTo(11); - } - - @Test - public void withinBox() { - - Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)); - Query query = query(where("location").within(box)); - - List venues = template.find(query, Venue.class); - assertThat(venues).hasSize(4); - assertThat(template.count(query, Venue.class)).isEqualTo(4); - } - - @Test - public void withinPolygon() { - - Point first = new Point(-73.99756, 40.73083); - Point second = new Point(-73.99756, 40.741404); - Point third = new Point(-73.988135, 40.741404); - Point fourth = new Point(-73.988135, 40.73083); - - Polygon polygon = new Polygon(first, second, third, fourth); - - Query query = query(where("location").within(polygon)); - List venues = template.find(query, Venue.class); - assertThat(venues).hasSize(4); - assertThat(template.count(query, Venue.class)).isEqualTo(4); - } - - @Test - public void nearSphere() { - - Point point = new Point(-73.99171, 40.738868); - Query query = query(where("location").nearSphere(point).maxDistance(0.003712240453784)); - - List venues = template.find(query, Venue.class); - assertThat(venues).hasSize(11); - assertThat(template.count(query, Venue.class)).isEqualTo(11); - } - - @Test // DATAMONGO-1360 - public void mapsQueryContainedInNearQuery() { - - Query query = query(where("openingDate").lt(LocalDate.now())); - template.geoNear(NearQuery.near(1.5, 1.7).spherical(true).query(query), Venue.class); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonModuleUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonModuleUnitTests.java deleted file mode 100644 index 9af58d12f4..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonModuleUnitTests.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2015-2021 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.geo; - -import static org.assertj.core.api.Assertions.*; - -import java.io.IOException; -import java.util.Arrays; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.data.geo.Point; - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -/** - * @author Christoph Strobl - */ -public class GeoJsonModuleUnitTests { - - ObjectMapper mapper; - - @BeforeEach - public void setUp() { - - mapper = new ObjectMapper(); - mapper.registerModule(new GeoJsonModule()); - } - - @Test // DATAMONGO-1181 - public void shouldDeserializeJsonPointCorrectly() throws JsonParseException, JsonMappingException, IOException { - - String json = "{ \"type\": \"Point\", \"coordinates\": [10.0, 20.0] }"; - - assertThat(mapper.readValue(json, GeoJsonPoint.class)).isEqualTo(new GeoJsonPoint(10D, 20D)); - } - - @Test // DATAMONGO-1181 - public void shouldDeserializeGeoJsonLineStringCorrectly() - throws JsonParseException, JsonMappingException, IOException { - - String json = "{ \"type\": \"LineString\", \"coordinates\": [ [10.0, 20.0], [30.0, 40.0], [50.0, 60.0] ]}"; - - assertThat(mapper.readValue(json, GeoJsonLineString.class)) - .isEqualTo(new GeoJsonLineString(Arrays.asList(new Point(10, 20), new Point(30, 40), new Point(50, 60)))); - } - - @Test // DATAMONGO-1181 - public void shouldDeserializeGeoJsonMultiPointCorrectly() - throws JsonParseException, JsonMappingException, IOException { - - String json = "{ \"type\": \"MultiPoint\", \"coordinates\": [ [10.0, 20.0], [30.0, 40.0], [50.0, 60.0] ]}"; - - assertThat(mapper.readValue(json, GeoJsonLineString.class)) - .isEqualTo(new GeoJsonMultiPoint(Arrays.asList(new Point(10, 20), new Point(30, 40), new Point(50, 60)))); - } - - @Test // DATAMONGO-1181 - @SuppressWarnings("unchecked") - public void shouldDeserializeGeoJsonMultiLineStringCorrectly() - throws JsonParseException, JsonMappingException, IOException { - - String json = "{ \"type\": \"MultiLineString\", \"coordinates\": [ [ [10.0, 20.0], [30.0, 40.0] ], [ [50.0, 60.0] , [70.0, 80.0] ] ]}"; - - assertThat(mapper.readValue(json, GeoJsonMultiLineString.class)).isEqualTo(new GeoJsonMultiLineString( - Arrays.asList(new Point(10, 20), new Point(30, 40)), Arrays.asList(new Point(50, 60), new Point(70, 80)))); - } - - @Test // DATAMONGO-1181 - public void shouldDeserializeGeoJsonPolygonCorrectly() throws JsonParseException, JsonMappingException, IOException { - - String json = "{ \"type\": \"Polygon\", \"coordinates\": [ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] ]}"; - - assertThat(mapper.readValue(json, GeoJsonPolygon.class)).isEqualTo(new GeoJsonPolygon( - Arrays.asList(new Point(100, 0), new Point(101, 0), new Point(101, 1), new Point(100, 1), new Point(100, 0)))); - } - - @Test // DATAMONGO-1181 - public void shouldDeserializeGeoJsonMultiPolygonCorrectly() - throws JsonParseException, JsonMappingException, IOException { - - String json = "{ \"type\": \"Polygon\", \"coordinates\": [" - + "[[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]]," - + "[[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]," - + "[[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]"// - + "]}"; - - assertThat(mapper.readValue(json, GeoJsonMultiPolygon.class)).isEqualTo(new GeoJsonMultiPolygon(Arrays.asList( - new GeoJsonPolygon(Arrays.asList(new Point(102, 2), new Point(103, 2), new Point(103, 3), new Point(102, 3), - new Point(102, 2))), - new GeoJsonPolygon(Arrays.asList(new Point(100, 0), new Point(101, 0), new Point(101, 1), new Point(100, 1), - new Point(100, 0))), - new GeoJsonPolygon(Arrays.asList(new Point(100.2, 0.2), new Point(100.8, 0.2), new Point(100.8, 0.8), - new Point(100.2, 0.8), new Point(100.2, 0.2)))))); - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersUnitTests.java deleted file mode 100644 index 1ea4b46351..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonSerializersUnitTests.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2021 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.geo; - -import static org.assertj.core.api.Assertions.*; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.data.geo.Point; - -import com.fasterxml.jackson.databind.ObjectMapper; - -/** - * Unit tests for {@link GeoJsonSerializersModule}. - * - * @author Bjorn Harvold - * @author Christoph Strobl - */ -class GeoJsonSerializersUnitTests { - - private ObjectMapper mapper; - - @BeforeEach - void beforeEach() { - - mapper = new ObjectMapper(); - mapper.registerModule(new GeoJsonSerializersModule()); - } - - @Test // GH-3517 - void shouldSerializeJsonPointCorrectly() throws IOException { - - GeoJsonPoint geoJsonPoint = new GeoJsonPoint(10D, 20D); - - assertThat(mapper.writeValueAsString(geoJsonPoint)).isEqualTo("{\"type\":\"Point\",\"coordinates\":[10.0,20.0]}"); - } - - @Test // GH-3517 - void shouldSerializeGeoJsonLineStringCorrectly() throws IOException { - - GeoJsonLineString lineString = new GeoJsonLineString( - Arrays.asList(new Point(10, 20), new Point(30, 40), new Point(50, 60))); - - assertThat(mapper.writeValueAsString(lineString)) - .isEqualTo("{\"type\":\"LineString\",\"coordinates\":[[10.0,20.0],[30.0,40.0],[50.0,60.0]]}"); - } - - @Test // GH-3517 - void shouldSerializeGeoJsonMultiPointCorrectly() throws IOException { - - GeoJsonMultiPoint multiPoint = new GeoJsonMultiPoint( - Arrays.asList(new Point(10, 20), new Point(30, 40), new Point(50, 60))); - - assertThat(mapper.writeValueAsString(multiPoint)) - .isEqualTo("{\"type\":\"MultiPoint\",\"coordinates\":[[10.0,20.0],[30.0,40.0],[50.0,60.0]]}"); - } - - @Test // GH-3517 - void shouldSerializeJsonMultiLineStringCorrectly() throws IOException { - - GeoJsonMultiLineString multiLineString = new GeoJsonMultiLineString( - Arrays.asList(new Point(10, 20), new Point(30, 40)), Arrays.asList(new Point(50, 60), new Point(70, 80))); - - assertThat(mapper.writeValueAsString(multiLineString)).isEqualTo( - "{\"type\":\"MultiLineString\",\"coordinates\":[[[10.0,20.0],[30.0,40.0]],[[50.0,60.0],[70.0,80.0]]]}"); - } - - @Test // GH-3517 - void shouldSerializeGeoJsonPolygonCorrectly() throws IOException { - - List points = Arrays.asList(new Point(100, 0), new Point(101, 0), new Point(101, 1), new Point(100, 1), - new Point(100, 0)); - GeoJsonPolygon polygon = new GeoJsonPolygon(points); - - assertThat(mapper.writeValueAsString(polygon)).isEqualTo( - "{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]]}"); - } - - @Test // GH-3517 - void shouldSerializeGeoJsonMultiPolygonCorrectly() throws IOException { - - String json = "{\"type\":\"MultiPolygon\",\"coordinates\":[" + "[" + "[" - + "[102.0,2.0],[103.0,2.0],[103.0,3.0],[102.0,3.0],[102.0,2.0]" + "]" + "]," + "[" + "[" - + "[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]" + "]" + "]," + "[" + "[" - + "[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]" + "]" + "]" + "]" + "}"; - - GeoJsonMultiPolygon multiPolygon = new GeoJsonMultiPolygon(Arrays.asList( - new GeoJsonPolygon(Arrays.asList(new Point(102, 2), new Point(103, 2), new Point(103, 3), new Point(102, 3), - new Point(102, 2))), - new GeoJsonPolygon(Arrays.asList(new Point(100, 0), new Point(101, 0), new Point(101, 1), new Point(100, 1), - new Point(100, 0))), - new GeoJsonPolygon(Arrays.asList(new Point(100.2, 0.2), new Point(100.8, 0.2), new Point(100.8, 0.8), - new Point(100.2, 0.8), new Point(100.2, 0.2))))); - - assertThat(mapper.writeValueAsString(multiPolygon)).isEqualTo(json); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java deleted file mode 100644 index fa7115c098..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Copyright 2015-2021 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.geo; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.assertj.core.data.Percentage; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.DataAccessException; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.BulkOperations.BulkMode; -import org.springframework.data.mongodb.core.CollectionCallback; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.BasicDbListBuilder; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.MongoException; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@ContextConfiguration -public class GeoJsonTests { - - static @Client MongoClient mongoClient; - - @Configuration - static class TestConfig extends AbstractMongoClientConfiguration { - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Override - public MongoClient mongoClient() { - return mongoClient; - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return new HashSet<>(Arrays.asList(Venue2DSphere.class, VenueWithDistanceField.class, OpenGeoJson.class, - DocumentWithPropertyUsingGeoJsonType.class)); - } - } - - @Autowired MongoTemplate template; - - @BeforeEach - public void setUp() { - - // template.setWriteConcern(WriteConcern.JOURNALED); - - // createIndex(); - // addVenues(); - } - - private void createIndexAndAddVenues() { - - createIndex(); - addVenues(); - } - - @AfterEach - public void tearDown() { - - dropIndex(); - removeCollections(); - } - - @Test // DATAMONGO-1135, DATAMONGO-2264 - public void geoNear() { - - createIndexAndAddVenues(); - - NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.KILOMETERS).num(10).maxDistance(150); - - GeoResults result = template.geoNear(geoNear, Venue2DSphere.class); - - assertThat(result.getContent()).isNotEmpty(); - assertThat(result.getAverageDistance().getMetric()).isEqualTo(Metrics.KILOMETERS); - assertThat(result.getAverageDistance().getValue()).isCloseTo(117.84629457941556, Percentage.withPercentage(0.001)); - } - - @Test // DATAMONGO-2264 - public void geoNearShouldNotOverridePropertyWithDefaultNameForCalculatedDistance/* namely "dis" */() { - - createIndexAndAddVenues(); - - NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.KILOMETERS).num(10).maxDistance(150); - - GeoResults result = template.geoNear(geoNear, VenueWithDistanceField.class); - - assertThat(result.getContent()).isNotEmpty(); - assertThat(result.getAverageDistance().getMetric()).isEqualTo(Metrics.KILOMETERS); - assertThat(result.getAverageDistance().getValue()).isCloseTo(117.84629457941556, Percentage.withPercentage(0.001)); - result.getContent().forEach(it -> { - - assertThat(it.getDistance().getValue()).isNotZero(); - assertThat(it.getContent().getDis()).isNull(); - }); - } - - @Test // DATAMONGO-2264 - public void geoNearShouldAllowToReadBackCalculatedDistanceIntoTargetTypeProperty/* namely "dis" */() { - - createIndexAndAddVenues(); - - NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.KILOMETERS).num(10).maxDistance(150); - - GeoResults result = template.geoNear(geoNear, Venue2DSphere.class, - template.getCollectionName(Venue2DSphere.class), VenueWithDistanceField.class); - - assertThat(result.getContent()).isNotEmpty(); - assertThat(result.getAverageDistance().getMetric()).isEqualTo(Metrics.KILOMETERS); - assertThat(result.getAverageDistance().getValue()).isCloseTo(117.84629457941556, Percentage.withPercentage(0.001)); - result.getContent().forEach(it -> { - - assertThat(it.getDistance().getValue()).isNotZero(); - assertThat(it.getContent().getDis()).isEqualTo(it.getDistance().getValue()); - }); - } - - @Test // DATAMONGO-1148 - public void geoNearShouldReturnDistanceCorrectlyUsingGeoJson/*which is using the meters*/() { - - createIndexAndAddVenues(); - NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73.99171, 40.738868), Metrics.KILOMETERS).num(10) - .maxDistance(0.4); - - GeoResults result = template.geoNear(geoNear, Venue2DSphere.class); - - assertThat(result.getContent()).hasSize(3); - assertThat(result.getAverageDistance().getMetric()).isEqualTo(Metrics.KILOMETERS); - assertThat(result.getContent().get(0).getDistance().getValue()).isCloseTo(0.0, offset(0.000001)); - assertThat(result.getContent().get(1).getDistance().getValue()).isCloseTo(0.0693582, offset(0.000001)); - assertThat(result.getContent().get(2).getDistance().getValue()).isCloseTo(0.0693582, offset(0.000001)); - } - - @Test // DATAMONGO-1348 - public void geoNearShouldReturnDistanceCorrectly/*which is using the meters*/() { - - createIndexAndAddVenues(); - NearQuery geoNear = NearQuery.near(new Point(-73.99171, 40.738868), Metrics.KILOMETERS).num(10).maxDistance(0.4); - - GeoResults result = template.geoNear(geoNear, Venue2DSphere.class); - - assertThat(result.getContent()).hasSize(3); - assertThat(result.getAverageDistance().getMetric()).isEqualTo(Metrics.KILOMETERS); - assertThat(result.getContent().get(0).getDistance().getValue()).isCloseTo(0.0, offset(0.000001)); - assertThat(result.getContent().get(1).getDistance().getValue()).isCloseTo(0.0693582, offset(0.000001)); - assertThat(result.getContent().get(2).getDistance().getValue()).isCloseTo(0.0693582, offset(0.000001)); - } - - @Test // DATAMONGO-1135 - public void geoNearWithMiles() { - - createIndexAndAddVenues(); - NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.MILES).num(10).maxDistance(93.2057); - - GeoResults result = template.geoNear(geoNear, Venue2DSphere.class); - - assertThat(result.getContent()).isNotEmpty(); - assertThat(result.getAverageDistance().getMetric()).isEqualTo(Metrics.MILES); - } - - @Test // DATAMONGO-1135 - public void withinPolygon() { - - createIndexAndAddVenues(); - - Point first = new Point(-73.99756, 40.73083); - Point second = new Point(-73.99756, 40.741404); - Point third = new Point(-73.988135, 40.741404); - Point fourth = new Point(-73.988135, 40.73083); - - GeoJsonPolygon polygon = new GeoJsonPolygon(first, second, third, fourth, first); - - List venues = template.find(query(where("location").within(polygon)), Venue2DSphere.class); - assertThat(venues).hasSize(4); - } - - @Test // DATAMONGO-1135 - public void nearPoint() { - - createIndexAndAddVenues(); - - GeoJsonPoint point = new GeoJsonPoint(-73.99171, 40.738868); - - Query query = query(where("location").near(point).maxDistance(0.01)); - List venues = template.find(query, Venue2DSphere.class); - assertThat(venues).hasSize(1); - } - - @Test // DATAMONGO-1135 - public void nearSphere() { - - createIndexAndAddVenues(); - - GeoJsonPoint point = new GeoJsonPoint(-73.99171, 40.738868); - - Query query = query(where("location").nearSphere(point).maxDistance(0.003712240453784)); - List venues = template.find(query, Venue2DSphere.class); - - assertThat(venues).hasSize(1); - } - - @Test // DATAMONGO-1137 - public void shouldSaveAndRetrieveDocumentWithGeoJsonPointTypeCorrectly() { - - DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); - obj.id = "geoJsonPoint"; - obj.geoJsonPoint = new GeoJsonPoint(100, 50); - - template.save(obj); - - DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)), - DocumentWithPropertyUsingGeoJsonType.class); - - assertThat(result.geoJsonPoint).isEqualTo(obj.geoJsonPoint); - } - - @Test // DATAMONGO-1137 - public void shouldSaveAndRetrieveDocumentWithGeoJsonPolygonTypeCorrectly() { - - DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); - obj.id = "geoJsonPolygon"; - obj.geoJsonPolygon = new GeoJsonPolygon(new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(1, 0), - new Point(0, 0)); - - template.save(obj); - - DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)), - DocumentWithPropertyUsingGeoJsonType.class); - - assertThat(result.geoJsonPolygon).isEqualTo(obj.geoJsonPolygon); - } - - @Test // DATAMONGO-1137 - public void shouldSaveAndRetrieveDocumentWithGeoJsonLineStringTypeCorrectly() { - - DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); - obj.id = "geoJsonLineString"; - obj.geoJsonLineString = new GeoJsonLineString(new Point(0, 0), new Point(0, 1), new Point(1, 1)); - - template.save(obj); - - DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)), - DocumentWithPropertyUsingGeoJsonType.class); - - assertThat(result.geoJsonLineString).isEqualTo(obj.geoJsonLineString); - } - - @Test // DATAMONGO-1137 - public void shouldSaveAndRetrieveDocumentWithGeoJsonMultiLineStringTypeCorrectly() { - - DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); - obj.id = "geoJsonMultiLineString"; - obj.geoJsonMultiLineString = new GeoJsonMultiLineString( - Arrays.asList(new GeoJsonLineString(new Point(0, 0), new Point(0, 1), new Point(1, 1)), - new GeoJsonLineString(new Point(199, 0), new Point(2, 3)))); - - template.save(obj); - - DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)), - DocumentWithPropertyUsingGeoJsonType.class); - - assertThat(result.geoJsonMultiLineString).isEqualTo(obj.geoJsonMultiLineString); - } - - @Test // DATAMONGO-1137 - public void shouldSaveAndRetrieveDocumentWithGeoJsonMultiPointTypeCorrectly() { - - DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); - obj.id = "geoJsonMultiPoint"; - obj.geoJsonMultiPoint = new GeoJsonMultiPoint(Arrays.asList(new Point(0, 0), new Point(0, 1), new Point(1, 1))); - - template.save(obj); - - DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)), - DocumentWithPropertyUsingGeoJsonType.class); - - assertThat(result.geoJsonMultiPoint).isEqualTo(obj.geoJsonMultiPoint); - } - - @Test // DATAMONGO-1137 - public void shouldSaveAndRetrieveDocumentWithGeoJsonMultiPolygonTypeCorrectly() { - - DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); - obj.id = "geoJsonMultiPolygon"; - obj.geoJsonMultiPolygon = new GeoJsonMultiPolygon( - Arrays.asList(new GeoJsonPolygon(new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(0, 0)))); - - template.save(obj); - - DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)), - DocumentWithPropertyUsingGeoJsonType.class); - - assertThat(result.geoJsonMultiPolygon).isEqualTo(obj.geoJsonMultiPolygon); - } - - @Test // DATAMONGO-1137 - public void shouldSaveAndRetrieveDocumentWithGeoJsonGeometryCollectionTypeCorrectly() { - - DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); - obj.id = "geoJsonGeometryCollection"; - obj.geoJsonGeometryCollection = new GeoJsonGeometryCollection(Arrays.> asList(new GeoJsonPoint(100, 200), - new GeoJsonPolygon(new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(1, 0), new Point(0, 0)))); - - template.save(obj); - - DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)), - DocumentWithPropertyUsingGeoJsonType.class); - - assertThat(result.geoJsonGeometryCollection).isEqualTo(obj.geoJsonGeometryCollection); - } - - @Test // DATAMONGO-1110 - public void nearWithMinDistance() { - - createIndexAndAddVenues(); - - Point point = new GeoJsonPoint(-73.99171, 40.738868); - List venues = template.find(query(where("location").near(point).minDistance(0.01)), - Venue2DSphere.class); - - assertThat(venues).hasSize(11); - } - - @Test // DATAMONGO-1110 - public void nearSphereWithMinDistance() { - - createIndexAndAddVenues(); - - Point point = new GeoJsonPoint(-73.99171, 40.738868); - List venues = template.find(query(where("location").nearSphere(point).minDistance(0.01)), - Venue2DSphere.class); - - assertThat(venues).hasSize(11); - } - - @Test // DATAMONGO-1135 - public void nearWithMinAndMaxDistance() { - - createIndexAndAddVenues(); - - GeoJsonPoint point = new GeoJsonPoint(-73.99171, 40.738868); - - Query query = query(where("location").near(point).minDistance(0.01).maxDistance(100)); - List venues = template.find(query, Venue2DSphere.class); - assertThat(venues).hasSize(2); - } - - @Test // DATAMONGO-1453 - public void shouldConvertPointRepresentationCorrectlyWhenSourceCoordinatesUsesInteger() { - - this.template.execute(template.getCollectionName(DocumentWithPropertyUsingGeoJsonType.class), - new CollectionCallback() { - - @Override - public Object doInCollection(MongoCollection collection) - throws MongoException, DataAccessException { - - org.bson.Document pointRepresentation = new org.bson.Document(); - pointRepresentation.put("type", "Point"); - pointRepresentation.put("coordinates", new BasicDbListBuilder().add(0).add(0).get()); - - org.bson.Document document = new org.bson.Document(); - document.append("_id", "datamongo-1453"); - document.append("geoJsonPoint", pointRepresentation); - - collection.insertOne(document); - - return document; - } - }); - - assertThat(template.findOne(query(where("id").is("datamongo-1453")), - DocumentWithPropertyUsingGeoJsonType.class).geoJsonPoint).isEqualTo(new GeoJsonPoint(0D, 0D)); - } - - @Test // DATAMONGO-1453 - public void shouldConvertLineStringRepresentationCorrectlyWhenSourceCoordinatesUsesInteger() { - - this.template.execute(template.getCollectionName(DocumentWithPropertyUsingGeoJsonType.class), - new CollectionCallback() { - - @Override - public Object doInCollection(MongoCollection collection) - throws MongoException, DataAccessException { - - org.bson.Document lineStringRepresentation = new org.bson.Document(); - lineStringRepresentation.put("type", "LineString"); - lineStringRepresentation.put("coordinates", - new BasicDbListBuilder().add(new BasicDbListBuilder().add(0).add(0).get()) - .add(new BasicDbListBuilder().add(1).add(1).get()).get()); - - org.bson.Document document = new org.bson.Document(); - document.append("_id", "datamongo-1453"); - document.append("geoJsonLineString", lineStringRepresentation); - - collection.insertOne(document); - - return document; - } - }); - - assertThat(template.findOne(query(where("id").is("datamongo-1453")), - DocumentWithPropertyUsingGeoJsonType.class).geoJsonLineString) - .isEqualTo(new GeoJsonLineString(new Point(0D, 0D), new Point(1, 1))); - } - - @Test // DATAMONGO-1466 - public void readGeoJsonBasedOnEmbeddedTypeInformation() { - - Point first = new Point(-73.99756, 40.73083); - Point second = new Point(-73.99756, 40.741404); - Point third = new Point(-73.988135, 40.741404); - Point fourth = new Point(-73.988135, 40.73083); - - GeoJsonPolygon polygon = new GeoJsonPolygon(first, second, third, fourth, first); - - ConcreteGeoJson source = new ConcreteGeoJson(); - source.shape = polygon; - source.id = "id-1"; - - template.save(source); - - OpenGeoJson target = template.findOne(query(where("id").is(source.id)), OpenGeoJson.class); - - assertThat(target.shape).isEqualTo(source.shape); - } - - private void addVenues() { - - List venues = new ArrayList<>(); - - venues.add(new Venue2DSphere("Penn Station", -73.99408, 40.75057)); - venues.add(new Venue2DSphere("10gen Office", -73.99171, 40.738868)); - venues.add(new Venue2DSphere("Flatiron Building", -73.988135, 40.741404)); - venues.add(new Venue2DSphere("Players Club", -73.997812, 40.739128)); - venues.add(new Venue2DSphere("City Bakery ", -73.992491, 40.738673)); - venues.add(new Venue2DSphere("Splash Bar", -73.992491, 40.738673)); - venues.add(new Venue2DSphere("Momofuku Milk Bar", -73.985839, 40.731698)); - venues.add(new Venue2DSphere("Shake Shack", -73.98820, 40.74164)); - venues.add(new Venue2DSphere("Penn Station", -73.99408, 40.75057)); - venues.add(new Venue2DSphere("Empire State Building", -73.98602, 40.74894)); - venues.add(new Venue2DSphere("Ulaanbaatar, Mongolia", 106.9154, 47.9245)); - venues.add(new Venue2DSphere("Maplewood, NJ", -74.2713, 40.73137)); - - template.bulkOps(BulkMode.UNORDERED, Venue2DSphere.class).insert(venues).execute(); - } - - protected void createIndex() { - dropIndex(); - template.indexOps(Venue2DSphere.class) - .ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE)); - } - - protected void dropIndex() { - try { - template.indexOps(Venue2DSphere.class).dropIndex("location"); - } catch (Exception e) { - - } - } - - protected void removeCollections() { - template.dropCollection(Venue2DSphere.class); - template.dropCollection(DocumentWithPropertyUsingGeoJsonType.class); - } - - @org.springframework.data.mongodb.core.mapping.Document(collection = "venue2dsphere") - static class Venue2DSphere { - - @Id private String id; - private String name; - private @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) double[] location; - - @PersistenceConstructor - public Venue2DSphere(String name, double[] location) { - this.name = name; - this.location = location; - } - - public Venue2DSphere(String name, double x, double y) { - this.name = name; - this.location = new double[] { x, y }; - } - - public String getName() { - return name; - } - - public double[] getLocation() { - return location; - } - - @Override - public String toString() { - return "Venue2DSphere [id=" + id + ", name=" + name + ", location=" + Arrays.toString(location) + "]"; - } - } - - static class VenueWithDistanceField extends Venue2DSphere { - - private Double dis; // geoNear command default distance field name - - public VenueWithDistanceField(String name, double[] location) { - super(name, location); - } - - public Double getDis() { - return dis; - } - - public void setDis(Double dis) { - this.dis = dis; - } - } - - static class DocumentWithPropertyUsingGeoJsonType { - - String id; - GeoJsonPoint geoJsonPoint; - GeoJsonPolygon geoJsonPolygon; - GeoJsonLineString geoJsonLineString; - GeoJsonMultiLineString geoJsonMultiLineString; - GeoJsonMultiPoint geoJsonMultiPoint; - GeoJsonMultiPolygon geoJsonMultiPolygon; - GeoJsonGeometryCollection geoJsonGeometryCollection; - } - - @Data - @Document("geo-json-shapes") - static class ConcreteGeoJson { - String id; - GeoJsonPolygon shape; - } - - @Data - @Document("geo-json-shapes") - static class OpenGeoJson { - String id; - GeoJson shape; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java deleted file mode 100644 index fe226ab9f3..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2010-2021 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.geo; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import java.util.List; - -import org.junit.Test; - -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Metric; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.Venue; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.index.IndexField; -import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.data.mongodb.core.index.IndexOperations; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; - -/** - * @author Christoph Strobl - */ -public class GeoSpatial2DSphereTests extends AbstractGeoSpatialTests { - - @Test // DATAMONGO-360 - public void indexInfoIsCorrect() { - - IndexOperations operations = template.indexOps(Venue.class); - List indexInfo = operations.getIndexInfo(); - - assertThat(indexInfo.size()).isEqualTo(2); - - List fields = indexInfo.get(0).getIndexFields(); - assertThat(fields.size()).isEqualTo(1); - assertThat(fields).contains(IndexField.create("_id", Direction.ASC)); - - fields = indexInfo.get(1).getIndexFields(); - assertThat(fields.size()).isEqualTo(1); - assertThat(fields).contains(IndexField.geo("location")); - } - - @Test // DATAMONGO-1110 - public void geoNearWithMinDistance() { - - NearQuery geoNear = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).minDistance(1); - - GeoResults result = template.geoNear(geoNear, Venue.class); - - assertThat(result.getContent().size()).isNotEqualTo(0); - assertThat(result.getAverageDistance().getMetric()).isEqualTo((Metric) Metrics.KILOMETERS); - } - - @Test // DATAMONGO-1110 - public void nearSphereWithMinDistance() { - - Point point = new Point(-73.99171, 40.738868); - Query query = query(where("location").nearSphere(point).minDistance(0.01)); - - List venues = template.find(query, Venue.class); - assertThat(venues.size()).isEqualTo(1); - } - - @Test - public void countNearSphereWithMinDistance() { - - Point point = new Point(-73.99171, 40.738868); - Query query = query(where("location").nearSphere(point).minDistance(0.01)); - - assertThat(template.count(query, Venue.class)).isEqualTo(1); - } - - @Override - protected void createIndex() { - template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE)); - } - - @Override - protected void dropIndex() { - template.indexOps(Venue.class).dropIndex("location_2dsphere"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java deleted file mode 100644 index bec2f3d7a9..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2010-2021 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.geo; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import java.util.List; - -import org.junit.Test; - -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.Venue; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.index.IndexField; -import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.data.mongodb.core.index.IndexOperations; -import org.springframework.data.mongodb.core.query.Query; - -/** - * Modified from https://github.com/deftlabs/mongo-java-geospatial-example - * - * @author Mark Pollack - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - */ -public class GeoSpatial2DTests extends AbstractGeoSpatialTests { - - @Test - public void nearPoint() { - - Point point = new Point(-73.99171, 40.738868); - Query query = query(where("location").near(point).maxDistance(0.01)); - - List venues = template.find(query, Venue.class); - assertThat(venues.size()).isEqualTo(7); - assertThat(template.count(query, Venue.class)).isEqualTo(7); - } - - @Test // DATAMONGO-360 - public void indexInfoIsCorrect() { - - IndexOperations operations = template.indexOps(Venue.class); - List indexInfo = operations.getIndexInfo(); - - assertThat(indexInfo.size()).isEqualTo(2); - - List fields = indexInfo.get(0).getIndexFields(); - assertThat(fields.size()).isEqualTo(1); - assertThat(fields).contains(IndexField.create("_id", Direction.ASC)); - - fields = indexInfo.get(1).getIndexFields(); - assertThat(fields.size()).isEqualTo(1); - assertThat(fields).contains(IndexField.geo("location")); - } - - @Override - protected void createIndex() { - template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2D)); - } - - @Override - protected void dropIndex() { - template.indexOps(Venue.class).dropIndex("location_2d"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialIndexTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialIndexTests.java deleted file mode 100644 index 449c78f225..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialIndexTests.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2013-2021 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.geo; - -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataAccessException; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.config.AbstractIntegrationTests; -import org.springframework.data.mongodb.core.CollectionCallback; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.WriteResultChecking; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; -import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.data.mongodb.core.index.IndexOperations; -import org.springframework.data.mongodb.core.mapping.Document; - -import com.mongodb.MongoException; -import com.mongodb.WriteConcern; -import com.mongodb.client.MongoCollection; - -/** - * Integration tests for geo-spatial indexing. - * - * @author Laurent Canet - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - */ -public class GeoSpatialIndexTests extends AbstractIntegrationTests { - - @Autowired private MongoTemplate template; - - @Before - public void setUp() { - - template.setWriteConcern(WriteConcern.JOURNALED); - template.setWriteResultChecking(WriteResultChecking.EXCEPTION); - } - - @Test // DATAMONGO-778 - public void test2dIndex() { - - try { - template.save(new GeoSpatialEntity2D(45.2, 4.6)); - assertThat(hasIndexOfType(GeoSpatialEntity2D.class, "2d")).isTrue(); - } finally { - template.dropCollection(GeoSpatialEntity2D.class); - } - } - - @Test // DATAMONGO-778 - public void test2dSphereIndex() { - - try { - template.save(new GeoSpatialEntity2DSphere(45.2, 4.6)); - assertThat(hasIndexOfType(GeoSpatialEntity2DSphere.class, "2dsphere")).isTrue(); - } finally { - template.dropCollection(GeoSpatialEntity2DSphere.class); - } - } - - @Test // DATAMONGO-778 - public void testHaystackIndex() { - - try { - template.save(new GeoSpatialEntityHaystack(45.2, 4.6, "Paris")); - assertThat(hasIndexOfType(GeoSpatialEntityHaystack.class, "geoHaystack")).isTrue(); - } finally { - template.dropCollection(GeoSpatialEntityHaystack.class); - } - } - - @Test // DATAMONGO-827 - public void useGeneratedNameShouldGenerateAnIndexName() { - - try { - - GeoSpatialEntity2dWithGeneratedIndex geo = new GeoSpatialEntity2dWithGeneratedIndex(45.2, 4.6); - template.save(geo); - - IndexOperations indexOps = template.indexOps(GeoSpatialEntity2dWithGeneratedIndex.class); - List indexInfo = indexOps.getIndexInfo(); - - assertThat(indexInfo).hasSize(2); - assertThat(indexInfo.get(1)).isNotNull(); - assertThat(indexInfo.get(1).getName()).isEqualTo("location_2d"); - - } finally { - template.dropCollection(GeoSpatialEntity2D.class); - } - } - - /** - * Returns whether an index with the given name exists for the given entity type. - * - * @param indexName - * @param entityType - * @return - */ - private boolean hasIndexOfType(Class entityType, final String type) { - - return template.execute(entityType, new CollectionCallback() { - - @SuppressWarnings("unchecked") - public Boolean doInCollection(MongoCollection collection) - throws MongoException, DataAccessException { - - List indexes = new ArrayList(); - collection.listIndexes(org.bson.Document.class).into(indexes); - - for (org.bson.Document indexInfo : indexes) { - - org.bson.Document keys = (org.bson.Document) indexInfo.get("key"); - Map keysMap = keys; - - for (String key : keysMap.keySet()) { - Object indexType = keys.get(key); - if (type.equals(indexType)) { - return true; - } - } - } - - return false; - } - }); - } - - @Document - static class GeoSpatialEntity2D { - public String id; - @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2D) public org.springframework.data.geo.Point location; - - public GeoSpatialEntity2D(double x, double y) { - this.location = new org.springframework.data.geo.Point(x, y); - } - } - - @Document - static class GeoSpatialEntityHaystack { - public String id; - public String name; - @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_HAYSTACK, additionalField = "name") public Point location; - - public GeoSpatialEntityHaystack(double x, double y, String name) { - this.location = new Point(x, y); - this.name = name; - } - } - - static class GeoJsonPoint { - String type = "Point"; - double coordinates[]; - } - - @Document - static class GeoSpatialEntity2DSphere { - public String id; - @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) public GeoJsonPoint location; - - public GeoSpatialEntity2DSphere(double x, double y) { - this.location = new GeoJsonPoint(); - this.location.coordinates = new double[] { x, y }; - } - } - - @Document - static class GeoSpatialEntity2dWithGeneratedIndex { - - public String id; - @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2D, useGeneratedName = true) public Point location; - - public GeoSpatialEntity2dWithGeneratedIndex(double x, double y) { - this.location = new Point(x, y); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexFieldUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexFieldUnitTests.java deleted file mode 100644 index 81f12a62dc..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexFieldUnitTests.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2012-2021 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.index; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -import org.springframework.data.domain.Sort.Direction; - -/** - * Unit tests for {@link IndexField}. - * - * @author Oliver Gierke - * @author Christoph Strobl - */ -public class IndexFieldUnitTests { - - @Test - public void createsPlainIndexFieldCorrectly() { - - IndexField field = IndexField.create("foo", Direction.ASC); - - assertThat(field.getKey()).isEqualTo("foo"); - assertThat(field.getDirection()).isEqualTo(Direction.ASC); - assertThat(field.isGeo()).isFalse(); - } - - @Test - public void createsGeoIndexFieldCorrectly() { - - IndexField field = IndexField.geo("foo"); - - assertThat(field.getKey()).isEqualTo("foo"); - assertThat(field.getDirection()).isNull(); - assertThat(field.isGeo()).isTrue(); - } - - @Test - public void correctEqualsForPlainFields() { - - IndexField first = IndexField.create("foo", Direction.ASC); - IndexField second = IndexField.create("foo", Direction.ASC); - - assertThat(first).isEqualTo(second); - assertThat(second).isEqualTo(first); - } - - @Test - public void correctEqualsForGeoFields() { - - IndexField first = IndexField.geo("bar"); - IndexField second = IndexField.geo("bar"); - - assertThat(first).isEqualTo(second); - assertThat(second).isEqualTo(first); - } - - @Test // DATAMONGO-1183 - public void correctTypeForHashedFields() { - assertThat(IndexField.hashed("key").isHashed()).isTrue(); - } - - @Test // DATAMONGO-1183 - public void correctEqualsForHashedFields() { - - IndexField first = IndexField.hashed("bar"); - IndexField second = IndexField.hashed("bar"); - - assertThat(first).isEqualTo(second); - assertThat(second).isEqualTo(first); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java deleted file mode 100644 index 2026dfc644..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2012-2021 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.index; - -import static org.assertj.core.api.Assertions.*; - -import java.time.Duration; -import java.util.Arrays; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.domain.Sort.Direction; - -/** - * Unit tests for {@link IndexInfo}. - * - * @author Oliver Gierke - * @author Christoph Strobl - */ -public class IndexInfoUnitTests { - - static final String ID_INDEX = "{ \"v\" : 2, \"key\" : { \"_id\" : 1 }, \"name\" : \"_id_\", \"ns\" : \"db.collection\" }"; - static final String INDEX_WITH_PARTIAL_FILTER = "{ \"v\" : 2, \"key\" : { \"k3y\" : 1 }, \"name\" : \"partial-filter-index\", \"ns\" : \"db.collection\", \"partialFilterExpression\" : { \"quantity\" : { \"$gte\" : 10 } } }"; - static final String INDEX_WITH_EXPIRATION_TIME = "{ \"v\" : 2, \"key\" : { \"lastModifiedDate\" : 1 },\"name\" : \"expire-after-last-modified\", \"ns\" : \"db.collectio\", \"expireAfterSeconds\" : 3600 }"; - static final String HASHED_INDEX = "{ \"v\" : 2, \"key\" : { \"score\" : \"hashed\" }, \"name\" : \"score_hashed\", \"ns\" : \"db.collection\" }"; - - @Test - public void isIndexForFieldsCorrectly() { - - IndexField fooField = IndexField.create("foo", Direction.ASC); - IndexField barField = IndexField.create("bar", Direction.DESC); - - IndexInfo info = new IndexInfo(Arrays.asList(fooField, barField), "myIndex", false, false, ""); - assertThat(info.isIndexForFields(Arrays.asList("foo", "bar"))).isTrue(); - } - - @Test // DATAMONGO-2170 - public void partialFilterExpressionShouldBeNullIfNotSetInSource() { - assertThat(getIndexInfo(ID_INDEX).getPartialFilterExpression()).isNull(); - } - - @Test // DATAMONGO-2170 - public void partialFilterExpressionShouldMatchSource() { - - assertThat(Document.parse(getIndexInfo(INDEX_WITH_PARTIAL_FILTER).getPartialFilterExpression())) - .isEqualTo(Document.parse("{ \"quantity\" : { \"$gte\" : 10 } }")); - } - - @Test // DATAMONGO-2081 - public void expireAfterIsParsedCorrectly() { - assertThat(getIndexInfo(INDEX_WITH_EXPIRATION_TIME).getExpireAfter()).contains(Duration.ofHours(1)); - } - - @Test // DATAMONGO-2081 - public void expireAfterIsEmptyIfNotSet() { - assertThat(getIndexInfo(ID_INDEX).getExpireAfter()).isEmpty(); - } - - @Test // DATAMONGO-1183 - public void readsHashedIndexCorrectly() { - assertThat(getIndexInfo(HASHED_INDEX).getIndexFields()).containsExactly(IndexField.hashed("score")); - } - - @Test // DATAMONGO-1183 - public void hashedIndexIsMarkedAsSuch() { - assertThat(getIndexInfo(HASHED_INDEX).isHashed()).isTrue(); - } - - private static IndexInfo getIndexInfo(String documentJson) { - return IndexInfo.indexInfoOf(Document.parse(documentJson)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java deleted file mode 100644 index 2c61b0fdbf..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2011-2021 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.index; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.MongoCollectionUtils; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.client.MongoClient; - -/** - * Integration tests for index handling. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Jordi Llach - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@ContextConfiguration -public class IndexingIntegrationTests { - - static @Client MongoClient mongoClient; - - @Autowired MongoOperations operations; - @Autowired MongoDatabaseFactory mongoDbFactory; - @Autowired ConfigurableApplicationContext context; - - @Configuration - static class Config extends AbstractMongoClientConfiguration { - - @Override - public MongoClient mongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Bean - TimeoutResolver myTimeoutResolver() { - return new TimeoutResolver("11s"); - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - - @Override - protected boolean autoIndexCreation() { - return true; - } - } - - @AfterEach - public void tearDown() { - operations.dropCollection(IndexedPerson.class); - } - - @Test // DATAMONGO-237 - @DirtiesContext - public void createsIndexWithFieldName() { - - operations.getConverter().getMappingContext().getPersistentEntity(IndexedPerson.class); - - assertThat(hasIndex("_firstname", IndexedPerson.class)).isTrue(); - } - - @Test // DATAMONGO-2188 - @DirtiesContext - public void shouldNotCreateIndexOnIndexingDisabled() { - - MongoMappingContext context = new MongoMappingContext(); - context.setAutoIndexCreation(false); - - MongoTemplate template = new MongoTemplate(mongoDbFactory, - new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, context)); - - template.getConverter().getMappingContext().getPersistentEntity(IndexedPerson.class); - - assertThat(hasIndex("_firstname", MongoCollectionUtils.getPreferredCollectionName(IndexedPerson.class))).isFalse(); - } - - @Test // DATAMONGO-1163 - @DirtiesContext - public void createsIndexFromMetaAnnotation() { - - operations.getConverter().getMappingContext().getPersistentEntity(IndexedPerson.class); - - assertThat(hasIndex("_lastname", IndexedPerson.class)).isTrue(); - } - - @Test // DATAMONGO-2112 - @DirtiesContext - public void evaluatesTimeoutSpelExpresssionWithBeanReference() { - - operations.getConverter().getMappingContext().getPersistentEntity(WithSpelIndexTimeout.class); - - Optional indexInfo = operations.execute("withSpelIndexTimeout", collection -> { - - return collection.listIndexes(org.bson.Document.class).into(new ArrayList<>()) // - .stream() // - .filter(it -> it.get("name").equals("someString")) // - .findFirst(); - }); - - assertThat(indexInfo).isPresent(); - assertThat(indexInfo.get()).containsEntry("expireAfterSeconds", 11L); - } - - @Target({ ElementType.FIELD }) - @Retention(RetentionPolicy.RUNTIME) - @Indexed - @interface IndexedFieldAnnotation { - } - - @Document - class IndexedPerson { - - @Field("_firstname") @Indexed String firstname; - @Field("_lastname") @IndexedFieldAnnotation String lastname; - } - - @RequiredArgsConstructor - @Getter - static class TimeoutResolver { - final String timeout; - } - - @Document - class WithSpelIndexTimeout { - @Indexed(expireAfter = "#{@myTimeoutResolver?.timeout}") String someString; - } - - /** - * Returns whether an index with the given name exists for the given entity type. - * - * @param indexName - * @param entityType - * @return - */ - private boolean hasIndex(String indexName, Class entityType) { - return hasIndex(indexName, operations.getCollectionName(entityType)); - } - - /** - * Returns whether an index with the given name exists for the given collection. - * - * @param indexName - * @param collectionName - * @return - */ - private boolean hasIndex(String indexName, String collectionName) { - - return operations.execute(collectionName, collection -> { - - List indexes = new ArrayList<>(); - collection.listIndexes(org.bson.Document.class).into(indexes); - - for (org.bson.Document indexInfo : indexes) { - if (indexName.equals(indexInfo.get("name"))) { - return true; - } - } - return false; - }); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests.java deleted file mode 100644 index a6c5eb9e6a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2012-2021 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.index; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.List; - -import org.hamcrest.core.IsInstanceOf; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.RuleChain; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.test.util.CleanMongoDB; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.data.mongodb.test.util.MongoVersionRule; -import org.springframework.data.util.Version; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.MongoCommandException; -import com.mongodb.client.MongoClient; - -/** - * Integration tests for {@link MongoPersistentEntityIndexCreator}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Thomas Darimont - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class MongoPersistentEntityIndexCreatorIntegrationTests { - - static final String SAMPLE_TYPE_COLLECTION_NAME = "sampleEntity"; - static final String RECURSIVE_TYPE_COLLECTION_NAME = "recursiveGenericTypes"; - - public static @ClassRule RuleChain rules = RuleChain.outerRule(MongoVersionRule.atLeast(new Version(2, 6))) - .around(CleanMongoDB.indexes(Arrays.asList(SAMPLE_TYPE_COLLECTION_NAME, RECURSIVE_TYPE_COLLECTION_NAME))); - - public @Rule ExpectedException expectedException = ExpectedException.none(); - - @Autowired @Qualifier("mongo1") MongoOperations templateOne; - - @Autowired @Qualifier("mongo2") MongoOperations templateTwo; - - @Test - public void createsIndexForConfiguredMappingContextOnly() { - - List indexInfo = templateOne.indexOps(SampleEntity.class).getIndexInfo(); - assertThat(indexInfo).isNotEmpty(); - assertThat(indexInfo).extracting(IndexInfo::getName).contains("prop"); - - indexInfo = templateTwo.indexOps(SAMPLE_TYPE_COLLECTION_NAME).getIndexInfo(); - assertThat(indexInfo).hasSize(0); - } - - @Test // DATAMONGO-1202 - public void shouldHonorIndexedPropertiesWithRecursiveMappings() { - - List indexInfo = templateOne.indexOps(RecursiveConcreteType.class).getIndexInfo(); - - assertThat(indexInfo).isNotEmpty(); - assertThat(indexInfo).extracting(IndexInfo::getName).contains("firstName"); - } - - @Test // DATAMONGO-1125 - public void createIndexShouldThrowMeaningfulExceptionWhenIndexCreationFails() { - - expectedException.expect(DataIntegrityViolationException.class); - expectedException.expectMessage("collection 'datamongo-1125'"); - expectedException.expectMessage("dalinar.kohlin"); - expectedException.expectMessage("lastname"); - expectedException.expectCause(IsInstanceOf. instanceOf(MongoCommandException.class)); - - try (MongoClient client = MongoTestUtils.client()) { - MongoTemplate mongoTemplate = new MongoTemplate(client, "issue"); - - MongoPersistentEntityIndexCreator indexCreator = new MongoPersistentEntityIndexCreator(new MongoMappingContext(), - mongoTemplate); - - indexCreator.createIndex(new IndexDefinitionHolder("dalinar.kohlin", - new Index().named("stormlight").on("lastname", Direction.ASC).unique(), "datamongo-1125")); - - indexCreator.createIndex(new IndexDefinitionHolder("dalinar.kohlin", - new Index().named("stormlight").on("lastname", Direction.ASC).sparse(), "datamongo-1125")); - } - } - - @Document(RECURSIVE_TYPE_COLLECTION_NAME) - static abstract class RecursiveGenericType> { - - @Id Long id; - - @org.springframework.data.mongodb.core.mapping.DBRef RGT referrer; - - @Indexed String firstName; - - public RecursiveGenericType(Long id, String firstName, RGT referrer) { - this.firstName = firstName; - this.id = id; - this.referrer = referrer; - } - } - - static class RecursiveConcreteType extends RecursiveGenericType { - - public RecursiveConcreteType(Long id, String firstName, RecursiveConcreteType referrer) { - super(id, firstName, referrer); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorUnitTests.java deleted file mode 100644 index 3033ad2e4d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorUnitTests.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright 2012-2021 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.index; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Collections; -import java.util.Date; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.dao.DataAccessException; -import org.springframework.data.geo.Point; -import org.springframework.data.mapping.context.MappingContextEvent; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoExceptionTranslator; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; - -import com.mongodb.MongoException; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.model.IndexOptions; - -/** - * Unit tests for {@link MongoPersistentEntityIndexCreator}. - * - * @author Oliver Gierke - * @author Philipp Schneider - * @author Johno Crawford - * @author Christoph Strobl - * @author Thomas Darimont - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -public class MongoPersistentEntityIndexCreatorUnitTests { - - private @Mock MongoDatabaseFactory factory; - private @Mock MongoDatabase db; - private @Mock MongoCollection collection; - private MongoTemplate mongoTemplate; - - private ArgumentCaptor keysCaptor; - private ArgumentCaptor optionsCaptor; - private ArgumentCaptor collectionCaptor; - - @BeforeEach - void setUp() { - - keysCaptor = ArgumentCaptor.forClass(org.bson.Document.class); - optionsCaptor = ArgumentCaptor.forClass(IndexOptions.class); - collectionCaptor = ArgumentCaptor.forClass(String.class); - - when(factory.getMongoDatabase()).thenReturn(db); - when(factory.getExceptionTranslator()).thenReturn(new MongoExceptionTranslator()); - when(db.getCollection(collectionCaptor.capture(), eq(org.bson.Document.class))) - .thenReturn((MongoCollection) collection); - - mongoTemplate = new MongoTemplate(factory); - - when(collection.createIndex(keysCaptor.capture(), optionsCaptor.capture())).thenReturn("OK"); - } - - @Test - void buildsIndexDefinitionUsingFieldName() { - - MongoMappingContext mappingContext = prepareMappingContext(Person.class); - - new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate); - - assertThat(keysCaptor.getValue()).isNotNull().containsKey("fieldname"); - assertThat(optionsCaptor.getValue().getName()).isEqualTo("indexName"); - assertThat(optionsCaptor.getValue().isBackground()).isFalse(); - assertThat(optionsCaptor.getValue().getExpireAfter(TimeUnit.SECONDS)).isNull(); - } - - @Test - void doesNotCreateIndexForEntityComingFromDifferentMappingContext() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - MongoMappingContext personMappingContext = prepareMappingContext(Person.class); - - MongoPersistentEntityIndexCreator creator = new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate); - - MongoPersistentEntity entity = personMappingContext.getRequiredPersistentEntity(Person.class); - MappingContextEvent, MongoPersistentProperty> event = new MappingContextEvent, MongoPersistentProperty>( - personMappingContext, entity); - - creator.onApplicationEvent(event); - - verifyZeroInteractions(collection); - } - - @Test // DATAMONGO-530 - void isIndexCreatorForMappingContextHandedIntoConstructor() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.initialize(); - - MongoPersistentEntityIndexCreator creator = new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate); - assertThat(creator.isIndexCreatorFor(mappingContext)).isTrue(); - assertThat(creator.isIndexCreatorFor(new MongoMappingContext())).isFalse(); - } - - @Test // DATAMONGO-554 - void triggersBackgroundIndexingIfConfigured() { - - MongoMappingContext mappingContext = prepareMappingContext(AnotherPerson.class); - new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate); - - assertThat(keysCaptor.getValue()).isNotNull().containsKey("lastname"); - assertThat(optionsCaptor.getValue().getName()).isEqualTo("lastname"); - assertThat(optionsCaptor.getValue().isBackground()).isTrue(); - assertThat(optionsCaptor.getValue().getExpireAfter(TimeUnit.SECONDS)).isNull(); - } - - @Test // DATAMONGO-544 - void expireAfterSecondsIfConfigured() { - - MongoMappingContext mappingContext = prepareMappingContext(Milk.class); - new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate); - - assertThat(keysCaptor.getValue()).isNotNull().containsKey("expiry"); - assertThat(optionsCaptor.getValue().getExpireAfter(TimeUnit.SECONDS)).isEqualTo(60); - } - - @Test // DATAMONGO-899 - void createsNotNestedGeoSpatialIndexCorrectly() { - - MongoMappingContext mappingContext = prepareMappingContext(Wrapper.class); - new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate); - - assertThat(keysCaptor.getValue()).isEqualTo(new org.bson.Document("company.address.location", "2d")); - - IndexOptions opts = optionsCaptor.getValue(); - assertThat(opts.getName()).isEqualTo("company.address.location"); - assertThat(opts.getMin()).isCloseTo(-180d, offset(0d)); - assertThat(opts.getMax()).isCloseTo(180d, offset(0d)); - assertThat(opts.getBits()).isEqualTo(26); - } - - @Test // DATAMONGO-827 - void autoGeneratedIndexNameShouldGenerateNoName() { - - MongoMappingContext mappingContext = prepareMappingContext(EntityWithGeneratedIndexName.class); - new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate); - - assertThat(keysCaptor.getValue()).doesNotContainKey("name").containsKey("lastname"); - assertThat(optionsCaptor.getValue().getName()).isNull(); - } - - @Test // DATAMONGO-367 - void indexCreationShouldNotCreateNewCollectionForNestedGeoSpatialIndexStructures() { - - MongoMappingContext mappingContext = prepareMappingContext(Wrapper.class); - new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate); - - ArgumentCaptor collectionNameCapturer = ArgumentCaptor.forClass(String.class); - - verify(db, times(1)).getCollection(collectionNameCapturer.capture(), any()); - assertThat(collectionNameCapturer.getValue()).isEqualTo("wrapper"); - } - - @Test // DATAMONGO-367 - void indexCreationShouldNotCreateNewCollectionForNestedIndexStructures() { - - MongoMappingContext mappingContext = prepareMappingContext(IndexedDocumentWrapper.class); - new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate); - - ArgumentCaptor collectionNameCapturer = ArgumentCaptor.forClass(String.class); - - verify(db, times(1)).getCollection(collectionNameCapturer.capture(), any()); - assertThat(collectionNameCapturer.getValue()).isEqualTo("indexedDocumentWrapper"); - } - - @Test // DATAMONGO-1125 - void createIndexShouldUsePersistenceExceptionTranslatorForNonDataIntegrityConcerns() { - - doThrow(new MongoException(6, "HostUnreachable")).when(collection).createIndex(any(org.bson.Document.class), - any(IndexOptions.class)); - - MongoMappingContext mappingContext = prepareMappingContext(Person.class); - - assertThatThrownBy(() -> new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate)) - .isInstanceOf(DataAccessException.class); - } - - @Test // DATAMONGO-1125 - void createIndexShouldNotConvertUnknownExceptionTypes() { - - doThrow(new ClassCastException("o_O")).when(collection).createIndex(any(org.bson.Document.class), - any(IndexOptions.class)); - - MongoMappingContext mappingContext = prepareMappingContext(Person.class); - - assertThatThrownBy(() -> new MongoPersistentEntityIndexCreator(mappingContext, mongoTemplate)) - .isInstanceOf(ClassCastException.class); - } - - private static MongoMappingContext prepareMappingContext(Class type) { - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setInitialEntitySet(Collections.singleton(type)); - mappingContext.initialize(); - - return mappingContext; - } - - @Document - static class Person { - - @Indexed(name = "indexName") // - @Field("fieldname") // - String field; - - } - - @Document - static class AnotherPerson { - - @Indexed(background = true) String lastname; - } - - @Document - static class Milk { - - @Indexed(expireAfterSeconds = 60) Date expiry; - } - - @Document - static class Wrapper { - - String id; - Company company; - - } - - static class Company { - - String name; - Address address; - } - - static class Address { - - String street; - String city; - - @GeoSpatialIndexed Point location; - } - - @Document - static class IndexedDocumentWrapper { - - IndexedDocument indexedDocument; - } - - static class IndexedDocument { - - @Indexed String indexedValue; - } - - @Document - class EntityWithGeneratedIndexName { - - @Indexed(useGeneratedName = true, name = "ignored") String lastname; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java deleted file mode 100644 index 489070548d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java +++ /dev/null @@ -1,1630 +0,0 @@ -/* - * Copyright 2014-2021 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.index; - -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.List; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; -import org.springframework.core.annotation.AliasFor; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.annotation.Id; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder; -import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.CompoundIndexResolutionTests; -import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.GeoSpatialIndexResolutionTests; -import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.IndexResolutionTests; -import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.MixedIndexResolutionTests; -import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.TextIndexedResolutionTests; -import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.Language; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.data.util.ClassTypeInformation; - -/** - * Tests for {@link MongoPersistentEntityIndexResolver}. - * - * @author Christoph Strobl - * @author Mark Paluch - * @author Dave Perryman - */ -@RunWith(Suite.class) -@SuiteClasses({ IndexResolutionTests.class, GeoSpatialIndexResolutionTests.class, CompoundIndexResolutionTests.class, - TextIndexedResolutionTests.class, MixedIndexResolutionTests.class }) -@SuppressWarnings("unused") -public class MongoPersistentEntityIndexResolverUnitTests { - - /** - * Test resolution of {@link Indexed}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ - public static class IndexResolutionTests { - - @Test // DATAMONGO-899 - public void indexPathOnLevelZeroIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - IndexOnLevelZero.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection("indexedProperty", "Zero", indexDefinitions.get(0)); - } - - @Test // DATAMONGO-899 - public void indexPathOnLevelOneIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexOnLevelOne.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection("zero.indexedProperty", "One", indexDefinitions.get(0)); - } - - @Test // DATAMONGO-899, DATAMONGO-2188 - public void shouldResolveIndexViaClass() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - IndexResolver indexResolver = IndexResolver.create(mappingContext); - Iterable definitions = indexResolver.resolveIndexFor(IndexOnLevelOne.class); - - assertThat(definitions).isNotEmpty(); - } - - @Test // DATAMONGO-899 - public void deeplyNestedIndexPathIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexOnLevelTwo.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection("one.zero.indexedProperty", "Two", indexDefinitions.get(0)); - } - - @Test // DATAMONGO-899 - public void resolvesIndexPathNameForNamedPropertiesCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - IndexOnLevelOneWithExplicitlyNamedField.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection("customZero.customFieldName", "indexOnLevelOneWithExplicitlyNamedField", - indexDefinitions.get(0)); - } - - @Test // DATAMONGO-899 - public void resolvesIndexDefinitionCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - IndexOnLevelZero.class); - - IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions()).isEqualTo(new org.bson.Document("name", "indexedProperty")); - } - - @Test // DATAMONGO-899 - public void resolvesIndexDefinitionOptionsCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithOptionsOnIndexedProperty.class); - - IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions()).isEqualTo(new org.bson.Document().append("name", "indexedProperty") - .append("unique", true).append("sparse", true).append("background", true).append("expireAfterSeconds", 10L)); - } - - @Test // DATAMONGO-1297 - public void resolvesIndexOnDbrefWhenDefined() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(WithDbRef.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0).getCollection()).isEqualTo("withDbRef"); - assertThat(indexDefinitions.get(0).getIndexKeys()).isEqualTo(new org.bson.Document("indexedDbRef", 1)); - } - - @Test // DATAMONGO-1297 - public void resolvesIndexOnDbrefWhenDefinedOnNestedElement() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WrapperOfWithDbRef.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0).getCollection()).isEqualTo("wrapperOfWithDbRef"); - assertThat(indexDefinitions.get(0).getIndexKeys()).isEqualTo(new org.bson.Document("nested.indexedDbRef", 1)); - } - - @Test // DATAMONGO-1163 - public void resolveIndexDefinitionInMetaAnnotatedFields() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - IndexOnMetaAnnotatedField.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0).getCollection()).isEqualTo("indexOnMetaAnnotatedField"); - assertThat(indexDefinitions.get(0).getIndexOptions()).isEqualTo(new org.bson.Document("name", "_name")); - } - - @Test // DATAMONGO-1373 - public void resolveIndexDefinitionInComposedAnnotatedFields() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - IndexedDocumentWithComposedAnnotations.class); - - assertThat(indexDefinitions).hasSize(2); - - IndexDefinitionHolder indexDefinitionHolder = indexDefinitions.get(1); - - assertThat(indexDefinitionHolder.getIndexKeys()).containsEntry("fieldWithMyIndexName", 1); - assertThat(indexDefinitionHolder.getIndexOptions()) // - .containsEntry("sparse", true) // - .containsEntry("unique", true) // - .containsEntry("name", "my_index_name"); - } - - @Test // DATAMONGO-1373 - public void resolveIndexDefinitionInCustomComposedAnnotatedFields() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - IndexedDocumentWithComposedAnnotations.class); - - assertThat(indexDefinitions).hasSize(2); - - IndexDefinitionHolder indexDefinitionHolder = indexDefinitions.get(0); - - assertThat(indexDefinitionHolder.getIndexKeys()).containsEntry("fieldWithDifferentIndexName", 1); - assertThat(indexDefinitionHolder.getIndexOptions()) // - .containsEntry("sparse", true) // - .containsEntry("name", "different_name") // - .doesNotContainKey("unique"); - } - - @Test // DATAMONGO-2112 - public void shouldResolveTimeoutFromString() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithExpireAfterAsPlainString.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 600L); - } - - @Test // DATAMONGO-2112 - public void shouldResolveTimeoutFromIso8601String() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithIso8601Style.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 86400L); - } - - @Test // DATAMONGO-2112 - public void shouldResolveTimeoutFromExpression() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithExpireAfterAsExpression.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 11L); - } - - @Test // DATAMONGO-2112 - public void shouldResolveTimeoutFromExpressionReturningDuration() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithExpireAfterAsExpressionResultingInDuration.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 100L); - } - - @Test // DATAMONGO-2112 - public void shouldErrorOnInvalidTimeoutExpression() { - - MongoMappingContext mappingContext = prepareMappingContext(WithInvalidExpireAfter.class); - MongoPersistentEntityIndexResolver indexResolver = new MongoPersistentEntityIndexResolver(mappingContext); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> indexResolver - .resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(WithInvalidExpireAfter.class))); - } - - @Test // DATAMONGO-2112 - public void shouldErrorOnDuplicateTimeoutExpression() { - - MongoMappingContext mappingContext = prepareMappingContext(WithDuplicateExpiry.class); - MongoPersistentEntityIndexResolver indexResolver = new MongoPersistentEntityIndexResolver(mappingContext); - - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> indexResolver - .resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(WithDuplicateExpiry.class))); - } - - @Test // DATAMONGO-2112 - public void resolveExpressionIndexName() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithIndexNameAsExpression.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "my1st"); - } - - @Test // DATAMONGO-1569 - public void resolvesPartialFilter() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithPartialFilter.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("partialFilterExpression", - org.bson.Document.parse("{'value': {'$exists': true}}")); - } - - @Document("Zero") - class IndexOnLevelZero { - @Indexed String indexedProperty; - } - - @Document("One") - class IndexOnLevelOne { - IndexOnLevelZero zero; - } - - @Document("Two") - class IndexOnLevelTwo { - IndexOnLevelOne one; - } - - @Document("WithOptionsOnIndexedProperty") - class WithOptionsOnIndexedProperty { - - @Indexed(background = true, direction = IndexDirection.DESCENDING, dropDups = true, expireAfterSeconds = 10, - sparse = true, unique = true) // - String indexedProperty; - } - - @Document - class IndexOnLevelOneWithExplicitlyNamedField { - - @Field("customZero") IndexOnLevelZeroWithExplicityNamedField zero; - } - - class IndexOnLevelZeroWithExplicityNamedField { - - @Indexed @Field("customFieldName") String namedProperty; - } - - @Document - class WrapperOfWithDbRef { - WithDbRef nested; - } - - @Document - class WithDbRef { - - @Indexed // - @DBRef // - NoIndex indexedDbRef; - } - - @Document("no-index") - class NoIndex { - @Id String id; - } - - @Document - class IndexedDocumentWithComposedAnnotations { - - @Id String id; - @CustomIndexedAnnotation String fieldWithDifferentIndexName; - @ComposedIndexedAnnotation String fieldWithMyIndexName; - } - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.FIELD }) - @ComposedIndexedAnnotation(indexName = "different_name", beUnique = false) - @interface CustomIndexedAnnotation { - } - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE }) - @Indexed - @interface ComposedIndexedAnnotation { - - @AliasFor(annotation = Indexed.class, attribute = "unique") - boolean beUnique() default true; - - @AliasFor(annotation = Indexed.class, attribute = "sparse") - boolean beSparse() default true; - - @AliasFor(annotation = Indexed.class, attribute = "name") - String indexName() default "my_index_name"; - } - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - @org.springframework.data.mongodb.core.mapping.Field - @interface ComposedFieldAnnotation { - - @AliasFor(annotation = org.springframework.data.mongodb.core.mapping.Field.class, attribute = "value") - String name() default "_id"; - } - - @Document - class WithExpireAfterAsPlainString { - @Indexed(expireAfter = "10m") String withTimeout; - } - - @Document - class WithIso8601Style { - @Indexed(expireAfter = "P1D") String withTimeout; - } - - @Document - class WithExpireAfterAsExpression { - @Indexed(expireAfter = "#{10 + 1 + 's'}") String withTimeout; - } - - @Document - class WithExpireAfterAsExpressionResultingInDuration { - @Indexed(expireAfter = "#{T(java.time.Duration).ofSeconds(100)}") String withTimeout; - } - - @Document - class WithInvalidExpireAfter { - @Indexed(expireAfter = "123ops") String withTimeout; - } - - @Document - class WithDuplicateExpiry { - @Indexed(expireAfter = "1s", expireAfterSeconds = 2) String withTimeout; - } - - @Document - class WithIndexNameAsExpression { - @Indexed(name = "#{'my' + 1 + 'st'}") String spelIndexName; - } - - @Document - class WithPartialFilter { - @Indexed(partialFilter = "{'value': {'$exists': true}}") String withPartialFilter; - } - } - - @Target({ ElementType.FIELD }) - @Retention(RetentionPolicy.RUNTIME) - @Indexed - @interface IndexedFieldAnnotation { - } - - @Document - class IndexOnMetaAnnotatedField { - @Field("_name") @IndexedFieldAnnotation String lastname; - } - - /** - * Test resolution of {@link GeoSpatialIndexed}. - * - * @author Christoph Strobl - */ - public static class GeoSpatialIndexResolutionTests { - - @Test // DATAMONGO-899 - public void geoSpatialIndexPathOnLevelZeroIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - GeoSpatialIndexOnLevelZero.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection("geoIndexedProperty", "Zero", indexDefinitions.get(0)); - } - - @Test // DATAMONGO-899 - public void geoSpatialIndexPathOnLevelOneIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - GeoSpatialIndexOnLevelOne.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection("zero.geoIndexedProperty", "One", indexDefinitions.get(0)); - } - - @Test // DATAMONGO-899 - public void depplyNestedGeoSpatialIndexPathIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - GeoSpatialIndexOnLevelTwo.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection("one.zero.geoIndexedProperty", "Two", indexDefinitions.get(0)); - } - - @Test // DATAMONGO-899 - public void resolvesIndexDefinitionOptionsCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithOptionsOnGeoSpatialIndexProperty.class); - - IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - - assertThat(indexDefinition.getIndexOptions()).isEqualTo( - new org.bson.Document().append("name", "location").append("min", 1).append("max", 100).append("bits", 2)); - } - - @Test // DATAMONGO-1373 - public void resolvesComposedAnnotationIndexDefinitionOptionsCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - GeoSpatialIndexedDocumentWithComposedAnnotation.class); - - IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - - assertThat(indexDefinition.getIndexKeys()).containsEntry("location", "geoHaystack").containsEntry("What light?", - 1); - assertThat(indexDefinition.getIndexOptions()).containsEntry("name", "my_geo_index_name") - .containsEntry("bucketSize", 2.0); - } - - @Test // DATAMONGO-2112 - public void resolveExpressionIndexNameForGeoIndex() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - GeoIndexWithNameAsExpression.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "my1st"); - } - - @Document("Zero") - class GeoSpatialIndexOnLevelZero { - @GeoSpatialIndexed Point geoIndexedProperty; - } - - @Document("One") - class GeoSpatialIndexOnLevelOne { - GeoSpatialIndexOnLevelZero zero; - } - - @Document("Two") - class GeoSpatialIndexOnLevelTwo { - GeoSpatialIndexOnLevelOne one; - } - - @Document("WithOptionsOnGeoSpatialIndexProperty") - class WithOptionsOnGeoSpatialIndexProperty { - - @GeoSpatialIndexed(bits = 2, max = 100, min = 1, type = GeoSpatialIndexType.GEO_2D) // - Point location; - } - - @Document("WithComposedAnnotation") - class GeoSpatialIndexedDocumentWithComposedAnnotation { - - @ComposedGeoSpatialIndexed // - Point location; - } - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.FIELD }) - @GeoSpatialIndexed - @interface ComposedGeoSpatialIndexed { - - @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "name") - String indexName() default "my_geo_index_name"; - - @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "additionalField") - String theAdditionalFieldINeedToDefine() default "What light?"; - - @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "bucketSize") - double size() default 2; - - @AliasFor(annotation = GeoSpatialIndexed.class, attribute = "type") - GeoSpatialIndexType indexType() default GeoSpatialIndexType.GEO_HAYSTACK; - } - - @Document - class GeoIndexWithNameAsExpression { - @GeoSpatialIndexed(name = "#{'my' + 1 + 'st'}") Point spelIndexName; - } - - } - - /** - * Test resolution of {@link CompoundIndexes}. - * - * @author Christoph Strobl - */ - public static class CompoundIndexResolutionTests { - - @Test // DATAMONGO-899 - public void compoundIndexPathOnLevelZeroIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - CompoundIndexOnLevelZero.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection(new String[] { "foo", "bar" }, "CompoundIndexOnLevelZero", indexDefinitions.get(0)); - } - - @Test // DATAMONGO-899 - public void compoundIndexOptionsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - CompoundIndexOnLevelZero.class); - - IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions()).isEqualTo(new org.bson.Document("name", "compound_index") - .append("unique", true).append("sparse", true).append("background", true)); - assertThat(indexDefinition.getIndexKeys()).isEqualTo(new org.bson.Document().append("foo", 1).append("bar", -1)); - } - - @Test // DATAMONGO-909 - public void compoundIndexOnSuperClassResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - IndexDefinedOnSuperClass.class); - - IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions()).isEqualTo(new org.bson.Document().append("name", "compound_index") - .append("unique", true).append("sparse", true).append("background", true)); - assertThat(indexDefinition.getIndexKeys()).isEqualTo(new org.bson.Document().append("foo", 1).append("bar", -1)); - } - - @Test // DATAMONGO-827 - public void compoundIndexDoesNotSpecifyNameWhenUsingGenerateName() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - ComountIndexWithAutogeneratedName.class); - - IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions()) - .isEqualTo(new org.bson.Document().append("unique", true).append("sparse", true).append("background", true)); - assertThat(indexDefinition.getIndexKeys()).isEqualTo(new org.bson.Document().append("foo", 1).append("bar", -1)); - } - - @Test // DATAMONGO-929 - public void compoundIndexPathOnLevelOneIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - CompoundIndexOnLevelOne.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection(new String[] { "zero.foo", "zero.bar" }, "CompoundIndexOnLevelOne", - indexDefinitions.get(0)); - } - - @Test // DATAMONGO-929 - public void emptyCompoundIndexPathOnLevelOneIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - CompoundIndexOnLevelOneWithEmptyIndexDefinition.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection(new String[] { "zero" }, "CompoundIndexOnLevelZeroWithEmptyIndexDef", - indexDefinitions.get(0)); - } - - @Test // DATAMONGO-929 - public void singleCompoundIndexPathOnLevelZeroIsResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - SingleCompoundIndex.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection(new String[] { "foo", "bar" }, "CompoundIndexOnLevelZero", indexDefinitions.get(0)); - } - - @Test // DATAMONGO-1373 - public void singleCompoundIndexUsingComposedAnnotationsOnTypeResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - CompoundIndexDocumentWithComposedAnnotation.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0).getIndexKeys()).containsEntry("foo", 1).containsEntry("bar", -1); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "my_compound_index_name") - .containsEntry("unique", true).containsEntry("background", true); - } - - @Test // DATAMONGO-2112 - public void resolveExpressionIndexNameForCompoundIndex() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - CompoundIndexWithNameExpression.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "cmp2name"); - } - - @Test // DATAMONGO-2112 - public void resolveExpressionDefForCompoundIndex() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - CompoundIndexWithDefExpression.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection(new String[] { "foo", "bar" }, "compoundIndexWithDefExpression", - indexDefinitions.get(0)); - } - - @Test // DATAMONGO-2067 - public void shouldIdentifyRepeatedAnnotationCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - RepeatedCompoundIndex.class); - - assertThat(indexDefinitions).hasSize(2); - assertIndexPathAndCollection(new String[] { "firstname", "lastname" }, "repeatedCompoundIndex", - indexDefinitions.get(0)); - assertIndexPathAndCollection(new String[] { "address.city", "address.street" }, "repeatedCompoundIndex", - indexDefinitions.get(1)); - } - - @Test // DATAMONGO-1569 - public void singleIndexWithPartialFilter() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - SingleCompoundIndexWithPartialFilter.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0).getIndexKeys()).containsEntry("foo", 1).containsEntry("bar", -1); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "compound_index_with_partial") - .containsEntry("unique", true).containsEntry("background", true); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("partialFilterExpression", - org.bson.Document.parse("{'value': {'$exists': true}}")); - } - - @Document("CompoundIndexOnLevelOne") - class CompoundIndexOnLevelOne { - - CompoundIndexOnLevelZero zero; - } - - @Document("CompoundIndexOnLevelZeroWithEmptyIndexDef") - class CompoundIndexOnLevelOneWithEmptyIndexDefinition { - - CompoundIndexOnLevelZeroWithEmptyIndexDef zero; - } - - @Document("CompoundIndexOnLevelZero") - @CompoundIndexes({ @CompoundIndex(name = "compound_index", def = "{'foo': 1, 'bar': -1}", background = true, - sparse = true, unique = true) }) - class CompoundIndexOnLevelZero {} - - @CompoundIndexes({ @CompoundIndex(name = "compound_index", background = true, sparse = true, unique = true) }) - class CompoundIndexOnLevelZeroWithEmptyIndexDef {} - - @Document("CompoundIndexOnLevelZero") - @CompoundIndex(name = "compound_index", def = "{'foo': 1, 'bar': -1}", background = true, sparse = true, - unique = true) - class SingleCompoundIndex {} - - class IndexDefinedOnSuperClass extends CompoundIndexOnLevelZero {} - - @Document("ComountIndexWithAutogeneratedName") - @CompoundIndexes({ @CompoundIndex(useGeneratedName = true, def = "{'foo': 1, 'bar': -1}", background = true, - sparse = true, unique = true) }) - class ComountIndexWithAutogeneratedName {} - - @Document("WithComposedAnnotation") - @ComposedCompoundIndex - class CompoundIndexDocumentWithComposedAnnotation {} - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.TYPE }) - @CompoundIndex - @interface ComposedCompoundIndex { - - @AliasFor(annotation = CompoundIndex.class, attribute = "def") - String fields() default "{'foo': 1, 'bar': -1}"; - - @AliasFor(annotation = CompoundIndex.class, attribute = "background") - boolean inBackground() default true; - - @AliasFor(annotation = CompoundIndex.class, attribute = "name") - String indexName() default "my_compound_index_name"; - - @AliasFor(annotation = CompoundIndex.class, attribute = "useGeneratedName") - boolean useGeneratedName() default false; - - @AliasFor(annotation = CompoundIndex.class, attribute = "unique") - boolean isUnique() default true; - - } - - @Document - @CompoundIndex(name = "#{'cmp' + 2 + 'name'}", def = "{'foo': 1, 'bar': -1}") - class CompoundIndexWithNameExpression {} - - @Document - @CompoundIndex(def = "#{T(org.bson.Document).parse(\"{ 'foo': 1, 'bar': -1 }\")}") - class CompoundIndexWithDefExpression {} - - @Document - @CompoundIndex(name = "cmp-idx-one", def = "{'firstname': 1, 'lastname': -1}") - @CompoundIndex(name = "cmp-idx-two", def = "{'address.city': -1, 'address.street': 1}") - class RepeatedCompoundIndex {} - - @Document("SingleCompoundIndexWithPartialFilter") - @CompoundIndex(name = "compound_index_with_partial", def = "{'foo': 1, 'bar': -1}", background = true, - unique = true, partialFilter = "{'value': {'$exists': true}}") - class SingleCompoundIndexWithPartialFilter {} - } - - public static class TextIndexedResolutionTests { - - @Test // DATAMONGO-937 - public void shouldResolveSingleFieldTextIndexCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - TextIndexOnSinglePropertyInRoot.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection("bar", "textIndexOnSinglePropertyInRoot", indexDefinitions.get(0)); - assertThat(indexDefinitions.get(0).getIndexOptions()).doesNotContainKey("collation"); - } - - @Test // DATAMONGO-2316 - public void shouldEnforceSimpleCollationOnTextIndex() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - TextIndexWithCollation.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("collation", - new org.bson.Document("locale", "simple")); - } - - @Test // DATAMONGO-937 - public void shouldResolveMultiFieldTextIndexCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - TextIndexOnMultiplePropertiesInRoot.class); - - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection(new String[] { "foo", "bar" }, "textIndexOnMultiplePropertiesInRoot", - indexDefinitions.get(0)); - } - - @Test // DATAMONGO-937 - public void shouldResolveTextIndexOnElementCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - TextIndexOnNestedRoot.class); - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection(new String[] { "nested.foo" }, "textIndexOnNestedRoot", indexDefinitions.get(0)); - } - - @Test // DATAMONGO-937 - public void shouldResolveTextIndexOnElementWithWeightCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - TextIndexOnNestedWithWeightRoot.class); - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection(new String[] { "nested.foo" }, "textIndexOnNestedWithWeightRoot", - indexDefinitions.get(0)); - - org.bson.Document weights = DocumentTestUtils.getAsDocument(indexDefinitions.get(0).getIndexOptions(), "weights"); - assertThat(weights.get("nested.foo")).isEqualTo(5F); - } - - @Test // DATAMONGO-937 - public void shouldResolveTextIndexOnElementWithMostSpecificWeightCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - TextIndexOnNestedWithMostSpecificValueRoot.class); - assertThat(indexDefinitions).hasSize(1); - assertIndexPathAndCollection(new String[] { "nested.foo", "nested.bar" }, - "textIndexOnNestedWithMostSpecificValueRoot", indexDefinitions.get(0)); - - org.bson.Document weights = DocumentTestUtils.getAsDocument(indexDefinitions.get(0).getIndexOptions(), "weights"); - assertThat(weights.get("nested.foo")).isEqualTo(5F); - assertThat(weights.get("nested.bar")).isEqualTo(10F); - } - - @Test // DATAMONGO-937 - public void shouldSetDefaultLanguageCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithDefaultLanguage.class); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("default_language", "spanish"); - } - - @Test // DATAMONGO-937, DATAMONGO-1049 - public void shouldResolveTextIndexLanguageOverrideCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithLanguageOverride.class); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("language_override", "lang"); - } - - @Test // DATAMONGO-1049 - public void shouldIgnoreTextIndexLanguageOverrideOnNestedElements() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithLanguageOverrideOnNestedElement.class); - assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override")).isNull(); - } - - @Test // DATAMONGO-1049 - public void shouldNotCreateIndexDefinitionWhenOnlyLanguageButNoTextIndexPresent() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithNoTextIndexPropertyButReservedFieldLanguage.class); - - assertThat(indexDefinitions).isEmpty(); - } - - @Test // DATAMONGO-1049 - public void shouldNotCreateIndexDefinitionWhenOnlyAnnotatedLanguageButNoTextIndexPresent() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithNoTextIndexPropertyButReservedFieldLanguageAnnotated.class); - - assertThat(indexDefinitions).isEmpty(); - } - - @Test // DATAMONGO-1049 - public void shouldPreferExplicitlyAnnotatedLanguageProperty() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithOverlappingLanguageProps.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("language_override", "lang"); - } - - @Test // DATAMONGO-1373 - public void shouldResolveComposedAnnotationCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - TextIndexedDocumentWithComposedAnnotation.class); - - org.bson.Document weights = DocumentTestUtils.getAsDocument(indexDefinitions.get(0).getIndexOptions(), "weights"); - assertThat(weights).containsEntry("foo", 99f); - } - - @Document - class TextIndexOnSinglePropertyInRoot { - - String foo; - - @TextIndexed String bar; - } - - @Document(collation = "de_AT") - class TextIndexWithCollation { - - @TextIndexed String foo; - } - - @Document - class TextIndexOnMultiplePropertiesInRoot { - - @TextIndexed String foo; - - @TextIndexed(weight = 5) String bar; - } - - @Document - class TextIndexOnNestedRoot { - - String bar; - - @TextIndexed TextIndexOnNested nested; - } - - class TextIndexOnNested { - - String foo; - } - - @Document - class TextIndexOnNestedWithWeightRoot { - - @TextIndexed(weight = 5) TextIndexOnNested nested; - } - - @Document - class TextIndexOnNestedWithMostSpecificValueRoot { - @TextIndexed(weight = 5) TextIndexOnNestedWithMostSpecificValue nested; - } - - class TextIndexOnNestedWithMostSpecificValue { - - String foo; - @TextIndexed(weight = 10) String bar; - } - - @Document(language = "spanish") - class DocumentWithDefaultLanguage { - @TextIndexed String foo; - } - - @Document - class DocumentWithLanguageOverrideOnNestedElement { - - DocumentWithLanguageOverride nested; - } - - @Document - class DocumentWithLanguageOverride { - - @TextIndexed String foo; - - @Language String lang; - } - - @Document - class DocumentWithNoTextIndexPropertyButReservedFieldLanguage { - - String language; - } - - @Document - class DocumentWithNoTextIndexPropertyButReservedFieldLanguageAnnotated { - - @Field("language") String lang; - } - - @Document - class DocumentWithOverlappingLanguageProps { - - @TextIndexed String foo; - String language; - @Language String lang; - } - - @Document - class TextIndexedDocumentWithComposedAnnotation { - - @ComposedTextIndexedAnnotation String foo; - String lang; - } - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE }) - @TextIndexed - @interface ComposedTextIndexedAnnotation { - - @AliasFor(annotation = TextIndexed.class, attribute = "weight") - float heavyweight() default 99f; - } - } - - public static class MixedIndexResolutionTests { - - @Test // DATAMONGO-899 - public void multipleIndexesResolvedCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(MixedIndexRoot.class); - - assertThat(indexDefinitions).hasSize(2); - assertThat(indexDefinitions.get(0).getIndexDefinition()).isInstanceOf(Index.class); - assertThat(indexDefinitions.get(1).getIndexDefinition()).isInstanceOf(GeospatialIndex.class); - } - - @Test // DATAMONGO-899 - public void cyclicPropertyReferenceOverDBRefShouldNotBeTraversed() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(Inner.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0).getIndexDefinition().getIndexKeys()) - .isEqualTo(new org.bson.Document().append("outer", 1)); - } - - @Test // DATAMONGO-899 - public void associationsShouldNotBeTraversed() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(Outer.class); - - assertThat(indexDefinitions).isEmpty(); - } - - @Test // DATAMONGO-926 - public void shouldNotRunIntoStackOverflow() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - CycleStartingInBetween.class); - - assertThat(indexDefinitions).hasSize(1); - } - - @Test // DATAMONGO-926 - public void indexShouldBeFoundEvenForCyclePropertyReferenceOnLevelZero() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(CycleLevelZero.class); - assertIndexPathAndCollection("indexedProperty", "cycleLevelZero", indexDefinitions.get(0)); - assertIndexPathAndCollection("cyclicReference.indexedProperty", "cycleLevelZero", indexDefinitions.get(1)); - assertThat(indexDefinitions).hasSize(2); - } - - @Test // DATAMONGO-926 - public void indexShouldBeFoundEvenForCyclePropertyReferenceOnLevelOne() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(CycleOnLevelOne.class); - assertIndexPathAndCollection("reference.indexedProperty", "cycleOnLevelOne", indexDefinitions.get(0)); - assertThat(indexDefinitions).hasSize(1); - } - - @Test // DATAMONGO-926 - public void indexBeResolvedCorrectlyWhenPropertiesOfDifferentTypesAreNamedEqually() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - NoCycleButIdenticallyNamedProperties.class); - - assertThat(indexDefinitions).hasSize(3); - assertIndexPathAndCollection("foo", "noCycleButIdenticallyNamedProperties", indexDefinitions.get(0)); - assertIndexPathAndCollection("reference.foo", "noCycleButIdenticallyNamedProperties", indexDefinitions.get(1)); - assertIndexPathAndCollection("reference.deep.foo", "noCycleButIdenticallyNamedProperties", - indexDefinitions.get(2)); - } - - @Test // DATAMONGO-949 - public void shouldNotDetectCycleInSimilarlyNamedProperties() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - SimilarityHolingBean.class); - assertIndexPathAndCollection("norm", "similarityHolingBean", indexDefinitions.get(0)); - assertThat(indexDefinitions).hasSize(1); - } - - @Test // DATAMONGO-962 - public void shouldDetectSelfCycleViaCollectionTypeCorrectly() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - SelfCyclingViaCollectionType.class); - - assertThat(indexDefinitions).isEmpty(); - } - - @Test // DATAMONGO-962 - public void shouldNotDetectCycleWhenTypeIsUsedMoreThanOnce() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - MultipleObjectsOfSameType.class); - - assertThat(indexDefinitions).isEmpty(); - } - - @Test // DATAMONGO-962 - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void shouldCatchCyclicReferenceExceptionOnRoot() { - - MongoPersistentEntity entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Object.class)); - - MongoPersistentProperty propertyMock = mock(MongoPersistentProperty.class); - when(propertyMock.isEntity()).thenReturn(true); - when(propertyMock.getOwner()).thenReturn(entity); - when(propertyMock.getActualType()).thenThrow( - new MongoPersistentEntityIndexResolver.CyclicPropertyReferenceException("foo", Object.class, "bar")); - - MongoPersistentEntity selfCyclingEntity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(SelfCyclingViaCollectionType.class)); - - new MongoPersistentEntityIndexResolver(prepareMappingContext(SelfCyclingViaCollectionType.class)) - .resolveIndexForEntity(selfCyclingEntity); - } - - @Test // DATAMONGO-1782 - public void shouldAllowMultiplePathsToDeeplyType() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - NoCycleManyPathsToDeepValueObject.class); - - assertThat(indexDefinitions).hasSize(2); - assertIndexPathAndCollection("l3.valueObject.value", "rules", indexDefinitions.get(0)); - assertIndexPathAndCollection("l2.l3.valueObject.value", "rules", indexDefinitions.get(1)); - } - - @Test // DATAMONGO-1025 - public void shouldUsePathIndexAsIndexNameForDocumentsHavingNamedNestedCompoundIndexFixedOnCollection() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithNestedDocumentHavingNamedCompoundIndex.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", - "propertyOfTypeHavingNamedCompoundIndex.c_index"); - } - - @Test // DATAMONGO-1025 - public void shouldUseIndexNameForNestedTypesWithNamedCompoundIndexDefinition() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithNestedTypeHavingNamedCompoundIndex.class); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", - "propertyOfTypeHavingNamedCompoundIndex.c_index"); - } - - @Test // DATAMONGO-1025 - public void shouldUsePathIndexAsIndexNameForDocumentsHavingNamedNestedIndexFixedOnCollection() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithNestedDocumentHavingNamedIndex.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", - "propertyOfTypeHavingNamedIndex.property_index"); - } - - @Test // DATAMONGO-1025 - public void shouldUseIndexNameForNestedTypesWithNamedIndexDefinition() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithNestedTypeHavingNamedIndex.class); - - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", - "propertyOfTypeHavingNamedIndex.property_index"); - } - - @Test // DATAMONGO-1025 - public void shouldUseIndexNameOnRootLevel() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - DocumentWithNamedIndex.class); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "property_index"); - } - - @Test // DATAMONGO-1087 - public void shouldAllowMultiplePropertiesOfSameTypeWithMatchingStartLettersOnRoot() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - MultiplePropertiesOfSameTypeWithMatchingStartLetters.class); - - assertThat(indexDefinitions).hasSize(2); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "name.component"); - assertThat(indexDefinitions.get(1).getIndexOptions()).containsEntry("name", "nameLast.component"); - } - - @Test // DATAMONGO-1087 - public void shouldAllowMultiplePropertiesOfSameTypeWithMatchingStartLettersOnNestedProperty() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - MultiplePropertiesOfSameTypeWithMatchingStartLettersOnNestedProperty.class); - - assertThat(indexDefinitions).hasSize(2); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "component.nameLast"); - assertThat(indexDefinitions.get(1).getIndexOptions()).containsEntry("name", "component.name"); - } - - @Test // DATAMONGO-1121 - public void shouldOnlyConsiderEntitiesAsPotentialCycleCandidates() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - OuterDocumentReferingToIndexedPropertyViaDifferentNonCyclingPaths.class); - - assertThat(indexDefinitions).hasSize(2); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "path1.foo"); - assertThat(indexDefinitions.get(1).getIndexOptions()).containsEntry("name", - "path2.propertyWithIndexedStructure.foo"); - } - - @Test // DATAMONGO-1263 - public void shouldConsiderGenericTypeArgumentsOfCollectionElements() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - EntityWithGenericTypeWrapperAsElement.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", - "listWithGeneircTypeElement.entity.property_index"); - } - - @Test // DATAMONGO-1183 - public void hashedIndexOnId() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithHashedIndexOnId.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0)).satisfies(it -> { - assertThat(it.getIndexKeys()).hasSize(1).containsEntry("_id", "hashed"); - }); - } - - @Test // DATAMONGO-1183 - public void hashedIndex() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(WithHashedIndex.class); - - assertThat(indexDefinitions).hasSize(1); - assertThat(indexDefinitions.get(0)).satisfies(it -> { - assertThat(it.getIndexKeys()).hasSize(1).containsEntry("value", "hashed"); - }); - } - - @Test // DATAMONGO-1183 - public void hashedIndexAndIndex() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithHashedIndexAndIndex.class); - - assertThat(indexDefinitions).hasSize(2); - assertThat(indexDefinitions.get(0)).satisfies(it -> { - assertThat(it.getIndexKeys()).containsEntry("value", 1); - }); - assertThat(indexDefinitions.get(1)).satisfies(it -> { - assertThat(it.getIndexKeys()).containsEntry("value", "hashed"); - }); - } - - @Test // DATAMONGO-1183 - public void hashedIndexAndIndexViaComposedAnnotation() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WithComposedHashedIndexAndIndex.class); - - assertThat(indexDefinitions).hasSize(2); - assertThat(indexDefinitions.get(0)).satisfies(it -> { - assertThat(it.getIndexKeys()).containsEntry("value", 1); - assertThat(it.getIndexOptions()).containsEntry("name", "idx-name"); - }); - assertThat(indexDefinitions.get(1)).satisfies(it -> { - assertThat(it.getIndexKeys()).containsEntry("value", "hashed"); - }); - } - - @Test // DATAMONGO-1902 - public void resolvedIndexOnUnwrappedType() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType(WithUnwrapped.class, - UnwrappableType.class); - - assertThat(indexDefinitions).hasSize(2); - assertThat(indexDefinitions.get(0)).satisfies(it -> { - assertThat(it.getIndexKeys()).containsEntry("stringValue", 1); - }); - assertThat(indexDefinitions.get(1)).satisfies(it -> { - assertThat(it.getIndexKeys()).containsEntry("with-at-field-annotation", 1); - }); - } - - @Test // DATAMONGO-1902 - public void resolvedIndexOnNestedUnwrappedType() { - - List indexDefinitions = prepareMappingContextAndResolveIndexForType( - WrapperAroundWithUnwrapped.class, WithUnwrapped.class, UnwrappableType.class); - - assertThat(indexDefinitions).hasSize(2); - assertThat(indexDefinitions.get(0)).satisfies(it -> { - assertThat(it.getIndexKeys()).containsEntry("withEmbedded.stringValue", 1); - }); - assertThat(indexDefinitions.get(1)).satisfies(it -> { - assertThat(it.getIndexKeys()).containsEntry("withEmbedded.with-at-field-annotation", 1); - }); - } - - @Test // DATAMONGO-1902 - public void errorsOnIndexOnEmbedded() { - - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) - .isThrownBy(() -> prepareMappingContextAndResolveIndexForType(InvalidIndexOnUnwrapped.class)); - - } - - @Document - class MixedIndexRoot { - - @Indexed String first; - NestedGeoIndex nestedGeo; - } - - class NestedGeoIndex { - - @GeoSpatialIndexed Point location; - } - - @Document - class Outer { - - @DBRef Inner inner; - } - - @Document - class Inner { - - @Indexed Outer outer; - } - - @Document - class CycleLevelZero { - - @Indexed String indexedProperty; - CycleLevelZero cyclicReference; - } - - @Document - class CycleOnLevelOne { - - CycleOnLevelOneReferenced reference; - } - - class CycleOnLevelOneReferenced { - - @Indexed String indexedProperty; - CycleOnLevelOne cyclicReference; - } - - @Document - static class CycleStartingInBetween { - - CycleOnLevelOne referenceToCycleStart; - } - - @Document - class NoCycleButIdenticallyNamedProperties { - - @Indexed String foo; - NoCycleButIdenticallyNamedPropertiesNested reference; - } - - class NoCycleButIdenticallyNamedPropertiesNested { - - @Indexed String foo; - NoCycleButIndenticallNamedPropertiesDeeplyNested deep; - } - - class NoCycleButIndenticallNamedPropertiesDeeplyNested { - - @Indexed String foo; - } - - @Document("rules") - class NoCycleManyPathsToDeepValueObject { - - private NoCycleLevel3 l3; - private NoCycleLevel2 l2; - } - - class NoCycleLevel2 { - private NoCycleLevel3 l3; - } - - class NoCycleLevel3 { - private ValueObject valueObject; - } - - class ValueObject { - @Indexed private String value; - } - - @Document - class SimilarityHolingBean { - - @Indexed @Field("norm") String normalProperty; - @Field("similarityL") private List listOfSimilarilyNamedEntities = null; - } - - class SimilaritySibling { - @Field("similarity") private String similarThoughNotEqualNamedProperty; - } - - @Document - class MultipleObjectsOfSameType { - - SelfCyclingViaCollectionType cycleOne; - - SelfCyclingViaCollectionType cycleTwo; - } - - @Document - class SelfCyclingViaCollectionType { - - List cyclic; - - } - - @Document - @CompoundIndex(name = "c_index", def = "{ foo:1, bar:1 }") - class DocumentWithNamedCompoundIndex { - - String property; - } - - @Document - class DocumentWithNamedIndex { - - @Indexed(name = "property_index") String property; - } - - class TypeWithNamedIndex { - - @Indexed(name = "property_index") String property; - } - - @Document - class DocumentWithNestedDocumentHavingNamedCompoundIndex { - - DocumentWithNamedCompoundIndex propertyOfTypeHavingNamedCompoundIndex; - } - - @CompoundIndex(name = "c_index", def = "{ foo:1, bar:1 }") - class TypeWithNamedCompoundIndex { - String property; - } - - @Document - class DocumentWithNestedTypeHavingNamedCompoundIndex { - - TypeWithNamedCompoundIndex propertyOfTypeHavingNamedCompoundIndex; - } - - @Document - class DocumentWithNestedDocumentHavingNamedIndex { - - DocumentWithNamedIndex propertyOfTypeHavingNamedIndex; - } - - @Document - class DocumentWithNestedTypeHavingNamedIndex { - - TypeWithNamedIndex propertyOfTypeHavingNamedIndex; - } - - @Document - class MultiplePropertiesOfSameTypeWithMatchingStartLetters { - - class NameComponent { - - @Indexed String component; - } - - NameComponent name; - NameComponent nameLast; - } - - @Document - class MultiplePropertiesOfSameTypeWithMatchingStartLettersOnNestedProperty { - - class NameComponent { - - @Indexed String nameLast; - @Indexed String name; - } - - NameComponent component; - } - - @Document - static class OuterDocumentReferingToIndexedPropertyViaDifferentNonCyclingPaths { - - NoCycleButIndenticallNamedPropertiesDeeplyNested path1; - AlternatePathToNoCycleButIndenticallNamedPropertiesDeeplyNestedDocument path2; - } - - @Document - static class WrapperAroundWithUnwrapped { - - String id; - WithUnwrapped withEmbedded; - } - - @Document - static class WithUnwrapped { - - String id; - - @Unwrapped.Nullable UnwrappableType unwrappableType; - } - - @Document - class InvalidIndexOnUnwrapped { - - @Indexed // - @Unwrapped.Nullable // - UnwrappableType unwrappableType; - - } - - static class UnwrappableType { - - @Indexed String stringValue; - - List listValue; - - @Indexed // - @Field("with-at-field-annotation") // - String atFieldAnnotatedValue; - } - - static class AlternatePathToNoCycleButIndenticallNamedPropertiesDeeplyNestedDocument { - NoCycleButIndenticallNamedPropertiesDeeplyNested propertyWithIndexedStructure; - } - - class GenericEntityWrapper { - T entity; - } - - @Document - class EntityWithGenericTypeWrapperAsElement { - List> listWithGeneircTypeElement; - } - - @Document - class WithHashedIndexOnId { - - @HashIndexed @Id String id; - } - - @Document - class WithHashedIndex { - - @HashIndexed String value; - } - - @Document - class WithHashedIndexAndIndex { - - @Indexed // - @HashIndexed // - String value; - } - - @Document - class WithComposedHashedIndexAndIndex { - - @ComposedHashIndexed(name = "idx-name") String value; - } - - @HashIndexed - @Indexed - @Retention(RetentionPolicy.RUNTIME) - @interface ComposedHashIndexed { - - @AliasFor(annotation = Indexed.class, attribute = "name") - String name() default ""; - } - } - - private static List prepareMappingContextAndResolveIndexForType(Class... types) { - - MongoMappingContext mappingContext = prepareMappingContext(types); - MongoPersistentEntityIndexResolver resolver = new MongoPersistentEntityIndexResolver(mappingContext); - return resolver.resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(types[0])); - } - - private static MongoMappingContext prepareMappingContext(Class... types) { - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setInitialEntitySet(new LinkedHashSet<>(Arrays.asList(types))); - mappingContext.initialize(); - - return mappingContext; - } - - private static void assertIndexPathAndCollection(String expectedPath, String expectedCollection, - IndexDefinitionHolder holder) { - assertIndexPathAndCollection(new String[] { expectedPath }, expectedCollection, holder); - } - - private static void assertIndexPathAndCollection(String[] expectedPaths, String expectedCollection, - IndexDefinitionHolder holder) { - - for (String expectedPath : expectedPaths) { - assertThat(holder.getIndexDefinition().getIndexKeys()).containsKey(expectedPath); - } - - assertThat(holder.getCollection()).isEqualTo(expectedCollection); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/PathUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/PathUnitTests.java deleted file mode 100644 index fe1eaa9f22..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/PathUnitTests.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2014-2021 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.index; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.CycleGuard.Path; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; - -/** - * Unit tests for {@link Path}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@RunWith(MockitoJUnitRunner.Silent.class) -public class PathUnitTests { - - @Mock MongoPersistentEntity entityMock; - - @Before - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void setUp() { - when(entityMock.getType()).thenReturn((Class) Object.class); - } - - @Test // DATAMONGO-962, DATAMONGO-1782 - public void shouldIdentifyCycle() { - - MongoPersistentProperty foo = createPersistentPropertyMock(entityMock, "foo"); - MongoPersistentProperty bar = createPersistentPropertyMock(entityMock, "bar"); - - Path path = Path.of(foo).append(bar).append(bar); - - assertThat(path.isCycle()).isTrue(); - assertThat(path.toCyclePath()).isEqualTo("bar -> bar"); - assertThat(path.toString()).isEqualTo("foo -> bar -> bar"); - } - - @Test // DATAMONGO-1782 - public void isCycleShouldReturnFalseWhenNoCyclePresent() { - - MongoPersistentProperty foo = createPersistentPropertyMock(entityMock, "foo"); - MongoPersistentProperty bar = createPersistentPropertyMock(entityMock, "bar"); - - Path path = Path.of(foo).append(bar); - - assertThat(path.isCycle()).isFalse(); - assertThat(path.toCyclePath()).isEqualTo(""); - assertThat(path.toString()).isEqualTo("foo -> bar"); - } - - @Test // DATAMONGO-1782 - public void isCycleShouldReturnFalseCycleForNonEqualProperties() { - - MongoPersistentProperty foo = createPersistentPropertyMock(entityMock, "foo"); - MongoPersistentProperty bar = createPersistentPropertyMock(entityMock, "bar"); - MongoPersistentProperty bar2 = createPersistentPropertyMock(mock(MongoPersistentEntity.class), "bar"); - - assertThat(Path.of(foo).append(bar).append(bar2).isCycle()).isFalse(); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static MongoPersistentProperty createPersistentPropertyMock(MongoPersistentEntity owner, String fieldname) { - - MongoPersistentProperty property = Mockito.mock(MongoPersistentProperty.class); - - when(property.getOwner()).thenReturn(owner); - when(property.getName()).thenReturn(fieldname); - - return property; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/ReactiveMongoPersistentEntityIndexCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/ReactiveMongoPersistentEntityIndexCreatorUnitTests.java deleted file mode 100644 index b0e2e05676..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/ReactiveMongoPersistentEntityIndexCreatorUnitTests.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2018-2021 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.index; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.dao.DataAccessResourceFailureException; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoExceptionTranslator; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -import com.mongodb.MongoException; -import com.mongodb.client.model.IndexOptions; -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * Unit tests for {@link ReactiveMongoPersistentEntityIndexCreator}. - * - * @author Mark Paluch - * @author Mathieu Ouellet - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -public class ReactiveMongoPersistentEntityIndexCreatorUnitTests { - - private ReactiveIndexOperations indexOperations; - - @Mock ReactiveMongoDatabaseFactory factory; - @Mock MongoDatabase db; - @Mock MongoCollection collection; - - private ArgumentCaptor keysCaptor; - private ArgumentCaptor optionsCaptor; - private ArgumentCaptor collectionCaptor; - - @BeforeEach - @SuppressWarnings("unchecked") - void setUp() { - - when(factory.getExceptionTranslator()).thenReturn(new MongoExceptionTranslator()); - when(factory.getMongoDatabase()).thenReturn(Mono.just(db)); - when(db.getCollection(any(), any(Class.class))).thenReturn(collection); - - indexOperations = new ReactiveMongoTemplate(factory).indexOps("foo"); - - keysCaptor = ArgumentCaptor.forClass(org.bson.Document.class); - optionsCaptor = ArgumentCaptor.forClass(IndexOptions.class); - collectionCaptor = ArgumentCaptor.forClass(String.class); - - when(collection.createIndex(keysCaptor.capture(), optionsCaptor.capture())).thenReturn(Mono.just("OK")); - } - - @Test // DATAMONGO-1928 - void buildsIndexDefinitionUsingFieldName() { - - MongoMappingContext mappingContext = prepareMappingContext(Person.class); - - Mono publisher = checkForIndexes(mappingContext); - - verifyZeroInteractions(collection); - - publisher.as(StepVerifier::create).verifyComplete(); - - assertThat(keysCaptor.getValue()).isNotNull().containsKey("fieldname"); - assertThat(optionsCaptor.getValue().getName()).isEqualTo("indexName"); - assertThat(optionsCaptor.getValue().isBackground()).isFalse(); - assertThat(optionsCaptor.getValue().getExpireAfter(TimeUnit.SECONDS)).isNull(); - } - - @Test // DATAMONGO-1928 - void createIndexShouldUsePersistenceExceptionTranslatorForNonDataIntegrityConcerns() { - - when(collection.createIndex(any(org.bson.Document.class), any(IndexOptions.class))) - .thenReturn(Mono.error(new MongoException(6, "HostUnreachable"))); - - MongoMappingContext mappingContext = prepareMappingContext(Person.class); - - Mono publisher = checkForIndexes(mappingContext); - - publisher.as(StepVerifier::create).expectError(DataAccessResourceFailureException.class).verify(); - } - - @Test // DATAMONGO-1928 - void createIndexShouldNotConvertUnknownExceptionTypes() { - - when(collection.createIndex(any(org.bson.Document.class), any(IndexOptions.class))) - .thenReturn(Mono.error(new ClassCastException("o_O"))); - - MongoMappingContext mappingContext = prepareMappingContext(Person.class); - - Mono publisher = checkForIndexes(mappingContext); - - publisher.as(StepVerifier::create).expectError(ClassCastException.class).verify(); - } - - private static MongoMappingContext prepareMappingContext(Class type) { - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setInitialEntitySet(Collections.singleton(type)); - mappingContext.initialize(); - - return mappingContext; - } - - private Mono checkForIndexes(MongoMappingContext mappingContext) { - - return new ReactiveMongoPersistentEntityIndexCreator(mappingContext, it -> indexOperations) - .checkForIndexes(mappingContext.getRequiredPersistentEntity(Person.class)); - } - - @Document - static class Person { - - @Indexed(name = "indexName") // - @Field("fieldname") // - String field; - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/SampleEntity.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/SampleEntity.java deleted file mode 100644 index e419a75012..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/SampleEntity.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.springframework.data.mongodb.core.index; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; - -@Document -public class SampleEntity { - - @Id String id; - - @Indexed String prop; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/TextIndexTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/TextIndexTests.java deleted file mode 100644 index a9e0efcccc..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/TextIndexTests.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2014-2021 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.index; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.CollectionOptions; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Language; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -public class TextIndexTests { - - @Template(initialEntitySet = TextIndexedDocumentRoot.class) - static MongoTestTemplate template; - - private IndexOperations indexOps; - - @BeforeEach - public void beforeEach() throws Exception { - - this.indexOps = template.indexOps(TextIndexedDocumentRoot.class); - - template.dropDatabase(); - - template.createCollection(TextIndexedDocumentRoot.class, - CollectionOptions.empty().collation(Collation.of("de_AT"))); - } - - @Test // DATAMONGO-937, DATAMONGO-2316 - public void indexInfoShouldHaveBeenCreatedCorrectly() { - - IndexResolver indexResolver = IndexResolver.create(template.getConverter().getMappingContext()); - - for (IndexDefinition indexDefinition : indexResolver.resolveIndexFor(TextIndexedDocumentRoot.class)) { - indexOps.ensureIndex(indexDefinition); - } - - List indexInfos = indexOps.getIndexInfo(); - - assertThat(indexInfos.size()).isEqualTo(2); - - List fields = indexInfos.get(0).getIndexFields(); - assertThat(fields).containsExactly(IndexField.create("_id", Direction.ASC)); - - IndexInfo textIndexInfo = indexInfos.get(1); - List textIndexFields = textIndexInfo.getIndexFields(); - assertThat(textIndexFields).hasSize(4).contains(IndexField.text("textIndexedPropertyWithDefaultWeight", 1F), - IndexField.text("textIndexedPropertyWithWeight", 5F), - IndexField.text("nestedDocument.textIndexedPropertyInNestedDocument", 1F), - IndexField.create("_ftsx", Direction.ASC)); - assertThat(textIndexInfo.getLanguage()).isEqualTo("spanish"); - } - - @Document(language = "spanish", collation = "de_AT") - static class TextIndexedDocumentRoot { - - @TextIndexed String textIndexedPropertyWithDefaultWeight; - @TextIndexed(weight = 5) String textIndexedPropertyWithWeight; - - TextIndexedDocumentWithLanguageOverride nestedDocument; - } - - static class TextIndexedDocumentWithLanguageOverride { - - @Language String lang; - - @TextIndexed String textIndexedPropertyInNestedDocument; - - String nonTextIndexedProperty; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Account.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Account.java deleted file mode 100644 index 82c3f97520..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Account.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.springframework.data.annotation.Id; - -/** - * @author Jon Brisbin - */ -@Document -public class Account { - - @Id - private String id; - private Float balance; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public Float getBalance() { - return balance; - } - - public void setBalance(Float balance) { - this.balance = balance; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/AccountPojo.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/AccountPojo.java deleted file mode 100644 index 307b54ab50..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/AccountPojo.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -/** - * @author Jon Brisbin - */ -public class AccountPojo { - - private String type; - private Float balance; - - public AccountPojo(String type, Float balance) { - this.type = type; - this.balance = balance; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public Float getBalance() { - return balance; - } - - public void setBalance(Float balance) { - this.balance = balance; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Address.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Address.java deleted file mode 100644 index e18605cb96..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Address.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -/** - * @author Jon Brisbin - */ -public class Address implements Comparable
{ - - @SuppressWarnings("unused") private String id; - private String[] lines; - private String city; - private String provinceOrState; - private Integer postalCode; - private String country; - - public String[] getLines() { - return lines; - } - - public void setLines(String[] lines) { - this.lines = lines; - } - - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - public String getProvinceOrState() { - return provinceOrState; - } - - public void setProvinceOrState(String provinceOrState) { - this.provinceOrState = provinceOrState; - } - - public Integer getPostalCode() { - return postalCode; - } - - public void setPostalCode(Integer postalCode) { - this.postalCode = postalCode; - } - - public String getCountry() { - return country; - } - - public void setCountry(String country) { - this.country = country; - } - - public int compareTo(Address address) { - return 0; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasePerson.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasePerson.java deleted file mode 100644 index 818a3c433c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasePerson.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import com.querydsl.core.annotations.QuerySupertype; - -/** - * {@link QuerySupertype} is necessary for Querydsl 2.2.0-beta4 to compile the query classes directly. Can be removed as - * soon as https://bugs.launchpad.net/querydsl/+bug/776219 - * is fixed. - * - * @see https://bugs.launchpad.net/querydsl/+bug/776219 - * @author Jon Brisbin - * @author Oliver Gierke - */ -@QuerySupertype -public abstract class BasePerson { - - protected Integer ssn; - protected String firstName; - protected String lastName; - - public BasePerson() {} - - public BasePerson(Integer ssn, String firstName, String lastName) { - this.ssn = ssn; - this.firstName = firstName; - this.lastName = lastName; - } - - public Integer getSsn() { - return ssn; - } - - public void setSsn(Integer ssn) { - this.ssn = ssn; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntityUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntityUnitTests.java deleted file mode 100644 index 28d5123502..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntityUnitTests.java +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AliasFor; -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.mongodb.core.index.Indexed; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider; -import org.springframework.data.spel.spi.EvaluationContextExtension; -import org.springframework.data.util.ClassTypeInformation; - -/** - * Unit tests for {@link BasicMongoPersistentEntity}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class BasicMongoPersistentEntityUnitTests { - - @Mock ApplicationContext context; - @Mock MongoPersistentProperty propertyMock; - - @Test - void subclassInheritsAtDocumentAnnotation() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(Person.class)); - assertThat(entity.getCollection()).isEqualTo("contacts"); - } - - @Test - void evaluatesSpELExpression() { - - MongoPersistentEntity entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Company.class)); - assertThat(entity.getCollection()).isEqualTo("35"); - } - - @Test // DATAMONGO-65, DATAMONGO-1108 - void collectionAllowsReferencingSpringBean() { - - CollectionProvider provider = new CollectionProvider(); - provider.collectionName = "reference"; - - when(context.getBean("myBean")).thenReturn(provider); - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(DynamicallyMapped.class)); - entity.setEvaluationContextProvider(new ExtensionAwareEvaluationContextProvider(context)); - - assertThat(entity.getCollection()).isEqualTo("reference"); - - provider.collectionName = "otherReference"; - assertThat(entity.getCollection()).isEqualTo("otherReference"); - } - - @Test // DATAMONGO-937 - void shouldDetectLanguageCorrectly() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(DocumentWithLanguage.class)); - - assertThat(entity.getLanguage()).isEqualTo("spanish"); - } - - @Test // DATAMONGO-1053 - void verifyShouldThrowExceptionForInvalidTypeOfExplicitLanguageProperty() { - - doReturn(true).when(propertyMock).isExplicitLanguageProperty(); - doReturn(Number.class).when(propertyMock).getActualType(); - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(AnyDocument.class)); - entity.addPersistentProperty(propertyMock); - - assertThatExceptionOfType(MappingException.class).isThrownBy(entity::verify); - } - - @Test // DATAMONGO-1053 - void verifyShouldPassForStringAsExplicitLanguageProperty() { - - doReturn(true).when(propertyMock).isExplicitLanguageProperty(); - doReturn(String.class).when(propertyMock).getActualType(); - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(AnyDocument.class)); - entity.addPersistentProperty(propertyMock); - - entity.verify(); - - verify(propertyMock, times(1)).isExplicitLanguageProperty(); - verify(propertyMock, times(1)).getActualType(); - } - - @Test // DATAMONGO-1053 - void verifyShouldIgnoreNonExplicitLanguageProperty() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(AnyDocument.class)); - when(propertyMock.isExplicitLanguageProperty()).thenReturn(false); - entity.addPersistentProperty(propertyMock); - - entity.verify(); - - verify(propertyMock, times(1)).isExplicitLanguageProperty(); - verify(propertyMock, never()).getActualType(); - } - - @Test // DATAMONGO-1157 - void verifyShouldThrowErrorForLazyDBRefOnFinalClass() { - - org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock( - org.springframework.data.mongodb.core.mapping.DBRef.class); - - doReturn(Class.class).when(propertyMock).getActualType(); - doReturn(true).when(propertyMock).isDbReference(); - doReturn(dbRefMock).when(propertyMock).getDBRef(); - doReturn(true).when(dbRefMock).lazy(); - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(AnyDocument.class)); - entity.addPersistentProperty(propertyMock); - - assertThatExceptionOfType(MappingException.class).isThrownBy(entity::verify); - } - - @Test // DATAMONGO-1157 - void verifyShouldThrowErrorForLazyDBRefArray() { - - org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock( - org.springframework.data.mongodb.core.mapping.DBRef.class); - - doReturn(true).when(propertyMock).isDbReference(); - doReturn(true).when(propertyMock).isArray(); - doReturn(dbRefMock).when(propertyMock).getDBRef(); - doReturn(true).when(dbRefMock).lazy(); - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(AnyDocument.class)); - entity.addPersistentProperty(propertyMock); - - assertThatExceptionOfType(MappingException.class).isThrownBy(entity::verify); - } - - @Test // DATAMONGO-1157 - void verifyShouldPassForLazyDBRefOnNonArrayNonFinalClass() { - - org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock( - org.springframework.data.mongodb.core.mapping.DBRef.class); - - doReturn(true).when(propertyMock).isDbReference(); - doReturn(Object.class).when(propertyMock).getActualType(); - doReturn(dbRefMock).when(propertyMock).getDBRef(); - doReturn(true).when(dbRefMock).lazy(); - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(AnyDocument.class)); - entity.addPersistentProperty(propertyMock); - entity.verify(); - - verify(propertyMock, times(1)).isDbReference(); - } - - @Test // DATAMONGO-1157 - void verifyShouldPassForNonLazyDBRefOnFinalClass() { - - org.springframework.data.mongodb.core.mapping.DBRef dbRefMock = mock( - org.springframework.data.mongodb.core.mapping.DBRef.class); - - doReturn(true).when(propertyMock).isDbReference(); - doReturn(dbRefMock).when(propertyMock).getDBRef(); - doReturn(false).when(dbRefMock).lazy(); - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(AnyDocument.class)); - entity.addPersistentProperty(propertyMock); - entity.verify(); - - verify(dbRefMock, times(1)).lazy(); - } - - @Test // DATAMONGO-1291 - void metaInformationShouldBeReadCorrectlyFromInheritedDocumentAnnotation() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(DocumentWithCustomAnnotation.class)); - - assertThat(entity.getCollection()).isEqualTo("collection-1"); - } - - @Test // DATAMONGO-1373 - void metaInformationShouldBeReadCorrectlyFromComposedDocumentAnnotation() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(DocumentWithComposedAnnotation.class)); - - assertThat(entity.getCollection()).isEqualTo("custom-collection"); - } - - @Test // DATAMONGO-1874 - void usesEvaluationContextExtensionInDynamicDocumentName() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(MappedWithExtension.class)); - entity.setEvaluationContextProvider( - new ExtensionAwareEvaluationContextProvider(Collections.singletonList(new SampleExtension()))); - - assertThat(entity.getCollection()).isEqualTo("collectionName"); - } - - @Test // DATAMONGO-1854 - void readsSimpleCollation() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(WithSimpleCollation.class)); - - assertThat(entity.getCollation()).isEqualTo(org.springframework.data.mongodb.core.query.Collation.of("en_US")); - } - - @Test // DATAMONGO-1854 - void readsDocumentCollation() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(WithDocumentCollation.class)); - - assertThat(entity.getCollation()).isEqualTo(org.springframework.data.mongodb.core.query.Collation.of("en_US")); - } - - @Test // DATAMONGO-2565 - void usesCorrectExpressionsForCollectionAndCollation() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(WithCollectionAndCollationFromSpEL.class)); - entity.setEvaluationContextProvider( - new ExtensionAwareEvaluationContextProvider(Collections.singletonList(new SampleExtension()))); - - assertThat(entity.getCollection()).isEqualTo("collectionName"); - assertThat(entity.getCollation()).isEqualTo(Collation.of("en_US")); - } - - @Test // DATAMONGO-2341 - void detectsShardedEntityCorrectly() { - - assertThat(entityOf(WithDefaultShardKey.class).isSharded()).isTrue(); - assertThat(entityOf(Contact.class).isSharded()).isFalse(); - } - - @Test // DATAMONGO-2341 - void readsDefaultShardKey() { - - assertThat(entityOf(WithDefaultShardKey.class).getShardKey().getDocument()) - .isEqualTo(new org.bson.Document("_id", 1)); - } - - @Test // DATAMONGO-2341 - void readsSingleShardKey() { - - assertThat(entityOf(WithSingleShardKey.class).getShardKey().getDocument()) - .isEqualTo(new org.bson.Document("country", 1)); - } - - @Test // DATAMONGO-2341 - void readsMultiShardKey() { - - assertThat(entityOf(WithMultiShardKey.class).getShardKey().getDocument()) - .isEqualTo(new org.bson.Document("country", 1).append("userid", 1)); - } - - static BasicMongoPersistentEntity entityOf(Class type) { - return new BasicMongoPersistentEntity<>(ClassTypeInformation.from(type)); - } - - @Document("contacts") - class Contact {} - - class Person extends Contact {} - - @Document("#{35}") - class Company {} - - @Document("#{@myBean.collectionName}") - class DynamicallyMapped {} - - class CollectionProvider { - String collectionName; - - public String getCollectionName() { - return collectionName; - } - } - - @Document(language = "spanish") - static class DocumentWithLanguage {} - - private static class AnyDocument {} - - @CustomDocumentAnnotation - private static class DocumentWithCustomAnnotation {} - - @ComposedDocumentAnnotation - private static class DocumentWithComposedAnnotation {} - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.TYPE }) - @Document("collection-1") - static @interface CustomDocumentAnnotation { - } - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.TYPE }) - @Document - static @interface ComposedDocumentAnnotation { - - @AliasFor(annotation = Document.class, attribute = "collection") - String name() default "custom-collection"; - } - - // DATAMONGO-1874 - @Document("#{myProperty}") - class MappedWithExtension {} - - @Document(collation = "#{myCollation}") - class WithCollationFromSpEL {} - - @Document(collection = "#{myProperty}", collation = "#{myCollation}") - class WithCollectionAndCollationFromSpEL {} - - @Document(collation = "en_US") - class WithSimpleCollation {} - - @Document(collation = "{ 'locale' : 'en_US' }") - class WithDocumentCollation {} - - @Sharded - private class WithDefaultShardKey {} - - @Sharded("country") - private class WithSingleShardKey {} - - @Sharded({ "country", "userid" }) - private class WithMultiShardKey {} - - static class SampleExtension implements EvaluationContextExtension { - - /* - * (non-Javadoc) - * @see org.springframework.data.spel.spi.EvaluationContextExtension#getExtensionId() - */ - @Override - public String getExtensionId() { - return "sampleExtension"; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.spel.spi.EvaluationContextExtension#getProperties() - */ - @Override - public Map getProperties() { - - Map properties = new LinkedHashMap<>(); - properties.put("myProperty", "collectionName"); - properties.put("myCollation", "en_US"); - return properties; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java deleted file mode 100644 index 3fb4f59084..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import static org.assertj.core.api.Assertions.*; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.core.annotation.AliasFor; -import org.springframework.data.annotation.Id; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.model.FieldNamingStrategy; -import org.springframework.data.mapping.model.Property; -import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; -import org.springframework.data.mapping.model.SimpleTypeHolder; -import org.springframework.data.util.ClassTypeInformation; -import org.springframework.util.ReflectionUtils; - -/** - * Unit test for {@link BasicMongoPersistentProperty}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - */ -public class BasicMongoPersistentPropertyUnitTests { - - MongoPersistentEntity entity; - - @BeforeEach - public void setup() { - entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Person.class)); - } - - @Test - public void usesAnnotatedFieldName() { - - Field field = ReflectionUtils.findField(Person.class, "firstname"); - assertThat(getPropertyFor(field).getFieldName()).isEqualTo("foo"); - } - - @Test - public void returns_IdForIdProperty() { - Field field = ReflectionUtils.findField(Person.class, "id"); - MongoPersistentProperty property = getPropertyFor(field); - assertThat(property.isIdProperty()).isTrue(); - assertThat(property.getFieldName()).isEqualTo("_id"); - } - - @Test - public void returnsPropertyNameForUnannotatedProperties() { - - Field field = ReflectionUtils.findField(Person.class, "lastname"); - assertThat(getPropertyFor(field).getFieldName()).isEqualTo("lastname"); - } - - @Test - public void preventsNegativeOrder() { - getPropertyFor(ReflectionUtils.findField(Person.class, "ssn")); - } - - @Test // DATAMONGO-553 - public void usesPropertyAccessForThrowableCause() { - - BasicMongoPersistentEntity entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(Throwable.class)); - MongoPersistentProperty property = getPropertyFor(entity, "cause"); - - assertThat(property.usePropertyAccess()).isTrue(); - } - - @Test // DATAMONGO-607 - public void usesCustomFieldNamingStrategyByDefault() throws Exception { - - ClassTypeInformation type = ClassTypeInformation.from(Person.class); - Field field = ReflectionUtils.findField(Person.class, "lastname"); - - MongoPersistentProperty property = new BasicMongoPersistentProperty(Property.of(type, field), entity, - SimpleTypeHolder.DEFAULT, UppercaseFieldNamingStrategy.INSTANCE); - assertThat(property.getFieldName()).isEqualTo("LASTNAME"); - - field = ReflectionUtils.findField(Person.class, "firstname"); - - property = new BasicMongoPersistentProperty(Property.of(type, field), entity, SimpleTypeHolder.DEFAULT, - UppercaseFieldNamingStrategy.INSTANCE); - assertThat(property.getFieldName()).isEqualTo("foo"); - } - - @Test // DATAMONGO-607 - public void rejectsInvalidValueReturnedByFieldNamingStrategy() { - - ClassTypeInformation type = ClassTypeInformation.from(Person.class); - Field field = ReflectionUtils.findField(Person.class, "lastname"); - - MongoPersistentProperty property = new BasicMongoPersistentProperty(Property.of(type, field), entity, - SimpleTypeHolder.DEFAULT, InvalidFieldNamingStrategy.INSTANCE); - - assertThatExceptionOfType(MappingException.class).isThrownBy(property::getFieldName) - .withMessageContaining(InvalidFieldNamingStrategy.class.getName()).withMessageContaining(property.toString()); - } - - @Test // DATAMONGO-937 - public void shouldDetectAnnotatedLanguagePropertyCorrectly() { - - MongoPersistentProperty property = getPropertyFor(DocumentWithLanguageProperty.class, "lang"); - assertThat(property.isLanguageProperty()).isTrue(); - } - - @Test // DATAMONGO-937 - public void shouldDetectImplicitLanguagePropertyCorrectly() { - - MongoPersistentProperty property = getPropertyFor(DocumentWithImplicitLanguageProperty.class, "language"); - assertThat(property.isLanguageProperty()).isTrue(); - } - - @Test // DATAMONGO-976 - public void shouldDetectTextScorePropertyCorrectly() { - - MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score"); - assertThat(property.isTextScoreProperty()).isTrue(); - } - - @Test // DATAMONGO-976 - public void shouldDetectTextScoreAsReadOnlyProperty() { - - MongoPersistentProperty property = getPropertyFor(DocumentWithTextScoreProperty.class, "score"); - assertThat(property.isWritable()).isFalse(); - } - - @Test // DATAMONGO-1050 - public void shouldNotConsiderExplicitlyNameFieldAsIdProperty() { - - MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdProperty.class, "id"); - assertThat(property.isIdProperty()).isFalse(); - } - - @Test // DATAMONGO-1050 - public void shouldConsiderPropertyAsIdWhenExplicitlyAnnotatedWithIdEvenWhenExplicitlyNamePresent() { - - MongoPersistentProperty property = getPropertyFor(DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation.class, - "id"); - assertThat(property.isIdProperty()).isTrue(); - } - - @Test // DATAMONGO-1373 - public void shouldConsiderComposedAnnotationsForIdField() { - - MongoPersistentProperty property = getPropertyFor(DocumentWithComposedAnnotations.class, "myId"); - assertThat(property.isIdProperty()).isTrue(); - assertThat(property.getFieldName()).isEqualTo("_id"); - } - - @Test // DATAMONGO-1373 - public void shouldConsiderComposedAnnotationsForFields() { - - MongoPersistentProperty property = getPropertyFor(DocumentWithComposedAnnotations.class, "myField"); - assertThat(property.getFieldName()).isEqualTo("myField"); - } - - @Test // DATAMONGO-1737 - public void honorsFieldOrderWhenIteratingOverProperties() { - - MongoMappingContext context = new MongoMappingContext(); - MongoPersistentEntity entity = context.getPersistentEntity(Sample.class); - - List properties = new ArrayList<>(); - - entity.doWithProperties((MongoPersistentProperty property) -> properties.add(property.getName())); - - assertThat(properties).containsExactly("first", "second", "third"); - } - - @Test // DATAMONGO-1798 - public void fieldTypeShouldReturnActualTypeForNonIdProperties() { - - MongoPersistentProperty property = getPropertyFor(Person.class, "lastname"); - assertThat(property.getFieldType()).isEqualTo(String.class); - } - - @Test // DATAMONGO-1798 - public void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithCommonsId() { - - MongoPersistentProperty property = getPropertyFor(Person.class, "id"); - assertThat(property.getFieldType()).isEqualTo(ObjectId.class); - } - - @Test // DATAMONGO-1798 - public void fieldTypeShouldBeImplicitForPropertiesAnnotatedWithMongoId() { - - MongoPersistentProperty property = getPropertyFor(WithStringMongoId.class, "id"); - assertThat(property.getFieldType()).isEqualTo(String.class); - } - - @Test // DATAMONGO-1798 - public void fieldTypeShouldBeObjectIdForPropertiesAnnotatedWithMongoIdAndTargetTypeObjectId() { - - MongoPersistentProperty property = getPropertyFor(WithStringMongoIdMappedToObjectId.class, "id"); - assertThat(property.getFieldType()).isEqualTo(ObjectId.class); - } - - @Test // DATAMONGO-2460 - public void fieldTypeShouldBeDocumentForPropertiesAnnotatedIdWhenAComplexTypeAndFieldTypeImplicit() { - - MongoPersistentProperty property = getPropertyFor(WithComplexId.class, "id"); - assertThat(property.getFieldType()).isEqualTo(Document.class); - } - - private MongoPersistentProperty getPropertyFor(Field field) { - return getPropertyFor(entity, field); - } - - private static MongoPersistentProperty getPropertyFor(Class type, String fieldname) { - return getPropertyFor(new BasicMongoPersistentEntity<>(ClassTypeInformation.from(type)), fieldname); - } - - private static MongoPersistentProperty getPropertyFor(MongoPersistentEntity entity, String fieldname) { - return getPropertyFor(entity, ReflectionUtils.findField(entity.getType(), fieldname)); - } - - private static MongoPersistentProperty getPropertyFor(MongoPersistentEntity entity, Field field) { - return new BasicMongoPersistentProperty(Property.of(entity.getTypeInformation(), field), entity, - SimpleTypeHolder.DEFAULT, PropertyNameFieldNamingStrategy.INSTANCE); - } - - class Person { - - @Id String id; - - @org.springframework.data.mongodb.core.mapping.Field("foo") String firstname; - String lastname; - - @org.springframework.data.mongodb.core.mapping.Field(order = -20) String ssn; - } - - class Sample { - - @org.springframework.data.mongodb.core.mapping.Field(order = 2) String second; - @org.springframework.data.mongodb.core.mapping.Field(order = 3) String third; - @org.springframework.data.mongodb.core.mapping.Field(order = 1) String first; - } - - enum UppercaseFieldNamingStrategy implements FieldNamingStrategy { - - INSTANCE; - - public String getFieldName(PersistentProperty property) { - return property.getName().toUpperCase(Locale.US); - } - } - - enum InvalidFieldNamingStrategy implements FieldNamingStrategy { - - INSTANCE; - - public String getFieldName(PersistentProperty property) { - return null; - } - } - - static class DocumentWithLanguageProperty { - - @Language String lang; - } - - static class DocumentWithImplicitLanguageProperty { - - String language; - } - - static class DocumentWithTextScoreProperty { - @TextScore Float score; - } - - static class DocumentWithExplicitlyRenamedIdProperty { - - @org.springframework.data.mongodb.core.mapping.Field("id") String id; - } - - static class DocumentWithExplicitlyRenamedIdPropertyHavingIdAnnotation { - - @Id @org.springframework.data.mongodb.core.mapping.Field("id") String id; - } - - static class DocumentWithComposedAnnotations { - - @ComposedIdAnnotation @ComposedFieldAnnotation String myId; - @ComposedFieldAnnotation(name = "myField") String myField; - } - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - @org.springframework.data.mongodb.core.mapping.Field - static @interface ComposedFieldAnnotation { - - @AliasFor(annotation = org.springframework.data.mongodb.core.mapping.Field.class, attribute = "value") - String name() default "_id"; - } - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - @Id - static @interface ComposedIdAnnotation { - } - - static class WithStringMongoId { - - @MongoId String id; - } - - static class WithStringMongoIdMappedToObjectId { - - @MongoId(FieldType.OBJECT_ID) String id; - } - - static class ComplexId { - - String value; - } - - static class WithComplexId { - - @Id @org.springframework.data.mongodb.core.mapping.Field ComplexId id; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/CustomCollectionWithIndex.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/CustomCollectionWithIndex.java deleted file mode 100644 index 7530b21732..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/CustomCollectionWithIndex.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.index.Indexed; - -/** - * @author Jon Brisbin - */ -@Document("foobar") -public class CustomCollectionWithIndex { - - @Id private String id; - @Indexed private String name; - - public CustomCollectionWithIndex(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/DetectedCollectionWithIndex.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/DetectedCollectionWithIndex.java deleted file mode 100644 index 345fd69724..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/DetectedCollectionWithIndex.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.index.Indexed; - -/** - * @author Jon Brisbin - */ -@Document -public class DetectedCollectionWithIndex { - - @Id private String id; - @Indexed private String name; - - public DetectedCollectionWithIndex(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeneratedId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeneratedId.java deleted file mode 100644 index 2ba14f4f52..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeneratedId.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.bson.types.ObjectId; - -import org.springframework.data.annotation.Id; - -/** - * @author Jon Brisbin - */ -@Document -public class GeneratedId { - - @Id private ObjectId id; - @SuppressWarnings("unused") private String name; - - public GeneratedId(String name) { - this.name = name; - } - - public ObjectId getId() { - return id; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GenericMappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GenericMappingTests.java deleted file mode 100644 index 7107de4572..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GenericMappingTests.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Collections; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; - -/** - * Unit tests for testing the mapping works with generic types. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -class GenericMappingTests { - - private MongoMappingContext context; - private MongoConverter converter; - - @Mock DbRefResolver resolver; - - @BeforeEach - void setUp() throws Exception { - - context = new MongoMappingContext(); - context.setInitialEntitySet(Collections.singleton(StringWrapper.class)); - context.initialize(); - - converter = new MappingMongoConverter(resolver, context); - } - - @Test - void writesGenericTypeCorrectly() { - - StringWrapper wrapper = new StringWrapper(); - wrapper.container = new Container(); - wrapper.container.content = "Foo!"; - - Document document = new Document(); - converter.write(wrapper, document); - - Object container = document.get("container"); - assertThat(container).isNotNull(); - assertThat(container instanceof Document).isTrue(); - - Object content = ((Document) container).get("content"); - assertThat(content instanceof String).isTrue(); - assertThat((String) content).isEqualTo("Foo!"); - } - - @Test - void readsGenericTypeCorrectly() { - - Document content = new Document("content", "Foo!"); - Document container = new Document("container", content); - - StringWrapper result = converter.read(StringWrapper.class, container); - assertThat(result.container).isNotNull(); - assertThat(result.container.content).isEqualTo("Foo!"); - } - - private static class StringWrapper extends Wrapper { - - } - - static class Wrapper { - Container container; - } - - static class Container { - T content; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoIndexedAppConfig.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoIndexedAppConfig.java deleted file mode 100644 index c18073d2b2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoIndexedAppConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import java.util.Collections; -import java.util.Set; - -import org.springframework.context.annotation.Bean; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.mapping.event.LoggingEventListener; -import org.springframework.data.mongodb.test.util.MongoTestUtils; - -import com.mongodb.client.MongoClient; - -public class GeoIndexedAppConfig extends AbstractMongoClientConfiguration { - - public static String GEO_DB = "database"; - public static String GEO_COLLECTION = "geolocation"; - - @Override - public String getDatabaseName() { - return GEO_DB; - } - - @Override - @Bean - public MongoClient mongoClient() { - return MongoTestUtils.client(); - } - - @Override - public String getMappingBasePackage() { - return "org.springframework.data.mongodb.core.core.mapping"; - } - - @Bean - public LoggingEventListener mappingEventsListener() { - return new LoggingEventListener(); - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - - @Override - protected boolean autoIndexCreation() { - return true; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoIndexedTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoIndexedTests.java deleted file mode 100644 index 171aa099a4..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoIndexedTests.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.List; - -import org.bson.Document; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.dao.DataAccessException; -import org.springframework.data.mongodb.core.CollectionCallback; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.MongoException; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; - -/** - * @author Jon Brisbin - * @author Oliver Gierke - * @author Christoph Strobl - */ -@RunWith(SpringRunner.class) -@ContextConfiguration(classes = GeoIndexedAppConfig.class) -public class GeoIndexedTests { - - private final String[] collectionsToDrop = new String[] { GeoIndexedAppConfig.GEO_COLLECTION, "Person" }; - - @Autowired ApplicationContext applicationContext; - @Autowired MongoTemplate template; - @Autowired MongoMappingContext mappingContext; - - @Before - public void setUp() { - cleanDb(); - } - - @After - public void cleanUp() { - cleanDb(); - } - - private void cleanDb() { - - try (MongoClient mongo = MongoTestUtils.client()) { - - MongoDatabase db = mongo.getDatabase(GeoIndexedAppConfig.GEO_DB); - - for (String coll : collectionsToDrop) { - db.getCollection(coll).drop(); - } - } - } - - @Test - public void testGeoLocation() { - - GeoLocation geo = new GeoLocation(new double[] { 40.714346, -74.005966 }); - template.insert(geo); - - boolean hasIndex = template.execute("geolocation", new CollectionCallback() { - public Boolean doInCollection(MongoCollection collection) throws MongoException, DataAccessException { - - List indexes = new ArrayList(); - collection.listIndexes(Document.class).into(indexes); - - for (Document document : indexes) { - if ("location".equals(document.get("name"))) { - return true; - } - } - return false; - } - }); - - assertThat(hasIndex).isTrue(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoLocation.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoLocation.java deleted file mode 100644 index 74f466fb2b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/GeoLocation.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.bson.types.ObjectId; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; - -/** - * @author Jon Brisbin - */ -@Document("geolocation") -public class GeoLocation { - - @Id private ObjectId id; - @GeoSpatialIndexed private double[] location; - - public GeoLocation(double[] location) { - this.location = location; - } - - public ObjectId getId() { - return id; - } - - public double[] getLocation() { - return location; - } - - public void setLocation(double[] location) { - this.location = location; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Location.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Location.java deleted file mode 100644 index 8b4800c33c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Location.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.bson.types.ObjectId; - -/** - * @author Jon Brisbin - */ -@Document("places") -public class Location { - - private ObjectId id; - private double[] latlong; - private int[] numbers; - private float[] amounts; - - public Location(double[] latlong, int[] numbers, float[] amounts) { - this.latlong = latlong; - this.numbers = numbers; - this.amounts = amounts; - } - - public ObjectId getId() { - return id; - } - - public double[] getLatlong() { - return latlong; - } - - public void setLatlong(double[] latlong) { - this.latlong = latlong; - } - - public int[] getNumbers() { - return numbers; - } - - public void setNumbers(int[] numbers) { - this.numbers = numbers; - } - - public float[] getAmounts() { - return amounts; - } - - public void setAmounts(float[] amounts) { - this.amounts = amounts; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java deleted file mode 100644 index ed35a2e49d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MappingTests.java +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.core.query.Update.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.MongoCollectionUtils; -import org.springframework.data.mongodb.core.CollectionCallback; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -import com.mongodb.MongoException; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; - -/** - * @author Jon Brisbin - * @author Oliver Gierke - * @author Thomas Darimont - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -public class MappingTests { - - static final String DB_NAME = "mapping-tests"; - - static @Client MongoClient client; - - @Template(database = DB_NAME, - initialEntitySet = { PersonWithDbRef.class, GeoLocation.class, PersonPojoStringId.class, Account.class, - DetectedCollectionWithIndex.class, Item.class, Container.class, Person.class, PersonCustomCollection1.class, - GeneratedId.class, PersonWithObjectId.class, PersonCustomIdName.class, PersonMapProperty.class }) // - static MongoTestTemplate template; - - @AfterEach - void afterEach() { - template.flush(); - } - - @Test - public void testGeneratedId() { - GeneratedId genId = new GeneratedId("test"); - template.insert(genId); - - assertThat(genId.getId()).isNotNull(); - } - - @Test - public void testPersonPojo() throws Exception { - - PersonWithObjectId p = new PersonWithObjectId(12345, "Person", "Pojo"); - template.insert(p); - assertThat(p.getId()).isNotNull(); - - List result = template.find(new Query(Criteria.where("ssn").is(12345)), - PersonWithObjectId.class); - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).getSsn()).isEqualTo(12345); - } - - @Test - public void testPersonWithCustomIdName() { - - PersonCustomIdName p = new PersonCustomIdName(123456, "Custom Id", null); - template.insert(p); - - List result = template.find(new Query(Criteria.where("lastName").is(p.getLastName())), - PersonCustomIdName.class); - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).getFirstName()).isEqualTo("Custom Id"); - - PersonCustomIdName p2 = new PersonCustomIdName(654321, "Custom Id", "LastName"); - template.insert(p2); - - List result2 = template.find(new Query(Criteria.where("lastName").is("LastName")), - PersonCustomIdName.class); - assertThat(result2.size()).isEqualTo(1); - assertThat(result2.get(0).getLastName()).isNotNull(); - assertThat(result2.get(0).getLastName()).isEqualTo("LastName"); - - // Test "in" query - List result3 = template.find(new Query(Criteria.where("lastName").in("LastName")), - PersonCustomIdName.class); - assertThat(result3.size()).isEqualTo(1); - assertThat(result3.get(0).getLastName()).isNotNull(); - assertThat(result3.get(0).getLastName()).isEqualTo("LastName"); - } - - @Test - public void testPersonMapProperty() { - PersonMapProperty p = new PersonMapProperty(1234567, "Map", "PropertyPath"); - Map accounts = new HashMap(); - - AccountPojo checking = new AccountPojo("checking", 1000.0f); - AccountPojo savings = new AccountPojo("savings", 10000.0f); - - accounts.put("checking", checking); - accounts.put("savings", savings); - p.setAccounts(accounts); - - template.insert(p); - assertThat(p.getId()).isNotNull(); - - List result = template.find(new Query(Criteria.where("ssn").is(1234567)), - PersonMapProperty.class); - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).getAccounts().size()).isEqualTo(2); - assertThat(result.get(0).getAccounts().get("checking").getBalance()).isEqualTo(1000.0f); - } - - @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void testWriteEntity() { - - Address addr = new Address(); - addr.setLines(new String[] { "1234 W. 1st Street", "Apt. 12" }); - addr.setCity("Anytown"); - addr.setPostalCode(12345); - addr.setCountry("USA"); - - Account acct = new Account(); - acct.setBalance(1000.00f); - template.insert(acct, "account"); - - List accounts = new ArrayList(); - accounts.add(acct); - - Person p = new Person(123456789, "John", "Doe", 37, addr); - p.setAccounts(accounts); - template.insert(p, "person"); - - Account newAcct = new Account(); - newAcct.setBalance(10000.00f); - template.insert(newAcct, "account"); - - accounts.add(newAcct); - template.save(p, "person"); - - assertThat(p.getId()).isNotNull(); - - List result = template.find(new Query(Criteria.where("ssn").is(123456789)), Person.class); - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).getAddress().getCountry()).isEqualTo("USA"); - assertThat(result.get(0).getAccounts()).isNotNull(); - } - - @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void testUniqueIndex() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setAutoIndexCreation(true); - - MongoTemplate template = new MongoTemplate(new SimpleMongoClientDatabaseFactory(client, DB_NAME), new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext)); - - Address addr = new Address(); - addr.setLines(new String[] { "1234 W. 1st Street", "Apt. 12" }); - addr.setCity("Anytown"); - addr.setPostalCode(12345); - addr.setCountry("USA"); - - Person p1 = new Person(1234567890, "John", "Doe", 37, addr); - Person p2 = new Person(1234567890, "Jane", "Doe", 38, addr); - - assertThatExceptionOfType(DuplicateKeyException.class).isThrownBy(() -> template.insertAll(Arrays.asList(p1, p2))); - } - - @Test - public void testCustomCollectionInList() { - List persons = new ArrayList(); - persons.add(new PersonCustomCollection1(55555, "Person", "One")); - persons.add(new PersonCustomCollection2(66666, "Person", "Two")); - template.insertAll(persons); - - List p1Results = template.find(new Query(Criteria.where("ssn").is(55555)), - PersonCustomCollection1.class, "person1"); - List p2Results = template.find(new Query(Criteria.where("ssn").is(66666)), - PersonCustomCollection2.class, "person2"); - assertThat(p1Results.size()).isEqualTo(1); - assertThat(p2Results.size()).isEqualTo(1); - } - - @Test - public void testPrimitivesAndCustomCollectionName() { - Location loc = new Location(new double[] { 1.0, 2.0 }, new int[] { 1, 2, 3, 4 }, new float[] { 1.0f, 2.0f }); - template.insert(loc); - - List result = template.find(new Query(Criteria.where("_id").is(loc.getId())), Location.class, "places"); - assertThat(result.size()).isEqualTo(1); - } - - @Test - public void testIndexesCreatedInRightCollection() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.setAutoIndexCreation(true); - - MongoTemplate template = new MongoTemplate(new SimpleMongoClientDatabaseFactory(client, DB_NAME), new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext)); - - - CustomCollectionWithIndex ccwi = new CustomCollectionWithIndex("test"); - template.insert(ccwi); - - assertThat(template.execute("foobar", new CollectionCallback() { - public Boolean doInCollection(MongoCollection collection) throws MongoException, DataAccessException { - - List indexes = new ArrayList(); - collection.listIndexes(Document.class).into(indexes); - - for (Document document : indexes) { - if (document.get("name") != null && document.get("name") instanceof String - && ((String) document.get("name")).startsWith("name")) { - return true; - } - } - return false; - } - })).isTrue(); - - DetectedCollectionWithIndex dcwi = new DetectedCollectionWithIndex("test"); - template.insert(dcwi); - - assertThat(template.execute(MongoCollectionUtils.getPreferredCollectionName(DetectedCollectionWithIndex.class), - new CollectionCallback() { - public Boolean doInCollection(MongoCollection collection) - throws MongoException, DataAccessException { - - List indexes = new ArrayList(); - collection.listIndexes(Document.class).into(indexes); - - for (Document document : indexes) { - if (document.get("name") != null && document.get("name") instanceof String - && ((String) document.get("name")).startsWith("name")) { - return true; - } - } - return false; - } - })).isTrue(); - } - - @Test - public void testMultiDimensionalArrayProperties() { - String[][] grid = new String[][] { new String[] { "1", "2", "3", "4" }, new String[] { "5", "6", "7", "8" }, - new String[] { "9", "10", "11", "12" } }; - PersonMultiDimArrays p = new PersonMultiDimArrays(123, "Multi", "Dimensional", grid); - - template.insert(p); - List result = template.find(new Query(Criteria.where("ssn").is(123)), - PersonMultiDimArrays.class); - assertThat(result.size()).isEqualTo(1); - - assertThat(result.get(0).getGrid().length).isEqualTo(3); - } - - @Test - public void testMultiDimensionalCollectionProperties() { - List> grid = new ArrayList>(); - ArrayList inner = new ArrayList(); - inner.add("1"); - inner.add("2"); - inner.add("3"); - inner.add("4"); - grid.add(inner); - - PersonMultiCollection p = new PersonMultiCollection(321, "Multi Dim", "Collections", grid); - template.insert(p); - - List result = template.find(new Query(Criteria.where("ssn").is(321)), - PersonMultiCollection.class); - assertThat(result.size()).isEqualTo(1); - - assertThat(result.get(0).getGrid().size()).isEqualTo(1); - } - - @Test - public void testDbRef() { - double[] pos = new double[] { 37.0625, -95.677068 }; - GeoLocation geo = new GeoLocation(pos); - template.insert(geo); - - PersonWithDbRef p = new PersonWithDbRef(4321, "With", "DBRef", geo); - template.insert(p); - - List result = template.find(new Query(Criteria.where("ssn").is(4321)), PersonWithDbRef.class); - assertThat(result.size()).isEqualTo(1); - assertThat(result.get(0).getHome().getLocation()).isEqualTo(pos); - } - - @Test - public void testPersonWithNullProperties() { - PersonNullProperties p = new PersonNullProperties(); - template.insert(p); - - assertThat(p.getId()).isNotNull(); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void testQueryUpdate() { - Address addr = new Address(); - addr.setLines(new String[] { "1234 W. 1st Street", "Apt. 12" }); - addr.setCity("Anytown"); - addr.setPostalCode(12345); - addr.setCountry("USA"); - - Person p = new Person(1111, "Query", "Update", 37, addr); - template.insert(p); - - addr.setCity("New Town"); - template.updateFirst(query(where("ssn").is(1111)), update("address", addr), Person.class); - - Person p2 = template.findOne(query(where("ssn").is(1111)), Person.class); - assertThat(p2.getAddress().getCity()).isEqualTo("New Town"); - } - - @Test - @SuppressWarnings("rawtypes") - public void testUpsert() { - Address addr = new Address(); - addr.setLines(new String[] { "1234 W. 1st Street", "Apt. 12" }); - addr.setCity("Anytown"); - addr.setPostalCode(12345); - addr.setCountry("USA"); - - Person p2 = template.findOne(query(where("ssn").is(1111)), Person.class); - assertThat(p2).isNull(); - - template.upsert(query(where("ssn").is(1111).and("firstName").is("Query").and("lastName").is("Update")), - update("address", addr), Person.class); - - p2 = template.findOne(query(where("ssn").is(1111)), Person.class); - assertThat(p2.getAddress().getCity()).isEqualTo("Anytown"); - - template.dropCollection(Person.class); - template.upsert(query(where("ssn").is(1111).and("firstName").is("Query").and("lastName").is("Update")), - update("address", addr), "person"); - p2 = template.findOne(query(where("ssn").is(1111)), Person.class); - assertThat(p2.getAddress().getCity()).isEqualTo("Anytown"); - - } - - @Test - public void testOrQuery() { - PersonWithObjectId p1 = new PersonWithObjectId(1, "first", ""); - template.save(p1); - PersonWithObjectId p2 = new PersonWithObjectId(2, "second", ""); - template.save(p2); - - List results = template - .find(new Query(new Criteria().orOperator(where("ssn").is(1), where("ssn").is(2))), PersonWithObjectId.class); - - assertThat(results).isNotNull(); - assertThat(results.size()).isEqualTo(2); - assertThat(results.get(1).getSsn()).isEqualTo(2); - } - - @Test - public void testPrimitivesAsIds() { - PrimitiveId p = new PrimitiveId(1); - p.setText("test text"); - - template.save(p); - - PrimitiveId p2 = template.findOne(query(where("id").is(1)), PrimitiveId.class); - assertThat(p2).isNotNull(); - } - - @Test - public void testNoMappingAnnotationsUsingIntAsId() { - PersonPojoIntId p = new PersonPojoIntId(1, "Text"); - template.insert(p); - template.updateFirst(query(where("id").is(1)), update("text", "New Text"), PersonPojoIntId.class); - - PersonPojoIntId p2 = template.findOne(query(where("id").is(1)), PersonPojoIntId.class); - assertThat(p2.getText()).isEqualTo("New Text"); - - p.setText("Different Text"); - template.save(p); - - PersonPojoIntId p3 = template.findOne(query(where("id").is(1)), PersonPojoIntId.class); - assertThat(p3.getText()).isEqualTo("Different Text"); - - } - - @Test - public void testNoMappingAnnotationsUsingLongAsId() { - PersonPojoLongId p = new PersonPojoLongId(1, "Text"); - template.insert(p); - template.updateFirst(query(where("id").is(1)), update("text", "New Text"), PersonPojoLongId.class); - - PersonPojoLongId p2 = template.findOne(query(where("id").is(1)), PersonPojoLongId.class); - assertThat(p2.getText()).isEqualTo("New Text"); - - p.setText("Different Text"); - template.save(p); - - PersonPojoLongId p3 = template.findOne(query(where("id").is(1)), PersonPojoLongId.class); - assertThat(p3.getText()).isEqualTo("Different Text"); - - } - - @Test - public void testNoMappingAnnotationsUsingStringAsId() { - // Assign the String Id in code - PersonPojoStringId p = new PersonPojoStringId("1", "Text"); - template.insert(p); - template.updateFirst(query(where("id").is("1")), update("text", "New Text"), PersonPojoStringId.class); - - PersonPojoStringId p2 = template.findOne(query(where("id").is("1")), PersonPojoStringId.class); - assertThat(p2.getText()).isEqualTo("New Text"); - - p.setText("Different Text"); - template.save(p); - - PersonPojoStringId p3 = template.findOne(query(where("id").is("1")), PersonPojoStringId.class); - assertThat(p3.getText()).isEqualTo("Different Text"); - - PersonPojoStringId p4 = new PersonPojoStringId("2", "Text-2"); - template.insert(p4); - - Query q = query(where("id").in("1", "2")); - q.with(Sort.by(Direction.ASC, "id")); - List people = template.find(q, PersonPojoStringId.class); - assertThat(people.size()).isEqualTo(2); - - } - - @Test - public void testPersonWithLongDBRef() { - PersonPojoLongId personPojoLongId = new PersonPojoLongId(12L, "PersonWithLongDBRef"); - template.insert(personPojoLongId); - - PersonWithLongDBRef personWithLongDBRef = new PersonWithLongDBRef(21, "PersonWith", "LongDBRef", personPojoLongId); - template.insert(personWithLongDBRef); - - Query q = query(where("ssn").is(21)); - PersonWithLongDBRef p2 = template.findOne(q, PersonWithLongDBRef.class); - assertThat(p2).isNotNull(); - assertThat(p2.getPersonPojoLongId()).isNotNull(); - assertThat(p2.getPersonPojoLongId().getId()).isEqualTo(12L); - } - - @Test // DATADOC-275 - public void readsAndWritesDBRefsCorrectly() { - - template.dropCollection(Item.class); - template.dropCollection(Container.class); - - Item item = new Item(); - Item items = new Item(); - template.insert(item); - template.insert(items); - - Container container = new Container(); - container.item = item; - container.items = Arrays.asList(items); - - template.insert(container); - - Container result = template.findOne(query(where("id").is(container.id)), Container.class); - assertThat(result.item.id).isEqualTo(item.id); - assertThat(result.items.size()).isEqualTo(1); - assertThat(result.items.get(0).id).isEqualTo(items.id); - } - - @Test // DATAMONGO-805 - public void supportExcludeDbRefAssociation() { - - template.dropCollection(Item.class); - template.dropCollection(Container.class); - - Item item = new Item(); - template.insert(item); - - Container container = new Container("foo"); - container.item = item; - - template.insert(container); - - Query query = new Query(Criteria.where("id").is("foo")); - query.fields().exclude("item"); - Container result = template.findOne(query, Container.class); - - assertThat(result).isNotNull(); - assertThat(result.item).isNull(); - } - - @Test // DATAMONGO-805 - public void shouldMapFieldsOfIterableEntity() { - - template.dropCollection(IterableItem.class); - template.dropCollection(Container.class); - - Item item = new IterableItem(); - item.value = "bar"; - template.insert(item); - - Container container = new Container("foo"); - container.item = item; - - template.insert(container); - - Query query = new Query(Criteria.where("id").is("foo")); - Container result = template.findOne(query, Container.class); - - assertThat(result).isNotNull(); - assertThat(result.item).isNotNull(); - assertThat(result.item.value).isEqualTo("bar"); - } - - static class Container { - - @Id String id; - - public Container() { - id = new ObjectId().toString(); - } - - public Container(String id) { - this.id = id; - } - - @DBRef Item item; - @DBRef List items; - } - - static class Item { - - @Id String id; - String value; - - public Item() { - this.id = new ObjectId().toString(); - } - } - - static class IterableItem extends Item implements Iterable { - - List data = new ArrayList(); - - @Override - public Iterator iterator() { - return data.iterator(); - } - } - - static class ItemData { - - String id; - String value; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java deleted file mode 100644 index a7e454c52a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoMappingContextUnitTests.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import static org.assertj.core.api.Assertions.*; - -import java.time.temporal.ChronoUnit; -import java.util.AbstractMap; -import java.util.Collections; -import java.util.Locale; -import java.util.Map; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.context.ApplicationContext; -import org.springframework.data.annotation.Id; -import org.springframework.data.mapping.MappingException; -import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.model.FieldNamingStrategy; - -import com.mongodb.DBRef; - -/** - * Unit tests for {@link MongoMappingContext}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class MongoMappingContextUnitTests { - - @Mock ApplicationContext applicationContext; - - @Test - void addsSelfReferencingPersistentEntityCorrectly() throws Exception { - - MongoMappingContext context = new MongoMappingContext(); - - context.setInitialEntitySet(Collections.singleton(SampleClass.class)); - context.initialize(); - } - - @Test - void doesNotReturnPersistentEntityForMongoSimpleType() { - - MongoMappingContext context = new MongoMappingContext(); - assertThat(context.getPersistentEntity(DBRef.class)).isNull(); - } - - @Test // DATAMONGO-638 - void doesNotCreatePersistentEntityForAbstractMap() { - - MongoMappingContext context = new MongoMappingContext(); - assertThat(context.getPersistentEntity(AbstractMap.class)).isNull(); - } - - @Test // DATAMONGO-607 - void populatesPersistentPropertyWithCustomFieldNamingStrategy() { - - MongoMappingContext context = new MongoMappingContext(); - context.setApplicationContext(applicationContext); - context.setFieldNamingStrategy(new FieldNamingStrategy() { - - public String getFieldName(PersistentProperty property) { - return property.getName().toUpperCase(Locale.US); - } - }); - - MongoPersistentEntity entity = context.getRequiredPersistentEntity(Person.class); - assertThat(entity.getRequiredPersistentProperty("firstname").getFieldName()).isEqualTo("FIRSTNAME"); - } - - @Test // DATAMONGO-607 - void rejectsClassWithAmbiguousFieldMappings() { - - MongoMappingContext context = new MongoMappingContext(); - context.setApplicationContext(applicationContext); - - assertThatExceptionOfType(MappingException.class).isThrownBy(() -> context.getPersistentEntity(InvalidPerson.class)) - .withMessageContaining("firstname").withMessageContaining("lastname").withMessageContaining("foo") - .withMessageContaining("@Field"); - } - - @Test // DATAMONGO-694 - void doesNotConsiderOverrridenAccessorANewField() { - - MongoMappingContext context = new MongoMappingContext(); - context.setApplicationContext(applicationContext); - context.getPersistentEntity(Child.class); - } - - @Test // DATAMONGO-688 - void mappingContextShouldAcceptClassWithImplicitIdProperty() { - - MongoMappingContext context = new MongoMappingContext(); - MongoPersistentEntity pe = context.getRequiredPersistentEntity(ClassWithImplicitId.class); - - assertThat(pe).isNotNull(); - assertThat(pe.isIdProperty(pe.getRequiredPersistentProperty("id"))).isTrue(); - } - - @Test // DATAMONGO-688 - void mappingContextShouldAcceptClassWithExplicitIdProperty() { - - MongoMappingContext context = new MongoMappingContext(); - MongoPersistentEntity pe = context.getRequiredPersistentEntity(ClassWithExplicitId.class); - - assertThat(pe).isNotNull(); - assertThat(pe.isIdProperty(pe.getRequiredPersistentProperty("myId"))).isTrue(); - } - - @Test // DATAMONGO-688 - void mappingContextShouldAcceptClassWithExplicitAndImplicitIdPropertyByGivingPrecedenceToExplicitIdProperty() { - - MongoMappingContext context = new MongoMappingContext(); - MongoPersistentEntity pe = context.getRequiredPersistentEntity(ClassWithExplicitIdAndImplicitId.class); - assertThat(pe).isNotNull(); - } - - @Test // DATAMONGO-688 - void rejectsClassWithAmbiguousExplicitIdPropertyFieldMappings() { - - MongoMappingContext context = new MongoMappingContext(); - assertThatThrownBy(() -> context.getPersistentEntity(ClassWithMultipleExplicitIds.class)) - .isInstanceOf(MappingException.class); - } - - @Test // DATAMONGO-688 - void rejectsClassWithAmbiguousImplicitIdPropertyFieldMappings() { - - MongoMappingContext context = new MongoMappingContext(); - assertThatThrownBy(() -> context.getPersistentEntity(ClassWithMultipleImplicitIds.class)) - .isInstanceOf(MappingException.class); - } - - @Test // DATAMONGO-976 - void shouldRejectClassWithInvalidTextScoreProperty() { - - MongoMappingContext context = new MongoMappingContext(); - - assertThatExceptionOfType(MappingException.class) - .isThrownBy(() -> context.getPersistentEntity(ClassWithInvalidTextScoreProperty.class)) - .withMessageContaining("score").withMessageContaining("Float").withMessageContaining("Double"); - } - - @Test // DATAMONGO-2599 - void shouldNotCreateEntityForEnum() { - - MongoMappingContext context = new MongoMappingContext(); - - MongoPersistentEntity entity = context.getRequiredPersistentEntity(ClassWithChronoUnit.class); - - assertThat(entity.getPersistentProperty("unit").isEntity()).isFalse(); - assertThat(context.hasPersistentEntityFor(ChronoUnit.class)).isFalse(); - assertThat(context.getPersistentEntity(ChronoUnit.class)).isNull(); - } - - public class SampleClass { - - Map children; - } - - class Person { - - String firstname, lastname; - } - - class InvalidPerson { - - @org.springframework.data.mongodb.core.mapping.Field("foo") String firstname, lastname; - } - - class Parent { - - String name; - - public String getName() { - return name; - } - } - - class Child extends Parent { - - @Override - public String getName() { - return super.getName(); - } - } - - class ClassWithImplicitId { - - String field; - String id; - } - - class ClassWithExplicitId { - - @Id String myId; - String field; - } - - class ClassWithExplicitIdAndImplicitId { - - @Id String myId; - String id; - } - - class ClassWithMultipleExplicitIds { - - @Id String myId; - @Id String id; - } - - class ClassWithMultipleImplicitIds { - - String _id; - String id; - } - - class ClassWithInvalidTextScoreProperty { - - @TextScore Locale score; - } - - class ClassWithChronoUnit { - - ChronoUnit unit; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoPersistentPropertyComparatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoPersistentPropertyComparatorUnitTests.java deleted file mode 100644 index 9ebeb68ceb..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/MongoPersistentPropertyComparatorUnitTests.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.springframework.data.mongodb.core.mapping; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity.MongoPersistentPropertyComparator; - -/** - * Unit tests for {@link MongoPersistentPropertyComparator}. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -class MongoPersistentPropertyComparatorUnitTests { - - @Mock MongoPersistentProperty firstName; - - @Mock MongoPersistentProperty lastName; - - @Mock MongoPersistentProperty ssn; - - @Test - void ordersPropertiesCorrectly() { - - when(ssn.getFieldOrder()).thenReturn(10); - when(firstName.getFieldOrder()).thenReturn(20); - when(lastName.getFieldOrder()).thenReturn(Integer.MAX_VALUE); - - List properties = Arrays.asList(firstName, lastName, ssn); - Collections.sort(properties, MongoPersistentPropertyComparator.INSTANCE); - - assertThat(properties.get(0)).isEqualTo(ssn); - assertThat(properties.get(1)).isEqualTo(firstName); - assertThat(properties.get(2)).isEqualTo(lastName); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java deleted file mode 100644 index b3394532d9..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import java.util.List; - -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.annotation.Transient; -import org.springframework.data.mongodb.core.index.CompoundIndex; -import org.springframework.data.mongodb.core.index.CompoundIndexes; -import org.springframework.data.mongodb.core.index.Indexed; - -/** - * @author Jon Brisbin - */ -@Document -@CompoundIndexes({ @CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}") }) -public class Person { - - @Id private String id; - @Indexed(unique = true) private Integer ssn; - private String firstName; - @Indexed private String lastName; - private Integer age; - @Transient private Integer accountTotal; - @DBRef private List accounts; - private T address; - - public Person(Integer ssn) { - this.ssn = ssn; - } - - @PersistenceConstructor - public Person(Integer ssn, String firstName, String lastName, Integer age, T address) { - this.ssn = ssn; - this.firstName = firstName; - this.lastName = lastName; - this.age = age; - this.address = address; - } - - public String getId() { - return id; - } - - public Integer getSsn() { - return ssn; - } - - public void setSsn(Integer ssn) { - this.ssn = ssn; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public Integer getAge() { - return age; - } - - public void setAge(Integer age) { - this.age = age; - } - - public Integer getAccountTotal() { - return accountTotal; - } - - public void setAccountTotal(Integer accountTotal) { - this.accountTotal = accountTotal; - } - - public List getAccounts() { - return accounts; - } - - public void setAccounts(List accounts) { - this.accounts = accounts; - } - - public T getAddress() { - return address; - } - - public void setAddress(T address) { - this.address = address; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomCollection1.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomCollection1.java deleted file mode 100644 index dddf885d23..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomCollection1.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.springframework.data.annotation.Id; - -/** - * @author Jon Brisbin - */ -@Document("person1") -public class PersonCustomCollection1 extends BasePerson { - - @Id private String id; - - public PersonCustomCollection1(Integer ssn, String firstName, String lastName) { - super(ssn, firstName, lastName); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomCollection2.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomCollection2.java deleted file mode 100644 index 44ece413ab..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomCollection2.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.springframework.data.annotation.Id; - -/** - * @author Jon Brisbin - */ -@Document("person2") -public class PersonCustomCollection2 extends BasePerson { - - @Id private String id; - - public PersonCustomCollection2(Integer ssn, String firstName, String lastName) { - super(ssn, firstName, lastName); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java deleted file mode 100644 index a26967ba9c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; - -/** - * @author Jon Brisbin - */ -public class PersonCustomIdName extends BasePerson { - - @Id private String lastName; - - public PersonCustomIdName(Integer ssn, String firstName) { - this.ssn = ssn; - this.firstName = firstName; - } - - @PersistenceConstructor - public PersonCustomIdName(Integer ssn, String firstName, String lastName) { - this.ssn = ssn; - this.firstName = firstName; - this.lastName = lastName; - } - - @Override - public String getLastName() { - return this.lastName; - } - - @Override - public void setLastName(String lastName) { - this.lastName = lastName; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonMapProperty.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonMapProperty.java deleted file mode 100644 index 243a5b2e00..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonMapProperty.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import java.util.Map; - -import org.bson.types.ObjectId; - -/** - * @author Jon Brisbin - */ -@Document -public class PersonMapProperty extends BasePerson { - - private ObjectId id; - private Map accounts; - - public PersonMapProperty(Integer ssn, String firstName, String lastName) { - super(ssn, firstName, lastName); - } - - public ObjectId getId() { - return id; - } - - public void setId(ObjectId id) { - this.id = id; - } - - public Map getAccounts() { - return accounts; - } - - public void setAccounts(Map accounts) { - this.accounts = accounts; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonMultiCollection.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonMultiCollection.java deleted file mode 100644 index eb1c067ffd..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonMultiCollection.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import java.util.List; - -/** - * @author Jon Brisbin - */ -public class PersonMultiCollection extends BasePerson { - - private List> grid; - - public PersonMultiCollection(Integer ssn, String firstName, String lastName, List> grid) { - super(ssn, firstName, lastName); - this.grid = grid; - } - - public List> getGrid() { - return grid; - } - - public void setGrid(List> grid) { - this.grid = grid; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonMultiDimArrays.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonMultiDimArrays.java deleted file mode 100644 index fb4e6599fa..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonMultiDimArrays.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.springframework.data.annotation.Id; - -/** - * @author Jon Brisbin - */ -@Document -public class PersonMultiDimArrays extends BasePerson { - - @Id private String id; - private String[][] grid; - - public PersonMultiDimArrays(Integer ssn, String firstName, String lastName, String[][] grid) { - super(ssn, firstName, lastName); - this.grid = grid; - } - - public String[][] getGrid() { - return grid; - } - - public void setGrid(String[][] grid) { - this.grid = grid; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonNullProperties.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonNullProperties.java deleted file mode 100644 index c50219f6ce..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonNullProperties.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.bson.types.ObjectId; - -import org.springframework.data.annotation.Id; - -/** - * @author Jon Brisbin - */ -@Document -public class PersonNullProperties extends BasePerson { - - @Id private ObjectId id; - - public PersonNullProperties() {} - - public ObjectId getId() { - return id; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonPojoIntId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonPojoIntId.java deleted file mode 100644 index fc71484f12..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonPojoIntId.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -/** - * @author Jon Brisbin - */ -public class PersonPojoIntId { - - private int id; - private String text; - - public PersonPojoIntId(int id, String text) { - this.id = id; - this.text = text; - } - - public int getId() { - return id; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonPojoLongId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonPojoLongId.java deleted file mode 100644 index d1b8ed676c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonPojoLongId.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -/** - * @author Jon Brisbin - */ -public class PersonPojoLongId { - - private long id; - private String text; - - public PersonPojoLongId(long id, String text) { - this.id = id; - this.text = text; - } - - public long getId() { - return id; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonPojoStringId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonPojoStringId.java deleted file mode 100644 index d60dce473a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonPojoStringId.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -/** - * @author Jon Brisbin - */ -public class PersonPojoStringId { - - private String id; - private String text; - - public PersonPojoStringId(String id, String text) { - this.id = id; - this.text = text; - } - - public String getId() { - return id; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonSimpleList.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonSimpleList.java deleted file mode 100644 index fe709aded2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonSimpleList.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import java.util.List; - -/** - * @author Jon Brisbin - */ -public class PersonSimpleList extends BasePerson { - - private List nicknames; - - public PersonSimpleList(Integer ssn, String firstName, String lastName) { - super(ssn, firstName, lastName); - } - - public List getNicknames() { - return nicknames; - } - - public void setNicknames(List nicknames) { - this.nicknames = nicknames; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonWithDbRef.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonWithDbRef.java deleted file mode 100644 index a3fae6d56f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonWithDbRef.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -/** - * @author Jon Brisbin - */ -public class PersonWithDbRef extends BasePerson { - - @DBRef private GeoLocation home; - - public PersonWithDbRef(Integer ssn, String firstName, String lastName, GeoLocation home) { - super(ssn, firstName, lastName); - this.home = home; - } - - public GeoLocation getHome() { - return home; - } - - public void setHome(GeoLocation home) { - this.home = home; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonWithLongDBRef.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonWithLongDBRef.java deleted file mode 100644 index 6af82cfeb1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonWithLongDBRef.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.springframework.data.mongodb.core.mapping; - -/** - * @author Jon Brisbin - */ -public class PersonWithLongDBRef extends BasePerson { - - @DBRef private PersonPojoLongId personPojoLongId; - - public PersonWithLongDBRef(Integer ssn, String firstName, String lastName, PersonPojoLongId personPojoLongId) { - super(ssn, firstName, lastName); - this.personPojoLongId = personPojoLongId; - } - - public PersonPojoLongId getPersonPojoLongId() { - return personPojoLongId; - } - - public void setPersonPojoLongId(PersonPojoLongId personPojoLongId) { - this.personPojoLongId = personPojoLongId; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonWithObjectId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonWithObjectId.java deleted file mode 100644 index 8a0a2f1917..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonWithObjectId.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.bson.types.ObjectId; - -/** - * @author Jon Brisbin - */ -public class PersonWithObjectId extends BasePerson { - - private ObjectId id; - - public PersonWithObjectId(Integer ssn, String firstName, String lastName) { - super(ssn, firstName, lastName); - } - - public ObjectId getId() { - return id; - } - - public void setId(ObjectId id) { - this.id = id; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PrimitiveId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PrimitiveId.java deleted file mode 100644 index 5192c4aed1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PrimitiveId.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping; - -import org.springframework.data.annotation.Id; - -/** - * @author Jon Brisbin - */ -@Document -public class PrimitiveId { - - @Id int id; - String text; - - public PrimitiveId(Integer id) { - this.id = id; - } - - public int getId() { - return id; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListenerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListenerUnitTests.java deleted file mode 100644 index 495f8748d7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListenerUnitTests.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping.event; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.data.mongodb.core.mapping.Account; -import org.springframework.data.mongodb.repository.Contact; -import org.springframework.data.mongodb.repository.Person; - -/** - * Unit tests for {@link AbstractMongoEventListener}. - * - * @author Oliver Gierke - * @author Martin Baumgartner - */ -public class AbstractMongoEventListenerUnitTests { - - @Test - public void invokesCallbackForEventForPerson() { - - MongoMappingEvent event = new BeforeConvertEvent(new Person("Dave", "Matthews"), "collection-1"); - SamplePersonEventListener listener = new SamplePersonEventListener(); - listener.onApplicationEvent(event); - assertThat(listener.invokedOnBeforeConvert).isTrue(); - } - - @Test - public void dropsEventIfNotForCorrectDomainType() { - - AbstractApplicationContext context = new ClassPathXmlApplicationContext(); - context.refresh(); - - SamplePersonEventListener listener = new SamplePersonEventListener(); - context.addApplicationListener(listener); - - context.publishEvent(new BeforeConvertEvent(new Person("Dave", "Matthews"), "collection-1")); - assertThat(listener.invokedOnBeforeConvert).isTrue(); - - listener.invokedOnBeforeConvert = false; - context.publishEvent(new BeforeConvertEvent("Test", "collection-1")); - assertThat(listener.invokedOnBeforeConvert).isFalse(); - - context.close(); - } - - @Test // DATAMONGO-289 - public void afterLoadEffectGetsHandledCorrectly() { - - SamplePersonEventListener listener = new SamplePersonEventListener(); - listener.onApplicationEvent(new AfterLoadEvent(new Document(), Person.class, "collection-1")); - assertThat(listener.invokedOnAfterLoad).isTrue(); - } - - @Test // DATAMONGO-289 - public void afterLoadEventGetsFilteredForDomainType() { - - SamplePersonEventListener personListener = new SamplePersonEventListener(); - SampleAccountEventListener accountListener = new SampleAccountEventListener(); - personListener.onApplicationEvent(new AfterLoadEvent(new Document(), Person.class, "collection-1")); - accountListener.onApplicationEvent(new AfterLoadEvent(new Document(), Person.class, "collection-1")); - - assertThat(personListener.invokedOnAfterLoad).isTrue(); - assertThat(accountListener.invokedOnAfterLoad).isFalse(); - } - - @Test // DATAMONGO-289 - public void afterLoadEventGetsFilteredForDomainTypeWorksForSubtypes() { - - SamplePersonEventListener personListener = new SamplePersonEventListener(); - SampleContactEventListener contactListener = new SampleContactEventListener(); - personListener.onApplicationEvent(new AfterLoadEvent(new Document(), Person.class, "collection-1")); - contactListener.onApplicationEvent(new AfterLoadEvent(new Document(), Person.class, "collection-1")); - - assertThat(personListener.invokedOnAfterLoad).isTrue(); - assertThat(contactListener.invokedOnAfterLoad).isTrue(); - } - - @Test // DATAMONGO-289 - public void afterLoadEventGetsFilteredForDomainTypeWorksForSubtypes2() { - - SamplePersonEventListener personListener = new SamplePersonEventListener(); - SampleContactEventListener contactListener = new SampleContactEventListener(); - personListener.onApplicationEvent(new AfterLoadEvent(new Document(), Contact.class, "collection-1")); - contactListener.onApplicationEvent(new AfterLoadEvent(new Document(), Contact.class, "collection-1")); - - assertThat(personListener.invokedOnAfterLoad).isFalse(); - assertThat(contactListener.invokedOnAfterLoad).isTrue(); - } - - @Test // DATAMONGO-333 - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void handlesUntypedImplementations() { - - UntypedEventListener listener = new UntypedEventListener(); - listener.onApplicationEvent(new MongoMappingEvent(new Object(), new Document(), "collection-1")); - } - - @Test // DATAMONGO-545 - public void invokeContactCallbackForPersonEvent() { - - MongoMappingEvent event = new BeforeDeleteEvent(new Document(), Person.class, "collection-1"); - SampleContactEventListener listener = new SampleContactEventListener(); - listener.onApplicationEvent(event); - - assertThat(listener.invokedOnBeforeDelete).isTrue(); - } - - @Test // DATAMONGO-545 - public void invokePersonCallbackForPersonEvent() { - - MongoMappingEvent event = new BeforeDeleteEvent(new Document(), Person.class, "collection-1"); - SamplePersonEventListener listener = new SamplePersonEventListener(); - listener.onApplicationEvent(event); - - assertThat(listener.invokedOnBeforeDelete).isTrue(); - } - - @Test // DATAMONGO-545 - public void dontInvokePersonCallbackForAccountEvent() { - - MongoMappingEvent event = new BeforeDeleteEvent(new Document(), Account.class, "collection-1"); - SamplePersonEventListener listener = new SamplePersonEventListener(); - listener.onApplicationEvent(event); - - assertThat(listener.invokedOnBeforeDelete).isFalse(); - } - - @Test // DATAMONGO-545 - public void donInvokePersonCallbackForUntypedEvent() { - - MongoMappingEvent event = new BeforeDeleteEvent(new Document(), null, "collection-1"); - SamplePersonEventListener listener = new SamplePersonEventListener(); - listener.onApplicationEvent(event); - - assertThat(listener.invokedOnBeforeDelete).isFalse(); - } - - class SamplePersonEventListener extends AbstractMongoEventListener { - - boolean invokedOnBeforeConvert; - boolean invokedOnAfterLoad; - boolean invokedOnBeforeDelete; - boolean invokedOnAfterDelete; - - @Override - public void onBeforeConvert(BeforeConvertEvent event) { - invokedOnBeforeConvert = true; - } - - @Override - public void onAfterLoad(AfterLoadEvent event) { - invokedOnAfterLoad = true; - } - - @Override - public void onAfterDelete(AfterDeleteEvent event) { - invokedOnAfterDelete = true; - } - - @Override - public void onBeforeDelete(BeforeDeleteEvent event) { - invokedOnBeforeDelete = true; - } - } - - class SampleContactEventListener extends AbstractMongoEventListener { - - boolean invokedOnBeforeConvert; - boolean invokedOnAfterLoad; - boolean invokedOnBeforeDelete; - boolean invokedOnAfterDelete; - - @Override - public void onBeforeConvert(BeforeConvertEvent event) { - invokedOnBeforeConvert = true; - } - - @Override - public void onAfterLoad(AfterLoadEvent event) { - invokedOnAfterLoad = true; - } - - @Override - public void onAfterDelete(AfterDeleteEvent event) { - invokedOnAfterDelete = true; - } - - @Override - public void onBeforeDelete(BeforeDeleteEvent event) { - invokedOnBeforeDelete = true; - } - } - - class SampleAccountEventListener extends AbstractMongoEventListener { - - boolean invokedOnBeforeConvert; - boolean invokedOnAfterLoad; - - @Override - public void onBeforeConvert(BeforeConvertEvent event) { - invokedOnBeforeConvert = true; - } - - @Override - public void onAfterLoad(AfterLoadEvent event) { - invokedOnAfterLoad = true; - } - } - - @SuppressWarnings("rawtypes") - class UntypedEventListener extends AbstractMongoEventListener { - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AfterSaveListener.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AfterSaveListener.java deleted file mode 100644 index 56a8fc5864..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AfterSaveListener.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping.event; - -import java.util.ArrayList; - -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; - -public class AfterSaveListener implements ApplicationListener> { - - public final ArrayList seenEvents = new ArrayList(); - - public void onApplicationEvent(AfterSaveEvent event) { - this.seenEvents.add(event); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java deleted file mode 100644 index 40dcd697af..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTests.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping.event; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.DocumentTestUtils.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.AllArgsConstructor; -import lombok.Data; - -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.aggregation.Aggregation; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.PersonPojoStringId; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.QPerson; -import org.springframework.data.mongodb.repository.query.MongoEntityInformation; -import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory; -import org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; - -import com.mongodb.WriteConcern; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoDatabase; - -/** - * Integration test for Mapping Events. - * - * @author Mark Pollack - * @author Christoph Strobl - * @author Jordi Llach - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class }) -public class ApplicationContextEventTests { - - private static final String COLLECTION_NAME = "personPojoStringId"; - private static final String ROOT_COLLECTION_NAME = "root"; - private static final String RELATED_COLLECTION_NAME = "related"; - - private final String[] collectionsToDrop = new String[] { COLLECTION_NAME, ROOT_COLLECTION_NAME, - RELATED_COLLECTION_NAME }; - - static @Client MongoClient mongoClient; - - private ApplicationContext applicationContext; - private MongoTemplate template; - private SimpleMappingEventListener listener; - - @BeforeEach - public void setUp() { - - cleanDb(); - - applicationContext = new AnnotationConfigApplicationContext(ApplicationContextEventTestsAppConfig.class); - template = applicationContext.getBean(MongoTemplate.class); - template.setWriteConcern(WriteConcern.JOURNALED); - listener = applicationContext.getBean(SimpleMappingEventListener.class); - } - - @AfterEach - public void cleanUp() { - cleanDb(); - } - - private void cleanDb() { - - MongoDatabase db = mongoClient.getDatabase("database"); - for (String coll : collectionsToDrop) { - db.getCollection(coll).drop(); - } - } - - @Test - @SuppressWarnings("unchecked") - public void beforeSaveEvent() { - - PersonBeforeSaveListener personBeforeSaveListener = applicationContext.getBean(PersonBeforeSaveListener.class); - AfterSaveListener afterSaveListener = applicationContext.getBean(AfterSaveListener.class); - - assertThat(personBeforeSaveListener.seenEvents).isEmpty(); - assertThat(afterSaveListener.seenEvents).isEmpty(); - - assertThat(listener.onBeforeSaveEvents).isEmpty(); - assertThat(listener.onAfterSaveEvents).isEmpty(); - - PersonPojoStringId p = new PersonPojoStringId("1", "Text"); - template.insert(p); - - assertThat(personBeforeSaveListener.seenEvents).hasSize(1); - assertThat(afterSaveListener.seenEvents).hasSize(1); - - assertThat(listener.onBeforeSaveEvents).hasSize(1); - assertThat(listener.onAfterSaveEvents).hasSize(1); - - assertThat(listener.onBeforeSaveEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - assertThat(listener.onAfterSaveEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - - assertThat(personBeforeSaveListener.seenEvents.get(0) instanceof BeforeSaveEvent).isTrue(); - assertThat(afterSaveListener.seenEvents.get(0) instanceof AfterSaveEvent).isTrue(); - - BeforeSaveEvent beforeSaveEvent = (BeforeSaveEvent) personBeforeSaveListener.seenEvents - .get(0); - PersonPojoStringId p2 = beforeSaveEvent.getSource(); - org.bson.Document document = beforeSaveEvent.getDocument(); - - comparePersonAndDocument(p, p2, document); - - AfterSaveEvent afterSaveEvent = (AfterSaveEvent) afterSaveListener.seenEvents.get(0); - assertThat(afterSaveEvent.getSource() instanceof PersonPojoStringId).isTrue(); - p2 = (PersonPojoStringId) afterSaveEvent.getSource(); - document = beforeSaveEvent.getDocument(); - - comparePersonAndDocument(p, p2, document); - } - - @Test // DATAMONGO-1256 - public void loadAndConvertEvents() { - - PersonPojoStringId entity = new PersonPojoStringId("1", "Text"); - template.insert(entity); - - template.findOne(query(where("id").is(entity.getId())), PersonPojoStringId.class); - - assertThat(listener.onAfterLoadEvents).hasSize(1); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - - assertThat(listener.onBeforeConvertEvents).hasSize(1); - assertThat(listener.onBeforeConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(1); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - } - - @Test // DATAMONGO-1256 - public void loadEventsOnAggregation() { - - template.insert(new PersonPojoStringId("1", "Text")); - - template.aggregate(Aggregation.newAggregation(Aggregation.project("text")), PersonPojoStringId.class, - PersonPojoStringId.class); - - assertThat(listener.onAfterLoadEvents).hasSize(1); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - - assertThat(listener.onBeforeConvertEvents).hasSize(1); - assertThat(listener.onBeforeConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(1); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - } - - @Test // DATAMONGO-1256 - public void deleteEvents() { - - PersonPojoStringId entity = new PersonPojoStringId("1", "Text"); - template.insert(entity); - - template.remove(entity); - - assertThat(listener.onBeforeDeleteEvents).hasSize(1); - assertThat(listener.onBeforeDeleteEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - - assertThat(listener.onAfterDeleteEvents).hasSize(1); - assertThat(listener.onAfterDeleteEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - } - - @Test // DATAMONGO-1271 - public void publishesAfterLoadAndAfterConvertEventsForDBRef() { - - Related ref1 = new Related(2L, "related desc1"); - - template.insert(ref1); - - Root source = new Root(); - source.id = 1L; - source.reference = ref1; - - template.insert(source); - - template.findOne(query(where("id").is(source.getId())), Root.class); - - assertThat(listener.onAfterLoadEvents).hasSize(2); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(2); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - } - - @Test // DATAMONGO-1271 - public void publishesAfterLoadAndAfterConvertEventsForLazyLoadingDBRef() { - - Related ref1 = new Related(2L, "related desc1"); - - template.insert(ref1); - - Root source = new Root(); - source.id = 1L; - source.lazyReference = ref1; - - template.insert(source); - - Root target = template.findOne(query(where("id").is(source.getId())), Root.class); - - assertThat(listener.onAfterLoadEvents).hasSize(1); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(1); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - - target.getLazyReference().getDescription(); - - assertThat(listener.onAfterLoadEvents).hasSize(2); - assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(2); - assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - } - - @Test // DATAMONGO-1271 - public void publishesAfterLoadAndAfterConvertEventsForListOfDBRef() { - - List references = Arrays.asList(new Related(20L, "ref 1"), new Related(30L, "ref 2")); - - template.insert(references, Related.class); - - Root source = new Root(); - source.id = 1L; - source.listOfReferences = references; - - template.insert(source); - - template.findOne(query(where("id").is(source.getId())), Root.class); - - assertThat(listener.onAfterLoadEvents).hasSize(3); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterLoadEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(3); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterConvertEvents.get(2).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - } - - @Test // DATAMONGO-1271 - public void publishesAfterLoadAndAfterConvertEventsForLazyLoadingListOfDBRef() { - - List references = Arrays.asList(new Related(20L, "ref 1"), new Related(30L, "ref 2")); - - template.insert(references, Related.class); - - Root source = new Root(); - source.id = 1L; - source.lazyListOfReferences = references; - - template.insert(source); - - Root target = template.findOne(query(where("id").is(source.getId())), Root.class); - - assertThat(listener.onAfterLoadEvents).hasSize(1); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - assertThat(listener.onAfterConvertEvents).hasSize(1); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - - target.getLazyListOfReferences().size(); - - assertThat(listener.onAfterLoadEvents).hasSize(3); - assertThat(listener.onAfterConvertEvents).hasSize(3); - - assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterLoadEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterConvertEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - } - - @Test // DATAMONGO-1271 - public void publishesAfterLoadAndAfterConvertEventsForMapOfDBRef() { - - Map references = new LinkedHashMap(); - references.put("ref-1", new Related(20L, "ref 1")); - references.put("ref-2", new Related(30L, "ref 2")); - - template.insert(references.values(), Related.class); - - Root source = new Root(); - source.id = 1L; - source.mapOfReferences = references; - - template.insert(source); - - template.findOne(query(where("id").is(source.getId())), Root.class); - - assertThat(listener.onAfterLoadEvents).hasSize(3); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterLoadEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(3); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterConvertEvents.get(2).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - } - - @Test // DATAMONGO-1271 - public void publishesAfterLoadAndAfterConvertEventsForLazyLoadingMapOfDBRef() { - - Map references = new LinkedHashMap(); - references.put("ref-1", new Related(20L, "ref 1")); - references.put("ref-2", new Related(30L, "ref 2")); - - template.insert(references.values(), Related.class); - - Root source = new Root(); - source.id = 1L; - source.lazyMapOfReferences = references; - - template.insert(source); - - Root target = template.findOne(query(where("id").is(source.getId())), Root.class); - - assertThat(listener.onAfterLoadEvents).hasSize(1); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(1); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(ROOT_COLLECTION_NAME); - - target.getLazyMapOfReferences().size(); - - assertThat(listener.onAfterLoadEvents).hasSize(3); - assertThat(listener.onAfterLoadEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterLoadEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(3); - assertThat(listener.onAfterConvertEvents.get(1).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - assertThat(listener.onAfterConvertEvents.get(2).getCollectionName()).isEqualTo(RELATED_COLLECTION_NAME); - } - - @Test // DATAMONGO-1823 - public void publishesAfterConvertEventForFindQueriesUsingProjections() { - - PersonPojoStringId entity = new PersonPojoStringId("1", "Text"); - template.insert(entity); - - template.query(PersonPojoStringId.class).matching(query(where("id").is(entity.getId()))).all(); - - assertThat(listener.onAfterLoadEvents).hasSize(1); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - - assertThat(listener.onBeforeConvertEvents).hasSize(1); - assertThat(listener.onBeforeConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - - assertThat(listener.onAfterConvertEvents).hasSize(1); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo(COLLECTION_NAME); - } - - @Test // DATAMONGO-700, DATAMONGO-1185, DATAMONGO-1848 - public void publishesEventsForQuerydslFindQueries() { - - template.dropCollection(Person.class); - - template.save(new Person("Boba", "Fett", 40)); - - MongoRepositoryFactory factory = new MongoRepositoryFactory(template); - MongoEntityInformation entityInformation = factory.getEntityInformation(Person.class); - QuerydslMongoPredicateExecutor executor = new QuerydslMongoPredicateExecutor<>(entityInformation, template); - - executor.findOne(QPerson.person.lastname.startsWith("Fe")); - - assertThat(listener.onAfterLoadEvents).hasSize(1); - assertThat(listener.onAfterLoadEvents.get(0).getCollectionName()).isEqualTo("person"); - - assertThat(listener.onBeforeConvertEvents).hasSize(1); - assertThat(listener.onBeforeConvertEvents.get(0).getCollectionName()).isEqualTo("person"); - - assertThat(listener.onAfterConvertEvents).hasSize(1); - assertThat(listener.onAfterConvertEvents.get(0).getCollectionName()).isEqualTo("person"); - } - - private void comparePersonAndDocument(PersonPojoStringId p, PersonPojoStringId p2, org.bson.Document document) { - - assertThat(p2.getId()).isEqualTo(p.getId()); - assertThat(p2.getText()).isEqualTo(p.getText()); - - assertThat(document.get("_id")).isEqualTo("1"); - assertThat(document.get("text")).isEqualTo("Text"); - assertTypeHint(document, PersonPojoStringId.class); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document - public static class Root { - - @Id Long id; - - @DBRef Related reference; - @DBRef(lazy = true) Related lazyReference; - - @DBRef List listOfReferences; - @DBRef(lazy = true) List lazyListOfReferences; - - @DBRef Map mapOfReferences; - @DBRef(lazy = true) Map lazyMapOfReferences; - } - - @Data - @AllArgsConstructor - @org.springframework.data.mongodb.core.mapping.Document - public static class Related { - - @Id Long id; - String description; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTestsAppConfig.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTestsAppConfig.java deleted file mode 100644 index ec0dccf25e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ApplicationContextEventTestsAppConfig.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2010-2021 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.mapping.event; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.test.util.MongoTestUtils; - -import com.mongodb.client.MongoClient; - -@Configuration -public class ApplicationContextEventTestsAppConfig extends AbstractMongoClientConfiguration { - - @Override - public String getDatabaseName() { - return "database"; - } - - @Override - @Bean - public MongoClient mongoClient() { - return MongoTestUtils.client(); - } - - @Bean - public PersonBeforeSaveListener personBeforeSaveListener() { - return new PersonBeforeSaveListener(); - } - - @Bean - public AfterSaveListener afterSaveListener() { - return new AfterSaveListener(); - } - - @Bean - public SimpleMappingEventListener simpleMappingEventListener() { - return new SimpleMappingEventListener(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AuditingEntityCallbackUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AuditingEntityCallbackUnitTests.java deleted file mode 100644 index f4233a288f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AuditingEntityCallbackUnitTests.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2019-2021 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.mapping.event; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; -import lombok.Value; -import lombok.experimental.Wither; - -import java.util.Arrays; -import java.util.Date; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.core.Ordered; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.auditing.IsNewAwareAuditingHandler; -import org.springframework.data.mapping.context.PersistentEntities; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -/** - * Unit tests for {@link AuditingEntityCallback}. - * - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class AuditingEntityCallbackUnitTests { - - private IsNewAwareAuditingHandler handler; - private AuditingEntityCallback callback; - - @BeforeEach - void setUp() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.getPersistentEntity(Sample.class); - - handler = spy(new IsNewAwareAuditingHandler(new PersistentEntities(Arrays.asList(mappingContext)))); - - callback = new AuditingEntityCallback(() -> handler); - } - - @Test // DATAMONGO-2261 - void rejectsNullAuditingHandler() { - assertThatIllegalArgumentException().isThrownBy(() -> new AuditingEntityCallback(null)); - } - - @Test // DATAMONGO-2261 - void triggersCreationMarkForObjectWithEmptyId() { - - Sample sample = new Sample(); - callback.onBeforeConvert(sample, "foo"); - - verify(handler, times(1)).markCreated(sample); - verify(handler, times(0)).markModified(any()); - } - - @Test // DATAMONGO-2261 - void triggersModificationMarkForObjectWithSetId() { - - Sample sample = new Sample(); - sample.id = "id"; - callback.onBeforeConvert(sample, "foo"); - - verify(handler, times(0)).markCreated(any()); - verify(handler, times(1)).markModified(sample); - } - - @Test // DATAMONGO-2261 - void hasExplicitOrder() { - - assertThat(callback).isInstanceOf(Ordered.class); - assertThat(callback.getOrder()).isEqualTo(100); - } - - @Test // DATAMONGO-2261 - void propagatesChangedInstanceToEvent() { - - ImmutableSample sample = new ImmutableSample(); - - ImmutableSample newSample = new ImmutableSample(); - IsNewAwareAuditingHandler handler = mock(IsNewAwareAuditingHandler.class); - doReturn(newSample).when(handler).markAudited(eq(sample)); - - AuditingEntityCallback listener = new AuditingEntityCallback(() -> handler); - Object result = listener.onBeforeConvert(sample, "foo"); - - assertThat(result).isSameAs(newSample); - } - - static class Sample { - - @Id String id; - @CreatedDate Date created; - @LastModifiedDate Date modified; - } - - @Value - @Wither - @AllArgsConstructor - @NoArgsConstructor(force = true) - private static class ImmutableSample { - - @Id String id; - @CreatedDate Date created; - @LastModifiedDate Date modified; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AuditingEventListenerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AuditingEventListenerUnitTests.java deleted file mode 100644 index f52508524b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AuditingEventListenerUnitTests.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2012-2021 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.mapping.event; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; -import lombok.Value; -import lombok.experimental.Wither; - -import java.util.Arrays; -import java.util.Date; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.core.Ordered; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.auditing.IsNewAwareAuditingHandler; -import org.springframework.data.mapping.context.PersistentEntities; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -/** - * Unit tests for {@link AuditingEventListener}. - * - * @author Oliver Gierke - * @author Thomas Darimont - */ -@ExtendWith(MockitoExtension.class) -public class AuditingEventListenerUnitTests { - - private IsNewAwareAuditingHandler handler; - private AuditingEventListener listener; - - @BeforeEach - void setUp() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - mappingContext.getPersistentEntity(Sample.class); - - handler = spy(new IsNewAwareAuditingHandler(new PersistentEntities(Arrays.asList(mappingContext)))); - listener = new AuditingEventListener(() -> handler); - } - - @Test // DATAMONGO-577 - void rejectsNullAuditingHandler() { - assertThatIllegalArgumentException().isThrownBy(() -> new AuditingEventListener(null)); - } - - @Test // DATAMONGO-577 - void triggersCreationMarkForObjectWithEmptyId() { - - Sample sample = new Sample(); - listener.onApplicationEvent(new BeforeConvertEvent(sample, "collection-1")); - - verify(handler, times(1)).markCreated(sample); - verify(handler, times(0)).markModified(any()); - } - - @Test // DATAMONGO-577 - void triggersModificationMarkForObjectWithSetId() { - - Sample sample = new Sample(); - sample.id = "id"; - listener.onApplicationEvent(new BeforeConvertEvent(sample, "collection-1")); - - verify(handler, times(0)).markCreated(any()); - verify(handler, times(1)).markModified(sample); - } - - @Test - void hasExplicitOrder() { - - assertThat(listener).isInstanceOf(Ordered.class); - assertThat(listener.getOrder()).isEqualTo(100); - } - - @Test // DATAMONGO-1992 - void propagatesChangedInstanceToEvent() { - - ImmutableSample sample = new ImmutableSample(); - BeforeConvertEvent event = new BeforeConvertEvent<>(sample, "collection"); - - ImmutableSample newSample = new ImmutableSample(); - IsNewAwareAuditingHandler handler = mock(IsNewAwareAuditingHandler.class); - doReturn(newSample).when(handler).markAudited(eq(sample)); - - AuditingEventListener listener = new AuditingEventListener(() -> handler); - listener.onApplicationEvent(event); - - assertThat(event.getSource()).isSameAs(newSample); - } - - static class Sample { - - @Id String id; - @CreatedDate Date created; - @LastModifiedDate Date modified; - } - - @Value - @Wither - @AllArgsConstructor - @NoArgsConstructor(force = true) - private static class ImmutableSample { - - @Id String id; - @CreatedDate Date created; - @LastModifiedDate Date modified; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/LoggingEventListenerTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/LoggingEventListenerTests.java deleted file mode 100644 index 8ac79c783c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/LoggingEventListenerTests.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2017-2021 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.mapping.event; - -import static org.assertj.core.api.Assertions.*; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.read.ListAppender; - -import org.bson.Document; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.LoggerFactory; - -/** - * Tests for {@link LoggingEventListener}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -public class LoggingEventListenerTests { - - ListAppender appender; - ch.qos.logback.classic.Logger logger; - LoggingEventListener listener; - - @BeforeEach - public void setUp() { - - appender = new ListAppender(); - - // set log level for LoggingEventListener to "info" and set up an appender capturing events. - logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(LoggingEventListener.class); - - logger.setAdditive(false); - logger.setLevel(Level.INFO); - logger.addAppender(appender); - - appender.start(); - - listener = new LoggingEventListener(); - } - - @AfterEach - public void tearDown() { - - // cleanup - if (logger != null) { - - logger.detachAppender(appender); - logger.setAdditive(true); - logger.setLevel(null); - } - - if (appender != null) { - appender.stop(); - } - } - - @Test // DATAMONGO-1645 - public void shouldSerializeAfterConvertEventCorrectly() { - - listener.onAfterConvert(new AfterConvertEvent(new Document("foo", new Foo()), this, "collection")); - - assertThat(appender.list.get(0).getFormattedMessage()).startsWith("onAfterConvert: { \"foo\""); - } - - @Test // DATAMONGO-1645 - public void shouldSerializeBeforeSaveEventEventCorrectly() { - - listener.onBeforeSave(new BeforeSaveEvent(new Foo(), new Document("foo", new Foo()), "collection")); - - assertThat(appender.list.get(0).getFormattedMessage()) - .startsWith("onBeforeSave: org.springframework.data.mongodb.core."); - } - - @Test // DATAMONGO-1645 - public void shouldSerializeAfterSaveEventEventCorrectly() { - - listener.onAfterSave(new AfterSaveEvent(new Foo(), new Document("foo", new Foo()), "collection")); - - assertThat(appender.list.get(0).getFormattedMessage()) - .startsWith("onAfterSave: org.springframework.data.mongodb.core."); - } - - @Test // DATAMONGO-1645 - public void shouldSerializeBeforeDeleteEventEventCorrectly() { - - listener.onBeforeDelete(new BeforeDeleteEvent(new Document("foo", new Foo()), Object.class, "collection")); - - assertThat(appender.list.get(0).getFormattedMessage()).startsWith("onBeforeDelete: { \"foo\""); - } - - @Test // DATAMONGO-1645 - public void shouldSerializeAfterDeleteEventEventCorrectly() { - - listener.onAfterDelete(new AfterDeleteEvent(new Document("foo", new Foo()), Object.class, "collection")); - - assertThat(appender.list.get(0).getFormattedMessage()).startsWith("onAfterDelete: { \"foo\""); - } - - static class Foo { - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/PersonBeforeSaveListener.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/PersonBeforeSaveListener.java deleted file mode 100644 index 9b7d6fef91..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/PersonBeforeSaveListener.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping.event; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.context.ApplicationEvent; -import org.springframework.data.mongodb.core.mapping.PersonPojoStringId; - -public class PersonBeforeSaveListener extends AbstractMongoEventListener { - - public final List seenEvents = new ArrayList(); - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onBeforeSave(java.lang.Object, com.mongodb.Document) - */ - @Override - public void onBeforeSave(BeforeSaveEvent event) { - seenEvents.add(event); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/SimpleMappingEventListener.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/SimpleMappingEventListener.java deleted file mode 100644 index 07913e9d19..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/SimpleMappingEventListener.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2011-2021 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.mapping.event; - -import java.util.ArrayList; - -/** - * @author Mark Pollak - * @author Oliver Gierke - * @author Christoph Leiter - * @author Christoph Strobl - */ -public class SimpleMappingEventListener extends AbstractMongoEventListener { - - public final ArrayList> onBeforeConvertEvents = new ArrayList>(); - public final ArrayList> onBeforeSaveEvents = new ArrayList>(); - public final ArrayList> onAfterSaveEvents = new ArrayList>(); - public final ArrayList> onAfterLoadEvents = new ArrayList>(); - public final ArrayList> onAfterConvertEvents = new ArrayList>(); - public final ArrayList> onBeforeDeleteEvents = new ArrayList>(); - public final ArrayList> onAfterDeleteEvents = new ArrayList>(); - - @Override - public void onBeforeConvert(BeforeConvertEvent event) { - onBeforeConvertEvents.add(event); - } - - @Override - public void onBeforeSave(BeforeSaveEvent event) { - onBeforeSaveEvents.add(event); - } - - @Override - public void onAfterSave(AfterSaveEvent event) { - onAfterSaveEvents.add(event); - } - - @Override - public void onAfterLoad(AfterLoadEvent event) { - onAfterLoadEvents.add(event); - } - - @Override - public void onAfterConvert(AfterConvertEvent event) { - onAfterConvertEvents.add(event); - } - - @Override - public void onAfterDelete(AfterDeleteEvent event) { - onAfterDeleteEvents.add(event); - } - - @Override - public void onBeforeDelete(BeforeDeleteEvent event) { - onBeforeDeleteEvents.add(event); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/User.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/User.java deleted file mode 100644 index 1477a7e8cc..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/User.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012-2021 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.mapping.event; - -import javax.validation.constraints.Min; -import javax.validation.constraints.Size; - -/** - * Class used to test JSR-303 validation - * {@link org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener} - * - * @author Maciej Walkowiak - */ -public class User { - - @Size(min = 10) private String name; - - @Min(18) private Integer age; - - public User(String name, Integer age) { - this.name = name; - this.age = age; - } - - public String getName() { - return name; - } - - public Integer getAge() { - return age; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ValidatingMongoEventListenerTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ValidatingMongoEventListenerTests.java deleted file mode 100644 index 3e84ffbd23..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/ValidatingMongoEventListenerTests.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2021 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.mapping.event; - -import static org.assertj.core.api.Assertions.*; - -import javax.validation.ConstraintViolationException; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -/** - * Integration test for {@link ValidatingMongoEventListener}. - * - * @author Maciej Walkowiak - * @author Oliver Gierke - * @author Christoph Strobl - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration -class ValidatingMongoEventListenerTests { - - @Autowired MongoTemplate mongoTemplate; - - @Test // DATAMONGO-36 - void shouldThrowConstraintViolationException() { - - User user = new User("john", 17); - - assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> mongoTemplate.save(user)) - .satisfies(e -> { - assertThat(e.getConstraintViolations()).hasSize(2); - }); - } - - @Test - void shouldNotThrowAnyExceptions() { - mongoTemplate.save(new User("john smith", 18)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/ContentAndVersion.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/ContentAndVersion.java deleted file mode 100644 index f469aec7bf..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/ContentAndVersion.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.springframework.data.mongodb.core.mapreduce; - -public class ContentAndVersion { - - private String id; - - private String document_id; - - private String content; - - private String author; - - private Long version; - - private Long value; - - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public String getDocumentId() { - return document_id; - } - - public Long getValue() { - return value; - } - - public void setValue(Long value) { - this.value = value; - } - - public void setDocumentId(String documentId) { - this.document_id = documentId; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - @Override - public String toString() { - return "ContentAndVersion [id=" + id + ", document_id=" + document_id + ", content=" + content + ", author=" - + author + ", version=" + version + ", value=" + value + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/GroupByTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/GroupByTests.java deleted file mode 100644 index 6aa7dd5ffa..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/GroupByTests.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2011-2021 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.mapreduce; - -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.data.Offset.offset; -import static org.springframework.data.mongodb.core.mapreduce.GroupBy.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.MongoServerCondition; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.client.MongoCollection; - -/** - * Integration tests for group-by operations. - * - * @author Mark Pollack - * @author Oliver Gierke - * @author Christoph Strobl - */ -@ExtendWith({ SpringExtension.class, MongoServerCondition.class }) -@EnableIfMongoServerVersion(isLessThan = "4.1") -@ContextConfiguration("classpath:infrastructure.xml") -public class GroupByTests { - - static final String GROUP_TEST_COLLECTION = "group_test_collection"; - - @Autowired MongoTemplate mongoTemplate; - - @BeforeEach - public void setUp() { - cleanDb(); - } - - protected void cleanDb() { - mongoTemplate.dropCollection(mongoTemplate.getCollectionName(XObject.class)); - mongoTemplate.dropCollection("group_test_collection"); - } - - @Test - public void singleKeyCreation() { - - Document gc = new GroupBy("a").getGroupByObject(); - - assertThat(gc).isEqualTo(Document.parse("{ \"key\" : { \"a\" : 1} , \"$reduce\" : null , \"initial\" : null }")); - } - - @Test - public void multipleKeyCreation() { - - Document gc = GroupBy.key("a", "b").getGroupByObject(); - - assertThat(gc).isEqualTo( - Document.parse("{ \"key\" : { \"a\" : 1 , \"b\" : 1} , \"$reduce\" : null , \"initial\" : null }")); - } - - @Test - public void keyFunctionCreation() { - - Document gc = GroupBy.keyFunction("classpath:keyFunction.js").getGroupByObject(); - - assertThat(gc).isEqualTo( - Document.parse("{ \"$keyf\" : \"classpath:keyFunction.js\" , \"$reduce\" : null , \"initial\" : null }")); - } - - @Test - public void simpleGroupFunction() { - - createGroupByData(); - GroupByResults results = mongoTemplate.group(GROUP_TEST_COLLECTION, GroupBy.key("x") - .initialDocument(new Document("count", 0)).reduceFunction("function(doc, prev) { prev.count += 1 }"), - XObject.class); - - assertMapReduceResults(results); - } - - @Test - public void simpleGroupWithKeyFunction() { - - createGroupByData(); - GroupByResults results = mongoTemplate - .group( - GROUP_TEST_COLLECTION, GroupBy.keyFunction("function(doc) { return { x : doc.x }; }") - .initialDocument("{ count: 0 }").reduceFunction("function(doc, prev) { prev.count += 1 }"), - XObject.class); - - assertMapReduceResults(results); - } - - @Test - public void simpleGroupWithFunctionsAsResources() { - - createGroupByData(); - GroupByResults results = mongoTemplate.group(GROUP_TEST_COLLECTION, - GroupBy.keyFunction("classpath:keyFunction.js").initialDocument("{ count: 0 }") - .reduceFunction("classpath:groupReduce.js"), - XObject.class); - - assertMapReduceResults(results); - } - - @Test - public void simpleGroupWithQueryAndFunctionsAsResources() { - - createGroupByData(); - GroupByResults results = mongoTemplate.group(where("x").gt(0), GROUP_TEST_COLLECTION, - keyFunction("classpath:keyFunction.js").initialDocument("{ count: 0 }") - .reduceFunction("classpath:groupReduce.js"), - XObject.class); - - assertMapReduceResults(results); - } - - private void assertMapReduceResults(GroupByResults results) { - - int numResults = 0; - for (XObject xObject : results) { - if (xObject.getX() == 1) { - assertThat(xObject.getCount()).isCloseTo(2, offset(0.001f)); - } - if (xObject.getX() == 2) { - assertThat(xObject.getCount()).isCloseTo(1, offset(0.001f)); - } - if (xObject.getX() == 3) { - assertThat(xObject.getCount()).isCloseTo(3, offset(0.001f)); - } - numResults++; - } - assertThat(numResults).isEqualTo(3); - assertThat(results.getKeys()).isEqualTo(3); - assertThat(results.getCount()).isCloseTo(6, offset(0.001)); - } - - private void createGroupByData() { - - MongoCollection c = mongoTemplate.getDb().getCollection(GROUP_TEST_COLLECTION, Document.class); - - c.insertOne(new Document("x", 1)); - c.insertOne(new Document("x", 1)); - c.insertOne(new Document("x", 2)); - c.insertOne(new Document("x", 3)); - c.insertOne(new Document("x", 3)); - c.insertOne(new Document("x", 3)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCountsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCountsUnitTests.java deleted file mode 100644 index 182eb79b2d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCountsUnitTests.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012-2021 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.mapreduce; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link MapReduceCounts}. - * - * @author Oliver Gierke - */ -public class MapReduceCountsUnitTests { - - @Test // DATACMNS-378 - public void equalsForSameNumberValues() { - - MapReduceCounts left = new MapReduceCounts(1L, 1L, 1L); - MapReduceCounts right = new MapReduceCounts(1L, 1L, 1L); - - assertThat(left).isEqualTo(right); - assertThat(right).isEqualTo(left); - assertThat(left.hashCode()).isEqualTo(right.hashCode()); - } - - @Test // DATACMNS-378 - public void notEqualForDifferentNumberValues() { - - MapReduceCounts left = new MapReduceCounts(1L, 1L, 1L); - MapReduceCounts right = new MapReduceCounts(1L, 2L, 1L); - - assertThat(left).isNotEqualTo(right); - assertThat(right).isNotEqualTo(left); - assertThat(left.hashCode()).isNotEqualTo(right.hashCode()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptionsTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptionsTests.java deleted file mode 100644 index b0d3d890e8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptionsTests.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2010-2021 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.mapreduce; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import org.junit.jupiter.api.Test; - -/** - * @author Mark Pollack - * @author Oliver Gierke - * @author Christoph Strobl - */ -public class MapReduceOptionsTests { - - @Test - public void testFinalize() { - new MapReduceOptions().finalizeFunction("code"); - } - - @Test // DATAMONGO-1334 - public void limitShouldBeIncludedCorrectly() { - - MapReduceOptions options = new MapReduceOptions(); - options.limit(10); - - assertThat(options.getOptionsObject()).containsEntry("limit", 10); - } - - @Test // DATAMONGO-1334 - public void limitShouldNotBePresentInDocumentWhenNotSet() { - assertThat(new MapReduceOptions().getOptionsObject()).doesNotContainKey("limit"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResultsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResultsUnitTests.java deleted file mode 100644 index 6edeefc954..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResultsUnitTests.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2012-2021 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.mapreduce; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Collections; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link MapReduceResults}. - * - * @author Oliver Gierke - */ -public class MapReduceResultsUnitTests { - - @Test // DATAMONGO-428 - public void resolvesOutputCollectionForPlainResult() { - - Document rawResult = new Document("result", "FOO"); - MapReduceResults results = new MapReduceResults(Collections.emptyList(), rawResult); - - assertThat(results.getOutputCollection()).isEqualTo("FOO"); - } - - @Test // DATAMONGO-428 - public void resolvesOutputCollectionForDocumentResult() { - - Document rawResult = new Document("result", new Document("collection", "FOO")); - MapReduceResults results = new MapReduceResults(Collections.emptyList(), rawResult); - - assertThat(results.getOutputCollection()).isEqualTo("FOO"); - } - - @Test // DATAMONGO-378 - public void handlesLongTotalInResult() { - - Document inner = new Document("total", 1L); - inner.put("mapTime", 1L); - inner.put("emitLoop", 1); - - Document source = new Document("timing", inner); - new MapReduceResults(Collections.emptyList(), source); - } - - @Test // DATAMONGO-378 - public void handlesLongResultsForCounts() { - - Document inner = new Document("input", 1L); - inner.put("emit", 1L); - inner.put("output", 1); - - Document source = new Document("counts", inner); - new MapReduceResults(Collections.emptyList(), source); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTests.java deleted file mode 100644 index 5672ee2d65..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTests.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright 2011-2021 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.mapreduce; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.mapreduce.MapReduceOptions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.bson.Document; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.geo.Box; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.client.MongoCollection; - -/** - * Integration test for {@link MongoTemplate}'s Map-Reduce operations - * - * @author Mark Pollack - * @author Thomas Darimont - * @author Mark Paluch - * @author Christoph Strobl - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:infrastructure.xml") -public class MapReduceTests { - - private static final String MAP_FUNCTION = "function(){ for ( var i=0; i results = mongoTemplate.mapReduce("jmr2", map, reduce, - new MapReduceOptions().outputCollection("jmr2_out"), ContentAndVersion.class); - - assertThat(results).hasSize(3); - for (ContentAndVersion cv : results) { - - if ("Resume".equals(cv.getId())) { - assertThat(cv.getValue().longValue()).isEqualTo(6); - } - if ("Schema".equals(cv.getId())) { - assertThat(cv.getValue().longValue()).isEqualTo(2); - } - if ("mongoDB How-To".equals(cv.getId())) { - assertThat(cv.getValue().longValue()).isEqualTo(2); - } - } - - } - - @Test // DATAMONGO-260 - public void testIssue260Part2() { - - createNumberAndVersionData(); - String map = "function () { emit(this.number, this.version); }"; - String reduce = "function (key, values) { return Math.max.apply(Math, values); }"; - - MapReduceResults results = mongoTemplate.mapReduce("jmr2", map, reduce, - new MapReduceOptions().outputCollection("jmr2_out"), NumberAndVersion.class); - - for (NumberAndVersion nv : results) { - if ("1".equals(nv.getId())) { - assertThat(nv.getValue().longValue()).isEqualTo(2); - } - if ("2".equals(nv.getId())) { - assertThat(nv.getValue().longValue()).isEqualTo(6); - } - if ("3".equals(nv.getId())) { - assertThat(nv.getValue().longValue()).isEqualTo(2); - } - } - - assertThat(results).hasSize(3); - } - - @Test // DATADOC-7, DATAMONGO-2027 - public void testMapReduce() { - - performMapReduce(false, false); - - List results = mongoTemplate.find(new Query(), ValueObject.class, "jmr1_out"); - assertMapReduceResults(copyToMap(results)); - } - - @Test // DATADOC-7, DATAMONGO-2027 - public void testMapReduceInline() { - - performMapReduce(true, false); - assertThat(template.collectionExists("jmr1_out")).isFalse(); - } - - @Test // DATAMONGO-2027 - public void mapReduceWithOutputDatabaseShouldWorkCorrectly() { - - createMapReduceData(); - - mongoTemplate.mapReduce("jmr1", MAP_FUNCTION, REDUCE_FUNCTION, - options().outputDatabase("jmr1-out-db").outputCollection("jmr1-out"), ValueObject.class); - - assertThat( - template.getMongoDbFactory().getMongoDatabase("jmr1-out-db").listCollectionNames().into(new ArrayList<>())) - .contains("jmr1-out"); - } - - @Test // DATADOC-7 - public void testMapReduceWithQuery() { - performMapReduce(false, true); - } - - @Test // DATADOC-7 - public void testMapReduceInlineWithScope() { - - createMapReduceData(); - - Map scopeVariables = new HashMap(); - scopeVariables.put("exclude", "a"); - - String mapWithExcludeFunction = "function(){ for ( var i=0; i results = mongoTemplate.mapReduce("jmr1", mapWithExcludeFunction, REDUCE_FUNCTION, - new MapReduceOptions().scopeVariables(scopeVariables).outputTypeInline(), ValueObject.class); - - assertThat(copyToMap(results)) // - .hasSize(3) // - .containsEntry("b", 2F) // - .containsEntry("c", 2F) // - .containsEntry("d", 1F); - } - - @Test // DATADOC-7 - public void testMapReduceExcludeQuery() { - - createMapReduceData(); - - Query query = new Query(where("x").ne(new String[] { "a", "b" })); - MapReduceResults results = mongoTemplate.mapReduce(query, "jmr1", MAP_FUNCTION, REDUCE_FUNCTION, - ValueObject.class); - - assertThat(copyToMap(results)) // - .hasSize(3) // - .containsEntry("b", 1F) // - .containsEntry("c", 2F) // - .containsEntry("d", 1F); - } - - @Test // DATAMONGO-938 - public void mapReduceShouldUseQueryMapper() { - - MongoCollection c = mongoTemplate.getDb().getCollection("jmrWithGeo", Document.class); - - c.insertOne(new Document("x", Arrays.asList("a", "b")).append("loc", Arrays.asList(0D, 0D))); - c.insertOne(new Document("x", Arrays.asList("b", "c")).append("loc", Arrays.asList(0D, 0D))); - c.insertOne(new Document("x", Arrays.asList("c", "d")).append("loc", Arrays.asList(0D, 0D))); - - Query query = new Query(where("x").ne(new String[] { "a", "b" }).and("loc") - .within(new Box(new double[] { 0, 0 }, new double[] { 1, 1 }))); - - MapReduceResults results = template.mapReduce(query, "jmrWithGeo", MAP_FUNCTION, REDUCE_FUNCTION, - ValueObject.class); - - assertThat(copyToMap(results)) // - .hasSize(3) // - .containsEntry("b", 1F) // - .containsEntry("c", 2F) // - .containsEntry("d", 1F); - } - - private void performMapReduce(boolean inline, boolean withQuery) { - - createMapReduceData(); - MapReduceResults results; - if (inline) { - if (withQuery) { - results = mongoTemplate.mapReduce(new Query(), "jmr1", "classpath:map.js", "classpath:reduce.js", - ValueObject.class); - } else { - results = mongoTemplate.mapReduce("jmr1", MAP_FUNCTION, REDUCE_FUNCTION, ValueObject.class); - } - } else { - if (withQuery) { - results = mongoTemplate.mapReduce(new Query(), "jmr1", MAP_FUNCTION, REDUCE_FUNCTION, - options().outputCollection("jmr1_out"), ValueObject.class); - } else { - results = mongoTemplate.mapReduce("jmr1", MAP_FUNCTION, REDUCE_FUNCTION, - new MapReduceOptions().outputCollection("jmr1_out"), ValueObject.class); - } - } - - assertMapReduceResults(copyToMap(results)); - } - - private void createMapReduceData() { - - MongoCollection c = mongoTemplate.getDb().getCollection("jmr1", Document.class); - c.insertOne(new Document("x", Arrays.asList("a", "b"))); - c.insertOne(new Document("x", Arrays.asList("b", "c"))); - c.insertOne(new Document("x", Arrays.asList("c", "d"))); - } - - private Map copyToMap(Iterable results) { - - List valueObjects = new ArrayList<>(); - for (ValueObject valueObject : results) { - valueObjects.add(valueObject); - } - - Map m = new HashMap<>(); - for (ValueObject vo : valueObjects) { - m.put(vo.getId(), vo.getValue()); - } - return m; - } - - private void assertMapReduceResults(Map map) { - - assertThat(map) // - .hasSize(4) // - .containsEntry("a", 1F) // - .containsEntry("b", 2F) // - .containsEntry("c", 2F) // - .containsEntry("d", 1F); - } - - private void createNumberAndVersionData() { - - NumberAndVersion nv1 = new NumberAndVersion(); - nv1.setNumber(1L); - nv1.setVersion(1L); - template.save(nv1, "jmr2"); - - NumberAndVersion nv2 = new NumberAndVersion(); - nv2.setNumber(1L); - nv2.setVersion(2L); - template.save(nv2, "jmr2"); - - NumberAndVersion nv3 = new NumberAndVersion(); - nv3.setNumber(2L); - nv3.setVersion(6L); - template.save(nv3, "jmr2"); - - NumberAndVersion nv4 = new NumberAndVersion(); - nv4.setNumber(3L); - nv4.setVersion(1L); - template.save(nv4, "jmr2"); - - NumberAndVersion nv5 = new NumberAndVersion(); - nv5.setNumber(3L); - nv5.setVersion(2L); - template.save(nv5, "jmr2"); - - } - - private void createContentAndVersionData() { - /* - { "_id" : 1, "document_id" : "mongoDB How-To", "author" : "Amos King", "content" : "...", "version" : 1 } - { "_id" : 2, "document_id" : "mongoDB How-To", "author" : "Amos King", "content" : "...", "version" : 1.1 } - { "_id" : 3, "document_id" : "Resume", "author" : "Author", "content" : "...", "version" : 6 } - { "_id" : 4, "document_id" : "Schema", "author" : "Someone Else", "content" : "...", "version" : 0.9 } - { "_id" : 5, "document_id" : "Schema", "author" : "Someone Else", "content" : "...", "version" : 1 } - - */ - ContentAndVersion cv1 = new ContentAndVersion(); - cv1.setDocumentId("mongoDB How-To"); - cv1.setAuthor("Amos King"); - cv1.setContent("..."); - cv1.setVersion(1L); - template.save(cv1, "jmr2"); - - ContentAndVersion cv2 = new ContentAndVersion(); - cv2.setDocumentId("mongoDB How-To"); - cv2.setAuthor("Amos King"); - cv2.setContent("..."); - cv2.setVersion(2L); - template.save(cv2, "jmr2"); - - ContentAndVersion cv3 = new ContentAndVersion(); - cv3.setDocumentId("Resume"); - cv3.setAuthor("Author"); - cv3.setContent("..."); - cv3.setVersion(6L); - template.save(cv3, "jmr2"); - - ContentAndVersion cv4 = new ContentAndVersion(); - cv4.setDocumentId("Schema"); - cv4.setAuthor("Someone Else"); - cv4.setContent("..."); - cv4.setVersion(1L); - template.save(cv4, "jmr2"); - - ContentAndVersion cv5 = new ContentAndVersion(); - cv5.setDocumentId("Schema"); - cv5.setAuthor("Someone Else"); - cv5.setContent("..."); - cv5.setVersion(2L); - template.save(cv5, "jmr2"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/NumberAndVersion.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/NumberAndVersion.java deleted file mode 100644 index 12e9402622..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/NumberAndVersion.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.springframework.data.mongodb.core.mapreduce; - -public class NumberAndVersion { - - private String id; - private Long number; - private Long version; - private Long value; - - public Long getValue() { - return value; - } - - public void setValue(Long value) { - this.value = value; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public Long getNumber() { - return number; - } - - public void setNumber(Long number) { - this.number = number; - } - - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - @Override - public String toString() { - return "NumberAndVersion [id=" + id + ", number=" + number + ", version=" + version + ", value=" + value + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/ReactiveMapReduceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/ReactiveMapReduceTests.java deleted file mode 100644 index 0ba4532dbb..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/ReactiveMapReduceTests.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2018-2021 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.mapreduce; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.Data; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.Arrays; - -import org.bson.Document; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mongodb.core.Person; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * @author Christoph Strobl - * @author Mark Paluch - * @author Mathieu Ouellet - * @currentRead Beyond the Shadows - Brent Weeks - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:reactive-infrastructure.xml") -public class ReactiveMapReduceTests { - - @Autowired SimpleReactiveMongoDatabaseFactory factory; - @Autowired ReactiveMongoTemplate template; - - private String mapFunction = "function(){ for ( var i=0; i { - assertThat(result).containsExactlyInAnyOrder(new ValueObject("a", 1), new ValueObject("b", 2), - new ValueObject("c", 2), new ValueObject("d", 1)); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-2027 - public void shouldStoreResultInCollection() { - - createMapReduceData(); - - template.mapReduce(new Query(), Person.class, "jmr1", ValueObject.class, mapFunction, reduceFunction, // - MapReduceOptions.options().outputCollection("mapreduceout")).as(StepVerifier::create) // - .expectNextCount(4) // - .verifyComplete(); - - template.find(new Query(), ValueObject.class, "mapreduceout").buffer(4).as(StepVerifier::create) // - .consumeNextWith(result -> { - assertThat(result).containsExactlyInAnyOrder(new ValueObject("a", 1), new ValueObject("b", 2), - new ValueObject("c", 2), new ValueObject("d", 1)); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1890 - public void mapReduceWithInlineAndFilterQuery() { - - createMapReduceData(); - - template - .mapReduce(query(where("x").ne(new String[] { "a", "b" })), ValueObject.class, "jmr1", ValueObject.class, - mapFunction, reduceFunction, MapReduceOptions.options()) - .buffer(4).as(StepVerifier::create) // - .consumeNextWith(result -> { - assertThat(result).containsExactlyInAnyOrder(new ValueObject("b", 1), new ValueObject("c", 2), - new ValueObject("d", 1)); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1890, DATAMONGO-2027 - public void mapReduceWithOutputCollection() { - - createMapReduceData(); - - template - .mapReduce(new Query(), ValueObject.class, "jmr1", ValueObject.class, mapFunction, reduceFunction, - MapReduceOptions.options().outputCollection("jmr1_out")) - .as(StepVerifier::create).expectNextCount(4).verifyComplete(); - - template.find(new Query(), ValueObject.class, "jmr1_out").buffer(4).as(StepVerifier::create) // - .consumeNextWith(result -> { - assertThat(result).containsExactlyInAnyOrder(new ValueObject("a", 1), new ValueObject("b", 2), - new ValueObject("c", 2), new ValueObject("d", 1)); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-2027 - public void mapReduceWithOutputDatabase() { - - createMapReduceData(); - - template - .mapReduce(new Query(), ValueObject.class, "jmr1", ValueObject.class, mapFunction, reduceFunction, - MapReduceOptions.options().outputDatabase("reactive-jrm1-out-db").outputCollection("jmr1_out")) - .as(StepVerifier::create).expectNextCount(4).verifyComplete(); - - factory.getMongoDatabase("reactive-jrm1-out-db").flatMapMany(MongoDatabase::listCollectionNames).buffer(10) - .map(list -> list.contains("jmr1_out")).as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-1890 - public void mapReduceWithInlineAndMappedFilterQuery() { - - createMapReduceData(); - - template - .mapReduce(query(where("values").ne(new String[] { "a", "b" })), MappedFieldsValueObject.class, "jmr1", - ValueObject.class, mapFunction, reduceFunction, MapReduceOptions.options()) - .buffer(4).as(StepVerifier::create) // - .consumeNextWith(result -> { - assertThat(result).containsExactlyInAnyOrder(new ValueObject("b", 1), new ValueObject("c", 2), - new ValueObject("d", 1)); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1890 - public void mapReduceWithInlineFilterQueryAndExtractedCollection() { - - createMapReduceData(); - - template - .mapReduce(query(where("values").ne(new String[] { "a", "b" })), MappedFieldsValueObject.class, - ValueObject.class, mapFunction, reduceFunction, MapReduceOptions.options()) - .buffer(4).as(StepVerifier::create) // - .consumeNextWith(result -> { - assertThat(result).containsExactlyInAnyOrder(new ValueObject("b", 1), new ValueObject("c", 2), - new ValueObject("d", 1)); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1890 - public void throwsExceptionWhenTryingToLoadFunctionsFromDisk() { - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> template.mapReduce(new Query(), - Person.class, "foo", ValueObject.class, "classpath:map.js", "classpath:reduce.js", MapReduceOptions.options())) - .withMessageContaining("classpath:map.js"); - } - - private void createMapReduceData() { - - factory.getMongoDatabase() - .flatMapMany(db -> db.getCollection("jmr1", Document.class) - .insertMany(Arrays.asList(new Document("x", Arrays.asList("a", "b")), - new Document("x", Arrays.asList("b", "c")), new Document("x", Arrays.asList("c", "d"))))) - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - } - - @org.springframework.data.mongodb.core.mapping.Document("jmr1") - @Data - static class MappedFieldsValueObject { - - @Field("x") String[] values; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/ValueObject.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/ValueObject.java deleted file mode 100644 index e9e6531868..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/ValueObject.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2011-2021 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.mapreduce; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @author Mark Pollack - * @author Oliver Gierke - * @author Christoph Strobl - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class ValueObject { - - private String id; - - private float value; - - @Override - public String toString() { - return "ValueObject [id=" + id + ", value=" + value + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/XObject.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/XObject.java deleted file mode 100644 index 1ebda35985..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapreduce/XObject.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.springframework.data.mongodb.core.mapreduce; - -public class XObject { - - private float x; - - private float count; - - public float getX() { - return x; - } - - public void setX(float x) { - this.x = x; - } - - public float getCount() { - return count; - } - - public void setCount(float count) { - this.count = count; - } - - @Override - public String toString() { - return "XObject [x=" + x + " count = " + count + "]"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTaskUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTaskUnitTests.java deleted file mode 100644 index 17778f1333..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTaskUnitTests.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2019-2021 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.messaging; - -import static org.mockito.Mockito.*; - -import java.util.UUID; - -import org.bson.BsonDocument; -import org.bson.BsonString; -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; - -import com.mongodb.client.ChangeStreamIterable; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoCursor; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.model.changestream.ChangeStreamDocument; - -/** - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -class ChangeStreamTaskUnitTests { - - ChangeStreamTask task; - @Mock MongoTemplate template; - @Mock MongoDatabase mongoDatabase; - @Mock MongoCollection mongoCollection; - @Mock ChangeStreamIterable changeStreamIterable; - private MongoConverter converter; - - @BeforeEach - void setUp() { - - MongoMappingContext mappingContext = new MongoMappingContext(); - converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); - - when(template.getConverter()).thenReturn(converter); - when(template.getDb()).thenReturn(mongoDatabase); - - when(mongoDatabase.getCollection(any())).thenReturn(mongoCollection); - - when(mongoCollection.watch(eq(Document.class))).thenReturn(changeStreamIterable); - - when(changeStreamIterable.fullDocument(any())).thenReturn(changeStreamIterable); - } - - @Test // DATAMONGO-2258 - void shouldNotBreakLovelaceBehavior() { - - BsonDocument resumeToken = new BsonDocument("token", new BsonString(UUID.randomUUID().toString())); - when(changeStreamIterable.resumeAfter(any())).thenReturn(changeStreamIterable); - - ChangeStreamRequest request = ChangeStreamRequest.builder() // - .collection("start-wars") // - .resumeToken(resumeToken) // - .publishTo(message -> {}) // - .build(); - - initTask(request, Document.class); - - verify(changeStreamIterable).resumeAfter(eq(resumeToken)); - } - - @Test // DATAMONGO-2258 - void shouldApplyResumeAfterToChangeStream() { - - when(changeStreamIterable.resumeAfter(any())).thenReturn(changeStreamIterable); - - BsonDocument resumeToken = new BsonDocument("token", new BsonString(UUID.randomUUID().toString())); - - ChangeStreamRequest request = ChangeStreamRequest.builder() // - .collection("start-wars") // - .resumeAfter(resumeToken) // - .publishTo(message -> {}) // - .build(); - - initTask(request, Document.class); - - verify(changeStreamIterable).resumeAfter(eq(resumeToken)); - } - - @Test // DATAMONGO-2258 - void shouldApplyStartAfterToChangeStream() { - - when(changeStreamIterable.startAfter(any())).thenReturn(changeStreamIterable); - - BsonDocument resumeToken = new BsonDocument("token", new BsonString(UUID.randomUUID().toString())); - - ChangeStreamRequest request = ChangeStreamRequest.builder() // - .collection("start-wars") // - .startAfter(resumeToken) // - .publishTo(message -> {}) // - .build(); - - initTask(request, Document.class); - - verify(changeStreamIterable).startAfter(eq(resumeToken)); - } - - private MongoCursor> initTask(ChangeStreamRequest request, Class targetType) { - - ChangeStreamTask task = new ChangeStreamTask(template, request, targetType, er -> {}); - return task.initCursor(template, request.getRequestOptions(), targetType); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTests.java deleted file mode 100644 index fac377e501..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTests.java +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright 2018-2021 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.messaging; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.core.messaging.SubscriptionUtils.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.Duration; -import java.time.Instant; -import java.util.List; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import org.bson.BsonDocument; -import org.bson.Document; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.ChangeStreamOptions; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.messaging.ChangeStreamRequest.ChangeStreamRequestOptions; -import org.springframework.data.mongodb.core.messaging.ChangeStreamTask.ChangeStreamEventMessage; -import org.springframework.data.mongodb.core.messaging.Message.MessageProperties; -import org.springframework.data.mongodb.core.messaging.SubscriptionUtils.*; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Update; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.MongoVersion; -import org.springframework.data.mongodb.test.util.Template; - -import com.mongodb.client.model.changestream.ChangeStreamDocument; -import com.mongodb.client.model.changestream.FullDocument; -import org.junitpioneer.jupiter.RepeatFailedTest; - -/** - * Integration test for subscribing to a {@link com.mongodb.operation.ChangeStreamBatchCursor} inside the - * {@link DefaultMessageListenerContainer} using {@link ChangeStreamRequest}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith({ MongoTemplateExtension.class }) -@EnableIfReplicaSetAvailable -class ChangeStreamTests { - - private static ThreadPoolExecutor executor; - - @Template(initialEntitySet = User.class, replicaSet = true) // - private static MongoTestTemplate template; - - private MessageListenerContainer container; - - private User jellyBelly; - private User huffyFluffy; - private User sugarSplashy; - - @BeforeAll - static void beforeClass() { - executor = new ThreadPoolExecutor(2, 2, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); - } - - @BeforeEach - void setUp() { - - template.dropCollection(User.class); - - container = new DefaultMessageListenerContainer(template, executor); - container.start(); - - jellyBelly = new User(); - jellyBelly.id = "id-1"; - jellyBelly.userName = "jellyBelly"; - jellyBelly.age = 7; - - huffyFluffy = new User(); - huffyFluffy.id = "id-2"; - huffyFluffy.userName = "huffyFluffy"; - huffyFluffy.age = 7; - - sugarSplashy = new User(); - sugarSplashy.id = "id-3"; - sugarSplashy.userName = "sugarSplashy"; - sugarSplashy.age = 5; - } - - @AfterEach - void tearDown() { - container.stop(); - } - - @AfterAll - static void afterClass() { - executor.shutdown(); - } - - @Test // DATAMONGO-1803 - void readsPlainDocumentMessageCorrectly() throws InterruptedException { - - CollectingMessageListener, Document> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = new ChangeStreamRequest<>(messageListener, - new ChangeStreamRequestOptions(null, "user", Duration.ofMillis(10), ChangeStreamOptions.builder().build())); - - Subscription subscription = container.register(request, Document.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - - awaitMessages(messageListener, 1); - - Message, Document> message1 = messageListener.getFirstMessage(); - - assertThat(message1.getRaw()).isNotNull(); - assertThat(message1.getProperties()) - .isEqualTo(MessageProperties.builder().collectionName("user").databaseName("change-stream-tests").build()); - assertThat(message1.getBody()).isEqualTo(new Document("_id", "id-1").append("user_name", "jellyBelly") - .append("age", 7).append("_class", User.class.getName())); - } - - @Test // DATAMONGO-1803 - void useSimpleAggregationToFilterMessages() throws InterruptedException { - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = ChangeStreamRequest.builder(messageListener) // - .collection("user") // - .filter(newAggregation(match(where("age").is(7)))) // - .maxAwaitTime(Duration.ofMillis(10)) // - .build(); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - template.save(sugarSplashy); - template.save(huffyFluffy); - - awaitMessages(messageListener); - - List messageBodies = messageListener.getMessages().stream().map(Message::getBody) - .collect(Collectors.toList()); - - assertThat(messageBodies).hasSize(2).doesNotContain(sugarSplashy); - } - - @Test // DATAMONGO-1803 - @MongoVersion(asOf = "4.0") - void useAggregationToFilterMessages() throws InterruptedException { - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = ChangeStreamRequest.builder(messageListener) // - .collection("user") // - .filter(newAggregation(match( - new Criteria().orOperator(where("user_name").is("huffyFluffy"), where("user_name").is("jellyBelly"))))) // - .maxAwaitTime(Duration.ofMillis(10)) // - .build(); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - template.save(sugarSplashy); - template.save(huffyFluffy); - - awaitMessages(messageListener); - - List messageBodies = messageListener.getMessages().stream().map(Message::getBody) - .collect(Collectors.toList()); - - assertThat(messageBodies).hasSize(2).doesNotContain(sugarSplashy); - } - - @RepeatFailedTest(3) // DATAMONGO-1803 - void mapsTypedAggregationToFilterMessages() throws InterruptedException { - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = ChangeStreamRequest.builder() // - .collection("user") // - .publishTo(messageListener) // - .filter(newAggregation(User.class, - match(new Criteria().orOperator(where("userName").is("huffyFluffy"), where("userName").is("jellyBelly"))))) // - .maxAwaitTime(Duration.ofMillis(10)) // - .build(); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - template.save(sugarSplashy); - template.save(huffyFluffy); - - awaitMessages(messageListener, 2); - - List messageBodies = messageListener.getMessages().stream().map(Message::getBody) - .collect(Collectors.toList()); - - assertThat(messageBodies).hasSize(2).doesNotContain(sugarSplashy); - } - - @Test // DATAMONGO-1803 - void mapsReservedWordsCorrectly() throws InterruptedException { - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = ChangeStreamRequest.builder() // - .collection("user") // - .publishTo(messageListener) // - .filter(newAggregation(User.class, match(where("operationType").is("replace")))) // - .maxAwaitTime(Duration.ofMillis(10)) // - .build(); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - template.save(sugarSplashy); - - User replacement = new User(); - replacement.id = jellyBelly.id; - replacement.userName = new StringBuilder(jellyBelly.userName).reverse().toString(); - replacement.age = jellyBelly.age; - - template.save(replacement); - - awaitMessages(messageListener, 1); - - List messageBodies = messageListener.getMessages().stream().map(Message::getBody) - .collect(Collectors.toList()); - - assertThat(messageBodies).hasSize(1).containsExactly(replacement); - } - - @Test // DATAMONGO-1803 - void plainAggregationPipelineToFilterMessages() throws InterruptedException { - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = ChangeStreamRequest.builder() // - .collection("user") // - .publishTo(messageListener) // - .filter(new Document("$match", new Document("fullDocument.user_name", "sugarSplashy"))) // - .maxAwaitTime(Duration.ofMillis(10)) // - .build(); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - template.save(sugarSplashy); - template.save(huffyFluffy); - - awaitMessages(messageListener, 1); - - List messageBodies = messageListener.getMessages().stream().map(Message::getBody) - .collect(Collectors.toList()); - - assertThat(messageBodies).hasSize(1).containsExactly(sugarSplashy); - } - - @Test // DATAMONGO-1803 - void resumesCorrectly() throws InterruptedException { - - CollectingMessageListener, User> messageListener1 = new CollectingMessageListener<>(); - Subscription subscription1 = container.register( - new ChangeStreamRequest<>(messageListener1, - new ChangeStreamRequestOptions(null, "user", Duration.ofMillis(10), ChangeStreamOptions.builder().build())), - User.class); - - awaitSubscription(subscription1); - - template.save(jellyBelly); - template.save(sugarSplashy); - template.save(huffyFluffy); - - awaitMessages(messageListener1, 3); - - BsonDocument resumeToken = messageListener1.getFirstMessage().getRaw().getResumeToken(); - - CollectingMessageListener, User> messageListener2 = new CollectingMessageListener<>(); - ChangeStreamRequest subSequentRequest = ChangeStreamRequest.builder().collection("user") - .publishTo(messageListener2).resumeToken(resumeToken).maxAwaitTime(Duration.ofMillis(10)).build(); - - Subscription subscription2 = container.register(subSequentRequest, User.class); - awaitSubscription(subscription2); - - awaitMessages(messageListener2, 2); - - List messageBodies = messageListener2.getMessages().stream().map(Message::getBody) - .collect(Collectors.toList()); - - assertThat(messageBodies).hasSize(2).doesNotContain(jellyBelly); - } - - @Test // DATAMONGO-1803 - void readsAndConvertsMessageBodyCorrectly() throws InterruptedException { - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = new ChangeStreamRequest<>(messageListener, - new ChangeStreamRequestOptions(null, "user", Duration.ofMillis(10), ChangeStreamOptions.builder().build())); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - - awaitMessages(messageListener, 1); - - Message, User> message1 = messageListener.getFirstMessage(); - - assertThat(message1.getRaw()).isNotNull(); - assertThat(message1.getProperties()) - .isEqualTo(MessageProperties.builder().collectionName("user").databaseName("change-stream-tests").build()); - assertThat(message1.getBody()).isEqualTo(jellyBelly); - } - - @Test // DATAMONGO-1803 - void readsAndConvertsUpdateMessageBodyCorrectly() throws InterruptedException { - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = new ChangeStreamRequest<>(messageListener, - new ChangeStreamRequestOptions(null, "user", Duration.ofMillis(10), ChangeStreamOptions.builder().build())); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - - template.update(User.class).matching(query(where("id").is(jellyBelly.id))).apply(Update.update("age", 8)).first(); - - awaitMessages(messageListener, 2); - - assertThat(messageListener.getFirstMessage().getBody()).isEqualTo(jellyBelly); - assertThat(messageListener.getLastMessage().getBody()).isNotNull().hasFieldOrPropertyWithValue("age", 8); - } - - @Test // DATAMONGO-1803 - void readsOnlyDiffForUpdateWhenNotMappedToDomainType() throws InterruptedException { - - CollectingMessageListener, Document> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = new ChangeStreamRequest<>(messageListener, - new ChangeStreamRequestOptions(null, "user", Duration.ofMillis(10), ChangeStreamOptions.builder().build())); - - Subscription subscription = container.register(request, Document.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - - template.update(User.class).matching(query(where("id").is(jellyBelly.id))).apply(Update.update("age", 8)).first(); - - awaitMessages(messageListener, 2); - - assertThat(messageListener.getFirstMessage().getBody()).isEqualTo(new Document("_id", "id-1") - .append("user_name", "jellyBelly").append("age", 7).append("_class", User.class.getName())); - assertThat(messageListener.getLastMessage().getBody()).isNull(); - } - - @Test // DATAMONGO-1803 - void readsOnlyDiffForUpdateWhenOptionsDeclareDefaultExplicitly() throws InterruptedException { - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = ChangeStreamRequest.builder() // - .collection("user") // - .fullDocumentLookup(FullDocument.DEFAULT) // - .maxAwaitTime(Duration.ofMillis(10)) // - .publishTo(messageListener).build(); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - - template.update(User.class).matching(query(where("id").is(jellyBelly.id))).apply(Update.update("age", 8)).first(); - - awaitMessages(messageListener, 2); - - assertThat(messageListener.getFirstMessage().getBody()).isEqualTo(jellyBelly); - assertThat(messageListener.getLastMessage().getBody()).isNull(); - } - - @Test // DATAMONGO-1803 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - void readsFullDocumentForUpdateWhenNotMappedToDomainTypeButLookupSpecified() throws InterruptedException { - - CollectingMessageListener, Document> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = ChangeStreamRequest.builder() // - .collection("user") // - .fullDocumentLookup(FullDocument.UPDATE_LOOKUP) // - .maxAwaitTime(Duration.ofMillis(10)) // - .publishTo(messageListener).build(); - - Subscription subscription = container.register(request, Document.class); - awaitSubscription(subscription); - - template.save(jellyBelly); - - template.update(User.class).matching(query(where("id").is(jellyBelly.id))).apply(Update.update("age", 8)).first(); - - awaitMessages(messageListener, 2); - - assertThat(messageListener.getFirstMessage().getBody()).isEqualTo(new Document("_id", "id-1") - .append("user_name", "jellyBelly").append("age", 7).append("_class", User.class.getName())); - assertThat(messageListener.getLastMessage().getBody()).isEqualTo(new Document("_id", "id-1") - .append("user_name", "jellyBelly").append("age", 8).append("_class", User.class.getName())); - } - - @Test // DATAMONGO-2012, DATAMONGO-2113 - @MongoVersion(asOf = "4.0") - void resumeAtTimestampCorrectly() throws InterruptedException { - - CollectingMessageListener, User> messageListener1 = new CollectingMessageListener<>(); - Subscription subscription1 = container.register( - new ChangeStreamRequest<>(messageListener1, - new ChangeStreamRequestOptions(null, "user", Duration.ofMillis(10), ChangeStreamOptions.builder().build())), - User.class); - - awaitSubscription(subscription1); - - template.save(jellyBelly); - - Thread.sleep(1000); // cluster timestamp is in seconds, so we need to wait at least one. - - template.save(sugarSplashy); - - awaitMessages(messageListener1, 12); - - Instant resumeAt = ((ChangeStreamEventMessage) messageListener1.getLastMessage()).getTimestamp(); - - template.save(huffyFluffy); - - awaitMessages(messageListener1, 3); - - CollectingMessageListener, User> messageListener2 = new CollectingMessageListener<>(); - ChangeStreamRequest subSequentRequest = ChangeStreamRequest.builder() // - .collection("user") // - .resumeAt(resumeAt) // - .publishTo(messageListener2) // - .maxAwaitTime(Duration.ofMillis(10)) // - .build(); - - Subscription subscription2 = container.register(subSequentRequest, User.class); - awaitSubscription(subscription2); - - awaitMessages(messageListener2, 2); - - List messageBodies = messageListener2.getMessages().stream().map(Message::getBody) - .collect(Collectors.toList()); - - assertThat(messageBodies).hasSize(2).doesNotContain(jellyBelly); - } - - @Test // DATAMONGO-1996 - void filterOnNestedElementWorksCorrectly() throws InterruptedException { - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = ChangeStreamRequest.builder(messageListener) // - .collection("user") // - .filter(newAggregation(User.class, match(where("address.street").is("flower street")))) // - .maxAwaitTime(Duration.ofMillis(10)) // - .build(); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - jellyBelly.address = new Address(); - jellyBelly.address.street = "candy ave"; - - huffyFluffy.address = new Address(); - huffyFluffy.address.street = "flower street"; - - template.save(jellyBelly); - template.save(sugarSplashy); - template.save(huffyFluffy); - - awaitMessages(messageListener); - - List messageBodies = messageListener.getMessages().stream().map(Message::getBody) - .collect(Collectors.toList()); - - assertThat(messageBodies).hasSize(1).contains(huffyFluffy); - } - - @Test // DATAMONGO-1996 - void filterOnUpdateDescriptionElement() throws InterruptedException { - - template.save(jellyBelly); - template.save(sugarSplashy); - template.save(huffyFluffy); - - CollectingMessageListener, User> messageListener = new CollectingMessageListener<>(); - ChangeStreamRequest request = ChangeStreamRequest.builder(messageListener) // - .collection("user") // - .filter(newAggregation(User.class, match(where("updateDescription.updatedFields.address").exists(true)))) // - .maxAwaitTime(Duration.ofMillis(10)) // - .fullDocumentLookup(FullDocument.UPDATE_LOOKUP).build(); - - Subscription subscription = container.register(request, User.class); - awaitSubscription(subscription); - - template.update(User.class).matching(query(where("id").is(jellyBelly.id))) - .apply(Update.update("address", new Address("candy ave"))).first(); - - template.update(User.class).matching(query(where("id").is(sugarSplashy.id))).apply(new Update().inc("age", 1)) - .first(); - - template.update(User.class).matching(query(where("id").is(huffyFluffy.id))) - .apply(Update.update("address", new Address("flower street"))).first(); - - awaitMessages(messageListener); - - List messageBodies = messageListener.getMessages().stream().map(Message::getBody) - .collect(Collectors.toList()); - - assertThat(messageBodies).hasSize(2); - } - - @Data - static class User { - - @Id String id; - @Field("user_name") String userName; - int age; - - Address address; - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - static class Address { - - @Field("s") String street; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/CursorReadingTaskUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/CursorReadingTaskUnitTests.java deleted file mode 100644 index 653c011376..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/CursorReadingTaskUnitTests.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2018-2021 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.messaging; - -import static edu.umd.cs.mtc.TestFramework.*; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import edu.umd.cs.mtc.MultithreadedTestCase; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.data.mongodb.core.MongoExceptionTranslator; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions; -import org.springframework.data.mongodb.core.messaging.Task.State; -import org.springframework.util.ErrorHandler; - -import com.mongodb.ServerAddress; -import com.mongodb.ServerCursor; -import com.mongodb.client.MongoCursor; -import com.mongodb.client.MongoDatabase; - -/** - * Unit test for mainly lifecycle issues of {@link CursorReadingTask}. - * - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -public class CursorReadingTaskUnitTests { - - @Mock MongoDatabase db; - @Mock MongoCursor cursor; - @Mock SubscriptionRequest request; - @Mock MessageListener listener; - @Mock RequestOptions options; - @Mock MongoTemplate template; - @Mock ErrorHandler errorHandler; - - ValueCapturingTaskStub task; - - @BeforeEach - public void setUp() { - - when(request.getRequestOptions()).thenReturn(options); - when(request.getMessageListener()).thenReturn(listener); - when(options.getCollectionName()).thenReturn("collection-name"); - when(template.getDb()).thenReturn(db); - when(template.getExceptionTranslator()).thenReturn(new MongoExceptionTranslator()); - when(db.getName()).thenReturn("mock-db"); - - task = new ValueCapturingTaskStub(template, request, Object.class, cursor, errorHandler); - } - - @Test // DATAMONGO-1803 - public void stopTaskWhileStarting() throws Throwable { - runOnce(new MultithreadedStopDuringStartupInitialization(task, cursor)); - } - - @Test // DATAMONGO-1803 - public void stopRunningTask() throws Throwable { - - when(cursor.getServerCursor()).thenReturn(new ServerCursor(10, new ServerAddress("mock"))); - - runOnce(new MultithreadedStopRunning(task, cursor)); - } - - @Test // DATAMONGO-1803 - public void stopTaskWhileEmittingMessages() throws Throwable { - - when(cursor.getServerCursor()).thenReturn(new ServerCursor(10, new ServerAddress("mock"))); - when(cursor.tryNext()).thenReturn("hooyah"); - - runOnce(new MultithreadedStopRunningWhileEmittingMessages(task, cursor)); - - verify(listener, times(task.getValues().size())).onMessage(any()); - } - - @Test // DATAMONGO-2173, DATAMONGO-2366 - public void writesErrorOnStartToErrorHandler() { - - ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(Throwable.class); - Task task = new ErrorOnInitCursorTaskStub(template, request, Object.class, errorHandler); - - task.run(); - verify(errorHandler).handleError(errorCaptor.capture()); - assertThat(errorCaptor.getValue()).hasMessageStartingWith("let's get it started (ha)"); - } - - @Test // DATAMONGO-2366 - public void errorOnNextNotifiesErrorHandlerOnlyOnce() { - - ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(Throwable.class); - when(cursor.getServerCursor()).thenReturn(new ServerCursor(10, new ServerAddress("mock"))); - when(cursor.tryNext()).thenThrow(new IllegalStateException()); - - task.run(); - verify(errorHandler).handleError(errorCaptor.capture()); - assertThat(errorCaptor.getValue()).isInstanceOf(IllegalStateException.class); - } - - private static class MultithreadedStopRunningWhileEmittingMessages extends MultithreadedTestCase { - - CursorReadingTask task; - MongoCursor cursor; - - public MultithreadedStopRunningWhileEmittingMessages(CursorReadingTask task, MongoCursor cursor) { - - this.task = task; - this.cursor = cursor; - } - - public void thread1() { - - assertTick(0); - - assertThat(task.getState()).isEqualTo(State.CREATED); - task.run(); - - waitForTick(1); - assertThat(task.isActive()).isFalse(); - assertThat(task.getState()).isEqualTo(State.CANCELLED); - verify(cursor).close(); - } - - public void thread2() throws InterruptedException { - - while (!task.isActive()) { - Thread.sleep(20); - } - - verify(cursor, never()).close(); - task.cancel(); - } - } - - private static class MultithreadedStopRunning extends MultithreadedTestCase { - - CursorReadingTask task; - MongoCursor cursor; - - public MultithreadedStopRunning(CursorReadingTask task, MongoCursor cursor) { - - this.task = task; - this.cursor = cursor; - } - - public void thread1() { - - assertTick(0); - - assertThat(task.getState()).isEqualTo(State.CREATED); - task.run(); - - waitForTick(2); - assertThat(task.isActive()).isFalse(); - assertThat(task.getState()).isEqualTo(State.CANCELLED); - verify(cursor).close(); - } - - public void thread2() throws InterruptedException { - - waitForTick(1); - assertThat(task.isActive()).isTrue(); - assertThat(task.getState()).isEqualTo(State.RUNNING); - verify(cursor, never()).close(); - - task.cancel(); - } - } - - private static class MultithreadedStopDuringStartupInitialization extends MultithreadedTestCase { - - CursorReadingTask task; - MongoCursor cursor; - - public MultithreadedStopDuringStartupInitialization(CursorReadingTask task, MongoCursor cursor) { - this.task = task; - this.cursor = cursor; - } - - public void thread1() { - - assertTick(0); - task.run(); - - waitForTick(2); - assertThat(task.isActive()).isFalse(); - assertThat(task.getState()).isEqualTo(State.CANCELLED); - verify(cursor).close(); - } - - public void thread2() throws InterruptedException { - - waitForTick(1); - assertThat(task.isActive()).isFalse(); - assertThat(task.getState()).isEqualTo(State.STARTING); - - task.cancel(); - } - } - - static class ValueCapturingTaskStub extends CursorReadingTask { - - final MongoCursor cursor; - final List values = new CopyOnWriteArrayList<>(); - - public ValueCapturingTaskStub(MongoTemplate template, SubscriptionRequest request, Class targetType, - MongoCursor cursor, ErrorHandler errorHandler) { - - super(template, request, targetType, errorHandler); - this.cursor = cursor; - } - - @Override - protected MongoCursor initCursor(MongoTemplate dbFactory, RequestOptions options, Class targetType) { - return cursor; - } - - @Override - protected Message createMessage(Object source, Class targetType, RequestOptions options) { - - values.add(source); - return super.createMessage(source, targetType, options); - } - - public List getValues() { - return values; - } - } - - static class ErrorOnInitCursorTaskStub extends CursorReadingTask { - - public ErrorOnInitCursorTaskStub(MongoTemplate template, SubscriptionRequest request, Class targetType, - ErrorHandler errorHandler) { - super(template, request, targetType, errorHandler); - } - - @Override - protected MongoCursor initCursor(MongoTemplate template, RequestOptions options, Class targetType) { - throw new RuntimeException("let's get it started (ha), let's get it started in here..."); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerTests.java deleted file mode 100644 index b973de0cf1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerTests.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright 2018-2021 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.messaging; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.messaging.SubscriptionUtils.*; - -import lombok.Data; - -import java.time.Duration; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.core.task.SimpleAsyncTaskExecutor; -import org.springframework.dao.DataAccessException; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.ChangeStreamOptions; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.messaging.ChangeStreamRequest.ChangeStreamRequestOptions; -import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoServerCondition; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.Template; -import org.springframework.util.ErrorHandler; - -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.CreateCollectionOptions; -import com.mongodb.client.model.changestream.ChangeStreamDocument; - -/** - * Integration tests for {@link DefaultMessageListenerContainer}. - * - * @author Christoph Strobl - */ -@ExtendWith({ MongoTemplateExtension.class, MongoServerCondition.class }) -public class DefaultMessageListenerContainerTests { - - static final String DATABASE_NAME = "change-stream-events"; - static final String COLLECTION_NAME = "collection-1"; - static final String COLLECTION_2_NAME = "collection-2"; - - static final Duration TIMEOUT = Duration.ofSeconds(2); - - @Template(database = DATABASE_NAME, initialEntitySet = Person.class) // - static MongoTemplate template; - - MongoDatabaseFactory dbFactory = template.getMongoDbFactory(); - - MongoCollection collection = template.getCollection(COLLECTION_NAME); - MongoCollection collection2 = template.getCollection(COLLECTION_2_NAME); - - private CollectingMessageListener messageListener; - - @BeforeEach - void beforeEach() { - - template.dropCollection(COLLECTION_NAME); - template.dropCollection(COLLECTION_2_NAME); - - messageListener = new CollectingMessageListener<>(); - } - - @Test // DATAMONGO-1803 - @EnableIfReplicaSetAvailable - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - public void shouldCollectMappedChangeStreamMessagesCorrectly() throws InterruptedException { - - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - Subscription subscription = container.register(new ChangeStreamRequest(messageListener, options()), Person.class); - container.start(); - - awaitSubscription(subscription, TIMEOUT); - - collection.insertOne(new Document("_id", "id-1").append("firstname", "foo")); - collection.insertOne(new Document("_id", "id-2").append("firstname", "bar")); - - awaitMessages(messageListener, 2, TIMEOUT); - - assertThat(messageListener.getMessages().stream().map(Message::getBody).collect(Collectors.toList())) - .containsExactly(new Person("id-1", "foo"), new Person("id-2", "bar")); - } - - @Test // DATAMONGO-2322 - @EnableIfReplicaSetAvailable - public void shouldNotifyErrorHandlerOnErrorInListener() throws InterruptedException { - - ErrorHandler errorHandler = mock(ErrorHandler.class); - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - AtomicBoolean thrownException = new AtomicBoolean(); - Subscription subscription = container.register(new ChangeStreamRequest(message -> { - - try { - if (thrownException.compareAndSet(false, true)) { - throw new IllegalStateException("Boom!"); - } - } finally { - messageListener.onMessage(message); - } - - }, options()), Person.class, errorHandler); - container.start(); - - awaitSubscription(subscription, TIMEOUT); - - collection.insertOne(new Document("_id", "id-1").append("firstname", "foo")); - collection.insertOne(new Document("_id", "id-2").append("firstname", "bar")); - - awaitMessages(messageListener, 2, TIMEOUT); - - verify(errorHandler, atLeast(1)).handleError(any(IllegalStateException.class)); - assertThat(messageListener.getTotalNumberMessagesReceived()).isEqualTo(2); - } - - @Test // DATAMONGO-1803 - @EnableIfReplicaSetAvailable - public void shouldNoLongerReceiveMessagesWhenContainerStopped() throws InterruptedException { - - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - Subscription subscription = container.register(new ChangeStreamRequest(messageListener, options()), Document.class); - container.start(); - - awaitSubscription(subscription, TIMEOUT); - - collection.insertOne(new Document("_id", "id-1").append("value", "foo")); - collection.insertOne(new Document("_id", "id-2").append("value", "bar")); - - awaitMessages(messageListener, 2, TIMEOUT); - - container.stop(); - - collection.insertOne(new Document("_id", "id-3").append("value", "bar")); - - Thread.sleep(200); - - assertThat(messageListener.getTotalNumberMessagesReceived()).isEqualTo(2); - } - - @Test // DATAMONGO-1803 - @EnableIfReplicaSetAvailable - public void shouldReceiveMessagesWhenAddingRequestToAlreadyStartedContainer() throws InterruptedException { - - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - container.start(); - - Document unexpected = new Document("_id", "id-1").append("value", "foo"); - collection.insertOne(unexpected); - - Subscription subscription = container.register(new ChangeStreamRequest(messageListener, options()), Document.class); - - awaitSubscription(subscription, TIMEOUT); - - Document expected = new Document("_id", "id-2").append("value", "bar"); - collection.insertOne(expected); - - awaitMessages(messageListener, 1, TIMEOUT); - container.stop(); - - assertThat(messageListener.getMessages().stream().map(Message::getBody).collect(Collectors.toList())) - .containsExactly(expected); - } - - @Test // DATAMONGO-1803 - @EnableIfReplicaSetAvailable - public void shouldStartReceivingMessagesWhenContainerStarts() throws InterruptedException { - - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - Subscription subscription = container.register(new ChangeStreamRequest(messageListener, options()), Document.class); - - collection.insertOne(new Document("_id", "id-1").append("value", "foo")); - - Thread.sleep(200); - - container.start(); - - awaitSubscription(subscription); - - Document expected = new Document("_id", "id-2").append("value", "bar"); - collection.insertOne(expected); - - awaitMessages(messageListener); - - container.stop(); - - assertThat(messageListener.getMessages().stream().map(Message::getBody).collect(Collectors.toList())) - .containsExactly(expected); - } - - @Test // DATAMONGO-1803 - public void tailableCursor() throws InterruptedException { - - dbFactory.getMongoDatabase().createCollection(COLLECTION_NAME, - new CreateCollectionOptions().capped(true).maxDocuments(10000).sizeInBytes(10000)); - - collection.insertOne(new Document("_id", "id-1").append("value", "foo")); - - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - container.start(); - - awaitSubscription(container.register(new TailableCursorRequest(messageListener, options()), Document.class), - TIMEOUT); - - collection.insertOne(new Document("_id", "id-2").append("value", "bar")); - - awaitMessages(messageListener, 2, TIMEOUT); - container.stop(); - - assertThat(messageListener.getTotalNumberMessagesReceived()).isEqualTo(2); - } - - @Test // DATAMONGO-1803 - public void tailableCursorOnEmptyCollection() throws InterruptedException { - - dbFactory.getMongoDatabase().createCollection(COLLECTION_NAME, - new CreateCollectionOptions().capped(true).maxDocuments(10000).sizeInBytes(10000)); - - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - container.start(); - - awaitSubscription(container.register(new TailableCursorRequest(messageListener, options()), Document.class), - TIMEOUT); - - collection.insertOne(new Document("_id", "id-1").append("value", "foo")); - collection.insertOne(new Document("_id", "id-2").append("value", "bar")); - - awaitMessages(messageListener, 2, TIMEOUT); - container.stop(); - - assertThat(messageListener.getTotalNumberMessagesReceived()).isEqualTo(2); - } - - @Test // DATAMONGO-1803 - public void abortsSubscriptionOnError() throws InterruptedException { - - dbFactory.getMongoDatabase().createCollection(COLLECTION_NAME, - new CreateCollectionOptions().capped(true).maxDocuments(10000).sizeInBytes(10000)); - - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - container.start(); - - collection.insertOne(new Document("_id", "id-1").append("value", "foo")); - - Subscription subscription = container.register(new TailableCursorRequest(messageListener, options()), - Document.class); - - awaitSubscription(subscription); - - assertThat(subscription.isActive()).isTrue(); - - collection.insertOne(new Document("_id", "id-2").append("value", "bar")); - collection.drop(); - - awaitMessages(messageListener); - - assertThat(subscription.isActive()).isFalse(); - - container.stop(); - } - - @Test // DATAMONGO-1803 - public void callsDefaultErrorHandlerOnError() throws InterruptedException { - - dbFactory.getMongoDatabase().createCollection(COLLECTION_NAME, - new CreateCollectionOptions().capped(true).maxDocuments(10000).sizeInBytes(10000)); - - collection.insertOne(new Document("_id", "id-1").append("value", "foo")); - - ErrorHandler errorHandler = mock(ErrorHandler.class); - - DefaultMessageListenerContainer container = new DefaultMessageListenerContainer(template, - new SimpleAsyncTaskExecutor(), errorHandler); - - try { - container.start(); - - Subscription subscription = container.register(new TailableCursorRequest(messageListener, options()), - Document.class); - - SubscriptionUtils.awaitSubscription(subscription); - - template.dropCollection(COLLECTION_NAME); - - Thread.sleep(20); - - verify(errorHandler, atLeast(1)).handleError(any(DataAccessException.class)); - } finally { - container.stop(); - } - } - - @Test // DATAMONGO-1803 - @EnableIfReplicaSetAvailable - public void runsMoreThanOneTaskAtOnce() throws InterruptedException { - - dbFactory.getMongoDatabase().createCollection(COLLECTION_NAME, - new CreateCollectionOptions().capped(true).maxDocuments(10000).sizeInBytes(10000)); - - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - container.start(); - - CollectingMessageListener tailableListener = new CollectingMessageListener<>(); - Subscription tailableSubscription = container.register(new TailableCursorRequest(tailableListener, options()), - Document.class); - - CollectingMessageListener, Document> changeStreamListener = new CollectingMessageListener<>(); - Subscription changeStreamSubscription = container.register(new ChangeStreamRequest(changeStreamListener, options()), - Document.class); - - awaitSubscriptions(tailableSubscription, changeStreamSubscription); - - collection.insertOne(new Document("_id", "id-1").append("value", "foo")); - - awaitMessages(tailableListener); - awaitMessages(changeStreamListener); - - assertThat(tailableListener.getTotalNumberMessagesReceived()).isEqualTo(1); - assertThat(tailableListener.getFirstMessage().getRaw()).isInstanceOf(Document.class); - - assertThat(changeStreamListener.getTotalNumberMessagesReceived()).isEqualTo(1); - assertThat(changeStreamListener.getFirstMessage().getRaw()).isInstanceOf(ChangeStreamDocument.class); - } - - @Test // DATAMONGO-2012 - @EnableIfReplicaSetAvailable - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - public void databaseLevelWatch() throws InterruptedException { - - MessageListenerContainer container = new DefaultMessageListenerContainer(template); - Subscription subscription = container.register(new ChangeStreamRequest(messageListener, RequestOptions.none()), - Person.class); - - container.start(); - - awaitSubscription(subscription, TIMEOUT); - - collection.insertOne(new Document("_id", "col-1-id-1").append("firstname", "foo")); - collection.insertOne(new Document("_id", "col-1-id-2").append("firstname", "bar")); - - collection2.insertOne(new Document("_id", "col-2-id-1").append("firstname", "bar")); - collection2.insertOne(new Document("_id", "col-2-id-2").append("firstname", "foo")); - - awaitMessages(messageListener, 4, TIMEOUT); - - assertThat(messageListener.getMessages().stream().map(Message::getBody).collect(Collectors.toList())) - .containsExactly(new Person("col-1-id-1", "foo"), new Person("col-1-id-2", "bar"), - new Person("col-2-id-1", "bar"), new Person("col-2-id-2", "foo")); - } - - @Data - static class Person { - @Id String id; - private String firstname; - private String lastname; - - public Person() {} - - public Person(String id, String firstname) { - this.id = id; - this.firstname = firstname; - } - } - - static ChangeStreamRequestOptions options() { - return new ChangeStreamRequestOptions(DATABASE_NAME, COLLECTION_NAME, Duration.ofMillis(10), - ChangeStreamOptions.builder().build()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerUnitTests.java deleted file mode 100644 index 68a002981f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainerUnitTests.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2018-2021 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.messaging; - -import static edu.umd.cs.mtc.TestFramework.*; -import static org.assertj.core.api.Assertions.*; - -import edu.umd.cs.mtc.MultithreadedTestCase; - -import java.time.Duration; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.dao.DataAccessResourceFailureException; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.util.ErrorHandler; - -/** - * Unit tests for {@link DefaultMessageListenerContainer}. - * - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -class DefaultMessageListenerContainerUnitTests { - - @Mock MongoTemplate template; - @Mock ErrorHandler errorHandler; - - private DefaultMessageListenerContainer container; - - @BeforeEach - void setUp() { - container = new DefaultMessageListenerContainer(template); - } - - @Test // DATAMONGO-1803 - void throwsErrorOnNullTemplate() { - assertThatIllegalArgumentException().isThrownBy(() -> new DefaultMessageListenerContainer(null)); - } - - @Test // DATAMONGO-1803 - void startStopContainer() throws Throwable { - runOnce(new MultithreadedStartStopContainer(container)); - } - - @Test // DATAMONGO-1803 - void subscribeToContainerBeforeStartup() throws Throwable { - runOnce(new MultithreadedSubscribeBeforeStartup(container)); - } - - @Test // DATAMONGO-1803 - void subscribeToContainerAfterStartup() throws Throwable { - runOnce(new MultithreadedSubscribeAfterStartup(container)); - } - - @Test // DATAMONGO-1803 - void stopSubscriptionWhileRunning() throws Throwable { - runOnce(new StopSubscriptionWhileRunning(container)); - } - - @Test // DATAMONGO-1803 - void removeSubscriptionWhileRunning() throws Throwable { - runOnce(new RemoveSubscriptionWhileRunning(container)); - } - - private static class RemoveSubscriptionWhileRunning extends MultithreadedTestCase { - - DefaultMessageListenerContainer container; - Subscription subscription; - - RemoveSubscriptionWhileRunning(DefaultMessageListenerContainer container) { - this.container = container; - subscription = container.register(new MockSubscriptionRequest(), new MockTask()); - } - - public void thread1() { - - assertTick(0); - container.start(); - - waitForTick(2); - assertThat(container.isRunning()); - container.stop(); - } - - public void thread2() throws InterruptedException { - - waitForTick(1); - assertThat(subscription.isActive()).isTrue(); - - container.remove(subscription); - assertThat(subscription.isActive()).isFalse(); - } - } - - private static class StopSubscriptionWhileRunning extends MultithreadedTestCase { - - DefaultMessageListenerContainer container; - Subscription subscription; - - StopSubscriptionWhileRunning(DefaultMessageListenerContainer container) { - this.container = container; - subscription = container.register(new MockSubscriptionRequest(), new MockTask()); - } - - public void thread1() { - - assertTick(0); - container.start(); - - waitForTick(2); - assertThat(container.isRunning()); - container.stop(); - } - - public void thread2() throws InterruptedException { - - waitForTick(1); - assertThat(subscription.isActive()).isTrue(); - - subscription.cancel(); - assertThat(subscription.isActive()).isFalse(); - } - - } - - private static class MultithreadedSubscribeAfterStartup extends MultithreadedTestCase { - - DefaultMessageListenerContainer container; - - MultithreadedSubscribeAfterStartup(DefaultMessageListenerContainer container) { - this.container = container; - } - - public void thread1() { - - assertTick(0); - container.start(); - - waitForTick(2); - container.stop(); - } - - public void thread2() throws InterruptedException { - - waitForTick(1); - Subscription subscription = container.register(new MockSubscriptionRequest(), new MockTask()); - Thread.sleep(10); - assertThat(subscription.isActive()).isTrue(); - - waitForTick(3); - assertThat(subscription.isActive()).isFalse(); - } - - } - - private static class MultithreadedSubscribeBeforeStartup extends MultithreadedTestCase { - - DefaultMessageListenerContainer container; - - MultithreadedSubscribeBeforeStartup(DefaultMessageListenerContainer container) { - this.container = container; - } - - public void thread1() { - - assertTick(0); - - Subscription subscription = container.register(new MockSubscriptionRequest(), new MockTask()); - assertThat(subscription.isActive()).isFalse(); - - waitForTick(2); - assertThat(subscription.isActive()).isTrue(); - - waitForTick(4); - assertThat(subscription.isActive()).isFalse(); - } - - public void thread2() { - - waitForTick(1); - container.start(); - - waitForTick(3); - container.stop(); - } - - } - - private static class MultithreadedStartStopContainer extends MultithreadedTestCase { - - DefaultMessageListenerContainer container; - - MultithreadedStartStopContainer(DefaultMessageListenerContainer container) { - this.container = container; - } - - public void thread1() { - - assertTick(0); - container.start(); - waitForTick(2); - assertThat(container.isRunning()).isFalse(); - } - - public void thread2() { - - waitForTick(1); - assertThat(container.isRunning()).isTrue(); - container.stop(); - } - } - - static class MockTask implements Task { - - volatile State state; - volatile RuntimeException error; - - @Override - public void cancel() throws DataAccessResourceFailureException { - state = State.CANCELLED; - } - - @Override - public boolean isLongLived() { - return true; - } - - @Override - public State getState() { - return state; - } - - @Override - public void run() { - - state = State.RUNNING; - - while (isActive()) { - - if (error != null) { - throw error; - } - - try { - Thread.sleep(10); - } catch (InterruptedException e) { - Thread.interrupted(); - } - } - } - - void emitError(RuntimeException error) { - this.error = error; - } - - @Override - public boolean awaitStart(Duration timeout) throws InterruptedException { - - while (getState() == State.STARTING) { - Thread.sleep(10); - } - - return true; - } - } - - static class MockSubscriptionRequest implements SubscriptionRequest { - - @Override - public MessageListener getMessageListener() { - return message -> {}; - } - - @Override - public RequestOptions getRequestOptions() { - return () -> "foo"; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/SubscriptionUtils.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/SubscriptionUtils.java deleted file mode 100644 index 7aea37f501..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/SubscriptionUtils.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2018-2021 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.messaging; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * Utilities for testing long running asnyc message retrieval. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -class SubscriptionUtils { - - static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(1500); - - /** - * Wait for {@link Subscription#isActive() to become active} but not longer than {@link #DEFAULT_TIMEOUT}. - * - * @param subscription - * @throws InterruptedException - */ - static void awaitSubscription(Subscription subscription) throws InterruptedException { - awaitSubscription(subscription, DEFAULT_TIMEOUT); - } - - /** - * Wait for all {@link Subscription Subscriptions} to {@link Subscription#isActive() become active} but not longer - * than {@link #DEFAULT_TIMEOUT}. - * - * @param subscription - * @throws InterruptedException - */ - static void awaitSubscriptions(Subscription... subscriptions) throws InterruptedException { - awaitSubscriptions(DEFAULT_TIMEOUT, subscriptions); - } - - /** - * Wait for all {@link Subscription Subscriptions} to {@link Subscription#isActive() become active} but not longer - * than {@literal timeout}. - * - * @param timeout - * @param subscriptions - * @throws InterruptedException - */ - static void awaitSubscriptions(Duration timeout, Subscription... subscriptions) throws InterruptedException { - - long passedMs = 0; - long maxMs = timeout.toMillis(); - - Collection subscriptionList = Arrays.asList(subscriptions); - - while (!subscriptionList.stream().allMatch(Subscription::isActive) && passedMs < maxMs) { - - Thread.sleep(10); - passedMs += 10; - } - } - - /** - * Wait for {@link Subscription#isActive() to become active} but not longer than {@literal timeout}. - * - * @param subscription - * @param timeout - * @throws InterruptedException - */ - static void awaitSubscription(Subscription subscription, Duration timeout) throws InterruptedException { - subscription.await(timeout); - } - - /** - * Wait for {@link CollectingMessageListener} to receive messages but not longer than {@link #DEFAULT_TIMEOUT}. - * - * @param listener - * @throws InterruptedException - */ - static void awaitMessages(CollectingMessageListener listener) throws InterruptedException { - awaitMessages(listener, Integer.MAX_VALUE); - } - - /** - * Wait for {@link CollectingMessageListener} to receive exactly {@literal nrMessages} messages but not longer than - * {@link #DEFAULT_TIMEOUT}. - * - * @param listener - * @param nrMessages - * @throws InterruptedException - */ - static void awaitMessages(CollectingMessageListener listener, int nrMessages) throws InterruptedException { - awaitMessages(listener, nrMessages, DEFAULT_TIMEOUT); - } - - /** - * Wait for {@link CollectingMessageListener} to receive exactly {@literal nrMessages} messages but not longer than - * {@literal timeout}. - * - * @param listener - * @param nrMessages - * @param timeout - * @throws InterruptedException - */ - static void awaitMessages(CollectingMessageListener listener, int nrMessages, Duration timeout) - throws InterruptedException { - - long passedMs = 0; - long maxMs = timeout.toMillis(); - - while (listener.getTotalNumberMessagesReceived() < nrMessages && passedMs < maxMs) { - Thread.sleep(10); - passedMs += 10; - } - } - - /** - * {@link MessageListener} implementation collecting received {@link Message messages}. - * - * @param - */ - static class CollectingMessageListener implements MessageListener { - - private volatile List> messages = new ArrayList<>(); - - @Override - public void onMessage(Message message) { - messages.add(message); - } - - int getTotalNumberMessagesReceived() { - return messages.size(); - } - - public List> getMessages() { - return messages; - } - - public Message getMessage(int nr) { - return messages.get(nr); - } - - public Message getFirstMessage() { - return messages.get(0); - } - - public Message getLastMessage() { - return messages.get(messages.size() - 1); - } - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequestUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequestUnitTests.java deleted file mode 100644 index 149daf28ae..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequestUnitTests.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2018-2021 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.messaging; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.messaging.DefaultMessageListenerContainerTests.Person; -import org.springframework.data.mongodb.core.query.Query; - -/** - * Unit tests for {@link TailableCursorRequest}. - * - * @author Mark Paluch - */ -public class TailableCursorRequestUnitTests { - - @Test // DATAMONGO-1803 - public void shouldBuildRequest() { - - MessageListener listener = System.out::println; - - TailableCursorRequest request = TailableCursorRequest.builder(listener).collection("foo") - .filter(Query.query(where("firstname").is("bar"))).build(); - - assertThat(request.getRequestOptions().getCollectionName()).isEqualTo("foo"); - assertThat(request.getRequestOptions().getQuery()).isPresent(); - assertThat(request.getMessageListener()).isEqualTo(listener); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorTests.java deleted file mode 100644 index 0cdc74cdea..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TailableCursorTests.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2018-2021 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.messaging; - -import static org.springframework.data.mongodb.core.messaging.SubscriptionUtils.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import lombok.Data; - -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.bson.Document; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.CollectionOptions; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.messaging.Message.MessageProperties; -import org.springframework.data.mongodb.core.messaging.TailableCursorRequest.TailableCursorRequestOptions; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; - -import com.mongodb.client.MongoClient; - -/** - * Integration test for subscribing to a capped {@link com.mongodb.client.MongoCollection} inside the - * {@link DefaultMessageListenerContainer} using {@link TailableCursorRequest}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class }) -public class TailableCursorTests { - - static final String COLLECTION_NAME = "user"; - - static @Client MongoClient mongoClient; - static ThreadPoolExecutor executor; - MongoTemplate template; - MessageListenerContainer container; - - User jellyBelly; - User huffyFluffy; - User sugarSplashy; - - @BeforeAll - public static void beforeClass() { - executor = new ThreadPoolExecutor(2, 2, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); - } - - @BeforeEach - public void setUp() { - - template = new MongoTemplate(mongoClient, "tailable-cursor-tests"); - - template.dropCollection(User.class); - template.createCollection(User.class, CollectionOptions.empty().capped().maxDocuments(10000).size(10000)); - - container = new DefaultMessageListenerContainer(template, executor); - container.start(); - - jellyBelly = new User(); - jellyBelly.id = "id-1"; - jellyBelly.userName = "jellyBelly"; - jellyBelly.age = 7; - - huffyFluffy = new User(); - huffyFluffy.id = "id-2"; - huffyFluffy.userName = "huffyFluffy"; - huffyFluffy.age = 7; - - sugarSplashy = new User(); - sugarSplashy.id = "id-3"; - sugarSplashy.userName = "sugarSplashy"; - sugarSplashy.age = 5; - } - - @AfterEach - public void tearDown() { - container.stop(); - } - - @AfterAll - public static void afterClass() { - executor.shutdown(); - } - - @Test // DATAMONGO-1803 - public void readsDocumentMessageCorrectly() throws InterruptedException { - - CollectingMessageListener messageListener = new CollectingMessageListener<>(); - - awaitSubscription( - container.register(new TailableCursorRequest<>(messageListener, () -> COLLECTION_NAME), Document.class)); - - template.save(jellyBelly); - - awaitMessages(messageListener, 1); - - Document expected = new Document("_id", "id-1").append("user_name", "jellyBelly").append("age", 7).append("_class", - TailableCursorTests.User.class.getName()); - - assertThat(messageListener.getFirstMessage().getProperties()) - .isEqualTo(MessageProperties.builder().collectionName("user").databaseName("tailable-cursor-tests").build()); - assertThat(messageListener.getFirstMessage().getRaw()).isEqualTo(expected); - assertThat(messageListener.getFirstMessage().getBody()).isEqualTo(expected); - } - - @Test // DATAMONGO-1803 - public void convertsMessageCorrectly() throws InterruptedException { - - CollectingMessageListener messageListener = new CollectingMessageListener<>(); - - awaitSubscription( - container.register(new TailableCursorRequest<>(messageListener, () -> COLLECTION_NAME), User.class)); - - template.save(jellyBelly); - - awaitMessages(messageListener, 1); - - Document expected = new Document("_id", "id-1").append("user_name", "jellyBelly").append("age", 7).append("_class", - TailableCursorTests.User.class.getName()); - - assertThat(messageListener.getFirstMessage().getProperties()) - .isEqualTo(MessageProperties.builder().collectionName("user").databaseName("tailable-cursor-tests").build()); - assertThat(messageListener.getFirstMessage().getRaw()).isEqualTo(expected); - assertThat(messageListener.getFirstMessage().getBody()).isEqualTo(jellyBelly); - } - - @Test // DATAMONGO-1803 - public void filtersMessagesCorrectly() throws InterruptedException { - - CollectingMessageListener messageListener = new CollectingMessageListener<>(); - - awaitSubscription(container.register(new TailableCursorRequest<>(messageListener, - TailableCursorRequestOptions.builder().collection(COLLECTION_NAME).filter(query(where("age").is(7))).build()), - User.class)); - - template.save(jellyBelly); - template.save(sugarSplashy); - template.save(huffyFluffy); - - awaitMessages(messageListener, 2); - - assertThat(messageListener.getMessages().stream().map(Message::getBody)).hasSize(2).doesNotContain(sugarSplashy); - } - - @Test // DATAMONGO-1803 - public void mapsFilterToDomainType() throws InterruptedException { - - CollectingMessageListener messageListener = new CollectingMessageListener<>(); - - awaitSubscription( - container - .register( - new TailableCursorRequest<>(messageListener, TailableCursorRequestOptions.builder() - .collection(COLLECTION_NAME).filter(query(where("userName").is("sugarSplashy"))).build()), - User.class)); - - template.save(jellyBelly); - template.save(sugarSplashy); - template.save(huffyFluffy); - - awaitMessages(messageListener, 1); - - assertThat(messageListener.getMessages().stream().map(Message::getBody)).hasSize(1).containsExactly(sugarSplashy); - } - - @Test // DATAMONGO-1803 - public void emitsFromStart() throws InterruptedException { - - template.save(jellyBelly); - template.save(huffyFluffy); - - CollectingMessageListener messageListener = new CollectingMessageListener<>(); - - awaitSubscription( - container.register(new TailableCursorRequest<>(messageListener, () -> COLLECTION_NAME), User.class)); - - template.save(sugarSplashy); - - awaitMessages(messageListener, 3); - - assertThat(messageListener.getMessages().stream().map(Message::getBody)).hasSize(3).containsExactly(jellyBelly, - huffyFluffy, sugarSplashy); - } - - @Data - static class User { - - @Id String id; - @Field("user_name") String userName; - int age; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TaskFactoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TaskFactoryUnitTests.java deleted file mode 100644 index 5843c15d9f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/messaging/TaskFactoryUnitTests.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2018-2021 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.messaging; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.messaging.ChangeStreamRequest.ChangeStreamRequestOptions; -import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions; -import org.springframework.util.ErrorHandler; - -/** - * Unit tests for {@link TaskFactory}. - * - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -class TaskFactoryUnitTests { - - @Mock MongoConverter converter; - @Mock MongoTemplate template; - @Mock MessageListener messageListener; - @Mock ErrorHandler errorHandler; - - private TaskFactory factory; - - @BeforeEach - void setUp() { - factory = new TaskFactory(template); - } - - @Test // DATAMONGO-1803 - void requestMustNotBeNull() { - assertThatIllegalArgumentException().isThrownBy(() -> factory.forRequest(null, Object.class, errorHandler)); - } - - @Test // DATAMONGO-1803 - void createsChangeStreamRequestCorrectly() { - - when(template.getConverter()).thenReturn(converter); - - ChangeStreamRequestOptions options = Mockito.mock(ChangeStreamRequestOptions.class); - Task task = factory.forRequest(new ChangeStreamRequest(messageListener, options), Object.class, errorHandler); - - assertThat(task).isInstanceOf(ChangeStreamTask.class); - } - - @Test // DATAMONGO-1803 - void createsTailableRequestCorrectly() { - - when(template.getConverter()).thenReturn(converter); - - RequestOptions options = Mockito.mock(RequestOptions.class); - when(options.getCollectionName()).thenReturn("collection-1"); - Task task = factory.forRequest(new TailableCursorRequest(messageListener, options), Object.class, errorHandler); - - assertThat(task).isInstanceOf(TailableCursorTask.class); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/BasicQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/BasicQueryUnitTests.java deleted file mode 100644 index 1a7477f099..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/BasicQueryUnitTests.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2011-2021 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.query; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import nl.jqno.equalsverifier.EqualsVerifier; -import nl.jqno.equalsverifier.Warning; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; - -/** - * Unit tests for {@link BasicQuery}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author John Willemin - */ -public class BasicQueryUnitTests { - - @Test - public void createsQueryFromPlainJson() { - Query q = new BasicQuery("{ \"name\" : \"Thomas\"}"); - Document reference = new Document("name", "Thomas"); - assertThat(q.getQueryObject()).isEqualTo(reference); - } - - @Test - public void addsCriteriaCorrectly() { - Query q = new BasicQuery("{ \"name\" : \"Thomas\"}").addCriteria(where("age").lt(80)); - Document reference = new Document("name", "Thomas"); - reference.put("age", new Document("$lt", 80)); - assertThat(q.getQueryObject()).isEqualTo(reference); - } - - @Test - public void overridesSortCorrectly() { - - BasicQuery query = new BasicQuery("{}"); - query.setSortObject(new Document("name", -1)); - query.with(Sort.by(Direction.ASC, "lastname")); - - Document sortReference = new Document("name", -1); - sortReference.put("lastname", 1); - assertThat(query.getSortObject()).isEqualTo(sortReference); - } - - @Test // DATAMONGO-1093 - public void equalsContract() { - - BasicQuery query1 = new BasicQuery("{ \"name\" : \"Thomas\"}", "{\"name\":1, \"age\":1}"); - query1.setSortObject(new Document("name", -1)); - - BasicQuery query2 = new BasicQuery("{ \"name\" : \"Oliver\"}", "{\"name\":1, \"address\":1}"); - query2.setSortObject(new Document("name", 1)); - - EqualsVerifier.forExamples(query1, query2) // - .withRedefinedSuperclass() // - .suppress(Warning.NONFINAL_FIELDS, Warning.NULL_FIELDS, Warning.STRICT_INHERITANCE) // - .verify(); - } - - @Test // DATAMONGO-1093 - public void handlesEqualsAndHashCodeCorrectlyForExactCopies() { - - String qry = "{ \"name\" : \"Thomas\"}"; - String fields = "{\"name\":1, \"age\":1}"; - - BasicQuery query1 = new BasicQuery(qry, fields); - query1.setSortObject(new Document("name", -1)); - - BasicQuery query2 = new BasicQuery(qry, fields); - query2.setSortObject(new Document("name", -1)); - - assertThat(query1).isEqualTo(query1); - assertThat(query1).isEqualTo(query2); - assertThat(query1.hashCode()).isEqualTo(query2.hashCode()); - } - - @Test // DATAMONGO-1093 - public void handlesEqualsAndHashCodeCorrectlyWhenBasicQuerySettingsDiffer() { - - String qry = "{ \"name\" : \"Thomas\"}"; - String fields = "{\"name\":1, \"age\":1}"; - - BasicQuery query1 = new BasicQuery(qry, fields); - query1.setSortObject(new Document("name", -1)); - - BasicQuery query2 = new BasicQuery(qry, fields); - query2.setSortObject(new Document("name", 1)); - - assertThat(query1).isNotEqualTo(query2); - assertThat(query1.hashCode()).isNotEqualTo(query2.hashCode()); - } - - @Test // DATAMONGO-1093 - public void handlesEqualsAndHashCodeCorrectlyWhenQuerySettingsDiffer() { - - String qry = "{ \"name\" : \"Thomas\"}"; - String fields = "{\"name\":1, \"age\":1}"; - - BasicQuery query1 = new BasicQuery(qry, fields); - query1.getMeta().setComment("foo"); - - BasicQuery query2 = new BasicQuery(qry, fields); - query2.getMeta().setComment("bar"); - - assertThat(query1).isNotEqualTo(query2); - assertThat(query1.hashCode()).isNotEqualTo(query2.hashCode()); - } - - @Test // DATAMONGO-1387 - public void returnsFieldsCorrectly() { - - String qry = "{ \"name\" : \"Thomas\"}"; - String fields = "{\"name\":1, \"age\":1}"; - - BasicQuery query1 = new BasicQuery(qry, fields); - - assertThat(query1.getFieldsObject()).containsKeys("name", "age"); - } - - @Test // DATAMONGO-1387 - public void handlesFieldsIncludeCorrectly() { - - String qry = "{ \"name\" : \"Thomas\"}"; - - BasicQuery query1 = new BasicQuery(qry); - query1.fields().include("name"); - - assertThat(query1.getFieldsObject()).containsKey("name"); - } - - @Test // DATAMONGO-1387 - public void combinesFieldsIncludeCorrectly() { - - String qry = "{ \"name\" : \"Thomas\"}"; - String fields = "{\"name\":1, \"age\":1}"; - - BasicQuery query1 = new BasicQuery(qry, fields); - query1.fields().include("gender"); - - assertThat(query1.getFieldsObject()).containsKeys("name", "age", "gender"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java deleted file mode 100644 index fa7f4f8ed3..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2018-2021 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.query; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.util.Arrays; - -import org.bson.types.Binary; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; -import org.springframework.util.Base64Utils; - -/** - * Integration tests for {@link Criteria} usage as part of a {@link Query}. - * - * @author Christoph Strobl - * @author Andreas Zink - */ -@ExtendWith(MongoTemplateExtension.class) -class CriteriaTests { - - @Template(initialEntitySet = { DocumentWithBitmask.class }) // - static MongoTestTemplate ops; - - static final DocumentWithBitmask FIFTY_FOUR/*00110110*/ = new DocumentWithBitmask("1", Integer.valueOf(54), - Integer.toBinaryString(54)); - static final DocumentWithBitmask TWENTY_INT/*00010100*/ = new DocumentWithBitmask("2", Integer.valueOf(20), - Integer.toBinaryString(20)); - static final DocumentWithBitmask TWENTY_FLOAT/*00010100*/ = new DocumentWithBitmask("3", Float.valueOf(20), - Integer.toBinaryString(20)); - static final DocumentWithBitmask ONE_HUNDRED_TWO/*01100110*/ = new DocumentWithBitmask("4", - new Binary(Base64Utils.decodeFromString("Zg==")), "01100110"); - - @BeforeEach - void beforeEach() { - - ops.flush(); - - ops.insert(FIFTY_FOUR); - ops.insert(TWENTY_INT); - ops.insert(TWENTY_FLOAT); - ops.insert(ONE_HUNDRED_TWO); - } - - @Test // DATAMONGO-1808 - public void bitsAllClearWithBitPositions() { - - assertThat(ops.find(query(where("value").bits().allClear(Arrays.asList(1, 5))), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT); - } - - @Test // DATAMONGO-1808 - public void bitsAllClearWithNumericBitmask() { - - assertThat(ops.find(query(where("value").bits().allClear(35)), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT); - } - - @Test // DATAMONGO-1808 - public void bitsAllClearWithStringBitmask() { - - assertThat(ops.find(query(where("value").bits().allClear("ID==")), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT); - } - - @Test // DATAMONGO-1808 - public void bitsAllSetWithBitPositions() { - - assertThat(ops.find(query(where("value").bits().allSet(Arrays.asList(1, 5))), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(FIFTY_FOUR, ONE_HUNDRED_TWO); - } - - @Test // DATAMONGO-1808 - public void bitsAllSetWithNumericBitmask() { - - assertThat(ops.find(query(where("value").bits().allSet(50)), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(FIFTY_FOUR); - } - - @Test // DATAMONGO-1808 - public void bitsAllSetWithStringBitmask() { - - assertThat(ops.find(query(where("value").bits().allSet("MC==")), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(FIFTY_FOUR); - } - - @Test // DATAMONGO-1808 - public void bitsAnyClearWithBitPositions() { - - assertThat(ops.find(query(where("value").bits().anyClear(Arrays.asList(1, 5))), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT); - } - - @Test // DATAMONGO-1808 - public void bitsAnyClearWithNumericBitmask() { - - assertThat(ops.find(query(where("value").bits().anyClear(35)), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(FIFTY_FOUR, TWENTY_INT, TWENTY_FLOAT, ONE_HUNDRED_TWO); - } - - @Test // DATAMONGO-1808 - public void bitsAnyClearWithStringBitmask() { - - assertThat(ops.find(query(where("value").bits().anyClear("MC==")), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(TWENTY_INT, TWENTY_FLOAT, ONE_HUNDRED_TWO); - } - - @Test // DATAMONGO-1808 - public void bitsAnySetWithBitPositions() { - - assertThat(ops.find(query(where("value").bits().anySet(Arrays.asList(1, 5))), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(FIFTY_FOUR, ONE_HUNDRED_TWO); - } - - @Test // DATAMONGO-1808 - public void bitsAnySetWithNumericBitmask() { - - assertThat(ops.find(query(where("value").bits().anySet(35)), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(FIFTY_FOUR, ONE_HUNDRED_TWO); - } - - @Test // DATAMONGO-1808 - public void bitsAnySetWithStringBitmask() { - - assertThat(ops.find(query(where("value").bits().anySet("MC==")), DocumentWithBitmask.class)) - .containsExactlyInAnyOrder(FIFTY_FOUR, TWENTY_INT, TWENTY_FLOAT, ONE_HUNDRED_TWO); - } - - @Data - @EqualsAndHashCode(exclude = "value") - @AllArgsConstructor - static class DocumentWithBitmask { - - @Id String id; - Object value; - String binaryValue; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java deleted file mode 100644 index 9edf3c43fd..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright 2010-2021 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.query; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import org.bson.Document; -import org.junit.Test; - -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; -import org.springframework.data.mongodb.core.geo.GeoJsonLineString; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.schema.MongoJsonSchema; - -/** - * Unit tests for {@link Criteria}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Andreas Zink - * @author Ziemowit Stolarczyk - * @author Clément Petit - * @author Mark Paluch - */ -public class CriteriaUnitTests { - - @Test - public void testSimpleCriteria() { - Criteria c = new Criteria("name").is("Bubba"); - assertThat(c.getCriteriaObject()).isEqualTo(Document.parse("{ \"name\" : \"Bubba\"}")); - } - - @Test - public void testNotEqualCriteria() { - Criteria c = new Criteria("name").ne("Bubba"); - assertThat(c.getCriteriaObject()).isEqualTo(Document.parse("{ \"name\" : { \"$ne\" : \"Bubba\"}}")); - } - - @Test - public void buildsIsNullCriteriaCorrectly() { - - Document reference = new Document("name", null); - - Criteria criteria = new Criteria("name").is(null); - assertThat(criteria.getCriteriaObject()).isEqualTo(reference); - } - - @Test - public void testChainedCriteria() { - Criteria c = new Criteria("name").is("Bubba").and("age").lt(21); - assertThat(c.getCriteriaObject()).isEqualTo(Document.parse("{ \"name\" : \"Bubba\" , \"age\" : { \"$lt\" : 21}}")); - } - - @Test(expected = InvalidMongoDbApiUsageException.class) - public void testCriteriaWithMultipleConditionsForSameKey() { - Criteria c = new Criteria("name").gte("M").and("name").ne("A"); - c.getCriteriaObject(); - } - - @Test - public void equalIfCriteriaMatches() { - - Criteria left = new Criteria("name").is("Foo").and("lastname").is("Bar"); - Criteria right = new Criteria("name").is("Bar").and("lastname").is("Bar"); - - assertThat(left).isNotEqualTo(right); - assertThat(right).isNotEqualTo(left); - } - - @Test // GH-3286 - public void shouldBuildCorrectAndOperator() { - - Collection operatorCriteria = Arrays.asList(Criteria.where("x").is(true), - Criteria.where("y").is(42), - Criteria.where("z").is("value")); - - Criteria criteria = Criteria.where("foo").is("bar").andOperator(operatorCriteria); - - assertThat(criteria.getCriteriaObject()) - .isEqualTo("{\"$and\":[{\"x\":true}, {\"y\":42}, {\"z\":\"value\"}], \"foo\":\"bar\"}"); - } - - @Test // GH-3286 - public void shouldBuildCorrectOrOperator() { - - Collection operatorCriteria = Arrays.asList(Criteria.where("x").is(true), - Criteria.where("y").is(42), - Criteria.where("z").is("value")); - - Criteria criteria = Criteria.where("foo").is("bar").orOperator(operatorCriteria); - - assertThat(criteria.getCriteriaObject()) - .isEqualTo("{\"$or\":[{\"x\":true}, {\"y\":42}, {\"z\":\"value\"}], \"foo\":\"bar\"}"); - } - - @Test // GH-3286 - public void shouldBuildCorrectNorOperator() { - - Collection operatorCriteria = Arrays.asList(Criteria.where("x").is(true), - Criteria.where("y").is(42), - Criteria.where("z").is("value")); - - Criteria criteria = Criteria.where("foo").is("bar").norOperator(operatorCriteria); - - assertThat(criteria.getCriteriaObject()) - .isEqualTo("{\"$nor\":[{\"x\":true}, {\"y\":42}, {\"z\":\"value\"}], \"foo\":\"bar\"}"); - } - - @Test // DATAMONGO-507 - public void shouldThrowExceptionWhenTryingToNegateAndOperation() { - assertThatIllegalArgumentException().isThrownBy(() -> new Criteria() // - .not() // - .andOperator(Criteria.where("delete").is(true).and("_id").is(42))); - } - - @Test // DATAMONGO-507 - public void shouldThrowExceptionWhenTryingToNegateOrOperation() { - assertThatIllegalArgumentException().isThrownBy(() -> new Criteria() // - .not() // - .orOperator(Criteria.where("delete").is(true).and("_id").is(42))); - } - - @Test // DATAMONGO-507 - public void shouldThrowExceptionWhenTryingToNegateNorOperation() { - assertThatIllegalArgumentException().isThrownBy(() -> new Criteria() // - .not() // - .norOperator(Criteria.where("delete").is(true).and("_id").is(42))); - } - - @Test // DATAMONGO-507 - public void shouldNegateFollowingSimpleExpression() { - - Criteria c = Criteria.where("age").not().gt(18).and("status").is("student"); - Document co = c.getCriteriaObject(); - - assertThat(co).isNotNull(); - assertThat(co).isEqualTo(Document.parse("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}")); - } - - @Test // DATAMONGO-1068 - public void getCriteriaObjectShouldReturnEmptyDocumentWhenNoCriteriaSpecified() { - - Document document = new Criteria().getCriteriaObject(); - - assertThat(document).isEqualTo(new Document()); - } - - @Test // DATAMONGO-1068 - public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresent() { - - Document document = new Criteria().lt("foo").getCriteriaObject(); - - assertThat(document).isEqualTo(new Document().append("$lt", "foo")); - } - - @Test // DATAMONGO-1068 - public void getCriteriaObjectShouldUseCritieraValuesWhenNoKeyIsPresentButMultipleCriteriasPresent() { - - Document document = new Criteria().lt("foo").gt("bar").getCriteriaObject(); - - assertThat(document).isEqualTo(new Document().append("$lt", "foo").append("$gt", "bar")); - } - - @Test // DATAMONGO-1068 - public void getCriteriaObjectShouldRespectNotWhenNoKeyPresent() { - - Document document = new Criteria().lt("foo").not().getCriteriaObject(); - - assertThat(document).isEqualTo(new Document().append("$not", new Document("$lt", "foo"))); - } - - @Test // DATAMONGO-1135 - public void geoJsonTypesShouldBeWrappedInGeometry() { - - Document document = new Criteria("foo").near(new GeoJsonPoint(100, 200)).getCriteriaObject(); - - assertThat(document).containsEntry("foo.$near.$geometry", new GeoJsonPoint(100, 200)); - } - - @Test // DATAMONGO-1135 - public void legacyCoordinateTypesShouldNotBeWrappedInGeometry() { - - Document document = new Criteria("foo").near(new Point(100, 200)).getCriteriaObject(); - - assertThat(document).doesNotContainKey("foo.$near.$geometry"); - } - - @Test // DATAMONGO-1135 - public void maxDistanceShouldBeMappedInsideNearWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").near(new GeoJsonPoint(100, 200)).maxDistance(50D).getCriteriaObject(); - - assertThat(document).containsEntry("foo.$near.$maxDistance", 50D); - } - - @Test // DATAMONGO-1135 - public void maxDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).maxDistance(50D).getCriteriaObject(); - - assertThat(document).containsEntry("foo.$nearSphere.$maxDistance", 50D); - } - - @Test // DATAMONGO-1110 - public void minDistanceShouldBeMappedInsideNearWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").near(new GeoJsonPoint(100, 200)).minDistance(50D).getCriteriaObject(); - - assertThat(document).containsEntry("foo.$near.$minDistance", 50D); - } - - @Test // DATAMONGO-1110 - public void minDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).minDistance(50D).getCriteriaObject(); - - assertThat(document).containsEntry("foo.$nearSphere.$minDistance", 50D); - } - - @Test // DATAMONGO-1110 - public void minAndMaxDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() { - - Document document = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).minDistance(50D).maxDistance(100D) - .getCriteriaObject(); - - assertThat(document).containsEntry("foo.$nearSphere.$minDistance", 50D); - assertThat(document).containsEntry("foo.$nearSphere.$maxDistance", 100D); - } - - @Test // DATAMONGO-1134 - public void intersectsShouldThrowExceptionWhenCalledWihtNullValue() { - assertThatIllegalArgumentException().isThrownBy(() -> new Criteria("foo").intersects(null)); - } - - @Test // DATAMONGO-1134 - public void intersectsShouldWrapGeoJsonTypeInGeometryCorrectly() { - - GeoJsonLineString lineString = new GeoJsonLineString(new Point(0, 0), new Point(10, 10)); - Document document = new Criteria("foo").intersects(lineString).getCriteriaObject(); - - assertThat(document).containsEntry("foo.$geoIntersects.$geometry", lineString); - } - - @Test // DATAMONGO-1835 - public void extractsJsonSchemaInChainCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build(); - Criteria criteria = Criteria.where("foo").is("bar").andDocumentStructureMatches(schema); - - assertThat(criteria.getCriteriaObject()).isEqualTo(new Document("foo", "bar").append("$jsonSchema", - new Document("type", "object").append("required", Collections.singletonList("name")))); - } - - @Test // DATAMONGO-1835 - public void extractsJsonSchemaFromFactoryMethodCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder().required("name").build(); - Criteria criteria = Criteria.matchingDocumentStructure(schema); - - assertThat(criteria.getCriteriaObject()).isEqualTo(new Document("$jsonSchema", - new Document("type", "object").append("required", Collections.singletonList("name")))); - } - - @Test // DATAMONGO-1808 - public void shouldAppendBitsAllClearWithIntBitmaskCorrectly() { - - Criteria numericBitmaskCriteria = new Criteria("field").bits().allClear(0b101); - - assertThat(numericBitmaskCriteria.getCriteriaObject()) - .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAllClear\" : 5} }")); - } - - @Test // DATAMONGO-1808 - public void shouldAppendBitsAllClearWithPositionListCorrectly() { - - Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().allClear(Arrays.asList(0, 2)); - - assertThat(bitPositionsBitmaskCriteria.getCriteriaObject()) - .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAllClear\" : [ 0, 2 ]} }")); - } - - @Test // DATAMONGO-1808 - public void shouldAppendBitsAllSetWithIntBitmaskCorrectly() { - - Criteria numericBitmaskCriteria = new Criteria("field").bits().allSet(0b101); - - assertThat(numericBitmaskCriteria.getCriteriaObject()) - .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAllSet\" : 5} }")); - } - - @Test // DATAMONGO-1808 - public void shouldAppendBitsAllSetWithPositionListCorrectly() { - - Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().allSet(Arrays.asList(0, 2)); - - assertThat(bitPositionsBitmaskCriteria.getCriteriaObject()) - .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAllSet\" : [ 0, 2 ]} }")); - } - - @Test // DATAMONGO-1808 - public void shouldAppendBitsAnyClearWithIntBitmaskCorrectly() { - - Criteria numericBitmaskCriteria = new Criteria("field").bits().anyClear(0b101); - - assertThat(numericBitmaskCriteria.getCriteriaObject()) - .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAnyClear\" : 5} }")); - } - - @Test // DATAMONGO-1808 - public void shouldAppendBitsAnyClearWithPositionListCorrectly() { - - Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().anyClear(Arrays.asList(0, 2)); - - assertThat(bitPositionsBitmaskCriteria.getCriteriaObject()) - .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAnyClear\" : [ 0, 2 ]} }")); - } - - @Test // DATAMONGO-1808 - public void shouldAppendBitsAnySetWithIntBitmaskCorrectly() { - - Criteria numericBitmaskCriteria = new Criteria("field").bits().anySet(0b101); - - assertThat(numericBitmaskCriteria.getCriteriaObject()) - .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAnySet\" : 5} }")); - } - - @Test // DATAMONGO-1808 - public void shouldAppendBitsAnySetWithPositionListCorrectly() { - - Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().anySet(Arrays.asList(0, 2)); - - assertThat(bitPositionsBitmaskCriteria.getCriteriaObject()) - .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAnySet\" : [ 0, 2 ]} }")); - } - - @Test // DATAMONGO-2002 - public void shouldEqualForSamePattern() { - - Criteria left = new Criteria("field").regex("foo"); - Criteria right = new Criteria("field").regex("foo"); - - assertThat(left).isEqualTo(right); - } - - @Test // DATAMONGO-2002 - public void shouldEqualForDocument() { - - assertThat(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null))) - .isEqualTo(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null))); - - assertThat(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null))) - .isNotEqualTo(new Criteria("field").is(new Document("one", 1).append("two", "two"))); - - assertThat(new Criteria("field").is(new Document("one", 1).append("two", "two"))) - .isNotEqualTo(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null))); - - assertThat(new Criteria("field").is(new Document("one", 1).append("null", null).append("two", "two"))) - .isNotEqualTo(new Criteria("field").is(new Document("one", 1).append("two", "two").append("null", null))); - - assertThat(new Criteria("field").is(new Document())).isNotEqualTo(new Criteria("field").is("foo")); - assertThat(new Criteria("field").is("foo")).isNotEqualTo(new Criteria("field").is(new Document())); - } - - @Test // DATAMONGO-2002 - public void shouldEqualForCollection() { - - assertThat(new Criteria("field").is(Arrays.asList("foo", "bar"))) - .isEqualTo(new Criteria("field").is(Arrays.asList("foo", "bar"))); - - assertThat(new Criteria("field").is(Arrays.asList("foo", 1))) - .isNotEqualTo(new Criteria("field").is(Arrays.asList("foo", "bar"))); - - assertThat(new Criteria("field").is(Collections.singletonList("foo"))) - .isNotEqualTo(new Criteria("field").is(Arrays.asList("foo", "bar"))); - - assertThat(new Criteria("field").is(Arrays.asList("foo", "bar"))) - .isNotEqualTo(new Criteria("field").is(Collections.singletonList("foo"))); - - assertThat(new Criteria("field").is(Arrays.asList("foo", "bar"))).isNotEqualTo(new Criteria("field").is("foo")); - - assertThat(new Criteria("field").is("foo")).isNotEqualTo(new Criteria("field").is(Arrays.asList("foo", "bar"))); - } - - @Test // GH-3414 - public void shouldEqualForSamePatternAndFlags() { - - Criteria left = new Criteria("field").regex("foo", "iu"); - Criteria right = new Criteria("field").regex("foo"); - - assertThat(left).isNotEqualTo(right); - } - - @Test // GH-3414 - public void shouldEqualForNestedPattern() { - - Criteria left = new Criteria("a").orOperator( - new Criteria("foo").regex("value", "i"), - new Criteria("bar").regex("value") - ); - Criteria right = new Criteria("a").orOperator( - new Criteria("foo").regex("value", "i"), - new Criteria("bar").regex("value") - ); - - assertThat(left).isEqualTo(right); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/FieldUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/FieldUnitTests.java deleted file mode 100644 index 82968b3b35..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/FieldUnitTests.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2013-2021 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.query; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link Field}. - * - * @author Oliver Gierke - * @author Owen Q - * @author Mark Paluch - */ -class FieldUnitTests { - - @Test - void sameObjectSetupCreatesEqualField() { - - Field left = new Field().elemMatch("key", Criteria.where("foo").is("bar")); - Field right = new Field().elemMatch("key", Criteria.where("foo").is("bar")); - - assertThat(left).isEqualTo(right); - assertThat(right).isEqualTo(left); - assertThat(left.getFieldsObject()).isEqualTo("{key: { $elemMatch: {foo:\"bar\"}}}"); - } - - @Test // DATAMONGO-2294 - void rendersInclusionCorrectly() { - - Field fields = new Field().include("foo", "bar").include("baz"); - - assertThat(fields.getFieldsObject()).isEqualTo("{foo:1, bar:1, baz:1}"); - } - - @Test - void differentObjectSetupCreatesEqualField() { - - Field left = new Field().elemMatch("key", Criteria.where("foo").is("bar")); - Field right = new Field().elemMatch("key", Criteria.where("foo").is("foo")); - - assertThat(left).isNotEqualTo(right); - assertThat(right).isNotEqualTo(left); - } - - @Test // DATAMONGO-2294 - void rendersExclusionCorrectly() { - - Field fields = new Field().exclude("foo", "bar").exclude("baz"); - - assertThat(fields.getFieldsObject()).isEqualTo("{foo:0, bar:0, baz:0}"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/IndexUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/IndexUnitTests.java deleted file mode 100644 index a7a8d16544..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/IndexUnitTests.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2010-2021 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.query; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.index.Index; - -/** - * Unit tests for {@link Index}. - * - * @author Oliver Gierke - * @author Laurent Canet - */ -public class IndexUnitTests { - - @Test - public void testWithAscendingIndex() { - Index i = new Index().on("name", Direction.ASC); - assertThat(i.getIndexKeys()).isEqualTo(Document.parse("{ \"name\" : 1}")); - } - - @Test - public void testWithDescendingIndex() { - Index i = new Index().on("name", Direction.DESC); - assertThat(i.getIndexKeys()).isEqualTo(Document.parse("{ \"name\" : -1}")); - } - - @Test - public void testNamedMultiFieldUniqueIndex() { - Index i = new Index().on("name", Direction.ASC).on("age", Direction.DESC); - i.named("test").unique(); - assertThat(i.getIndexKeys()).isEqualTo(Document.parse("{ \"name\" : 1 , \"age\" : -1}")); - assertThat(i.getIndexOptions()).isEqualTo(Document.parse("{ \"name\" : \"test\" , \"unique\" : true}")); - } - - @Test - public void testWithSparse() { - Index i = new Index().on("name", Direction.ASC); - i.sparse().unique(); - assertThat(i.getIndexKeys()).isEqualTo(Document.parse("{ \"name\" : 1}")); - assertThat(i.getIndexOptions()).isEqualTo(Document.parse("{ \"unique\" : true , \"sparse\" : true}")); - } - - @Test - public void testGeospatialIndex() { - GeospatialIndex i = new GeospatialIndex("location").withMin(0); - assertThat(i.getIndexKeys()).isEqualTo(Document.parse("{ \"location\" : \"2d\"}")); - assertThat(i.getIndexOptions()).isEqualTo(Document.parse("{ \"min\" : 0}")); - } - - @Test // DATAMONGO-778 - public void testGeospatialIndex2DSphere() { - - GeospatialIndex i = new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE); - assertThat(i.getIndexKeys()).isEqualTo(Document.parse("{ \"location\" : \"2dsphere\"}")); - assertThat(i.getIndexOptions()).isEqualTo(Document.parse("{ }")); - } - - @Test // DATAMONGO-778 - public void testGeospatialIndexGeoHaystack() { - - GeospatialIndex i = new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_HAYSTACK) - .withAdditionalField("name").withBucketSize(40); - assertThat(i.getIndexKeys()).isEqualTo(Document.parse("{ \"location\" : \"geoHaystack\" , \"name\" : 1}")); - assertThat(i.getIndexOptions()).isEqualTo(Document.parse("{ \"bucketSize\" : 40.0}")); - } - - @Test - public void ensuresPropertyOrder() { - - Index on = new Index("foo", Direction.ASC).on("bar", Direction.ASC); - assertThat(on.getIndexKeys()).isEqualTo(Document.parse("{ \"foo\" : 1 , \"bar\" : 1}")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/MetricConversionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/MetricConversionUnitTests.java deleted file mode 100644 index 1bf283ad34..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/MetricConversionUnitTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2016-2021 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.query; - -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.data.Offset.offset; - -import org.junit.jupiter.api.Test; - -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.Metrics; - -/** - * Unit tests for {@link MetricConversion}. - * - * @author Mark Paluch - */ -public class MetricConversionUnitTests { - - @Test // DATAMONGO-1348 - public void shouldConvertMilesToMeters() { - - Distance distance = new Distance(1, Metrics.MILES); - double distanceInMeters = MetricConversion.getDistanceInMeters(distance); - - assertThat(distanceInMeters).isCloseTo(1609.3438343d, offset(0.000000001)); - } - - @Test // DATAMONGO-1348 - public void shouldConvertKilometersToMeters() { - - Distance distance = new Distance(1, Metrics.KILOMETERS); - double distanceInMeters = MetricConversion.getDistanceInMeters(distance); - - assertThat(distanceInMeters).isCloseTo(1000, offset(0.000000001)); - } - - @Test // DATAMONGO-1348 - public void shouldCalculateMetersToKilometersMultiplier() { - - double multiplier = MetricConversion.getMetersToMetricMultiplier(Metrics.KILOMETERS); - - assertThat(multiplier).isCloseTo(0.001, offset(0.000000001)); - } - - @Test // DATAMONGO-1348 - public void shouldCalculateMetersToMilesMultiplier() { - - double multiplier = MetricConversion.getMetersToMetricMultiplier(Metrics.MILES); - - assertThat(multiplier).isCloseTo(0.00062137, offset(0.000000001)); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/MongoRegexCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/MongoRegexCreatorUnitTests.java deleted file mode 100644 index fb35a05204..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/MongoRegexCreatorUnitTests.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2017-2021 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.query; - -import static java.util.Arrays.*; -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.MongoRegexCreatorUnitTests.TestParameter.*; - -import java.util.List; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; -import org.springframework.data.mongodb.core.query.MongoRegexCreator.MatchMode; - -/** - * Tests the creation of Regex's in {@link MongoRegexCreator} - * - * @author Jens Schauder - * @author Oliver Gierke - */ -@RunWith(Parameterized.class) -public class MongoRegexCreatorUnitTests { - - @Parameters(name = "{index}: {0}") // - public static List parameters() { - - return asList(// - test(null, MatchMode.EXACT, null, "Null input string -> null"), // - test("anystring", null, "anystring", "type=null -> input"), // - test("anystring", MatchMode.REGEX, "anystring", "REGEX -> input"), // - test("*", MatchMode.LIKE, ".*", "LIKE * -> .*"), // - test("*.*", MatchMode.LIKE, ".*\\Q.\\E.*", "Wildcards & Punctuation"), // - test("*.", MatchMode.LIKE, ".*\\Q.\\E", "Leading Wildcard & Punctuation"), // - test(".*", MatchMode.LIKE, "\\Q.\\E.*", "Trailing Wildcard & Punctuation"), // - test("other", MatchMode.LIKE, "other", "No Wildcard & Other"), // - test("other*", MatchMode.LIKE, "other.*", "Trailing Wildcard & Other"), // - test("*other", MatchMode.LIKE, ".*other", "Leading Wildcard & Other"), // - test("o*t.*h.er", MatchMode.LIKE, "\\Qo*t.*h.er\\E", "Dots & Stars"), // - test("other", MatchMode.STARTING_WITH, "^other", "Dots & Stars"), // - test("other", MatchMode.ENDING_WITH, "other$", "Dots & Stars"), // - test("other", MatchMode.CONTAINING, ".*other.*", "Dots & Stars"), // - test("other", MatchMode.EXACT, "^other$", "Dots & Stars")); - } - - @Parameter(0) // - public TestParameter parameter; - - @Test - public void testSpecialCases() { - parameter.check(); - } - - @lombok.RequiredArgsConstructor(staticName = "test") - static class TestParameter { - - private final String source; - private final MatchMode mode; - private final String expectedResult, comment; - - void check() { - - assertThat(MongoRegexCreator.INSTANCE.toRegularExpression(source, mode))// - .as(comment)// - .isEqualTo(expectedResult); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return String.format("Mode: %s, %s", mode, comment); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java deleted file mode 100644 index 0eab98cb0b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2011-2021 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.query; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.math.BigDecimal; -import java.math.RoundingMode; - -import org.junit.jupiter.api.Test; - -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.Metric; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; - -/** - * Unit tests for {@link NearQuery}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - */ -public class NearQueryUnitTests { - - private static final Distance ONE_FIFTY_KILOMETERS = new Distance(150, Metrics.KILOMETERS); - - @Test - public void rejectsNullPoint() { - assertThatIllegalArgumentException().isThrownBy(() -> NearQuery.near(null)); - } - - @Test - public void settingUpNearWithMetricRecalculatesDistance() { - - NearQuery query = NearQuery.near(2.5, 2.5, Metrics.KILOMETERS).maxDistance(150); - - assertThat(query.getMaxDistance()).isEqualTo(ONE_FIFTY_KILOMETERS); - assertThat(query.getMetric()).isEqualTo((Metric) Metrics.KILOMETERS); - assertThat(query.isSpherical()).isTrue(); - } - - @Test - public void settingMetricRecalculatesMaxDistance() { - - NearQuery query = NearQuery.near(2.5, 2.5, Metrics.KILOMETERS).maxDistance(150); - - query.inMiles(); - - assertThat(query.getMetric()).isEqualTo((Metric) Metrics.MILES); - } - - @Test - public void configuresResultMetricCorrectly() { - - NearQuery query = NearQuery.near(2.5, 2.1); - assertThat(query.getMetric()).isEqualTo((Metric) Metrics.NEUTRAL); - - query = query.maxDistance(ONE_FIFTY_KILOMETERS); - assertThat(query.getMetric()).isEqualTo((Metric) Metrics.KILOMETERS); - assertThat(query.getMaxDistance()).isEqualTo(ONE_FIFTY_KILOMETERS); - assertThat(query.isSpherical()).isTrue(); - - query = query.in(Metrics.MILES); - assertThat(query.getMetric()).isEqualTo((Metric) Metrics.MILES); - assertThat(query.getMaxDistance()).isEqualTo(ONE_FIFTY_KILOMETERS); - assertThat(query.isSpherical()).isTrue(); - - query = query.maxDistance(new Distance(200, Metrics.KILOMETERS)); - assertThat(query.getMetric()).isEqualTo((Metric) Metrics.MILES); - } - - @Test // DATAMONGO-445, DATAMONGO-2264 - public void shouldTakeSkipAndLimitSettingsFromGivenPageable() { - - Pageable pageable = PageRequest.of(3, 5); - NearQuery query = NearQuery.near(new Point(1, 1)).with(pageable); - - assertThat(query.getSkip()).isEqualTo((long) pageable.getPageNumber() * pageable.getPageSize()); - assertThat(query.toDocument().get("num")).isEqualTo((long) pageable.getPageSize()); - } - - @Test // DATAMONGO-445 - public void shouldTakeSkipAndLimitSettingsFromGivenQuery() { - - int limit = 10; - long skip = 5; - NearQuery query = NearQuery.near(new Point(1, 1)) - .query(Query.query(Criteria.where("foo").is("bar")).limit(limit).skip(skip)); - - assertThat(query.getSkip()).isEqualTo(skip); - assertThat((Long) query.toDocument().get("num")).isEqualTo((long) limit); - } - - @Test // DATAMONGO-445, DATAMONGO-2264 - public void shouldTakeSkipAndLimitSettingsFromPageableEvenIfItWasSpecifiedOnQuery() { - - int limit = 10; - int skip = 5; - Pageable pageable = PageRequest.of(3, 5); - NearQuery query = NearQuery.near(new Point(1, 1)) - .query(Query.query(Criteria.where("foo").is("bar")).limit(limit).skip(skip)).with(pageable); - - assertThat(query.getSkip()).isEqualTo((long) pageable.getPageNumber() * pageable.getPageSize()); - assertThat(query.toDocument().get("num")).isEqualTo((long) pageable.getPageSize()); - } - - @Test // DATAMONGO-829 - public void nearQueryShouldInoreZeroLimitFromQuery() { - - NearQuery query = NearQuery.near(new Point(1, 2)).query(Query.query(Criteria.where("foo").is("bar"))); - assertThat(query.toDocument().get("num")).isNull(); - } - - @Test // DATAMONOGO-829 - public void nearQueryShouldThrowExceptionWhenGivenANullQuery() { - assertThatIllegalArgumentException().isThrownBy(() -> NearQuery.near(new Point(1, 2)).query(null)); - } - - @Test // DATAMONGO-829 - public void numShouldNotBeAlteredByQueryWithoutPageable() { - - long num = 100; - NearQuery query = NearQuery.near(new Point(1, 2)); - query.num(num); - query.query(Query.query(Criteria.where("foo").is("bar"))); - - assertThat(DocumentTestUtils.getTypedValue(query.toDocument(), "num", Long.class)).isEqualTo(num); - } - - @Test // DATAMONGO-1348 - public void shouldNotUseSphericalForLegacyPoint() { - - NearQuery query = NearQuery.near(new Point(27.987901, 86.9165379)); - - assertThat(query.toDocument()).containsEntry("spherical", false); - } - - @Test // DATAMONGO-1348 - public void shouldUseSphericalForLegacyPointIfSet() { - - NearQuery query = NearQuery.near(new Point(27.987901, 86.9165379)); - query.spherical(true); - - assertThat(query.toDocument()).containsEntry("spherical", true); - } - - @Test // DATAMONGO-1348 - public void shouldUseSphericalForGeoJsonData() { - - NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379)); - - assertThat(query.toDocument()).containsEntry("spherical", true); - } - - @Test // DATAMONGO-1348 - public void shouldUseSphericalForGeoJsonDataIfSphericalIsFalse() { - - NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379)); - query.spherical(false); - - assertThat(query.toDocument()).containsEntry("spherical", true); - } - - @Test // DATAMONGO-1348 - public void shouldUseMetersForGeoJsonData() { - - NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379)); - query.maxDistance(1); - - double meterToRadianMultiplier = BigDecimal.valueOf(1 / Metrics.KILOMETERS.getMultiplier() / 1000).// - setScale(8, RoundingMode.HALF_UP).// - doubleValue(); - assertThat(query.toDocument()).containsEntry("maxDistance", Metrics.KILOMETERS.getMultiplier() * 1000) - .containsEntry("distanceMultiplier", meterToRadianMultiplier); - } - - @Test // DATAMONGO-1348 - public void shouldUseMetersForGeoJsonDataWhenDistanceInKilometers() { - - NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379)); - query.maxDistance(new Distance(1, Metrics.KILOMETERS)); - - assertThat(query.toDocument()).containsEntry("maxDistance", 1000D).containsEntry("distanceMultiplier", 0.001D); - } - - @Test // DATAMONGO-1348 - public void shouldUseMetersForGeoJsonDataWhenDistanceInMiles() { - - NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379)); - query.maxDistance(new Distance(1, Metrics.MILES)); - - assertThat(query.toDocument()).containsEntry("maxDistance", 1609.3438343D).containsEntry("distanceMultiplier", - 0.00062137D); - } - - @Test // DATAMONGO-1348 - public void shouldUseKilometersForDistanceWhenMaxDistanceInMiles() { - - NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379)); - query.maxDistance(new Distance(1, Metrics.MILES)).in(Metrics.KILOMETERS); - - assertThat(query.toDocument()).containsEntry("maxDistance", 1609.3438343D).containsEntry("distanceMultiplier", - 0.001D); - } - - @Test // DATAMONGO-1348 - public void shouldUseMilesForDistanceWhenMaxDistanceInKilometers() { - - NearQuery query = NearQuery.near(new GeoJsonPoint(27.987901, 86.9165379)); - query.maxDistance(new Distance(1, Metrics.KILOMETERS)).in(Metrics.MILES); - - assertThat(query.toDocument()).containsEntry("maxDistance", 1000D).containsEntry("distanceMultiplier", 0.00062137D); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryTests.java deleted file mode 100644 index 01dddcd084..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryTests.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright 2010-2021 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.query; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.domain.Sort.Order; -import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; -import org.springframework.data.mongodb.core.SpecialDoc; - -/** - * Unit tests for {@link Query}. - * - * @author Thomas Risberg - * @author Oliver Gierke - * @author Patryk Wasik - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - */ -class QueryTests { - - @Test - void testSimpleQuery() { - - Query q = new Query(where("name").is("Thomas").and("age").lt(80)); - assertThat(q.getQueryObject()).isEqualTo(Document.parse("{ \"name\" : \"Thomas\" , \"age\" : { \"$lt\" : 80}}")); - } - - @Test - void testQueryWithNot() { - - Query q = new Query(where("name").is("Thomas").and("age").not().mod(10, 0)); - assertThat(q.getQueryObject()) - .isEqualTo(Document.parse("{ \"name\" : \"Thomas\" , \"age\" : { \"$not\" : { \"$mod\" : [ 10 , 0]}}}")); - } - - @Test - void testInvalidQueryWithNotIs() { - - assertThatExceptionOfType(InvalidMongoDbApiUsageException.class) - .isThrownBy(() -> new Query(where("name").not().is("Thomas"))); - } - - @Test - void testOrQuery() { - - Query q = new Query(new Criteria().orOperator(where("name").is("Sven").and("age").lt(50), where("age").lt(50), - where("name").is("Thomas"))); - assertThat(q.getQueryObject()).isEqualTo(Document.parse( - "{ \"$or\" : [ { \"name\" : \"Sven\" , \"age\" : { \"$lt\" : 50}} , { \"age\" : { \"$lt\" : 50}} , { \"name\" : \"Thomas\"}]}")); - } - - @Test - void testAndQuery() { - - Query q = new Query(new Criteria().andOperator(where("name").is("Sven"), where("age").lt(50))); - Document expected = Document.parse("{ \"$and\" : [ { \"name\" : \"Sven\"} , { \"age\" : { \"$lt\" : 50}}]}"); - assertThat(q.getQueryObject()).isEqualTo(expected); - } - - @Test - void testNorQuery() { - - Query q = new Query( - new Criteria().norOperator(where("name").is("Sven"), where("age").lt(50), where("name").is("Thomas"))); - assertThat(q.getQueryObject()).isEqualTo(Document - .parse("{ \"$nor\" : [ { \"name\" : \"Sven\"} , { \"age\" : { \"$lt\" : 50}} , { \"name\" : \"Thomas\"}]}")); - } - - @Test - void testQueryWithLimit() { - - Query q = new Query(where("name").gte("M").lte("T").and("age").not().gt(22)); - q.limit(50); - - assertThat(q.getQueryObject()).isEqualTo(Document - .parse("{ \"name\" : { \"$gte\" : \"M\" , \"$lte\" : \"T\"} , \"age\" : { \"$not\" : { \"$gt\" : 22}}}")); - assertThat(q.getLimit()).isEqualTo(50); - } - - @Test - void testQueryWithFieldsAndSlice() { - - Query q = new Query(where("name").gte("M").lte("T").and("age").not().gt(22)); - q.fields().exclude("address").include("name").slice("orders", 10); - - assertThat(q.getQueryObject()).isEqualTo(Document - .parse("{ \"name\" : { \"$gte\" : \"M\" , \"$lte\" : \"T\"} , \"age\" : { \"$not\" : { \"$gt\" : 22}}}")); - - assertThat(q.getFieldsObject()) - .isEqualTo(Document.parse("{ \"address\" : 0 , \"name\" : 1 , \"orders\" : { \"$slice\" : 10}}")); - } - - @Test // DATAMONGO-652 - void testQueryWithFieldsElemMatchAndPositionalOperator() { - - Query query = query(where("name").gte("M").lte("T").and("age").not().gt(22)); - query.fields().elemMatch("products", where("name").is("milk")).position("comments", 2); - - assertThat(query.getQueryObject()).isEqualTo(Document - .parse("{ \"name\" : { \"$gte\" : \"M\" , \"$lte\" : \"T\"} , \"age\" : { \"$not\" : { \"$gt\" : 22}}}")); - assertThat(query.getFieldsObject()) - .isEqualTo(Document.parse("{ \"products\" : { \"$elemMatch\" : { \"name\" : \"milk\"}} , \"comments.$\" : 2}")); - } - - @Test - void testSimpleQueryWithChainedCriteria() { - - Query q = new Query(where("name").is("Thomas").and("age").lt(80)); - assertThat(q.getQueryObject()).isEqualTo(Document.parse("{ \"name\" : \"Thomas\" , \"age\" : { \"$lt\" : 80}}")); - } - - @Test - void testComplexQueryWithMultipleChainedCriteria() { - - Query q = new Query( - where("name").regex("^T.*").and("age").gt(20).lt(80).and("city").in("Stockholm", "London", "New York")); - assertThat(q.getQueryObject().toJson()).isEqualTo(Document.parse( - "{ \"name\" : { \"$regex\" : \"^T.*\", \"$options\" : \"\" } , \"age\" : { \"$gt\" : 20 , \"$lt\" : 80} , " - + "\"city\" : { \"$in\" : [ \"Stockholm\" , \"London\" , \"New York\"]}}") - .toJson()); - } - - @Test - void testAddCriteriaWithComplexQueryWithMultipleChainedCriteria() { - - Query q1 = new Query( - where("name").regex("^T.*").and("age").gt(20).lt(80).and("city").in("Stockholm", "London", "New York")); - Query q2 = new Query(where("name").regex("^T.*").and("age").gt(20).lt(80)) - .addCriteria(where("city").in("Stockholm", "London", "New York")); - - assertThat(q1.getQueryObject()).hasToString(q2.getQueryObject().toString()); - - Query q3 = new Query(where("name").regex("^T.*")).addCriteria(where("age").gt(20).lt(80)) - .addCriteria(where("city").in("Stockholm", "London", "New York")); - assertThat(q1.getQueryObject()).hasToString(q3.getQueryObject().toString()); - } - - @Test - void testQueryWithElemMatch() { - - Query q = new Query(where("openingHours").elemMatch(where("dayOfWeek").is("Monday").and("open").lte("1800"))); - assertThat(q.getQueryObject()).isEqualTo(Document.parse( - "{ \"openingHours\" : { \"$elemMatch\" : { \"dayOfWeek\" : \"Monday\" , \"open\" : { \"$lte\" : \"1800\"}}}}")); - } - - @Test - void testQueryWithIn() { - - Query q = new Query(where("state").in("NY", "NJ", "PA")); - assertThat(q.getQueryObject()).isEqualTo(Document.parse("{ \"state\" : { \"$in\" : [ \"NY\" , \"NJ\" , \"PA\"]}}")); - } - - @Test - void testQueryWithRegex() { - - Query q = new Query(where("name").regex("b.*")); - assertThat(q.getQueryObject().toJson()) - .isEqualTo(Document.parse("{ \"name\" : { \"$regex\" : \"b.*\", \"$options\" : \"\" }}").toJson()); - } - - @Test - void testQueryWithRegexAndOption() { - Query q = new Query(where("name").regex("b.*", "i")); - assertThat(q.getQueryObject().toJson()) - .isEqualTo(Document.parse("{ \"name\" : { \"$regex\" : \"b.*\" , \"$options\" : \"i\"}}").toJson()); - } - - @Test // DATAMONGO-538 - void addsSortCorrectly() { - - Query query = new Query().with(Sort.by(Direction.DESC, "foo")); - assertThat(query.getSortObject()).isEqualTo(Document.parse("{ \"foo\" : -1}")); - } - - @Test - void rejectsOrderWithIgnoreCase() { - - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> new Query().with(Sort.by(Order.asc("foo").ignoreCase()))); - } - - @Test // DATAMONGO-709, DATAMONGO-1735, // DATAMONGO-2198 - void shouldReturnClassHierarchyOfRestrictedTypes() { - - Query query = new Query(where("name").is("foo")).restrict(SpecialDoc.class); - - assertThat(query.getRestrictedTypes()).containsExactly(SpecialDoc.class); - } - - @Test // DATAMONGO-1421 - void addCriteriaForSamePropertyMultipleTimesShouldThrowAndSafelySerializeErrorMessage() { - - assertThatExceptionOfType(InvalidMongoDbApiUsageException.class).isThrownBy(() -> { - - Query query = new Query(); - query.addCriteria(where("value").is(EnumType.VAL_1)); - query.addCriteria(where("value").is(EnumType.VAL_2)); - }).withMessageContaining("second 'value' criteria") - .withMessageContaining("already contains '{ \"value\" : { \"$java\" : VAL_1 } }'"); - } - - @Test // DATAMONGO-1783 - void queryOfShouldCreateNewQueryWithEqualBehaviour() { - - Query source = new Query(); - source.addCriteria(where("This you must ken!").is(EnumType.VAL_1)); - - compareQueries(Query.of(source), source); - } - - @Test // DATAMONGO-1783 - void clonedQueryShouldNotDependOnCriteriaFromSource() { - - Query source = new Query(); - source.addCriteria(where("From one make ten").is("and two let be.")); - Query target = Query.of(source); - - compareQueries(target, source); - source.addCriteria(where("Make even three").is("then rich you'll be.")); - - assertThat(target.getQueryObject()).isEqualTo(new Document("From one make ten", "and two let be.")) - .isNotEqualTo(source.getQueryObject()); - } - - @Test // DATAMONGO-1783 - void clonedQueryShouldAppendCriteria() { - - Query source = new Query(); - source.addCriteria(where("Skip o'er the four!").is("From five and six")); - Query target = Query.of(source); - - compareQueries(target, source); - target.addCriteria(where("the Witch's tricks").is("make seven and eight")); - - assertThat(target.getQueryObject()).isEqualTo( - new Document("Skip o'er the four!", "From five and six").append("the Witch's tricks", "make seven and eight")); - } - - @Test // DATAMONGO-1783 - void clonedQueryShouldNotDependOnCollationFromSource() { - - Query source = new Query().collation(Collation.simple()); - Query target = Query.of(source); - - compareQueries(target, source); - source.collation(Collation.of("Tis finished straight")); - - assertThat(target.getCollation()).contains(Collation.simple()).isNotEqualTo(source.getCollation()); - } - - @Test // DATAMONGO-1783 - void clonedQueryShouldNotDependOnSortFromSource() { - - Query source = new Query().with(Sort.by("And nine is one")); - Query target = Query.of(source); - - compareQueries(target, source); - source.with(Sort.by("And ten is none")); - - assertThat(target.getSortObject()).isEqualTo(new Document("And nine is one", 1)) - .isNotEqualTo(source.getSortObject()); - } - - @Test // DATAMONGO-1783 - void clonedQueryShouldNotDependOnFieldsFromSource() { - - Query source = new Query(); - source.fields().include("That is the witch's one-time-one!"); - Query target = Query.of(source); - - compareQueries(target, source); - source.fields().exclude("Goethe"); - - assertThat(target.getFieldsObject()).isEqualTo(new Document("That is the witch's one-time-one!", 1)) - .isNotEqualTo(source.getFieldsObject()); - } - - @Test // DATAMONGO-1783, DATAMONGO-2572 - void clonedQueryShouldNotDependOnMetaFromSource() { - - Query source = new Query().maxTimeMsec(100); - Query target = Query.of(source); - - compareQueries(target, source); - source.allowSecondaryReads(); - - Meta meta = new Meta(); - meta.setMaxTimeMsec(100); - assertThat(target.getMeta()).isEqualTo(meta).isNotEqualTo(source.getMeta()); - } - - @Test // DATAMONGO-1783 - void clonedQueryShouldNotDependOnRestrictedTypesFromSource() { - - Query source = new Query(); - source.restrict(EnumType.class); - Query target = Query.of(source); - - compareQueries(target, source); - source.restrict(Query.class); - - assertThat(target.getRestrictedTypes()).containsExactly(EnumType.class).isNotEqualTo(source.getRestrictedTypes()); - } - - @Test // DATAMONGO-1783 - void clonedQueryShouldApplyRestrictionsFromBasicQuery() { - - BasicQuery source = new BasicQuery("{ 'foo' : 'bar'}"); - Query target = Query.of(source); - - compareQueries(target, source); - - target.addCriteria(where("one").is("10")); - assertThat(target.getQueryObject()).isEqualTo(new Document("foo", "bar").append("one", "10")) - .isNotEqualTo(source.getQueryObject()); - } - - @Test // DATAMONGO-2478 - void queryOfShouldWorkOnProxiedObjects() { - - BasicQuery source = new BasicQuery("{ 'foo' : 'bar'}", "{ '_id' : -1, 'foo' : 1 }"); - source.withHint("the hint"); - source.limit(10); - source.setSortObject(new Document("_id", 1)); - - Query target = Query.of((Query) new ProxyFactory(source).getProxy()); - - compareQueries(target, source); - } - - private void compareQueries(Query actual, Query expected) { - - assertThat(actual.getCollation()).isEqualTo(expected.getCollation()); - assertThat(actual.getSortObject()).isEqualTo(expected.getSortObject()); - assertThat(actual.getFieldsObject()).isEqualTo(expected.getFieldsObject()); - assertThat(actual.getQueryObject()).isEqualTo(expected.getQueryObject()); - assertThat(actual.getHint()).isEqualTo(expected.getHint()); - assertThat(actual.getLimit()).isEqualTo(expected.getLimit()); - assertThat(actual.getSkip()).isEqualTo(expected.getSkip()); - assertThat(actual.getMeta()).isEqualTo(expected.getMeta()); - assertThat(actual.getRestrictedTypes()).isEqualTo(expected.getRestrictedTypes()); - } - - enum EnumType { - VAL_1, VAL_2 - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/SortTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/SortTests.java deleted file mode 100644 index a2cfb3ade1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/SortTests.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2010-2021 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.query; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; - -/** - * Unit tests for sorting. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -public class SortTests { - - @Test - public void testWithSortAscending() { - - Query s = new Query().with(Sort.by(Direction.ASC, "name")); - assertThat(s.getSortObject()).isEqualTo(Document.parse("{ \"name\" : 1}")); - } - - @Test - public void testWithSortDescending() { - - Query s = new Query().with(Sort.by(Direction.DESC, "name")); - assertThat(s.getSortObject()).isEqualTo(Document.parse("{ \"name\" : -1}")); - } - - @Test // DATADOC-177 - public void preservesOrderKeysOnMultipleSorts() { - - Query sort = new Query().with(Sort.by(Direction.DESC, "foo").and(Sort.by(Direction.DESC, "bar"))); - assertThat(sort.getSortObject()).isEqualTo(Document.parse("{ \"foo\" : -1 , \"bar\" : -1}")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextCriteriaUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextCriteriaUnitTests.java deleted file mode 100644 index 24a728beb2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextCriteriaUnitTests.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2014-2021 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.query; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.core.DocumentTestUtils; - -/** - * Unit tests for {@link TextCriteria}. - * - * @author Christoph Strobl - * @author Daniel Debray - */ -class TextCriteriaUnitTests { - - @Test // DATAMONGO-850 - void shouldNotHaveLanguageField() { - - TextCriteria criteria = TextCriteria.forDefaultLanguage(); - - assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ }")); - } - - @Test // DATAMONGO-850 - void shouldNotHaveLanguageForNonDefaultLanguageField() { - - TextCriteria criteria = TextCriteria.forLanguage("spanish"); - - assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$language\" : \"spanish\" }")); - } - - @Test // DATAMONGO-850 - void shouldCreateSearchFieldForSingleTermCorrectly() { - - TextCriteria criteria = TextCriteria.forDefaultLanguage().matching("cake"); - - assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$search\" : \"cake\" }")); - } - - @Test // DATAMONGO-850 - void shouldCreateSearchFieldCorrectlyForMultipleTermsCorrectly() { - - TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny("bake", "coffee", "cake"); - - assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$search\" : \"bake coffee cake\" }")); - } - - @Test // DATAMONGO-850 - void shouldCreateSearchFieldForPhraseCorrectly() { - - TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingPhrase("coffee cake"); - - assertThat(DocumentTestUtils.getAsDocument(criteria.getCriteriaObject(), "$text")) - .isEqualTo(new Document("$search", "\"coffee cake\"")); - } - - @Test // DATAMONGO-850 - void shouldCreateNotFieldCorrectly() { - - TextCriteria criteria = TextCriteria.forDefaultLanguage().notMatching("cake"); - - assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$search\" : \"-cake\" }")); - } - - @Test // DATAMONGO-850 - void shouldCreateSearchFieldCorrectlyForNotMultipleTermsCorrectly() { - - TextCriteria criteria = TextCriteria.forDefaultLanguage().notMatchingAny("bake", "coffee", "cake"); - - assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$search\" : \"-bake -coffee -cake\" }")); - } - - @Test // DATAMONGO-850 - void shouldCreateSearchFieldForNotPhraseCorrectly() { - - TextCriteria criteria = TextCriteria.forDefaultLanguage().notMatchingPhrase("coffee cake"); - - assertThat(DocumentTestUtils.getAsDocument(criteria.getCriteriaObject(), "$text")) - .isEqualTo(new Document("$search", "-\"coffee cake\"")); - } - - @Test // DATAMONGO-1455 - void caseSensitiveOperatorShouldBeSetCorrectly() { - - TextCriteria criteria = TextCriteria.forDefaultLanguage().matching("coffee").caseSensitive(true); - - assertThat(DocumentTestUtils.getAsDocument(criteria.getCriteriaObject(), "$text")) - .isEqualTo(new Document("$search", "coffee").append("$caseSensitive", true)); - } - - @Test // DATAMONGO-1456 - void diacriticSensitiveOperatorShouldBeSetCorrectly() { - - TextCriteria criteria = TextCriteria.forDefaultLanguage().matching("coffee").diacriticSensitive(true); - - assertThat(DocumentTestUtils.getAsDocument(criteria.getCriteriaObject(), "$text")) - .isEqualTo(new Document("$search", "coffee").append("$diacriticSensitive", true)); - } - - @Test // DATAMONGO-2504 - void twoIdenticalCriteriaShouldBeEqual() { - - TextCriteria criteriaOne = TextCriteria.forDefaultLanguage().matching("coffee"); - TextCriteria criteriaTwo = TextCriteria.forDefaultLanguage().matching("coffee"); - - assertThat(criteriaOne).isEqualTo(criteriaTwo); - assertThat(criteriaOne).hasSameHashCodeAs(criteriaTwo); - assertThat(criteriaOne).isNotEqualTo(criteriaTwo.diacriticSensitive(false)); - assertThat(criteriaOne.hashCode()).isNotEqualTo(criteriaTwo.diacriticSensitive(false).hashCode()); - } - - private Document searchObject(String json) { - return new Document("$text", Document.parse(json)); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java deleted file mode 100644 index 716d767437..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2014-2021 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.query; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; - -import lombok.ToString; - -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.mongodb.core.index.IndexDefinition; -import org.springframework.data.mongodb.core.index.IndexOperations; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.Language; -import org.springframework.data.mongodb.core.mapping.TextScore; -import org.springframework.data.mongodb.core.query.TextQueryTests.FullTextDoc.FullTextDocBuilder; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -public class TextQueryTests { - - private static final FullTextDoc BAKE = new FullTextDocBuilder().headline("bake").build(); - private static final FullTextDoc COFFEE = new FullTextDocBuilder().subHeadline("coffee").build(); - private static final FullTextDoc CAKE = new FullTextDocBuilder().body("cake").build(); - private static final FullTextDoc NOT_TO_BE_FOUND = new FullTextDocBuilder().headline("o_O").build(); - private static final FullTextDoc SPANISH_MILK = new FullTextDocBuilder().headline("leche").lanugage("spanish") - .build(); - private static final FullTextDoc FRENCH_MILK = new FullTextDocBuilder().headline("leche").lanugage("french").build(); - private static final FullTextDoc MILK_AND_SUGAR = new FullTextDocBuilder().headline("milk and sugar").build(); - - @Template(initialEntitySet = FullTextDoc.class) // - static MongoTestTemplate template; - - @BeforeEach - public void setUp() { - - template.flush(); - - IndexOperations indexOps = template.indexOps(FullTextDoc.class); - indexOps.dropAllIndexes(); - - indexOps.ensureIndex(new IndexDefinition() { - - @Override - public Document getIndexOptions() { - Document options = new Document(); - options.put("weights", weights()); - options.put("name", "TextQueryTests_TextIndex"); - options.put("language_override", "lang"); - options.put("default_language", "english"); - return options; - } - - @Override - public Document getIndexKeys() { - Document keys = new Document(); - keys.put("headline", "text"); - keys.put("subheadline", "text"); - keys.put("body", "text"); - return keys; - } - - private Document weights() { - Document weights = new Document(); - weights.put("headline", 10); - weights.put("subheadline", 5); - weights.put("body", 1); - return weights; - } - }); - } - - @Test // DATAMONGO-850 - public void shouldOnlyFindDocumentsMatchingAnyWordOfGivenQuery() { - - initWithDefaultDocuments(); - - List result = template.find(new TextQuery("bake coffee cake"), FullTextDoc.class); - assertThat(result).hasSize(3); - assertThat(result).contains(BAKE, COFFEE, CAKE); - } - - @Test // DATAMONGO-850 - public void shouldNotFindDocumentsWhenQueryDoesNotMatchAnyDocumentInIndex() { - - initWithDefaultDocuments(); - - List result = template.find(new TextQuery("tasmanian devil"), FullTextDoc.class); - assertThat(result).hasSize(0); - } - - @Test // DATAMONGO-850 - public void shouldApplySortByScoreCorrectly() { - - initWithDefaultDocuments(); - FullTextDoc coffee2 = new FullTextDocBuilder().headline("coffee").build(); - template.insert(coffee2); - - List result = template.find(new TextQuery("bake coffee cake").sortByScore(), FullTextDoc.class); - assertThat(result).hasSize(4); - assertThat(result.get(0)).isIn(BAKE, coffee2); - assertThat(result.get(1)).isIn(BAKE, coffee2); - assertThat(result.get(2)).isEqualTo(COFFEE); - assertThat(result.get(3)).isEqualTo(CAKE); - } - - @Test // DATAMONGO-850 - public void shouldFindTextInAnyLanguage() { - - initWithDefaultDocuments(); - List result = template.find(new TextQuery("leche"), FullTextDoc.class); - assertThat(result).hasSize(2); - assertThat(result).contains(SPANISH_MILK, FRENCH_MILK); - } - - @Test // DATAMONGO-850 - public void shouldOnlyFindTextInSpecificLanguage() { - - initWithDefaultDocuments(); - List result = template.find(new TextQuery("leche").addCriteria(where("language").is("spanish")), - FullTextDoc.class); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(SPANISH_MILK); - } - - @Test // DATAMONGO-850 - public void shouldNotFindDocumentsWithNegatedTerms() { - - initWithDefaultDocuments(); - - List result = template.find(new TextQuery("bake coffee -cake"), FullTextDoc.class); - assertThat(result).hasSize(2); - assertThat(result).contains(BAKE, COFFEE); - } - - @Test // DATAMONGO-976 - public void shouldInlcudeScoreCorreclty() { - - initWithDefaultDocuments(); - - List result = template.find(new TextQuery("bake coffee -cake").includeScore().sortByScore(), - FullTextDoc.class); - - assertThat(result).hasSize(2); - for (FullTextDoc scoredDoc : result) { - assertThat(scoredDoc.score > 0F).isTrue(); - } - } - - @Test // DATAMONGO-850 - public void shouldApplyPhraseCorrectly() { - - initWithDefaultDocuments(); - - TextQuery query = TextQuery.queryText(TextCriteria.forDefaultLanguage().matchingPhrase("milk and sugar")); - List result = template.find(query, FullTextDoc.class); - - assertThat(result).hasSize(1); - assertThat(result).containsExactly(MILK_AND_SUGAR); - } - - @Test // DATAMONGO-850 - public void shouldReturnEmptyListWhenNoDocumentsMatchGivenPhrase() { - - initWithDefaultDocuments(); - - TextQuery query = TextQuery.queryText(TextCriteria.forDefaultLanguage().matchingPhrase("milk no sugar")); - List result = template.find(query, FullTextDoc.class); - - assertThat(result).isEmpty(); - } - - @Test // DATAMONGO-850 - public void shouldApplyPaginationCorrectly() { - - initWithDefaultDocuments(); - - // page 1 - List result = template.find(new TextQuery("bake coffee cake").sortByScore().with(PageRequest.of(0, 2)), - FullTextDoc.class); - assertThat(result).hasSize(2); - assertThat(result).containsExactly(BAKE, COFFEE); - - // page 2 - result = template.find(new TextQuery("bake coffee cake").sortByScore().with(PageRequest.of(1, 2)), - FullTextDoc.class); - assertThat(result).hasSize(1); - assertThat(result).containsExactly(CAKE); - } - - private void initWithDefaultDocuments() { - this.template.save(BAKE); - this.template.save(COFFEE); - this.template.save(CAKE); - this.template.save(NOT_TO_BE_FOUND); - this.template.save(SPANISH_MILK); - this.template.save(FRENCH_MILK); - this.template.save(MILK_AND_SUGAR); - } - - @org.springframework.data.mongodb.core.mapping.Document(collection = "fullTextDoc") - @ToString - static class FullTextDoc { - - @Id String id; - - private @Language @Field("lang") String language; - - private String headline; - private String subheadline; - private String body; - - private @TextScore Float score; - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((body == null) ? 0 : body.hashCode()); - result = prime * result + ((headline == null) ? 0 : headline.hashCode()); - result = prime * result + ((id == null) ? 0 : id.hashCode()); - result = prime * result + ((language == null) ? 0 : language.hashCode()); - result = prime * result + ((subheadline == null) ? 0 : subheadline.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof FullTextDoc)) { - return false; - } - FullTextDoc other = (FullTextDoc) obj; - if (body == null) { - if (other.body != null) { - return false; - } - } else if (!body.equals(other.body)) { - return false; - } - if (headline == null) { - if (other.headline != null) { - return false; - } - } else if (!headline.equals(other.headline)) { - return false; - } - if (id == null) { - if (other.id != null) { - return false; - } - } else if (!id.equals(other.id)) { - return false; - } - if (language == null) { - if (other.language != null) { - return false; - } - } else if (!language.equals(other.language)) { - return false; - } - if (subheadline == null) { - if (other.subheadline != null) { - return false; - } - } else if (!subheadline.equals(other.subheadline)) { - return false; - } - return true; - } - - static class FullTextDocBuilder { - - private FullTextDoc instance; - - public FullTextDocBuilder() { - this.instance = new FullTextDoc(); - } - - public FullTextDocBuilder headline(String headline) { - this.instance.headline = headline; - return this; - } - - public FullTextDocBuilder subHeadline(String subHeadline) { - this.instance.subheadline = subHeadline; - return this; - } - - public FullTextDocBuilder body(String body) { - this.instance.body = body; - return this; - } - - public FullTextDocBuilder lanugage(String language) { - this.instance.language = language; - return this; - } - - public FullTextDoc build() { - return this.instance; - } - } - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryUnitTests.java deleted file mode 100644 index 78616e7928..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryUnitTests.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2014-2021 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.query; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import org.junit.jupiter.api.Test; - -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; - -/** - * Unit tests for {@link TextQuery}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -public class TextQueryUnitTests { - - private static final String QUERY = "bake coffee cake"; - private static final String LANGUAGE_SPANISH = "spanish"; - - @Test // DATAMONGO-850 - public void shouldCreateQueryObjectCorrectly() { - assertThat(new TextQuery(QUERY).getQueryObject()).containsEntry("$text.$search", QUERY); - } - - @Test // DATAMONGO-850 - public void shouldIncludeLanguageInQueryObjectWhenNotNull() { - assertThat(new TextQuery(QUERY, LANGUAGE_SPANISH).getQueryObject()).containsEntry("$text.$search", QUERY) - .containsEntry("$text.$language", LANGUAGE_SPANISH); - } - - @Test // DATAMONGO-850 - public void shouldIncludeScoreFieldCorrectly() { - - TextQuery textQuery = new TextQuery(QUERY).includeScore(); - assertThat(textQuery.getQueryObject()).containsEntry("$text.$search", QUERY); - assertThat(textQuery.getFieldsObject()).containsKey("score"); - } - - @Test // DATAMONGO-850 - public void shouldNotOverrideExistingProjections() { - - TextQuery query = new TextQuery(TextCriteria.forDefaultLanguage().matching(QUERY)).includeScore(); - query.fields().include("foo"); - - assertThat(query.getQueryObject()).containsEntry("$text.$search", QUERY); - assertThat(query.getFieldsObject()).containsKeys("score", "foo"); - } - - @Test // DATAMONGO-850 - public void shouldIncludeSortingByScoreCorrectly() { - - TextQuery textQuery = new TextQuery(QUERY).sortByScore(); - - assertThat(textQuery.getQueryObject()).containsEntry("$text.$search", QUERY); - assertThat(textQuery.getFieldsObject()).containsKey("score"); - assertThat(textQuery.getSortObject()).containsKey("score"); - } - - @Test // DATAMONGO-850 - public void shouldNotOverrideExistingSort() { - - TextQuery query = new TextQuery(QUERY); - query.with(Sort.by(Direction.DESC, "foo")); - query.sortByScore(); - - assertThat(query.getQueryObject()).containsEntry("$text.$search", QUERY); - assertThat(query.getFieldsObject()).containsKeys("score"); - assertThat(query.getSortObject()).containsEntry("foo", -1).containsKey("score"); - } - - @Test // DATAMONGO-850 - public void shouldUseCustomFieldnameForScoring() { - - TextQuery query = new TextQuery(QUERY).includeScore("customFieldForScore").sortByScore(); - - assertThat(query.getQueryObject()).containsEntry("$text.$search", QUERY); - assertThat(query.getFieldsObject()).containsKeys("customFieldForScore"); - assertThat(query.getSortObject()).containsKey("customFieldForScore"); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcherUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcherUnitTests.java deleted file mode 100644 index c652a2adc7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcherUnitTests.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2017-2021 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.query; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.data.domain.ExampleMatcher; -import org.springframework.data.domain.ExampleMatcher.NullHandler; -import org.springframework.data.domain.ExampleMatcher.StringMatcher; - -/** - * @author Christoph Strobl - */ -public class UntypedExampleMatcherUnitTests { - - ExampleMatcher matcher; - - @BeforeEach - public void setUp() { - matcher = UntypedExampleMatcher.matching(); - } - - @Test // DATAMONGO-1768 - public void defaultStringMatcherShouldReturnDefault() { - assertThat(matcher.getDefaultStringMatcher()).isEqualTo(StringMatcher.DEFAULT); - } - - @Test // DATAMONGO-1768 - public void ignoreCaseShouldReturnFalseByDefault() { - assertThat(matcher.isIgnoreCaseEnabled()).isFalse(); - } - - @Test // DATAMONGO-1768 - public void ignoredPathsIsEmptyByDefault() { - assertThat(matcher.getIgnoredPaths()).isEmpty(); - } - - @Test // DATAMONGO-1768 - public void nullHandlerShouldReturnIgnoreByDefault() { - assertThat(matcher.getNullHandler()).isEqualTo(NullHandler.IGNORE); - } - - @Test // DATAMONGO-1768 - public void ignoredPathsIsNotModifiable() { - assertThatExceptionOfType(UnsupportedOperationException.class) - .isThrownBy(() -> matcher.getIgnoredPaths().add("¯\\_(ツ)_/¯")); - } - - @Test // DATAMONGO-1768 - public void ignoreCaseShouldReturnTrueWhenIgnoreCaseEnabled() { - - matcher = UntypedExampleMatcher.matching().withIgnoreCase(); - - assertThat(matcher.isIgnoreCaseEnabled()).isTrue(); - } - - @Test // DATAMONGO-1768 - public void ignoreCaseShouldReturnTrueWhenIgnoreCaseSet() { - - matcher = UntypedExampleMatcher.matching().withIgnoreCase(true); - - assertThat(matcher.isIgnoreCaseEnabled()).isTrue(); - } - - @Test // DATAMONGO-1768 - public void nullHandlerShouldReturnInclude() throws Exception { - - matcher = UntypedExampleMatcher.matching().withIncludeNullValues(); - - assertThat(matcher.getNullHandler()).isEqualTo(NullHandler.INCLUDE); - } - - @Test // DATAMONGO-1768 - public void nullHandlerShouldReturnIgnore() { - - matcher = UntypedExampleMatcher.matching().withIgnoreNullValues(); - - assertThat(matcher.getNullHandler()).isEqualTo(NullHandler.IGNORE); - } - - @Test // DATAMONGO-1768 - public void nullHandlerShouldReturnConfiguredValue() { - - matcher = UntypedExampleMatcher.matching().withNullHandler(NullHandler.INCLUDE); - - assertThat(matcher.getNullHandler()).isEqualTo(NullHandler.INCLUDE); - } - - @Test // DATAMONGO-1768 - public void ignoredPathsShouldReturnCorrectProperties() { - - matcher = UntypedExampleMatcher.matching().withIgnorePaths("foo", "bar", "baz"); - - assertThat(matcher.getIgnoredPaths()).contains("foo", "bar", "baz"); - assertThat(matcher.getIgnoredPaths()).hasSize(3); - } - - @Test // DATAMONGO-1768 - public void ignoredPathsShouldReturnUniqueProperties() { - - matcher = UntypedExampleMatcher.matching().withIgnorePaths("foo", "bar", "foo"); - - assertThat(matcher.getIgnoredPaths()).contains("foo", "bar"); - assertThat(matcher.getIgnoredPaths()).hasSize(2); - } - - @Test // DATAMONGO-1768 - public void withCreatesNewInstance() { - - matcher = UntypedExampleMatcher.matching().withIgnorePaths("foo", "bar", "foo"); - ExampleMatcher configuredExampleSpec = matcher.withIgnoreCase(); - - assertThat(matcher).isNotSameAs(configuredExampleSpec); - assertThat(matcher.getIgnoredPaths()).hasSize(2); - assertThat(matcher.isIgnoreCaseEnabled()).isFalse(); - - assertThat(configuredExampleSpec.getIgnoredPaths()).hasSize(2); - assertThat(configuredExampleSpec.isIgnoreCaseEnabled()).isTrue(); - } - - @Test // DATAMONGO-1768 - public void defaultMatcherRequiresAllMatching() { - - assertThat(UntypedExampleMatcher.matching().isAllMatching()).isTrue(); - assertThat(UntypedExampleMatcher.matching().isAnyMatching()).isFalse(); - } - - @Test // DATAMONGO-1768 - public void allMatcherRequiresAllMatching() { - - assertThat(UntypedExampleMatcher.matchingAll().isAllMatching()).isTrue(); - assertThat(UntypedExampleMatcher.matchingAll().isAnyMatching()).isFalse(); - } - - @Test // DATAMONGO-1768 - public void anyMatcherYieldsAnyMatching() { - - assertThat(UntypedExampleMatcher.matchingAny().isAnyMatching()).isTrue(); - assertThat(UntypedExampleMatcher.matchingAny().isAllMatching()).isFalse(); - } - - @Test // DATAMONGO-1768 - public void shouldCompareUsingHashCodeAndEquals() { - - matcher = UntypedExampleMatcher.matching() // - .withIgnorePaths("foo", "bar", "baz") // - .withNullHandler(NullHandler.IGNORE) // - .withIgnoreCase("ignored-case") // - .withMatcher("hello", ExampleMatcher.GenericPropertyMatchers.contains().caseSensitive()) // - .withMatcher("world", matcher -> matcher.endsWith()); - - ExampleMatcher sameAsMatcher = UntypedExampleMatcher.matching() // - .withIgnorePaths("foo", "bar", "baz") // - .withNullHandler(NullHandler.IGNORE) // - .withIgnoreCase("ignored-case") // - .withMatcher("hello", ExampleMatcher.GenericPropertyMatchers.contains().caseSensitive()) // - .withMatcher("world", matcher -> matcher.endsWith()); - - ExampleMatcher different = UntypedExampleMatcher.matching() // - .withIgnorePaths("foo", "bar", "baz") // - .withNullHandler(NullHandler.IGNORE) // - .withMatcher("hello", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase()); - - assertThat(matcher.hashCode()).isEqualTo(sameAsMatcher.hashCode()); - assertThat(matcher.hashCode()).isNotEqualTo(different.hashCode()); - assertThat(matcher).isEqualTo(sameAsMatcher); - assertThat(matcher).isNotEqualTo(different); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/UpdateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/UpdateTests.java deleted file mode 100644 index 927aa28327..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/UpdateTests.java +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright 2010-2021 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.query; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Collections; -import java.util.Date; -import java.util.Map; - -import org.bson.Document; -import org.joda.time.DateTime; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.query.Update.Position; - -/** - * Test cases for {@link Update}. - * - * @author Oliver Gierke - * @author Thomas Risberg - * @author Becca Gaspard - * @author Christoph Strobl - * @author Thomas Darimont - * @author Alexey Plotnik - */ -public class UpdateTests { - - @Test - public void testSet() { - - Update u = new Update().set("directory", "/Users/Test/Desktop"); - assertThat(u.getUpdateObject()) - .isEqualTo(Document.parse("{ \"$set\" : { \"directory\" : \"/Users/Test/Desktop\"}}")); - } - - @Test - public void testSetSet() { - - Update u = new Update().set("directory", "/Users/Test/Desktop").set("size", 0); - assertThat(u.getUpdateObject()) - .isEqualTo((Document.parse("{ \"$set\" : { \"directory\" : \"/Users/Test/Desktop\" , \"size\" : 0}}"))); - } - - @Test - public void testInc() { - - Update u = new Update().inc("size", 1); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$inc\" : { \"size\" : 1}}")); - } - - @Test - public void testIncInc() { - - Update u = new Update().inc("size", 1).inc("count", 1); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$inc\" : { \"size\" : 1 , \"count\" : 1}}")); - } - - @Test - public void testIncAndSet() { - - Update u = new Update().inc("size", 1).set("directory", "/Users/Test/Desktop"); - assertThat(u.getUpdateObject()).isEqualTo( - Document.parse("{ \"$inc\" : { \"size\" : 1} , \"$set\" : { \"directory\" : \"/Users/Test/Desktop\"}}")); - } - - @Test - public void testUnset() { - - Update u = new Update().unset("directory"); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$unset\" : { \"directory\" : 1}}")); - } - - @Test - public void testPush() { - - Update u = new Update().push("authors", Collections.singletonMap("name", "Sven")); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$push\" : { \"authors\" : { \"name\" : \"Sven\"}}}")); - } - - @Test - public void testPushAll() { - - Map m1 = Collections.singletonMap("name", "Sven"); - Map m2 = Collections.singletonMap("name", "Maria"); - - Update u = new Update().pushAll("authors", new Object[] { m1, m2 }); - assertThat(u.getUpdateObject()).isEqualTo( - Document.parse("{ \"$pushAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}]}}")); - } - - @Test // DATAMONGO-354 - public void testMultiplePushAllShouldBePossibleWhenUsingDifferentFields() { - - Map m1 = Collections.singletonMap("name", "Sven"); - Map m2 = Collections.singletonMap("name", "Maria"); - - Update u = new Update().pushAll("authors", new Object[] { m1, m2 }); - u.pushAll("books", new Object[] { "Spring in Action" }); - - assertThat(u.getUpdateObject()).isEqualTo(Document.parse( - "{ \"$pushAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}] , \"books\" : [ \"Spring in Action\"]}}")); - } - - @Test - public void testAddToSet() { - - Update u = new Update().addToSet("authors", Collections.singletonMap("name", "Sven")); - assertThat(u.getUpdateObject()) - .isEqualTo(Document.parse("{ \"$addToSet\" : { \"authors\" : { \"name\" : \"Sven\"}}}")); - } - - @Test - public void testPop() { - - Update u = new Update().pop("authors", Update.Position.FIRST); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$pop\" : { \"authors\" : -1}}")); - - u = new Update().pop("authors", Update.Position.LAST); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$pop\" : { \"authors\" : 1}}")); - } - - @Test - public void testPull() { - - Update u = new Update().pull("authors", Collections.singletonMap("name", "Sven")); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$pull\" : { \"authors\" : { \"name\" : \"Sven\"}}}")); - } - - @Test - public void testPullAll() { - - Map m1 = Collections.singletonMap("name", "Sven"); - Map m2 = Collections.singletonMap("name", "Maria"); - - Update u = new Update().pullAll("authors", new Object[] { m1, m2 }); - assertThat(u.getUpdateObject()).isEqualTo( - Document.parse("{ \"$pullAll\" : { \"authors\" : [ { \"name\" : \"Sven\"} , { \"name\" : \"Maria\"}]}}")); - } - - @Test - public void testRename() { - - Update u = new Update().rename("directory", "folder"); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$rename\" : { \"directory\" : \"folder\"}}")); - } - - @Test - public void testBasicUpdateInc() { - - Update u = new Update().inc("size", 1); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$inc\" : { \"size\" : 1}}")); - } - - @Test - public void testBasicUpdateIncAndSet() { - - Update u = new BasicUpdate("{ \"$inc\" : { \"size\" : 1}}").set("directory", "/Users/Test/Desktop"); - assertThat(u.getUpdateObject()).isEqualTo( - Document.parse("{ \"$inc\" : { \"size\" : 1} , \"$set\" : { \"directory\" : \"/Users/Test/Desktop\"}}")); - } - - @Test // DATAMONGO-630 - public void testSetOnInsert() { - - Update u = new Update().setOnInsert("size", 1); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$setOnInsert\" : { \"size\" : 1}}")); - } - - @Test // DATAMONGO-630 - public void testSetOnInsertSetOnInsert() { - - Update u = new Update().setOnInsert("size", 1).setOnInsert("count", 1); - assertThat(u.getUpdateObject()).isEqualTo(Document.parse("{ \"$setOnInsert\" : { \"size\" : 1 , \"count\" : 1}}")); - } - - @Test // DATAMONGO-852 - public void testUpdateAffectsFieldShouldReturnTrueWhenMultiFieldOperationAddedForField() { - - Update update = new Update().set("foo", "bar"); - assertThat(update.modifies("foo")).isTrue(); - } - - @Test // DATAMONGO-852 - public void testUpdateAffectsFieldShouldReturnFalseWhenMultiFieldOperationAddedForField() { - - Update update = new Update().set("foo", "bar"); - assertThat(update.modifies("oof")).isFalse(); - } - - @Test // DATAMONGO-852 - public void testUpdateAffectsFieldShouldReturnTrueWhenSingleFieldOperationAddedForField() { - - Update update = new Update().pullAll("foo", new Object[] { "bar" }); - assertThat(update.modifies("foo")).isTrue(); - } - - @Test // DATAMONGO-852 - public void testUpdateAffectsFieldShouldReturnFalseWhenSingleFieldOperationAddedForField() { - - Update update = new Update().pullAll("foo", new Object[] { "bar" }); - assertThat(update.modifies("oof")).isFalse(); - } - - @Test // DATAMONGO-852 - public void testUpdateAffectsFieldShouldReturnFalseWhenCalledOnEmptyUpdate() { - assertThat(new Update().modifies("foo")).isFalse(); - } - - @Test // DATAMONGO-852 - public void testUpdateAffectsFieldShouldReturnTrueWhenUpdateWithKeyCreatedFromDocument() { - - Update update = new Update().set("foo", "bar"); - Update clone = Update.fromDocument(update.getUpdateObject()); - - assertThat(clone.modifies("foo")).isTrue(); - } - - @Test // DATAMONGO-852 - public void testUpdateAffectsFieldShouldReturnFalseWhenUpdateWithoutKeyCreatedFromDocument() { - - Update update = new Update().set("foo", "bar"); - Update clone = Update.fromDocument(update.getUpdateObject()); - - assertThat(clone.modifies("oof")).isFalse(); - } - - @Test // DATAMONGO-853 - public void testAddingMultiFieldOperationThrowsExceptionWhenCalledWithNullKey() { - assertThatIllegalArgumentException().isThrownBy( - () -> new Update().addMultiFieldOperation("$op", null, "exprected to throw IllegalArgumentException.")); - } - - @Test // DATAMONGO-853 - public void testAddingSingleFieldOperationThrowsExceptionWhenCalledWithNullKey() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new Update().addFieldOperation("$op", null, "exprected to throw IllegalArgumentException.")); - } - - @Test // DATAMONGO-853 - public void testCreatingUpdateWithNullKeyThrowsException() { - assertThatIllegalArgumentException().isThrownBy(() -> Update.update(null, "value")); - } - - @Test // DATAMONGO-953 - public void testEquality() { - - Update actualUpdate = new Update() // - .inc("size", 1) // - .set("nl", null) // - .set("directory", "/Users/Test/Desktop") // - .push("authors", Collections.singletonMap("name", "Sven")) // - .pop("authors", Update.Position.FIRST) // - .set("foo", "bar"); - - Update expectedUpdate = new Update() // - .inc("size", 1) // - .set("nl", null) // - .set("directory", "/Users/Test/Desktop") // - .push("authors", Collections.singletonMap("name", "Sven")) // - .pop("authors", Update.Position.FIRST) // - .set("foo", "bar"); - - assertThat(actualUpdate).isEqualTo(actualUpdate); - assertThat(actualUpdate.hashCode()).isEqualTo(actualUpdate.hashCode()); - assertThat(actualUpdate).isEqualTo(expectedUpdate); - assertThat(actualUpdate.hashCode()).isEqualTo(expectedUpdate.hashCode()); - } - - @Test // DATAMONGO-953 - public void testToString() { - - Update actualUpdate = new Update() // - .inc("size", 1) // - .set("nl", null) // - .set("directory", "/Users/Test/Desktop") // - .push("authors", Collections.singletonMap("name", "Sven")) // - .pop("authors", Update.Position.FIRST) // - .set("foo", "bar"); - - Update expectedUpdate = new Update() // - .inc("size", 1) // - .set("nl", null) // - .set("directory", "/Users/Test/Desktop") // - .push("authors", Collections.singletonMap("name", "Sven")) // - .pop("authors", Update.Position.FIRST) // - .set("foo", "bar"); - - assertThat(actualUpdate.toString()).isEqualTo(expectedUpdate.toString()); - assertThat(actualUpdate.getUpdateObject()).isEqualTo(Document.parse("{ \"$inc\" : { \"size\" : 1} ," // - + " \"$set\" : { \"nl\" : null , \"directory\" : \"/Users/Test/Desktop\" , \"foo\" : \"bar\"} , " // - + "\"$push\" : { \"authors\" : { \"name\" : \"Sven\"}} " // - + ", \"$pop\" : { \"authors\" : -1}}")); // - } - - @Test // DATAMONGO-944 - public void getUpdateObjectShouldReturnCurrentDateCorrectlyForSingleFieldWhenUsingDate() { - - Update update = new Update().currentDate("foo"); - assertThat(update.getUpdateObject()).isEqualTo(new Document().append("$currentDate", new Document("foo", true))); - } - - @Test // DATAMONGO-944 - public void getUpdateObjectShouldReturnCurrentDateCorrectlyForMultipleFieldsWhenUsingDate() { - - Update update = new Update().currentDate("foo").currentDate("bar"); - assertThat(update.getUpdateObject()) - .isEqualTo(new Document().append("$currentDate", new Document("foo", true).append("bar", true))); - } - - @Test // DATAMONGO-944 - public void getUpdateObjectShouldReturnCurrentDateCorrectlyForSingleFieldWhenUsingTimestamp() { - - Update update = new Update().currentTimestamp("foo"); - assertThat(update.getUpdateObject()) - .isEqualTo(new Document().append("$currentDate", new Document("foo", new Document("$type", "timestamp")))); - } - - @Test // DATAMONGO-944 - public void getUpdateObjectShouldReturnCurrentDateCorrectlyForMultipleFieldsWhenUsingTimestamp() { - - Update update = new Update().currentTimestamp("foo").currentTimestamp("bar"); - assertThat(update.getUpdateObject()).isEqualTo(new Document().append("$currentDate", - new Document("foo", new Document("$type", "timestamp")).append("bar", new Document("$type", "timestamp")))); - } - - @Test // DATAMONGO-944 - public void getUpdateObjectShouldReturnCurrentDateCorrectlyWhenUsingMixedDateAndTimestamp() { - - Update update = new Update().currentDate("foo").currentTimestamp("bar"); - assertThat(update.getUpdateObject()).isEqualTo(new Document().append("$currentDate", - new Document("foo", true).append("bar", new Document("$type", "timestamp")))); - } - - @Test // DATAMONGO-1002 - public void toStringWorksForUpdateWithComplexObject() { - - Update update = new Update().addToSet("key", new DateTime()); - assertThat(update.toString()).isNotNull(); - } - - @Test // DATAMONGO-1097 - public void multiplyShouldThrowExceptionForNullMultiplier() { - assertThatIllegalArgumentException().isThrownBy(() -> new Update().multiply("key", null)); - } - - @Test // DATAMONGO-1097 - public void multiplyShouldAddMultiplierAsItsDoubleValue() { - - Update update = new Update().multiply("key", 10); - - assertThat(update.getUpdateObject()).isEqualTo(new Document().append("$mul", new Document("key", 10D))); - } - - @Test // DATAMONGO-1101 - public void getUpdateObjectShouldReturnCorrectRepresentationForBitwiseAnd() { - - Update update = new Update().bitwise("key").and(10L); - - assertThat(update.getUpdateObject()) - .isEqualTo(new Document().append("$bit", new Document("key", new Document("and", 10L)))); - } - - @Test // DATAMONGO-1101 - public void getUpdateObjectShouldReturnCorrectRepresentationForBitwiseOr() { - - Update update = new Update().bitwise("key").or(10L); - - assertThat(update.getUpdateObject()) - .isEqualTo(new Document().append("$bit", new Document("key", new Document("or", 10L)))); - } - - @Test // DATAMONGO-1101 - public void getUpdateObjectShouldReturnCorrectRepresentationForBitwiseXor() { - - Update update = new Update().bitwise("key").xor(10L); - - assertThat(update.getUpdateObject()) - .isEqualTo(new Document().append("$bit", new Document("key", new Document("xor", 10L)))); - } - - @Test // DATAMONGO-1346 - public void registersMultiplePullAllClauses() { - - Update update = new Update(); - update.pullAll("field1", new String[] { "foo" }); - update.pullAll("field2", new String[] { "bar" }); - - Document updateObject = update.getUpdateObject(); - - Document pullAll = DocumentTestUtils.getAsDocument(updateObject, "$pullAll"); - - assertThat(pullAll.get("field1")).isNotNull(); - assertThat(pullAll.get("field2")).isNotNull(); - } - - @Test // DATAMONGO-1404 - public void maxShouldThrowExceptionForNullMultiplier() { - assertThatIllegalArgumentException().isThrownBy(() -> new Update().max("key", null)); - } - - @Test // DATAMONGO-1404 - public void minShouldThrowExceptionForNullMultiplier() { - assertThatIllegalArgumentException().isThrownBy(() -> new Update().min("key", null)); - } - - @Test // DATAMONGO-1404 - public void getUpdateObjectShouldReturnCorrectRepresentationForMax() { - - Update update = new Update().max("key", 10); - - assertThat(update.getUpdateObject()).isEqualTo(new Document("$max", new Document("key", 10))); - } - - @Test // DATAMONGO-1404 - public void getUpdateObjectShouldReturnCorrectRepresentationForMin() { - - Update update = new Update().min("key", 10); - - assertThat(update.getUpdateObject()).isEqualTo(new Document("$min", new Document("key", 10))); - } - - @Test // DATAMONGO-1404 - public void shouldSuppressPreviousValueForMax() { - - Update update = new Update().max("key", 10); - update.max("key", 99); - - assertThat(update.getUpdateObject()).isEqualTo(new Document("$max", new Document("key", 99))); - } - - @Test // DATAMONGO-1404 - public void shouldSuppressPreviousValueForMin() { - - Update update = new Update().min("key", 10); - update.min("key", 99); - - assertThat(update.getUpdateObject()).isEqualTo(new Document("$min", new Document("key", 99))); - } - - @Test // DATAMONGO-1404 - public void getUpdateObjectShouldReturnCorrectDateRepresentationForMax() { - - Date date = new Date(); - Update update = new Update().max("key", date); - - assertThat(update.getUpdateObject()).isEqualTo(new Document("$max", new Document("key", date))); - } - - @Test // DATAMONGO-1404 - public void getUpdateObjectShouldReturnCorrectDateRepresentationForMin() { - - Date date = new Date(); - Update update = new Update().min("key", date); - - assertThat(update.getUpdateObject()).isEqualTo(new Document("$min", new Document("key", date))); - } - - @Test // DATAMONGO-1777, DATAMONGO-2199 - public void toStringShouldPrettyPrintModifiers() { - - assertThat(new Update().push("key").atPosition(Position.FIRST).value("Arya").toString()).isEqualTo( - "{ \"$push\" : { \"key\" : { \"$java\" : { \"$position\" : { \"$java\" : { \"$position\" : 0} }, \"$each\" : { \"$java\" : { \"$each\" : [ \"Arya\" ] } } } } } }"); - } - - @Test // DATAMONGO-1777, DATAMONGO-2198 - public void toStringConsidersIsolated() { - - assertThat(new Update().set("key", "value").isolated().toString()).contains("\"$isolated\""); - } - - @Test // DATAMONGO-1778 - public void equalsShouldConsiderModifiers() { - - Update update1 = new Update().inc("version", 1).push("someField").slice(-10).each("test"); - Update update2 = new Update().inc("version", 1).push("someField").slice(-10).each("test"); - Update update3 = new Update().inc("version", 1).push("someField").slice(10).each("test"); - - assertThat(update1).isEqualTo(update2); - assertThat(update1).isNotEqualTo(update3); - } - - @Test // DATAMONGO-1778 - public void equalsShouldConsiderIsolated() { - - Update update1 = new Update().inc("version", 1).isolated(); - Update update2 = new Update().inc("version", 1).isolated(); - - assertThat(update1).isEqualTo(update2); - } - - @Test // DATAMONGO-1778 - public void hashCodeShouldConsiderModifiers() { - - Update update1 = new Update().inc("version", 1).push("someField").slice(-10).each("test"); - Update update2 = new Update().inc("version", 1).push("someField").slice(-10).each("test"); - Update update3 = new Update().inc("version", 1).push("someField").slice(10).each("test"); - - assertThat(update1.hashCode()).isEqualTo(update2.hashCode()); - assertThat(update1.hashCode()).isNotEqualTo(update3.hashCode()); - } - - @Test // DATAMONGO-1778 - public void hashCodeShouldConsiderIsolated() { - - Update update1 = new Update().inc("version", 1).isolated(); - Update update2 = new Update().inc("version", 1).isolated(); - Update update3 = new Update().inc("version", 1); - - assertThat(update1.hashCode()).isEqualTo(update2.hashCode()); - assertThat(update1.hashCode()).isNotEqualTo(update3.hashCode()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/JsonSchemaObjectUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/JsonSchemaObjectUnitTests.java deleted file mode 100644 index 0751bc7f40..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/JsonSchemaObjectUnitTests.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright 2018-2021 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.schema; - -import static org.springframework.data.domain.Range.from; -import static org.springframework.data.domain.Range.Bound.*; -import static org.springframework.data.mongodb.core.schema.JsonSchemaObject.*; -import static org.springframework.data.mongodb.core.schema.JsonSchemaObject.array; -import static org.springframework.data.mongodb.core.schema.JsonSchemaObject.of; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.domain.Range; -import org.springframework.data.domain.Range.*; - -/** - * Tests verifying {@link org.bson.Document} representation of {@link JsonSchemaObject}s. - * - * @author Christoph Strobl - * @author Mark Paluch - * @author Michał Kurcius - */ -class JsonSchemaObjectUnitTests { - - // ----------------- - // type from class - // ----------------- - - @Test // DATAMONGO-1849 - void primitiveType() { - - assertThat(JsonSchemaObject.of(boolean.class).getTypes()).containsExactly(Type.booleanType()); - assertThat(JsonSchemaObject.of(int.class).getTypes()).containsExactly(Type.intType()); - assertThat(JsonSchemaObject.of(long.class).getTypes()).containsExactly(Type.longType()); - assertThat(JsonSchemaObject.of(float.class).getTypes()).containsExactly(Type.doubleType()); - assertThat(JsonSchemaObject.of(double.class).getTypes()).containsExactly(Type.doubleType()); - assertThat(JsonSchemaObject.of(short.class).getTypes()).containsExactly(Type.numberType()); - } - - @Test // DATAMONGO-1849 - void objectType() { - - assertThat(JsonSchemaObject.of(Object.class).getTypes()).containsExactly(Type.objectType()); - assertThat(JsonSchemaObject.of(Map.class).getTypes()).containsExactly(Type.objectType()); - assertThat(JsonSchemaObject.of(Document.class).getTypes()).containsExactly(Type.objectType()); - } - - @Test // DATAMONGO-1849 - void binaryData() { - assertThat(JsonSchemaObject.of(byte[].class).getTypes()).containsExactly(Type.binaryType()); - } - - @Test // DATAMONGO-1849 - void collectionType() { - - assertThat(JsonSchemaObject.of(Object[].class).getTypes()).containsExactly(Type.arrayType()); - assertThat(JsonSchemaObject.of(Collection.class).getTypes()).containsExactly(Type.arrayType()); - assertThat(JsonSchemaObject.of(List.class).getTypes()).containsExactly(Type.arrayType()); - assertThat(JsonSchemaObject.of(Set.class).getTypes()).containsExactly(Type.arrayType()); - } - - @Test // DATAMONGO-1849 - void dateType() { - assertThat(JsonSchemaObject.of(Date.class).getTypes()).containsExactly(Type.dateType()); - } - - // ----------------- - // type : 'object' - // ----------------- - - @Test // DATAMONGO-1835 - void objectObjectShouldRenderTypeCorrectly() { - - assertThat(object().generatedDescription().toDocument()) - .isEqualTo(new Document("type", "object").append("description", "Must be an object.")); - } - - @Test // DATAMONGO-1835 - void objectObjectShouldRenderNrPropertiesCorrectly() { - - assertThat(object().propertiesCount(from(inclusive(10)).to(inclusive(20))).generatedDescription().toDocument()) - .isEqualTo(new Document("type", "object").append("description", "Must be an object with [10-20] properties.") - .append("minProperties", 10).append("maxProperties", 20)); - } - - @Test // DATAMONGO-1835 - void objectObjectShouldRenderRequiredPropertiesCorrectly() { - - assertThat(object().required("spring", "data", "mongodb").generatedDescription().toDocument()) - .isEqualTo(new Document("type", "object") - .append("description", "Must be an object where spring, data, mongodb are mandatory.") - .append("required", Arrays.asList("spring", "data", "mongodb"))); - } - - @Test // DATAMONGO-1835 - void objectObjectShouldRenderAdditionalPropertiesCorrectlyWhenBoolean() { - - assertThat(object().additionalProperties(true).generatedDescription().toDocument()).isEqualTo( - new Document("type", "object").append("description", "Must be an object allowing additional properties.") - .append("additionalProperties", true)); - - assertThat(object().additionalProperties(false).generatedDescription().toDocument()).isEqualTo( - new Document("type", "object").append("description", "Must be an object not allowing additional properties.") - .append("additionalProperties", false)); - } - - @Test // DATAMONGO-1835 - void objectObjectShouldRenderPropertiesCorrectly() { - - Document expected = new Document("type", "object") - .append("description", "Must be an object defining restrictions for name, active.").append("properties", - new Document("name", new Document("type", "string") - .append("description", "Must be a string with length unbounded-10].").append("maxLength", 10)) - .append("active", new Document("type", "boolean"))); - - assertThat(object().generatedDescription() - .properties(JsonSchemaProperty.string("name").maxLength(10).generatedDescription(), - JsonSchemaProperty.bool("active")) - .generatedDescription().toDocument()).isEqualTo(expected); - } - - @Test // DATAMONGO-1835 - void objectObjectShouldRenderNestedObjectPropertiesCorrectly() { - - Document expected = new Document("type", "object") - .append("description", "Must be an object defining restrictions for address.") - .append("properties", new Document("address", - new Document("type", "object").append("description", "Must be an object defining restrictions for city.") - .append("properties", new Document("city", new Document("type", "string") - .append("description", "Must be a string with length [3-unbounded.").append("minLength", 3))))); - - assertThat(object() - .properties(JsonSchemaProperty.object("address") - .properties(JsonSchemaProperty.string("city").minLength(3).generatedDescription()).generatedDescription()) - .generatedDescription().toDocument()).isEqualTo(expected); - } - - @Test // DATAMONGO-1835 - void objectObjectShouldRenderPatternPropertiesCorrectly() { - - Document expected = new Document("type", "object") - .append("description", "Must be an object defining restrictions for patterns na.*.") - .append("patternProperties", new Document("na.*", new Document("type", "string") - .append("description", "Must be a string with length unbounded-10].").append("maxLength", 10))); - - assertThat(object().patternProperties(JsonSchemaProperty.string("na.*").maxLength(10).generatedDescription()) - .generatedDescription().toDocument()).isEqualTo(expected); - } - - @Test // DATAMONGO-1849 - void objectShouldIncludeRequiredNestedCorrectly() { - - assertThat(object() // - .properties( // - JsonSchemaProperty.required(JsonSchemaProperty.string("lastname")) // - ).toDocument()) - .isEqualTo(new Document("type", "object").append("required", Collections.singletonList("lastname")) - .append("properties", new Document("lastname", new Document("type", "string")))); - } - - // ----------------- - // type : 'string' - // ----------------- - - @Test // DATAMONGO-1835 - void stringObjectShouldRenderTypeCorrectly() { - - assertThat(string().generatedDescription().toDocument()) - .isEqualTo(new Document("type", "string").append("description", "Must be a string.")); - } - - @Test // DATAMONGO-1835 - void stringObjectShouldRenderDescriptionCorrectly() { - - assertThat(string().description("error msg").toDocument()) - .isEqualTo(new Document("type", "string").append("description", "error msg")); - } - - @Test // DATAMONGO-1835 - void stringObjectShouldRenderRangeCorrectly() { - - assertThat(string().length(from(inclusive(10)).to(inclusive(20))).generatedDescription().toDocument()) - .isEqualTo(new Document("type", "string").append("description", "Must be a string with length [10-20].") - .append("minLength", 10).append("maxLength", 20)); - } - - @Test // DATAMONGO-1835 - void stringObjectShouldRenderPatternCorrectly() { - - assertThat(string().matching("^spring$").generatedDescription().toDocument()) - .isEqualTo(new Document("type", "string").append("description", "Must be a string matching ^spring$.") - .append("pattern", "^spring$")); - } - - // ----------------- - // type : 'number' - // ----------------- - - @Test // DATAMONGO-1835 - void numberObjectShouldRenderMultipleOfCorrectly() { - - assertThat(number().multipleOf(3.141592F).generatedDescription().toDocument()) - .isEqualTo(new Document("type", "number").append("description", "Must be a numeric value multiple of 3.141592.") - .append("multipleOf", 3.141592F)); - } - - @Test // DATAMONGO-1835 - void numberObjectShouldRenderMaximumCorrectly() { - - assertThat( - number().within(Range.of(Bound.unbounded(), Bound.inclusive(3.141592F))).generatedDescription().toDocument()) - .isEqualTo(new Document("type", "number") - .append("description", "Must be a numeric value within range unbounded-3.141592].") - .append("maximum", 3.141592F)); - - assertThat( - number().within(Range.of(Bound.unbounded(), Bound.exclusive(3.141592F))).generatedDescription().toDocument()) - .isEqualTo(new Document("type", "number") - .append("description", "Must be a numeric value within range unbounded-3.141592).") - .append("maximum", 3.141592F).append("exclusiveMaximum", true)); - } - - @Test // DATAMONGO-1835 - void numberObjectShouldRenderMinimumCorrectly() { - - assertThat( - number().within(Range.of(Bound.inclusive(3.141592F), Bound.unbounded())).generatedDescription().toDocument()) - .isEqualTo(new Document("type", "number") - .append("description", "Must be a numeric value within range [3.141592-unbounded.") - .append("minimum", 3.141592F)); - - assertThat( - number().within(Range.of(Bound.exclusive(3.141592F), Bound.unbounded())).generatedDescription().toDocument()) - .isEqualTo(new Document("type", "number") - .append("description", "Must be a numeric value within range (3.141592-unbounded.") - .append("minimum", 3.141592F).append("exclusiveMinimum", true)); - } - - // ----------------- - // type : 'arrays' - // ----------------- - - @Test // DATAMONGO-1835 - void arrayObjectShouldRenderItemsCorrectly() { - - assertThat(array().items(Arrays.asList(string(), bool())).toDocument()).isEqualTo(new Document("type", "array") - .append("items", Arrays.asList(new Document("type", "string"), new Document("type", "boolean")))); - } - - @Test // DATAMONGO-2613 - void arrayObjectShouldRenderItemsCorrectlyAsObjectIfContainsOnlyOneElement() { - - assertThat(array().items(Collections.singletonList(string())).toDocument()) - .isEqualTo(new Document("type", "array").append("items", new Document("type", "string"))); - } - - @Test // DATAMONGO-1835 - void arrayObjectShouldRenderMaxItemsCorrectly() { - - assertThat(array().maxItems(5).generatedDescription().toDocument()).isEqualTo(new Document("type", "array") - .append("description", "Must be an array having size unbounded-5].").append("maxItems", 5)); - } - - @Test // DATAMONGO-1835 - void arrayObjectShouldRenderMinItemsCorrectly() { - - assertThat(array().minItems(5).generatedDescription().toDocument()).isEqualTo(new Document("type", "array") - .append("description", "Must be an array having size [5-unbounded.").append("minItems", 5)); - } - - @Test // DATAMONGO-1835 - void arrayObjectShouldRenderUniqueItemsCorrectly() { - - assertThat(array().uniqueItems(true).generatedDescription().toDocument()).isEqualTo(new Document("type", "array") - .append("description", "Must be an array of unique values.").append("uniqueItems", true)); - } - - @Test // DATAMONGO-1835 - void arrayObjectShouldRenderAdditionalItemsItemsCorrectly() { - - assertThat(array().additionalItems(true).generatedDescription().toDocument()) - .isEqualTo(new Document("type", "array").append("description", "Must be an array with additional items.") - .append("additionalItems", true)); - assertThat(array().additionalItems(false).generatedDescription().toDocument()) - .isEqualTo(new Document("type", "array").append("description", "Must be an array with no additional items.") - .append("additionalItems", false)); - } - - // ----------------- - // type : 'boolean' - // ----------------- - - @Test // DATAMONGO-1835 - void booleanShouldRenderCorrectly() { - - assertThat(bool().generatedDescription().toDocument()) - .isEqualTo(new Document("type", "boolean").append("description", "Must be a boolean.")); - } - - // ----------------- - // type : 'null' - // ----------------- - - @Test // DATAMONGO-1835 - void nullShouldRenderCorrectly() { - - assertThat(nil().generatedDescription().toDocument()) - .isEqualTo(new Document("type", "null").append("description", "Must be null.")); - } - - // ----------------- - // type : 'date' - // ----------------- - - @Test // DATAMONGO-1877 - void dateShouldRenderCorrectly() { - - assertThat(date().generatedDescription().toDocument()) - .isEqualTo(new Document("bsonType", "date").append("description", "Must be a date.")); - } - - // ----------------- - // type : 'timestamp' - // ----------------- - - @Test // DATAMONGO-1877 - void timestampShouldRenderCorrectly() { - - assertThat(timestamp().generatedDescription().toDocument()) - .isEqualTo(new Document("bsonType", "timestamp").append("description", "Must be a timestamp.")); - } - - // ----------------- - // type : 'any' - // ----------------- - - @Test // DATAMONGO-1835 - void typedObjectShouldRenderEnumCorrectly() { - - assertThat(of(String.class).possibleValues(Arrays.asList("one", "two")).toDocument()) - .isEqualTo(new Document("type", "string").append("enum", Arrays.asList("one", "two"))); - } - - @Test // DATAMONGO-1835 - void typedObjectShouldRenderAllOfCorrectly() { - - assertThat(of(Object.class).allOf(Arrays.asList(string())).toDocument()) - .isEqualTo(new Document("type", "object").append("allOf", Arrays.asList(new Document("type", "string")))); - } - - @Test // DATAMONGO-1835 - void typedObjectShouldRenderAnyOfCorrectly() { - - assertThat(of(String.class).anyOf(Arrays.asList(string())).toDocument()) - .isEqualTo(new Document("type", "string").append("anyOf", Arrays.asList(new Document("type", "string")))); - } - - @Test // DATAMONGO-1835 - void typedObjectShouldRenderOneOfCorrectly() { - - assertThat(of(String.class).oneOf(Arrays.asList(string())).toDocument()) - .isEqualTo(new Document("type", "string").append("oneOf", Arrays.asList(new Document("type", "string")))); - } - - @Test // DATAMONGO-1835 - void typedObjectShouldRenderNotCorrectly() { - - assertThat(untyped().notMatch(string()).toDocument()) - .isEqualTo(new Document("not", new Document("type", "string"))); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/JsonSchemaPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/JsonSchemaPropertyUnitTests.java deleted file mode 100644 index 2470b89f5d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/JsonSchemaPropertyUnitTests.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2018-2021 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.schema; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type; - -/** - * Unit tests for {@link JsonSchemaProperty}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -public class JsonSchemaPropertyUnitTests { - - @Test // DATAMONGO-1835 - public void shouldRenderInt32Correctly() { - assertThat(JsonSchemaProperty.int32("foo").toDocument()).containsEntry("foo.bsonType", "int"); - } - - @Test // DATAMONGO-1835 - public void shouldRenderInt64Correctly() { - assertThat(JsonSchemaProperty.int64("foo").toDocument()).containsEntry("foo.bsonType", "long"); - } - - @Test // DATAMONGO-1835 - public void shouldRenderDecimal128Correctly() { - assertThat(JsonSchemaProperty.decimal128("foo").toDocument()).containsEntry("foo.bsonType", "decimal"); - } - - @Test // DATAMONGO-1835 - public void shouldRenderNullCorrectly() { - assertThat(JsonSchemaProperty.nil("foo").toDocument()).containsEntry("foo.type", "null"); - } - - @Test // DATAMONGO-1835 - public void shouldRenderUntypedCorrectly() { - assertThat(JsonSchemaProperty.named("foo").ofType(Type.binaryType()).toDocument()).containsEntry("foo.bsonType", - "binData"); - } - - @Test // DATAMONGO-1877 - public void shouldRenderDateCorrectly() { - assertThat(JsonSchemaProperty.date("foo").toDocument()).containsEntry("foo.bsonType", "date"); - } - - @Test // DATAMONGO-1877 - public void shouldRenderTimestampCorrectly() { - assertThat(JsonSchemaProperty.timestamp("foo").toDocument()).containsEntry("foo.bsonType", "timestamp"); - } - - @Test // DATAMONGO-2282 - public void objectIdShouldBeRenderedCorrectly() { - assertThat(JsonSchemaProperty.objectId("_id").toDocument()).containsEntry("_id.bsonType", "objectId"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java deleted file mode 100644 index 5b83b36459..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2018-2021 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.schema; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import lombok.Data; - -import java.util.Collections; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.CollectionOptions; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.validation.Validator; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.model.CreateCollectionOptions; -import com.mongodb.client.model.ValidationAction; -import com.mongodb.client.model.ValidationLevel; -import com.mongodb.client.model.ValidationOptions; - -/** - * Integration tests for {@link MongoJsonSchema}. - * - * @author Christoph Strobl - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@ContextConfiguration -public class MongoJsonSchemaTests { - - static @Client MongoClient mongoClient; - - @Configuration - static class Config extends AbstractMongoClientConfiguration { - - @Override - public MongoClient mongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return "json-schema-tests"; - } - - - } - - @Autowired MongoTemplate template; - - @BeforeEach - public void setUp() { - - template.dropCollection(Person.class); - } - - @Test // DATAMONGO-1835 - public void writeSchemaViaTemplate() { - - MongoJsonSchema schema = MongoJsonSchema.builder() // - .required("firstname", "lastname") // - .properties( // - JsonSchemaProperty.string("firstname").possibleValues("luke", "han").maxLength(10), // - JsonSchemaProperty.object("address") // - .properties(JsonSchemaProperty.string("postCode").minLength(4).maxLength(5)) - - ).build(); - - template.createCollection(Person.class, CollectionOptions.empty().schema(schema)); - - Document $jsonSchema = new MongoJsonSchemaMapper(template.getConverter()).mapSchema(schema.toDocument(), - Person.class); - - Document fromDb = readSchemaFromDatabase("persons"); - assertThat(fromDb).isEqualTo($jsonSchema); - } - - @Test // DATAMONGO-1835 - public void writeSchemaInDocumentValidatorCorrectly() { - - Document unmappedSchema = new Document("$jsonSchema", - new Document("type", "object").append("required", Collections.singletonList("firstname"))); - - Document mappedSchema = new Document("$jsonSchema", - new Document("type", "object").append("required", Collections.singletonList("first_name"))); - - template.createCollection(Person.class, CollectionOptions.empty().validator(Validator.document(unmappedSchema))); - - assertThat(readSchemaFromDatabase("persons")).isEqualTo(mappedSchema); - } - - @Test // DATAMONGO-1835 - public void nonMappedSchema() { - - MongoJsonSchema schema = MongoJsonSchema.builder() // - .required("firstname", "lastname") // - .properties( // - JsonSchemaProperty.string("firstname").possibleValues("luke", "han").maxLength(10), // - JsonSchemaProperty.object("address") // - .properties(JsonSchemaProperty.string("postCode").minLength(4).maxLength(5)) - - ).build(); - - template.createCollection("persons", CollectionOptions.empty().schema(schema)); - - Document fromDb = readSchemaFromDatabase("persons"); - assertThat(fromDb) - .isNotEqualTo(new MongoJsonSchemaMapper(template.getConverter()).mapSchema(schema.toDocument(), Person.class)); - } - - @Test // DATAMONGO-1835 - public void writeSchemaManually() { - - MongoJsonSchema schema = MongoJsonSchema.builder() // - .required("firstname", "lastname") // - .properties( // - JsonSchemaProperty.string("firstname").possibleValues("luke", "han").maxLength(10), // - JsonSchemaProperty.object("address") // - .properties(JsonSchemaProperty.string("postCode").minLength(4).maxLength(5)) - - ).build(); - - Document $jsonSchema = new MongoJsonSchemaMapper(template.getConverter()).mapSchema(schema.toDocument(), - Person.class); - - ValidationOptions options = new ValidationOptions(); - options.validationLevel(ValidationLevel.MODERATE); - options.validationAction(ValidationAction.ERROR); - options.validator($jsonSchema); - - CreateCollectionOptions cco = new CreateCollectionOptions(); - cco.validationOptions(options); - - MongoDatabase db = template.getDb(); - db.createCollection("persons", cco); - - Document fromDb = readSchemaFromDatabase("persons"); - assertThat(fromDb).isEqualTo($jsonSchema); - } - - Document readSchemaFromDatabase(String collectionName) { - - Document collectionInfo = template - .executeCommand(new Document("listCollections", 1).append("filter", new Document("name", collectionName))); - - if (collectionInfo.containsKey("cursor")) { - collectionInfo = (Document) collectionInfo.get("cursor", Document.class).get("firstBatch", List.class).iterator() - .next(); - } - - if (!collectionInfo.containsKey("options")) { - return new Document(); - } - - return collectionInfo.get("options", Document.class).get("validator", Document.class); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = "persons") - static class Person { - - @Field("first_name") String firstname; - String lastname; - Address address; - - } - - static class Address { - - String city; - String street; - - @Field("post_code") String postCode; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaUnitTests.java deleted file mode 100644 index 0f8cc40c02..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaUnitTests.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2018-2021 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.schema; - -import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.UUID; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -/** - * Unit tests for {@link MongoJsonSchema}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -class MongoJsonSchemaUnitTests { - - @Test // DATAMONGO-1835 - void toDocumentRendersSchemaCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder() // - .required("firstname", "lastname") // - .build(); - - assertThat(schema.toDocument()).isEqualTo(new Document("$jsonSchema", - new Document("type", "object").append("required", Arrays.asList("firstname", "lastname")))); - } - - @Test // DATAMONGO-1835 - void rendersDocumentBasedSchemaCorrectly() { - - Document document = MongoJsonSchema.builder() // - .required("firstname", "lastname") // - .build().toDocument(); - - MongoJsonSchema jsonSchema = MongoJsonSchema.of(document.get("$jsonSchema", Document.class)); - - assertThat(jsonSchema.toDocument()).isEqualTo(new Document("$jsonSchema", - new Document("type", "object").append("required", Arrays.asList("firstname", "lastname")))); - } - - @Test // DATAMONGO-1849 - void rendersRequiredPropertiesCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder() // - .required("firstname") // - .properties( // - JsonSchemaProperty.required(JsonSchemaProperty.string("lastname")) // - ).build(); - - assertThat(schema.toDocument()).isEqualTo(new Document("$jsonSchema", - new Document("type", "object").append("required", Arrays.asList("firstname", "lastname")).append("properties", - new Document("lastname", new Document("type", "string"))))); - } - - @Test // DATAMONGO-2306 - void rendersEncryptedPropertyCorrectly() { - - MongoJsonSchema schema = MongoJsonSchema.builder().properties( // - encrypted(string("ssn")) // - .aead_aes_256_cbc_hmac_sha_512_deterministic() // - .keyId("*key0_id") // - ).build(); - - assertThat(schema.toDocument()).isEqualTo(new Document("$jsonSchema", - new Document("type", "object").append("properties", - new Document("ssn", new Document("encrypt", new Document("keyId", "*key0_id") - .append("algorithm", "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").append("bsonType", "string")))))); - } - - @Test // DATAMONGO-2306 - void rendersEncryptedPropertyWithKeyIdCorrectly() { - - UUID uuid = UUID.randomUUID(); - MongoJsonSchema schema = MongoJsonSchema.builder().properties( // - encrypted(string("ssn")) // - .aead_aes_256_cbc_hmac_sha_512_deterministic() // - .keys(uuid) // - ).build(); - - assertThat(schema.toDocument()).isEqualTo(new Document("$jsonSchema", - new Document("type", "object").append("properties", - new Document("ssn", new Document("encrypt", new Document("keyId", Collections.singletonList(uuid)) - .append("algorithm", "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").append("bsonType", "string")))))); - } - - @Test // DATAMONGO-1835 - void throwsExceptionOnNullRoot() { - assertThatIllegalArgumentException().isThrownBy(() -> MongoJsonSchema.of((JsonSchemaObject) null)); - } - - @Test // DATAMONGO-1835 - void throwsExceptionOnNullDocument() { - assertThatIllegalArgumentException().isThrownBy(() -> MongoJsonSchema.of((Document) null)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/ReactiveMongoJsonSchemaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/ReactiveMongoJsonSchemaTests.java deleted file mode 100644 index 878f65232d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/ReactiveMongoJsonSchemaTests.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2018-2021 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.schema; - -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import lombok.Data; -import reactor.test.StepVerifier; - -import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.DataRetrievalFailureException; -import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; -import org.springframework.data.mongodb.core.CollectionOptions; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Integration tests for {@link MongoJsonSchema} using reactive infrastructure. - * - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@ContextConfiguration -public class ReactiveMongoJsonSchemaTests { - - static @Client MongoClient mongoClient; - - @Configuration - static class Config extends AbstractReactiveMongoConfiguration { - - @Override - public MongoClient reactiveMongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return "json-schema-tests"; - } - - @Override - protected Set> getInitialEntitySet() { - return Collections.emptySet(); - } - } - - @Autowired ReactiveMongoTemplate template; - - @BeforeEach - public void setUp() { - template.dropCollection(Person.class).as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1835 - public void writeSchemaViaTemplate() { - - MongoJsonSchema schema = MongoJsonSchema.builder() // - .required("firstname", "lastname") // - .properties( // - JsonSchemaProperty.string("firstname").possibleValues("luke", "han").maxLength(10), // - JsonSchemaProperty.object("address") // - .properties(JsonSchemaProperty.string("postCode").minLength(4).maxLength(5)) - - ).build(); - - template.createCollection(Person.class, CollectionOptions.empty().schema(schema)).as(StepVerifier::create) - .expectNextCount(1).verifyComplete(); - - Document $jsonSchema = new MongoJsonSchemaMapper(template.getConverter()).mapSchema(schema.toDocument(), - Person.class); - - Document fromDb = readSchemaFromDatabase("persons"); - assertThat(fromDb).isEqualTo($jsonSchema); - } - - Document readSchemaFromDatabase(String collectionName) { - - Document collectionInfo = template - .executeCommand(new Document("listCollections", 1).append("filter", new Document("name", collectionName))) - .block(Duration.ofSeconds(5)); - - if (collectionInfo == null) { - throw new DataRetrievalFailureException(String.format("Collection %s was not found.", collectionName)); - } - - if (collectionInfo.containsKey("cursor")) { - collectionInfo = (Document) collectionInfo.get("cursor", Document.class).get("firstBatch", List.class).iterator() - .next(); - } - - if (!collectionInfo.containsKey("options")) { - return new Document(); - } - - return collectionInfo.get("options", Document.class).get("validator", Document.class); - } - - @Data - @org.springframework.data.mongodb.core.mapping.Document(collection = "persons") - static class Person { - - @Field("first_name") String firstname; - String lastname; - Address address; - - } - - static class Address { - - String city; - String street; - - @Field("post_code") String postCode; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/script/ExecutableMongoScriptUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/script/ExecutableMongoScriptUnitTests.java deleted file mode 100644 index 1e9feee22e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/script/ExecutableMongoScriptUnitTests.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2014-2021 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.script; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -/** - * @author Christoph Strobl - */ -class ExecutableMongoScriptUnitTests { - - @Test // DATAMONGO-479 - void constructorShouldThrowExceptionWhenRawScriptIsNull() { - assertThatIllegalArgumentException().isThrownBy(() -> new ExecutableMongoScript(null)) - .withMessageContaining("must not be").withMessageContaining("null"); - } - - @Test // DATAMONGO-479 - void constructorShouldThrowExceptionWhenRawScriptIsEmpty() { - assertThatIllegalArgumentException().isThrownBy(() -> new ExecutableMongoScript("")) - .withMessageContaining("must not be").withMessageContaining("empty"); - } - - @Test // DATAMONGO-479 - void getCodeShouldReturnCodeRepresentationOfRawScript() { - - String jsFunction = "function(x) { return x; }"; - - ExecutableMongoScript script = new ExecutableMongoScript(jsFunction); - - assertThat(script.getCode()).isNotNull().hasToString(jsFunction); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/script/NamedMongoScriptUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/script/NamedMongoScriptUnitTests.java deleted file mode 100644 index c5b7e57cce..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/script/NamedMongoScriptUnitTests.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014-2021 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.script; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link NamedMongoScript}. - * - * @author Christoph Strobl - * @author Oliver Gierke - * @since 1.7 - */ -public class NamedMongoScriptUnitTests { - - @Test // DATAMONGO-479 - public void shouldThrowExceptionWhenScriptNameIsNull() { - assertThatIllegalArgumentException().isThrownBy(() -> new NamedMongoScript(null, "return 1;")); - } - - @Test // DATAMONGO-479 - public void shouldThrowExceptionWhenScriptNameIsEmptyString() { - assertThatIllegalArgumentException().isThrownBy(() -> new NamedMongoScript("", "return 1")); - } - - @Test // DATAMONGO-479 - public void shouldThrowExceptionWhenRawScriptIsEmptyString() { - assertThatIllegalArgumentException().isThrownBy(() -> new NamedMongoScript("foo", "")); - } - - @Test // DATAMONGO-479 - public void getCodeShouldReturnCodeRepresentationOfRawScript() { - - String jsFunction = "function(x) { return x; }"; - - assertThat(new NamedMongoScript("echo", jsFunction).getCode()).isEqualTo(jsFunction); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/spel/ExpressionNodeUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/spel/ExpressionNodeUnitTests.java deleted file mode 100644 index 2b681e4f75..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/spel/ExpressionNodeUnitTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2013-2021 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.spel; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collection; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.expression.spel.ExpressionState; -import org.springframework.expression.spel.SpelNode; -import org.springframework.expression.spel.ast.OpDivide; -import org.springframework.expression.spel.ast.OpMinus; -import org.springframework.expression.spel.ast.OpMultiply; -import org.springframework.expression.spel.ast.OpPlus; - -/** - * Unit tests for {@link ExpressionNode}. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -class ExpressionNodeUnitTests { - - @Mock ExpressionState state; - - @Mock OpMinus minus; - @Mock OpPlus plus; - @Mock OpDivide divide; - @Mock OpMultiply multiply; - - private Collection operators; - - @BeforeEach - void setUp() { - this.operators = Arrays.asList(minus, plus, divide, multiply); - } - - @Test // DATAMONGO-774 - void createsOperatorNodeForOperations() { - - for (SpelNode operator : operators) { - assertThat(ExpressionNode.from(operator, state)).isInstanceOf(OperatorNode.class); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/validation/CriteriaValidatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/validation/CriteriaValidatorUnitTests.java deleted file mode 100644 index bcb9117fb7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/validation/CriteriaValidatorUnitTests.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017-2021 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.validation; - -import static org.assertj.core.api.Assertions.*; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.core.query.Criteria; - -/** - * Unit tests for {@link CriteriaValidator}. - * - * @author Andreas Zink - * @author Christoph Strobl - */ -public class CriteriaValidatorUnitTests { - - @Test // DATAMONGO-1322 - public void testSimpleCriteria() { - - Criteria criteria = Criteria.where("nonNullString").ne(null).type(2).and("rangedInteger").type(16).gte(0).lte(122); - Document validator = CriteriaValidator.of(criteria).toDocument(); - - assertThat(validator.get("nonNullString")).isEqualTo(new Document("$ne", null).append("$type", 2)); - assertThat(validator.get("rangedInteger")) - .isEqualTo(new Document("$type", 16).append("$gte", 0).append("$lte", 122)); - } - - @Test // DATAMONGO-1322 - public void testFailOnNull() { - assertThatIllegalArgumentException().isThrownBy(() -> CriteriaValidator.of(null)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/AntPathUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/AntPathUnitTests.java deleted file mode 100644 index f8141f5ac8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/AntPathUnitTests.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2011-2021 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.gridfs; - -import static org.assertj.core.api.Assertions.*; - -import java.util.regex.Pattern; - -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link AntPath}. - * - * @author Oliver Gierke - */ -public class AntPathUnitTests { - - @Test - public void buildRegexCorrectly() { - - AntPath path = new AntPath("**/foo/*-bar.xml"); - String regex = path.toRegex(); - - assertThat(Pattern.matches(regex, "foo/bar/foo/foo-bar.xml")).isTrue(); - assertThat(Pattern.matches(regex, "foo/bar/foo/bar/foo-bar.xml")).isFalse(); - assertThat(regex).isEqualTo(".*\\Q/foo/\\E[^/]*\\Q-bar.xml\\E"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsResourceUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsResourceUnitTests.java deleted file mode 100644 index 052b780a0c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsResourceUnitTests.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2018-2021 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.gridfs; - -import static org.assertj.core.api.Assertions.*; - -import java.io.FileNotFoundException; -import java.util.Date; - -import org.bson.BsonObjectId; -import org.bson.Document; -import org.junit.jupiter.api.Test; - -import com.mongodb.MongoGridFSException; -import com.mongodb.client.gridfs.model.GridFSFile; - -/** - * Unit tests for {@link GridFsResource}. - * - * @author Mark Paluch - * @auhtor Christoph Strobl - */ -public class GridFsResourceUnitTests { - - @Test // DATAMONGO-1850 - public void shouldReadContentTypeCorrectly() { - - Document metadata = new Document(GridFsResource.CONTENT_TYPE_FIELD, "text/plain"); - GridFSFile file = new GridFSFile(new BsonObjectId(), "foo", 0, 0, new Date(), metadata); - GridFsResource resource = new GridFsResource(file); - - assertThat(resource.getContentType()).isEqualTo("text/plain"); - } - - @Test // DATAMONGO-2240 - public void shouldReturnGridFSFile() { - - GridFSFile file = new GridFSFile(new BsonObjectId(), "foo", 0, 0, new Date(), new Document()); - GridFsResource resource = new GridFsResource(file); - - assertThat(resource.getGridFSFile()).isSameAs(file); - } - - @Test // DATAMONGO-1850 - public void shouldThrowExceptionOnEmptyContentType() { - - GridFSFile file = new GridFSFile(new BsonObjectId(), "foo", 0, 0, new Date(), null); - GridFsResource resource = new GridFsResource(file); - - assertThatThrownBy(resource::getContentType).isInstanceOf(MongoGridFSException.class); - } - - @Test // DATAMONGO-1850 - public void shouldThrowExceptionOnEmptyContentTypeInMetadata() { - - GridFSFile file = new GridFSFile(new BsonObjectId(), "foo", 0, 0, new Date(), new Document()); - GridFsResource resource = new GridFsResource(file); - - assertThatThrownBy(resource::getContentType).isInstanceOf(MongoGridFSException.class); - } - - @Test // DATAMONGO-1914 - public void gettersThrowExceptionForAbsentResource() { - - GridFsResource absent = GridFsResource.absent("foo"); - - assertThat(absent.exists()).isFalse(); - assertThat(absent.getDescription()).contains("GridFs resource [foo]"); - - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(absent::getContentType); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(absent::getId); - - assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(absent::contentLength); - assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(absent::getInputStream); - assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(absent::lastModified); - assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(absent::getURI); - assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(absent::getURL); - } - - @Test // DATAMONGO-1914 - public void shouldReturnFilenameForAbsentResource() { - - GridFsResource absent = GridFsResource.absent("foo"); - - assertThat(absent.exists()).isFalse(); - assertThat(absent.getDescription()).contains("GridFs resource [foo]"); - assertThat(absent.getFilename()).isEqualTo("foo"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateIntegrationTests.java deleted file mode 100644 index 5fc10483c0..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateIntegrationTests.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2011-2021 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.gridfs; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.gridfs.GridFsCriteria.*; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Stream; - -import org.bson.BsonObjectId; -import org.bson.BsonString; -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.StreamUtils; - -import com.mongodb.MongoGridFSException; -import com.mongodb.client.gridfs.GridFSFindIterable; -import com.mongodb.client.gridfs.model.GridFSFile; - -/** - * Integration tests for {@link GridFsTemplate}. - * - * @author Oliver Gierke - * @author Philipp Schneider - * @author Thomas Darimont - * @author Martin Baumgartner - * @author Hartmut Lang - * @author Mark Paluch - * @author Denis Zavedeev - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:gridfs/gridfs.xml") -public class GridFsTemplateIntegrationTests { - - Resource resource = new ClassPathResource("gridfs/gridfs.xml"); - - @Autowired GridFsOperations operations; - @Autowired SimpleMongoClientDatabaseFactory mongoClient; - - @Before - public void setUp() { - operations.delete(new Query()); - } - - @Test // DATAMONGO-6 - public void storesAndFindsSimpleDocument() throws IOException { - - ObjectId reference = operations.store(resource.getInputStream(), "foo.xml"); - - List files = new ArrayList<>(); - GridFSFindIterable result = operations.find(query(where("_id").is(reference))); - result.into(files); - assertThat(files).hasSize(1); - assertThat(((BsonObjectId) files.get(0).getId()).getValue()).isEqualTo(reference); - } - - // @Test // DATAMONGO-2392 - // public void storesAndFindsByUUID() throws IOException { - // - // UUID uuid = UUID.randomUUID(); - // - // GridFSFile fs = new GridFSFile(new BsonObjectId(new ObjectId(uuid.to)) - // GridFSInputFile in = fs.createFile(resource.getInputStream(), "gridfs.xml"); - // - // in.put("_id", uuid); - // in.put("contentType", "application/octet-stream"); - // in.save(); - // - // GridFSFile file = operations.findOne(query(where("_id").is(uuid))); - // GridFsResource resource = operations.getResource(file); - // - // assertThat(resource.exists()).isTrue(); - // } - - @Test // DATAMONGO-6 - public void writesMetadataCorrectly() throws IOException { - - Document metadata = new Document("key", "value"); - ObjectId reference = operations.store(resource.getInputStream(), "foo.xml", metadata); - - List files = new ArrayList<>(); - GridFSFindIterable result = operations.find(query(whereMetaData("key").is("value"))); - result.into(files); - - assertThat(files).hasSize(1); - assertThat(((BsonObjectId) files.get(0).getId()).getValue()).isEqualTo(reference); - } - - @Test // DATAMONGO-6 - public void marshalsComplexMetadata() throws IOException { - - Metadata metadata = new Metadata(); - metadata.version = "1.0"; - - ObjectId reference = operations.store(resource.getInputStream(), "foo.xml", metadata); - - List files = new ArrayList<>(); - GridFSFindIterable result = operations.find(query(whereFilename().is("foo.xml"))); - result.into(files); - - assertThat(files).hasSize(1); - assertThat(((BsonObjectId) files.get(0).getId()).getValue()).isEqualTo(reference); - } - - @Test // DATAMONGO-6 - public void findsFilesByResourcePattern() throws IOException { - - ObjectId reference = operations.store(resource.getInputStream(), "foo.xml"); - - GridFsResource[] resources = operations.getResources("*.xml"); - - assertThat(resources).hasSize(1); - assertThat(((BsonObjectId) resources[0].getId()).getValue()).isEqualTo(reference); - assertThat(resources[0].contentLength()).isEqualTo(resource.contentLength()); - } - - @Test // DATAMONGO-6 - public void findsFilesByResourceLocation() throws IOException { - - ObjectId reference = operations.store(resource.getInputStream(), "foo.xml"); - - GridFsResource[] resources = operations.getResources("foo.xml"); - assertThat(resources).hasSize(1); - assertThat(((BsonObjectId) resources[0].getId()).getValue()).isEqualTo(reference); - assertThat(resources[0].contentLength()).isEqualTo(resource.contentLength()); - } - - @Test // DATAMONGO-503 - public void storesContentType() throws IOException { - - ObjectId reference = operations.store(resource.getInputStream(), "foo2.xml", "application/xml"); - - List files = new ArrayList<>(); - GridFSFindIterable result = operations.find(query(whereContentType().is("application/xml"))); - result.into(files); - - assertThat(files).hasSize(1); - assertThat(((BsonObjectId) files.get(0).getId()).getValue()).isEqualTo(reference); - } - - @Test // DATAMONGO-534 - public void considersSortWhenQueryingFiles() throws IOException { - - ObjectId second = operations.store(resource.getInputStream(), "foo.xml"); - ObjectId third = operations.store(resource.getInputStream(), "foobar.xml"); - ObjectId first = operations.store(resource.getInputStream(), "bar.xml"); - - Query query = new Query().with(Sort.by(Direction.ASC, "filename")); - - List files = new ArrayList<>(); - GridFSFindIterable result = operations.find(query); - result.into(files); - - assertThat(files).hasSize(3).extracting(it -> ((BsonObjectId) it.getId()).getValue()).containsExactly(first, second, - third); - } - - @Test // DATAMONGO-534, DATAMONGO-1762 - public void queryingWithEmptyQueryReturnsAllFiles() throws IOException { - - ObjectId reference = operations.store(resource.getInputStream(), "foo.xml"); - - List files = new ArrayList<>(); - GridFSFindIterable result = operations.find(new Query()); - result.into(files); - - assertThat(files).hasSize(1).extracting(it -> ((BsonObjectId) it.getId()).getValue()).containsExactly(reference); - } - - @Test // DATAMONGO-1762 - public void queryingWithNullQueryThrowsException() { - assertThatIllegalArgumentException().isThrownBy(() -> operations.find(null)); - } - - @Test // DATAMONGO-813, DATAMONGO-1914 - public void getResourceShouldReturnAbsentResourceForNonExistingResource() { - assertThat(operations.getResource("doesnotexist")).isEqualTo(GridFsResource.absent("doesnotexist")); - } - - @Test // DATAMONGO-809 - public void storesAndFindsSimpleDocumentWithMetadataDocument() throws IOException { - - Document metadata = new Document("key", "value"); - ObjectId reference = operations.store(resource.getInputStream(), "foobar", metadata); - - List files = new ArrayList<>(); - GridFSFindIterable result = operations.find(query(whereMetaData("key").is("value"))); - result.into(files); - - assertThat(files).hasSize(1).extracting(it -> ((BsonObjectId) it.getId()).getValue()).containsExactly(reference); - } - - @Test // DATAMONGO-809 - public void storesAndFindsSimpleDocumentWithMetadataObject() throws IOException { - - Metadata metadata = new Metadata(); - metadata.version = "1.0"; - ObjectId reference = operations.store(resource.getInputStream(), "foobar", metadata); - - List files = new ArrayList<>(); - GridFSFindIterable result = operations.find(query(whereMetaData("version").is("1.0"))); - result.into(files); - - assertThat(files).hasSize(1).extracting(it -> ((BsonObjectId) it.getId()).getValue()).containsExactly(reference); - } - - @Test // DATAMONGO-1695 - public void readsContentTypeCorrectly() throws IOException { - - operations.store(resource.getInputStream(), "someName", "contentType"); - - assertThat(operations.getResource("someName").getContentType()).isEqualTo("contentType"); - } - - @Test // DATAMONGO-1850 - public void failsOnNonExistingContentTypeRetrieval() throws IOException { - - operations.store(resource.getInputStream(), "no-content-type", (String) null); - GridFsResource result = operations.getResource("no-content-type"); - - assertThatThrownBy(result::getContentType).isInstanceOf(MongoGridFSException.class); - } - - @Test // DATAMONGO-1813 - public void convertFileToResource() throws IOException { - - Document metadata = new Document("key", "value"); - ObjectId reference = operations.store(resource.getInputStream(), "foobar", metadata); - - GridFSFile file = operations.findOne(query(whereMetaData("key").is("value"))); - GridFsResource result = operations.getResource(file); - - assertThat(result.contentLength()).isEqualTo(resource.contentLength()); - assertThat(((BsonObjectId) result.getId()).getValue()).isEqualTo(reference); - } - - @Test // DATAMONGO-2021 - public void getResourceShouldRetrieveContentByIdentity() throws IOException { - - ClassPathResource secondResource = new ClassPathResource("gridfs/another-resource.xml"); - - ObjectId reference1 = operations.store(resource.getInputStream(), "foo.xml"); - ObjectId reference2 = operations.store(secondResource.getInputStream(), "foo.xml"); - - Map fixture = new LinkedHashMap<>(); - fixture.put(reference1, resource); - fixture.put(reference2, secondResource); - - for (Entry entry : fixture.entrySet()) { - - GridFsResource fsFile = operations.getResource(operations.findOne(query(where("_id").is(entry.getKey())))); - byte[] content = StreamUtils.copyToByteArray(fsFile.getInputStream()); - - assertThat(content).isEqualTo(StreamUtils.copyToByteArray(entry.getValue().getInputStream())); - } - } - - @Test // DATAMONGO-625 - public void storeSavesGridFsUploadWithGivenIdCorrectly() throws IOException { - - String id = "id-1"; - - GridFsUpload upload = GridFsUpload.fromStream(resource.getInputStream()) // - .id(id) // - .filename("gridFsUpload.xml") // - .contentType("xml") // - .build(); - - assertThat(operations.store(upload)).isEqualTo(id); - - GridFsResource fsFile = operations.getResource(operations.findOne(query(where("_id").is(id)))); - byte[] content = StreamUtils.copyToByteArray(fsFile.getInputStream()); - - assertThat(content).isEqualTo(StreamUtils.copyToByteArray(resource.getInputStream())); - assertThat(fsFile.getFilename()).isEqualTo("gridFsUpload.xml"); - assertThat(fsFile.getId()).isEqualTo(new BsonString(id)); - assertThat(fsFile.getFileId()).isEqualTo(id); - assertThat(fsFile.getContentType()).isEqualTo("xml"); - } - - @Test // DATAMONGO-765 - public void considersSkipLimitWhenQueryingFiles() { - - Stream.of("a", "aa", "aaa", // - "b", "bb", "bb", // - "c", "cc", "ccc", // - "d", "dd", "ddd") // - .forEach(filename -> operations.store(new ByteArrayInputStream(new byte[0]), filename)); - - PageRequest pageRequest = PageRequest.of(2, 3, Direction.ASC, "filename"); - List filenames = operations.find(new Query().with(pageRequest)) // - .map(GridFSFile::getFilename) // - .into(new ArrayList<>()); - - assertThat(filenames).containsExactly("c", "cc", "ccc"); - } - - class Metadata { - String version; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateUnitTests.java deleted file mode 100644 index 38d5d45588..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/GridFsTemplateUnitTests.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2020-2021 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.gridfs; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.MongoConverter; - -/** - * @author Christoph Strobl - */ -class GridFsTemplateUnitTests { - - private GridFsTemplateStub template; - - @BeforeEach - void beforeEach() { - template = new GridFsTemplateStub(); - } - - @Test // DATAMONGO-2574 - void contentMetadataDoesNotOverrideContentTypeIfSet() { - - template.onStoreReturn(new ObjectId()); - template.store(new ByteArrayInputStream(new byte[] {}), "filename", "json", new Document("meta", "data")); - - assertThat(template.capturedUpload().getOptions().getContentType()).isEqualTo("json"); - assertThat(template.capturedUpload().getOptions().getMetadata()).containsEntry("meta", "data"); - } - - private static class GridFsTemplateStub extends GridFsTemplate { - - private Object onStoreResult; - private GridFsObject capturedUpload; - - GridFsTemplateStub() { - super(mock(MongoDatabaseFactory.class), mock(MongoConverter.class)); - } - - @Override - public T store(GridFsObject upload) { - - this.capturedUpload = upload; - return (T) onStoreResult; - } - - GridFsTemplateStub onStoreReturn(Object result) { - - this.onStoreResult = result; - return this; - } - - GridFsObject capturedUpload() { - return capturedUpload; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResourceUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResourceUnitTests.java deleted file mode 100644 index f6e0573df5..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResourceUnitTests.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2020-2021 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.gridfs; - -import static org.assertj.core.api.Assertions.*; - -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.nio.ByteBuffer; - -import org.junit.jupiter.api.Test; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import com.mongodb.client.gridfs.model.GridFSFile; -import com.mongodb.reactivestreams.client.gridfs.GridFSDownloadPublisher; - -/** - * Unit tests for {@link ReactiveGridFsResource}. - * - * @author Christoph Strobl - */ -class ReactiveGridFsResourceUnitTests { - - @Test // DATAMONGO-2427 - void streamCanOnlyBeConsumedOnce() { - - ReactiveGridFsResource resource = new ReactiveGridFsResource("file.name", new StubGridFSDownloadPublisher()); - - assertThat(resource.exists()).isTrue(); - - resource.getInputStream().as(StepVerifier::create).verifyComplete(); - resource.getInputStream().as(StepVerifier::create).verifyError(IllegalStateException.class); - resource.getDownloadStream().as(StepVerifier::create).verifyError(IllegalStateException.class); - } - - @Test // DATAMONGO-2427 - void existReturnsFalseForNullPublisher() { - - ReactiveGridFsResource resource = new ReactiveGridFsResource("file.name", null); - - assertThat(resource.exists()).isFalse(); - } - - @Test // DATAMONGO-2427 - void nonExistingResourceProducesEmptyDownloadStream() { - - ReactiveGridFsResource resource = new ReactiveGridFsResource("file.name", null); - - resource.getInputStream().as(StepVerifier::create).verifyComplete(); - resource.getInputStream().as(StepVerifier::create).verifyComplete(); - resource.getDownloadStream().as(StepVerifier::create).verifyComplete(); - } - - private static class StubGridFSDownloadPublisher implements GridFSDownloadPublisher { - - @Override - public Publisher getGridFSFile() { - return Mono.empty(); - } - - @Override - public GridFSDownloadPublisher bufferSizeBytes(int bufferSizeBytes) { - return null; - } - - @Override - public void subscribe(Subscriber s) { - - s.onSubscribe(new Subscription() { - @Override - public void request(long n) { - s.onComplete(); - } - - @Override - public void cancel() { - - } - }); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateTests.java deleted file mode 100644 index c34e4a4d4a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateTests.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2019-2021 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.gridfs; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.gridfs.GridFsCriteria.*; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.ByteBuffer; - -import org.bson.BsonObjectId; -import org.bson.BsonString; -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferFactory; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.core.io.buffer.DefaultDataBuffer; -import org.springframework.core.io.buffer.DefaultDataBufferFactory; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.FileCopyUtils; -import org.springframework.util.StreamUtils; - -import com.mongodb.client.gridfs.model.GridFSFile; -import com.mongodb.internal.HexUtils; - -/** - * Integration tests for {@link ReactiveGridFsTemplate}. - * - * @author Mark Paluch - * @author Christoph Strobl - * @author Nick Stolwijk - * @author Denis Zavedeev - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:gridfs/reactive-gridfs.xml") -public class ReactiveGridFsTemplateTests { - - Resource resource = new ClassPathResource("gridfs/gridfs.xml"); - - @Autowired ReactiveGridFsOperations operations; - @Autowired SimpleMongoClientDatabaseFactory mongoClient; - @Autowired ReactiveMongoDatabaseFactory dbFactory; - @Autowired MongoConverter mongoConverter; - - @Before - public void setUp() { - - operations.delete(new Query()) // - .as(StepVerifier::create) // - .verifyComplete(); - } - - @Test // DATAMONGO-1855 - public void storesAndFindsSimpleDocument() { - - DefaultDataBufferFactory factory = new DefaultDataBufferFactory(); - DefaultDataBuffer first = factory.wrap("first".getBytes()); - DefaultDataBuffer second = factory.wrap("second".getBytes()); - - ObjectId reference = operations.store(Flux.just(first, second), "foo.xml").block(); - - operations.find(query(where("_id").is(reference))) // - .as(StepVerifier::create) // - .assertNext(actual -> { - assertThat(((BsonObjectId) actual.getId()).getValue()).isEqualTo(reference); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1855 - public void storesAndLoadsLargeFileCorrectly() { - - ByteBuffer buffer = ByteBuffer.allocate(1000 * 1000); // 1 mb - int i = 0; - while (buffer.remaining() != 0) { - buffer.put(HexUtils.toHex(new byte[] { (byte) (i++ % 16) }).getBytes()); - } - buffer.flip(); - - DefaultDataBufferFactory factory = new DefaultDataBufferFactory(); - - ObjectId reference = operations.store(Flux.just(factory.wrap(buffer)), "large.txt").block(); - - buffer.clear(); - - // default chunk size - operations.findOne(query(where("_id").is(reference))).flatMap(operations::getResource) - .flatMapMany(ReactiveGridFsResource::getDownloadStream) // - .transform(DataBufferUtils::join) // - .as(StepVerifier::create) // - .consumeNextWith(dataBuffer -> { - - assertThat(dataBuffer.readableByteCount()).isEqualTo(buffer.remaining()); - assertThat(dataBuffer.asByteBuffer()).isEqualTo(buffer); - }).verifyComplete(); - - // small chunk size - operations.findOne(query(where("_id").is(reference))).flatMap(operations::getResource) - .flatMapMany(reactiveGridFsResource -> reactiveGridFsResource.getDownloadStream(256)) // - .transform(DataBufferUtils::join) // - .as(StepVerifier::create) // - .consumeNextWith(dataBuffer -> { - - assertThat(dataBuffer.readableByteCount()).isEqualTo(buffer.remaining()); - assertThat(dataBuffer.asByteBuffer()).isEqualTo(buffer); - }).verifyComplete(); - } - - @Test // DATAMONGO-1855 - public void writesMetadataCorrectly() throws IOException { - - Document metadata = new Document("key", "value"); - - Flux source = DataBufferUtils.read(resource, new DefaultDataBufferFactory(), 256); - ObjectId reference = operations.store(source, "foo.xml", "binary/octet-stream", metadata).block(); - - operations.find(query(whereMetaData("key").is("value"))) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual.getObjectId()).isEqualTo(reference); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1855 - public void marshalsComplexMetadata() { - - Metadata metadata = new Metadata(); - metadata.version = "1.0"; - - Flux source = DataBufferUtils.read(resource, new DefaultDataBufferFactory(), 256); - ObjectId reference = operations.store(source, "foo.xml", "binary/octet-stream", metadata).block(); - - operations.find(query(whereMetaData("version").is("1.0"))) // - .as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual.getObjectId()).isEqualTo(reference); - assertThat(actual.getMetadata()).containsEntry("version", "1.0"); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1855 - public void getResourceShouldRetrieveContentByIdentity() throws IOException { - - byte[] content = StreamUtils.copyToByteArray(resource.getInputStream()); - Flux source = DataBufferUtils.read(resource, new DefaultDataBufferFactory(), 256); - ObjectId reference = operations.store(source, "foo.xml", null, null).block(); - - operations.findOne(query(where("_id").is(reference))).flatMap(operations::getResource) - .flatMapMany(ReactiveGridFsResource::getDownloadStream) // - .transform(DataBufferUtils::join) // - .as(StepVerifier::create) // - .consumeNextWith(dataBuffer -> { - - byte[] actual = new byte[dataBuffer.readableByteCount()]; - dataBuffer.read(actual); - - assertThat(actual).isEqualTo(content); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1855, DATAMONGO-2240 - public void shouldEmitFirstEntryWhenFindFirstRetrievesMoreThanOneResult() throws IOException { - - Flux upload1 = DataBufferUtils.read(resource, new DefaultDataBufferFactory(), 256); - Flux upload2 = DataBufferUtils.read(new ClassPathResource("gridfs/another-resource.xml"), - new DefaultDataBufferFactory(), 256); - - operations.store(upload1, "foo.xml", null, null).block(); - operations.store(upload2, "foo2.xml", null, null).block(); - - operations.findFirst(query(where("filename").regex("foo*"))) // - .flatMap(operations::getResource) // - .as(StepVerifier::create) // - .assertNext(actual -> { - - assertThat(actual.getGridFSFile()).isNotNull(); - }).verifyComplete(); - } - - @Test // DATAMONGO-2240 - public void shouldReturnNoGridFsFileWhenAbsent() { - - operations.getResource("absent") // - .as(StepVerifier::create) // - .assertNext(actual -> { - - assertThat(actual.exists()).isFalse(); - assertThat(actual.getGridFSFile()).isEqualTo(Mono.empty()); - }).verifyComplete(); - } - - @Test // DATAMONGO-1855 - public void shouldEmitErrorWhenFindOneRetrievesMoreThanOneResult() throws IOException { - - Flux upload1 = DataBufferUtils.read(resource, new DefaultDataBufferFactory(), 256); - Flux upload2 = DataBufferUtils.read(new ClassPathResource("gridfs/another-resource.xml"), - new DefaultDataBufferFactory(), 256); - - operations.store(upload1, "foo.xml", null, null).block(); - operations.store(upload2, "foo2.xml", null, null).block(); - - operations.findOne(query(where("filename").regex("foo*"))) // - .as(StepVerifier::create) // - .expectError(IncorrectResultSizeDataAccessException.class) // - .verify(); - } - - @Test // DATAMONGO-1855 - public void getResourcesByPattern() throws IOException { - - byte[] content = StreamUtils.copyToByteArray(resource.getInputStream()); - Flux upload = DataBufferUtils.read(resource, new DefaultDataBufferFactory(), 256); - - operations.store(upload, "foo.xml", null, null).block(); - - operations.getResources("foo*") // - .flatMap(ReactiveGridFsResource::getDownloadStream) // - .transform(DataBufferUtils::join) // - .as(StepVerifier::create) // - .consumeNextWith(dataBuffer -> { - - byte[] actual = new byte[dataBuffer.readableByteCount()]; - dataBuffer.read(actual); - - assertThat(actual).isEqualTo(content); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-625 - public void storeSavesGridFsUploadWithGivenIdCorrectly() throws IOException { - - String id = "id-1"; - byte[] content = StreamUtils.copyToByteArray(resource.getInputStream()); - Flux data = DataBufferUtils.read(resource, new DefaultDataBufferFactory(), 256); - - ReactiveGridFsUpload upload = ReactiveGridFsUpload.fromPublisher(data) // - .id(id) // - .filename("gridFsUpload.xml") // - .contentType("xml") // - .build(); - - operations.store(upload).as(StepVerifier::create).expectNext(id).verifyComplete(); - - operations.findOne(query(where("_id").is(id))).flatMap(operations::getResource) - .flatMapMany(ReactiveGridFsResource::getDownloadStream) // - .transform(DataBufferUtils::join) // - .as(StepVerifier::create) // - .consumeNextWith(dataBuffer -> { - - byte[] actual = new byte[dataBuffer.readableByteCount()]; - dataBuffer.read(actual); - - assertThat(actual).isEqualTo(content); - }) // - .verifyComplete(); - - operations.findOne(query(where("_id").is(id))).as(StepVerifier::create).consumeNextWith(it -> { - assertThat(it.getFilename()).isEqualTo("gridFsUpload.xml"); - assertThat(it.getId()).isEqualTo(new BsonString(id)); - assertThat(it.getMetadata()).containsValue("xml"); - }).verifyComplete(); - } - - @Test // DATAMONGO-765 - public void considersSkipLimitWhenQueryingFiles() { - - DataBufferFactory bufferFactory = new DefaultDataBufferFactory(); - DataBuffer buffer = bufferFactory.allocateBuffer(0); - Flux.just("a", "aa", "aaa", // - "b", "bb", "bbb", // - "c", "cc", "ccc", // - "d", "dd", "ddd") // - .flatMap(fileName -> operations.store(Mono.just(buffer), fileName)) // - .as(StepVerifier::create) // - .expectNextCount(12) // - .verifyComplete(); - - PageRequest pageRequest = PageRequest.of(2, 3, Sort.Direction.ASC, "filename"); - operations.find(new Query().with(pageRequest)) // - .map(GridFSFile::getFilename) // - .as(StepVerifier::create) // - .expectNext("c", "cc", "ccc") // - .verifyComplete(); - } - - static class Metadata { - String version; - } - - public static String readToString(DataBuffer dataBuffer) { - try { - return FileCopyUtils.copyToString(new InputStreamReader(dataBuffer.asInputStream())); - } catch (IOException e) { - return e.getMessage(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateUnitTests.java deleted file mode 100644 index b2422cbd24..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplateUnitTests.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2020-2021 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.gridfs; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.io.InputStream; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.reactivestreams.Publisher; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.MongoConverter; - -/** - * @author Christoph Strobl - */ -class ReactiveGridFsTemplateUnitTests { - - private ReactiveGridFsTemplateStub template; - - @BeforeEach - void beforeEach() { - template = new ReactiveGridFsTemplateStub(); - } - - @Test // DATAMONGO-2574 - void contentMetadataDoesNotOverrideContentTypeIfSet() { - - template.onStoreReturn(new ObjectId()); - template.store(Flux.empty(), "filename", "json", new Document("meta", "data")); - - assertThat(template.capturedUpload().getOptions().getContentType()).isEqualTo("json"); - assertThat(template.capturedUpload().getOptions().getMetadata()).containsEntry("meta", "data"); - } - - private static class ReactiveGridFsTemplateStub extends ReactiveGridFsTemplate { - - private Object onStoreResult; - private GridFsObject> capturedUpload; - - ReactiveGridFsTemplateStub() { - super(mock(ReactiveMongoDatabaseFactory.class), mock(MongoConverter.class)); - } - - @Override - public Mono store(GridFsObject> upload) { - - capturedUpload = upload; - return Mono.just((T) onStoreResult); - } - - ReactiveGridFsTemplateStub onStoreReturn(Object result) { - - this.onStoreResult = result; - return this; - } - - GridFsObject> capturedUpload() { - return capturedUpload; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java deleted file mode 100644 index 7686d3abd5..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002-2021 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.monitor; - -import static org.assertj.core.api.Assertions.*; - -import java.net.UnknownHostException; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; - -import com.mongodb.client.MongoClient; - -/** - * This test class assumes that you are already running the MongoDB server. - * - * @author Mark Pollack - * @author Thomas Darimont - * @author Mark Paluch - */ -@ExtendWith(MongoClientExtension.class) -public class MongoMonitorIntegrationTests { - - static @Client MongoClient mongoClient; - - @Test - public void serverInfo() { - ServerInfo serverInfo = new ServerInfo(mongoClient); - serverInfo.getVersion(); - } - - @Test // DATAMONGO-685 - public void getHostNameShouldReturnServerNameReportedByMongo() throws UnknownHostException { - - ServerInfo serverInfo = new ServerInfo(mongoClient); - - String hostName = null; - try { - hostName = serverInfo.getHostName(); - } catch (UnknownHostException e) { - throw e; - } - - assertThat(hostName).isNotNull(); - assertThat(hostName).isEqualTo("127.0.0.1:27017"); - } - - @Test - public void operationCounters() { - OperationCounters operationCounters = new OperationCounters(mongoClient); - operationCounters.getInsertCount(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/Resumeable.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/Resumeable.java deleted file mode 100644 index 4b2de4cd81..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/Resumeable.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2018-2021 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.monitor; - -import java.util.function.Supplier; - -/** - * @author Christoph Strobl - * @since 2018/01 - */ -interface Resumeable { - - void resumeAt(Supplier token); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java deleted file mode 100644 index 37a2667287..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java +++ /dev/null @@ -1,876 +0,0 @@ -/* - * Copyright 2012-2021 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.performance; - -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import java.text.DecimalFormat; -import java.util.*; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.core.Constants; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean; -import org.springframework.util.Assert; -import org.springframework.util.StopWatch; -import org.springframework.util.StringUtils; - -import com.mongodb.WriteConcern; -import com.mongodb.client.FindIterable; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.model.CreateCollectionOptions; - -/** - * Test class to execute performance tests for plain MongoDB driver usage, {@link MongoTemplate} and the repositories - * abstraction. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - */ -public class PerformanceTests { - - private static final String DATABASE_NAME = "performance"; - private static final int NUMBER_OF_PERSONS = 300; - private static final int ITERATIONS = 50; - private static final StopWatch watch = new StopWatch(); - private static final Collection IGNORED_WRITE_CONCERNS = Arrays.asList("MAJORITY", "REPLICAS_SAFE", - "FSYNC_SAFE", "FSYNCED", "JOURNAL_SAFE", "JOURNALED", "REPLICA_ACKNOWLEDGED", "W2", "W3"); - private static final int COLLECTION_SIZE = 1024 - 2018 * 1024 - 2018 * 256; // 256 MB - private static final Collection COLLECTION_NAMES = Arrays.asList("template", "driver", "person"); - - MongoClient mongo; - MongoTemplate operations; - PersonRepository repository; - MongoConverter converter; - - @BeforeEach - public void setUp() throws Exception { - - this.mongo = MongoClients.create(); - - SimpleMongoClientDatabaseFactory mongoDbFactory = new SimpleMongoClientDatabaseFactory(this.mongo, DATABASE_NAME); - - MongoMappingContext context = new MongoMappingContext(); - context.setInitialEntitySet(Collections.singleton(Person.class)); - context.afterPropertiesSet(); - - this.converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), context); - this.operations = new MongoTemplate(new SimpleMongoClientDatabaseFactory(this.mongo, DATABASE_NAME), converter); - - MongoRepositoryFactoryBean factory = new MongoRepositoryFactoryBean( - PersonRepository.class); - factory.setMongoOperations(operations); - factory.afterPropertiesSet(); - - this.repository = factory.getObject(); - - } - - @Test - public void writeWithWriteConcerns() { - executeWithWriteConcerns(new WriteConcernCallback() { - public void doWithWriteConcern(String constantName, WriteConcern concern) { - writeHeadline("WriteConcern: " + constantName); - System.out.println(String.format("Writing %s objects using plain driver took %sms", NUMBER_OF_PERSONS, - writingObjectsUsingPlainDriver(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects using template took %sms", NUMBER_OF_PERSONS, - writingObjectsUsingMongoTemplate(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects using repository took %sms", NUMBER_OF_PERSONS, - writingObjectsUsingRepositories(NUMBER_OF_PERSONS, concern))); - writeFooter(); - } - }); - } - - @Test - public void plainConversion() throws InterruptedException { - - Statistics statistics = new Statistics( - "Plain conversion of " + NUMBER_OF_PERSONS * 100 + " persons - After %s iterations"); - - List documents = getPersonDocuments(NUMBER_OF_PERSONS * 100); - - for (int i = 0; i < ITERATIONS; i++) { - statistics.registerTime(Api.DIRECT, Mode.READ, convertDirectly(documents)); - statistics.registerTime(Api.CONVERTER, Mode.READ, convertUsingConverter(documents)); - } - - statistics.printResults(ITERATIONS); - } - - private long convertDirectly(final List documents) { - - executeWatched(new WatchCallback>() { - - @Override - public List doInWatch() { - - List persons = new ArrayList(); - - for (Document document : documents) { - persons.add(Person.from(document)); - } - - return persons; - } - }); - - return watch.getLastTaskTimeMillis(); - } - - private long convertUsingConverter(final List documents) { - - executeWatched(new WatchCallback>() { - - @Override - public List doInWatch() { - - List persons = new ArrayList(); - - for (Document document : documents) { - persons.add(converter.read(Person.class, document)); - } - - return persons; - } - }); - - return watch.getLastTaskTimeMillis(); - } - - @Test - public void writeAndRead() throws Exception { - readsAndWrites(NUMBER_OF_PERSONS, ITERATIONS, WriteConcern.ACKNOWLEDGED); - } - - private void readsAndWrites(int numberOfPersons, int iterations, WriteConcern writeConcern) { - - Statistics statistics = new Statistics("Reading " + numberOfPersons + " - After %s iterations"); - - for (int i = 0; i < iterations; i++) { - - setupCollections(); - - statistics.registerTime(Api.DRIVER, Mode.WRITE, writingObjectsUsingPlainDriver(numberOfPersons, writeConcern)); - statistics.registerTime(Api.TEMPLATE, Mode.WRITE, - writingObjectsUsingMongoTemplate(numberOfPersons, writeConcern)); - statistics.registerTime(Api.REPOSITORY, Mode.WRITE, - writingObjectsUsingRepositories(numberOfPersons, writeConcern)); - - statistics.registerTime(Api.DRIVER, Mode.READ, readingUsingPlainDriver()); - statistics.registerTime(Api.TEMPLATE, Mode.READ, readingUsingTemplate()); - statistics.registerTime(Api.REPOSITORY, Mode.READ, readingUsingRepository()); - - statistics.registerTime(Api.DRIVER, Mode.QUERY, queryUsingPlainDriver()); - statistics.registerTime(Api.TEMPLATE, Mode.QUERY, queryUsingTemplate()); - statistics.registerTime(Api.REPOSITORY, Mode.QUERY, queryUsingRepository()); - - if (i > 0 && i % (iterations / 10) == 0) { - statistics.printResults(i); - } - } - - statistics.printResults(iterations); - } - - private void writeHeadline(String headline) { - System.out.println(headline); - System.out.println(createUnderline(headline)); - } - - private void writeFooter() { - System.out.println(); - } - - private long queryUsingTemplate() { - executeWatched(() -> operations.find(query(where("addresses.zipCode").regex(".*1.*")), Person.class, "template")); - - return watch.getLastTaskTimeMillis(); - } - - private long queryUsingRepository() { - executeWatched(() -> repository.findByAddressesZipCodeContaining("1")); - - return watch.getLastTaskTimeMillis(); - } - - private void executeWithWriteConcerns(WriteConcernCallback callback) { - - Constants constants = new Constants(WriteConcern.class); - - for (String constantName : constants.getNames(null)) { - - if (IGNORED_WRITE_CONCERNS.contains(constantName)) { - continue; - } - - WriteConcern writeConcern = (WriteConcern) constants.asObject(constantName); - setupCollections(); - - callback.doWithWriteConcern(constantName, writeConcern); - } - } - - private void setupCollections() { - - MongoDatabase db = this.mongo.getDatabase(DATABASE_NAME); - - for (String collectionName : COLLECTION_NAMES) { - - MongoCollection collection = db.getCollection(collectionName); - collection.drop(); - - CreateCollectionOptions collectionOptions = new CreateCollectionOptions(); - collectionOptions.capped(false); - collectionOptions.sizeInBytes(COLLECTION_SIZE); - - db.createCollection(collectionName, collectionOptions); - - collection.createIndex(new Document("firstname", -1)); - collection.createIndex(new Document("lastname", -1)); - } - } - - private Document getCreateCollectionCommand(String name) { - Document document = new Document(); - document.put("createCollection", name); - document.put("capped", false); - document.put("size", COLLECTION_SIZE); - return document; - } - - private long writingObjectsUsingPlainDriver(int numberOfPersons, WriteConcern writeConcern) { - - MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver") - .withWriteConcern(writeConcern); - ; - List persons = getPersonObjects(numberOfPersons); - - executeWatched(() -> persons.stream().map(Person::toDocument).map(it -> { - - collection.insertOne(it); - return true; - })); - - return watch.getLastTaskTimeMillis(); - } - - private long writingObjectsUsingRepositories(int numberOfPersons, WriteConcern writeConcern) { - - List persons = getPersonObjects(numberOfPersons); - - executeWatched(() -> repository.saveAll(persons)); - - return watch.getLastTaskTimeMillis(); - } - - private long writingObjectsUsingMongoTemplate(int numberOfPersons, WriteConcern writeConcern) { - - List persons = getPersonObjects(numberOfPersons); - operations.setWriteConcern(writeConcern); - - executeWatched(() -> persons.stream()// - .peek(it -> operations.save(it, "template"))// - .collect(Collectors.toList())); - - return watch.getLastTaskTimeMillis(); - } - - private long readingUsingPlainDriver() { - - executeWatched(() -> toPersons(mongo.getDatabase(DATABASE_NAME).getCollection("driver").find())); - - return watch.getLastTaskTimeMillis(); - } - - private long readingUsingTemplate() { - executeWatched(() -> operations.findAll(Person.class, "template")); - - return watch.getLastTaskTimeMillis(); - } - - private long readingUsingRepository() { - executeWatched(repository::findAll); - - return watch.getLastTaskTimeMillis(); - } - - private long queryUsingPlainDriver() { - - executeWatched(() -> { - - MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver"); - - Document regex = new Document("$regex", Pattern.compile(".*1.*")); - Document query = new Document("addresses.zipCode", regex); - return toPersons(collection.find(query)); - }); - - return watch.getLastTaskTimeMillis(); - } - - private List getPersonObjects(int numberOfPersons) { - - List result = new ArrayList(); - - for (int i = 0; i < numberOfPersons; i++) { - - List
addresses = new ArrayList
(); - - for (int a = 0; a < 5; a++) { - addresses.add(new Address("zip" + a, "city" + a)); - } - - Person person = new Person("Firstname" + i, "Lastname" + i, addresses); - - for (int o = 0; o < 10; o++) { - person.orders.add(new Order(LineItem.generate())); - } - - result.add(person); - } - - return result; - } - - private List getPersonDocuments(int numberOfPersons) { - - List documents = new ArrayList(numberOfPersons); - - for (Person person : getPersonObjects(numberOfPersons)) { - documents.add(person.toDocument()); - } - - return documents; - } - - private T executeWatched(WatchCallback callback) { - - watch.start(); - - try { - return callback.doInWatch(); - } finally { - watch.stop(); - } - } - - private static List toPersons(FindIterable cursor) { - - List persons = new ArrayList(); - - Iterator it = cursor.iterator(); - while (it.hasNext()) { - persons.add(Person.from(it.next())); - } - - return persons; - } - - static class Person { - - ObjectId id; - String firstname, lastname; - List
addresses; - Set orders; - - public Person(String firstname, String lastname, List
addresses) { - this.firstname = firstname; - this.lastname = lastname; - this.addresses = addresses; - this.orders = new HashSet(); - } - - public static Person from(Document source) { - - List addressesSource = (List) source.get("addresses"); - List
addresses = new ArrayList
(addressesSource.size()); - for (Object addressSource : addressesSource) { - addresses.add(Address.from((Document) addressSource)); - } - - List ordersSource = (List) source.get("orders"); - Set orders = new HashSet(ordersSource.size()); - for (Object orderSource : ordersSource) { - orders.add(Order.from((Document) orderSource)); - } - - Person person = new Person((String) source.get("firstname"), (String) source.get("lastname"), addresses); - person.orders.addAll(orders); - return person; - } - - public Document toDocument() { - - Document document = new Document(); - document.put("firstname", firstname); - document.put("lastname", lastname); - document.put("addresses", writeAll(addresses)); - document.put("orders", writeAll(orders)); - return document; - } - } - - static class Address implements Convertible { - - final String zipCode; - final String city; - final Set types; - - public Address(String zipCode, String city) { - this(zipCode, city, new HashSet(pickRandomNumerOfItemsFrom(Arrays.asList(AddressType.values())))); - } - - @PersistenceConstructor - public Address(String zipCode, String city, Set types) { - this.zipCode = zipCode; - this.city = city; - this.types = types; - } - - public static Address from(Document source) { - String zipCode = (String) source.get("zipCode"); - String city = (String) source.get("city"); - List types = (List) source.get("types"); - - return new Address(zipCode, city, new HashSet(readFromBasicDBList(types, AddressType.class))); - } - - public Document toDocument() { - Document document = new Document(); - document.put("zipCode", zipCode); - document.put("city", city); - document.put("types", toBasicDBList(types)); - return document; - } - } - - private static > List readFromBasicDBList(List source, Class type) { - - List result = new ArrayList(source.size()); - for (Object object : source) { - result.add(Enum.valueOf(type, object.toString())); - } - return result; - } - - private static > List toBasicDBList(Collection enums) { - List result = new ArrayList<>(); - for (T element : enums) { - result.add(element.toString()); - } - - return result; - } - - static class Order implements Convertible { - - static enum Status { - ORDERED, PAYED, SHIPPED; - } - - Date createdAt; - List lineItems; - Status status; - - public Order(List lineItems, Date createdAt) { - this.lineItems = lineItems; - this.createdAt = createdAt; - this.status = Status.ORDERED; - } - - @PersistenceConstructor - public Order(List lineItems, Date createdAt, Status status) { - this.lineItems = lineItems; - this.createdAt = createdAt; - this.status = status; - } - - public static Order from(Document source) { - - List lineItemsSource = (List) source.get("lineItems"); - List lineItems = new ArrayList(lineItemsSource.size()); - for (Object lineItemSource : lineItemsSource) { - lineItems.add(LineItem.from((Document) lineItemSource)); - } - - Date date = (Date) source.get("createdAt"); - Status status = Status.valueOf((String) source.get("status")); - return new Order(lineItems, date, status); - } - - public Order(List lineItems) { - this(lineItems, new Date()); - } - - public Document toDocument() { - Document result = new Document(); - result.put("createdAt", createdAt); - result.put("lineItems", writeAll(lineItems)); - result.put("status", status.toString()); - return result; - } - } - - static class LineItem implements Convertible { - - String description; - double price; - int amount; - - public LineItem(String description, int amount, double price) { - this.description = description; - this.amount = amount; - this.price = price; - } - - public static List generate() { - - LineItem iPad = new LineItem("iPad", 1, 649); - LineItem iPhone = new LineItem("iPhone", 1, 499); - LineItem macBook = new LineItem("MacBook", 2, 1299); - - return pickRandomNumerOfItemsFrom(Arrays.asList(iPad, iPhone, macBook)); - } - - public static LineItem from(Document source) { - - String description = (String) source.get("description"); - double price = (Double) source.get("price"); - int amount = (Integer) source.get("amount"); - - return new LineItem(description, amount, price); - } - - public Document toDocument() { - - Document document = new Document(); - document.put("description", description); - document.put("price", price); - document.put("amount", amount); - return document; - } - } - - private static List pickRandomNumerOfItemsFrom(List source) { - - Assert.isTrue(!source.isEmpty(), "Source must not be empty!"); - - Random random = new Random(); - int numberOfItems = random.nextInt(source.size()); - numberOfItems = numberOfItems == 0 ? 1 : numberOfItems; - - List result = new ArrayList(numberOfItems); - while (result.size() < numberOfItems) { - int index = random.nextInt(source.size()); - T candidate = source.get(index); - if (!result.contains(candidate)) { - result.add(candidate); - } - } - - return result; - } - - static enum AddressType { - SHIPPING, BILLING; - } - - private interface WriteConcernCallback { - void doWithWriteConcern(String constantName, WriteConcern concern); - } - - private interface WatchCallback { - T doInWatch(); - } - - private interface PersonRepository extends MongoRepository { - - List findByAddressesZipCodeContaining(String parameter); - } - - private interface Convertible { - - Document toDocument(); - } - - private static List writeAll(Collection convertibles) { - List result = new ArrayList<>(); - for (Convertible convertible : convertibles) { - result.add(convertible.toDocument()); - } - return result; - } - - static enum Api { - DRIVER, TEMPLATE, REPOSITORY, DIRECT, CONVERTER; - } - - static enum Mode { - WRITE, READ, QUERY; - } - - private static class Statistics { - - private final String headline; - private final Map times; - - public Statistics(String headline) { - - this.headline = headline; - this.times = new HashMap(); - - for (Mode mode : Mode.values()) { - times.put(mode, new ModeTimes(mode)); - } - } - - public void registerTime(Api api, Mode mode, double time) { - times.get(mode).add(api, time); - } - - public void printResults(int iterations) { - - String title = String.format(headline, iterations); - - System.out.println(title); - System.out.println(createUnderline(title)); - - StringBuilder builder = new StringBuilder(); - for (Mode mode : Mode.values()) { - String print = times.get(mode).print(); - if (!print.isEmpty()) { - builder.append(print).append('\n'); - } - } - - System.out.println(builder.toString()); - } - - @Override - public String toString() { - - StringBuilder builder = new StringBuilder(times.size()); - - for (ModeTimes times : this.times.values()) { - builder.append(times.toString()); - } - - return builder.toString(); - } - } - - private static String createUnderline(String input) { - - StringBuilder builder = new StringBuilder(input.length()); - - for (int i = 0; i < input.length(); i++) { - builder.append("-"); - } - - return builder.toString(); - } - - static class ApiTimes { - - private static final String TIME_TEMPLATE = "%s %s time -\tAverage: %sms%s,%sMedian: %sms%s"; - - private static final DecimalFormat TIME_FORMAT; - private static final DecimalFormat DEVIATION_FORMAT; - - static { - - TIME_FORMAT = new DecimalFormat("0.00"); - - DEVIATION_FORMAT = new DecimalFormat("0.00"); - DEVIATION_FORMAT.setPositivePrefix("+"); - } - - private final Api api; - private final Mode mode; - private final List times; - - public ApiTimes(Api api, Mode mode) { - this.api = api; - this.mode = mode; - this.times = new ArrayList(); - } - - public void add(double time) { - this.times.add(time); - } - - public boolean hasTimes() { - return !times.isEmpty(); - } - - public double getAverage() { - - double result = 0; - - for (Double time : times) { - result += time; - } - - return result == 0.0 ? 0.0 : result / times.size(); - } - - public double getMedian() { - - if (times.isEmpty()) { - return 0.0; - } - - ArrayList list = new ArrayList(times); - Collections.sort(list); - - int size = list.size(); - - if (size % 2 == 0) { - return (list.get(size / 2 - 1) + list.get(size / 2)) / 2; - } else { - return list.get(size / 2); - } - } - - private double getDeviationFrom(double otherAverage) { - - double average = getAverage(); - return average * 100 / otherAverage - 100; - } - - private double getMediaDeviationFrom(double otherMedian) { - double median = getMedian(); - return median * 100 / otherMedian - 100; - } - - public String print() { - - if (times.isEmpty()) { - return ""; - } - - return basicPrint("", "\t\t", "") + '\n'; - } - - private String basicPrint(String extension, String middle, String foo) { - return String.format(TIME_TEMPLATE, api, mode, TIME_FORMAT.format(getAverage()), extension, middle, - TIME_FORMAT.format(getMedian()), foo); - } - - public String print(double referenceAverage, double referenceMedian) { - - if (times.isEmpty()) { - return ""; - } - - return basicPrint(String.format(" %s%%", DEVIATION_FORMAT.format(getDeviationFrom(referenceAverage))), "\t", - String.format(" %s%%", DEVIATION_FORMAT.format(getMediaDeviationFrom(referenceMedian)))) + '\n'; - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return times.isEmpty() ? "" - : String.format("%s, %s: %s", api, mode, StringUtils.collectionToCommaDelimitedString(times)) + '\n'; - } - } - - static class ModeTimes { - - private final Map times; - - public ModeTimes(Mode mode) { - - this.times = new HashMap(); - - for (Api api : Api.values()) { - this.times.put(api, new ApiTimes(api, mode)); - } - } - - public void add(Api api, double time) { - times.get(api).add(time); - } - - @SuppressWarnings("null") - public String print() { - - if (times.isEmpty()) { - return ""; - } - - Double previousTime = null; - Double previousMedian = null; - StringBuilder builder = new StringBuilder(); - - for (Api api : Api.values()) { - - ApiTimes apiTimes = times.get(api); - - if (!apiTimes.hasTimes()) { - continue; - } - - if (previousTime == null) { - builder.append(apiTimes.print()); - previousTime = apiTimes.getAverage(); - previousMedian = apiTimes.getMedian(); - } else { - builder.append(apiTimes.print(previousTime, previousMedian)); - } - } - - return builder.toString(); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - - StringBuilder builder = new StringBuilder(times.size()); - - for (ApiTimes times : this.times.values()) { - builder.append(times.toString()); - } - - return builder.toString(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java deleted file mode 100644 index e310d7d298..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java +++ /dev/null @@ -1,922 +0,0 @@ -/* - * Copyright 2016-2021 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.performance; - -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.text.DecimalFormat; -import java.util.*; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.core.Constants; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.DbRefProxyHandler; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DbRefResolverCallback; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.ReactiveMongoRepository; -import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory; -import org.springframework.util.Assert; -import org.springframework.util.StopWatch; -import org.springframework.util.StringUtils; - -import com.mongodb.BasicDBList; -import com.mongodb.BasicDBObject; -import com.mongodb.DBRef; -import com.mongodb.WriteConcern; -import com.mongodb.client.model.CreateCollectionOptions; -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoClients; -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * Test class to execute performance tests for plain Reactive Streams MongoDB driver usage, - * {@link ReactiveMongoOperations} and the repositories abstraction. - * - * @author Mark Paluch - */ -public class ReactivePerformanceTests { - - private static final String DATABASE_NAME = "performance"; - private static final int NUMBER_OF_PERSONS = 300; - private static final int ITERATIONS = 50; - private static final StopWatch watch = new StopWatch(); - private static final Collection IGNORED_WRITE_CONCERNS = Arrays.asList("MAJORITY", "REPLICAS_SAFE", - "FSYNC_SAFE", "FSYNCED", "JOURNAL_SAFE", "JOURNALED", "REPLICA_ACKNOWLEDGED"); - private static final int COLLECTION_SIZE = 1024 - 2018 * 1024 - 2018 * 256; // 256 MB - private static final Collection COLLECTION_NAMES = Arrays.asList("template", "driver", "person"); - - MongoClient mongo; - ReactiveMongoTemplate operations; - ReactivePersonRepository repository; - MongoConverter converter; - - @BeforeEach - public void setUp() throws Exception { - - mongo = MongoClients.create(); - - SimpleReactiveMongoDatabaseFactory mongoDbFactory = new SimpleReactiveMongoDatabaseFactory(this.mongo, - DATABASE_NAME); - - MongoMappingContext context = new MongoMappingContext(); - context.setInitialEntitySet(Collections.singleton(Person.class)); - context.afterPropertiesSet(); - - converter = new MappingMongoConverter(new DbRefResolver() { - @Override - public Object resolveDbRef(MongoPersistentProperty property, DBRef dbref, DbRefResolverCallback callback, - DbRefProxyHandler proxyHandler) { - return null; - } - - @Override - public DBRef createDbRef(org.springframework.data.mongodb.core.mapping.DBRef annotation, - MongoPersistentEntity entity, Object id) { - return null; - } - - @Override - public Document fetch(DBRef dbRef) { - return null; - } - - @Override - public List bulkFetch(List dbRefs) { - return null; - } - }, context); - operations = new ReactiveMongoTemplate(mongoDbFactory, converter); - - ReactiveMongoRepositoryFactory factory = new ReactiveMongoRepositoryFactory(operations); - repository = factory.getRepository(ReactivePersonRepository.class); - } - - @Test // DATAMONGO-1444 - public void writeWithWriteConcerns() { - executeWithWriteConcerns((constantName, concern) -> { - - writeHeadline("WriteConcern: " + constantName); - System.out.println(String.format("Writing %s objects using plain driver took %sms", NUMBER_OF_PERSONS, - writingObjectsUsingPlainDriver(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects using template took %sms", NUMBER_OF_PERSONS, - writingObjectsUsingMongoTemplate(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects using repository took %sms", NUMBER_OF_PERSONS, - writingObjectsUsingRepositories(NUMBER_OF_PERSONS, concern))); - - System.out.println(String.format("Writing %s objects async using plain driver took %sms", NUMBER_OF_PERSONS, - writingAsyncObjectsUsingPlainDriver(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects async using template took %sms", NUMBER_OF_PERSONS, - writingAsyncObjectsUsingMongoTemplate(NUMBER_OF_PERSONS, concern))); - System.out.println(String.format("Writing %s objects async using repository took %sms", NUMBER_OF_PERSONS, - writingAsyncObjectsUsingRepositories(NUMBER_OF_PERSONS, concern))); - writeFooter(); - }); - } - - @Test - public void plainConversion() throws InterruptedException { - - Statistics statistics = new Statistics( - "Plain conversion of " + NUMBER_OF_PERSONS * 100 + " persons - After %s iterations"); - - List dbObjects = getPersonDocuments(NUMBER_OF_PERSONS * 100); - - for (int i = 0; i < ITERATIONS; i++) { - statistics.registerTime(Api.DIRECT, Mode.READ, convertDirectly(dbObjects)); - statistics.registerTime(Api.CONVERTER, Mode.READ, convertUsingConverter(dbObjects)); - } - - statistics.printResults(ITERATIONS); - } - - private long convertDirectly(final List dbObjects) { - - executeWatched(() -> { - - List persons = new ArrayList(); - - for (Document dbObject : dbObjects) { - persons.add(Person.from(new Document(dbObject))); - } - - return persons; - }); - - return watch.getLastTaskTimeMillis(); - } - - private long convertUsingConverter(final List dbObjects) { - - executeWatched(() -> { - - List persons = new ArrayList(); - - for (Document dbObject : dbObjects) { - persons.add(converter.read(Person.class, dbObject)); - } - - return persons; - }); - - return watch.getLastTaskTimeMillis(); - } - - @Test // DATAMONGO-1444 - public void writeAndRead() throws Exception { - - readsAndWrites(NUMBER_OF_PERSONS, ITERATIONS, WriteConcern.ACKNOWLEDGED); - } - - private void readsAndWrites(int numberOfPersons, int iterations, WriteConcern concern) { - - Statistics statistics = new Statistics("Reading " + numberOfPersons + " - After %s iterations"); - - for (int i = 0; i < iterations; i++) { - - setupCollections(); - - statistics.registerTime(Api.DRIVER, Mode.WRITE, writingObjectsUsingPlainDriver(numberOfPersons, concern)); - statistics.registerTime(Api.TEMPLATE, Mode.WRITE, writingObjectsUsingMongoTemplate(numberOfPersons, concern)); - statistics.registerTime(Api.REPOSITORY, Mode.WRITE, writingObjectsUsingRepositories(numberOfPersons, concern)); - - statistics.registerTime(Api.DRIVER, Mode.WRITE_ASYNC, - writingAsyncObjectsUsingPlainDriver(numberOfPersons, concern)); - statistics.registerTime(Api.TEMPLATE, Mode.WRITE_ASYNC, - writingAsyncObjectsUsingMongoTemplate(numberOfPersons, concern)); - statistics.registerTime(Api.REPOSITORY, Mode.WRITE_ASYNC, - writingAsyncObjectsUsingRepositories(numberOfPersons, concern)); - - statistics.registerTime(Api.DRIVER, Mode.READ, readingUsingPlainDriver()); - statistics.registerTime(Api.TEMPLATE, Mode.READ, readingUsingTemplate()); - statistics.registerTime(Api.REPOSITORY, Mode.READ, readingUsingRepository()); - - statistics.registerTime(Api.DRIVER, Mode.QUERY, queryUsingPlainDriver()); - statistics.registerTime(Api.TEMPLATE, Mode.QUERY, queryUsingTemplate()); - statistics.registerTime(Api.REPOSITORY, Mode.QUERY, queryUsingRepository()); - - if (i > 0 && i % (iterations / 10) == 0) { - statistics.printResults(i); - } - } - - statistics.printResults(iterations); - } - - private void writeHeadline(String headline) { - System.out.println(headline); - System.out.println(createUnderline(headline)); - } - - private void writeFooter() { - System.out.println(); - } - - private long queryUsingTemplate() { - executeWatched(() -> { - Query query = query(where("addresses.zipCode").regex(".*1.*")); - return operations.find(query, Person.class, "template").collectList().block(); - }); - - return watch.getLastTaskTimeMillis(); - } - - private long queryUsingRepository() { - executeWatched(() -> repository.findByAddressesZipCodeContaining("1").collectList().block()); - - return watch.getLastTaskTimeMillis(); - } - - private void executeWithWriteConcerns(WriteConcernCallback callback) { - - Constants constants = new Constants(WriteConcern.class); - - for (String constantName : constants.getNames(null)) { - - if (IGNORED_WRITE_CONCERNS.contains(constantName)) { - continue; - } - - WriteConcern writeConcern = (WriteConcern) constants.asObject(constantName); - - setupCollections(); - - callback.doWithWriteConcern(constantName, writeConcern); - } - } - - private void setupCollections() { - - MongoDatabase db = this.mongo.getDatabase(DATABASE_NAME); - - for (String collectionName : COLLECTION_NAMES) { - MongoCollection collection = db.getCollection(collectionName); - Mono.from(collection.drop()).block(); - Mono.from(db.createCollection(collectionName, getCreateCollectionOptions())).block(); - collection.createIndex(new BasicDBObject("firstname", -1)); - collection.createIndex(new BasicDBObject("lastname", -1)); - } - } - - private CreateCollectionOptions getCreateCollectionOptions() { - CreateCollectionOptions options = new CreateCollectionOptions(); - return options.sizeInBytes(COLLECTION_SIZE).capped(false); - } - - private long writingObjectsUsingPlainDriver(int numberOfPersons, WriteConcern concern) { - - MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver") - .withWriteConcern(concern); - List persons = getPersonObjects(numberOfPersons); - - executeWatched( - () -> persons.stream().map(it -> Mono.from(collection.insertOne(new Document(it.toDocument()))).block())); - - return watch.getLastTaskTimeMillis(); - } - - private long writingObjectsUsingRepositories(int numberOfPersons, WriteConcern concern) { - - final List persons = getPersonObjects(numberOfPersons); - operations.setWriteConcern(concern); - executeWatched(() -> persons.stream().map(it -> repository.save(it).block())); - - return watch.getLastTaskTimeMillis(); - } - - private long writingObjectsUsingMongoTemplate(int numberOfPersons, WriteConcern concern) { - - final List persons = getPersonObjects(numberOfPersons); - - executeWatched(() -> { - operations.setWriteConcern(concern); - return persons.stream().map(it -> operations.save(it, "template").block()); - }); - - return watch.getLastTaskTimeMillis(); - } - - private long writingAsyncObjectsUsingPlainDriver(int numberOfPersons, WriteConcern concern) { - - MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver") - .withWriteConcern(concern); - List persons = getPersonObjects(numberOfPersons); - - executeWatched(() -> Flux - .from(collection - .insertMany(persons.stream().map(person -> new Document(person.toDocument())).collect(Collectors.toList()))) - .then().block()); - - return watch.getLastTaskTimeMillis(); - } - - private long writingAsyncObjectsUsingRepositories(int numberOfPersons, WriteConcern concern) { - - List persons = getPersonObjects(numberOfPersons); - operations.setWriteConcern(concern); - executeWatched(() -> repository.saveAll(persons).then().block()); - - return watch.getLastTaskTimeMillis(); - } - - private long writingAsyncObjectsUsingMongoTemplate(int numberOfPersons, WriteConcern concern) { - - List persons = getPersonObjects(numberOfPersons); - - executeWatched(() -> { - operations.setWriteConcern(concern); - return Flux.from(operations.insertAll(persons)).then().block(); - }); - - return watch.getLastTaskTimeMillis(); - } - - private long readingUsingPlainDriver() { - - executeWatched(() -> Flux.from(mongo.getDatabase(DATABASE_NAME).getCollection("driver").find()).map(Person::from) - .collectList().block()); - - return watch.getLastTaskTimeMillis(); - } - - private long readingUsingTemplate() { - executeWatched(() -> operations.findAll(Person.class, "template").collectList().block()); - - return watch.getLastTaskTimeMillis(); - } - - private long readingUsingRepository() { - executeWatched(() -> repository.findAll().collectList().block()); - - return watch.getLastTaskTimeMillis(); - } - - private long queryUsingPlainDriver() { - - executeWatched(() -> { - - MongoCollection collection = mongo.getDatabase(DATABASE_NAME).getCollection("driver"); - - Document regex = new Document("$regex", Pattern.compile(".*1.*")); - Document query = new Document("addresses.zipCode", regex); - return Flux.from(collection.find(query)).map(Person::from).collectList().block(); - }); - - return watch.getLastTaskTimeMillis(); - } - - private List getPersonObjects(int numberOfPersons) { - - List result = new ArrayList(); - - for (int i = 0; i < numberOfPersons; i++) { - - List
addresses = new ArrayList
(); - - for (int a = 0; a < 5; a++) { - addresses.add(new Address("zip" + a, "city" + a)); - } - - Person person = new Person("Firstname" + i, "Lastname" + i, addresses); - - for (int o = 0; o < 10; o++) { - person.orders.add(new Order(LineItem.generate())); - } - - result.add(person); - } - - return result; - } - - private List getPersonDocuments(int numberOfPersons) { - - List dbObjects = new ArrayList(numberOfPersons); - - for (Person person : getPersonObjects(numberOfPersons)) { - dbObjects.add(person.toDocument()); - } - - return dbObjects; - } - - private T executeWatched(WatchCallback callback) { - - watch.start(); - - try { - return callback.doInWatch(); - } finally { - watch.stop(); - } - } - - static class Person { - - ObjectId id; - String firstname, lastname; - List
addresses; - Set orders; - - public Person(String firstname, String lastname, List
addresses) { - this.firstname = firstname; - this.lastname = lastname; - this.addresses = addresses; - this.orders = new HashSet(); - } - - public static Person from(Document source) { - - List addressesSource = (List) source.get("addresses"); - List
addresses = new ArrayList
(addressesSource.size()); - for (Object addressSource : addressesSource) { - addresses.add(Address.from((Document) addressSource)); - } - - List ordersSource = (List) source.get("orders"); - Set orders = new HashSet(ordersSource.size()); - for (Object orderSource : ordersSource) { - orders.add(Order.from((Document) orderSource)); - } - - Person person = new Person((String) source.get("firstname"), (String) source.get("lastname"), addresses); - person.orders.addAll(orders); - return person; - } - - public Document toDocument() { - - Document dbObject = new Document(); - dbObject.put("firstname", firstname); - dbObject.put("lastname", lastname); - dbObject.put("addresses", writeAll(addresses)); - dbObject.put("orders", writeAll(orders)); - return dbObject; - } - } - - static class Address implements Convertible { - - final String zipCode; - final String city; - final Set types; - - public Address(String zipCode, String city) { - this(zipCode, city, new HashSet(pickRandomNumerOfItemsFrom(Arrays.asList(AddressType.values())))); - } - - @PersistenceConstructor - public Address(String zipCode, String city, Set types) { - this.zipCode = zipCode; - this.city = city; - this.types = types; - } - - public static Address from(Document source) { - String zipCode = (String) source.get("zipCode"); - String city = (String) source.get("city"); - List types = (List) source.get("types"); - - return new Address(zipCode, city, new HashSet(fromList(types, AddressType.class))); - } - - public Document toDocument() { - Document dbObject = new Document(); - dbObject.put("zipCode", zipCode); - dbObject.put("city", city); - dbObject.put("types", toList(types)); - return dbObject; - } - } - - private static > List fromList(List source, Class type) { - - List result = new ArrayList(source.size()); - for (Object object : source) { - result.add(Enum.valueOf(type, object.toString())); - } - return result; - } - - private static > List toList(Collection enums) { - List result = new ArrayList<>(); - for (T element : enums) { - result.add(element.toString()); - } - - return result; - } - - static class Order implements Convertible { - - enum Status { - ORDERED, PAYED, SHIPPED - } - - Date createdAt; - List lineItems; - Status status; - - public Order(List lineItems, Date createdAt) { - this.lineItems = lineItems; - this.createdAt = createdAt; - this.status = Status.ORDERED; - } - - @PersistenceConstructor - public Order(List lineItems, Date createdAt, Status status) { - this.lineItems = lineItems; - this.createdAt = createdAt; - this.status = status; - } - - public static Order from(Document source) { - - List lineItemsSource = (List) source.get("lineItems"); - List lineItems = new ArrayList(lineItemsSource.size()); - for (Object lineItemSource : lineItemsSource) { - lineItems.add(LineItem.from((Document) lineItemSource)); - } - - Date date = (Date) source.get("createdAt"); - Status status = Status.valueOf((String) source.get("status")); - return new Order(lineItems, date, status); - } - - public Order(List lineItems) { - this(lineItems, new Date()); - } - - public Document toDocument() { - Document result = new Document(); - result.put("createdAt", createdAt); - result.put("lineItems", writeAll(lineItems)); - result.put("status", status.toString()); - return result; - } - } - - static class LineItem implements Convertible { - - String description; - double price; - int amount; - - public LineItem(String description, int amount, double price) { - this.description = description; - this.amount = amount; - this.price = price; - } - - public static List generate() { - - LineItem iPad = new LineItem("iPad", 1, 649); - LineItem iPhone = new LineItem("iPhone", 1, 499); - LineItem macBook = new LineItem("MacBook", 2, 1299); - - return pickRandomNumerOfItemsFrom(Arrays.asList(iPad, iPhone, macBook)); - } - - public static LineItem from(Document source) { - - String description = (String) source.get("description"); - double price = (Double) source.get("price"); - int amount = (Integer) source.get("amount"); - - return new LineItem(description, amount, price); - } - - public Document toDocument() { - - Document dbObject = new Document(); - dbObject.put("description", description); - dbObject.put("price", price); - dbObject.put("amount", amount); - return dbObject; - } - } - - private static List pickRandomNumerOfItemsFrom(List source) { - - Assert.isTrue(!source.isEmpty(), "Source must not be empty!"); - - Random random = new Random(); - int numberOfItems = random.nextInt(source.size()); - numberOfItems = numberOfItems == 0 ? 1 : numberOfItems; - - List result = new ArrayList(numberOfItems); - while (result.size() < numberOfItems) { - int index = random.nextInt(source.size()); - T candidate = source.get(index); - if (!result.contains(candidate)) { - result.add(candidate); - } - } - - return result; - } - - enum AddressType { - SHIPPING, BILLING - } - - private interface WriteConcernCallback { - void doWithWriteConcern(String constantName, WriteConcern concern); - } - - private interface WatchCallback { - T doInWatch(); - } - - private interface ReactivePersonRepository extends ReactiveMongoRepository { - - Flux findByAddressesZipCodeContaining(String parameter); - } - - private interface Convertible { - - Document toDocument(); - } - - private static BasicDBList writeAll(Collection convertibles) { - BasicDBList result = new BasicDBList(); - for (Convertible convertible : convertibles) { - result.add(convertible.toDocument()); - } - return result; - } - - enum Api { - DRIVER, TEMPLATE, REPOSITORY, DIRECT, CONVERTER - } - - enum Mode { - WRITE, READ, QUERY, WRITE_ASYNC - } - - private static class Statistics { - - private final String headline; - private final Map times; - - public Statistics(String headline) { - - this.headline = headline; - this.times = new HashMap(); - - for (Mode mode : Mode.values()) { - times.put(mode, new ModeTimes(mode)); - } - } - - public void registerTime(Api api, Mode mode, double time) { - times.get(mode).add(api, time); - } - - public void printResults(int iterations) { - - String title = String.format(headline, iterations); - - System.out.println(title); - System.out.println(createUnderline(title)); - - StringBuilder builder = new StringBuilder(); - for (Mode mode : Mode.values()) { - String print = times.get(mode).print(); - if (!print.isEmpty()) { - builder.append(print).append('\n'); - } - } - - System.out.println(builder.toString()); - } - - @Override - public String toString() { - - StringBuilder builder = new StringBuilder(times.size()); - - for (ModeTimes times : this.times.values()) { - builder.append(times.toString()); - } - - return builder.toString(); - } - } - - private static String createUnderline(String input) { - - StringBuilder builder = new StringBuilder(input.length()); - - for (int i = 0; i < input.length(); i++) { - builder.append("-"); - } - - return builder.toString(); - } - - static class ApiTimes { - - private static final String TIME_TEMPLATE = "%s %s time -\tAverage: %sms%s,%sMedian: %sms%s"; - - private static final DecimalFormat TIME_FORMAT; - private static final DecimalFormat DEVIATION_FORMAT; - - static { - - TIME_FORMAT = new DecimalFormat("0.00"); - - DEVIATION_FORMAT = new DecimalFormat("0.00"); - DEVIATION_FORMAT.setPositivePrefix("+"); - } - - private final Api api; - private final Mode mode; - private final List times; - - public ApiTimes(Api api, Mode mode) { - this.api = api; - this.mode = mode; - this.times = new ArrayList(); - } - - public void add(double time) { - this.times.add(time); - } - - public boolean hasTimes() { - return !times.isEmpty(); - } - - public double getAverage() { - - double result = 0; - - for (Double time : times) { - result += time; - } - - return result == 0.0 ? 0.0 : result / times.size(); - } - - public double getMedian() { - - if (times.isEmpty()) { - return 0.0; - } - - ArrayList list = new ArrayList(times); - Collections.sort(list); - - int size = list.size(); - - if (size % 2 == 0) { - return (list.get(size / 2 - 1) + list.get(size / 2)) / 2; - } else { - return list.get(size / 2); - } - } - - private double getDeviationFrom(double otherAverage) { - - double average = getAverage(); - return average * 100 / otherAverage - 100; - } - - private double getMediaDeviationFrom(double otherMedian) { - double median = getMedian(); - return median * 100 / otherMedian - 100; - } - - public String print() { - - if (times.isEmpty()) { - return ""; - } - - return basicPrint("", "\t\t", "") + '\n'; - } - - private String basicPrint(String extension, String middle, String foo) { - return String.format(TIME_TEMPLATE, api, mode, TIME_FORMAT.format(getAverage()), extension, middle, - TIME_FORMAT.format(getMedian()), foo); - } - - public String print(double referenceAverage, double referenceMedian) { - - if (times.isEmpty()) { - return ""; - } - - return basicPrint(String.format(" %s%%", DEVIATION_FORMAT.format(getDeviationFrom(referenceAverage))), "\t", - String.format(" %s%%", DEVIATION_FORMAT.format(getMediaDeviationFrom(referenceMedian)))) + '\n'; - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return times.isEmpty() ? "" - : String.format("%s, %s: %s", api, mode, StringUtils.collectionToCommaDelimitedString(times)) + '\n'; - } - } - - static class ModeTimes { - - private final Map times; - - public ModeTimes(Mode mode) { - - this.times = new HashMap(); - - for (Api api : Api.values()) { - this.times.put(api, new ApiTimes(api, mode)); - } - } - - public void add(Api api, double time) { - times.get(api).add(time); - } - - @SuppressWarnings("null") - public String print() { - - if (times.isEmpty()) { - return ""; - } - - Double previousTime = null; - Double previousMedian = null; - StringBuilder builder = new StringBuilder(); - - for (Api api : Api.values()) { - - ApiTimes apiTimes = times.get(api); - - if (!apiTimes.hasTimes()) { - continue; - } - - if (previousTime == null) { - builder.append(apiTimes.print()); - previousTime = apiTimes.getAverage(); - previousMedian = apiTimes.getMedian(); - } else { - builder.append(apiTimes.print(previousTime, previousMedian)); - } - } - - return builder.toString(); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - - StringBuilder builder = new StringBuilder(times.size()); - - for (ApiTimes times : this.times.values()) { - builder.append(times.toString()); - } - - return builder.toString(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java deleted file mode 100644 index 74a48fc679..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java +++ /dev/null @@ -1,1425 +0,0 @@ -/* - * Copyright 2011-2021 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.repository; - -import static java.util.Arrays.*; -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.geo.Metrics.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import org.assertj.core.api.Assertions; -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Range; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.geo.Box; -import org.springframework.data.geo.Circle; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoPage; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Metric; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.geo.Polygon; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.aggregation.AggregationResults; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.Person.Sex; -import org.springframework.data.mongodb.repository.SampleEvaluationContextExtension.SampleSecurityContextHolder; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.querydsl.QSort; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.util.ReflectionTestUtils; - -/** - * Base class for tests for {@link PersonRepository}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - * @author Fırat KÜÇÜK - * @author Edward Prentice - */ -@ExtendWith(SpringExtension.class) -public abstract class AbstractPersonRepositoryIntegrationTests { - - @Autowired protected PersonRepository repository; - - @Autowired MongoOperations operations; - - Person dave, oliver, carter, boyd, stefan, leroi, alicia; - QPerson person; - - List all; - - @BeforeEach - public void setUp() throws InterruptedException { - - repository.deleteAll(); - - dave = new Person("Dave", "Matthews", 42); - oliver = new Person("Oliver August", "Matthews", 4); - carter = new Person("Carter", "Beauford", 49); - carter.setSkills(Arrays.asList("Drums", "percussion", "vocals")); - - boyd = new Person("Boyd", "Tinsley", 45); - boyd.setSkills(Arrays.asList("Violin", "Electric Violin", "Viola", "Mandolin", "Vocals", "Guitar")); - stefan = new Person("Stefan", "Lessard", 34); - leroi = new Person("Leroi", "Moore", 41); - alicia = new Person("Alicia", "Keys", 30, Sex.FEMALE); - person = new QPerson("person"); - - Arrays.asList(boyd, stefan, leroi, alicia).forEach(it -> { - it.createdAt = new Date(dave.createdAt.getTime() + 1000L); - }); - - List toSave = asList(oliver, dave, carter, boyd, stefan, leroi, alicia); - toSave.forEach(it -> it.setId(null)); - - all = repository.saveAll(toSave); - } - - @Test - void findsPersonById() { - - assertThat(repository.findById(dave.getId())).contains(dave); - } - - @Test - void findsAllMusicians() { - List result = repository.findAll(); - assertThat(result).hasSameSizeAs(all).containsAll(all); - } - - @Test - void findsAllWithGivenIds() { - - Iterable result = repository.findAllById(asList(dave.id, boyd.id)); - assertThat(result).contains(dave, boyd).doesNotContain(oliver, carter, stefan, leroi, alicia); - } - - @Test - void deletesPersonCorrectly() { - - repository.delete(dave); - - List result = repository.findAll(); - - assertThat(result).hasSize(all.size() - 1).doesNotContain(dave); - } - - @Test - void deletesPersonByIdCorrectly() { - - repository.deleteById(dave.getId()); - - List result = repository.findAll(); - - assertThat(result).hasSize(all.size() - 1).doesNotContain(dave); - } - - @Test - void findsPersonsByLastname() { - - List result = repository.findByLastname("Beauford"); - assertThat(result).hasSize(1).contains(carter); - } - - @Test - void findsPersonsByFirstname() { - - List result = repository.findByThePersonsFirstname("Leroi"); - assertThat(result).hasSize(1).contains(leroi); - assertThat(result.get(0).getAge()).isNull(); - } - - @Test - void findsPersonsByFirstnameLike() { - - List result = repository.findByFirstnameLike("Bo*"); - assertThat(result).hasSize(1).contains(boyd); - } - - @Test // DATAMONGO-1608 - void findByFirstnameLikeWithNull() { - - assertThatIllegalArgumentException().isThrownBy(() -> repository.findByFirstnameLike(null)); - } - - @Test - void findsPagedPersons() { - - Page result = repository.findAll(PageRequest.of(1, 2, Direction.ASC, "lastname", "firstname")); - assertThat(result.isFirst()).isFalse(); - assertThat(result.isLast()).isFalse(); - assertThat(result).contains(dave, stefan); - } - - @Test - void executesPagedFinderCorrectly() { - - Page page = repository.findByLastnameLike("*a*", - PageRequest.of(0, 2, Direction.ASC, "lastname", "firstname")); - assertThat(page.isFirst()).isTrue(); - assertThat(page.isLast()).isFalse(); - assertThat(page.getNumberOfElements()).isEqualTo(2); - assertThat(page).contains(carter, stefan); - } - - @Test - void executesPagedFinderWithAnnotatedQueryCorrectly() { - - Page page = repository.findByLastnameLikeWithPageable(".*a.*", - PageRequest.of(0, 2, Direction.ASC, "lastname", "firstname")); - assertThat(page.isFirst()).isTrue(); - assertThat(page.isLast()).isFalse(); - assertThat(page.getNumberOfElements()).isEqualTo(2); - assertThat(page).contains(carter, stefan); - } - - @Test - void findsPersonInAgeRangeCorrectly() { - - List result = repository.findByAgeBetween(40, 45); - assertThat(result).hasSize(2).contains(dave, leroi); - } - - @Test - void findsPersonByShippingAddressesCorrectly() { - - Address address = new Address("Foo Street 1", "C0123", "Bar"); - dave.setShippingAddresses(new HashSet
(asList(address))); - - repository.save(dave); - assertThat(repository.findByShippingAddresses(address)).isEqualTo(dave); - } - - @Test - void findsPersonByAddressCorrectly() { - - Address address = new Address("Foo Street 1", "C0123", "Bar"); - dave.setAddress(address); - repository.save(dave); - - List result = repository.findByAddress(address); - assertThat(result).hasSize(1).contains(dave); - } - - @Test - void findsPeopleByZipCode() { - - Address address = new Address("Foo Street 1", "C0123", "Bar"); - dave.setAddress(address); - repository.save(dave); - - List result = repository.findByAddressZipCode(address.getZipCode()); - assertThat(result).hasSize(1).contains(dave); - } - - @Test - void findsPeopleByQueryDslLastnameSpec() { - - Iterable result = repository.findAll(person.lastname.eq("Matthews")); - assertThat(result).contains(dave).doesNotContain(carter, boyd, stefan, leroi, alicia); - } - - @Test - void findsPeopleByzipCodePredicate() { - - Address address = new Address("Foo Street 1", "C0123", "Bar"); - dave.setAddress(address); - repository.save(dave); - - Iterable result = repository.findAll(person.address.zipCode.eq("C0123")); - assertThat(result).contains(dave).doesNotContain(carter, boyd, stefan, leroi, alicia); - } - - @Test - void findsPeopleByLocationNear() { - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave); - - List result = repository.findByLocationNear(point); - assertThat(result).hasSize(1).contains(dave); - } - - @Test // DATAMONGO-1588 - void findsPeopleByLocationNearUsingGeoJsonType() { - - GeoJsonPoint point = new GeoJsonPoint(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave); - - List result = repository.findByLocationNear(point); - assertThat(result).hasSize(1).contains(dave); - } - - @Test - void findsPeopleByLocationWithinCircle() { - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave); - - List result = repository.findByLocationWithin(new Circle(-78.99171, 45.738868, 170)); - assertThat(result).hasSize(1).contains(dave); - } - - @Test - void findsPeopleByLocationWithinBox() { - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave); - - Box box = new Box(new Point(-78.99171, 35.738868), new Point(-68.99171, 45.738868)); - - List result = repository.findByLocationWithin(box); - assertThat(result).hasSize(1).contains(dave); - } - - @Test - void findsPeopleByLocationWithinPolygon() { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave); - - Point first = new Point(-78.99171, 35.738868); - Point second = new Point(-78.99171, 45.738868); - Point third = new Point(-68.99171, 45.738868); - Point fourth = new Point(-68.99171, 35.738868); - - List result = repository.findByLocationWithin(new Polygon(first, second, third, fourth)); - assertThat(result).hasSize(1).contains(dave); - } - - @Test - void findsPagedPeopleByPredicate() { - - Page page = repository.findAll(person.lastname.contains("a"), - PageRequest.of(0, 2, Direction.ASC, "lastname")); - assertThat(page.isFirst()).isTrue(); - assertThat(page.isLast()).isFalse(); - assertThat(page.getNumberOfElements()).isEqualTo(2); - assertThat(page.getTotalElements()).isEqualTo(4L); - assertThat(page).contains(carter, stefan); - } - - @Test // DATADOC-136 - void findsPeopleBySexCorrectly() { - - List females = repository.findBySex(Sex.FEMALE); - assertThat(females).hasSize(1); - assertThat(females.get(0)).isEqualTo(alicia); - } - - @Test // DATAMONGO-446 - void findsPeopleBySexPaginated() { - - List males = repository.findBySex(Sex.MALE, PageRequest.of(0, 2)); - assertThat(males).hasSize(2); - } - - @Test - void findsPeopleByNamedQuery() { - List result = repository.findByNamedQuery("Dave"); - assertThat(result).hasSize(1).contains(dave); - } - - @Test // DATADOC-190 - void existsWorksCorrectly() { - assertThat(repository.existsById(dave.getId())).isTrue(); - } - - @Test - void rejectsDuplicateEmailAddressOnSave() { - - assertThat(dave.getEmail()).isEqualTo("dave@dmband.com"); - - Person daveSyer = new Person("Dave", "Syer"); - assertThat(daveSyer.getEmail()).isEqualTo("dave@dmband.com"); - - Assertions.assertThatExceptionOfType(DuplicateKeyException.class).isThrownBy(() -> repository.save(daveSyer)); - } - - @Test // DATADOC-236 - void findsPeopleByLastnameAndOrdersCorrectly() { - List result = repository.findByLastnameOrderByFirstnameAsc("Matthews"); - assertThat(result).hasSize(2); - assertThat(result.get(0)).isEqualTo(dave); - assertThat(result.get(1)).isEqualTo(oliver); - } - - @Test // DATADOC-236 - void appliesStaticAndDynamicSorting() { - List result = repository.findByFirstnameLikeOrderByLastnameAsc("*e*", Sort.by("age")); - assertThat(result).hasSize(5); - assertThat(result.get(0)).isEqualTo(carter); - assertThat(result.get(1)).isEqualTo(stefan); - assertThat(result.get(2)).isEqualTo(oliver); - assertThat(result.get(3)).isEqualTo(dave); - assertThat(result.get(4)).isEqualTo(leroi); - } - - @Test - void executesGeoNearQueryForResultsCorrectly() { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave); - - GeoResults results = repository.findByLocationNear(new Point(-73.99, 40.73), - new Distance(2000, Metrics.KILOMETERS)); - assertThat(results.getContent()).isNotEmpty(); - } - - @Test - void executesGeoPageQueryForResultsCorrectly() { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave); - - GeoPage results = repository.findByLocationNear(new Point(-73.99, 40.73), - new Distance(2000, Metrics.KILOMETERS), PageRequest.of(0, 20)); - assertThat(results.getContent()).isNotEmpty(); - - // DATAMONGO-607 - assertThat(results.getAverageDistance().getMetric()).isEqualTo((Metric) Metrics.KILOMETERS); - } - - @Test // DATAMONGO-323 - void considersSortForAnnotatedQuery() { - - List result = repository.findByAgeLessThan(60, Sort.by("firstname")); - - assertThat(result).hasSize(7); - assertThat(result.get(0)).isEqualTo(alicia); - assertThat(result.get(1)).isEqualTo(boyd); - assertThat(result.get(2)).isEqualTo(carter); - assertThat(result.get(3)).isEqualTo(dave); - assertThat(result.get(4)).isEqualTo(leroi); - assertThat(result.get(5)).isEqualTo(oliver); - assertThat(result.get(6)).isEqualTo(stefan); - } - - @Test // DATAMONGO-347 - void executesQueryWithDBRefReferenceCorrectly() { - - operations.remove(new Query(), User.class); - - User user = new User(); - user.username = "Oliver"; - - operations.save(user); - - dave.creator = user; - repository.save(dave); - - List result = repository.findByCreator(user); - assertThat(result).hasSize(1).contains(dave); - } - - @Test // DATAMONGO-425 - void bindsDateParameterForLessThanPredicateCorrectly() { - - List result = repository.findByCreatedAtLessThan(boyd.createdAt); - assertThat(result).hasSize(3).contains(dave, oliver, carter); - } - - @Test // DATAMONGO-425 - void bindsDateParameterForGreaterThanPredicateCorrectly() { - - List result = repository.findByCreatedAtGreaterThan(carter.createdAt); - assertThat(result).hasSize(4).contains(boyd, stefan, leroi, alicia); - } - - @Test // DATAMONGO-427 - void bindsDateParameterToBeforePredicateCorrectly() { - - List result = repository.findByCreatedAtBefore(boyd.createdAt); - assertThat(result).hasSize(3).contains(dave, oliver, carter); - } - - @Test // DATAMONGO-427 - void bindsDateParameterForAfterPredicateCorrectly() { - - List result = repository.findByCreatedAtAfter(carter.createdAt); - assertThat(result).hasSize(4).contains(boyd, stefan, leroi, alicia); - } - - @Test // DATAMONGO-425 - void bindsDateParameterForManuallyDefinedQueryCorrectly() { - - List result = repository.findByCreatedAtLessThanManually(boyd.createdAt); - assertThat(result).isNotEmpty(); - } - - @Test // DATAMONGO-472 - void findsPeopleUsingNotPredicate() { - - List result = repository.findByLastnameNot("Matthews"); - assertThat(result).doesNotContain(dave).hasSize(5); - } - - @Test // DATAMONGO-521 - void executesAndQueryCorrectly() { - - List result = repository.findByFirstnameAndLastname("Dave", "Matthews"); - - assertThat(result).hasSize(1).contains(dave); - - result = repository.findByFirstnameAndLastname("Oliver August", "Matthews"); - - assertThat(result).hasSize(1).contains(oliver); - } - - @Test // DATAMONGO-600 - void readsDocumentsWithNestedPolymorphismCorrectly() { - - UsernameAndPassword usernameAndPassword = new UsernameAndPassword(); - usernameAndPassword.username = "dave"; - usernameAndPassword.password = "btcs"; - - dave.credentials = usernameAndPassword; - - repository.save(dave); - - List result = repository.findByCredentials(usernameAndPassword); - assertThat(result).hasSize(1).contains(dave); - } - - @Test // DATAMONGO-636 - void executesDerivedCountProjection() { - assertThat(repository.countByLastname("Matthews")).isEqualTo(2L); - } - - @Test // DATAMONGO-636 - void executesDerivedCountProjectionToInt() { - assertThat(repository.countByFirstname("Oliver August")).isEqualTo(1); - } - - @Test // DATAMONGO-636 - void executesAnnotatedCountProjection() { - assertThat(repository.someCountQuery("Matthews")).isEqualTo(2L); - } - - @Test // DATAMONGO-1454 - void executesDerivedExistsProjectionToBoolean() { - - assertThat(repository.existsByFirstname("Oliver August")).isTrue(); - assertThat(repository.existsByFirstname("Hans Peter")).isFalse(); - } - - @Test // DATAMONGO-1454 - void executesAnnotatedExistProjection() { - assertThat(repository.someExistQuery("Matthews")).isTrue(); - } - - @Test // DATAMONGO-701 - void executesDerivedStartsWithQueryCorrectly() { - - List result = repository.findByLastnameStartsWith("Matt"); - assertThat(result).hasSize(2).contains(dave, oliver); - } - - @Test // DATAMONGO-701 - void executesDerivedEndsWithQueryCorrectly() { - - List result = repository.findByLastnameEndsWith("thews"); - assertThat(result).hasSize(2).contains(dave, oliver); - } - - @Test // DATAMONGO-445 - void executesGeoPageQueryForWithPageRequestForPageInBetween() { - - Point farAway = new Point(-73.9, 40.7); - Point here = new Point(-73.99, 40.73); - - dave.setLocation(farAway); - oliver.setLocation(here); - carter.setLocation(here); - boyd.setLocation(here); - leroi.setLocation(here); - - repository.saveAll(Arrays.asList(dave, oliver, carter, boyd, leroi)); - - GeoPage results = repository.findByLocationNear(new Point(-73.99, 40.73), - new Distance(2000, Metrics.KILOMETERS), PageRequest.of(1, 2)); - - assertThat(results.getContent()).isNotEmpty(); - assertThat(results.getNumberOfElements()).isEqualTo(2); - assertThat(results.isFirst()).isFalse(); - assertThat(results.isLast()).isFalse(); - assertThat(results.getAverageDistance().getMetric()).isEqualTo((Metric) Metrics.KILOMETERS); - assertThat(results.getAverageDistance().getNormalizedValue()).isEqualTo(0.0); - } - - @Test // DATAMONGO-445 - void executesGeoPageQueryForWithPageRequestForPageAtTheEnd() { - - Point point = new Point(-73.99171, 40.738868); - - dave.setLocation(point); - oliver.setLocation(point); - carter.setLocation(point); - - repository.saveAll(Arrays.asList(dave, oliver, carter)); - - GeoPage results = repository.findByLocationNear(new Point(-73.99, 40.73), - new Distance(2000, Metrics.KILOMETERS), PageRequest.of(1, 2)); - assertThat(results.getContent()).isNotEmpty(); - assertThat(results.getNumberOfElements()).isEqualTo(1); - assertThat(results.isFirst()).isFalse(); - assertThat(results.isLast()).isTrue(); - assertThat(results.getAverageDistance().getMetric()).isEqualTo((Metric) Metrics.KILOMETERS); - } - - @Test // DATAMONGO-445 - void executesGeoPageQueryForWithPageRequestForJustOneElement() { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave); - - GeoPage results = repository.findByLocationNear(new Point(-73.99, 40.73), - new Distance(2000, Metrics.KILOMETERS), PageRequest.of(0, 2)); - - assertThat(results.getContent()).isNotEmpty(); - assertThat(results.getNumberOfElements()).isEqualTo(1); - assertThat(results.isFirst()).isTrue(); - assertThat(results.isLast()).isTrue(); - assertThat(results.getAverageDistance().getMetric()).isEqualTo((Metric) Metrics.KILOMETERS); - } - - @Test // DATAMONGO-445 - void executesGeoPageQueryForWithPageRequestForJustOneElementEmptyPage() { - - dave.setLocation(new Point(-73.99171, 40.738868)); - repository.save(dave); - - GeoPage results = repository.findByLocationNear(new Point(-73.99, 40.73), - new Distance(2000, Metrics.KILOMETERS), PageRequest.of(1, 2)); - - assertThat(results.getContent()).isEmpty(); - assertThat(results.getNumberOfElements()).isEqualTo(0); - assertThat(results.isFirst()).isFalse(); - assertThat(results.isLast()).isTrue(); - assertThat(results.getAverageDistance().getMetric()).isEqualTo((Metric) Metrics.KILOMETERS); - } - - @Test // DATAMONGO-1608 - void findByFirstNameIgnoreCaseWithNull() { - - assertThatIllegalArgumentException().isThrownBy(() -> repository.findByFirstnameIgnoreCase(null)); - } - - @Test // DATAMONGO-770 - void findByFirstNameIgnoreCase() { - - List result = repository.findByFirstnameIgnoreCase("dave"); - - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(dave); - } - - @Test // DATAMONGO-770 - void findByFirstnameNotIgnoreCase() { - - List result = repository.findByFirstnameNotIgnoreCase("dave"); - - assertThat(result).hasSize(6).doesNotContain(dave); - } - - @Test // DATAMONGO-770 - void findByFirstnameStartingWithIgnoreCase() { - - List result = repository.findByFirstnameStartingWithIgnoreCase("da"); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(dave); - } - - @Test // DATAMONGO-770 - void findByFirstnameEndingWithIgnoreCase() { - - List result = repository.findByFirstnameEndingWithIgnoreCase("VE"); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(dave); - } - - @Test // DATAMONGO-770 - void findByFirstnameContainingIgnoreCase() { - - List result = repository.findByFirstnameContainingIgnoreCase("AV"); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(dave); - } - - @Test // DATAMONGO-870 - void findsSliceOfPersons() { - - Slice result = repository.findByAgeGreaterThan(40, PageRequest.of(0, 2, Direction.DESC, "firstname")); - - assertThat(result.hasNext()).isTrue(); - } - - @Test // DATAMONGO-871 - void findsPersonsByFirstnameAsArray() { - - Person[] result = repository.findByThePersonsFirstnameAsArray("Leroi"); - - assertThat(result).hasSize(1).containsExactly(leroi); - } - - @Test // DATAMONGO-821 - void findUsingAnnotatedQueryOnDBRef() { - - operations.remove(new org.springframework.data.mongodb.core.query.Query(), User.class); - - User user = new User(); - user.username = "Terria"; - operations.save(user); - - alicia.creator = user; - repository.save(alicia); - - Page result = repository.findByHavingCreator(PageRequest.of(0, 100)); - - assertThat(result.getNumberOfElements()).isEqualTo(1); - assertThat(result.getContent().get(0)).isEqualTo(alicia); - } - - @Test // DATAMONGO-566 - void deleteByShouldReturnListOfDeletedElementsWhenRetunTypeIsCollectionLike() { - - List result = repository.deleteByLastname("Beauford"); - - assertThat(result).contains(carter).hasSize(1); - } - - @Test // DATAMONGO-566 - void deleteByShouldRemoveElementsMatchingDerivedQuery() { - - repository.deleteByLastname("Beauford"); - - assertThat(operations.count(new BasicQuery("{'lastname':'Beauford'}"), Person.class)).isEqualTo(0L); - } - - @Test // DATAMONGO-566 - void deleteByShouldReturnNumberOfDocumentsRemovedIfReturnTypeIsLong() { - assertThat(repository.deletePersonByLastname("Beauford")).isEqualTo(1L); - } - - @Test // DATAMONGO-1997 - void deleteByShouldResultWrappedInOptionalCorrectly() { - - assertThat(repository.deleteOptionalByLastname("Beauford")).isPresent(); - assertThat(repository.deleteOptionalByLastname("dorfuaeB")).isNotPresent(); - } - - @Test // DATAMONGO-566 - void deleteByShouldReturnZeroInCaseNoDocumentHasBeenRemovedAndReturnTypeIsNumber() { - assertThat(repository.deletePersonByLastname("dorfuaeB")).isEqualTo(0L); - } - - @Test // DATAMONGO-566 - void deleteByShouldReturnEmptyListInCaseNoDocumentHasBeenRemovedAndReturnTypeIsCollectionLike() { - assertThat(repository.deleteByLastname("dorfuaeB")).isEmpty(); - } - - @Test // DATAMONGO-566 - void deleteByUsingAnnotatedQueryShouldReturnListOfDeletedElementsWhenRetunTypeIsCollectionLike() { - - List result = repository.removeByLastnameUsingAnnotatedQuery("Beauford"); - - assertThat(result).contains(carter).hasSize(1); - } - - @Test // DATAMONGO-566 - void deleteByUsingAnnotatedQueryShouldRemoveElementsMatchingDerivedQuery() { - - repository.removeByLastnameUsingAnnotatedQuery("Beauford"); - - assertThat(operations.count(new BasicQuery("{'lastname':'Beauford'}"), Person.class)).isEqualTo(0L); - } - - @Test // DATAMONGO-566 - void deleteByUsingAnnotatedQueryShouldReturnNumberOfDocumentsRemovedIfReturnTypeIsLong() { - assertThat(repository.removePersonByLastnameUsingAnnotatedQuery("Beauford")).isEqualTo(1L); - } - - @Test // DATAMONGO-893 - void findByNestedPropertyInCollectionShouldFindMatchingDocuments() { - - Person p = new Person("Mary", "Poppins"); - Address adr = new Address("some", "2", "where"); - p.setAddress(adr); - - repository.save(p); - - Page result = repository.findByAddressIn(Arrays.asList(adr), PageRequest.of(0, 10)); - - assertThat(result.getContent()).hasSize(1); - } - - @Test // DATAMONGO-745 - void findByCustomQueryFirstnamesInListAndLastname() { - - repository.save(new Person("foo", "bar")); - repository.save(new Person("bar", "bar")); - repository.save(new Person("fuu", "bar")); - repository.save(new Person("notfound", "bar")); - - Page result = repository.findByCustomQueryFirstnamesAndLastname(Arrays.asList("bar", "foo", "fuu"), "bar", - PageRequest.of(0, 2)); - - assertThat(result.getContent()).hasSize(2); - assertThat(result.getTotalPages()).isEqualTo(2); - assertThat(result.getTotalElements()).isEqualTo(3L); - } - - @Test // DATAMONGO-745 - void findByCustomQueryLastnameAndStreetInList() { - - repository.save(new Person("foo", "bar").withAddress(new Address("street1", "1", "SB"))); - repository.save(new Person("bar", "bar").withAddress(new Address("street2", "1", "SB"))); - repository.save(new Person("fuu", "bar").withAddress(new Address("street1", "2", "RGB"))); - repository.save(new Person("notfound", "notfound")); - - Page result = repository.findByCustomQueryLastnameAndAddressStreetInList("bar", - Arrays.asList("street1", "street2"), PageRequest.of(0, 2)); - - assertThat(result.getContent()).hasSize(2); - assertThat(result.getTotalPages()).isEqualTo(2); - assertThat(result.getTotalElements()).isEqualTo(3L); - - } - - @Test // DATAMONGO-950 - void shouldLimitCollectionQueryToMaxResultsWhenPresent() { - - repository.saveAll(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), - new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan"))); - List result = repository.findTop3ByLastnameStartingWith("Dylan"); - assertThat(result).hasSize(3); - } - - @Test // DATAMONGO-950, DATAMONGO-1464 - void shouldNotLimitPagedQueryWhenPageRequestWithinBounds() { - - repository.saveAll(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), - new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan"))); - Page result = repository.findTop3ByLastnameStartingWith("Dylan", PageRequest.of(0, 2)); - assertThat(result.getContent()).hasSize(2); - assertThat(result.getTotalElements()).isEqualTo(3L); - } - - @Test // DATAMONGO-950 - void shouldLimitPagedQueryWhenPageRequestExceedsUpperBoundary() { - - repository.saveAll(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), - new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan"))); - Page result = repository.findTop3ByLastnameStartingWith("Dylan", PageRequest.of(1, 2)); - assertThat(result.getContent()).hasSize(1); - } - - @Test // DATAMONGO-950, DATAMONGO-1464 - void shouldReturnEmptyWhenPageRequestedPageIsTotallyOutOfScopeForLimit() { - - repository.saveAll(Arrays.asList(new Person("Bob-1", "Dylan"), new Person("Bob-2", "Dylan"), - new Person("Bob-3", "Dylan"), new Person("Bob-4", "Dylan"), new Person("Bob-5", "Dylan"))); - Page result = repository.findTop3ByLastnameStartingWith("Dylan", PageRequest.of(100, 2)); - assertThat(result.getContent()).isEmpty(); - assertThat(result.getTotalElements()).isEqualTo(3L); - } - - @Test // DATAMONGO-996, DATAMONGO-950, DATAMONGO-1464 - void gettingNonFirstPageWorksWithoutLimitBeingSet() { - - Page slice = repository.findByLastnameLike("Matthews", PageRequest.of(1, 1)); - - assertThat(slice.getContent()).hasSize(1); - assertThat(slice.hasPrevious()).isTrue(); - assertThat(slice.hasNext()).isFalse(); - assertThat(slice.getTotalElements()).isEqualTo(2L); - } - - @Test // DATAMONGO-972 - void shouldExecuteFindOnDbRefCorrectly() { - - operations.remove(new org.springframework.data.mongodb.core.query.Query(), User.class); - - User user = new User(); - user.setUsername("Valerie Matthews"); - - operations.save(user); - - dave.setCreator(user); - operations.save(dave); - - assertThat(repository.findOne(QPerson.person.creator.eq(user)).get()).isEqualTo(dave); - } - - @Test // DATAMONGO-969 - void shouldFindPersonsWhenUsingQueryDslPerdicatedOnIdProperty() { - assertThat(repository.findAll(person.id.in(Arrays.asList(dave.id, carter.id)))).contains(dave, carter); - } - - @Test // DATAMONGO-1030 - void executesSingleEntityQueryWithProjectionCorrectly() { - - PersonSummaryDto result = repository.findSummaryByLastname("Beauford"); - - assertThat(result).isNotNull(); - assertThat(result.firstname).isEqualTo("Carter"); - assertThat(result.lastname).isEqualTo("Beauford"); - } - - @Test // DATAMONGO-1057 - void sliceShouldTraverseElementsWithoutSkippingOnes() { - - repository.deleteAll(); - - List persons = new ArrayList(100); - for (int i = 0; i < 100; i++) { - // format firstname to assert sorting retains proper order - persons.add(new Person(String.format("%03d", i), "ln" + 1, 100)); - } - - repository.saveAll(persons); - - Slice slice = repository.findByAgeGreaterThan(50, PageRequest.of(0, 20, Direction.ASC, "firstname")); - assertThat(slice).containsExactlyElementsOf(persons.subList(0, 20)); - - slice = repository.findByAgeGreaterThan(50, slice.nextPageable()); - assertThat(slice).containsExactlyElementsOf(persons.subList(20, 40)); - } - - @Test // DATAMONGO-1072 - void shouldBindPlaceholdersUsedAsKeysCorrectly() { - - List persons = repository.findByKeyValue("firstname", alicia.getFirstname()); - - assertThat(persons).hasSize(1).contains(alicia); - } - - @Test // DATAMONGO-1105 - void returnsOrderedResultsForQuerydslOrderSpecifier() { - - Iterable result = repository.findAll(person.firstname.asc()); - - assertThat(result).containsExactly(alicia, boyd, carter, dave, leroi, oliver, stefan); - } - - @Test // DATAMONGO-1085 - void shouldSupportSortingByQueryDslOrderSpecifier() { - - repository.deleteAll(); - - List persons = new ArrayList(); - - for (int i = 0; i < 3; i++) { - Person person = new Person(String.format("Siggi %s", i), "Bar", 30); - person.setAddress(new Address(String.format("Street %s", i), "12345", "SinCity")); - persons.add(person); - } - - repository.saveAll(persons); - - QPerson person = QPerson.person; - - Iterable result = repository.findAll(person.firstname.isNotNull(), person.address.street.desc()); - - assertThat(result).hasSize(persons.size()); - assertThat(result.iterator().next().getFirstname()).isEqualTo(persons.get(2).getFirstname()); - } - - @Test // DATAMONGO-1085 - void shouldSupportSortingWithQSortByQueryDslOrderSpecifier() { - - repository.deleteAll(); - - List persons = new ArrayList(); - - for (int i = 0; i < 3; i++) { - Person person = new Person(String.format("Siggi %s", i), "Bar", 30); - person.setAddress(new Address(String.format("Street %s", i), "12345", "SinCity")); - persons.add(person); - } - - repository.saveAll(persons); - - PageRequest pageRequest = PageRequest.of(0, 2, new QSort(person.address.street.desc())); - Iterable result = repository.findAll(pageRequest); - - assertThat(result).hasSize(2); - assertThat(result.iterator().next().getFirstname()).isEqualTo("Siggi 2"); - } - - @Test // DATAMONGO-1085 - void shouldSupportSortingWithQSort() { - - repository.deleteAll(); - - List persons = new ArrayList(); - - for (int i = 0; i < 3; i++) { - Person person = new Person(String.format("Siggi %s", i), "Bar", 30); - person.setAddress(new Address(String.format("Street %s", i), "12345", "SinCity")); - persons.add(person); - } - - repository.saveAll(persons); - - Iterable result = repository.findAll(new QSort(person.address.street.desc())); - - assertThat(result).hasSize(persons.size()); - assertThat(result.iterator().next().getFirstname()).isEqualTo("Siggi 2"); - } - - @Test // DATAMONGO-1165 - void shouldAllowReturningJava8StreamInCustomQuery() { - - Stream result = repository.findByCustomQueryWithStreamingCursorByFirstnames(Arrays.asList("Dave")); - - try { - assertThat(result.collect(Collectors. toList())).contains(dave); - } finally { - result.close(); - } - } - - @Test // DATAMONGO-1110 - void executesGeoNearQueryForResultsCorrectlyWhenGivenMinAndMaxDistance() { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave); - - Range range = Distance.between(new Distance(0.01, KILOMETERS), new Distance(2000, KILOMETERS)); - - GeoResults results = repository.findPersonByLocationNear(new Point(-73.99, 40.73), range); - assertThat(results.getContent()).isNotEmpty(); - } - - @Test // DATAMONGO-990 - void shouldFindByFirstnameForSpELExpressionWithParameterIndexOnly() { - - List users = repository.findWithSpelByFirstnameForSpELExpressionWithParameterIndexOnly("Dave"); - - assertThat(users).hasSize(1); - assertThat(users.get(0)).isEqualTo(dave); - } - - @Test // DATAMONGO-990 - void shouldFindByFirstnameAndCurrentUserWithCustomQuery() { - - SampleSecurityContextHolder.getCurrent().setPrincipal(dave); - List users = repository.findWithSpelByFirstnameAndCurrentUserWithCustomQuery("Dave"); - - assertThat(users).hasSize(1); - assertThat(users.get(0)).isEqualTo(dave); - } - - @Test // DATAMONGO-990 - void shouldFindByFirstnameForSpELExpressionWithParameterVariableOnly() { - - List users = repository.findWithSpelByFirstnameForSpELExpressionWithParameterVariableOnly("Dave"); - - assertThat(users).hasSize(1); - assertThat(users.get(0)).isEqualTo(dave); - } - - @Test // DATAMONGO-1911 - void findByUUIDShouldReturnCorrectResult() { - - dave.setUniqueId(UUID.randomUUID()); - repository.save(dave); - - Person dave = repository.findByUniqueId(this.dave.getUniqueId()); - - assertThat(dave).isEqualTo(dave); - } - - @Test // DATAMONGO-1245 - void findByExampleShouldResolveStuffCorrectly() { - - Person sample = new Person(); - sample.setLastname("Matthews"); - - // needed to tweak stuff a bit since some field are automatically set - so we need to undo this - ReflectionTestUtils.setField(sample, "id", null); - ReflectionTestUtils.setField(sample, "createdAt", null); - ReflectionTestUtils.setField(sample, "email", null); - - Page result = repository.findAll(Example.of(sample), PageRequest.of(0, 10)); - assertThat(result.getNumberOfElements()).isEqualTo(2); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldResolveStuffCorrectly() { - - Person sample = new Person(); - sample.setLastname("Matthews"); - - // needed to tweak stuff a bit since some field are automatically set - so we need to undo this - ReflectionTestUtils.setField(sample, "id", null); - ReflectionTestUtils.setField(sample, "createdAt", null); - ReflectionTestUtils.setField(sample, "email", null); - - List result = repository.findAll(Example.of(sample)); - assertThat(result).hasSize(2); - } - - @Test // DATAMONGO-1425 - void findsPersonsByFirstnameNotContains() { - - List result = repository.findByFirstnameNotContains("Boyd"); - assertThat(result).hasSize((int) (repository.count() - 1)); - assertThat(result).doesNotContain(boyd); - } - - @Test // DATAMONGO-1425 - void findBySkillsContains() { - - List result = repository.findBySkillsContains(asList("Drums")); - assertThat(result).hasSize(1).contains(carter); - } - - @Test // DATAMONGO-1425 - void findBySkillsNotContains() { - - List result = repository.findBySkillsNotContains(Arrays.asList("Drums")); - assertThat(result).hasSize((int) (repository.count() - 1)); - assertThat(result).doesNotContain(carter); - } - - @Test // DATAMONGO-1424 - void findsPersonsByFirstnameNotLike() { - - List result = repository.findByFirstnameNotLike("Bo*"); - assertThat(result).hasSize((int) (repository.count() - 1)); - assertThat(result).doesNotContain(boyd); - } - - @Test // DATAMONGO-1539 - void countsPersonsByFirstname() { - assertThat(repository.countByThePersonsFirstname("Dave")).isEqualTo(1L); - } - - @Test // DATAMONGO-1539 - void deletesPersonsByFirstname() { - - repository.deleteByThePersonsFirstname("Dave"); - - assertThat(repository.countByThePersonsFirstname("Dave")).isEqualTo(0L); - } - - @Test // DATAMONGO-1752 - void readsOpenProjection() { - assertThat(repository.findOpenProjectionBy()).isNotEmpty(); - } - - @Test // DATAMONGO-1752 - void readsClosedProjection() { - assertThat(repository.findClosedProjectionBy()).isNotEmpty(); - } - - @Test // DATAMONGO-1865 - void findFirstEntityReturnsFirstResultEvenForNonUniqueMatches() { - assertThat(repository.findFirstBy()).isNotNull(); - } - - @Test // DATAMONGO-1865 - void findSingleEntityThrowsErrorWhenNotUnique() { - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) - .isThrownBy(() -> repository.findPersonByLastnameLike(dave.getLastname())); - } - - @Test // DATAMONGO-1865 - void findOptionalSingleEntityThrowsErrorWhenNotUnique() { - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) - .isThrownBy(() -> repository.findOptionalPersonByLastnameLike(dave.getLastname())); - } - - @Test // DATAMONGO-1979 - void findAppliesAnnotatedSort() { - assertThat(repository.findByAgeGreaterThan(40)).containsExactly(carter, boyd, dave, leroi); - } - - @Test // DATAMONGO-1979 - void findWithSortOverwritesAnnotatedSort() { - assertThat(repository.findByAgeGreaterThan(40, Sort.by(Direction.ASC, "age"))).containsExactly(leroi, dave, boyd, - carter); - } - - @Test // DATAMONGO-2003 - void findByRegexWithPattern() { - assertThat(repository.findByFirstnameRegex(Pattern.compile(alicia.getFirstname()))).hasSize(1); - } - - @Test // DATAMONGO-2003 - void findByRegexWithPatternAndOptions() { - - String fn = alicia.getFirstname().toUpperCase(); - - assertThat(repository.findByFirstnameRegex(Pattern.compile(fn))).hasSize(0); - assertThat(repository.findByFirstnameRegex(Pattern.compile(fn, Pattern.CASE_INSENSITIVE))).hasSize(1); - } - - @Test // DATAMONGO-2149 - void annotatedQueryShouldAllowSliceInFieldsProjectionWithDbRef() { - - operations.remove(new Query(), User.class); - - List users = IntStream.range(0, 10).mapToObj(it -> { - - User user = new User(); - user.id = "id" + it; - user.username = "user" + it; - - return user; - }).collect(Collectors.toList()); - - users.forEach(operations::save); - - alicia.fans = new ArrayList<>(users); - operations.save(alicia); - - Person target = repository.findWithSliceInProjection(alicia.getId(), 0, 5); - assertThat(target.getFans()).hasSize(5); - } - - @Test // DATAMONGO-2149 - void annotatedQueryShouldAllowPositionalParameterInFieldsProjection() { - - Set
addressList = IntStream.range(0, 10).mapToObj(it -> new Address("street-" + it, "zip", "lnz")) - .collect(Collectors.toSet()); - - alicia.setShippingAddresses(addressList); - operations.save(alicia); - - Person target = repository.findWithArrayPositionInProjection(1); - - assertThat(target).isNotNull(); - assertThat(target.getShippingAddresses()).hasSize(1); - } - - @Test // DATAMONGO-2149, DATAMONGO-2154, DATAMONGO-2199 - void annotatedQueryShouldAllowPositionalParameterInFieldsProjectionWithDbRef() { - - List userList = IntStream.range(0, 10).mapToObj(it -> { - - User user = new User(); - user.id = "" + it; - user.username = "user" + it; - - return user; - }).collect(Collectors.toList()); - - userList.forEach(operations::save); - - alicia.setFans(userList); - operations.save(alicia); - - Person target = repository.findWithArrayPositionInProjectionWithDbRef(1); - - assertThat(target).isNotNull(); - assertThat(target.getFans()).hasSize(1); - } - - @Test // DATAMONGO-2153 - void findListOfSingleValue() { - - assertThat(repository.findAllLastnames()) // - .contains("Lessard") // - .contains("Keys") // - .contains("Tinsley") // - .contains("Beauford") // - .contains("Moore") // - .contains("Matthews"); // - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithPlaceholderValue() { - - assertThat(repository.groupByLastnameAnd("firstname")) - .contains(new PersonAggregate("Lessard", Collections.singletonList("Stefan"))) // - .contains(new PersonAggregate("Keys", Collections.singletonList("Alicia"))) // - .contains(new PersonAggregate("Tinsley", Collections.singletonList("Boyd"))) // - .contains(new PersonAggregate("Beauford", Collections.singletonList("Carter"))) // - .contains(new PersonAggregate("Moore", Collections.singletonList("Leroi"))) // - .contains(new PersonAggregate("Matthews", Arrays.asList("Dave", "Oliver August"))); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithSort() { - - assertThat(repository.groupByLastnameAnd("firstname", Sort.by("lastname"))) // - .containsSequence( // - new PersonAggregate("Beauford", Collections.singletonList("Carter")), // - new PersonAggregate("Keys", Collections.singletonList("Alicia")), // - new PersonAggregate("Lessard", Collections.singletonList("Stefan")), // - new PersonAggregate("Matthews", Arrays.asList("Dave", "Oliver August")), // - new PersonAggregate("Moore", Collections.singletonList("Leroi")), // - new PersonAggregate("Tinsley", Collections.singletonList("Boyd"))); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithPageable() { - - assertThat(repository.groupByLastnameAnd("firstname", PageRequest.of(1, 2, Sort.by("lastname")))) // - .containsExactly( // - new PersonAggregate("Lessard", Collections.singletonList("Stefan")), // - new PersonAggregate("Matthews", Arrays.asList("Dave", "Oliver August"))); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithSingleSimpleResult() { - assertThat(repository.sumAge()).isEqualTo(245); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithAggregationResultAsReturnType() { - - assertThat(repository.sumAgeAndReturnAggregationResultWrapper()) // - .isInstanceOf(AggregationResults.class) // - .containsExactly(new Document("_id", null).append("total", 245)); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithAggregationResultAsReturnTypeAndProjection() { - - assertThat(repository.sumAgeAndReturnAggregationResultWrapperWithConcreteType()) // - .isInstanceOf(AggregationResults.class) // - .containsExactly(new SumAge(245L)); - } - - @Test // DATAMONGO-2374 - void findsWithNativeProjection() { - - assertThat(repository.findDocumentById(dave.getId()).get()).containsEntry("firstname", dave.getFirstname()) - .containsEntry("lastname", dave.getLastname()); - } - - @Test // DATAMONGO-1677 - void findWithMoreThan10Arguments() { - - alicia.setSkills(Arrays.asList("musician", "singer", "composer", "actress", "pianist")); - alicia.setAddress(new Address("street", "zipCode", "city")); - alicia.setUniqueId(UUID.randomUUID()); - UsernameAndPassword credentials = new UsernameAndPassword(); - credentials.password = "keys"; - credentials.username = "alicia"; - alicia.credentials = credentials; - - alicia = repository.save(this.alicia); - - assertThat(repository.findPersonByManyArguments(this.alicia.getFirstname(), this.alicia.getLastname(), - this.alicia.getEmail(), this.alicia.getAge(), Sex.FEMALE, this.alicia.createdAt, alicia.getSkills(), "street", - "zipCode", "city", alicia.getUniqueId(), credentials.username, credentials.password)).isNotNull(); - } - - @Test // DATAMONGO-1894 - void spelExpressionArgumentsGetReevaluatedOnEveryInvocation() { - - assertThat(repository.findWithSpelByFirstnameForSpELExpressionWithParameterIndexOnly("Dave")).containsExactly(dave); - assertThat(repository.findWithSpelByFirstnameForSpELExpressionWithParameterIndexOnly("Carter")) - .containsExactly(carter); - } - - @Test // DATAMONGO-1902 - void findByValueInsideUnwrapped() { - - Person bart = new Person("bart", "simpson"); - User user = new User(); - user.setUsername("bartman"); - user.setId("84r1m4n"); - bart.setUnwrappedUser(user); - - operations.save(bart); - - List result = repository.findByUnwrappedUserUsername(user.getUsername()); - - assertThat(result).hasSize(1); - assertThat(result.get(0).getId().equals(bart.getId())); - } - - @Test // DATAMONGO-1902 - void findByUnwrapped() { - - Person bart = new Person("bart", "simpson"); - User user = new User(); - user.setUsername("bartman"); - user.setId("84r1m4n"); - bart.setUnwrappedUser(user); - - operations.save(bart); - - List result = repository.findByUnwrappedUser(user); - - assertThat(result).hasSize(1); - assertThat(result.get(0).getId().equals(bart.getId())); - } - - @Test // GH-3395 - void caseInSensitiveInClause() { - assertThat(repository.findByLastnameIgnoreCaseIn("bEAuFoRd", "maTTheWs")).hasSize(3); - } - - @Test // GH-3395 - void caseInSensitiveInClauseQuotesExpressions() { - assertThat(repository.findByLastnameIgnoreCaseIn(".*")).isEmpty(); - } - - @Test // GH-3395 - void caseSensitiveInClauseIgnoresExpressions() { - assertThat(repository.findByFirstnameIn(".*")).isEmpty(); - } - - @Test // GH-3583 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.4") - void annotatedQueryShouldAllowAggregationInProjection() { - - Person target = repository.findWithAggregationInProjection(alicia.getId()); - assertThat(target.getFirstname()).isEqualTo(alicia.getFirstname().toUpperCase()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java deleted file mode 100644 index b935815bfb..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2011-2021 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.repository; - -import com.querydsl.core.annotations.QueryEmbeddable; - -/** - * @author Oliver Gierke - */ -@QueryEmbeddable -public class Address { - - private String street; - private String zipCode; - private String city; - - protected Address() { - - } - - /** - * @param street - * @param zipcode - * @param city - */ - public Address(String street, String zipcode, String city) { - this.street = street; - this.zipCode = zipcode; - this.city = city; - } - - /** - * @return the street - */ - public String getStreet() { - return street; - } - - /** - * @param street the street to set - */ - public void setStreet(String street) { - this.street = street; - } - - /** - * @return the zipCode - */ - public String getZipCode() { - return zipCode; - } - - /** - * @param zipCode the zipCode to set - */ - public void setZipCode(String zipCode) { - this.zipCode = zipCode; - } - - /** - * @return the city - */ - public String getCity() { - return city; - } - - /** - * @param city the city to set - */ - public void setCity(String city) { - this.city = city; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ComplexIdRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ComplexIdRepositoryIntegrationTests.java deleted file mode 100644 index 0b445fd659..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ComplexIdRepositoryIntegrationTests.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2014-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.client.MongoClient; - -/** - * @author Christoph Strobl - * @author Oliver Gierke - * @author Mark Paluch - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -public class ComplexIdRepositoryIntegrationTests { - - static @Client MongoClient mongoClient; - - @Configuration - @EnableMongoRepositories(includeFilters=@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = UserWithComplexIdRepository.class)) - static class Config extends AbstractMongoClientConfiguration { - - @Override - protected String getDatabaseName() { - return "complexIdTest"; - } - - @Override - public MongoClient mongoClient() { - return mongoClient; - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.emptySet(); - } - } - - @Autowired UserWithComplexIdRepository repo; - @Autowired MongoTemplate template; - - MyId id; - UserWithComplexId userWithId; - - @BeforeEach - public void setUp() { - - repo.deleteAll(); - - id = new MyId(); - id.val1 = "v1"; - id.val2 = "v2"; - - userWithId = new UserWithComplexId(); - userWithId.firstname = "foo"; - userWithId.id = id; - } - - @Test // DATAMONGO-1078 - public void annotatedFindQueryShouldWorkWhenUsingComplexId() { - - repo.save(userWithId); - - assertThat(repo.getUserByComplexId(id)).isEqualTo(userWithId); - } - - @Test // DATAMONGO-1078 - public void annotatedFindQueryShouldWorkWhenUsingComplexIdWithinCollection() { - - repo.save(userWithId); - - List loaded = repo.findByUserIds(Collections.singleton(id)); - - assertThat(loaded).hasSize(1); - assertThat(loaded).containsExactly(userWithId); - } - - @Test // DATAMONGO-1078 - public void findOneShouldWorkWhenUsingComplexId() { - - repo.save(userWithId); - - assertThat(repo.findById(id)).isEqualTo(Optional.of(userWithId)); - } - - @Test // DATAMONGO-1078 - public void findAllShouldWorkWhenUsingComplexId() { - - repo.save(userWithId); - - Iterable loaded = repo.findAllById(Collections.singleton(id)); - - assertThat(loaded).hasSize(1); - assertThat(loaded).containsExactly(userWithId); - } - - @Test // DATAMONGO-1373 - public void composedAnnotationFindQueryShouldWorkWhenUsingComplexId() { - - repo.save(userWithId); - - assertThat(repo.getUserUsingComposedAnnotationByComplexId(id)).isEqualTo(userWithId); - } - - @Test // DATAMONGO-1373 - public void composedAnnotationFindMetaShouldWorkWhenUsingComplexId() { - - repo.save(userWithId); - - assertThat(repo.findUsersUsingComposedMetaAnnotationByUserIds(Arrays.asList(id))).hasSize(1); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Contact.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Contact.java deleted file mode 100644 index 0f73f2280b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Contact.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2011-2021 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.repository; - -import org.bson.types.ObjectId; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; - -/** - * Sample contact domain class. - * - * @author Oliver Gierke - */ -@Document -public abstract class Contact { - - @Id protected String id; - - public Contact() { - this.id = new ObjectId().toString(); - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ContactRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ContactRepository.java deleted file mode 100644 index d51ada03ae..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ContactRepository.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2010-2021 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.repository; - -/** - * Simple repository interface managing {@link Contact}s. - * - * @author Oliver Gierke - */ -public interface ContactRepository extends MongoRepository { - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ContactRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ContactRepositoryIntegrationTests.java deleted file mode 100644 index d3bf634d84..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ContactRepositoryIntegrationTests.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2010-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Example; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration tests for {@link ContactRepository}. Mostly related to mapping inheritance. - * - * @author Oliver Gierke - * @author Mark Paluch - * @author Christoph Strobl - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("config/MongoNamespaceIntegrationTests-context.xml") -public class ContactRepositoryIntegrationTests { - - @Autowired ContactRepository repository; - - @Before - public void setUp() throws Exception { - repository.deleteAll(); - } - - @Test - public void readsAndWritesContactCorrectly() { - - Person person = new Person("Oliver", "Gierke"); - Contact result = repository.save(person); - - assertThat(repository.findById(result.getId().toString())).containsInstanceOf(Person.class); - } - - @Test // DATAMONGO-1245 - public void findsContactByTypedExample() { - - Person person = repository.save(new Person("Oliver", "Gierke")); - - assertThat(repository.findOne(Example.of(person))).containsInstanceOf(Person.class); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ConvertingReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ConvertingReactiveMongoRepositoryTests.java deleted file mode 100644 index cf2dc6a8dc..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ConvertingReactiveMongoRepositoryTests.java +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Copyright 2016-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; - -import io.reactivex.Flowable; -import io.reactivex.Maybe; -import io.reactivex.observers.TestObserver; -import io.reactivex.rxjava3.subscribers.TestSubscriber; -import lombok.Data; -import lombok.NoArgsConstructor; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; -import rx.Observable; -import rx.Single; - -import java.util.Arrays; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.reactivestreams.Publisher; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.FilterType; -import org.springframework.context.annotation.ImportResource; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Sort; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; -import org.springframework.data.repository.reactive.ReactiveSortingRepository; -import org.springframework.data.repository.reactive.RxJava2SortingRepository; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Test for {@link ReactiveMongoRepository} using reactive wrapper type conversion. - * - * @author Mark Paluch - */ -@RunWith(SpringRunner.class) -@ContextConfiguration(classes = ConvertingReactiveMongoRepositoryTests.Config.class) -public class ConvertingReactiveMongoRepositoryTests { - - @EnableReactiveMongoRepositories( - includeFilters = { @Filter(value = ReactivePersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE), - @Filter(value = RxJava1PersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE), - @Filter(value = RxJava2PersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE), - @Filter(value = RxJava3PersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE), - @Filter(value = MixedReactivePersonRepostitory.class, type = FilterType.ASSIGNABLE_TYPE) }, - considerNestedRepositories = true) - @ImportResource("classpath:reactive-infrastructure.xml") - static class Config {} - - @Autowired MixedReactivePersonRepostitory reactiveRepository; - @Autowired ReactivePersonRepostitory reactivePersonRepostitory; - @Autowired RxJava1PersonRepostitory rxJava1PersonRepostitory; - @Autowired RxJava2PersonRepostitory rxJava2PersonRepostitory; - @Autowired RxJava3PersonRepostitory rxJava3PersonRepostitory; - - ReactivePerson dave, oliver, carter, boyd, stefan, leroi, alicia; - - @Before - public void setUp() { - - reactiveRepository.deleteAll().as(StepVerifier::create).verifyComplete(); - - dave = new ReactivePerson("Dave", "Matthews", 42); - oliver = new ReactivePerson("Oliver August", "Matthews", 4); - carter = new ReactivePerson("Carter", "Beauford", 49); - boyd = new ReactivePerson("Boyd", "Tinsley", 45); - stefan = new ReactivePerson("Stefan", "Lessard", 34); - leroi = new ReactivePerson("Leroi", "Moore", 41); - alicia = new ReactivePerson("Alicia", "Keys", 30); - - reactiveRepository.saveAll(Arrays.asList(oliver, dave, carter, boyd, stefan, leroi, alicia)) - .as(StepVerifier::create) // - .expectNextCount(7) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void reactiveStreamsMethodsShouldWork() { - reactivePersonRepostitory.existsById(dave.getId()).as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void reactiveStreamsQueryMethodsShouldWork() { - StepVerifier.create(reactivePersonRepostitory.findByLastname(boyd.getLastname())).expectNext(boyd).verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void simpleRxJava1MethodsShouldWork() { - - rxJava1PersonRepostitory.existsById(dave.getId()) // - .test() // - .awaitTerminalEvent() // - .assertValue(true) // - .assertNoErrors() // - .assertCompleted(); - } - - @Test // DATAMONGO-1444 - public void existsWithSingleRxJava1IdMethodsShouldWork() { - - rxJava1PersonRepostitory.existsById(Single.just(dave.getId())) // - .test() // - .awaitTerminalEvent() // - .assertValue(true) // - .assertNoErrors() // - .assertCompleted(); - } - - @Test // DATAMONGO-1444 - public void singleRxJava1QueryMethodShouldWork() { - - rxJava1PersonRepostitory.findByFirstnameAndLastname(dave.getFirstname(), dave.getLastname()) // - .test() // - .awaitTerminalEvent() // - .assertValue(dave) // - .assertNoErrors() // - .assertCompleted(); - } - - @Test // DATAMONGO-1444 - public void singleProjectedRxJava1QueryMethodShouldWork() { - - List people = rxJava1PersonRepostitory.findProjectedByLastname(carter.getLastname()) // - .test() // - .awaitTerminalEvent() // - .assertValueCount(1) // - .assertNoErrors() // - .assertCompleted() // - .getOnNextEvents(); - - ProjectedPerson projectedPerson = people.get(0); - assertThat(projectedPerson.getFirstname()).isEqualTo(carter.getFirstname()); - } - - @Test // DATAMONGO-1444 - public void observableRxJava1QueryMethodShouldWork() { - - rxJava1PersonRepostitory.findByLastname(boyd.getLastname()) // - .test() // - .awaitTerminalEvent() // - .assertValue(boyd) // - .assertNoErrors() // - .assertCompleted() // - .getOnNextEvents(); - } - - @Test // DATAMONGO-1610 - public void simpleRxJava2MethodsShouldWork() { - - TestObserver testObserver = rxJava2PersonRepostitory.existsById(dave.getId()).test(); - - testObserver.awaitTerminalEvent(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - testObserver.assertValue(true); - } - - @Test // DATAMONGO-1610 - public void existsWithSingleRxJava2IdMethodsShouldWork() { - - TestObserver testObserver = rxJava2PersonRepostitory.existsById(io.reactivex.Single.just(dave.getId())) - .test(); - - testObserver.awaitTerminalEvent(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - testObserver.assertValue(true); - } - - @Test // DATAMONGO-1610 - public void flowableRxJava2QueryMethodShouldWork() { - - io.reactivex.subscribers.TestSubscriber testSubscriber = rxJava2PersonRepostitory - .findByFirstnameAndLastname(dave.getFirstname(), dave.getLastname()).test(); - - testSubscriber.awaitTerminalEvent(); - testSubscriber.assertComplete(); - testSubscriber.assertNoErrors(); - testSubscriber.assertValue(dave); - } - - @Test // DATAMONGO-1610 - public void singleProjectedRxJava2QueryMethodShouldWork() { - - TestObserver testObserver = rxJava2PersonRepostitory - .findProjectedByLastname(Maybe.just(carter.getLastname())).test(); - - testObserver.awaitTerminalEvent(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - - testObserver.assertValue(actual -> { - assertThat(actual.getFirstname()).isEqualTo(carter.getFirstname()); - return true; - }); - } - - @Test // DATAMONGO-1610 - public void observableProjectedRxJava2QueryMethodShouldWork() { - - TestObserver testObserver = rxJava2PersonRepostitory - .findProjectedByLastname(Single.just(carter.getLastname())).test(); - - testObserver.awaitTerminalEvent(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - - testObserver.assertValue(actual -> { - assertThat(actual.getFirstname()).isEqualTo(carter.getFirstname()); - return true; - }); - } - - @Test // DATAMONGO-1610 - public void maybeRxJava2QueryMethodShouldWork() { - - TestObserver testObserver = rxJava2PersonRepostitory.findByLastname(boyd.getLastname()).test(); - - testObserver.awaitTerminalEvent(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - testObserver.assertValue(boyd); - } - - @Test // DATAMONGO-2558 - public void simpleRxJava3MethodsShouldWork() { - - TestObserver testObserver = rxJava3PersonRepostitory.existsById(dave.getId()).test(); - - testObserver.awaitTerminalEvent(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - testObserver.assertValue(true); - } - - @Test // DATAMONGO-2558 - public void existsWithSingleRxJava3IdMethodsShouldWork() { - - TestObserver testObserver = rxJava3PersonRepostitory.existsById(io.reactivex.Single.just(dave.getId())) - .test(); - - testObserver.awaitTerminalEvent(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - testObserver.assertValue(true); - } - - @Test // DATAMONGO-2558 - public void flowableRxJava3QueryMethodShouldWork() throws InterruptedException { - - TestSubscriber testSubscriber = rxJava3PersonRepostitory - .findByFirstnameAndLastname(dave.getFirstname(), dave.getLastname()).test(); - - testSubscriber.await(); - testSubscriber.assertComplete(); - testSubscriber.assertNoErrors(); - testSubscriber.assertValue(dave); - } - - @Test // DATAMONGO-2558 - public void singleProjectedRxJava3QueryMethodShouldWork() throws InterruptedException { - - io.reactivex.rxjava3.observers.TestObserver testObserver = rxJava3PersonRepostitory - .findProjectedByLastname(io.reactivex.rxjava3.core.Maybe.just(carter.getLastname())).test(); - - testObserver.await(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - - testObserver.assertValue(actual -> { - assertThat(actual.getFirstname()).isEqualTo(carter.getFirstname()); - return true; - }); - } - - @Test // DATAMONGO-2558 - public void observableProjectedRxJava3QueryMethodShouldWork() throws InterruptedException { - - io.reactivex.rxjava3.observers.TestObserver testObserver = rxJava3PersonRepostitory - .findProjectedByLastname(io.reactivex.rxjava3.core.Single.just(carter.getLastname())).test(); - - testObserver.await(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - - testObserver.assertValue(actual -> { - assertThat(actual.getFirstname()).isEqualTo(carter.getFirstname()); - return true; - }); - } - - @Test // DATAMONGO-2558 - public void maybeRxJava3QueryMethodShouldWork() throws InterruptedException { - - io.reactivex.rxjava3.observers.TestObserver testObserver = rxJava3PersonRepostitory - .findByLastname(boyd.getLastname()).test(); - - testObserver.await(); - testObserver.assertComplete(); - testObserver.assertNoErrors(); - testObserver.assertValue(boyd); - } - - @Test // DATAMONGO-1444 - public void mixedRepositoryShouldWork() { - - reactiveRepository.findByLastname(boyd.getLastname()) // - .test() // - .awaitTerminalEvent() // - .assertValue(boyd) // - .assertNoErrors() // - .assertCompleted() // - .getOnNextEvents(); - } - - @Test // DATAMONGO-1444 - public void shouldFindOneBySingleOfLastName() { - - reactiveRepository.findByLastname(Single.just(carter.getLastname())).as(StepVerifier::create) // - .expectNext(carter) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void shouldFindByObservableOfLastNameIn() { - - reactiveRepository.findByLastnameIn(Observable.just(carter.getLastname(), dave.getLastname())) - .as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - public void shouldFindByPublisherOfLastNameInAndAgeGreater() { - - List people = reactiveRepository - .findByLastnameInAndAgeGreaterThan(Flux.just(carter.getLastname(), dave.getLastname()), 41).test() // - .awaitTerminalEvent() // - .assertValueCount(2) // - .assertNoErrors() // - .assertCompleted() // - .getOnNextEvents(); - - assertThat(people).contains(carter, dave); - } - - interface ReactivePersonRepostitory extends ReactiveSortingRepository { - - Publisher findByLastname(String lastname); - } - - interface RxJava1PersonRepostitory extends org.springframework.data.repository.Repository { - - Observable findByFirstnameAndLastname(String firstname, String lastname); - - Single findByLastname(String lastname); - - Single findProjectedByLastname(String lastname); - - Single existsById(String id); - - Single existsById(Single id); - } - - interface RxJava2PersonRepostitory extends RxJava2SortingRepository { - - Flowable findByFirstnameAndLastname(String firstname, String lastname); - - Maybe findByLastname(String lastname); - - io.reactivex.Single findProjectedByLastname(Maybe lastname); - - io.reactivex.Observable findProjectedByLastname(Single lastname); - } - - interface RxJava3PersonRepostitory extends RxJava2SortingRepository { - - io.reactivex.rxjava3.core.Flowable findByFirstnameAndLastname(String firstname, String lastname); - - io.reactivex.rxjava3.core.Maybe findByLastname(String lastname); - - io.reactivex.rxjava3.core.Single findProjectedByLastname( - io.reactivex.rxjava3.core.Maybe lastname); - - io.reactivex.rxjava3.core.Observable findProjectedByLastname( - io.reactivex.rxjava3.core.Single lastname); - } - - interface MixedReactivePersonRepostitory extends ReactiveMongoRepository { - - Single findByLastname(String lastname); - - Mono findByLastname(Single lastname); - - Flux findByLastnameIn(Observable lastname); - - Flux findByLastname(String lastname, Sort sort); - - Observable findByLastnameInAndAgeGreaterThan(Flux lastname, int age); - } - - @Document - @Data - @NoArgsConstructor - static class ReactivePerson { - - @Id String id; - - String firstname; - String lastname; - int age; - - public ReactivePerson(String firstname, String lastname, int age) { - - this.firstname = firstname; - this.lastname = lastname; - this.age = age; - } - } - - interface ProjectedPerson { - - String getId(); - - String getFirstname(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Credentials.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Credentials.java deleted file mode 100644 index 30a51d2943..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Credentials.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2013-2021 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.repository; - -/** - * @author Oliver Gierke - */ -public interface Credentials { - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java deleted file mode 100644 index 611c8a26c8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2014-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.List; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.index.TextIndexDefinition.TextIndexDefinitionBuilder; -import org.springframework.data.mongodb.core.index.TextIndexed; -import org.springframework.data.mongodb.core.mapping.TextScore; -import org.springframework.data.mongodb.core.query.TextCriteria; -import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; -import org.springframework.util.ObjectUtils; - -/** - * Integration tests for text searches on repository. - * - * @author Christoph Strobl - * @author Oliver Gierke - * @author Mark Paluch - */ -@ExtendWith(MongoTemplateExtension.class) -class MongoRepositoryTextSearchIntegrationTests { - - private static final FullTextDocument PASSENGER_57 = new FullTextDocument("1", "Passenger 57", - "Passenger 57 is an action film that stars Wesley Snipes and Bruce Payne."); - private static final FullTextDocument DEMOLITION_MAN = new FullTextDocument("2", "Demolition Man", - "Demolition Man is a science fiction action comedy film staring Wesley Snipes and Sylvester Stallone."); - private static final FullTextDocument DROP_ZONE = new FullTextDocument("3", "Drop Zone", - "Drop Zone is an action film featuring Wesley Snipes and Gary Busey."); - - @Template(initialEntitySet = FullTextDocument.class) // - private static MongoTestTemplate template; - - private FullTextRepository repo = new MongoRepositoryFactory(this.template).getRepository(FullTextRepository.class); - - @BeforeEach - void setUp() { - - template.indexOps(FullTextDocument.class) - .ensureIndex(new TextIndexDefinitionBuilder().onField("title").onField("content").build()); - } - - @AfterEach - void tearDown() { - template.flush(); - } - - @Test // DATAMONGO-973 - void findAllByTextCriteriaShouldReturnMatchingDocuments() { - - initRepoWithDefaultDocuments(); - - List result = repo.findAllBy(TextCriteria.forDefaultLanguage().matchingAny("stallone", "payne")); - - assertThat(result).hasSize(2); - assertThat(result).contains(PASSENGER_57, DEMOLITION_MAN); - } - - @Test // DATAMONGO-973 - void derivedFinderWithTextCriteriaReturnsCorrectResult() { - - initRepoWithDefaultDocuments(); - FullTextDocument blade = new FullTextDocument("4", "Blade", - "Blade is a 1998-2018 American vampire-superhero-vigilante action film starring Wesley Snipes and Stephen Dorff, loosely based on the Marvel Comics character Blade"); - blade.nonTextIndexProperty = "foo"; - repo.save(blade); - - List result = repo.findByNonTextIndexProperty("foo", - TextCriteria.forDefaultLanguage().matching("snipes")); - - assertThat(result).hasSize(1); - assertThat(result).contains(blade); - } - - @Test // DATAMONGO-973 - void findByWithPaginationWorksCorrectlyWhenUsingTextCriteria() { - - initRepoWithDefaultDocuments(); - - Page page = repo.findAllBy(TextCriteria.forDefaultLanguage().matching("film"), - PageRequest.of(1, 1, Direction.ASC, "id")); - - assertThat(page.hasNext()).isTrue(); - assertThat(page.hasPrevious()).isTrue(); - assertThat(page.getTotalElements()).isEqualTo(3L); - assertThat(page.getContent().get(0)).isEqualTo(DEMOLITION_MAN); - } - - @Test // DATAMONGO-973 - void findAllByTextCriteriaWithSortWorksCorrectly() { - - initRepoWithDefaultDocuments(); - FullTextDocument snipes = new FullTextDocument("4", "Snipes", "Wesley Trent Snipes is an actor and film producer."); - repo.save(snipes); - - List result = repo.findAllBy(TextCriteria.forDefaultLanguage().matching("snipes"), - Sort.by("score")); - - assertThat(result.size()).isEqualTo(4); - assertThat(result.get(0)).isEqualTo(snipes); - } - - @Test // DATAMONGO-973 - void findByWithSortByScoreViaPageRequestTriggersSortingCorrectly() { - - initRepoWithDefaultDocuments(); - FullTextDocument snipes = new FullTextDocument("4", "Snipes", "Wesley Trent Snipes is an actor and film producer."); - repo.save(snipes); - - Page page = repo.findAllBy(TextCriteria.forDefaultLanguage().matching("snipes"), - PageRequest.of(0, 10, Direction.ASC, "score")); - - assertThat(page.getTotalElements()).isEqualTo(4L); - assertThat(page.getContent().get(0)).isEqualTo(snipes); - } - - @Test // DATAMONGO-973 - void findByWithSortViaPageRequestIgnoresTextScoreWhenSortedByOtherProperty() { - - initRepoWithDefaultDocuments(); - FullTextDocument snipes = new FullTextDocument("4", "Snipes", "Wesley Trent Snipes is an actor and film producer."); - repo.save(snipes); - - Page page = repo.findAllBy(TextCriteria.forDefaultLanguage().matching("snipes"), - PageRequest.of(0, 10, Direction.ASC, "id")); - - assertThat(page.getTotalElements()).isEqualTo(4L); - assertThat(page.getContent().get(0)).isEqualTo(PASSENGER_57); - } - - @Test // DATAMONGO-973 - void derivedSortForTextScorePropertyWorksCorrectly() { - - initRepoWithDefaultDocuments(); - FullTextDocument snipes = new FullTextDocument("4", "Snipes", "Wesley Trent Snipes is an actor and film producer."); - repo.save(snipes); - - List result = repo - .findByNonTextIndexPropertyIsNullOrderByScoreDesc(TextCriteria.forDefaultLanguage().matching("snipes")); - assertThat(result.get(0)).isEqualTo(snipes); - } - - @Test // DATAMONGO-973, DATAMONGO-2516 - void derivedFinderMethodWithoutFullTextShouldNoCauseTroubleWhenHavingEntityWithTextScoreProperty() { - - initRepoWithDefaultDocuments(); - List result = repo.findByTitle(DROP_ZONE.getTitle()); - - assertThat(result.get(0)).isEqualTo(DROP_ZONE); - assertThat(result.get(0).score).isNull(); - } - - private void initRepoWithDefaultDocuments() { - repo.saveAll(Arrays.asList(PASSENGER_57, DEMOLITION_MAN, DROP_ZONE)); - } - - static class FullTextDocument { - - private @Id String id; - private @TextIndexed String title; - private @TextIndexed String content; - String nonTextIndexProperty; - @TextScore Float score; - - public FullTextDocument() { - - } - - public FullTextDocument(String id, String title, String content) { - - this.id = id; - this.title = title; - this.content = content; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - @Override - public int hashCode() { - return ObjectUtils.nullSafeHashCode(this.id); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof FullTextDocument)) { - return false; - } - FullTextDocument other = (FullTextDocument) obj; - return ObjectUtils.nullSafeEquals(this.id, other.id); - } - - } - - static interface FullTextRepository extends MongoRepository { - - List findByNonTextIndexProperty(String nonTextIndexProperty, TextCriteria criteria); - - List findByNonTextIndexPropertyIsNullOrderByScoreDesc(TextCriteria criteria); - - List findByTitle(String title); - - List findAllBy(TextCriteria textCriteria); - - List findAllBy(TextCriteria textCriteria, Sort sort); - - Page findAllBy(TextCriteria textCriteria, Pageable pageable); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java deleted file mode 100644 index 65c6a1d4ab..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2014-2021 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.repository; - -import java.io.Serializable; - -import org.springframework.util.ObjectUtils; - -/** - * @author Christoph Strobl - * @author Oliver Gierke - */ -public class MyId implements Serializable { - - private static final long serialVersionUID = -7129201311241750831L; - - String val1; - String val2; - - @Override - public int hashCode() { - - int result = 31; - - result += 17 * ObjectUtils.nullSafeHashCode(val1); - result += 17 * ObjectUtils.nullSafeHashCode(val2); - - return result; - } - - @Override - public boolean equals(Object obj) { - - if (obj == this) { - return true; - } - - if (!(obj instanceof MyId)) { - return false; - } - - MyId that = (MyId) obj; - - return ObjectUtils.nullSafeEquals(this.val1, that.val1) && ObjectUtils.nullSafeEquals(this.val2, that.val2); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java deleted file mode 100644 index 01b0c28de2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright 2010-2021 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.repository; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Set; -import java.util.UUID; - -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; -import org.springframework.data.mongodb.core.index.Indexed; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.Unwrapped; - -/** - * Sample domain class. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - */ -@Document -public class Person extends Contact { - - public enum Sex { - MALE, FEMALE; - } - - private String firstname; - private String lastname; - @Indexed(unique = true, dropDups = true) private String email; - private Integer age; - @SuppressWarnings("unused") private Sex sex; - Date createdAt; - - List skills; - - @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) private Point location; - - private @Field("add") Address address; - private Set
shippingAddresses; - - private UUID uniqueId; - - @DBRef User creator; - - @DBRef(lazy = true) User coworker; - - @DBRef(lazy = true) List fans; - - @DBRef(lazy = true) ArrayList realFans; - - Credentials credentials; - - @Unwrapped.Nullable(prefix = "u") // - User unwrappedUser; - - public Person() { - - this(null, null); - } - - public Person(String firstname, String lastname) { - - this(firstname, lastname, null); - } - - public Person(String firstname, String lastname, Integer age) { - - this(firstname, lastname, age, Sex.MALE); - } - - public Person(String firstname, String lastname, Integer age, Sex sex) { - - super(); - this.firstname = firstname; - this.lastname = lastname; - this.age = age; - this.sex = sex; - this.email = (firstname == null ? "noone" : firstname.toLowerCase()) + "@dmband.com"; - this.createdAt = new Date(); - } - - /** - * @return the firstname - */ - public String getFirstname() { - - return firstname; - } - - /** - * @param firstname the firstname to set - */ - public void setFirstname(String firstname) { - - this.firstname = firstname; - } - - /** - * @return the lastname - */ - public String getLastname() { - - return lastname; - } - - /** - * @param lastname the lastname to set - */ - public void setLastname(String lastname) { - - this.lastname = lastname; - } - - /** - * @return the email - */ - public String getEmail() { - return email; - } - - /** - * @param email the email to set - */ - public void setEmail(String email) { - this.email = email; - } - - /** - * @return the age - */ - public Integer getAge() { - - return age; - } - - /** - * @param age the age to set - */ - public void setAge(Integer age) { - - this.age = age; - } - - /** - * @return the location - */ - public Point getLocation() { - return location; - } - - /** - * @param location the location to set - */ - public void setLocation(Point location) { - this.location = location; - } - - /** - * @return the address - */ - public Address getAddress() { - return address; - } - - /** - * @param address the address to set - */ - public void setAddress(Address address) { - this.address = address; - } - - /** - * @return the addresses - */ - public Set
getShippingAddresses() { - return shippingAddresses; - } - - /** - * @param addresses the addresses to set - */ - public void setShippingAddresses(Set
addresses) { - this.shippingAddresses = addresses; - } - - public UUID getUniqueId() { - return uniqueId; - } - - public void setUniqueId(UUID uniqueId) { - this.uniqueId = uniqueId; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.repository.Contact#getName() - */ - public String getName() { - return String.format("%s %s", firstname, lastname); - } - - /** - * @return the fans - */ - public List getFans() { - return fans; - } - - /** - * @param fans the fans to set - */ - public void setFans(List fans) { - this.fans = fans; - } - - /** - * @return the realFans - */ - public ArrayList getRealFans() { - return realFans; - } - - /** - * @param realFans the realFans to set - */ - public void setRealFans(ArrayList realFans) { - this.realFans = realFans; - } - - /** - * @return the coworker - */ - public User getCoworker() { - return coworker; - } - - /** - * @param coworker the coworker to set - */ - public void setCoworker(User coworker) { - this.coworker = coworker; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - - if (this == obj) { - return true; - } - - if (obj == null || !getClass().equals(obj.getClass())) { - return false; - } - - Person that = (Person) obj; - - return this.getId().equals(that.getId()); - } - - public Person withAddress(Address address) { - - this.address = address; - return this; - } - - public void setCreator(User creator) { - this.creator = creator; - } - - public void setSkills(List skills) { - this.skills = skills; - } - - public List getSkills() { - return skills; - } - - public User getUnwrappedUser() { - return unwrappedUser; - } - - public void setUnwrappedUser(User unwrappedUser) { - this.unwrappedUser = unwrappedUser; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - - return getId().hashCode(); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return String.format("%s %s", firstname, lastname); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java deleted file mode 100644 index c725137410..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019-2021 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.repository; - -import lombok.Value; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@Value -class PersonAggregate { - - @Id private String lastname; - private Set names; - - @PersistenceConstructor - public PersonAggregate(String lastname, Collection names) { - this.lastname = lastname; - this.names = new HashSet<>(names); - } - - public PersonAggregate(String lastname, String name) { - this(lastname, Collections.singletonList(name)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonExcerpt.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonExcerpt.java deleted file mode 100644 index f1c7e3eed2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonExcerpt.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017-2021 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.repository; - -import org.springframework.beans.factory.annotation.Value; - -/** - * @author Oliver Gierke - */ -public interface PersonExcerpt { - - @Value("#{target.firstname + ' ' + target.lastname}") - String getFullName(); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java deleted file mode 100644 index c3b765c910..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2010-2021 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.repository; - -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Range; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; -import org.springframework.data.geo.Box; -import org.springframework.data.geo.Circle; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoPage; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Point; -import org.springframework.data.geo.Polygon; -import org.springframework.data.mongodb.core.aggregation.AggregationResults; -import org.springframework.data.mongodb.repository.Person.Sex; -import org.springframework.data.querydsl.QuerydslPredicateExecutor; -import org.springframework.data.repository.query.Param; -import org.springframework.lang.Nullable; - -/** - * Sample repository managing {@link Person} entities. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - * @author Fırat KÜÇÜK - * @author Mark Paluch - */ -public interface PersonRepository extends MongoRepository, QuerydslPredicateExecutor { - - /** - * Returns all {@link Person}s with the given lastname. - * - * @param lastname - * @return - */ - List findByLastname(String lastname); - - List findByLastnameStartsWith(String prefix); - - List findByLastnameEndsWith(String postfix); - - /** - * Returns all {@link Person}s with the given lastname ordered by their firstname. - * - * @param lastname - * @return - */ - List findByLastnameOrderByFirstnameAsc(String lastname); - - /** - * Returns the {@link Person}s with the given firstname. Uses {@link Query} annotation to define the query to be - * executed. - * - * @param firstname - * @return - */ - @Query(value = "{ 'firstname' : ?0 }", fields = "{ 'firstname': 1, 'lastname': 1}") - List findByThePersonsFirstname(String firstname); - - // DATAMONGO-871 - @Query(value = "{ 'firstname' : ?0 }") - Person[] findByThePersonsFirstnameAsArray(String firstname); - - /** - * Returns all {@link Person}s with a firstname matching the given one (*-wildcard supported). - * - * @param firstname - * @return - */ - List findByFirstnameLike(@Nullable String firstname); - - List findByFirstnameNotContains(String firstname); - - /** - * Returns all {@link Person}s with a firstname not matching the given one (*-wildcard supported). - * - * @param firstname - * @return - */ - List findByFirstnameNotLike(String firstname); - - List findByFirstnameLikeOrderByLastnameAsc(String firstname, Sort sort); - - List findBySkillsContains(List skills); - - List findBySkillsNotContains(List skills); - - @Query("{'age' : { '$lt' : ?0 } }") - List findByAgeLessThan(int age, Sort sort); - - /** - * Returns a page of {@link Person}s with a lastname mathing the given one (*-wildcards supported). - * - * @param lastname - * @param pageable - * @return - */ - Page findByLastnameLike(String lastname, Pageable pageable); - - @Query("{ 'lastname' : { '$regex' : '?0', '$options' : 'i'}}") - Page findByLastnameLikeWithPageable(String lastname, Pageable pageable); - - List findByLastnameIgnoreCaseIn(String... lastname); - - /** - * Returns all {@link Person}s with a firstname contained in the given varargs. - * - * @param firstnames - * @return - */ - List findByFirstnameIn(String... firstnames); - - /** - * Returns all {@link Person}s with a firstname not contained in the given collection. - * - * @param firstnames - * @return - */ - List findByFirstnameNotIn(Collection firstnames); - - List findByFirstnameAndLastname(String firstname, String lastname); - - /** - * Returns all {@link Person}s with an age between the two given values. - * - * @param from - * @param to - * @return - */ - List findByAgeBetween(int from, int to); - - /** - * Returns the {@link Person} with the given {@link Address} as shipping address. - * - * @param address - * @return - */ - Person findByShippingAddresses(Address address); - - /** - * Returns all {@link Person}s with the given {@link Address}. - * - * @param address - * @return - */ - List findByAddress(Address address); - - List findByAddressZipCode(String zipCode); - - List findByLastnameLikeAndAgeBetween(String lastname, int from, int to); - - List findByAgeOrLastnameLikeAndFirstnameLike(int age, String lastname, String firstname); - - List findByLocationNear(Point point); - - List findByLocationWithin(Circle circle); - - List findByLocationWithin(Box box); - - List findByLocationWithin(Polygon polygon); - - List findBySex(Sex sex); - - List findBySex(Sex sex, Pageable pageable); - - List findByNamedQuery(String firstname); - - GeoResults findByLocationNear(Point point, Distance maxDistance); - - // DATAMONGO-1110 - GeoResults findPersonByLocationNear(Point point, Range distance); - - GeoPage findByLocationNear(Point point, Distance maxDistance, Pageable pageable); - - List findByCreator(User user); - - // DATAMONGO-425 - List findByCreatedAtLessThan(Date date); - - // DATAMONGO-425 - List findByCreatedAtGreaterThan(Date date); - - // DATAMONGO-425 - @Query("{ 'createdAt' : { '$lt' : ?0 }}") - List findByCreatedAtLessThanManually(Date date); - - // DATAMONGO-427 - List findByCreatedAtBefore(Date date); - - // DATAMONGO-427 - List findByCreatedAtAfter(Date date); - - // DATAMONGO-472 - List findByLastnameNot(String lastname); - - // DATAMONGO-600 - List findByCredentials(Credentials credentials); - - // DATAMONGO-636 - long countByLastname(String lastname); - - // DATAMONGO-636 - int countByFirstname(String firstname); - - // DATAMONGO-636 - @Query(value = "{ 'lastname' : ?0 }", count = true) - long someCountQuery(String lastname); - - // DATAMONGO-1454 - boolean existsByFirstname(String firstname); - - // DATAMONGO-1454 - @ExistsQuery(value = "{ 'lastname' : ?0 }") - boolean someExistQuery(String lastname); - - // DATAMONGO-770 - List findByFirstnameIgnoreCase(@Nullable String firstName); - - // DATAMONGO-770 - List findByFirstnameNotIgnoreCase(String firstName); - - // DATAMONGO-770 - List findByFirstnameStartingWithIgnoreCase(String firstName); - - // DATAMONGO-770 - List findByFirstnameEndingWithIgnoreCase(String firstName); - - // DATAMONGO-770 - List findByFirstnameContainingIgnoreCase(String firstName); - - // DATAMONGO-870 - Slice findByAgeGreaterThan(int age, Pageable pageable); - - // DATAMONGO-821 - @Query("{ creator : { $exists : true } }") - Page findByHavingCreator(Pageable page); - - // DATAMONGO-566 - List deleteByLastname(String lastname); - - // DATAMONGO-566 - Long deletePersonByLastname(String lastname); - - // DATAMONGO-1997 - Optional deleteOptionalByLastname(String lastname); - - // DATAMONGO-566 - @Query(value = "{ 'lastname' : ?0 }", delete = true) - List removeByLastnameUsingAnnotatedQuery(String lastname); - - // DATAMONGO-566 - @Query(value = "{ 'lastname' : ?0 }", delete = true) - Long removePersonByLastnameUsingAnnotatedQuery(String lastname); - - // DATAMONGO-893 - Page findByAddressIn(List
address, Pageable page); - - // DATAMONGO-745 - @Query("{firstname:{$in:?0}, lastname:?1}") - Page findByCustomQueryFirstnamesAndLastname(List firstnames, String lastname, Pageable page); - - // DATAMONGO-745 - @Query("{lastname:?0, 'address.street':{$in:?1}}") - Page findByCustomQueryLastnameAndAddressStreetInList(String lastname, List streetNames, - Pageable page); - - // DATAMONGO-950 - List findTop3ByLastnameStartingWith(String lastname); - - // DATAMONGO-950 - Page findTop3ByLastnameStartingWith(String lastname, Pageable pageRequest); - - // DATAMONGO-1865 - Person findFirstBy(); // limits to 1 result if more, just return the first one - - // DATAMONGO-1865 - Person findPersonByLastnameLike(String firstname); // single person, error if more than one - - // DATAMONGO-1865 - Optional findOptionalPersonByLastnameLike(String firstname); // optional still, error when more than one - - // DATAMONGO-1030 - PersonSummaryDto findSummaryByLastname(String lastname); - - @Query("{ ?0 : ?1 }") - List findByKeyValue(String key, String value); - - // DATAMONGO-1165 - @Query("{ firstname : { $in : ?0 }}") - Stream findByCustomQueryWithStreamingCursorByFirstnames(List firstnames); - - // DATAMONGO-990 - @Query("{ firstname : ?#{[0]}}") - List findWithSpelByFirstnameForSpELExpressionWithParameterIndexOnly(String firstname); - - // DATAMONGO-990 - @Query("{ firstname : ?#{[0]}, email: ?#{principal.email} }") - List findWithSpelByFirstnameAndCurrentUserWithCustomQuery(String firstname); - - // DATAMONGO-990 - @Query("{ firstname : :#{#firstname}}") - List findWithSpelByFirstnameForSpELExpressionWithParameterVariableOnly(@Param("firstname") String firstname); - - // DATAMONGO-1911 - @Query("{ uniqueId: ?0}") - Person findByUniqueId(UUID uniqueId); - - /** - * Returns the count of {@link Person} with the given firstname. Uses {@link CountQuery} annotation to define the - * query to be executed. - * - * @param firstname - * @return - */ - @CountQuery("{ 'firstname' : ?0 }") // DATAMONGO-1539 - long countByThePersonsFirstname(String firstname); - - /** - * Deletes {@link Person} entities with the given firstname. Uses {@link DeleteQuery} annotation to define the query - * to be executed. - * - * @param firstname - */ - @DeleteQuery("{ 'firstname' : ?0 }") // DATAMONGO-1539 - void deleteByThePersonsFirstname(String firstname); - - // DATAMONGO-1752 - Iterable findOpenProjectionBy(); - - // DATAMONGO-1752 - Iterable findClosedProjectionBy(); - - @Query(sort = "{ age : -1 }") - List findByAgeGreaterThan(int age); - - @Query(sort = "{ age : -1 }") - List findByAgeGreaterThan(int age, Sort sort); - - List findByFirstnameRegex(Pattern pattern); - - @Query(value = "{ 'id' : ?0 }", fields = "{ 'fans': { '$slice': [ ?1, ?2 ] } }") - Person findWithSliceInProjection(String id, int skip, int limit); - - @Query(value = "{ 'id' : ?0 }", fields = "{ 'firstname': { '$toUpper': '$firstname' } }") - Person findWithAggregationInProjection(String id); - - @Query(value = "{ 'shippingAddresses' : { '$elemMatch' : { 'city' : { '$eq' : 'lnz' } } } }", - fields = "{ 'shippingAddresses.$': ?0 }") - Person findWithArrayPositionInProjection(int position); - - @Query(value = "{ 'fans' : { '$elemMatch' : { '$ref' : 'user' } } }", fields = "{ 'fans.$': ?0 }") - Person findWithArrayPositionInProjectionWithDbRef(int position); - - @Aggregation("{ '$project': { '_id' : '$lastname' } }") - List findAllLastnames(); - - @Aggregation("{ '$group': { '_id' : '$lastname', names : { $addToSet : '$?0' } } }") - List groupByLastnameAnd(String property); - - @Aggregation("{ '$group': { '_id' : '$lastname', names : { $addToSet : '$?0' } } }") - List groupByLastnameAnd(String property, Sort sort); - - @Aggregation("{ '$group': { '_id' : '$lastname', names : { $addToSet : '$?0' } } }") - List groupByLastnameAnd(String property, Pageable page); - - @Aggregation(pipeline = "{ '$group' : { '_id' : null, 'total' : { $sum: '$age' } } }") - int sumAge(); - - @Aggregation(pipeline = "{ '$group' : { '_id' : null, 'total' : { $sum: '$age' } } }") - AggregationResults sumAgeAndReturnAggregationResultWrapper(); - - @Aggregation(pipeline = "{ '$group' : { '_id' : null, 'total' : { $sum: '$age' } } }") - AggregationResults sumAgeAndReturnAggregationResultWrapperWithConcreteType(); - - @Query(value = "{_id:?0}") - Optional findDocumentById(String id); - - @Query(value = "{ 'firstname' : ?0, 'lastname' : ?1, 'email' : ?2 , 'age' : ?3, 'sex' : ?4, " - + "'createdAt' : ?5, 'skills' : ?6, 'address.street' : ?7, 'address.zipCode' : ?8, " // - + "'address.city' : ?9, 'uniqueId' : ?10, 'credentials.username' : ?11, 'credentials.password' : ?12 }") - Person findPersonByManyArguments(String firstname, String lastname, String email, Integer age, Sex sex, - Date createdAt, List skills, String street, String zipCode, // - String city, UUID uniqueId, String username, String password); - - List findByUnwrappedUserUsername(String username); - - List findByUnwrappedUser(User user); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests.java deleted file mode 100644 index e09b2caa46..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2010-2021 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.repository; - -import org.springframework.test.context.ContextConfiguration; - -/** - * Integration test for {@link PersonRepository}. - * - * @author Oliver Gierke - * @author Thomas Darimont - */ -@ContextConfiguration -public class PersonRepositoryIntegrationTests extends AbstractPersonRepositoryIntegrationTests {} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryLazyLoadingIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryLazyLoadingIntegrationTests.java deleted file mode 100644 index 5cc8e82599..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryLazyLoadingIntegrationTests.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2010-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration test for {@link PersonRepository} for lazy loading support. - * - * @author Thomas Darimont - * @author Oliver Gierke - */ -@ContextConfiguration(locations = "PersonRepositoryIntegrationTests-context.xml") -@RunWith(SpringRunner.class) -public class PersonRepositoryLazyLoadingIntegrationTests { - - @Autowired PersonRepository repository; - @Autowired MongoOperations operations; - - @Before - public void setUp() throws InterruptedException { - - repository.deleteAll(); - operations.remove(new org.springframework.data.mongodb.core.query.Query(), User.class); - } - - @Test // DATAMONGO-348 - public void shouldLoadAssociationWithDbRefOnInterfaceAndLazyLoadingEnabled() throws Exception { - - User thomas = new User(); - thomas.username = "Thomas"; - operations.save(thomas); - - Person person = new Person(); - person.setFirstname("Oliver"); - person.setFans(Arrays.asList(thomas)); - person.setRealFans(new ArrayList(Arrays.asList(thomas))); - repository.save(person); - - Person oliver = repository.findById(person.id).get(); - List fans = oliver.getFans(); - - assertProxyIsResolved(fans, false); - - User user = fans.get(0); - assertProxyIsResolved(fans, true); - assertThat(user.getUsername()).isEqualTo(thomas.getUsername()); - } - - @Test // DATAMONGO-348 - public void shouldLoadAssociationWithDbRefOnConcreteCollectionAndLazyLoadingEnabled() throws Exception { - - User thomas = new User(); - thomas.username = "Thomas"; - operations.save(thomas); - - Person person = new Person(); - person.setFirstname("Oliver"); - person.setFans(Arrays.asList(thomas)); - person.setRealFans(new ArrayList(Arrays.asList(thomas))); - repository.save(person); - - Person oliver = repository.findById(person.id).get(); - List realFans = oliver.getRealFans(); - - assertProxyIsResolved(realFans, false); - User realFan = realFans.get(0); - assertProxyIsResolved(realFans, true); - assertThat(realFan.getUsername()).isEqualTo(thomas.getUsername()); - - realFans = oliver.getRealFans(); - assertProxyIsResolved(realFans, true); - - realFan = realFans.get(0); - assertThat(realFan.getUsername()).isEqualTo(thomas.getUsername()); - } - - @Test // DATAMONGO-348 - public void shouldLoadAssociationWithDbRefOnConcreteDomainClassAndLazyLoadingEnabled() throws Exception { - - User thomas = new User(); - thomas.username = "Thomas"; - operations.save(thomas); - - Person person = new Person(); - person.setFirstname("Oliver"); - person.setCoworker(thomas); - repository.save(person); - - Person oliver = repository.findById(person.id).get(); - - User coworker = oliver.getCoworker(); - - assertProxyIsResolved(coworker, false); - assertThat(coworker.getUsername()).isEqualTo(thomas.getUsername()); - assertProxyIsResolved(coworker, true); - assertThat(coworker.getUsername()).isEqualTo(thomas.getUsername()); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java deleted file mode 100644 index 991e16d700..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2018-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.mongodb.test.util.MongoTestUtils.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.data.domain.Persistable; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.MongoTransactionManager; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; -import org.springframework.data.mongodb.test.util.AfterTransactionAssertion; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.ReplSetClient; -import org.springframework.lang.Nullable; -import org.springframework.test.annotation.Rollback; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.context.transaction.AfterTransaction; -import org.springframework.test.context.transaction.BeforeTransaction; -import org.springframework.transaction.annotation.Transactional; - -import com.mongodb.ReadPreference; -import com.mongodb.WriteConcern; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.Filters; - -/** - * @author Christoph Strobl - * @currentRead Shadow's Edge - Brent Weeks - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -@EnableIfReplicaSetAvailable -@EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") -@Transactional(transactionManager = "txManager") -public class PersonRepositoryTransactionalTests { - - static final String DB_NAME = "repository-tx-tests"; - static @ReplSetClient MongoClient mongoClient; - - @Configuration - @EnableMongoRepositories(includeFilters=@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = PersonRepository.class)) - static class Config extends AbstractMongoClientConfiguration { - - @Bean - public MongoClient mongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return DB_NAME; - } - - @Bean - MongoTransactionManager txManager(MongoDatabaseFactory dbFactory) { - return new MongoTransactionManager(dbFactory); - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return Collections.singleton(Person.class); - } - } - - @Autowired MongoClient client; - @Autowired PersonRepository repository; - @Autowired MongoTemplate template; - - Person durzo, kylar, vi; - - List all; - - List>> assertionList; - - @BeforeEach - public void setUp() { - assertionList = new CopyOnWriteArrayList<>(); - } - - @BeforeTransaction - public void beforeTransaction() { - - createOrReplaceCollection(DB_NAME, template.getCollectionName(Person.class), client); - createOrReplaceCollection(DB_NAME, template.getCollectionName(User.class), client); - - durzo = new Person("Durzo", "Blint", 700); - kylar = new Person("Kylar", "Stern", 21); - vi = new Person("Viridiana", "Sovari", 20); - - all = repository.saveAll(Arrays.asList(durzo, kylar, vi)); - } - - @AfterTransaction - public void verifyDbState() throws InterruptedException { - - Thread.sleep(100); - - MongoCollection collection = client.getDatabase(DB_NAME) // - .withWriteConcern(WriteConcern.MAJORITY) // - .withReadPreference(ReadPreference.primary()) // - .getCollection(template.getCollectionName(Person.class)); - - try { - assertionList.forEach(it -> { - - boolean isPresent = collection.find(Filters.eq("_id", new ObjectId(it.getId().toString()))).iterator() - .hasNext(); - - assertThat(isPresent) // - .withFailMessage(String.format("After transaction entity %s should %s.", it.getPersistable(), - it.shouldBePresent() ? "be present" : "NOT be present")) - .isEqualTo(it.shouldBePresent()); - - }); - } finally { - assertionList.clear(); - } - } - - @Rollback(false) - @Test // DATAMONGO-1920 - public void shouldHonorCommitForDerivedQuery() { - - repository.removePersonByLastnameUsingAnnotatedQuery(durzo.getLastname()); - - assertAfterTransaction(durzo).isNotPresent(); - } - - @Rollback(false) - @Test // DATAMONGO-1920 - public void shouldHonorCommit() { - - Person hu = new Person("Hu", "Gibbet", 43); - - repository.save(hu); - - assertAfterTransaction(hu).isPresent(); - } - - @Test // DATAMONGO-1920 - public void shouldHonorRollback() { - - Person hu = new Person("Hu", "Gibbet", 43); - - repository.save(hu); - - assertAfterTransaction(hu).isNotPresent(); - } - - @Test // DATAMONGO-2490 - public void shouldBeAbleToReadDbRefDuringTransaction() { - - User rat = new User(); - rat.setUsername("rat"); - - template.save(rat); - - Person elene = new Person("Elene", "Cromwyll", 18); - elene.setCoworker(rat); - - repository.save(elene); - - Optional loaded = repository.findById(elene.getId()); - assertThat(loaded).isPresent(); - assertThat(loaded.get().getCoworker()).isNotNull(); - assertThat(loaded.get().getCoworker().getUsername()).isEqualTo(rat.getUsername()); - } - - private AfterTransactionAssertion assertAfterTransaction(Person person) { - - AfterTransactionAssertion assertion = new AfterTransactionAssertion<>(new Persistable() { - - @Nullable - @Override - public Object getId() { - return person.id; - } - - @Override - public boolean isNew() { - return person.id != null; - } - - @Override - public String toString() { - return getId() + " - " + person.toString(); - } - }); - - assertionList.add(assertion); - return assertion; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonSummary.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonSummary.java deleted file mode 100644 index 3cb8d265ff..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonSummary.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017-2021 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.repository; - -/** - * @author Oliver Gierke - */ -public interface PersonSummary { - - String getFirstname(); - - String getLastname(); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonSummaryDto.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonSummaryDto.java deleted file mode 100644 index c40e8331d6..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonSummaryDto.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2014-2021 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.repository; - -/** - * @author Oliver Gierke - */ -public class PersonSummaryDto { - - String firstname; - String lastname; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java deleted file mode 100644 index 5e26503e4a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Copyright 2016-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.domain.Sort.Direction.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.test.util.Assertions.assertThat; - -import lombok.Data; -import lombok.NoArgsConstructor; -import reactor.core.Disposable; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.Arrays; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.reactivestreams.Publisher; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.geo.Circle; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoResult; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; -import org.springframework.data.mongodb.core.CollectionOptions; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.repository.Person.Sex; -import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory; -import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * Test for {@link ReactiveMongoRepository} query methods. - * - * @author Mark Paluch - * @author Christoph Strobl - * @author Jens Schauder - */ -@ExtendWith({ MongoClientExtension.class, SpringExtension.class }) -class ReactiveMongoRepositoryTests { - - private static final int PERSON_COUNT = 7; - private static @Client MongoClient mongoClient; - - @Autowired ReactiveMongoTemplate template; - - @Autowired ReactivePersonRepository repository; - @Autowired ReactiveContactRepository contactRepository; - @Autowired ReactiveCappedCollectionRepository cappedRepository; - - private Person dave, oliver, carter, boyd, stefan, leroi, alicia; - private QPerson person = QPerson.person; - - @Configuration - static class Config extends AbstractReactiveMongoConfiguration { - - @Bean - @Override - public MongoClient reactiveMongoClient() { - return mongoClient; - } - - @Override - protected String getDatabaseName() { - return "reactive"; - } - - @Bean - ReactiveMongoRepositoryFactory factory(ReactiveMongoOperations template, BeanFactory beanFactory) { - - ReactiveMongoRepositoryFactory factory = new ReactiveMongoRepositoryFactory(template); - factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class); - factory.setBeanClassLoader(beanFactory.getClass().getClassLoader()); - factory.setBeanFactory(beanFactory); - factory.setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT); - - return factory; - } - - @Bean - ReactivePersonRepository reactivePersonRepository(ReactiveMongoRepositoryFactory factory) { - return factory.getRepository(ReactivePersonRepository.class); - } - - @Bean - ReactiveContactRepository reactiveContactRepository(ReactiveMongoRepositoryFactory factory) { - return factory.getRepository(ReactiveContactRepository.class); - } - - @Bean - ReactiveCappedCollectionRepository reactiveCappedCollectionRepository(ReactiveMongoRepositoryFactory factory) { - return factory.getRepository(ReactiveCappedCollectionRepository.class); - } - - @Override - protected boolean autoIndexCreation() { - return true; - } - } - - @BeforeAll - static void cleanDb() { - - MongoTestUtils.createOrReplaceCollectionNow("reactive", "person", mongoClient); - MongoTestUtils.createOrReplaceCollectionNow("reactive", "capped", mongoClient); - } - - @BeforeEach - void setUp() throws Exception { - - repository.deleteAll().as(StepVerifier::create).verifyComplete(); - - dave = new Person("Dave", "Matthews", 42); - oliver = new Person("Oliver August", "Matthews", 4); - carter = new Person("Carter", "Beauford", 49); - carter.setSkills(Arrays.asList("Drums", "percussion", "vocals")); - Thread.sleep(10); - boyd = new Person("Boyd", "Tinsley", 45); - boyd.setSkills(Arrays.asList("Violin", "Electric Violin", "Viola", "Mandolin", "Vocals", "Guitar")); - stefan = new Person("Stefan", "Lessard", 34); - leroi = new Person("Leroi", "Moore", 41); - - alicia = new Person("Alicia", "Keys", 30, Sex.FEMALE); - - repository.saveAll(Arrays.asList(oliver, carter, boyd, stefan, leroi, alicia, dave)).as(StepVerifier::create) // - .expectNextCount(PERSON_COUNT) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void shouldFindByLastName() { - repository.findByLastname(dave.getLastname()).as(StepVerifier::create).expectNextCount(2).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void shouldFindOneByLastName() { - repository.findOneByLastname(carter.getLastname()).as(StepVerifier::create).expectNext(carter).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void shouldFindOneByPublisherOfLastName() { - repository.findByLastname(Mono.just(carter.getLastname())).as(StepVerifier::create).expectNext(carter) - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void shouldFindByPublisherOfLastNameIn() { - repository.findByLastnameIn(Flux.just(carter.getLastname(), dave.getLastname())).as(StepVerifier::create) // - .expectNextCount(3) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void shouldFindByPublisherOfLastNameInAndAgeGreater() { - - repository.findByLastnameInAndAgeGreaterThan(Flux.just(carter.getLastname(), dave.getLastname()), 41) - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void shouldFindUsingPublishersInStringQuery() { - - repository.findStringQuery(Flux.just("Beauford", "Matthews"), Mono.just(41)).as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void shouldFindByLastNameAndSort() { - - repository.findByLastname("Matthews", Sort.by(ASC, "age")).as(StepVerifier::create) // - .expectNext(oliver, dave) // - .verifyComplete(); - - repository.findByLastname("Matthews", Sort.by(DESC, "age")).as(StepVerifier::create) // - .expectNext(dave, oliver) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void shouldUseTailableCursor() throws Exception { - - template.dropCollection(Capped.class) // - .then(template.createCollection(Capped.class, // - CollectionOptions.empty().size(1000).maxDocuments(100).capped())) - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.insert(new Capped("value", Math.random())).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue documents = new LinkedBlockingDeque<>(100); - - Disposable disposable = cappedRepository.findByKey("value").doOnNext(documents::add).subscribe(); - - assertThat(documents.poll(5, TimeUnit.SECONDS)).isNotNull(); - - template.insert(new Capped("value", Math.random())).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - assertThat(documents.poll(5, TimeUnit.SECONDS)).isNotNull(); - assertThat(documents).isEmpty(); - - disposable.dispose(); - } - - @Test // DATAMONGO-1444 - void shouldUseTailableCursorWithProjection() throws Exception { - - template.dropCollection(Capped.class) // - .then(template.createCollection(Capped.class, // - CollectionOptions.empty().size(1000).maxDocuments(100).capped())) - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - template.insert(new Capped("value", Math.random())).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - BlockingQueue documents = new LinkedBlockingDeque<>(100); - - Disposable disposable = cappedRepository.findProjectionByKey("value").doOnNext(documents::add).subscribe(); - - CappedProjection projection1 = documents.poll(5, TimeUnit.SECONDS); - assertThat(projection1).isNotNull(); - assertThat(projection1.getRandom()).isNotEqualTo(0); - - template.insert(new Capped("value", Math.random())).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - CappedProjection projection2 = documents.poll(5, TimeUnit.SECONDS); - assertThat(projection2).isNotNull(); - assertThat(projection2.getRandom()).isNotEqualTo(0); - - assertThat(documents).isEmpty(); - - disposable.dispose(); - } - - @Test // DATAMONGO-2080 - void shouldUseTailableCursorWithDtoProjection() { - - template.dropCollection(Capped.class) // - .then(template.createCollection(Capped.class, // - CollectionOptions.empty().size(1000).maxDocuments(100).capped())) // - .as(StepVerifier::create).expectNextCount(1) // - .verifyComplete(); - - template.insert(new Capped("value", Math.random())).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - cappedRepository.findDtoProjectionByKey("value").as(StepVerifier::create).expectNextCount(1).thenCancel().verify(); - } - - @Test // DATAMONGO-1444 - void findsPeopleByLocationWithinCircle() { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - repository.findByLocationWithin(new Circle(-78.99171, 45.738868, 170)).as(StepVerifier::create) // - .expectNext(dave) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findsPeopleByPageableLocationWithinCircle() { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - repository.findByLocationWithin(new Circle(-78.99171, 45.738868, 170), // - PageRequest.of(0, 10)).as(StepVerifier::create) // - .expectNext(dave) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findsPeopleGeoresultByLocationWithinBox() { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - repository.findByLocationNear(new Point(-73.99, 40.73), // - new Distance(2000, Metrics.KILOMETERS)).as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual.getDistance().getValue()).isCloseTo(1, offset(1d)); - assertThat(actual.getContent()).isEqualTo(dave); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findsPeoplePageableGeoresultByLocationWithinBox() throws InterruptedException { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - // Allow for index creation - Thread.sleep(500); - - repository.findByLocationNear(new Point(-73.99, 40.73), // - new Distance(2000, Metrics.KILOMETERS), // - PageRequest.of(0, 10)).as(StepVerifier::create) // - .consumeNextWith(actual -> { - - assertThat(actual.getDistance().getValue()).isCloseTo(1, offset(1d)); - assertThat(actual.getContent()).isEqualTo(dave); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findsPeopleByLocationWithinBox() throws InterruptedException { - - Point point = new Point(-73.99171, 40.738868); - dave.setLocation(point); - repository.save(dave).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - - // Allow for index creation - Thread.sleep(500); - - repository.findPersonByLocationNear(new Point(-73.99, 40.73), // - new Distance(2000, Metrics.KILOMETERS)).as(StepVerifier::create) // - .expectNext(dave) // - .verifyComplete(); - } - - @Test // DATAMONGO-1865 - void shouldErrorOnFindOneWithNonUniqueResult() { - repository.findOneByLastname(dave.getLastname()).as(StepVerifier::create) - .expectError(IncorrectResultSizeDataAccessException.class).verify(); - } - - @Test // DATAMONGO-1865 - void shouldReturnFirstFindFirstWithMoreResults() { - repository.findFirstByLastname(dave.getLastname()).as(StepVerifier::create).expectNextCount(1).verifyComplete(); - } - - @Test // DATAMONGO-2030 - void shouldReturnExistsBy() { - repository.existsByLastname(dave.getLastname()).as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-1979 - void findAppliesAnnotatedSort() { - - repository.findByAgeGreaterThan(40).collectList().as(StepVerifier::create).consumeNextWith(result -> { - assertThat(result).containsSequence(carter, boyd, dave, leroi); - }).verifyComplete(); - } - - @Test // DATAMONGO-1979 - void findWithSortOverwritesAnnotatedSort() { - - repository.findByAgeGreaterThan(40, Sort.by(Direction.ASC, "age")).collectList().as(StepVerifier::create) - .consumeNextWith(result -> { - assertThat(result).containsSequence(leroi, dave, boyd, carter); - }).verifyComplete(); - } - - @Test // DATAMONGO-2181 - void considersRepositoryCollectionName() { - - repository.deleteAll() // - .as(StepVerifier::create) // - .verifyComplete(); - - contactRepository.deleteAll() // - .as(StepVerifier::create) // - .verifyComplete(); - - leroi.id = null; - boyd.id = null; - contactRepository.saveAll(Arrays.asList(leroi, boyd)) // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - - repository.count() // - .as(StepVerifier::create) // - .expectNext(0L) // - .verifyComplete(); - - contactRepository.count() // - .as(StepVerifier::create) // - .expectNext(2L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2182 - void shouldFindPersonsWhenUsingQueryDslPerdicatedOnIdProperty() { - - repository.findAll(person.id.in(Arrays.asList(dave.id, carter.id))) // - .collectList() // - .as(StepVerifier::create) // - .assertNext(actual -> { - assertThat(actual).containsExactlyInAnyOrder(dave, carter); - }).verifyComplete(); - } - - @Test // DATAMONGO-2153 - void findListOfSingleValue() { - - repository.findAllLastnames() // - .collectList() // - .as(StepVerifier::create) // - .assertNext(actual -> { - assertThat(actual) - .contains("Lessard", "Keys", "Tinsley", "Beauford", "Moore", "Matthews"); - }).verifyComplete(); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithPlaceholderValue() { - - repository.groupByLastnameAnd("firstname") // - .collectList() // - .as(StepVerifier::create) // - .assertNext(actual -> { - assertThat(actual) // - .contains(new PersonAggregate("Lessard", "Stefan")) // - .contains(new PersonAggregate("Keys", "Alicia")) // - .contains(new PersonAggregate("Tinsley", "Boyd")) // - .contains(new PersonAggregate("Beauford", "Carter")) // - .contains(new PersonAggregate("Moore", "Leroi")) // - .contains(new PersonAggregate("Matthews", Arrays.asList("Dave", "Oliver August"))); - }).verifyComplete(); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithSort() { - - repository.groupByLastnameAnd("firstname", Sort.by("lastname")) // - .collectList() // - .as(StepVerifier::create) // - .assertNext(actual -> { - assertThat(actual) // - .containsSequence( // - new PersonAggregate("Beauford", "Carter"), // - new PersonAggregate("Keys", "Alicia"), // - new PersonAggregate("Lessard", "Stefan"), // - new PersonAggregate("Matthews", Arrays.asList("Dave", "Oliver August")), // - new PersonAggregate("Moore", "Leroi"), // - new PersonAggregate("Tinsley", "Boyd")); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithPageable() { - - repository.groupByLastnameAnd("firstname", PageRequest.of(1, 2, Sort.by("lastname"))) // - .collectList() // - .as(StepVerifier::create) // - .assertNext(actual -> { - assertThat(actual) // - .containsExactly( // - new PersonAggregate("Lessard", "Stefan"), // - new PersonAggregate("Matthews", Arrays.asList("Dave", "Oliver August"))); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithSingleSimpleResult() { - - repository.sumAge() // - .as(StepVerifier::create) // - .expectNext(245L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithAggregationResultAsReturnType() { - - repository.sumAgeAndReturnRawResult() // - .as(StepVerifier::create) // - .expectNext(new org.bson.Document("_id", null).append("total", 245)) // - .verifyComplete(); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithAggregationResultAsReturnTypeAndProjection() { - - repository.sumAgeAndReturnSumWrapper() // - .as(StepVerifier::create) // - .expectNext(new SumAge(245L)) // - .verifyComplete(); - } - - @Test // DATAMONGO-2374 - void findsWithNativeProjection() { - - repository.findDocumentById(dave.getId()) // - .as(StepVerifier::create) // - .consumeNextWith(it -> { - assertThat(it).containsEntry("firstname", dave.getFirstname()).containsEntry("lastname", dave.getLastname()); - }).verifyComplete(); - } - - @Test // DATAMONGO-2153 - void annotatedAggregationWithAggregationResultAsMap() { - - repository.sumAgeAndReturnSumAsMap() // - .as(StepVerifier::create) // - .consumeNextWith(it -> { - assertThat(it).isInstanceOf(Map.class); - }).verifyComplete(); - } - - @Test // DATAMONGO-2403 - void annotatedAggregationExtractingSimpleValueIsEmptyForEmptyDocument() { - - Person p = new Person("project-on-lastanme", null); - repository.save(p).then().as(StepVerifier::create).verifyComplete(); - - repository.projectToLastnameAndRemoveId(p.getFirstname()) // - .as(StepVerifier::create) // - .verifyComplete(); - } - - @Test // DATAMONGO-2403 - void annotatedAggregationSkipsEmptyDocumentsWhenExtractingSimpleValue() { - - String firstname = "project-on-lastanme"; - - Person p1 = new Person(firstname, null); - p1.setEmail("p1@example.com"); - Person p2 = new Person(firstname, "lastname"); - p2.setEmail("p2@example.com"); - Person p3 = new Person(firstname, null); - p3.setEmail("p3@example.com"); - - repository.saveAll(Arrays.asList(p1, p2, p3)).then().as(StepVerifier::create).verifyComplete(); - - repository.projectToLastnameAndRemoveId(firstname) // - .as(StepVerifier::create) // - .expectNext("lastname").verifyComplete(); - } - - @Test // DATAMONGO-2406 - void deleteByShouldHandleVoidResultTypeCorrectly() { - - repository.deleteByLastname(dave.getLastname()) // - .as(StepVerifier::create) // - .verifyComplete(); - - template.find(query(where("lastname").is(dave.getLastname())), Person.class) // - .as(StepVerifier::create) // - .verifyComplete(); - } - - @Test // DATAMONGO-1997 - void deleteByShouldAllowDeletedCountAsResult() { - - repository.deleteCountByLastname(dave.getLastname()) // - .as(StepVerifier::create) // - .expectNext(2L) // - .verifyComplete(); - } - - @Test // DATAMONGO-1997 - void deleteByShouldAllowSingleDocumentRemovalCorrectly() { - - repository.deleteSinglePersonByLastname(carter.getLastname()) // - .as(StepVerifier::create) // - .expectNext(carter) // - .verifyComplete(); - - repository.deleteSinglePersonByLastname("dorfuaeB") // - .as(StepVerifier::create) // - .verifyComplete(); - } - - @Test // DATAMONGO-2652 - void deleteAllById() { - - repository.deleteAllById(Arrays.asList(carter.id, dave.id)) // - .as(StepVerifier::create) // - .verifyComplete(); - - repository.count().as(StepVerifier::create) // - .expectNext(PERSON_COUNT - 2L) // - .verifyComplete(); - } - - interface ReactivePersonRepository - extends ReactiveMongoRepository, ReactiveQuerydslPredicateExecutor { - - Flux findByLastname(String lastname); - - Mono findOneByLastname(String lastname); - - Mono findOneProjectedByLastname(String lastname); - - Mono findByLastname(Publisher lastname); - - Flux findByLastnameIn(Publisher lastname); - - Flux findByLastname(String lastname, Sort sort); - - Flux findByLastnameInAndAgeGreaterThan(Flux lastname, int age); - - @Query("{ lastname: { $in: ?0 }, age: { $gt : ?1 } }") - Flux findStringQuery(Flux lastname, Mono age); - - Flux findByLocationWithin(Circle circle); - - Flux findByLocationWithin(Circle circle, Pageable pageable); - - Flux> findByLocationNear(Point point, Distance maxDistance); - - Flux> findByLocationNear(Point point, Distance maxDistance, Pageable pageable); - - Flux findPersonByLocationNear(Point point, Distance maxDistance); - - Mono existsByLastname(String lastname); - - Mono findFirstByLastname(String lastname); - - @Query(sort = "{ age : -1 }") - Flux findByAgeGreaterThan(int age); - - @Query(sort = "{ age : -1 }") - Flux findByAgeGreaterThan(int age, Sort sort); - - @Aggregation("{ '$project': { '_id' : '$lastname' } }") - Flux findAllLastnames(); - - @Aggregation("{ '$group': { '_id' : '$lastname', names : { $addToSet : '$?0' } } }") - Flux groupByLastnameAnd(String property); - - @Aggregation("{ '$group': { '_id' : '$lastname', names : { $addToSet : '$?0' } } }") - Flux groupByLastnameAnd(String property, Sort sort); - - @Aggregation("{ '$group': { '_id' : '$lastname', names : { $addToSet : '$?0' } } }") - Flux groupByLastnameAnd(String property, Pageable page); - - @Aggregation(pipeline = "{ '$group' : { '_id' : null, 'total' : { $sum: '$age' } } }") - Mono sumAge(); - - @Aggregation(pipeline = "{ '$group' : { '_id' : null, 'total' : { $sum: '$age' } } }") - Mono sumAgeAndReturnRawResult(); - - @Aggregation(pipeline = "{ '$group' : { '_id' : null, 'total' : { $sum: '$age' } } }") - Mono sumAgeAndReturnSumWrapper(); - - @Aggregation(pipeline = "{ '$group' : { '_id' : null, 'total' : { $sum: '$age' } } }") - Mono sumAgeAndReturnSumAsMap(); - - @Aggregation( - pipeline = { "{ '$match' : { 'firstname' : '?0' } }", "{ '$project' : { '_id' : 0, 'lastname' : 1 } }" }) - Mono projectToLastnameAndRemoveId(String firstname); - - @Query(value = "{_id:?0}") - Mono findDocumentById(String id); - - Mono deleteByLastname(String lastname); - - Mono deleteCountByLastname(String lastname); - - Mono deleteSinglePersonByLastname(String lastname); - } - - interface ReactiveContactRepository extends ReactiveMongoRepository {} - - interface ReactiveCappedCollectionRepository extends Repository { - - @Tailable - Flux findByKey(String key); - - @Tailable - Flux findProjectionByKey(String key); - - @Tailable - Flux findDtoProjectionByKey(String key); - } - - @Document - @NoArgsConstructor - static class Capped { - - String id; - String key; - double random; - - Capped(String key, double random) { - this.key = key; - this.random = random; - } - } - - interface CappedProjection { - double getRandom(); - } - - @Data - static class DtoProjection { - String id; - double unknown; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactivePersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactivePersonRepository.java deleted file mode 100644 index 2e9ebd98c1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactivePersonRepository.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2016-2021 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.repository; - -import reactor.core.publisher.Flux; - -/** - * Sample reactive repository managing {@link Person} entities. - * - * @author Mark Paluch - */ -public interface ReactivePersonRepository extends ReactiveMongoRepository { - - /** - * Returns all {@link Person}s with the given lastname. - * - * @param lastname - * @return - */ - Flux findByLastname(String lastname); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RedeclaringRepositoryMethodsRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RedeclaringRepositoryMethodsRepository.java deleted file mode 100644 index ee2ab7dce2..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RedeclaringRepositoryMethodsRepository.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2013-2021 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.repository; - -import java.util.List; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -/** - * @author Thomas Darimont - */ -public interface RedeclaringRepositoryMethodsRepository extends MongoRepository { - - /** - * Should not find any users at all. - */ - @Query("{ 'firstname' : '' }") - List findAll(); - - /** - * Should only find users with the firstname 'Oliver'. - * - * @param page - * @return - */ - @Query("{ 'firstname' : 'Oliver August' }") - Page findAll(Pageable page); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RedeclaringRepositoryMethodsTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RedeclaringRepositoryMethodsTests.java deleted file mode 100644 index 997e02d3f6..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RedeclaringRepositoryMethodsTests.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2013-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.test.context.ContextConfiguration; - -/** - * @author Thomas Darimont - * @author Mark Paluch - */ -@ContextConfiguration("config/MongoNamespaceIntegrationTests-context.xml") -class RedeclaringRepositoryMethodsTests extends AbstractPersonRepositoryIntegrationTests { - - @Autowired RedeclaringRepositoryMethodsRepository repository; - - @Test // DATAMONGO-760 - void adjustedWellKnownPagedFindAllMethodShouldReturnOnlyTheUserWithFirstnameOliverAugust() { - - Page page = repository.findAll(PageRequest.of(0, 2)); - - assertThat(page.getNumberOfElements()).isEqualTo(1); - assertThat(page.getContent().get(0).getFirstname()).isEqualTo(oliver.getFirstname()); - } - - @Test // DATAMONGO-760 - void adjustedWllKnownFindAllMethodShouldReturnAnEmptyList() { - - List result = repository.findAll(); - - assertThat(result.isEmpty()).isTrue(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests.java deleted file mode 100644 index 2955d1f3d3..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2011-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataAccessException; -import org.springframework.data.mongodb.core.CollectionCallback; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.MongoException; -import com.mongodb.client.MongoCollection; - -/** - * Integration test for index creation for query methods. - * - * @author Oliver Gierke - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class RepositoryIndexCreationIntegrationTests { - - @Autowired MongoOperations operations; - - @Autowired PersonRepository repository; - - @After - public void tearDown() { - operations.execute(Person.class, new CollectionCallback() { - - public Void doInCollection(MongoCollection collection) throws MongoException, DataAccessException { - - List indexes = new ArrayList(); - collection.listIndexes(Document.class).into(indexes); - - for (Document index : indexes) { - String indexName = index.get("name").toString(); - if (indexName.startsWith("find")) { - collection.dropIndex(indexName); - } - } - - return null; - } - }); - } - - @Test - public void testname() { - - List indexInfo = operations.indexOps(Person.class).getIndexInfo(); - - assertHasIndexForField(indexInfo, "lastname"); - assertHasIndexForField(indexInfo, "firstname"); - assertHasIndexForField(indexInfo, "add"); - } - - private static void assertHasIndexForField(List indexInfo, String... fields) { - - for (IndexInfo info : indexInfo) { - if (info.isIndexForFields(Arrays.asList(fields))) { - return; - } - } - - fail(String.format("Did not find index for field(s) %s in %s!", fields, indexInfo)); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SampleEvaluationContextExtension.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SampleEvaluationContextExtension.java deleted file mode 100644 index 47ce681fe5..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SampleEvaluationContextExtension.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2015-2021 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.repository; - -import java.util.Collections; -import java.util.Map; - -import org.springframework.data.spel.spi.EvaluationContextExtension; - -/** - * A sample implementation of a custom {@link EvaluationContextExtension}. - * - * @author Thomas Darimont - */ -public class SampleEvaluationContextExtension implements EvaluationContextExtension { - - @Override - public String getExtensionId() { - return "security"; - } - - @Override - public Map getProperties() { - return Collections.singletonMap("principal", SampleSecurityContextHolder.getCurrent().getPrincipal()); - } - - /** - * @author Thomas Darimont - */ - public static class SampleSecurityContextHolder { - - private static ThreadLocal auth = new ThreadLocal() { - - @Override - protected SampleAuthentication initialValue() { - return new SampleAuthentication(new SampleUser(-1, "anonymous")); - } - - }; - - public static SampleAuthentication getCurrent() { - return auth.get(); - } - - public static void clear() { - auth.remove(); - } - } - - /** - * @author Thomas Darimont - */ - public static class SampleAuthentication { - - private Object principal; - - public SampleAuthentication(Object principal) { - this.principal = principal; - } - - public Object getPrincipal() { - return principal; - } - - public void setPrincipal(Object principal) { - this.principal = principal; - } - } - - /** - * @author Thomas Darimont - */ - public static class SampleUser { - - private Object id; - private String name; - - public SampleUser(Object id, String name) { - this.id = id; - this.name = name; - } - - public Object getId() { - return id; - } - - public void setId(Object id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public SampleUser withName(String name) { - - this.name = name; - return this; - } - - public SampleUser withId(Object id) { - - this.id = id; - return this; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java deleted file mode 100644 index 5584ae6e3b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright 2016-2021 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.repository; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.domain.ExampleMatcher.*; - -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.Value; -import lombok.With; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.Arrays; - -import javax.annotation.Nullable; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.data.annotation.Id; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.domain.Sort.Order; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory; -import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.util.ClassUtils; - -/** - * Tests for {@link ReactiveMongoRepository}. - * - * @author Mark Paluch - * @author Christoph Strobl - * @author Ruben J Garcia - * @author Clément Petit - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration("classpath:reactive-infrastructure.xml") -public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware, BeanFactoryAware { - - @Autowired private ReactiveMongoTemplate template; - - private ReactiveMongoRepositoryFactory factory; - private ClassLoader classLoader; - private BeanFactory beanFactory; - private ReactivePersonRepository repository; - private ReactiveImmutablePersonRepository immutableRepository; - - private ReactivePerson dave, oliver, carter, boyd, stefan, leroi, alicia; - private ImmutableReactivePerson keith, james, mariah; - - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader == null ? ClassUtils.getDefaultClassLoader() : classLoader; - } - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } - - @BeforeEach - void setUp() { - - factory = new ReactiveMongoRepositoryFactory(template); - factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class); - factory.setBeanClassLoader(classLoader); - factory.setBeanFactory(beanFactory); - factory.setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT); - - repository = factory.getRepository(ReactivePersonRepository.class); - immutableRepository = factory.getRepository(ReactiveImmutablePersonRepository.class); - - repository.deleteAll().as(StepVerifier::create).verifyComplete(); - immutableRepository.deleteAll().as(StepVerifier::create).verifyComplete(); - - dave = new ReactivePerson("Dave", "Matthews", 42); - oliver = new ReactivePerson("Oliver August", "Matthews", 4); - carter = new ReactivePerson("Carter", "Beauford", 49); - boyd = new ReactivePerson("Boyd", "Tinsley", 45); - stefan = new ReactivePerson("Stefan", "Lessard", 34); - leroi = new ReactivePerson("Leroi", "Moore", 41); - alicia = new ReactivePerson("Alicia", "Keys", 30); - keith = new ImmutableReactivePerson(null, "Keith", "Urban", 53); - james = new ImmutableReactivePerson(null, "James", "Arthur", 33); - mariah = new ImmutableReactivePerson(null, "Mariah", "Carey", 51); - - repository.saveAll(Arrays.asList(oliver, dave, carter, boyd, stefan, leroi, alicia)).as(StepVerifier::create) // - .expectNextCount(7) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void existsByIdShouldReturnTrueForExistingObject() { - repository.existsById(dave.id).as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void existsByIdShouldReturnFalseForAbsentObject() { - repository.existsById("unknown").as(StepVerifier::create).expectNext(false).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void existsByMonoOfIdShouldReturnTrueForExistingObject() { - repository.existsById(Mono.just(dave.id)).as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-1712 - void existsByFluxOfIdShouldReturnTrueForExistingObject() { - repository.existsById(Flux.just(dave.id, oliver.id)).as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void existsByEmptyMonoOfIdShouldReturnEmptyMono() { - repository.existsById(Mono.empty()).as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findByIdShouldReturnObject() { - repository.findById(dave.id).as(StepVerifier::create).expectNext(dave).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findByIdShouldCompleteWithoutValueForAbsentObject() { - repository.findById("unknown").as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findByIdByMonoOfIdShouldReturnTrueForExistingObject() { - repository.findById(Mono.just(dave.id)).as(StepVerifier::create).expectNext(dave).verifyComplete(); - } - - @Test // DATAMONGO-1712 - void findByIdByFluxOfIdShouldReturnTrueForExistingObject() { - repository.findById(Flux.just(dave.id, oliver.id)).as(StepVerifier::create).expectNext(dave).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findByIdByEmptyMonoOfIdShouldReturnEmptyMono() { - repository.findById(Mono.empty()).as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findAllShouldReturnAllResults() { - repository.findAll().as(StepVerifier::create).expectNextCount(7).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findAllByIterableOfIdShouldReturnResults() { - repository.findAllById(Arrays.asList(dave.id, boyd.id)).as(StepVerifier::create).expectNextCount(2) - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findAllByPublisherOfIdShouldReturnResults() { - repository.findAllById(Flux.just(dave.id, boyd.id)).as(StepVerifier::create).expectNextCount(2).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findAllByEmptyPublisherOfIdShouldReturnResults() { - repository.findAllById(Flux.empty()).as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void findAllWithSortShouldReturnResults() { - - repository.findAll(Sort.by(new Order(Direction.ASC, "age"))).as(StepVerifier::create) // - .expectNextCount(7) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void countShouldReturnNumberOfRecords() { - repository.count().as(StepVerifier::create).expectNext(7L).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void insertEntityShouldInsertEntity() { - - repository.deleteAll().as(StepVerifier::create).verifyComplete(); - - ReactivePerson person = new ReactivePerson("Homer", "Simpson", 36); - - repository.insert(person).as(StepVerifier::create).expectNext(person).verifyComplete(); - - assertThat(person.getId()).isNotNull(); - } - - @Test // DATAMONGO-1444 - void insertShouldDeferredWrite() { - - ReactivePerson person = new ReactivePerson("Homer", "Simpson", 36); - - repository.insert(person); - - assertThat(person.getId()).isNull(); - } - - @Test // DATAMONGO-1444 - void insertIterableOfEntitiesShouldInsertEntity() { - - repository.deleteAll().as(StepVerifier::create).verifyComplete(); - - dave.setId(null); - oliver.setId(null); - boyd.setId(null); - - repository.insert(Arrays.asList(dave, oliver, boyd)).as(StepVerifier::create) // - .expectNext(dave, oliver, boyd) // - .verifyComplete(); - - assertThat(dave.getId()).isNotNull(); - assertThat(oliver.getId()).isNotNull(); - assertThat(boyd.getId()).isNotNull(); - } - - @Test // DATAMONGO-1444 - void insertPublisherOfEntitiesShouldInsertEntity() { - - repository.deleteAll().as(StepVerifier::create).verifyComplete(); - - dave.setId(null); - oliver.setId(null); - boyd.setId(null); - - repository.insert(Flux.just(dave, oliver, boyd)).as(StepVerifier::create).expectNextCount(3).verifyComplete(); - - assertThat(dave.getId()).isNotNull(); - assertThat(oliver.getId()).isNotNull(); - assertThat(boyd.getId()).isNotNull(); - } - - @Test // DATAMONGO-1444 - void saveEntityShouldUpdateExistingEntity() { - - dave.setFirstname("Hello, Dave"); - dave.setLastname("Bowman"); - - repository.save(dave).as(StepVerifier::create).expectNext(dave).verifyComplete(); - - repository.findByLastname("Matthews").as(StepVerifier::create).expectNext(oliver).verifyComplete(); - - repository.findById(dave.id).as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual.getFirstname()).isEqualTo(dave.getFirstname()); - assertThat(actual.getLastname()).isEqualTo(dave.getLastname()); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void saveEntityShouldInsertNewEntity() { - - ReactivePerson person = new ReactivePerson("Homer", "Simpson", 36); - - repository.save(person).as(StepVerifier::create).expectNext(person).verifyComplete(); - - repository.findById(person.id).as(StepVerifier::create).consumeNextWith(actual -> { - - assertThat(actual.getFirstname()).isEqualTo(person.getFirstname()); - assertThat(actual.getLastname()).isEqualTo(person.getLastname()); - }).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void saveIterableOfNewEntitiesShouldInsertEntity() { - - repository.deleteAll().as(StepVerifier::create).verifyComplete(); - - dave.setId(null); - oliver.setId(null); - boyd.setId(null); - - repository.saveAll(Arrays.asList(dave, oliver, boyd)).as(StepVerifier::create).expectNextCount(3).verifyComplete(); - - assertThat(dave.getId()).isNotNull(); - assertThat(oliver.getId()).isNotNull(); - assertThat(boyd.getId()).isNotNull(); - } - - @Test // DATAMONGO-1444 - void saveIterableOfMixedEntitiesShouldInsertEntity() { - - ReactivePerson person = new ReactivePerson("Homer", "Simpson", 36); - - dave.setFirstname("Hello, Dave"); - dave.setLastname("Bowman"); - - repository.saveAll(Arrays.asList(person, dave)).as(StepVerifier::create).expectNextCount(2).verifyComplete(); - - repository.findById(dave.id).as(StepVerifier::create).expectNext(dave).verifyComplete(); - - assertThat(person.id).isNotNull(); - repository.findById(person.id).as(StepVerifier::create).expectNext(person).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void savePublisherOfEntitiesShouldInsertEntity() { - - repository.deleteAll().as(StepVerifier::create).verifyComplete(); - - dave.setId(null); - oliver.setId(null); - boyd.setId(null); - - repository.saveAll(Flux.just(dave, oliver, boyd)).as(StepVerifier::create).expectNextCount(3).verifyComplete(); - - assertThat(dave.getId()).isNotNull(); - assertThat(oliver.getId()).isNotNull(); - assertThat(boyd.getId()).isNotNull(); - } - - @Test // GH-3609 - void savePublisherOfImmutableEntitiesShouldInsertEntity() { - - immutableRepository.deleteAll().as(StepVerifier::create).verifyComplete(); - - immutableRepository.saveAll(Flux.just(keith)).as(StepVerifier::create) // - .consumeNextWith(actual -> { - assertThat(actual.id).isNotNull(); - }) // - .verifyComplete(); - } - - @Test // DATAMONGO-1444 - void deleteAllShouldRemoveEntities() { - - repository.deleteAll().as(StepVerifier::create).verifyComplete(); - - repository.findAll().as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void deleteByIdShouldRemoveEntity() { - - repository.deleteById(dave.id).as(StepVerifier::create).verifyComplete(); - - repository.findById(dave.id).as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-1712 - void deleteByIdUsingMonoShouldRemoveEntity() { - - repository.deleteById(Mono.just(dave.id)).as(StepVerifier::create).verifyComplete(); - - repository.existsById(dave.id).as(StepVerifier::create).expectNext(false).verifyComplete(); - } - - @Test // DATAMONGO-1712 - void deleteByIdUsingFluxShouldRemoveEntity() { - - repository.deleteById(Flux.just(dave.id, oliver.id)).as(StepVerifier::create).verifyComplete(); - - repository.existsById(dave.id).as(StepVerifier::create).expectNext(false).verifyComplete(); - repository.existsById(oliver.id).as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void deleteShouldRemoveEntity() { - - repository.delete(dave).as(StepVerifier::create).verifyComplete(); - - repository.findById(dave.id).as(StepVerifier::create).verifyComplete(); - - } - - @Test // DATAMONGO-1444 - void deleteIterableOfEntitiesShouldRemoveEntities() { - - repository.deleteAll(Arrays.asList(dave, boyd)).as(StepVerifier::create).verifyComplete(); - - repository.findById(boyd.id).as(StepVerifier::create).verifyComplete(); - - repository.findByLastname("Matthews").as(StepVerifier::create).expectNext(oliver).verifyComplete(); - } - - @Test // DATAMONGO-1444 - void deletePublisherOfEntitiesShouldRemoveEntities() { - - repository.deleteAll(Flux.just(dave, boyd)).as(StepVerifier::create).verifyComplete(); - - repository.findById(boyd.id).as(StepVerifier::create).verifyComplete(); - - repository.findByLastname("Matthews").as(StepVerifier::create).expectNext(oliver).verifyComplete(); - } - - @Test // DATAMONGO-1619 - void findOneByExampleShouldReturnObject() { - - Example example = Example.of(dave); - - repository.findOne(example).as(StepVerifier::create).expectNext(dave).verifyComplete(); - } - - @Test // DATAMONGO-1619 - void findAllByExampleShouldReturnObjects() { - - Example example = Example.of(dave, matching().withIgnorePaths("id", "age", "firstname")); - - repository.findAll(example).as(StepVerifier::create).expectNextCount(2).verifyComplete(); - } - - @Test // DATAMONGO-1619 - void findAllByExampleAndSortShouldReturnObjects() { - - Example example = Example.of(dave, matching().withIgnorePaths("id", "age", "firstname")); - - repository.findAll(example, Sort.by("firstname")).as(StepVerifier::create).expectNext(dave, oliver) - .verifyComplete(); - } - - @Test // DATAMONGO-1619 - void countByExampleShouldCountObjects() { - - Example example = Example.of(dave, matching().withIgnorePaths("id", "age", "firstname")); - - repository.count(example).as(StepVerifier::create).expectNext(2L).verifyComplete(); - } - - @Test // DATAMONGO-1619 - void existsByExampleShouldReturnExisting() { - - Example example = Example.of(dave, matching().withIgnorePaths("id", "age", "firstname")); - - repository.exists(example).as(StepVerifier::create).expectNext(true).verifyComplete(); - } - - @Test // DATAMONGO-1619 - void existsByExampleShouldReturnNonExisting() { - - Example example = Example.of(new ReactivePerson("foo", "bar", -1)); - - repository.exists(example).as(StepVerifier::create).expectNext(false).verifyComplete(); - } - - @Test // DATAMONGO-1619 - void findOneShouldEmitIncorrectResultSizeDataAccessExceptionWhenMoreThanOneElementFound() { - - Example example = Example.of(new ReactivePerson(null, "Matthews", -1), - matching().withIgnorePaths("age")); - - repository.findOne(example).as(StepVerifier::create).expectError(IncorrectResultSizeDataAccessException.class); - } - - @Test // DATAMONGO-1907 - void findOneByExampleWithoutResultShouldCompleteEmpty() { - - Example example = Example.of(new ReactivePerson("foo", "bar", -1)); - - repository.findOne(example).as(StepVerifier::create).verifyComplete(); - } - - interface ReactivePersonRepository extends ReactiveMongoRepository { - - Flux findByLastname(String lastname); - - } - - interface ReactiveImmutablePersonRepository extends ReactiveMongoRepository { - - } - - @Data - @NoArgsConstructor - static class ReactivePerson { - - @Id String id; - - String firstname; - String lastname; - int age; - - ReactivePerson(String firstname, String lastname, int age) { - - this.firstname = firstname; - this.lastname = lastname; - this.age = age; - } - } - - @With - @Value - static class ImmutableReactivePerson { - - @Id String id; - - String firstname; - String lastname; - int age; - - ImmutableReactivePerson(@Nullable String id, String firstname, String lastname, int age) { - this.id = id; - this.firstname = firstname; - this.lastname = lastname; - this.age = age; - } - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SumAge.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SumAge.java deleted file mode 100644 index 76c17aa734..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SumAge.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2019-2021 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.repository; - -import lombok.Value; - -/** - * @author Christoph Strobl - */ -@Value -class SumAge { - - private Long total; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/User.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/User.java deleted file mode 100644 index 8f5176add1..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/User.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012-2021 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.repository; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; - -/** - * @author Oliver Gierke - * @author Thomas Darimont - */ -@Document -public class User { - - @Id String id; - String username; - - /** - * @return the id - */ - public String getId() { - return id; - } - - /** - * @param id the id to set - */ - public void setId(String id) { - this.id = id; - } - - /** - * @return the username - */ - public String getUsername() { - return username; - } - - /** - * @param username the username to set - */ - public void setUsername(String username) { - this.username = username; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java deleted file mode 100644 index 960fa062ba..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014-2021 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.repository; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.util.ObjectUtils; - -/** - * @author Christoph Strobl - * @author Oliver Gierke - */ -@Document -public class UserWithComplexId { - - @Id MyId id; - String firstname; - - @Override - public int hashCode() { - - int result = 31; - - result += 17 * ObjectUtils.nullSafeHashCode(id); - - return result; - } - - @Override - public boolean equals(Object obj) { - - if (obj == this) { - return true; - } - - if (!(obj instanceof UserWithComplexId)) { - return false; - } - - UserWithComplexId that = (UserWithComplexId) obj; - - return ObjectUtils.nullSafeEquals(this.id, that.id); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexIdRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexIdRepository.java deleted file mode 100644 index c333acb436..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexIdRepository.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2014-2021 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.repository; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Collection; -import java.util.List; - -import org.springframework.core.annotation.AliasFor; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.repository.CrudRepository; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -public interface UserWithComplexIdRepository extends CrudRepository { - - @Query("{'_id': {$in: ?0}}") - List findByUserIds(Collection ids); - - @Query("{'_id': ?0}") - UserWithComplexId getUserByComplexId(MyId id); - - @ComposedQueryAnnotation - UserWithComplexId getUserUsingComposedAnnotationByComplexId(MyId id); - - @ComposedMetaAnnotation - @Query("{'_id': {$in: ?0}}") - List findUsersUsingComposedMetaAnnotationByUserIds(Collection ids); - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.METHOD }) - @Document - @Query - @interface ComposedQueryAnnotation { - - @AliasFor(annotation = Query.class, attribute = "value") - String myQuery() default "{'_id': ?0}"; - } - - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.METHOD }) - @Meta - @interface ComposedMetaAnnotation { - - @AliasFor(annotation = Meta.class, attribute = "maxExecutionTimeMs") - long execTime() default -1; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UsernameAndPassword.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UsernameAndPassword.java deleted file mode 100644 index 44eccf7e43..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UsernameAndPassword.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2013-2021 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.repository; - -/** - * @author Oliver Gierke - */ -public class UsernameAndPassword implements Credentials { - - String username; - String password; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java deleted file mode 100644 index 5178e88bb7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019-2021 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.repository; - -import lombok.Data; -import lombok.NoArgsConstructor; - -import org.springframework.data.annotation.Version; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.lang.Nullable; - -/** - * @author Christoph Strobl - */ -@Document -@Data -@NoArgsConstructor -public class VersionedPerson extends Contact { - - private String firstname; - private @Nullable String lastname; - - private @Version Long version; - - public VersionedPerson(String firstname) { - this(firstname, null); - } - - public VersionedPerson(String firstname, @Nullable String lastname) { - - this.firstname = firstname; - this.lastname = lastname; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiExtensionIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiExtensionIntegrationTests.java deleted file mode 100644 index 73ff1c22e0..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiExtensionIntegrationTests.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2012-2021 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.repository.cdi; - -import static org.assertj.core.api.Assertions.*; - -import javax.enterprise.inject.se.SeContainer; -import javax.enterprise.inject.se.SeContainerInitializer; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import org.springframework.data.mongodb.repository.Person; - -/** - * Integration tests for {@link MongoRepositoryExtension}. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -public class CdiExtensionIntegrationTests { - - static SeContainer container; - - @BeforeAll - public static void setUp() { - - container = SeContainerInitializer.newInstance() // - .disableDiscovery() // - .addPackages(CdiExtensionIntegrationTests.class) // - .initialize(); - } - - @AfterAll - public static void tearDown() { - container.close(); - } - - @Test // DATAMONGO-356, DATAMONGO-1785 - public void bootstrapsRepositoryCorrectly() { - - RepositoryClient client = container.select(RepositoryClient.class).get(); - CdiPersonRepository repository = client.getRepository(); - - assertThat(repository).isNotNull(); - - repository.deleteAll(); - - Person person = new Person("Dave", "Matthews"); - Person result = repository.save(person); - - assertThat(result).isNotNull(); - assertThat(repository.findById(person.getId()).get().getId()).isEqualTo(result.getId()); - } - - @Test // DATAMONGO-1017, DATAMONGO-1785 - public void returnOneFromCustomImpl() { - - RepositoryClient repositoryConsumer = container.select(RepositoryClient.class).get(); - assertThat(repositoryConsumer.getSamplePersonRepository().returnOne()).isEqualTo(1); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiPersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiPersonRepository.java deleted file mode 100644 index c8150925ed..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/CdiPersonRepository.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012-2021 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.repository.cdi; - -import java.util.Optional; - -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.repository.Repository; - -public interface CdiPersonRepository extends Repository { - - void deleteAll(); - - Person save(Person person); - - Optional findById(String id); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/MongoTemplateProducer.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/MongoTemplateProducer.java deleted file mode 100644 index 22de5a6205..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/MongoTemplateProducer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012-2021 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.repository.cdi; - -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.inject.Produces; - -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.data.mongodb.test.util.MongoTestUtils; - -/** - * Simple component exposing a {@link MongoOperations} instance as CDI bean. - * - * @author Oliver Gierke - */ -class MongoTemplateProducer { - - @Produces - @ApplicationScoped - public MongoOperations createMongoTemplate() { - - MongoDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(MongoTestUtils.client(), "database"); - return new MongoTemplate(factory); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/RepositoryClient.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/RepositoryClient.java deleted file mode 100644 index de5e760f7b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/RepositoryClient.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2021 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.repository.cdi; - -import javax.inject.Inject; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -class RepositoryClient { - - @Inject CdiPersonRepository repository; - @Inject SamplePersonRepository samplePersonRepository; - - /** - * @return the repository - */ - public CdiPersonRepository getRepository() { - return repository; - } - - public SamplePersonRepository getSamplePersonRepository() { - return samplePersonRepository; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/SamplePersonFragment.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/SamplePersonFragment.java deleted file mode 100644 index e929d71475..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/SamplePersonFragment.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014-2021 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.repository.cdi; - -/** - * @author Mark Paluch - */ -interface SamplePersonFragment { - - int returnOne(); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/SamplePersonFragmentImpl.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/SamplePersonFragmentImpl.java deleted file mode 100644 index 342754a5b7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/SamplePersonFragmentImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2014-2021 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.repository.cdi; - -/** - * @author Mark Paluch - */ -class SamplePersonFragmentImpl implements SamplePersonFragment { - - @Override - public int returnOne() { - return 1; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/SamplePersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/SamplePersonRepository.java deleted file mode 100644 index 567370a4dc..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/cdi/SamplePersonRepository.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014-2021 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.repository.cdi; - -import org.springframework.data.mongodb.core.Person; -import org.springframework.data.repository.Repository; - -/** - * @author Mark Paluch - */ -public interface SamplePersonRepository extends Repository, SamplePersonFragment {} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests.java deleted file mode 100644 index 803903bb7e..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012-2021 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.repository.config; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionReader; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.context.ApplicationContext; -import org.springframework.core.io.ClassPathResource; -import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.repository.AbstractPersonRepositoryIntegrationTests; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.repository.support.Repositories; -import org.springframework.test.context.ContextConfiguration; - -/** - * Test class using the namespace configuration to set up the repository instance. - * - * @author Oliver Gierke - */ -@ContextConfiguration -class MongoNamespaceIntegrationTests extends AbstractPersonRepositoryIntegrationTests { - - DefaultListableBeanFactory factory; - BeanDefinitionReader reader; - - @Autowired ApplicationContext context; - - @BeforeEach - @Override - public void setUp() throws InterruptedException { - super.setUp(); - factory = new DefaultListableBeanFactory(); - reader = new XmlBeanDefinitionReader(factory); - } - - @Test - void assertDefaultMappingContextIsWired() { - - reader.loadBeanDefinitions(new ClassPathResource("MongoNamespaceIntegrationTests-context.xml", getClass())); - BeanDefinition definition = factory.getBeanDefinition("personRepository"); - assertThat(definition).isNotNull(); - } - - @Test // DATAMONGO-581 - void exposesPersistentEntity() { - - Repositories repositories = new Repositories(context); - PersistentEntity entity = repositories.getPersistentEntity(Person.class); - assertThat(entity).isNotNull(); - assertThat(entity).isInstanceOf(MongoPersistentEntity.class); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoriesRegistrarIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoriesRegistrarIntegrationTests.java deleted file mode 100644 index f40400212a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoriesRegistrarIntegrationTests.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2021 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.repository.config; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.data.mongodb.repository.PersonRepository; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration tests for {@link MongoRepositoriesRegistrar}. - * - * @author Oliver Gierke - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class MongoRepositoriesRegistrarIntegrationTests { - - @Configuration - @EnableMongoRepositories(basePackages = "org.springframework.data.mongodb.repository", includeFilters=@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = PersonRepository.class)) - static class Config { - - @Bean - public MongoOperations mongoTemplate() throws Exception { - return new MongoTemplate(new SimpleMongoClientDatabaseFactory(MongoTestUtils.client(), "database")); - } - } - - @Autowired PersonRepository personRepository; - @Autowired ApplicationContext context; - - @Test - public void testConfiguration() {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java deleted file mode 100644 index 3b3b58a7ba..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2014-2021 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.repository.config; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Collection; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.core.env.Environment; -import org.springframework.core.env.StandardEnvironment; -import org.springframework.core.io.ResourceLoader; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.type.StandardAnnotationMetadata; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; -import org.springframework.data.repository.config.RepositoryConfiguration; -import org.springframework.data.repository.config.RepositoryConfigurationSource; - -/** - * Unit tests for {@link MongoRepositoryConfigurationExtension}. - * - * @author Oliver Gierke - * @since 1.6 - */ -public class MongoRepositoryConfigurationExtensionUnitTests { - - StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(Config.class, true); - ResourceLoader loader = new PathMatchingResourcePatternResolver(); - Environment environment = new StandardEnvironment(); - BeanDefinitionRegistry registry = new DefaultListableBeanFactory(); - - RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata, - EnableMongoRepositories.class, loader, environment, registry); - - @Test // DATAMONGO-1009 - public void isStrictMatchIfDomainTypeIsAnnotatedWithDocument() { - - MongoRepositoryConfigurationExtension extension = new MongoRepositoryConfigurationExtension(); - assertHasRepo(SampleRepository.class, extension.getRepositoryConfigurations(configurationSource, loader, true)); - } - - @Test // DATAMONGO-1009 - public void isStrictMatchIfRepositoryExtendsStoreSpecificBase() { - - MongoRepositoryConfigurationExtension extension = new MongoRepositoryConfigurationExtension(); - assertHasRepo(StoreRepository.class, extension.getRepositoryConfigurations(configurationSource, loader, true)); - } - - @Test // DATAMONGO-1009 - public void isNotStrictMatchIfDomainTypeIsNotAnnotatedWithDocument() { - - MongoRepositoryConfigurationExtension extension = new MongoRepositoryConfigurationExtension(); - assertDoesNotHaveRepo(UnannotatedRepository.class, - extension.getRepositoryConfigurations(configurationSource, loader, true)); - } - - private static void assertHasRepo(Class repositoryInterface, - Collection> configs) { - - for (RepositoryConfiguration config : configs) { - if (config.getRepositoryInterface().equals(repositoryInterface.getName())) { - return; - } - } - - fail("Expected to find config for repository interface ".concat(repositoryInterface.getName()).concat(" but got ") - .concat(configs.toString())); - } - - private static void assertDoesNotHaveRepo(Class repositoryInterface, - Collection> configs) { - - for (RepositoryConfiguration config : configs) { - if (config.getRepositoryInterface().equals(repositoryInterface.getName())) { - fail("Expected not to find config for repository interface ".concat(repositoryInterface.getName())); - } - } - } - - @EnableMongoRepositories(considerNestedRepositories = true) - static class Config { - - } - - @Document - static class Sample {} - - interface SampleRepository extends Repository {} - - interface UnannotatedRepository extends Repository {} - - interface StoreRepository extends MongoRepository {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoriesRegistrarIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoriesRegistrarIntegrationTests.java deleted file mode 100644 index a98b385941..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoriesRegistrarIntegrationTests.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.config; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.repository.ReactivePersonRepository; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration tests for {@link ReactiveMongoRepositoriesRegistrar}. - * - * @author Mark Paluch - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class ReactiveMongoRepositoriesRegistrarIntegrationTests { - - @Configuration - @EnableReactiveMongoRepositories(basePackages = "org.springframework.data.mongodb.repository") - static class Config { - - @Bean - public ReactiveMongoTemplate reactiveMongoTemplate() throws Exception { - return new ReactiveMongoTemplate( - new SimpleReactiveMongoDatabaseFactory(MongoTestUtils.reactiveClient(), "database")); - } - } - - @Autowired ReactivePersonRepository personRepository; - @Autowired ApplicationContext context; - - @Test // DATAMONGO-1444 - public void testConfiguration() {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java deleted file mode 100644 index 0897369c90..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.config; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Collection; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.core.env.Environment; -import org.springframework.core.env.StandardEnvironment; -import org.springframework.core.io.ResourceLoader; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.type.StandardAnnotationMetadata; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.repository.ReactiveMongoRepository; -import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; -import org.springframework.data.repository.config.RepositoryConfiguration; -import org.springframework.data.repository.config.RepositoryConfigurationSource; -import org.springframework.data.repository.reactive.ReactiveCrudRepository; -import org.springframework.data.repository.reactive.RxJava2CrudRepository; - -/** - * Unit tests for {@link ReactiveMongoRepositoryConfigurationExtension}. - * - * @author Mark Paluch - */ -public class ReactiveMongoRepositoryConfigurationExtensionUnitTests { - - StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(Config.class, true); - ResourceLoader loader = new PathMatchingResourcePatternResolver(); - Environment environment = new StandardEnvironment(); - BeanDefinitionRegistry registry = new DefaultListableBeanFactory(); - - RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata, - EnableReactiveMongoRepositories.class, loader, environment, registry); - - @Test // DATAMONGO-1444 - public void isStrictMatchIfDomainTypeIsAnnotatedWithDocument() { - - ReactiveMongoRepositoryConfigurationExtension extension = new ReactiveMongoRepositoryConfigurationExtension(); - assertHasRepo(SampleRepository.class, extension.getRepositoryConfigurations(configurationSource, loader, true)); - } - - @Test // DATAMONGO-1444 - public void isStrictMatchIfRepositoryExtendsStoreSpecificBase() { - - ReactiveMongoRepositoryConfigurationExtension extension = new ReactiveMongoRepositoryConfigurationExtension(); - assertHasRepo(StoreRepository.class, extension.getRepositoryConfigurations(configurationSource, loader, true)); - } - - @Test // DATAMONGO-1444 - public void isNotStrictMatchIfDomainTypeIsNotAnnotatedWithDocument() { - - ReactiveMongoRepositoryConfigurationExtension extension = new ReactiveMongoRepositoryConfigurationExtension(); - assertDoesNotHaveRepo(UnannotatedRepository.class, - extension.getRepositoryConfigurations(configurationSource, loader, true)); - } - - private static void assertHasRepo(Class repositoryInterface, - Collection> configs) { - - for (RepositoryConfiguration config : configs) { - if (config.getRepositoryInterface().equals(repositoryInterface.getName())) { - return; - } - } - - fail("Expected to find config for repository interface ".concat(repositoryInterface.getName()).concat(" but got ") - .concat(configs.toString())); - } - - private static void assertDoesNotHaveRepo(Class repositoryInterface, - Collection> configs) { - - for (RepositoryConfiguration config : configs) { - if (config.getRepositoryInterface().equals(repositoryInterface.getName())) { - fail("Expected not to find config for repository interface ".concat(repositoryInterface.getName())); - } - } - } - - @EnableReactiveMongoRepositories(considerNestedRepositories = true) - static class Config { - - } - - @Document - static class Sample {} - - static class Store {} - - interface SampleRepository extends ReactiveCrudRepository {} - - interface UnannotatedRepository extends RxJava2CrudRepository {} - - interface StoreRepository extends ReactiveMongoRepository {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/lazy/AllowNestedMongoRepositoriesRepositoryConfigTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/lazy/AllowNestedMongoRepositoriesRepositoryConfigTests.java deleted file mode 100644 index 0b4b82a229..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/lazy/AllowNestedMongoRepositoriesRepositoryConfigTests.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2013-2021 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.repository.config.lazy; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.mongodb.repository.config.lazy.ClassWithNestedRepository.NestedUserRepository; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration test for repository namespace configuration with nested repositories. - * - * @author Thomas Darimont - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("AllowNestedMongoRepositoriesRepositoryConfigTests-context.xml") -public class AllowNestedMongoRepositoriesRepositoryConfigTests { - - @Autowired NestedUserRepository fooRepository; - - @Test // DATAMONGO-780 - public void shouldFindNestedRepository() { - assertThat(fooRepository).isNotNull(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/lazy/ClassWithNestedRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/lazy/ClassWithNestedRepository.java deleted file mode 100644 index dd7fed445d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/lazy/ClassWithNestedRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2013-2021 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.repository.config.lazy; - -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.data.mongodb.repository.User; - -/** - * @author Thomas Darimont - */ -public class ClassWithNestedRepository { - - interface NestedUserRepository extends MongoRepository {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/lazy/NestedMongoRepositoriesJavaConfigTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/lazy/NestedMongoRepositoriesJavaConfigTests.java deleted file mode 100644 index 54d92e4d93..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/lazy/NestedMongoRepositoriesJavaConfigTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2013-2021 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.repository.config.lazy; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportResource; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; -import org.springframework.data.mongodb.repository.config.lazy.ClassWithNestedRepository.NestedUserRepository; -import org.springframework.data.repository.support.Repositories; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration test for the combination of JavaConfig and an {@link Repositories} wrapper. - * - * @author Thomas Darimont - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class NestedMongoRepositoriesJavaConfigTests { - - @Configuration - @EnableMongoRepositories(considerNestedRepositories = true) - @ImportResource("classpath:infrastructure.xml") - static class Config {} - - @Autowired NestedUserRepository nestedUserRepository; - - @Test // DATAMONGO-780 - public void shouldSupportNestedRepositories() { - assertThat(nestedUserRepository).isNotNull(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/ComposedRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/ComposedRepository.java deleted file mode 100644 index 704b764c0d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/ComposedRepository.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017-2021 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.repository.custom; - -import org.springframework.data.mongodb.core.User; -import org.springframework.data.repository.Repository; - -/** - * @author Mark Paluch - */ -public interface ComposedRepository extends Repository, RepositoryMixin { - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/ComposedRepositoryImplementationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/ComposedRepositoryImplementationTests.java deleted file mode 100644 index 6b174aba50..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/ComposedRepositoryImplementationTests.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017-2021 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.repository.custom; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.context.annotation.ImportResource; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration tests for composed Repository implementations. - * - * @author Mark Paluch - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class ComposedRepositoryImplementationTests { - - @Configuration - @EnableMongoRepositories(includeFilters=@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ComposedRepository.class)) - @ImportResource("classpath:infrastructure.xml") - static class Config {} - - @Autowired ComposedRepository composedRepository; - - @Test // DATAMONGO-1702 - public void shouldExecuteMethodOnCustomRepositoryImplementation() { - assertThat(composedRepository.getFoo()).isEqualTo("foo"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomMongoRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomMongoRepository.java deleted file mode 100644 index ff7a3ee515..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomMongoRepository.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2013-2021 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.repository.custom; - -import java.util.List; - -import org.springframework.data.mongodb.repository.User; -import org.springframework.data.repository.Repository; - -/** - * @author Thomas Darimont - */ -public interface CustomMongoRepository extends Repository { - - List findByUsernameCustom(String username); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomMongoRepositoryImpl.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomMongoRepositoryImpl.java deleted file mode 100644 index 0db511de16..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomMongoRepositoryImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013-2021 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.repository.custom; - -import java.util.Arrays; -import java.util.List; - -import org.springframework.data.mongodb.repository.User; - -/** - * @author Thomas Darimont - */ -public class CustomMongoRepositoryImpl implements CustomMongoRepository { - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.custom.CustomMongoRepository#findByFullName() - */ - @Override - public List findByUsernameCustom(String username) { - - User user = new User(); - user.setUsername(username); - return Arrays.asList(user); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveMongoRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveMongoRepository.java deleted file mode 100644 index 003a1589b3..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveMongoRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.custom; - -import org.springframework.data.mongodb.repository.User; -import org.springframework.data.repository.reactive.RxJava2CrudRepository; - -/** - * @author Mark Paluch - */ -public interface CustomReactiveMongoRepository - extends RxJava2CrudRepository, CustomReactiveMongoRepositoryCustom { - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveMongoRepositoryCustom.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveMongoRepositoryCustom.java deleted file mode 100644 index 8205ceac7c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveMongoRepositoryCustom.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.custom; - -import java.util.List; - -import org.springframework.data.mongodb.repository.User; - -/** - * @author Mark Paluch - */ -public interface CustomReactiveMongoRepositoryCustom { - - List findByUsernameCustom(String username); - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveMongoRepositoryImpl.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveMongoRepositoryImpl.java deleted file mode 100644 index ea8935606f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveMongoRepositoryImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.custom; - -import java.util.Collections; -import java.util.List; - -import org.springframework.data.mongodb.repository.User; - -/** - * @author Mark Paluch - */ -public class CustomReactiveMongoRepositoryImpl implements CustomReactiveMongoRepositoryCustom { - - @Override - public List findByUsernameCustom(String username) { - - User user = new User(); - user.setUsername(username); - - return Collections.singletonList(user); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveRepositoryImplementationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveRepositoryImplementationTests.java deleted file mode 100644 index e3f0c397f5..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomReactiveRepositoryImplementationTests.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.custom; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.context.annotation.ImportResource; -import org.springframework.data.mongodb.repository.User; -import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration tests for custom reactive Repository implementations. - * - * @author Mark Paluch - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class CustomReactiveRepositoryImplementationTests { - - @Configuration - @EnableReactiveMongoRepositories(includeFilters=@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = CustomReactiveMongoRepository.class)) - @ImportResource("classpath:reactive-infrastructure.xml") - static class Config {} - - @Autowired CustomReactiveMongoRepository customMongoRepository; - - @Test // DATAMONGO-1444 - public void shouldExecuteMethodOnCustomRepositoryImplementation() { - - String username = "bubu"; - List users = customMongoRepository.findByUsernameCustom(username); - - assertThat(users.size()).isEqualTo(1); - assertThat(users.get(0)).isNotNull(); - assertThat(users.get(0).getUsername()).isEqualTo(username); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomRepositoryImplementationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomRepositoryImplementationTests.java deleted file mode 100644 index ec20650757..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/CustomRepositoryImplementationTests.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2013-2021 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.repository.custom; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.context.annotation.ImportResource; -import org.springframework.data.mongodb.repository.User; -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -/** - * Integration tests for custom Repository implementations. - * - * @author Thomas Darimont - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class CustomRepositoryImplementationTests { - - @Configuration - @EnableMongoRepositories(includeFilters=@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = CustomMongoRepository.class)) - @ImportResource("classpath:infrastructure.xml") - static class Config {} - - @Autowired CustomMongoRepository customMongoRepository; - - @Test // DATAMONGO-804 - public void shouldExecuteMethodOnCustomRepositoryImplementation() { - - String username = "bubu"; - List users = customMongoRepository.findByUsernameCustom(username); - - assertThat(users.size()).isEqualTo(1); - assertThat(users.get(0)).isNotNull(); - assertThat(users.get(0).getUsername()).isEqualTo(username); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/RepositoryMixin.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/RepositoryMixin.java deleted file mode 100644 index d4ba891cf6..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/RepositoryMixin.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017-2021 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.repository.custom; - -/** - * @author Mark Paluch - */ -public interface RepositoryMixin { - - String getFoo(); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/RepositoryMixinImpl.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/RepositoryMixinImpl.java deleted file mode 100644 index 603ae14dd7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/custom/RepositoryMixinImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017-2021 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.repository.custom; - -/** - * @author Mark Paluch - */ -public class RepositoryMixinImpl implements RepositoryMixin { - - @Override - public String getFoo() { - return "foo"; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java deleted file mode 100644 index c2803b6124..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright 2014-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import java.lang.reflect.Method; -import java.util.List; -import java.util.Locale; -import java.util.Optional; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind; -import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.Person; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.Meta; -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; - -import com.mongodb.client.result.DeleteResult; - -/** - * Unit tests for {@link AbstractMongoQuery}. - * - * @author Christoph Strobl - * @author Oliver Gierke - * @author Thomas Darimont - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class AbstractMongoQueryUnitTests { - - @Mock MongoOperations mongoOperationsMock; - @Mock ExecutableFind executableFind; - @Mock FindWithQuery withQueryMock; - @Mock BasicMongoPersistentEntity persitentEntityMock; - @Mock MongoMappingContext mappingContextMock; - @Mock DeleteResult deleteResultMock; - - @BeforeEach - void setUp() { - - doReturn("persons").when(persitentEntityMock).getCollection(); - doReturn(persitentEntityMock).when(mappingContextMock).getPersistentEntity(Mockito.any(Class.class)); - doReturn(persitentEntityMock).when(mappingContextMock).getRequiredPersistentEntity(Mockito.any(Class.class)); - doReturn(Person.class).when(persitentEntityMock).getType(); - - DbRefResolver dbRefResolver = new DefaultDbRefResolver(mock(MongoDatabaseFactory.class)); - MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContextMock); - converter.afterPropertiesSet(); - - doReturn(converter).when(mongoOperationsMock).getConverter(); - doReturn(executableFind).when(mongoOperationsMock).query(any()); - doReturn(withQueryMock).when(executableFind).as(any()); - doReturn(withQueryMock).when(withQueryMock).matching(any(Query.class)); - - when(mongoOperationsMock.remove(any(), any(), anyString())).thenReturn(deleteResultMock); - } - - @Test // DATAMONGO-566 - void testDeleteExecutionCallsRemoveCorrectly() { - - createQueryForMethod("deletePersonByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" }); - - verify(mongoOperationsMock, times(1)).remove(any(), eq(Person.class), eq("persons")); - verify(mongoOperationsMock, times(0)).find(any(), any(), any()); - } - - @Test // DATAMONGO-566, DATAMONGO-1040 - void testDeleteExecutionLoadsListOfRemovedDocumentsWhenReturnTypeIsCollectionLike() { - - createQueryForMethod("deleteByLastname", String.class).setDeleteQuery(true).execute(new Object[] { "booh" }); - - verify(mongoOperationsMock, times(1)).findAllAndRemove(any(), eq(Person.class), eq("persons")); - } - - @Test // DATAMONGO-566 - void testDeleteExecutionReturnsZeroWhenWriteResultIsNull() { - - MongoQueryFake query = createQueryForMethod("deletePersonByLastname", String.class); - query.setDeleteQuery(true); - - assertThat(query.execute(new Object[] { "fake" })).isEqualTo(0L); - } - - @Test // DATAMONGO-566, DATAMONGO-978 - void testDeleteExecutionReturnsNrDocumentsDeletedFromWriteResult() { - - when(deleteResultMock.getDeletedCount()).thenReturn(100L); - when(deleteResultMock.wasAcknowledged()).thenReturn(true); - - MongoQueryFake query = createQueryForMethod("deletePersonByLastname", String.class); - query.setDeleteQuery(true); - - assertThat(query.execute(new Object[] { "fake" })).isEqualTo(100L); - verify(mongoOperationsMock, times(1)).remove(any(), eq(Person.class), eq("persons")); - } - - @Test // DATAMONGO-957 - void metadataShouldNotBeAddedToQueryWhenNotPresent() { - - MongoQueryFake query = createQueryForMethod("findByFirstname", String.class); - query.execute(new Object[] { "fake" }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - - verify(executableFind).as(Person.class); - verify(withQueryMock).matching(captor.capture()); - - assertThat(captor.getValue().getMeta().getComment()).isNull(); - ; - } - - @Test // DATAMONGO-957 - void metadataShouldBeAddedToQueryCorrectly() { - - MongoQueryFake query = createQueryForMethod("findByFirstname", String.class, Pageable.class); - query.execute(new Object[] { "fake", PageRequest.of(0, 10) }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - - verify(executableFind).as(Person.class); - verify(withQueryMock).matching(captor.capture()); - - assertThat(captor.getValue().getMeta().getComment()).isEqualTo("comment"); - } - - @Test // DATAMONGO-957 - void metadataShouldBeAddedToCountQueryCorrectly() { - - MongoQueryFake query = createQueryForMethod("findByFirstname", String.class, Pageable.class); - query.execute(new Object[] { "fake", PageRequest.of(1, 10) }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - - verify(executableFind).as(Person.class); - verify(withQueryMock, atLeast(1)).matching(captor.capture()); - - assertThat(captor.getValue().getMeta().getComment()).isEqualTo("comment"); - } - - @Test // DATAMONGO-957, DATAMONGO-1783 - void metadataShouldBeAddedToStringBasedQueryCorrectly() { - - MongoQueryFake query = createQueryForMethod("findByAnnotatedQuery", String.class, Pageable.class); - query.execute(new Object[] { "fake", PageRequest.of(0, 10) }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - - verify(executableFind).as(Person.class); - verify(withQueryMock).matching(captor.capture()); - - assertThat(captor.getValue().getMeta().getComment()).isEqualTo("comment"); - } - - @Test // DATAMONGO-1057 - void slicedExecutionShouldRetainNrOfElementsToSkip() { - - MongoQueryFake query = createQueryForMethod("findByLastname", String.class, Pageable.class); - Pageable page1 = PageRequest.of(0, 10); - Pageable page2 = page1.next(); - - query.execute(new Object[] { "fake", page1 }); - query.execute(new Object[] { "fake", page2 }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - - verify(executableFind, times(2)).as(Person.class); - verify(withQueryMock, times(2)).matching(captor.capture()); - - assertThat(captor.getAllValues().get(0).getSkip()).isZero(); - assertThat(captor.getAllValues().get(1).getSkip()).isEqualTo(10); - } - - @Test // DATAMONGO-1057 - void slicedExecutionShouldIncrementLimitByOne() { - - MongoQueryFake query = createQueryForMethod("findByLastname", String.class, Pageable.class); - Pageable page1 = PageRequest.of(0, 10); - Pageable page2 = page1.next(); - - query.execute(new Object[] { "fake", page1 }); - query.execute(new Object[] { "fake", page2 }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - - verify(executableFind, times(2)).as(Person.class); - verify(withQueryMock, times(2)).matching(captor.capture()); - - assertThat(captor.getAllValues().get(0).getLimit()).isEqualTo(11); - assertThat(captor.getAllValues().get(1).getLimit()).isEqualTo(11); - } - - @Test // DATAMONGO-1057 - void slicedExecutionShouldRetainSort() { - - MongoQueryFake query = createQueryForMethod("findByLastname", String.class, Pageable.class); - Pageable page1 = PageRequest.of(0, 10, Sort.Direction.DESC, "bar"); - Pageable page2 = page1.next(); - - query.execute(new Object[] { "fake", page1 }); - query.execute(new Object[] { "fake", page2 }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - - verify(executableFind, times(2)).as(Person.class); - verify(withQueryMock, times(2)).matching(captor.capture()); - - Document expectedSortObject = new Document().append("bar", -1); - assertThat(captor.getAllValues().get(0).getSortObject()).isEqualTo(expectedSortObject); - assertThat(captor.getAllValues().get(1).getSortObject()).isEqualTo(expectedSortObject); - } - - @Test // DATAMONGO-1080 - void doesNotTryToPostProcessQueryResultIntoWrapperType() { - - Person reference = new Person(); - - doReturn(reference).when(withQueryMock).oneValue(); - - AbstractMongoQuery query = createQueryForMethod("findByLastname", String.class); - - assertThat(query.execute(new Object[] { "lastname" })).isEqualTo(reference); - } - - @Test // DATAMONGO-1865 - void limitingSingleEntityQueryCallsFirst() { - - Person reference = new Person(); - - doReturn(reference).when(withQueryMock).firstValue(); - - AbstractMongoQuery query = createQueryForMethod("findFirstByLastname", String.class).setLimitingQuery(true); - - assertThat(query.execute(new Object[] { "lastname" })).isEqualTo(reference); - } - - @Test // DATAMONGO-1872 - void doesNotFixCollectionOnPreparation() { - - AbstractMongoQuery query = createQueryForMethod(DynamicallyMappedRepository.class, "findBy"); - - query.execute(new Object[0]); - - verify(executableFind, never()).inCollection(anyString()); - verify(executableFind).as(DynamicallyMapped.class); - } - - @Test // DATAMONGO-1979 - void usesAnnotatedSortWhenPresent() { - - createQueryForMethod("findByAge", Integer.class) // - .execute(new Object[] { 1000 }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getSortObject()).isEqualTo(new Document("age", 1)); - } - - @Test // DATAMONGO-1979 - void usesExplicitSortOverridesAnnotatedSortWhenPresent() { - - createQueryForMethod("findByAge", Integer.class, Sort.class) // - .execute(new Object[] { 1000, Sort.by(Direction.DESC, "age") }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getSortObject()).isEqualTo(new Document("age", -1)); - } - - @Test // DATAMONGO-1854 - void shouldApplyStaticAnnotatedCollation() { - - createQueryForMethod("findWithCollationUsingSpimpleStringValueByFirstName", String.class) // - .execute(new Object[] { "dalinar" }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyStaticAnnotatedCollationAsDocument() { - - createQueryForMethod("findWithCollationUsingDocumentByFirstName", String.class) // - .execute(new Object[] { "dalinar" }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyDynamicAnnotatedCollationAsString() { - - createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) // - .execute(new Object[] { "dalinar", "en_US" }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyDynamicAnnotatedCollationAsDocument() { - - createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) // - .execute(new Object[] { "dalinar", new Document("locale", "en_US") }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyDynamicAnnotatedCollationAsLocale() { - - createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) // - .execute(new Object[] { "dalinar", Locale.US }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldThrowExceptionOnNonParsableCollation() { - - assertThatIllegalArgumentException().isThrownBy(() -> { - - createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) // - .execute(new Object[] { "dalinar", 100 }); - }); - } - - @Test // DATAMONGO-1854 - void shouldApplyDynamicAnnotatedCollationIn() { - - createQueryForMethod("findWithCollationUsingPlaceholderInDocumentByFirstName", String.class, String.class) // - .execute(new Object[] { "dalinar", "en_US" }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyCollationParameter() { - - Collation collation = Collation.of("en_US"); - createQueryForMethod("findWithCollationParameterByFirstName", String.class, Collation.class) // - .execute(new Object[] { "dalinar", collation }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - void collationParameterShouldOverrideAnnotation() { - - Collation collation = Collation.of("de_AT"); - createQueryForMethod("findWithWithCollationParameterAndAnnotationByFirstName", String.class, Collation.class) // - .execute(new Object[] { "dalinar", collation }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - void collationParameterShouldNotBeAppliedWhenNullOverrideAnnotation() { - - createQueryForMethod("findWithWithCollationParameterAndAnnotationByFirstName", String.class, Collation.class) // - .execute(new Object[] { "dalinar", null }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - private MongoQueryFake createQueryForMethod(String methodName, Class... paramTypes) { - return createQueryForMethod(Repo.class, methodName, paramTypes); - } - - private MongoQueryFake createQueryForMethod(Class repository, String methodName, Class... paramTypes) { - - try { - - Method method = repository.getMethod(methodName, paramTypes); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(repository), factory, - mappingContextMock); - - return new MongoQueryFake(queryMethod, mongoOperationsMock); - } catch (Exception e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - private static class MongoQueryFake extends AbstractMongoQuery { - - private boolean isDeleteQuery; - private boolean isLimitingQuery; - - MongoQueryFake(MongoQueryMethod method, MongoOperations operations) { - super(method, operations, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT); - } - - @Override - protected Query createQuery(ConvertingParameterAccessor accessor) { - return new BasicQuery("{'foo':'bar'}"); - } - - @Override - protected boolean isCountQuery() { - return false; - } - - @Override - protected boolean isExistsQuery() { - return false; - } - - @Override - protected boolean isDeleteQuery() { - return isDeleteQuery; - } - - @Override - protected boolean isLimiting() { - return isLimitingQuery; - } - - MongoQueryFake setDeleteQuery(boolean isDeleteQuery) { - this.isDeleteQuery = isDeleteQuery; - return this; - } - - MongoQueryFake setLimitingQuery(boolean limitingQuery) { - - isLimitingQuery = limitingQuery; - return this; - } - } - - private interface Repo extends MongoRepository { - - List deleteByLastname(String lastname); - - Long deletePersonByLastname(String lastname); - - List findByFirstname(String firstname); - - @Meta(comment = "comment", flags = { org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT }) - Page findByFirstname(String firstnanme, Pageable pageable); - - @Meta(comment = "comment") - @org.springframework.data.mongodb.repository.Query("{}") - Page findByAnnotatedQuery(String firstnanme, Pageable pageable); - - // DATAMONGO-1057 - Slice findByLastname(String lastname, Pageable page); - - Optional findByLastname(String lastname); - - Person findFirstByLastname(String lastname); - - @org.springframework.data.mongodb.repository.Query(sort = "{ age : 1 }") - List findByAge(Integer age); - - @org.springframework.data.mongodb.repository.Query(sort = "{ age : 1 }") - List findByAge(Integer age, Sort page); - - @org.springframework.data.mongodb.repository.Query(collation = "en_US") - List findWithCollationUsingSpimpleStringValueByFirstName(String firstname); - - @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : 'en_US' }") - List findWithCollationUsingDocumentByFirstName(String firstname); - - @org.springframework.data.mongodb.repository.Query(collation = "?1") - List findWithCollationUsingPlaceholderByFirstName(String firstname, Object collation); - - @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : '?1' }") - List findWithCollationUsingPlaceholderInDocumentByFirstName(String firstname, String collation); - - List findWithCollationParameterByFirstName(String firstname, Collation collation); - - @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : 'en_US' }") - List findWithWithCollationParameterAndAnnotationByFirstName(String firstname, Collation collation); - } - - // DATAMONGO-1872 - - @org.springframework.data.mongodb.core.mapping.Document("#{T(java.lang.Math).random()}") - static class DynamicallyMapped {} - - interface DynamicallyMappedRepository extends Repository { - DynamicallyMapped findBy(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java deleted file mode 100644 index 2f0423901a..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright 2019-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.lang.reflect.Method; -import java.util.List; -import java.util.Locale; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.data.mongodb.core.Person; -import org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithQuery; -import org.springframework.data.mongodb.core.ReactiveFindOperation.ReactiveFind; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; -import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.ReactiveMongoRepository; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; - -/** - * Unit tests for {@link AbstractReactiveMongoQuery}. - * - * @author Christoph Strobl - * @author Mark Paluch - * @currentRead Way of Kings - Brandon Sanderson - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class AbstractReactiveMongoQueryUnitTests { - - @Mock ReactiveMongoOperations mongoOperationsMock; - @Mock BasicMongoPersistentEntity persitentEntityMock; - @Mock MongoMappingContext mappingContextMock; - - @Mock ReactiveFind executableFind; - @Mock FindWithQuery withQueryMock; - - @BeforeEach - void setUp() { - - doReturn("persons").when(persitentEntityMock).getCollection(); - doReturn(persitentEntityMock).when(mappingContextMock).getPersistentEntity(Mockito.any(Class.class)); - doReturn(persitentEntityMock).when(mappingContextMock).getRequiredPersistentEntity(Mockito.any(Class.class)); - doReturn(Person.class).when(persitentEntityMock).getType(); - - MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContextMock); - converter.afterPropertiesSet(); - - doReturn(converter).when(mongoOperationsMock).getConverter(); - - doReturn(executableFind).when(mongoOperationsMock).query(any()); - doReturn(withQueryMock).when(executableFind).as(any()); - doReturn(withQueryMock).when(withQueryMock).matching(any(Query.class)); - doReturn(Flux.empty()).when(withQueryMock).all(); - doReturn(Mono.empty()).when(withQueryMock).first(); - doReturn(Mono.empty()).when(withQueryMock).one(); - } - - @Test // DATAMONGO-1854 - void shouldApplyStaticAnnotatedCollation() { - - createQueryForMethod("findWithCollationUsingSpimpleStringValueByFirstName", String.class) // - .executeBlocking(new Object[] { "dalinar" }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyStaticAnnotatedCollationAsDocument() { - - createQueryForMethod("findWithCollationUsingDocumentByFirstName", String.class) // - .executeBlocking(new Object[] { "dalinar" }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyDynamicAnnotatedCollationAsString() { - - createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) // - .executeBlocking(new Object[] { "dalinar", "en_US" }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyDynamicAnnotatedCollationAsDocument() { - - createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) // - .executeBlocking(new Object[] { "dalinar", new Document("locale", "en_US") }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyDynamicAnnotatedCollationAsLocale() { - - createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) // - .executeBlocking(new Object[] { "dalinar", Locale.US }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldThrowExceptionOnNonParsableCollation() { - - assertThatIllegalArgumentException().isThrownBy(() -> { - createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) // - .executeBlocking(new Object[] { "dalinar", 100 }); - }); - } - - @Test // DATAMONGO-1854 - void shouldApplyDynamicAnnotatedCollationIn() { - - createQueryForMethod("findWithCollationUsingPlaceholderInDocumentByFirstName", String.class, String.class) // - .executeBlocking(new Object[] { "dalinar", "en_US" }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyDynamicAnnotatedCollationWithMultiplePlaceholders() { - - createQueryForMethod("findWithCollationUsingPlaceholdersInDocumentByFirstName", String.class, String.class, - int.class) // - .executeBlocking(new Object[] { "dalinar", "en_US", 2 }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").strength(2).toDocument()); - } - - @Test // DATAMONGO-1854 - void shouldApplyCollationParameter() { - - Collation collation = Collation.of("en_US"); - createQueryForMethod("findWithCollationParameterByFirstName", String.class, Collation.class) // - .executeBlocking(new Object[] { "dalinar", collation }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - void collationParameterShouldOverrideAnnotation() { - - Collation collation = Collation.of("de_AT"); - createQueryForMethod("findWithWithCollationParameterAndAnnotationByFirstName", String.class, Collation.class) // - .executeBlocking(new Object[] { "dalinar", collation }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - void collationParameterShouldNotBeAppliedWhenNullOverrideAnnotation() { - - createQueryForMethod("findWithWithCollationParameterAndAnnotationByFirstName", String.class, Collation.class) // - .executeBlocking(new Object[] { "dalinar", null }); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); - verify(withQueryMock).matching(captor.capture()); - assertThat(captor.getValue().getCollation().map(Collation::toDocument)) - .contains(Collation.of("en_US").toDocument()); - } - - private ReactiveMongoQueryFake createQueryForMethod(String methodName, Class... paramTypes) { - return createQueryForMethod(Repo.class, methodName, paramTypes); - } - - private ReactiveMongoQueryFake createQueryForMethod(Class repository, String methodName, Class... paramTypes) { - - try { - - Method method = repository.getMethod(methodName, paramTypes); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method, - new DefaultRepositoryMetadata(repository), factory, mappingContextMock); - - return new ReactiveMongoQueryFake(queryMethod, mongoOperationsMock); - } catch (Exception e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - private static class ReactiveMongoQueryFake extends AbstractReactiveMongoQuery { - - private boolean isDeleteQuery; - private boolean isLimitingQuery; - - ReactiveMongoQueryFake(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations) { - super(method, operations, new SpelExpressionParser(), - ReactiveExtensionAwareQueryMethodEvaluationContextProvider.DEFAULT); - } - - @Override - protected Mono createQuery(ConvertingParameterAccessor accessor) { - return Mono.just(new BasicQuery("{'foo':'bar'}")); - } - - Object executeBlocking(Object[] parameters) { - return Flux.from(super.execute(parameters)).collectList().block(); - } - - @Override - protected boolean isCountQuery() { - return false; - } - - @Override - protected boolean isExistsQuery() { - return false; - } - - @Override - protected boolean isDeleteQuery() { - return isDeleteQuery; - } - - @Override - protected boolean isLimiting() { - return isLimitingQuery; - } - - public ReactiveMongoQueryFake setDeleteQuery(boolean isDeleteQuery) { - this.isDeleteQuery = isDeleteQuery; - return this; - } - - public ReactiveMongoQueryFake setLimitingQuery(boolean limitingQuery) { - - isLimitingQuery = limitingQuery; - return this; - } - } - - private interface Repo extends ReactiveMongoRepository { - - @org.springframework.data.mongodb.repository.Query(collation = "en_US") - List findWithCollationUsingSpimpleStringValueByFirstName(String firstname); - - @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : 'en_US' }") - List findWithCollationUsingDocumentByFirstName(String firstname); - - @org.springframework.data.mongodb.repository.Query(collation = "?1") - List findWithCollationUsingPlaceholderByFirstName(String firstname, Object collation); - - @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : '?1' }") - List findWithCollationUsingPlaceholderInDocumentByFirstName(String firstname, String collation); - - @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : '?1', 'strength' : ?#{[2]}}") - List findWithCollationUsingPlaceholdersInDocumentByFirstName(String firstname, String collation, - int strength); - - List findWithCollationParameterByFirstName(String firstname, Collation collation); - - @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : 'en_US' }") - List findWithWithCollationParameterAndAnnotationByFirstName(String firstname, Collation collation); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java deleted file mode 100644 index 87994bcbec..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessorUnitTests.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.Collection; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor.PotentiallyConvertingIterator; - -import com.mongodb.BasicDBList; - -/** - * Unit tests for {@link ConvertingParameterAccessor}. - * - * @author Oliver Gierke - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -public class ConvertingParameterAccessorUnitTests { - - @Mock MongoDatabaseFactory factory; - @Mock MongoParameterAccessor accessor; - - MongoMappingContext context; - MappingMongoConverter converter; - DbRefResolver resolver; - - @BeforeEach - public void setUp() { - - this.context = new MongoMappingContext(); - this.resolver = new DefaultDbRefResolver(factory); - this.converter = new MappingMongoConverter(resolver, context); - } - - @Test - public void rejectsNullDbRefResolver() { - assertThatIllegalArgumentException().isThrownBy(() -> new MappingMongoConverter((DbRefResolver) null, context)); - } - - @Test - public void rejectsNullContext() { - assertThatIllegalArgumentException().isThrownBy(() -> new MappingMongoConverter(resolver, null)); - } - - @Test - public void convertsCollectionUponAccess() { - - when(accessor.getBindableValue(0)).thenReturn(Arrays.asList("Foo")); - - ConvertingParameterAccessor parameterAccessor = new ConvertingParameterAccessor(converter, accessor); - Object result = parameterAccessor.getBindableValue(0); - - BasicDBList reference = new BasicDBList(); - reference.add("Foo"); - - assertThat(result).isEqualTo((Object) reference); - } - - @Test // DATAMONGO-505 - public void convertsAssociationsToDBRef() { - - Property property = new Property(); - property.id = 5L; - - Object result = setupAndConvert(property); - - assertThat(result).isInstanceOf(com.mongodb.DBRef.class); - com.mongodb.DBRef dbRef = (com.mongodb.DBRef) result; - assertThat(dbRef.getCollectionName()).isEqualTo("property"); - assertThat(dbRef.getId()).isEqualTo((Object) 5L); - } - - @Test // DATAMONGO-505 - public void convertsAssociationsToDBRefForCollections() { - - Property property = new Property(); - property.id = 5L; - - Object result = setupAndConvert(Arrays.asList(property)); - - assertThat(result).isInstanceOf(Collection.class); - Collection collection = (Collection) result; - - assertThat(collection).hasSize(1); - Object element = collection.iterator().next(); - - assertThat(element).isInstanceOf(com.mongodb.DBRef.class); - com.mongodb.DBRef dbRef = (com.mongodb.DBRef) element; - assertThat(dbRef.getCollectionName()).isEqualTo("property"); - assertThat(dbRef.getId()).isEqualTo((Object) 5L); - } - - private Object setupAndConvert(Object... parameters) { - - MongoParameterAccessor delegate = new StubParameterAccessor(parameters); - PotentiallyConvertingIterator iterator = new ConvertingParameterAccessor(converter, delegate).iterator(); - - MongoPersistentEntity entity = context.getRequiredPersistentEntity(Entity.class); - MongoPersistentProperty property = entity.getRequiredPersistentProperty("property"); - - return iterator.nextConverted(property); - } - - static class Entity { - - @DBRef Property property; - } - - static class Property { - - Long id; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MappingMongoEntityInformationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MappingMongoEntityInformationUnitTests.java deleted file mode 100644 index f392c53c82..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MappingMongoEntityInformationUnitTests.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import lombok.Value; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.domain.Persistable; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation; -import org.springframework.data.repository.core.EntityInformation; - -/** - * Unit tests for {@link MappingMongoEntityInformation}. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -public class MappingMongoEntityInformationUnitTests { - - @Mock MongoPersistentEntity info; - @Mock MongoPersistentEntity persistableImplementingEntityTypeInfo; - - @Test // DATAMONGO-248 - public void usesEntityCollectionIfNoCustomOneGiven() { - - when(info.getCollection()).thenReturn("Person"); - - MongoEntityInformation information = new MappingMongoEntityInformation(info); - assertThat(information.getCollectionName()).isEqualTo("Person"); - } - - @Test // DATAMONGO-248 - public void usesCustomCollectionIfGiven() { - - MongoEntityInformation information = new MappingMongoEntityInformation(info, "foobar"); - assertThat(information.getCollectionName()).isEqualTo("foobar"); - } - - @Test // DATAMONGO-1590 - public void considersPersistableIsNew() { - - EntityInformation information = new MappingMongoEntityInformation<>( - persistableImplementingEntityTypeInfo); - - assertThat(information.isNew(new TypeImplementingPersistable(100L, false))).isFalse(); - } - - @Value - static class TypeImplementingPersistable implements Persistable { - - Long id; - boolean isNew; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessorUnitTests.java deleted file mode 100644 index e995bc5d56..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessorUnitTests.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; - -import java.lang.reflect.Method; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.Test; -import org.springframework.data.domain.Range; -import org.springframework.data.domain.Range.Bound; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.TextCriteria; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.core.RepositoryMetadata; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; - -/** - * Unit tests for {@link MongoParametersParameterAccessor}. - * - * @author Oliver Gierke - * @author Christoph Strobl - */ -public class MongoParametersParameterAccessorUnitTests { - - Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS); - RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class); - MongoMappingContext context = new MongoMappingContext(); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - - @Test - public void returnsUnboundedForDistanceIfNoneAvailable() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - - MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, - new Object[] { new Point(10, 20) }); - assertThat(accessor.getDistanceRange().getUpperBound().isBounded()).isFalse(); - } - - @Test - public void returnsDistanceIfAvailable() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - - MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, - new Object[] { new Point(10, 20), DISTANCE }); - assertThat(accessor.getDistanceRange().getUpperBound()).isEqualTo(Bound.inclusive(DISTANCE)); - } - - @Test // DATAMONGO-973 - public void shouldReturnAsFullTextStringWhenNoneDefinedForMethod() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - - MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, - new Object[] { new Point(10, 20), DISTANCE }); - assertThat(accessor.getFullText()).isNull(); - } - - @Test // DATAMONGO-973 - public void shouldProperlyConvertTextCriteria() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByFirstname", String.class, TextCriteria.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - - MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, - new Object[] { "spring", TextCriteria.forDefaultLanguage().matching("data") }); - assertThat(accessor.getFullText().getCriteriaObject().toJson()) - .isEqualTo(Document.parse("{ \"$text\" : { \"$search\" : \"data\"}}").toJson()); - } - - @Test // DATAMONGO-1110 - public void shouldDetectMinAndMaxDistance() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Range.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - - Distance min = new Distance(10, Metrics.KILOMETERS); - Distance max = new Distance(20, Metrics.KILOMETERS); - - MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, - new Object[] { new Point(10, 20), Distance.between(min, max) }); - - Range range = accessor.getDistanceRange(); - - assertThat(range.getLowerBound()).isEqualTo(Bound.inclusive(min)); - assertThat(range.getUpperBound()).isEqualTo(Bound.inclusive(max)); - } - - @Test // DATAMONGO-1854 - public void shouldDetectCollation() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByFirstname", String.class, Collation.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - - Collation collation = Collation.of("en_US"); - MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, - new Object[] { "dalinar", collation }); - - assertThat(accessor.getCollation()).isEqualTo(collation); - } - - interface PersonRepository extends Repository { - - List findByLocationNear(Point point); - - List findByLocationNear(Point point, Distance distance); - - List findByLocationNear(Point point, Range distances); - - List findByFirstname(String firstname, TextCriteria fullText); - - List findByFirstname(String firstname, Collation collation); - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersUnitTests.java deleted file mode 100644 index 00f488d00c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoParametersUnitTests.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; - -import java.lang.reflect.Method; -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.domain.Range; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.TextCriteria; -import org.springframework.data.mongodb.repository.Near; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.repository.query.Parameter; - -/** - * Unit tests for {@link MongoParameters}. - * - * @author Oliver Gierke - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -class MongoParametersUnitTests { - - @Mock MongoQueryMethod queryMethod; - - @Test - void discoversDistanceParameter() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class); - MongoParameters parameters = new MongoParameters(method, false); - - assertThat(parameters.getNumberOfParameters()).isEqualTo(2); - assertThat(parameters.getMaxDistanceIndex()).isEqualTo(1); - assertThat(parameters.getBindableParameters().getNumberOfParameters()).isOne(); - - Parameter parameter = parameters.getParameter(1); - - assertThat(parameter.isSpecialParameter()).isTrue(); - assertThat(parameter.isBindable()).isFalse(); - } - - @Test - void doesNotConsiderPointAsNearForSimpleQuery() throws Exception { - Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class); - MongoParameters parameters = new MongoParameters(method, false); - - assertThat(parameters.getNearIndex()).isEqualTo(-1); - } - - @Test - void rejectsMultiplePointsForGeoNearMethod() throws Exception { - - Method method = PersonRepository.class.getMethod("findByLocationNearAndOtherLocation", Point.class, Point.class); - - assertThatIllegalStateException().isThrownBy(() -> new MongoParameters(method, true)); - } - - @Test - void rejectsMultipleDoubleArraysForGeoNearMethod() throws Exception { - - Method method = PersonRepository.class.getMethod("invalidDoubleArrays", double[].class, double[].class); - - assertThatIllegalStateException().isThrownBy(() -> new MongoParameters(method, true)); - } - - @Test - void doesNotRejectMultiplePointsForSimpleQueryMethod() throws Exception { - - Method method = PersonRepository.class.getMethod("someOtherMethod", Point.class, Point.class); - new MongoParameters(method, false); - } - - @Test - void findsAnnotatedPointForGeoNearQuery() throws Exception { - - Method method = PersonRepository.class.getMethod("findByOtherLocationAndLocationNear", Point.class, Point.class); - MongoParameters parameters = new MongoParameters(method, true); - assertThat(parameters.getNearIndex()).isOne(); - } - - @Test - void findsAnnotatedDoubleArrayForGeoNearQuery() throws Exception { - - Method method = PersonRepository.class.getMethod("validDoubleArrays", double[].class, double[].class); - MongoParameters parameters = new MongoParameters(method, true); - assertThat(parameters.getNearIndex()).isOne(); - } - - @Test // DATAMONGO-973 - void shouldFindTextCriteriaAtItsIndex() throws SecurityException, NoSuchMethodException { - - Method method = PersonRepository.class.getMethod("findByNameAndText", String.class, TextCriteria.class); - MongoParameters parameters = new MongoParameters(method, false); - assertThat(parameters.getFullTextParameterIndex()).isOne(); - } - - @Test // DATAMONGO-973 - void shouldTreatTextCriteriaParameterAsSpecialParameter() throws SecurityException, NoSuchMethodException { - - Method method = PersonRepository.class.getMethod("findByNameAndText", String.class, TextCriteria.class); - MongoParameters parameters = new MongoParameters(method, false); - assertThat(parameters.getParameter(parameters.getFullTextParameterIndex()).isSpecialParameter()).isTrue(); - } - - @Test // DATAMONGO-1110 - void shouldFindMinAndMaxDistanceParameters() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Range.class); - MongoParameters parameters = new MongoParameters(method, false); - - assertThat(parameters.getRangeIndex()).isOne(); - assertThat(parameters.getMaxDistanceIndex()).isEqualTo(-1); - } - - @Test // DATAMONGO-1110 - void shouldNotHaveMinDistanceIfOnlyOneDistanceParameterPresent() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class); - MongoParameters parameters = new MongoParameters(method, false); - - assertThat(parameters.getRangeIndex()).isEqualTo(-1); - assertThat(parameters.getMaxDistanceIndex()).isOne(); - } - - @Test // DATAMONGO-1854 - void shouldReturnMinusOneIfCollationParameterDoesNotExist() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByLocationNear", Point.class, Distance.class); - MongoParameters parameters = new MongoParameters(method, false); - - assertThat(parameters.getCollationParameterIndex()).isEqualTo(-1); - } - - @Test // DATAMONGO-1854 - void shouldReturnIndexOfCollationParameterIfExists() throws NoSuchMethodException, SecurityException { - - Method method = PersonRepository.class.getMethod("findByText", String.class, Collation.class); - MongoParameters parameters = new MongoParameters(method, false); - - assertThat(parameters.getCollationParameterIndex()).isOne(); - } - - interface PersonRepository { - - List findByLocationNear(Point point, Distance distance); - - GeoResults findByLocationNearAndOtherLocation(Point point, Point anotherLocation); - - GeoResults invalidDoubleArrays(double[] first, double[] second); - - List someOtherMethod(Point first, Point second); - - GeoResults findByOtherLocationAndLocationNear(Point point, @Near Point anotherLocation); - - GeoResults validDoubleArrays(double[] first, @Near double[] second); - - List findByNameAndText(String name, TextCriteria text); - - List findByLocationNear(Point point, Range range); - - List findByText(String text, Collation collation); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java deleted file mode 100644 index 12446f7e0f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.query; - -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; -import static org.springframework.data.mongodb.repository.query.StubParameterAccessor.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.lang.reflect.Method; -import java.util.List; -import java.util.regex.Pattern; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.data.domain.Range; -import org.springframework.data.domain.Range.Bound; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.geo.Polygon; -import org.springframework.data.geo.Shape; -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.Person; -import org.springframework.data.mongodb.core.Venue; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.geo.GeoJsonLineString; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; -import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; -import org.springframework.data.mongodb.core.mapping.DBRef; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.parser.PartTree; - -/** - * Unit test for {@link MongoQueryCreator}. - * - * @author Oliver Gierke - * @author Thomas Darimont - * @author Christoph Strobl - */ -public class MongoQueryCreatorUnitTests { - - MappingContext, MongoPersistentProperty> context; - MongoConverter converter; - - @BeforeEach - public void beforeEach() { - - context = new MongoMappingContext(); - - DbRefResolver resolver = new DefaultDbRefResolver(mock(MongoDatabaseFactory.class)); - converter = new MappingMongoConverter(resolver, context); - } - - @Test - public void createsQueryCorrectly() { - - PartTree tree = new PartTree("findByFirstName", Person.class); - - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Oliver"), context); - Query query = creator.createQuery(); - assertThat(query).isEqualTo(query(where("firstName").is("Oliver"))); - } - - @Test // DATAMONGO-469 - public void createsAndQueryCorrectly() { - - Person person = new Person(); - MongoQueryCreator creator = new MongoQueryCreator(new PartTree("findByFirstNameAndFriend", Person.class), - getAccessor(converter, "Oliver", person), context); - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("firstName").is("Oliver").and("friend").is(person))); - } - - @Test - public void createsNotNullQueryCorrectly() { - - PartTree tree = new PartTree("findByFirstNameNotNull", Person.class); - Query query = new MongoQueryCreator(tree, getAccessor(converter), context).createQuery(); - - assertThat(query).isEqualTo(new Query(Criteria.where("firstName").ne(null))); - } - - @Test - public void createsIsNullQueryCorrectly() { - - PartTree tree = new PartTree("findByFirstNameIsNull", Person.class); - Query query = new MongoQueryCreator(tree, getAccessor(converter), context).createQuery(); - - assertThat(query).isEqualTo(new Query(Criteria.where("firstName").is(null))); - } - - @Test - public void bindsMetricDistanceParameterToNearSphereCorrectly() throws Exception { - - Point point = new Point(10, 20); - Distance distance = new Distance(2.5, Metrics.KILOMETERS); - - Query query = query( - where("location").nearSphere(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave")); - assertBindsDistanceToQuery(point, distance, query); - } - - @Test - public void bindsDistanceParameterToNearCorrectly() throws Exception { - - Point point = new Point(10, 20); - Distance distance = new Distance(2.5); - - Query query = query( - where("location").near(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave")); - assertBindsDistanceToQuery(point, distance, query); - } - - @Test - public void createsLessThanEqualQueryCorrectly() { - - PartTree tree = new PartTree("findByAgeLessThanEqual", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context); - - Query reference = query(where("age").lte(18)); - assertThat(creator.createQuery()).isEqualTo(reference); - } - - @Test - public void createsGreaterThanEqualQueryCorrectly() { - - PartTree tree = new PartTree("findByAgeGreaterThanEqual", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context); - - Query reference = query(where("age").gte(18)); - assertThat(creator.createQuery()).isEqualTo(reference); - } - - @Test // DATAMONGO-338 - public void createsExistsClauseCorrectly() { - - PartTree tree = new PartTree("findByAgeExists", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, true), context); - Query query = query(where("age").exists(true)); - assertThat(creator.createQuery()).isEqualTo(query); - } - - @Test // DATAMONGO-338 - public void createsRegexClauseCorrectly() { - - PartTree tree = new PartTree("findByFirstNameRegex", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, ".*"), context); - Query query = query(where("firstName").regex(".*")); - assertThat(creator.createQuery()).isEqualTo(query); - } - - @Test // DATAMONGO-338 - public void createsTrueClauseCorrectly() { - - PartTree tree = new PartTree("findByActiveTrue", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter), context); - Query query = query(where("active").is(true)); - assertThat(creator.createQuery()).isEqualTo(query); - } - - @Test // DATAMONGO-338 - public void createsFalseClauseCorrectly() { - - PartTree tree = new PartTree("findByActiveFalse", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter), context); - Query query = query(where("active").is(false)); - assertThat(creator.createQuery()).isEqualTo(query); - } - - @Test // DATAMONGO-413 - public void createsOrQueryCorrectly() { - - PartTree tree = new PartTree("findByFirstNameOrAge", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Dave", 42), context); - - Query query = creator.createQuery(); - assertThat(query).isEqualTo(query(new Criteria().orOperator(where("firstName").is("Dave"), where("age").is(42)))); - } - - @Test // DATAMONGO-347 - public void createsQueryReferencingADBRefCorrectly() { - - User user = new User(); - user.id = new ObjectId(); - - PartTree tree = new PartTree("findByCreator", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, user), context); - Document queryObject = creator.createQuery().getQueryObject(); - - assertThat(queryObject.get("creator")).isEqualTo(user); - } - - @Test // DATAMONGO-418 - public void createsQueryWithStartingWithPredicateCorrectly() { - - PartTree tree = new PartTree("findByUsernameStartingWith", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Matt"), context); - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("username").regex("^Matt"))); - } - - @Test // DATAMONGO-418 - public void createsQueryWithEndingWithPredicateCorrectly() { - - PartTree tree = new PartTree("findByUsernameEndingWith", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "ews"), context); - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("username").regex("ews$"))); - } - - @Test // DATAMONGO-418 - public void createsQueryWithContainingPredicateCorrectly() { - - PartTree tree = new PartTree("findByUsernameContaining", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context); - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("username").regex(".*thew.*"))); - } - - private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception { - - PartTree tree = new PartTree("findByLocationNearAndFirstname", - org.springframework.data.mongodb.repository.Person.class); - Method method = PersonRepository.class.getMethod("findByLocationNearAndFirstname", Point.class, Distance.class, - String.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class), - new SpelAwareProxyProjectionFactory(), new MongoMappingContext()); - MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod, - new Object[] { point, distance, "Dave" }); - - Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor), context) - .createQuery(); - assertThat(query).isEqualTo(query); - } - - @Test // DATAMONGO-770 - public void createsQueryWithFindByIgnoreCaseCorrectly() { - - PartTree tree = new PartTree("findByfirstNameIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); - - Query query = creator.createQuery(); - assertThat(query).isEqualTo(query(where("firstName").regex("^dave$", "i"))); - } - - @Test // DATAMONGO-770 - public void createsQueryWithFindByNotIgnoreCaseCorrectly() { - - PartTree tree = new PartTree("findByFirstNameNotIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); - - Query query = creator.createQuery(); - assertThat(query.toString()).isEqualTo(query(where("firstName").not().regex("^dave$", "i")).toString()); - } - - @Test // DATAMONGO-770 - public void createsQueryWithFindByStartingWithIgnoreCaseCorrectly() { - - PartTree tree = new PartTree("findByFirstNameStartingWithIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); - - Query query = creator.createQuery(); - assertThat(query).isEqualTo(query(where("firstName").regex("^dave", "i"))); - } - - @Test // DATAMONGO-770 - public void createsQueryWithFindByEndingWithIgnoreCaseCorrectly() { - - PartTree tree = new PartTree("findByFirstNameEndingWithIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); - - Query query = creator.createQuery(); - assertThat(query).isEqualTo(query(where("firstName").regex("dave$", "i"))); - } - - @Test // DATAMONGO-770 - public void createsQueryWithFindByContainingIgnoreCaseCorrectly() { - - PartTree tree = new PartTree("findByFirstNameContainingIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); - - Query query = creator.createQuery(); - assertThat(query).isEqualTo(query(where("firstName").regex(".*dave.*", "i"))); - } - - @Test // DATAMONGO-770 - public void shouldThrowExceptionForQueryWithFindByIgnoreCaseOnNonStringProperty() { - - PartTree tree = new PartTree("findByFirstNameAndAgeIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "foo", 42), context); - - assertThatIllegalArgumentException().isThrownBy(creator::createQuery) - .withMessageContaining("must be of type String"); - } - - @Test // DATAMONGO-770 - public void shouldOnlyGenerateLikeExpressionsForStringPropertiesIfAllIgnoreCase() { - - PartTree tree = new PartTree("findByFirstNameAndAgeAllIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context); - - Query query = creator.createQuery(); - assertThat(query).isEqualTo(query(where("firstName").regex("^dave$", "i").and("age").is(42))); - } - - @Test // DATAMONGO-566 - public void shouldCreateDeleteByQueryCorrectly() { - - PartTree tree = new PartTree("deleteByFirstName", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context); - - Query query = creator.createQuery(); - - assertThat(tree.isDelete()).isTrue(); - assertThat(query).isEqualTo(query(where("firstName").is("dave"))); - } - - @Test // DATAMONGO-566 - public void shouldCreateDeleteByQueryCorrectlyForMultipleCriteriaAndCaseExpressions() { - - PartTree tree = new PartTree("deleteByFirstNameAndAgeAllIgnoreCase", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context); - - Query query = creator.createQuery(); - - assertThat(tree.isDelete()).isTrue(); - assertThat(query).isEqualTo(query(where("firstName").regex("^dave$", "i").and("age").is(42))); - } - - @Test // DATAMONGO-1075 - public void shouldCreateInClauseWhenUsingContainsOnCollectionLikeProperty() { - - PartTree tree = new PartTree("findByEmailAddressesContaining", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); - - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("emailAddresses").in("dave"))); - } - - @Test // DATAMONGO-1075 - public void shouldCreateInClauseWhenUsingNotContainsOnCollectionLikeProperty() { - - PartTree tree = new PartTree("findByEmailAddressesNotContaining", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); - - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("emailAddresses").not().in("dave"))); - } - - @Test // DATAMONGO-1075, DATAMONGO-1425 - public void shouldCreateRegexWhenUsingNotContainsOnStringProperty() { - - PartTree tree = new PartTree("findByUsernameNotContaining", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context); - Query query = creator.createQuery(); - - assertThat(query.getQueryObject().toJson()) - .isEqualTo(query(where("username").not().regex(".*thew.*")).getQueryObject().toJson()); - } - - @Test // DATAMONGO-1139 - public void createsNonSphericalNearForDistanceWithDefaultMetric() { - - Point point = new Point(1.0, 1.0); - Distance distance = new Distance(1.0); - - PartTree tree = new PartTree("findByLocationNear", Venue.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context); - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("location").near(point).maxDistance(1.0))); - } - - @Test // DATAMONGO-1136 - public void shouldCreateWithinQueryCorrectly() { - - Point first = new Point(1, 1); - Point second = new Point(2, 2); - Point third = new Point(3, 3); - Shape shape = new Polygon(first, second, third); - - PartTree tree = new PartTree("findByAddress_GeoWithin", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, shape), context); - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("address.geo").within(shape))); - } - - @Test // DATAMONGO-1110 - public void shouldCreateNearSphereQueryForSphericalProperty() { - - Point point = new Point(10, 20); - - PartTree tree = new PartTree("findByAddress2dSphere_GeoNear", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point), context); - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("address2dSphere.geo").nearSphere(point))); - } - - @Test // DATAMONGO-1110 - public void shouldCreateNearSphereQueryForSphericalPropertyHavingDistanceWithDefaultMetric() { - - Point point = new Point(1.0, 1.0); - Distance distance = new Distance(1.0); - - PartTree tree = new PartTree("findByAddress2dSphere_GeoNear", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context); - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("address2dSphere.geo").nearSphere(point).maxDistance(1.0))); - } - - @Test // DATAMONGO-1110 - public void shouldCreateNearQueryForMinMaxDistance() { - - Point point = new Point(10, 20); - Range range = Distance.between(new Distance(10), new Distance(20)); - - PartTree tree = new PartTree("findByAddress_GeoNear", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, range), context); - Query query = creator.createQuery(); - - assertThat(query).isEqualTo(query(where("address.geo").near(point).minDistance(10D).maxDistance(20D))); - } - - @Test // DATAMONGO-1229 - public void appliesIgnoreCaseToLeafProperty() { - - PartTree tree = new PartTree("findByAddressStreetIgnoreCase", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "Street"); - - assertThat(new MongoQueryCreator(tree, accessor, context).createQuery()).isNotNull(); - } - - @Test // DATAMONGO-1232 - public void ignoreCaseShouldEscapeSource() { - - PartTree tree = new PartTree("findByUsernameIgnoreCase", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "con.flux+"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - - assertThat(query).isEqualTo(query(where("username").regex("^\\Qcon.flux+\\E$", "i"))); - } - - @Test // DATAMONGO-1232 - public void ignoreCaseShouldEscapeSourceWhenUsedForStartingWith() { - - PartTree tree = new PartTree("findByUsernameStartingWithIgnoreCase", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "dawns.light+"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - - assertThat(query).isEqualTo(query(where("username").regex("^\\Qdawns.light+\\E", "i"))); - } - - @Test // DATAMONGO-1232 - public void ignoreCaseShouldEscapeSourceWhenUsedForEndingWith() { - - PartTree tree = new PartTree("findByUsernameEndingWithIgnoreCase", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "new.ton+"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - - assertThat(query).isEqualTo(query(where("username").regex("\\Qnew.ton+\\E$", "i"))); - } - - @Test // DATAMONGO-1232 - public void likeShouldEscapeSourceWhenUsedWithLeadingAndTrailingWildcard() { - - PartTree tree = new PartTree("findByUsernameLike", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "*fire.fight+*"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - - assertThat(query).isEqualTo(query(where("username").regex(".*\\Qfire.fight+\\E.*"))); - } - - @Test // DATAMONGO-1232 - public void likeShouldEscapeSourceWhenUsedWithLeadingWildcard() { - - PartTree tree = new PartTree("findByUsernameLike", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "*steel.heart+"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - - assertThat(query).isEqualTo(query(where("username").regex(".*\\Qsteel.heart+\\E"))); - } - - @Test // DATAMONGO-1232 - public void likeShouldEscapeSourceWhenUsedWithTrailingWildcard() { - - PartTree tree = new PartTree("findByUsernameLike", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "cala.mity+*"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - assertThat(query).isEqualTo(query(where("username").regex("\\Qcala.mity+\\E.*"))); - } - - @Test // DATAMONGO-1232 - public void likeShouldBeTreatedCorrectlyWhenUsedWithWildcardOnly() { - - PartTree tree = new PartTree("findByUsernameLike", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "*"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - assertThat(query).isEqualTo(query(where("username").regex(".*"))); - } - - @Test // DATAMONGO-1342 - public void bindsNullValueToContainsClause() { - - PartTree partTree = new PartTree("emailAddressesContains", User.class); - - ConvertingParameterAccessor accessor = getAccessor(converter, new Object[] { null }); - Query query = new MongoQueryCreator(partTree, accessor, context).createQuery(); - - assertThat(query).isEqualTo(query(where("emailAddresses").in((Object) null))); - } - - @Test // DATAMONGO-1424 - public void notLikeShouldEscapeSourceWhenUsedWithLeadingAndTrailingWildcard() { - - PartTree tree = new PartTree("findByUsernameNotLike", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "*fire.fight+*"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - - assertThat(query.getQueryObject().toJson()) - .isEqualTo(query(where("username").not().regex(".*\\Qfire.fight+\\E.*")).getQueryObject().toJson()); - } - - @Test // DATAMONGO-1424 - public void notLikeShouldEscapeSourceWhenUsedWithLeadingWildcard() { - - PartTree tree = new PartTree("findByUsernameNotLike", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "*steel.heart+"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - - assertThat(query.getQueryObject().toJson()) - .isEqualTo(query(where("username").not().regex(".*\\Qsteel.heart+\\E")).getQueryObject().toJson()); - } - - @Test // DATAMONGO-1424 - public void notLikeShouldEscapeSourceWhenUsedWithTrailingWildcard() { - - PartTree tree = new PartTree("findByUsernameNotLike", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "cala.mity+*"), context); - Query query = creator.createQuery(); - - assertThat(query.getQueryObject().toJson()) - .isEqualTo(query(where("username").not().regex("\\Qcala.mity+\\E.*")).getQueryObject().toJson()); - } - - @Test // DATAMONGO-1424 - public void notLikeShouldBeTreatedCorrectlyWhenUsedWithWildcardOnly() { - - PartTree tree = new PartTree("findByUsernameNotLike", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, "*"); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - assertThat(query.getQueryObject().toJson()) - .isEqualTo(query(where("username").not().regex(".*")).getQueryObject().toJson()); - } - - @Test // DATAMONGO-1588 - public void queryShouldAcceptSubclassOfDeclaredArgument() { - - PartTree tree = new PartTree("findByLocationNear", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, new GeoJsonPoint(-74.044502D, 40.689247D)); - - Query query = new MongoQueryCreator(tree, accessor, context).createQuery(); - assertThat(query.getQueryObject()).containsKey("location"); - } - - @Test // DATAMONGO-1588 - public void queryShouldThrowExceptionWhenArgumentDoesNotMatchDeclaration() { - - PartTree tree = new PartTree("findByLocationNear", User.class); - ConvertingParameterAccessor accessor = getAccessor(converter, - new GeoJsonLineString(new Point(-74.044502D, 40.689247D), new Point(-73.997330D, 40.730824D))); - - assertThatIllegalArgumentException().isThrownBy(() -> new MongoQueryCreator(tree, accessor, context).createQuery()) - .withMessageContaining("Expected parameter type of " + Point.class); - } - - @Test // DATAMONGO-2003 - public void createsRegexQueryForPatternCorrectly() { - - PartTree tree = new PartTree("findByFirstNameRegex", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, Pattern.compile(".*")), context); - - assertThat(creator.createQuery()).isEqualTo(query(where("firstName").regex(".*"))); - } - - @Test // DATAMONGO-2003 - public void createsRegexQueryForPatternWithOptionsCorrectly() { - - Pattern pattern = Pattern.compile(".*", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); - - PartTree tree = new PartTree("findByFirstNameRegex", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, pattern), context); - assertThat(creator.createQuery()).isEqualTo(query(where("firstName").regex(".*", "iu"))); - } - - @Test // DATAMONGO-2071 - public void betweenShouldAllowSingleRageParameter() { - - PartTree tree = new PartTree("findByAgeBetween", Person.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, - getAccessor(converter, Range.of(Bound.exclusive(10), Bound.exclusive(11))), context); - - assertThat(creator.createQuery()).isEqualTo(query(where("age").gt(10).lt(11))); - } - - @Test // DATAMONGO-2394 - public void nearShouldUseMetricDistanceForGeoJsonTypes() { - - GeoJsonPoint point = new GeoJsonPoint(27.987901, 86.9165379); - PartTree tree = new PartTree("findByLocationNear", User.class); - MongoQueryCreator creator = new MongoQueryCreator(tree, - getAccessor(converter, point, new Distance(1, Metrics.KILOMETERS)), context); - - assertThat(creator.createQuery()).isEqualTo(query(where("location").nearSphere(point).maxDistance(1000.0D))); - } - - interface PersonRepository extends Repository { - - List findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname); - } - - class User { - - ObjectId id; - - @Field("foo") String username; - - @DBRef User creator; - - List emailAddresses; - - Address address; - - Address2dSphere address2dSphere; - - Point location; - } - - static class Address { - - String street; - - Point geo; - } - - static class Address2dSphere { - - String street; - - @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) Point geo; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java deleted file mode 100644 index e0fa9200fe..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoPage; -import org.springframework.data.geo.GeoResult; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Metrics; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind; -import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery; -import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind; -import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFindNear; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.query.MongoQueryExecution.DeleteExecution; -import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagedExecution; -import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagingGeoNearExecution; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.core.RepositoryMetadata; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.util.ReflectionUtils; - -import com.mongodb.client.result.DeleteResult; - -/** - * Unit tests for {@link MongoQueryExecution}. - * - * @author Mark Paluch - * @author Oliver Gierke - * @author Artyom Gabeev - * @author Christoph Strobl - * @soundtrack U Can't Touch This - MC Hammer - */ -@ExtendWith(MockitoExtension.class) -class MongoQueryExecutionUnitTests { - - @Mock MongoOperations mongoOperationsMock; - @Mock ExecutableFind findOperationMock; - @Mock FindWithQuery operationMock; - @Mock TerminatingFind terminatingMock; - @Mock TerminatingFindNear terminatingGeoMock; - @Mock DbRefResolver dbRefResolver; - - private SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); - private Point POINT = new Point(10, 20); - private Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS); - private RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class); - private MongoMappingContext context = new MongoMappingContext(); - private ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - private Method method = ReflectionUtils.findMethod(PersonRepository.class, "findByLocationNear", Point.class, - Distance.class, - Pageable.class); - private MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - private MappingMongoConverter converter; - - @BeforeEach - @SuppressWarnings("unchecked") - void setUp() { - - converter = new MappingMongoConverter(dbRefResolver, context); - - } - - @Test // DATAMONGO-1464 - void pagedExecutionShouldNotGenerateCountQueryIfQueryReportedNoResults() { - - doReturn(terminatingMock).when(operationMock).matching(any(Query.class)); - doReturn(Collections.emptyList()).when(terminatingMock).all(); - - PagedExecution execution = new PagedExecution(operationMock, PageRequest.of(0, 10)); - execution.execute(new Query()); - - verify(terminatingMock).all(); - verify(terminatingMock, never()).count(); - } - - @Test // DATAMONGO-1464 - void pagedExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSize() { - - doReturn(terminatingMock).when(operationMock).matching(any(Query.class)); - doReturn(Arrays.asList(new Person(), new Person(), new Person(), new Person())).when(terminatingMock).all(); - - PagedExecution execution = new PagedExecution(operationMock, PageRequest.of(0, 10)); - execution.execute(new Query()); - - verify(terminatingMock).all(); - verify(terminatingMock, never()).count(); - } - - @Test // DATAMONGO-1464 - void pagedExecutionRetrievesObjectsForPageableOutOfRange() { - - doReturn(terminatingMock).when(operationMock).matching(any(Query.class)); - doReturn(Collections.emptyList()).when(terminatingMock).all(); - - PagedExecution execution = new PagedExecution(operationMock, PageRequest.of(2, 10)); - execution.execute(new Query()); - - verify(terminatingMock).all(); - verify(terminatingMock).count(); - } - - @Test // DATAMONGO-1464 - void pagingGeoExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSize() { - - GeoResult result = new GeoResult<>(new Person(), DISTANCE); - when(mongoOperationsMock.getConverter()).thenReturn(converter); - when(mongoOperationsMock.query(any(Class.class))).thenReturn(findOperationMock); - when(findOperationMock.near(any(NearQuery.class))).thenReturn(terminatingGeoMock); - doReturn(new GeoResults<>(Arrays.asList(result, result, result, result))).when(terminatingGeoMock).all(); - - ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, - new MongoParametersParameterAccessor(queryMethod, new Object[] { POINT, DISTANCE, PageRequest.of(0, 10) })); - - PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, EXPRESSION_PARSER, - QueryMethodEvaluationContextProvider.DEFAULT); - - PagingGeoNearExecution execution = new PagingGeoNearExecution(findOperationMock, queryMethod, accessor, query); - execution.execute(new Query()); - - verify(terminatingGeoMock).all(); - } - - @Test // DATAMONGO-1464 - void pagingGeoExecutionRetrievesObjectsForPageableOutOfRange() { - - when(mongoOperationsMock.getConverter()).thenReturn(converter); - when(mongoOperationsMock.query(any(Class.class))).thenReturn(findOperationMock); - when(findOperationMock.near(any(NearQuery.class))).thenReturn(terminatingGeoMock); - doReturn(new GeoResults<>(Collections.emptyList())).when(terminatingGeoMock).all(); - doReturn(terminatingMock).when(findOperationMock).matching(any(Query.class)); - - ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, - new MongoParametersParameterAccessor(queryMethod, new Object[] { POINT, DISTANCE, PageRequest.of(2, 10) })); - - PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, EXPRESSION_PARSER, - QueryMethodEvaluationContextProvider.DEFAULT); - - PagingGeoNearExecution execution = new PagingGeoNearExecution(findOperationMock, queryMethod, accessor, query); - execution.execute(new Query()); - - verify(terminatingGeoMock).all(); - verify(terminatingMock).count(); - } - - @Test // DATAMONGO-2351 - void acknowledgedDeleteReturnsDeletedCount() { - - Method method = ReflectionUtils.findMethod(PersonRepository.class, "deleteAllByLastname", String.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - - when(mongoOperationsMock.remove(any(Query.class), any(Class.class), anyString())) - .thenReturn(DeleteResult.acknowledged(10)); - - assertThat(new DeleteExecution(mongoOperationsMock, queryMethod).execute(new Query())).isEqualTo(10L); - } - - @Test // DATAMONGO-2351 - void unacknowledgedDeleteReturnsZeroDeletedCount() { - - Method method = ReflectionUtils.findMethod(PersonRepository.class, "deleteAllByLastname", String.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - - when(mongoOperationsMock.remove(any(Query.class), any(Class.class), anyString())) - .thenReturn(DeleteResult.unacknowledged()); - - assertThat(new DeleteExecution(mongoOperationsMock, queryMethod).execute(new Query())).isEqualTo(0L); - } - - @Test // DATAMONGO-1997 - void deleteExecutionWithEntityReturnTypeTriggersFindAndRemove() { - - Method method = ReflectionUtils.findMethod(PersonRepository.class, "deleteByLastname", String.class); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); - - Person person = new Person(); - - when(mongoOperationsMock.findAndRemove(any(Query.class), any(Class.class), anyString())).thenReturn(person); - - assertThat(new DeleteExecution(mongoOperationsMock, queryMethod).execute(new Query())).isEqualTo(person); - } - - interface PersonRepository extends Repository { - - GeoPage findByLocationNear(Point point, Distance distance, Pageable pageable); - - Long deleteAllByLastname(String lastname); - - Person deleteByLastname(String lastname); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java deleted file mode 100644 index a9c142f103..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; - -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.List; - -import org.assertj.core.api.Assertions; -import org.junit.Before; -import org.junit.Test; -import org.springframework.data.domain.Pageable; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoPage; -import org.springframework.data.geo.GeoResult; -import org.springframework.data.geo.GeoResults; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.User; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.repository.Address; -import org.springframework.data.mongodb.repository.Aggregation; -import org.springframework.data.mongodb.repository.Contact; -import org.springframework.data.mongodb.repository.Meta; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; - -/** - * Unit test for {@link MongoQueryMethod}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - */ -public class MongoQueryMethodUnitTests { - - MongoMappingContext context; - - @Before - public void setUp() { - context = new MongoMappingContext(); - } - - @Test - public void detectsCollectionFromRepoTypeIfReturnTypeNotAssignable() throws Exception { - - MongoQueryMethod queryMethod = queryMethod(SampleRepository.class, "method"); - MongoEntityMetadata metadata = queryMethod.getEntityInformation(); - - assertThat(metadata.getJavaType()).isAssignableFrom(Address.class); - assertThat(metadata.getCollectionName()).isEqualTo("contact"); - } - - @Test - public void detectsCollectionFromReturnTypeIfReturnTypeAssignable() throws Exception { - - MongoQueryMethod queryMethod = queryMethod(SampleRepository2.class, "method"); - MongoEntityMetadata entityInformation = queryMethod.getEntityInformation(); - - assertThat(entityInformation.getJavaType()).isAssignableFrom(Person.class); - assertThat(entityInformation.getCollectionName()).isEqualTo("person"); - } - - @Test - public void discoversUserAsDomainTypeForGeoPageQueryMethod() throws Exception { - - MongoQueryMethod queryMethod = queryMethod(PersonRepository.class, "findByLocationNear", Point.class, - Distance.class, Pageable.class); - assertThat(queryMethod.isGeoNearQuery()).isTrue(); - assertThat(queryMethod.isPageQuery()).isTrue(); - - queryMethod = queryMethod(PersonRepository.class, "findByFirstname", String.class, Point.class); - assertThat(queryMethod.isGeoNearQuery()).isTrue(); - assertThat(queryMethod.isPageQuery()).isFalse(); - assertThat(queryMethod.getEntityInformation().getJavaType()).isAssignableFrom(User.class); - - assertThat(queryMethod(PersonRepository.class, "findByEmailAddress", String.class, Point.class).isGeoNearQuery()) - .isTrue(); - assertThat(queryMethod(PersonRepository.class, "findByFirstname", String.class, Point.class).isGeoNearQuery()) - .isTrue(); - assertThat(queryMethod(PersonRepository.class, "findByLastname", String.class, Point.class).isGeoNearQuery()) - .isTrue(); - } - - @Test - public void rejectsGeoPageQueryWithoutPageable() { - assertThatIllegalArgumentException() - .isThrownBy(() -> queryMethod(PersonRepository.class, "findByLocationNear", Point.class, Distance.class)); - } - - @Test(expected = IllegalArgumentException.class) - public void rejectsNullMappingContext() throws Exception { - - Method method = PersonRepository.class.getMethod("findByFirstname", String.class, Point.class); - - new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class), - new SpelAwareProxyProjectionFactory(), null); - } - - @Test - public void considersMethodReturningGeoPageAsPagingMethod() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "findByLocationNear", Point.class, Distance.class, - Pageable.class); - - assertThat(method.isPageQuery()).isTrue(); - assertThat(method.isCollectionQuery()).isFalse(); - } - - @Test - public void createsMongoQueryMethodObjectForMethodReturningAnInterface() throws Exception { - - queryMethod(SampleRepository2.class, "methodReturningAnInterface"); - } - - @Test // DATAMONGO-957 - public void createsMongoQueryMethodWithEmptyMetaCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "emptyMetaAnnotation"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().hasValues()).isFalse(); - } - - @Test // DATAMONGO-957 - public void createsMongoQueryMethodWithMaxExecutionTimeCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithMaxExecutionTime"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().getMaxTimeMsec()).isEqualTo(100L); - } - - @Test // DATAMONGO-1311 - public void createsMongoQueryMethodWithBatchSizeCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "batchSize"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().getCursorBatchSize()).isEqualTo(100); - } - - @Test // DATAMONGO-1311 - public void createsMongoQueryMethodWithNegativeBatchSizeCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "negativeBatchSize"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().getCursorBatchSize()).isEqualTo(-200); - } - - @Test // DATAMONGO-1403 - public void createsMongoQueryMethodWithSpellFixedMaxExecutionTimeCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithSpellFixedMaxExecutionTime"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().getMaxTimeMsec()).isEqualTo(100L); - } - - @Test // DATAMONGO-957 - public void createsMongoQueryMethodWithCommentCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithComment"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().getComment()).isEqualTo("foo bar"); - } - - @Test // DATAMONGO-1480 - public void createsMongoQueryMethodWithNoCursorTimeoutCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithNoCursorTimeout"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().getFlags()) - .contains(org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT); - } - - @Test // DATAMONGO-1480, DATAMONGO-2572 - public void createsMongoQueryMethodWithMultipleFlagsCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithMultipleFlags"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().getFlags()).contains( - org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT, - org.springframework.data.mongodb.core.query.Meta.CursorOption.SECONDARY_READS); - } - - @Test // DATAMONGO-1266 - public void fallsBackToRepositoryDomainTypeIfMethodDoesNotReturnADomainType() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "deleteByUserName", String.class); - - assertThat(method.getEntityInformation().getJavaType()).isAssignableFrom(User.class); - } - - @Test // DATAMONGO-2153 - public void findsAnnotatedAggregation() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "findByAggregation"); - - Assertions.assertThat(method.hasAnnotatedAggregation()).isTrue(); - Assertions.assertThat(method.getAnnotatedAggregation()).hasSize(1); - } - - @Test // DATAMONGO-2153 - public void detectsCollationForAggregation() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "findByAggregationWithCollation"); - - Assertions.assertThat(method.hasAnnotatedCollation()).isTrue(); - Assertions.assertThat(method.getAnnotatedCollation()).isEqualTo("de_AT"); - } - - private MongoQueryMethod queryMethod(Class repository, String name, Class... parameters) throws Exception { - - Method method = repository.getMethod(name, parameters); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - return new MongoQueryMethod(method, new DefaultRepositoryMetadata(repository), factory, context); - } - - interface PersonRepository extends Repository { - - // Misses Pageable - GeoPage findByLocationNear(Point point, Distance distance); - - GeoPage findByLocationNear(Point point, Distance distance, Pageable pageable); - - GeoResult findByEmailAddress(String lastname, Point location); - - GeoResults findByFirstname(String firstname, Point location); - - Collection> findByLastname(String lastname, Point location); - - @Meta - List emptyMetaAnnotation(); - - @Meta(cursorBatchSize = 100) - List batchSize(); - - @Meta(cursorBatchSize = -200) - List negativeBatchSize(); - - @Meta(maxExecutionTimeMs = 100) - List metaWithMaxExecutionTime(); - - @Meta(maxExecutionTimeMs = 100) - List metaWithSpellFixedMaxExecutionTime(); - - @Meta(comment = "foo bar") - List metaWithComment(); - - @Meta(flags = { org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT }) - List metaWithNoCursorTimeout(); - - @Meta(flags = { org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT, - org.springframework.data.mongodb.core.query.Meta.CursorOption.SECONDARY_READS }) - List metaWithMultipleFlags(); - - // DATAMONGO-1266 - void deleteByUserName(String userName); - - @Aggregation("{'$group': { _id: '$templateId', maxVersion : { $max : '$version'} } }") - List findByAggregation(); - - @Aggregation(pipeline = "{'$group': { _id: '$templateId', maxVersion : { $max : '$version'} } }", - collation = "de_AT") - List findByAggregationWithCollation(); - } - - interface SampleRepository extends Repository { - - List
method(); - } - - interface SampleRepository2 extends Repository { - - List method(); - - Customer methodReturningAnInterface(); - } - - interface Customer { - - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java deleted file mode 100644 index fe2f447d78..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2014-2021 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.repository.query; - -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.test.util.Assertions.*; - -import java.lang.reflect.Method; -import java.util.List; - -import org.bson.Document; -import org.bson.json.JsonParseException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.TextCriteria; -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.Person.Sex; -import org.springframework.data.mongodb.repository.Query; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; - -/** - * Unit tests for {@link PartTreeMongoQuery}. - * - * @author Christoph Strobl - * @author Oliver Gierke - * @author Thomas Darimont - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class PartTreeMongoQueryUnitTests { - - @Mock MongoOperations mongoOperationsMock; - @Mock ExecutableFind findOperationMock; - - MongoMappingContext mappingContext; - - @BeforeEach - public void setUp() { - - mappingContext = new MongoMappingContext(); - DbRefResolver dbRefResolver = new DefaultDbRefResolver(mock(MongoDatabaseFactory.class)); - MongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext); - - doReturn(converter).when(mongoOperationsMock).getConverter(); - doReturn(findOperationMock).when(mongoOperationsMock).query(any()); - } - - @Test // DATAMOGO-952 - public void rejectsInvalidFieldSpecification() { - - assertThatIllegalStateException().isThrownBy(() -> deriveQueryFromMethod("findByLastname", "foo")) - .withMessageContaining("findByLastname"); - } - - @Test // DATAMOGO-952 - public void singleFieldJsonIncludeRestrictionShouldBeConsidered() { - - org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findByFirstname", "foo"); - - assertThat(query.getFieldsObject()).isEqualTo(new Document().append("firstname", 1)); - } - - @Test // DATAMOGO-952 - public void multiFieldJsonIncludeRestrictionShouldBeConsidered() { - - org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findByFirstnameAndLastname", "foo", - "bar"); - - assertThat(query.getFieldsObject()).isEqualTo(new Document().append("firstname", 1).append("lastname", 1)); - } - - @Test // DATAMOGO-952 - public void multiFieldJsonExcludeRestrictionShouldBeConsidered() { - - org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findPersonByFirstnameAndLastname", - "foo", "bar"); - - assertThat(query.getFieldsObject()).isEqualTo(new Document().append("firstname", 0).append("lastname", 0)); - } - - @Test // DATAMOGO-973 - public void shouldAddFullTextParamCorrectlyToDerivedQuery() { - - org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findPersonByFirstname", "text", - TextCriteria.forDefaultLanguage().matching("search")); - - assertThat(query.getQueryObject()).containsEntry("$text.$search", "search").containsEntry("firstname", "text"); - } - - @Test // DATAMONGO-1180 - public void propagatesRootExceptionForInvalidQuery() { - - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> deriveQueryFromMethod("findByAge", 1)) - .withCauseInstanceOf(JsonParseException.class); - } - - @Test // DATAMONGO-1345, DATAMONGO-1735 - public void doesNotDeriveFieldSpecForNormalDomainType() { - assertThat(deriveQueryFromMethod("findPersonBy", new Object[0]).getFieldsObject()).isEqualTo(new Document()); - } - - @Test // DATAMONGO-1345 - public void restrictsQueryToFieldsRequiredForProjection() { - - Document fieldsObject = deriveQueryFromMethod("findPersonProjectedBy", new Object[0]).getFieldsObject(); - - assertThat(fieldsObject.get("firstname")).isEqualTo(1); - assertThat(fieldsObject.get("lastname")).isEqualTo(1); - } - - @Test // DATAMONGO-1345 - public void restrictsQueryToFieldsRequiredForDto() { - - Document fieldsObject = deriveQueryFromMethod("findPersonDtoByAge", new Object[] { 42 }).getFieldsObject(); - - assertThat(fieldsObject.get("firstname")).isEqualTo(1); - assertThat(fieldsObject.get("lastname")).isEqualTo(1); - } - - @Test // DATAMONGO-1345 - public void usesDynamicProjection() { - - Document fields = deriveQueryFromMethod("findDynamicallyProjectedBy", ExtendedProjection.class).getFieldsObject(); - - assertThat(fields.get("firstname")).isEqualTo(1); - assertThat(fields.get("lastname")).isEqualTo(1); - assertThat(fields.get("age")).isEqualTo(1); - } - - @Test // DATAMONGO-1500 - public void shouldLeaveParameterConversionToQueryMapper() { - - org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findBySex", Sex.FEMALE); - - assertThat(query.getQueryObject().get("sex")).isEqualTo(Sex.FEMALE); - assertThat(query.getFieldsObject().get("firstname")).isEqualTo(1); - } - - @Test // DATAMONGO-1729, DATAMONGO-1735 - public void doesNotCreateFieldsObjectForOpenProjection() { - - org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findAllBy"); - - assertThat(query.getFieldsObject()).isEqualTo(new Document()); - } - - @Test // DATAMONGO-1865 - public void limitingReturnsTrueIfTreeIsLimiting() { - assertThat(createQueryForMethod("findFirstBy").isLimiting()).isTrue(); - } - - @Test // DATAMONGO-1865 - public void limitingReturnsFalseIfTreeIsNotLimiting() { - assertThat(createQueryForMethod("findPersonBy").isLimiting()).isFalse(); - } - - private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object... args) { - - Class[] types = new Class[args.length]; - - for (int i = 0; i < args.length; i++) { - types[i] = args[i].getClass(); - } - - PartTreeMongoQuery partTreeQuery = createQueryForMethod(method, types); - - MongoParameterAccessor accessor = new MongoParametersParameterAccessor(partTreeQuery.getQueryMethod(), args); - return partTreeQuery.createQuery(new ConvertingParameterAccessor(mongoOperationsMock.getConverter(), accessor)); - } - - private PartTreeMongoQuery createQueryForMethod(String methodName, Class... paramTypes) { - - try { - - Method method = Repo.class.getMethod(methodName, paramTypes); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(Repo.class), factory, - mappingContext); - - return new PartTreeMongoQuery(queryMethod, mongoOperationsMock, new SpelExpressionParser(), - QueryMethodEvaluationContextProvider.DEFAULT); - } catch (Exception e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - interface Repo extends MongoRepository { - - @Query(fields = "firstname") - Person findByLastname(String lastname); - - @Query(fields = "{ 'firstname' : 1 }") - Person findByFirstname(String lastname); - - @Query(fields = "{ 'firstname' : 1, 'lastname' : 1 }") - Person findByFirstnameAndLastname(String firstname, String lastname); - - @Query(fields = "{ 'firstname' : 0, 'lastname' : 0 }") - Person findPersonByFirstnameAndLastname(String firstname, String lastname); - - Person findPersonByFirstname(String firstname, TextCriteria fullText); - - @Query(fields = "{ 'firstname }") - Person findByAge(Integer age); - - Person findPersonBy(); - - PersonProjection findPersonProjectedBy(); - - PersonDto findPersonDtoByAge(Integer age); - - T findDynamicallyProjectedBy(Class type); - - @Query(fields = "{ 'firstname' : 1 }") - List findBySex(Sex sex); - - OpenProjection findAllBy(); - - Person findFirstBy(); - } - - interface PersonProjection { - - String getFirstname(); - - String getLastname(); - } - - interface ExtendedProjection extends PersonProjection { - - int getAge(); - } - - static class PersonDto { - - public String firstname, lastname; - - public PersonDto(String firstname, String lastname) { - - this.firstname = firstname; - this.lastname = lastname; - } - } - - interface OpenProjection { - - String getFirstname(); - - @Value("#{target.firstname + ' ' + target.lastname}") - String getFullname(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java deleted file mode 100644 index 3d5297bf26..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.lang.reflect.Method; -import java.util.Arrays; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.reactivestreams.Publisher; - -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Range; -import org.springframework.data.domain.Range.Bound; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoResult; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.DeleteExecution; -import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.GeoNearExecution; -import org.springframework.data.util.ClassTypeInformation; -import org.springframework.util.ClassUtils; - -import com.mongodb.client.result.DeleteResult; - -/** - * Unit tests for {@link ReactiveMongoQueryExecution}. - * - * @author Mark Paluch - * @author Artyom Gabeev - */ -@ExtendWith(MockitoExtension.class) -public class ReactiveMongoQueryExecutionUnitTests { - - @Mock private ReactiveMongoOperations operations; - @Mock private MongoParameterAccessor parameterAccessor; - @Mock private MongoQueryMethod method; - - @Test // DATAMONGO-1444 - public void geoNearExecutionShouldApplyQuerySettings() throws Exception { - - Method geoNear = ClassUtils.getMethod(GeoRepo.class, "geoNear"); - Query query = new Query(); - when(parameterAccessor.getGeoNearLocation()).thenReturn(new Point(1, 2)); - when(parameterAccessor.getDistanceRange()) - .thenReturn(Range.from(Bound.inclusive(new Distance(10))).to(Bound.inclusive(new Distance(15)))); - when(parameterAccessor.getPageable()).thenReturn(PageRequest.of(1, 10)); - - new GeoNearExecution(operations, parameterAccessor, ClassTypeInformation.fromReturnTypeOf(geoNear)).execute(query, - Person.class, "person"); - - ArgumentCaptor queryArgumentCaptor = ArgumentCaptor.forClass(NearQuery.class); - verify(operations).geoNear(queryArgumentCaptor.capture(), eq(Person.class), eq("person")); - - NearQuery nearQuery = queryArgumentCaptor.getValue(); - assertThat(nearQuery.toDocument().get("near")).isEqualTo(Arrays.asList(1d, 2d)); - assertThat(nearQuery.getSkip()).isEqualTo(10L); - assertThat(nearQuery.getMinDistance()).isEqualTo(new Distance(10)); - assertThat(nearQuery.getMaxDistance()).isEqualTo(new Distance(15)); - } - - @Test // DATAMONGO-1444 - public void geoNearExecutionShouldApplyMinimalSettings() throws Exception { - - Method geoNear = ClassUtils.getMethod(GeoRepo.class, "geoNear"); - Query query = new Query(); - when(parameterAccessor.getPageable()).thenReturn(Pageable.unpaged()); - when(parameterAccessor.getGeoNearLocation()).thenReturn(new Point(1, 2)); - when(parameterAccessor.getDistanceRange()).thenReturn(Range.unbounded()); - - new GeoNearExecution(operations, parameterAccessor, ClassTypeInformation.fromReturnTypeOf(geoNear)).execute(query, - Person.class, "person"); - - ArgumentCaptor queryArgumentCaptor = ArgumentCaptor.forClass(NearQuery.class); - verify(operations).geoNear(queryArgumentCaptor.capture(), eq(Person.class), eq("person")); - - NearQuery nearQuery = queryArgumentCaptor.getValue(); - assertThat(nearQuery.toDocument().get("near")).isEqualTo(Arrays.asList(1d, 2d)); - assertThat(nearQuery.getSkip()).isEqualTo(0L); - assertThat(nearQuery.getMinDistance()).isNull(); - assertThat(nearQuery.getMaxDistance()).isNull(); - } - - @Test // DATAMONGO-2351 - public void acknowledgedDeleteReturnsDeletedCount() { - - when(operations.remove(any(Query.class), any(Class.class), anyString())) - .thenReturn(Mono.just(DeleteResult.acknowledged(10))); - - Mono.from((Publisher) new DeleteExecution(operations, method).execute(new Query(), Class.class, "")) // - .as(StepVerifier::create) // - .expectNext(10L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2351 - public void unacknowledgedDeleteReturnsZeroDeletedCount() { - - when(operations.remove(any(Query.class), any(Class.class), anyString())) - .thenReturn(Mono.just(DeleteResult.unacknowledged())); - - Mono.from((Publisher) new DeleteExecution(operations, method).execute(new Query(), Class.class, "")) // - .as(StepVerifier::create) // - .expectNext(0L) // - .verifyComplete(); - } - - interface GeoRepo { - Flux> geoNear(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodUnitTests.java deleted file mode 100644 index 12fffca0fe..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodUnitTests.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.lang.reflect.Method; -import java.util.List; - -import org.assertj.core.api.Assertions; -import org.junit.Before; -import org.junit.Test; - -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.GeoResult; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.User; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.repository.Address; -import org.springframework.data.mongodb.repository.Aggregation; -import org.springframework.data.mongodb.repository.Contact; -import org.springframework.data.mongodb.repository.Meta; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; - -/** - * Unit test for {@link ReactiveMongoQueryMethod}. - * - * @author Mark Paluch - */ -public class ReactiveMongoQueryMethodUnitTests { - - MongoMappingContext context; - - @Before - public void setUp() { - context = new MongoMappingContext(); - } - - @Test // DATAMONGO-1444 - public void detectsCollectionFromRepoTypeIfReturnTypeNotAssignable() throws Exception { - - ReactiveMongoQueryMethod queryMethod = queryMethod(SampleRepository.class, "method"); - MongoEntityMetadata metadata = queryMethod.getEntityInformation(); - - assertThat(metadata.getJavaType()).isAssignableFrom(Address.class); - assertThat(metadata.getCollectionName()).isEqualTo("contact"); - } - - @Test // DATAMONGO-1444 - public void detectsCollectionFromReturnTypeIfReturnTypeAssignable() throws Exception { - - MongoQueryMethod queryMethod = queryMethod(SampleRepository2.class, "method"); - MongoEntityMetadata entityInformation = queryMethod.getEntityInformation(); - - assertThat(entityInformation.getJavaType()).isAssignableFrom(Person.class); - assertThat(entityInformation.getCollectionName()).isEqualTo("person"); - } - - @Test // DATAMONGO-1444 - public void discoversUserAsDomainTypeForGeoPagingQueryMethod() throws Exception { - - MongoQueryMethod queryMethod = queryMethod(PersonRepository.class, "findByLocationNear", Point.class, - Distance.class, Pageable.class); - assertThat(queryMethod.isGeoNearQuery()).isFalse(); - assertThat(queryMethod.isPageQuery()).isFalse(); - - queryMethod = queryMethod(PersonRepository.class, "findByFirstname", String.class, Point.class); - assertThat(queryMethod.isGeoNearQuery()).isFalse(); - assertThat(queryMethod.isPageQuery()).isFalse(); - assertThat(queryMethod.getEntityInformation().getJavaType()).isAssignableFrom(User.class); - - assertThat(queryMethod(PersonRepository.class, "findByEmailAddress", String.class, Point.class).isGeoNearQuery()) - .isTrue(); - assertThat(queryMethod(PersonRepository.class, "findByFirstname", String.class, Point.class).isGeoNearQuery()) - .isFalse(); - assertThat(queryMethod(PersonRepository.class, "findByLastname", String.class, Point.class).isGeoNearQuery()) - .isTrue(); - } - - @Test(expected = IllegalArgumentException.class) // DATAMONGO-1444 - public void rejectsNullMappingContext() throws Exception { - - Method method = PersonRepository.class.getMethod("findByFirstname", String.class, Point.class); - - new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class), - new SpelAwareProxyProjectionFactory(), null); - } - - @Test // DATAMONGO-1444 - public void rejectsMonoPageableResult() { - assertThatIllegalStateException() - .isThrownBy(() -> queryMethod(PersonRepository.class, "findMonoByLastname", String.class, Pageable.class)); - } - - @Test // DATAMONGO-1444 - public void createsMongoQueryMethodObjectForMethodReturningAnInterface() throws Exception { - queryMethod(SampleRepository2.class, "methodReturningAnInterface"); - } - - @Test // DATAMONGO-1444 - public void createsMongoQueryMethodWithEmptyMetaCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "emptyMetaAnnotation"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().hasValues()).isFalse(); - } - - @Test // DATAMONGO-1444 - public void createsMongoQueryMethodWithMaxExecutionTimeCorrectly() throws Exception { - - MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithMaxExecutionTime"); - - assertThat(method.hasQueryMetaAttributes()).isTrue(); - assertThat(method.getQueryMetaAttributes().getMaxTimeMsec()).isEqualTo(100L); - } - - @Test // DATAMONGO-1444 - public void throwsExceptionOnWrappedPage() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) - .isThrownBy(() -> queryMethod(PersonRepository.class, "findMonoPageByLastname", String.class, Pageable.class)); - } - - @Test // DATAMONGO-1444 - public void throwsExceptionOnWrappedSlice() { - assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) - .isThrownBy(() -> queryMethod(PersonRepository.class, "findMonoSliceByLastname", String.class, Pageable.class)); - } - - @Test // DATAMONGO-1444 - public void fallsBackToRepositoryDomainTypeIfMethodDoesNotReturnADomainType() throws Exception { - - ReactiveMongoQueryMethod method = queryMethod(PersonRepository.class, "deleteByUserName", String.class); - - assertThat(method.getEntityInformation().getJavaType()).isAssignableFrom(User.class); - } - - @Test // DATAMONGO-2153 - public void findsAnnotatedAggregation() throws Exception { - - ReactiveMongoQueryMethod method = queryMethod(PersonRepository.class, "findByAggregation"); - - Assertions.assertThat(method.hasAnnotatedAggregation()).isTrue(); - Assertions.assertThat(method.getAnnotatedAggregation()).hasSize(1); - } - - @Test // DATAMONGO-2153 - public void detectsCollationForAggregation() throws Exception { - - ReactiveMongoQueryMethod method = queryMethod(PersonRepository.class, "findByAggregationWithCollation"); - - Assertions.assertThat(method.hasAnnotatedCollation()).isTrue(); - Assertions.assertThat(method.getAnnotatedCollation()).isEqualTo("de_AT"); - } - - private ReactiveMongoQueryMethod queryMethod(Class repository, String name, Class... parameters) - throws Exception { - - Method method = repository.getMethod(name, parameters); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - return new ReactiveMongoQueryMethod(method, new DefaultRepositoryMetadata(repository), factory, context); - } - - interface PersonRepository extends Repository { - - Mono findMonoByLastname(String lastname, Pageable pageRequest); - - Mono> findMonoPageByLastname(String lastname, Pageable pageRequest); - - Mono> findMonoSliceByLastname(String lastname, Pageable pageRequest); - - // Misses Pageable - Flux findByLocationNear(Point point, Distance distance); - - Flux findByLocationNear(Point point, Distance distance, Pageable pageable); - - Mono> findByEmailAddress(String lastname, Point location); - - Flux findByFirstname(String firstname, Point location); - - Flux> findByLastname(String lastname, Point location); - - @Meta - Flux emptyMetaAnnotation(); - - @Meta(maxExecutionTimeMs = 100) - Flux metaWithMaxExecutionTime(); - - void deleteByUserName(String userName); - - @Aggregation("{'$group': { _id: '$templateId', maxVersion : { $max : '$version'} } }") - Flux findByAggregation(); - - @Aggregation(pipeline = "{'$group': { _id: '$templateId', maxVersion : { $max : '$version'} } }", - collation = "de_AT") - Flux findByAggregationWithCollation(); - } - - interface SampleRepository extends Repository { - - List
method(); - } - - interface SampleRepository2 extends Repository { - - List method(); - - Customer methodReturningAnInterface(); - } - - interface Customer {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java deleted file mode 100644 index f5b3a7c583..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2019-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import lombok.Value; -import org.reactivestreams.Publisher; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; -import org.springframework.data.mongodb.core.aggregation.AggregationOptions; -import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext; -import org.springframework.data.mongodb.core.aggregation.TypedAggregation; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.repository.Aggregation; -import org.springframework.data.mongodb.repository.Meta; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; -import org.springframework.data.repository.reactive.ReactiveCrudRepository; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; - -/** - * Unit tests for {@link ReactiveStringBasedAggregation}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -public class ReactiveStringBasedAggregationUnitTests { - - SpelExpressionParser PARSER = new SpelExpressionParser(); - - @Mock ReactiveMongoOperations operations; - @Mock DbRefResolver dbRefResolver; - MongoConverter converter; - - private static final String RAW_SORT_STRING = "{ '$sort' : { 'lastname' : -1 } }"; - private static final String RAW_GROUP_BY_LASTNAME_STRING = "{ '$group': { '_id' : '$lastname', 'names' : { '$addToSet' : '$firstname' } } }"; - private static final String GROUP_BY_LASTNAME_STRING_WITH_PARAMETER_PLACEHOLDER = "{ '$group': { '_id' : '$lastname', names : { '$addToSet' : '$?0' } } }"; - private static final String GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER = "{ '$group': { '_id' : '$lastname', 'names' : { '$addToSet' : '$?#{[0]}' } } }"; - - private static final Document SORT = Document.parse(RAW_SORT_STRING); - private static final Document GROUP_BY_LASTNAME = Document.parse(RAW_GROUP_BY_LASTNAME_STRING); - - @BeforeEach - public void setUp() { - - converter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext()); - when(operations.getConverter()).thenReturn(converter); - when(operations.aggregate(any(TypedAggregation.class), any())).thenReturn(Flux.empty()); - when(operations.execute(any())).thenReturn(Flux.empty()); - } - - @Test // DATAMONGO-2153 - public void plainStringAggregation() { - - AggregationInvocation invocation = executeAggregation("plainStringAggregation"); - - assertThat(inputTypeOf(invocation)).isEqualTo(Person.class); - assertThat(targetTypeOf(invocation)).isEqualTo(PersonAggregate.class); - assertThat(pipelineOf(invocation)).containsExactly(GROUP_BY_LASTNAME, SORT); - } - - @Test // DATAMONGO-2153 - public void plainStringAggregationConsidersMeta() { - - AggregationInvocation invocation = executeAggregation("plainStringAggregation"); - - AggregationOptions options = invocation.aggregation.getOptions(); - - assertThat(options.getComment()).contains("expensive-aggregation"); - assertThat(options.getCursorBatchSize()).isEqualTo(42); - } - - @Test // DATAMONGO-2153 - public void plainStringAggregationWithSortParameter() { - - AggregationInvocation invocation = executeAggregation("plainStringAggregation", - Sort.by(Direction.DESC, "lastname")); - - assertThat(inputTypeOf(invocation)).isEqualTo(Person.class); - assertThat(targetTypeOf(invocation)).isEqualTo(PersonAggregate.class); - assertThat(pipelineOf(invocation)).containsExactly(GROUP_BY_LASTNAME, SORT); - - AggregationOptions options = invocation.aggregation.getOptions(); - - assertThat(options.getComment()).isEmpty(); - assertThat(options.getCursorBatchSize()).isNull(); - } - - @Test // DATAMONGO-2153 - public void replaceParameter() { - - AggregationInvocation invocation = executeAggregation("parameterReplacementAggregation", "firstname"); - - assertThat(inputTypeOf(invocation)).isEqualTo(Person.class); - assertThat(targetTypeOf(invocation)).isEqualTo(PersonAggregate.class); - assertThat(pipelineOf(invocation)).containsExactly(GROUP_BY_LASTNAME); - } - - @Test // DATAMONGO-2153 - public void replaceSpElParameter() { - - AggregationInvocation invocation = executeAggregation("spelParameterReplacementAggregation", "firstname"); - - assertThat(inputTypeOf(invocation)).isEqualTo(Person.class); - assertThat(targetTypeOf(invocation)).isEqualTo(PersonAggregate.class); - assertThat(pipelineOf(invocation)).containsExactly(GROUP_BY_LASTNAME); - } - - @Test // DATAMONGO-2153 - public void aggregateWithCollation() { - - AggregationInvocation invocation = executeAggregation("aggregateWithCollation"); - - assertThat(collationOf(invocation)).isEqualTo(Collation.of("de_AT")); - } - - @Test // DATAMONGO-2153 - public void aggregateWithCollationParameter() { - - AggregationInvocation invocation = executeAggregation("aggregateWithCollation", Collation.of("en_US")); - - assertThat(collationOf(invocation)).isEqualTo(Collation.of("en_US")); - } - - @Test // DATAMONGO-2557 - void aggregationRetrievesCodecFromDriverJustOnceForMultipleAggregationOperationsInPipeline() { - - executeAggregation("multiOperationPipeline", "firstname"); - verify(operations).execute(any()); - } - - private AggregationInvocation executeAggregation(String name, Object... args) { - - Class[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(size -> new Class[size]); - ReactiveStringBasedAggregation aggregation = createAggregationForMethod(name, argTypes); - - ArgumentCaptor aggregationCaptor = ArgumentCaptor.forClass(TypedAggregation.class); - ArgumentCaptor targetTypeCaptor = ArgumentCaptor.forClass(Class.class); - - Object result = Flux.from((Publisher) aggregation.execute(args)).blockLast(); - - verify(operations).aggregate(aggregationCaptor.capture(), targetTypeCaptor.capture()); - - return new AggregationInvocation(aggregationCaptor.getValue(), targetTypeCaptor.getValue(), result); - } - - private ReactiveStringBasedAggregation createAggregationForMethod(String name, Class... parameters) { - - Method method = ClassUtils.getMethod(SampleRepository.class, name, parameters); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method, - new DefaultRepositoryMetadata(SampleRepository.class), factory, converter.getMappingContext()); - return new ReactiveStringBasedAggregation(queryMethod, operations, PARSER, - ReactiveQueryMethodEvaluationContextProvider.DEFAULT); - } - - private List pipelineOf(AggregationInvocation invocation) { - - AggregationOperationContext context = new TypeBasedAggregationOperationContext( - invocation.aggregation.getInputType(), converter.getMappingContext(), new QueryMapper(converter)); - - return invocation.aggregation.toPipeline(context); - } - - private Class inputTypeOf(AggregationInvocation invocation) { - return invocation.aggregation.getInputType(); - } - - @Nullable - private Collation collationOf(AggregationInvocation invocation) { - return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getCollation().orElse(null) - : null; - } - - private Class targetTypeOf(AggregationInvocation invocation) { - return invocation.getTargetType(); - } - - private interface SampleRepository extends ReactiveCrudRepository { - - @Meta(cursorBatchSize = 42, comment = "expensive-aggregation") - @Aggregation({ RAW_GROUP_BY_LASTNAME_STRING, RAW_SORT_STRING }) - Mono plainStringAggregation(); - - @Aggregation(RAW_GROUP_BY_LASTNAME_STRING) - Mono plainStringAggregation(Sort sort); - - @Aggregation(GROUP_BY_LASTNAME_STRING_WITH_PARAMETER_PLACEHOLDER) - Mono parameterReplacementAggregation(String attribute); - - @Aggregation(GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER) - Mono spelParameterReplacementAggregation(String arg0); - - @Aggregation(pipeline = {RAW_GROUP_BY_LASTNAME_STRING, GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER}) - Mono multiOperationPipeline(String arg0); - - @Aggregation(pipeline = RAW_GROUP_BY_LASTNAME_STRING, collation = "de_AT") - Mono aggregateWithCollation(); - - @Aggregation(pipeline = RAW_GROUP_BY_LASTNAME_STRING, collation = "de_AT") - Mono aggregateWithCollation(Collation collation); - } - - static class PersonAggregate { - - } - - @Value - static class AggregationInvocation { - - TypedAggregation aggregation; - Class targetType; - Object result; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java deleted file mode 100644 index f2e9d3e06c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright 2016-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.Map; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.core.ReactiveFindOperation.ReactiveFind; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.repository.Address; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.Query; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; -import org.springframework.data.spel.spi.EvaluationContextExtension; -import org.springframework.data.spel.spi.ReactiveEvaluationContextExtension; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.util.Base64Utils; - -/** - * Unit tests for {@link ReactiveStringBasedMongoQuery}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -public class ReactiveStringBasedMongoQueryUnitTests { - - SpelExpressionParser PARSER = new SpelExpressionParser(); - - @Mock ReactiveMongoOperations operations; - @Mock DbRefResolver factory; - @Mock ReactiveFind reactiveFind; - - MongoConverter converter; - - @BeforeEach - public void setUp() { - - when(operations.query(any())).thenReturn(reactiveFind); - when(operations.execute(any())).thenReturn(Flux.empty()); - - this.converter = new MappingMongoConverter(factory, new MongoMappingContext()); - } - - @Test // DATAMONGO-1444 - public void bindsSimplePropertyCorrectly() throws Exception { - - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class); - ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block(); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-1444 - public void bindsComplexPropertyCorrectly() throws Exception { - - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByAddress", Address.class); - - Address address = new Address("Foo", "0123", "Bar"); - ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, address); - - Document dbObject = new Document(); - converter.write(address, dbObject); - dbObject.remove(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block(); - Document queryObject = new Document("address", dbObject); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject); - - assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson()); - } - - @Test // DATAMONGO-1444 - public void constructsDeleteQueryCorrectly() throws Exception { - - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("removeByLastname", String.class); - assertThat(mongoQuery.isDeleteQuery()).isTrue(); - } - - @Test // DATAMONGO-1444 - public void preventsDeleteAndCountFlagAtTheSameTime() { - assertThatIllegalArgumentException().isThrownBy(() -> createQueryForMethod("invalidMethod", String.class)); - } - - @Test // DATAMONGO-2030 - public void shouldSupportExistsProjection() throws Exception { - - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("existsByLastname", String.class); - - assertThat(mongoQuery.isExistsQuery()).isTrue(); - } - - @Test // DATAMONGO-1444 - public void shouldSupportFindByParameterizedCriteriaAndFields() throws Exception { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, - new Document("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1)); - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByParameterizedCriteriaAndFields", - Document.class, Map.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor).block(); - - assertThat(query.getQueryObject()) - .isEqualTo(new BasicQuery("{ \"firstname\": \"first\", \"lastname\": \"last\"}").getQueryObject()); - assertThat(query.getFieldsObject()).isEqualTo(new BasicQuery(null, "{ \"lastname\": 1}").getFieldsObject()); - } - - @Test // DATAMONGO-1444 - public void shouldParseQueryWithParametersInExpression() throws Exception { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 1, 2, 3, 4); - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithParametersInExpression", int.class, - int.class, int.class, int.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor).block(); - - assertThat(query.getQueryObject()) - .isEqualTo(new BasicQuery("{$where: 'return this.date.getUTCMonth() == 3 && this.date.getUTCDay() == 4;'}") - .getQueryObject()); - } - - @Test // DATAMONGO-1444 - public void shouldParseJsonKeyReplacementCorrectly() throws Exception { - - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("methodWithPlaceholderInKeyOfJsonStructure", - String.class, String.class); - ConvertingParameterAccessor parameterAccessor = StubParameterAccessor.getAccessor(converter, "key", "value"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor).block(); - - assertThat(query.getQueryObject()).isEqualTo(new Document().append("key", "value")); - } - - @Test // DATAMONGO-1444 - public void shouldSupportExpressionsInCustomQueries() throws Exception { - - ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews"); - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpression", String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block(); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-1444 - public void shouldSupportExpressionsInCustomQueriesWithNestedObject() throws Exception { - - ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2"); - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndNestedObject", - boolean.class, String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block(); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{ \"id\" : { \"$exists\" : true}}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-1444 - public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() throws Exception { - - ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2"); - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndMultipleNestedObjects", - boolean.class, String.class, String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block(); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( - "{ \"id\" : { \"$exists\" : true} , \"foo\" : 42 , \"bar\" : { \"$exists\" : false}}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-1444 - public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception { - - byte[] binaryData = "Matthews".getBytes("UTF-8"); - ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, binaryData); - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block(); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( - "{'lastname' : { '$binary' : '" + Base64Utils.encodeToString(binaryData) + "', '$type' : '" + 0 + "'}}"); - - assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson()); - } - - @Test // DATAMONGO-1894 - void shouldConsiderReactiveSpelExtension() throws Exception { - - ReactiveExtensionAwareQueryMethodEvaluationContextProvider contextProvider = new ReactiveExtensionAwareQueryMethodEvaluationContextProvider( - Collections.singletonList(ReactiveSpelExtension.INSTANCE)); - - ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter); - ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod(contextProvider, "withReactiveSpelExtensions"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block(); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{lastname: true}", "{project: true}"); - - assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson()); - } - - private ReactiveStringBasedMongoQuery createQueryForMethod(String name, Class... parameters) throws Exception { - return createQueryForMethod(ReactiveQueryMethodEvaluationContextProvider.DEFAULT, name, parameters); - } - - private ReactiveStringBasedMongoQuery createQueryForMethod( - ReactiveQueryMethodEvaluationContextProvider contextProvider, String name, Class... parameters) - throws Exception { - - Method method = SampleRepository.class.getMethod(name, parameters); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method, - new DefaultRepositoryMetadata(SampleRepository.class), factory, converter.getMappingContext()); - - return new ReactiveStringBasedMongoQuery(queryMethod, operations, PARSER, - contextProvider); - } - - private interface SampleRepository extends Repository { - - @Query("{ 'lastname' : ?0 }") - Mono findByLastname(String lastname); - - @Query("{ 'lastname' : ?0 }") - Mono findByLastnameAsBinary(byte[] lastname); - - @Query("{ 'address' : ?0 }") - Mono findByAddress(Address address); - - @Query(value = "{ 'lastname' : ?0 }", delete = true) - Mono removeByLastname(String lastname); - - @Query(value = "{ 'lastname' : ?0 }", delete = true, count = true) - Mono invalidMethod(String lastname); - - @Query(value = "?0", fields = "?1") - Mono findByParameterizedCriteriaAndFields(Document criteria, Map fields); - - @Query("{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}") - Flux findByQueryWithParametersInExpression(int param1, int param2, int param3, int param4); - - @Query("{ ?0 : ?1}") - Mono methodWithPlaceholderInKeyOfJsonStructure(String keyReplacement, String valueReplacement); - - @Query("{'lastname': ?#{[0]} }") - Flux findByQueryWithExpression(String param0); - - @Query("{'id':?#{ [0] ? { $exists :true} : [1] }}") - Flux findByQueryWithExpressionAndNestedObject(boolean param0, String param1); - - @Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}") - Flux findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2); - - @Query(value = "{ 'lastname' : ?0 }", exists = true) - Mono existsByLastname(String lastname); - - @Query(value = "{ 'lastname' : ?#{hasRole()} }", fields = "{project: ?#{hasRole()}}") - Mono withReactiveSpelExtensions(); - } - - public enum ReactiveSpelExtension implements ReactiveEvaluationContextExtension { - - INSTANCE; - - @Override - public Mono getExtension() { - return Mono.just(SpelExtension.INSTANCE); - } - - @Override - public String getExtensionId() { - return "sample"; - } - } - - public enum SpelExtension implements EvaluationContextExtension { - - INSTANCE; - - @Override - public Object getRootObject() { - return this; - } - - @Override - public String getExtensionId() { - return "sample"; - } - - public boolean hasRole() { - return true; - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java deleted file mode 100644 index 00506229ea..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright 2019-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import lombok.Value; - -import java.lang.reflect.Method; -import java.time.Duration; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; -import org.springframework.data.mongodb.core.aggregation.AggregationOptions; -import org.springframework.data.mongodb.core.aggregation.AggregationResults; -import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext; -import org.springframework.data.mongodb.core.aggregation.TypedAggregation; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.repository.Aggregation; -import org.springframework.data.mongodb.repository.Meta; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; - -import com.mongodb.MongoClientSettings; - -/** - * Unit tests for {@link StringBasedAggregation}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -public class StringBasedAggregationUnitTests { - - SpelExpressionParser PARSER = new SpelExpressionParser(); - - @Mock MongoOperations operations; - @Mock DbRefResolver dbRefResolver; - @Mock AggregationResults aggregationResults; - MongoConverter converter; - - private static final String RAW_SORT_STRING = "{ '$sort' : { 'lastname' : -1 } }"; - private static final String RAW_GROUP_BY_LASTNAME_STRING = "{ '$group': { '_id' : '$lastname', 'names' : { '$addToSet' : '$firstname' } } }"; - private static final String GROUP_BY_LASTNAME_STRING_WITH_PARAMETER_PLACEHOLDER = "{ '$group': { '_id' : '$lastname', names : { '$addToSet' : '$?0' } } }"; - private static final String GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER = "{ '$group': { '_id' : '$lastname', 'names' : { '$addToSet' : '$?#{[0]}' } } }"; - - private static final Document SORT = Document.parse(RAW_SORT_STRING); - private static final Document GROUP_BY_LASTNAME = Document.parse(RAW_GROUP_BY_LASTNAME_STRING); - - @BeforeEach - public void setUp() { - - converter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext()); - when(operations.getConverter()).thenReturn(converter); - when(operations.aggregate(any(TypedAggregation.class), any())).thenReturn(aggregationResults); - when(operations.execute(any())).thenReturn(MongoClientSettings.getDefaultCodecRegistry()); - } - - @Test // DATAMONGO-2153 - public void plainStringAggregation() { - - AggregationInvocation invocation = executeAggregation("plainStringAggregation"); - - assertThat(inputTypeOf(invocation)).isEqualTo(Person.class); - assertThat(targetTypeOf(invocation)).isEqualTo(PersonAggregate.class); - assertThat(pipelineOf(invocation)).containsExactly(GROUP_BY_LASTNAME, SORT); - } - - @Test // DATAMONGO-2153, DATAMONGO-2449 - public void plainStringAggregationConsidersMeta() { - - AggregationInvocation invocation = executeAggregation("plainStringAggregation"); - AggregationOptions options = invocation.aggregation.getOptions(); - - assertThat(options.getComment()).contains("expensive-aggregation"); - assertThat(options.getCursorBatchSize()).isEqualTo(42); - assertThat(options.isAllowDiskUse()).isTrue(); - assertThat(options.getMaxTime()).isEqualTo(Duration.ofMillis(100)); - } - - @Test // DATAMONGO-2153, DATAMONGO-2449 - public void returnSingleObject() { - - PersonAggregate expected = new PersonAggregate(); - when(aggregationResults.getUniqueMappedResult()).thenReturn(Collections.singletonList(expected)); - - AggregationInvocation invocation = executeAggregation("returnSingleEntity"); - assertThat(invocation.result).isEqualTo(expected); - - AggregationOptions options = invocation.aggregation.getOptions(); - - assertThat(options.getComment()).isEmpty(); - assertThat(options.getCursorBatchSize()).isNull(); - assertThat(options.isAllowDiskUse()).isFalse(); - assertThat(options.getMaxTime()).isEqualTo(Duration.ZERO); - } - - @Test // DATAMONGO-2153 - public void returnSingleObjectThrowsError() { - - when(aggregationResults.getUniqueMappedResult()).thenThrow(new IllegalArgumentException("o_O")); - - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> executeAggregation("returnSingleEntity")); - } - - @Test // DATAMONGO-2153 - public void returnCollection() { - - List expected = Collections.singletonList(new PersonAggregate()); - when(aggregationResults.getMappedResults()).thenReturn(expected); - - assertThat(executeAggregation("returnCollection").result).isEqualTo(expected); - } - - @Test // GH-3623 - public void returnNullWhenSingleResultIsNotPresent() { - - when(aggregationResults.getMappedResults()).thenReturn(Collections.emptyList()); - - assertThat(executeAggregation("simpleReturnType").result).isNull(); - } - - @Test // DATAMONGO-2153 - public void returnRawResultType() { - assertThat(executeAggregation("returnRawResultType").result).isEqualTo(aggregationResults); - } - - @Test // DATAMONGO-2153 - public void plainStringAggregationWithSortParameter() { - - AggregationInvocation invocation = executeAggregation("plainStringAggregation", - Sort.by(Direction.DESC, "lastname")); - - assertThat(inputTypeOf(invocation)).isEqualTo(Person.class); - assertThat(targetTypeOf(invocation)).isEqualTo(PersonAggregate.class); - assertThat(pipelineOf(invocation)).containsExactly(GROUP_BY_LASTNAME, SORT); - } - - @Test // DATAMONGO-2153 - public void replaceParameter() { - - AggregationInvocation invocation = executeAggregation("parameterReplacementAggregation", "firstname"); - - assertThat(inputTypeOf(invocation)).isEqualTo(Person.class); - assertThat(targetTypeOf(invocation)).isEqualTo(PersonAggregate.class); - assertThat(pipelineOf(invocation)).containsExactly(GROUP_BY_LASTNAME); - } - - @Test // DATAMONGO-2153 - public void replaceSpElParameter() { - - AggregationInvocation invocation = executeAggregation("spelParameterReplacementAggregation", "firstname"); - - assertThat(inputTypeOf(invocation)).isEqualTo(Person.class); - assertThat(targetTypeOf(invocation)).isEqualTo(PersonAggregate.class); - assertThat(pipelineOf(invocation)).containsExactly(GROUP_BY_LASTNAME); - } - - @Test // DATAMONGO-2153 - public void aggregateWithCollation() { - - AggregationInvocation invocation = executeAggregation("aggregateWithCollation"); - - assertThat(collationOf(invocation)).isEqualTo(Collation.of("de_AT")); - } - - @Test // DATAMONGO-2153 - public void aggregateWithCollationParameter() { - - AggregationInvocation invocation = executeAggregation("aggregateWithCollation", Collation.of("en_US")); - - assertThat(collationOf(invocation)).isEqualTo(Collation.of("en_US")); - } - - @Test // DATAMONGO-2506 - public void aggregateRaisesErrorOnInvalidReturnType() { - - StringBasedAggregation sba = createAggregationForMethod("invalidPageReturnType", Pageable.class); - assertThatExceptionOfType(InvalidMongoDbApiUsageException.class) // - .isThrownBy(() -> sba.execute(new Object[] { PageRequest.of(0, 1) })) // - .withMessageContaining("invalidPageReturnType") // - .withMessageContaining("Page"); - } - - @Test // DATAMONGO-2557 - void aggregationRetrievesCodecFromDriverJustOnceForMultipleAggregationOperationsInPipeline() { - - executeAggregation("multiOperationPipeline", "firstname"); - verify(operations).execute(any()); - } - - private AggregationInvocation executeAggregation(String name, Object... args) { - - Class[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new); - StringBasedAggregation aggregation = createAggregationForMethod(name, argTypes); - - ArgumentCaptor aggregationCaptor = ArgumentCaptor.forClass(TypedAggregation.class); - ArgumentCaptor targetTypeCaptor = ArgumentCaptor.forClass(Class.class); - - Object result = aggregation.execute(args); - - verify(operations).aggregate(aggregationCaptor.capture(), targetTypeCaptor.capture()); - - return new AggregationInvocation(aggregationCaptor.getValue(), targetTypeCaptor.getValue(), result); - } - - private StringBasedAggregation createAggregationForMethod(String name, Class... parameters) { - - Method method = ClassUtils.getMethod(SampleRepository.class, name, parameters); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class), - factory, converter.getMappingContext()); - return new StringBasedAggregation(queryMethod, operations, PARSER, QueryMethodEvaluationContextProvider.DEFAULT); - } - - private List pipelineOf(AggregationInvocation invocation) { - - AggregationOperationContext context = new TypeBasedAggregationOperationContext( - invocation.aggregation.getInputType(), converter.getMappingContext(), new QueryMapper(converter)); - - return invocation.aggregation.toPipeline(context); - } - - private Class inputTypeOf(AggregationInvocation invocation) { - return invocation.aggregation.getInputType(); - } - - @Nullable - private Collation collationOf(AggregationInvocation invocation) { - return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getCollation().orElse(null) - : null; - } - - private Class targetTypeOf(AggregationInvocation invocation) { - return invocation.getTargetType(); - } - - private interface SampleRepository extends Repository { - - @Meta(cursorBatchSize = 42, comment = "expensive-aggregation", allowDiskUse = true, maxExecutionTimeMs = 100) - @Aggregation({ RAW_GROUP_BY_LASTNAME_STRING, RAW_SORT_STRING }) - PersonAggregate plainStringAggregation(); - - @Aggregation(RAW_GROUP_BY_LASTNAME_STRING) - PersonAggregate plainStringAggregation(Sort sort); - - @Aggregation(RAW_GROUP_BY_LASTNAME_STRING) - PersonAggregate returnSingleEntity(); - - @Aggregation(RAW_GROUP_BY_LASTNAME_STRING) - List returnCollection(); - - @Aggregation(RAW_GROUP_BY_LASTNAME_STRING) - AggregationResults returnRawResultType(); - - @Aggregation(RAW_GROUP_BY_LASTNAME_STRING) - AggregationResults returnRawResults(); - - @Aggregation(GROUP_BY_LASTNAME_STRING_WITH_PARAMETER_PLACEHOLDER) - PersonAggregate parameterReplacementAggregation(String attribute); - - @Aggregation(GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER) - PersonAggregate spelParameterReplacementAggregation(String arg0); - - @Aggregation(pipeline = { RAW_GROUP_BY_LASTNAME_STRING, GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER }) - PersonAggregate multiOperationPipeline(String arg0); - - @Aggregation(pipeline = RAW_GROUP_BY_LASTNAME_STRING, collation = "de_AT") - PersonAggregate aggregateWithCollation(); - - @Aggregation(pipeline = RAW_GROUP_BY_LASTNAME_STRING, collation = "de_AT") - PersonAggregate aggregateWithCollation(Collation collation); - - @Aggregation(RAW_GROUP_BY_LASTNAME_STRING) - Page invalidPageReturnType(Pageable page); - - @Aggregation(RAW_GROUP_BY_LASTNAME_STRING) - String simpleReturnType(); - } - - static class PersonAggregate { - - } - - @Value - static class AggregationInvocation { - - TypedAggregation aggregation; - Class targetType; - Object result; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java deleted file mode 100644 index c0464f1bbf..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java +++ /dev/null @@ -1,829 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.query; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.bson.BsonBinarySubType; -import org.bson.Document; -import org.bson.UuidRepresentation; -import org.bson.codecs.configuration.CodecRegistry; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; -import org.springframework.data.mongodb.core.DbCallback; -import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.repository.Address; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.Query; -import org.springframework.data.projection.ProjectionFactory; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.data.repository.Repository; -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.util.Base64Utils; - -import com.mongodb.MongoClientSettings; -import com.mongodb.reactivestreams.client.MongoClients; - -/** - * Unit tests for {@link StringBasedMongoQuery}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Thomas Darimont - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -public class StringBasedMongoQueryUnitTests { - - SpelExpressionParser PARSER = new SpelExpressionParser(); - - @Mock MongoOperations operations; - @Mock ExecutableFind findOperation; - @Mock DbRefResolver factory; - - MongoConverter converter; - - @BeforeEach - public void setUp() { - - this.converter = new MappingMongoConverter(factory, new MongoMappingContext()); - - doReturn(findOperation).when(operations).query(any()); - doReturn(MongoClientSettings.getDefaultCodecRegistry()).when(operations).execute(any()); - } - - @Test - public void bindsSimplePropertyCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test - public void bindsComplexPropertyCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAddress", Address.class); - - Address address = new Address("Foo", "0123", "Bar"); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, address); - - Document document = new Document(); - converter.write(address, document); - document.remove(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - Document queryObject = new Document("address", document); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject); - - assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson()); - } - - @Test - public void bindsMultipleParametersCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAndAddress", String.class, Address.class); - - Address address = new Address("Foo", "0123", "Bar"); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews", address); - - Document addressDocument = new Document(); - converter.write(address, addressDocument); - addressDocument.remove(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY); - - Document reference = new Document("lastname", "Matthews"); - reference.append("address", addressDocument); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject().toJson()).isEqualTo(reference.toJson()); - } - - @Test - public void bindsNullParametersCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAddress", Address.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] { null }); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject().containsKey("address")).isTrue(); - assertThat(query.getQueryObject().get("address")).isNull(); - } - - @Test // DATAMONGO-821 - public void bindsDbrefCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByHavingSizeFansNotZero"); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()) - .isEqualTo(new BasicQuery("{ fans : { $not : { $size : 0 } } }").getQueryObject()); - } - - @Test // DATAMONGO-566 - public void constructsDeleteQueryCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("removeByLastname", String.class); - assertThat(mongoQuery.isDeleteQuery()).isTrue(); - } - - @Test // DATAMONGO-566 - public void preventsDeleteAndCountFlagAtTheSameTime() { - assertThatIllegalArgumentException().isThrownBy(() -> createQueryForMethod("invalidMethod", String.class)); - } - - @Test // DATAMONGO-420 - public void shouldSupportFindByParameterizedCriteriaAndFields() { - - ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, - StubParameterAccessor.getAccessor(converter, // - new Document("firstname", "first").append("lastname", "last"), // - Collections.singletonMap("lastname", 1))); - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByParameterizedCriteriaAndFields", Document.class, - Map.class); - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - - assertThat(query.getQueryObject()) - .isEqualTo(new BasicQuery("{ \"firstname\": \"first\", \"lastname\": \"last\"}").getQueryObject()); - assertThat(query.getFieldsObject()).isEqualTo(new BasicQuery(null, "{ \"lastname\": 1}").getFieldsObject()); - } - - @Test // DATAMONGO-420 - public void shouldSupportRespectExistingQuotingInFindByTitleBeginsWithExplicitQuoting() { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "fun"); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByTitleBeginsWithExplicitQuoting", String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - - assertThat(query.getQueryObject().toJson()) - .isEqualTo(new BasicQuery("{title: {$regex: '^fun', $options: 'i'}}").getQueryObject().toJson()); - } - - @Test // DATAMONGO-995, DATAMONGO-420 - public void shouldParseQueryWithParametersInExpression() { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 1, 2, 3, 4); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithParametersInExpression", int.class, - int.class, int.class, int.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - - assertThat(query.getQueryObject()) - .isEqualTo(new BasicQuery("{$where: 'return this.date.getUTCMonth() == 3 && this.date.getUTCDay() == 4;'}") - .getQueryObject()); - } - - @Test // DATAMONGO-995, DATAMONGO-420 - public void bindsSimplePropertyAlreadyQuotedCorrectly() { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews"); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-995, DATAMONGO-420 - public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*"); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-995, DATAMONGO-420 - public void bindsSimplePropertyWithRegexCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-1070 - public void parsesDbRefDeclarationsCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("methodWithManuallyDefinedDbRef", String.class); - ConvertingParameterAccessor parameterAccessor = StubParameterAccessor.getAccessor(converter, "myid"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor); - - Document dbRef = DocumentTestUtils.getTypedValue(query.getQueryObject(), "reference", Document.class); - assertThat(dbRef).isEqualTo(new Document("$ref", "reference").append("$id", "myid")); - } - - @Test // DATAMONGO-1072 - public void shouldParseJsonKeyReplacementCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("methodWithPlaceholderInKeyOfJsonStructure", String.class, - String.class); - ConvertingParameterAccessor parameterAccessor = StubParameterAccessor.getAccessor(converter, "key", "value"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor); - - assertThat(query.getQueryObject()).isEqualTo(new Document().append("key", "value")); - } - - @Test // DATAMONGO-990 - public void shouldSupportExpressionsInCustomQueries() { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews"); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpression", String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-1244 - public void shouldSupportExpressionsInCustomQueriesWithNestedObject() { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2"); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndNestedObject", boolean.class, - String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{ \"id\" : { \"$exists\" : true}}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-1244 - public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2"); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndMultipleNestedObjects", - boolean.class, String.class, String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( - "{ \"id\" : { \"$exists\" : true} , \"foo\" : 42 , \"bar\" : { \"$exists\" : false}}"); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-1290 - public void shouldSupportNonQuotedBinaryDataReplacement() { - - byte[] binaryData = "Matthews".getBytes(StandardCharsets.UTF_8); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, (Object) binaryData); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { '$binary' : '" - + Base64Utils.encodeToString(binaryData) + "', '$type' : '" + BsonBinarySubType.BINARY.getValue() + "'}}"); - - assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson()); - } - - @Test // DATAMONGO-2029 - public void shouldSupportNonQuotedBinaryCollectionDataReplacement() { - - byte[] binaryData = "Matthews".getBytes(StandardCharsets.UTF_8); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, - (Object) Collections.singletonList(binaryData)); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinaryIn", List.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { $in: [{'$binary' : '" - + Base64Utils.encodeToString(binaryData) + "', '$type' : '" + BsonBinarySubType.BINARY.getValue() + "'}] }}"); - - assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson()); - } - - @Test // DATAMONGO-1911 - public void shouldSupportNonQuotedUUIDReplacement() { - - UUID uuid = UUID.fromString("864de43b-e3ea-f1e4-3663-fb8240b659b9"); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, (Object) uuid); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsUUID", UUID.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( - "{'lastname' : { $binary:\"5PHq4zvkTYa5WbZAgvtjNg==\", $type: \"03\"}}"); - - // CodecRegistry registry = - // MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.JAVA_LEGACY).build().getCodecRegistry(); - - // TODO: use OverridableUuidRepresentationCodecRegistry instead to save resources - CodecRegistry registry = MongoClients - .create(MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.JAVA_LEGACY).build()) - .getDatabase("database").getCodecRegistry(); - - // OverridableUuidRepresentationCodecRegistry - - assertThat(query.getQueryObject().toJson(registry.get(Document.class))) - .isEqualTo(reference.getQueryObject().toJson()); - } - - @Test // DATAMONGO-2029 - public void shouldSupportNonQuotedUUIDCollectionReplacement() { - - UUID uuid1 = UUID.fromString("864de43b-e3ea-f1e4-3663-fb8240b659b9"); - UUID uuid2 = UUID.fromString("864de43b-cafe-f1e4-3663-fb8240b659b9"); - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, - (Object) Arrays.asList(uuid1, uuid2)); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsUUIDIn", List.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( - "{'lastname' : { $in: [{ $binary : \"5PHq4zvkTYa5WbZAgvtjNg==\", $type : \"03\" }, { $binary : \"5PH+yjvkTYa5WbZAgvtjNg==\", $type : \"03\" }]}}"); - - // TODO: use OverridableUuidRepresentationCodecRegistry instead to save resources - CodecRegistry registry = MongoClients - .create(MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.JAVA_LEGACY).build()) - .getDatabase("database").getCodecRegistry(); - assertThat(query.getQueryObject().toJson(registry.get(Document.class))) - .isEqualTo(reference.getQueryObject().toJson()); - } - - @Test // DATAMONGO-2427 - public void shouldSupportNonQuotedUUIDCollectionReplacementWhenUsingNonLegacyUUIDCodec() { - - // TODO: use OverridableUuidRepresentationCodecRegistry instead to save resources - CodecRegistry registry = MongoClients - .create(MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.STANDARD).build()) - .getDatabase("database").getCodecRegistry(); - when(operations.execute(any(DbCallback.class))).thenReturn(registry); - - UUID uuid1 = UUID.fromString("864de43b-e3ea-f1e4-3663-fb8240b659b9"); - UUID uuid2 = UUID.fromString("864de43b-cafe-f1e4-3663-fb8240b659b9"); - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, - (Object) Arrays.asList(uuid1, uuid2)); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsUUIDIn", List.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( - "{'lastname' : { $in: [{ $binary : \"hk3kO+Pq8eQ2Y/uCQLZZuQ==\", $type : \"04\" }, { $binary : \"hk3kO8r+8eQ2Y/uCQLZZuQ==\", $type : \"04\" }]}}"); - - assertThat(query.getQueryObject().toJson(registry.get(Document.class))) - .isEqualTo(reference.getQueryObject().toJson()); - } - - @Test // DATAMONGO-1911 - public void shouldSupportQuotedUUIDReplacement() { - - UUID uuid = UUID.randomUUID(); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, (Object) uuid); - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsStringUUID", UUID.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( - "{'lastname' : '" + uuid.toString() + "'}"); - - assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson()); - } - - @Test // DATAMONGO-1454 - public void shouldSupportExistsProjection() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("existsByLastname", String.class); - - assertThat(mongoQuery.isExistsQuery()).isTrue(); - } - - @Test // DATAMONGO-1565 - public void bindsPropertyReferenceMultipleTimesCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAgeQuotedAndUnquoted", Integer.TYPE); - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 3); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - List or = new ArrayList<>(); - or.add(new Document("age", 3)); - or.add(new Document("displayAge", "3")); - Document queryObject = new Document("$or", or); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject); - - assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject()); - } - - @Test // DATAMONGO-1565 - public void shouldIgnorePlaceholderPatternInReplacementValue() { - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "argWith?1andText", - "nothing-special"); - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByStringWithWildcardChar", String.class, String.class); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()) - .isEqualTo(Document.parse("{ \"arg0\" : \"argWith?1andText\" , \"arg1\" : \"nothing-special\"}")); - } - - @Test // DATAMONGO-1565 - public void shouldQuoteStringReplacementCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews', password: 'foo"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()) - .isNotEqualTo(new Document().append("lastname", "Matthews").append("password", "foo")); - assertThat(query.getQueryObject()).isEqualTo(new Document("lastname", "Matthews', password: 'foo")); - } - - @Test // DATAMONGO-1565 - public void shouldQuoteStringReplacementContainingQuotesCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews\", password: \"foo"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()) - .isNotEqualTo(new Document().append("lastname", "Matthews").append("password", "foo")); - assertThat(query.getQueryObject()).isEqualTo(new Document("lastname", "Matthews\", password: \"foo")); - } - - @Test // DATAMONGO-1565 - public void shouldQuoteStringReplacementWithQuotationsCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, - "\"Dave Matthews\", password: 'foo"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()).isEqualTo(new Document("lastname", "\"Dave Matthews\", password: 'foo")); - } - - @Test // DATAMONGO-1565, DATAMONGO-1575 - public void shouldQuoteComplexQueryStringCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "{ $ne : \"calamity\" }"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()).isEqualTo(new Document("lastname", "{ $ne : \"calamity\" }")); - } - - @Test // DATAMONGO-1565, DATAMONGO-1575 - public void shouldQuotationInQuotedComplexQueryString() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, - "{ $ne : \"\\\"calamity\\\"\" }"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - - assertThat(query.getQueryObject()).isEqualTo(new Document("lastname", "{ $ne : \"\\\"calamity\\\"\" }")); - } - - @Test // DATAMONGO-1575, DATAMONGO-1770 - public void shouldTakeBsonParameterAsIs() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByWithBsonArgument", Document.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, - new Document("$regex", "^calamity$")); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()).isEqualTo(new Document("arg0", new Document("$regex", "^calamity$"))); - } - - @Test // DATAMONGO-1575, DATAMONGO-1770 - public void shouldReplaceParametersInInQuotedExpressionOfNestedQueryOperator() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameRegex", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()).isEqualTo(new Document("lastname", new Document("$regex", "^(calamity)"))); - } - - @Test // DATAMONGO-1603 - public void shouldAllowReuseOfPlaceholderWithinQuery() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByReusingPlaceholdersMultipleTimes", String.class, - String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()) - .isEqualTo(new Document().append("arg0", "calamity").append("arg1", "regalia").append("arg2", "calamity")); - } - - @Test // DATAMONGO-1603 - public void shouldAllowReuseOfQuotedPlaceholderWithinQuery() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByReusingPlaceholdersMultipleTimesWhenQuoted", - String.class, String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()) - .isEqualTo(new Document().append("arg0", "calamity").append("arg1", "regalia").append("arg2", "calamity")); - } - - @Test // DATAMONGO-1603 - public void shouldAllowReuseOfQuotedPlaceholderWithinQueryAndIncludeSuffixCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod( - "findByReusingPlaceholdersMultipleTimesWhenQuotedAndSomeStuffAppended", String.class, String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()) - .isEqualTo(new Document().append("arg0", "calamity").append("arg1", "regalia").append("arg2", "calamitys")); - } - - @Test // DATAMONGO-1603 - public void shouldAllowQuotedParameterWithSuffixAppended() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByWhenQuotedAndSomeStuffAppended", String.class, - String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()).isEqualTo(new Document().append("arg0", "calamity").append("arg1", "regalias")); - } - - @Test // DATAMONGO-1603 - public void shouldCaptureReplacementWithComplexSuffixCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByMultiRegex", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - - assertThat(query.getQueryObject()).isEqualTo(Document.parse( - "{ \"$or\" : [ { \"firstname\" : { \"$regex\" : \".*calamity.*\" , \"$options\" : \"i\"}} , { \"lastname\" : { \"$regex\" : \".*calamityxyz.*\" , \"$options\" : \"i\"}}]}")); - } - - @Test // DATAMONGO-1603 - public void shouldAllowPlaceholderReuseInQuotedValue() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameRegex", String.class, String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia"); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - - assertThat(query.getQueryObject()) - .isEqualTo(Document.parse("{ 'lastname' : { '$regex' : '^(calamity|John regalia|regalia)'} }")); - } - - @Test // DATAMONGO-1605 - public void findUsingSpelShouldRetainParameterType() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByUsingSpel", Object.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 100.01D); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()).isEqualTo(new Document("arg0", 100.01D)); - } - - @Test // DATAMONGO-1605 - public void findUsingSpelShouldRetainNullValues() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByUsingSpel", Object.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] { null }); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()).isEqualTo(new Document("arg0", null)); - } - - @Test // DATAMONGO-2119 - public void spelShouldIgnoreJsonParseErrorsForRegex() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findByPersonLastnameRegex", Person.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, - new Person("Molly", "Chandler")); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - - assertThat(query.getQueryObject().toJson()) - .isEqualTo(new BasicQuery("{lastname: {$regex: 'Chandler'}}").getQueryObject().toJson()); - } - - @Test // DATAMONGO-2149 - public void shouldParseFieldsProjectionWithSliceCorrectly() { - - StringBasedMongoQuery mongoQuery = createQueryForMethod("findWithSliceInProjection", String.class, int.class, - int.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Bruce Banner", 0, 5); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - - assertThat(query.getFieldsObject()).isEqualTo(Document.parse("{ \"fans\" : { \"$slice\" : [0, 5] } }")); - } - - @Test // DATAMONGO-1593 - public void shouldRenderObjectIdParameterCorrectly() { - - ObjectId id = new ObjectId(); - - StringBasedMongoQuery mongoQuery = createQueryForMethod("singeObjectIdArgInQueryString", String.class); - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, id.toString()); - - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - assertThat(query.getQueryObject()).isEqualTo(new Document("arg0", id)); - } - - @Test // DATAMONGO-1593 - public void shouldRenderMultipleObjectIdParametersCorrectly() { - - ObjectId id = new ObjectId(); - ObjectId readUsersId = new ObjectId(); - - StringBasedMongoQuery mongoQuery = createQueryForMethod("multipleObjectIdArgsInQueryString", String.class, - String.class); - - ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, id.toString(), - readUsersId.toString()); - org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); - - assertThat(query.getQueryObject().get("arg0")).isEqualTo(id); - assertThat(query.getQueryObject().get("$or")).isInstanceOf(List.class); - assertThat(DocumentTestUtils.getAsDBList(query.getQueryObject(), "$or").get(0)) - .isEqualTo(new Document("arg1.value0", readUsersId)); - assertThat(DocumentTestUtils.getAsDBList(query.getQueryObject(), "$or").get(1)) - .isEqualTo(new Document("arg1.value1", readUsersId)); - } - - private StringBasedMongoQuery createQueryForMethod(String name, Class... parameters) { - - try { - - Method method = SampleRepository.class.getMethod(name, parameters); - ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); - MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class), - factory, converter.getMappingContext()); - return new StringBasedMongoQuery(queryMethod, operations, PARSER, QueryMethodEvaluationContextProvider.DEFAULT); - - } catch (Exception e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - private interface SampleRepository extends Repository { - - @Query("{ 'lastname' : ?0 }") - Person findByLastname(String lastname); - - @Query("{ 'lastname' : ?0 }") - Person findByLastnameAsBinary(byte[] lastname); - - @Query("{ 'lastname' : { $in: ?0} }") - Person findByLastnameAsBinaryIn(List lastname); - - @Query("{ 'lastname' : ?0 }") - Person findByLastnameAsUUID(UUID lastname); - - @Query("{ 'lastname' : { $in : ?0} }") - Person findByLastnameAsUUIDIn(List lastname); - - @Query("{ 'lastname' : '?0' }") - Person findByLastnameAsStringUUID(UUID lastname); - - @Query("{ 'lastname' : '?0' }") - Person findByLastnameQuoted(String lastname); - - @Query("{ 'lastname' : { '$regex' : '^(?0)'} }") - Person findByLastnameRegex(String lastname); - - @Query("{'$or' : [{'firstname': {'$regex': '.*?0.*', '$options': 'i'}}, {'lastname' : {'$regex': '.*?0xyz.*', '$options': 'i'}} ]}") - Person findByMultiRegex(String arg0); - - @Query("{ 'address' : ?0 }") - Person findByAddress(Address address); - - @Query("{ 'lastname' : ?0, 'address' : ?1 }") - Person findByLastnameAndAddress(String lastname, Address address); - - @Query("{ fans : { $not : { $size : 0 } } }") - Person findByHavingSizeFansNotZero(); - - @Query(value = "{ 'lastname' : ?0 }", delete = true) - void removeByLastname(String lastname); - - @Query(value = "{ 'lastname' : ?0 }", delete = true, count = true) - void invalidMethod(String lastname); - - @Query(value = "?0", fields = "?1") - Document findByParameterizedCriteriaAndFields(Document criteria, Map fields); - - @Query("{'title': { $regex : '^?0', $options : 'i'}}") - List findByTitleBeginsWithExplicitQuoting(String title); - - @Query("{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}") - List findByQueryWithParametersInExpression(int param1, int param2, int param3, int param4); - - @Query("{ 'reference' : { $ref : 'reference', $id : ?0 }}") - Object methodWithManuallyDefinedDbRef(String id); - - @Query("{ ?0 : ?1}") - Object methodWithPlaceholderInKeyOfJsonStructure(String keyReplacement, String valueReplacement); - - @Query("{'lastname': ?#{[0]} }") - List findByQueryWithExpression(String param0); - - @Query("{'id':?#{ [0] ? { $exists :true} : [1] }}") - List findByQueryWithExpressionAndNestedObject(boolean param0, String param1); - - @Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}") - List findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2); - - @Query(value = "{ $or : [{'age' : ?0 }, {'displayAge' : '?0'}] }") - boolean findByAgeQuotedAndUnquoted(int age); - - @Query(value = "{ 'lastname' : ?0 }", exists = true) - boolean existsByLastname(String lastname); - - @Query("{ 'arg0' : ?0, 'arg1' : ?1 }") - List findByStringWithWildcardChar(String arg0, String arg1); - - @Query("{ 'arg0' : ?0 }") - List findByWithBsonArgument(Document arg0); - - @Query("{ 'arg0' : ?0, 'arg1' : ?1, 'arg2' : ?0 }") - List findByReusingPlaceholdersMultipleTimes(String arg0, String arg1); - - @Query("{ 'arg0' : ?0, 'arg1' : ?1, 'arg2' : '?0' }") - List findByReusingPlaceholdersMultipleTimesWhenQuoted(String arg0, String arg1); - - @Query("{ 'arg0' : '?0', 'arg1' : ?1, 'arg2' : '?0s' }") - List findByReusingPlaceholdersMultipleTimesWhenQuotedAndSomeStuffAppended(String arg0, String arg1); - - @Query("{ 'arg0' : '?0', 'arg1' : '?1s' }") - List findByWhenQuotedAndSomeStuffAppended(String arg0, String arg1); - - @Query("{ 'lastname' : { '$regex' : '^(?0|John ?1|?1)'} }") - // use spel or some regex string this is bad - Person findByLastnameRegex(String lastname, String alternative); - - @Query("{ arg0 : ?#{[0]} }") - List findByUsingSpel(Object arg0); - - @Query("{ 'lastname' : { '$regex' : ?#{[0].lastname} } }") - Person findByPersonLastnameRegex(Person key); - - @Query(value = "{ 'id' : ?0 }", fields = "{ 'fans': { '$slice': [ ?1, ?2 ] } }") - Person findWithSliceInProjection(String id, int skip, int limit); - - @Query("{ 'arg0' : { \"$oid\" : ?0} }") - List singeObjectIdArgInQueryString(String arg0); - - @Query("{ 'arg0' : { \"$oid\" : ?0} , '$or' : [ { 'arg1.value0' : { \"$oid\" : ?1 } }, { 'arg1.value1' : { \"$oid\" : ?1 } } ] }") - List multipleObjectIdArgsInQueryString(String arg0, String arg1); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java deleted file mode 100644 index f43e3a1074..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.query; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.Optional; - -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Range; -import org.springframework.data.domain.Range.Bound; -import org.springframework.data.domain.Sort; -import org.springframework.data.geo.Distance; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.core.convert.MongoWriter; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.TextCriteria; -import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.lang.Nullable; - -/** - * Simple {@link ParameterAccessor} that returns the given parameters unfiltered. - * - * @author Oliver Gierke - * @author Christoh Strobl - * @author Thomas Darimont - */ -class StubParameterAccessor implements MongoParameterAccessor { - - private final Object[] values; - private Range range = Range.unbounded(); - private @Nullable Collation colllation; - - /** - * Creates a new {@link ConvertingParameterAccessor} backed by a {@link StubParameterAccessor} simply returning the - * given parameters converted but unfiltered. - * - * @param converter - * @param parameters - * @return - */ - public static ConvertingParameterAccessor getAccessor(MongoWriter converter, Object... parameters) { - return new ConvertingParameterAccessor(converter, new StubParameterAccessor(parameters)); - } - - @SuppressWarnings("unchecked") - public StubParameterAccessor(Object... values) { - - this.values = values; - - for (Object value : values) { - if (value instanceof Range) { - this.range = (Range) value; - } else if (value instanceof Distance) { - this.range = Range.from(Bound. unbounded()).to(Bound.inclusive((Distance) value)); - } else if (value instanceof Collation) { - this.colllation = Collation.class.cast(value); - } - } - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.ParameterAccessor#getPageable() - */ - public Pageable getPageable() { - return null; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.ParameterAccessor#getBindableValue(int) - */ - public Object getBindableValue(int index) { - return values[index]; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.ParameterAccessor#hasBindableNullValue() - */ - public boolean hasBindableNullValue() { - return false; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.ParameterAccessor#getSort() - */ - public Sort getSort() { - return Sort.unsorted(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getDistanceRange() - */ - @Override - public Range getDistanceRange() { - return range; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.ParameterAccessor#iterator() - */ - public Iterator iterator() { - return Arrays.asList(values).iterator(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation() - */ - public Point getGeoNearLocation() { - return null; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getFullText() - */ - @Override - public TextCriteria getFullText() { - return null; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getCollation() - */ - @Override - public Collation getCollation() { - return this.colllation; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getValues() - */ - @Override - public Object[] getValues() { - return this.values; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.ParameterAccessor#getDynamicProjection() - */ - @Override - public Optional> getDynamicProjection() { - return Optional.empty(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.query.ParameterAccessor#findDynamicProjection() - */ - @Override - public Class findDynamicProjection() { - return null; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListenerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListenerUnitTests.java deleted file mode 100644 index a8dcf08f81..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListenerUnitTests.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2017-2021 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.repository.support; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.anyString; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.data.domain.Sort; -import org.springframework.data.mongodb.core.index.IndexDefinition; -import org.springframework.data.mongodb.core.index.IndexOperations; -import org.springframework.data.mongodb.core.index.IndexOperationsProvider; -import org.springframework.data.mongodb.repository.query.MongoEntityMetadata; -import org.springframework.data.mongodb.repository.query.MongoQueryMethod; -import org.springframework.data.mongodb.repository.query.PartTreeMongoQuery; -import org.springframework.data.repository.query.parser.PartTree; -import org.springframework.data.util.Streamable; - -/** - * Unit tests for {@link IndexEnsuringQueryCreationListener}. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class IndexEnsuringQueryCreationListenerUnitTests { - - private IndexEnsuringQueryCreationListener listener; - - @Mock IndexOperationsProvider provider; - @Mock PartTree partTree; - @Mock PartTreeMongoQuery partTreeQuery; - @Mock MongoQueryMethod queryMethod; - @Mock IndexOperations indexOperations; - @Mock MongoEntityMetadata entityInformation; - - @BeforeEach - void setUp() { - - this.listener = new IndexEnsuringQueryCreationListener(provider); - - partTreeQuery = mock(PartTreeMongoQuery.class, Answers.RETURNS_MOCKS); - when(partTreeQuery.getTree()).thenReturn(partTree); - when(provider.indexOps(anyString(), any())).thenReturn(indexOperations); - when(queryMethod.getEntityInformation()).thenReturn(entityInformation); - when(entityInformation.getCollectionName()).thenReturn("persons"); - } - - @Test // DATAMONGO-1753 - void skipsQueryCreationForMethodWithoutPredicate() { - - when(partTree.hasPredicate()).thenReturn(false); - - listener.onCreation(partTreeQuery); - - verify(provider, times(0)).indexOps(any()); - } - - @Test // DATAMONGO-1854 - void usesCollationWhenPresentAndFixedValue() { - - when(partTree.hasPredicate()).thenReturn(true); - when(partTree.getParts()).thenReturn(Streamable.empty()); - when(partTree.getSort()).thenReturn(Sort.unsorted()); - when(partTreeQuery.getQueryMethod()).thenReturn(queryMethod); - when(queryMethod.hasAnnotatedCollation()).thenReturn(true); - when(queryMethod.getAnnotatedCollation()).thenReturn("en_US"); - - listener.onCreation(partTreeQuery); - - ArgumentCaptor indexArgumentCaptor = ArgumentCaptor.forClass(IndexDefinition.class); - verify(indexOperations).ensureIndex(indexArgumentCaptor.capture()); - - IndexDefinition indexDefinition = indexArgumentCaptor.getValue(); - assertThat(indexDefinition.getIndexOptions()).isEqualTo(new Document("collation", new Document("locale", "en_US"))); - } - - @Test // DATAMONGO-1854 - void usesCollationWhenPresentAndFixedDocumentValue() { - - when(partTree.hasPredicate()).thenReturn(true); - when(partTree.getParts()).thenReturn(Streamable.empty()); - when(partTree.getSort()).thenReturn(Sort.unsorted()); - when(partTreeQuery.getQueryMethod()).thenReturn(queryMethod); - when(queryMethod.hasAnnotatedCollation()).thenReturn(true); - when(queryMethod.getAnnotatedCollation()).thenReturn("{ 'locale' : 'en_US' }"); - - listener.onCreation(partTreeQuery); - - ArgumentCaptor indexArgumentCaptor = ArgumentCaptor.forClass(IndexDefinition.class); - verify(indexOperations).ensureIndex(indexArgumentCaptor.capture()); - - IndexDefinition indexDefinition = indexArgumentCaptor.getValue(); - assertThat(indexDefinition.getIndexOptions()).isEqualTo(new Document("collation", new Document("locale", "en_US"))); - } - - @Test // DATAMONGO-1854 - void skipsCollationWhenPresentButDynamic() { - - when(partTree.hasPredicate()).thenReturn(true); - when(partTree.getParts()).thenReturn(Streamable.empty()); - when(partTree.getSort()).thenReturn(Sort.unsorted()); - when(partTreeQuery.getQueryMethod()).thenReturn(queryMethod); - when(queryMethod.hasAnnotatedCollation()).thenReturn(true); - when(queryMethod.getAnnotatedCollation()).thenReturn("{ 'locale' : '?0' }"); - - listener.onCreation(partTreeQuery); - - ArgumentCaptor indexArgumentCaptor = ArgumentCaptor.forClass(IndexDefinition.class); - verify(indexOperations).ensureIndex(indexArgumentCaptor.capture()); - - IndexDefinition indexDefinition = indexArgumentCaptor.getValue(); - assertThat(indexDefinition.getIndexOptions()).isEmpty(); - } - - @Test // DATAMONGO-1854 - void skipsCollationWhenNotPresent() { - - when(partTree.hasPredicate()).thenReturn(true); - when(partTree.getParts()).thenReturn(Streamable.empty()); - when(partTree.getSort()).thenReturn(Sort.unsorted()); - when(partTreeQuery.getQueryMethod()).thenReturn(queryMethod); - when(queryMethod.hasAnnotatedCollation()).thenReturn(false); - - listener.onCreation(partTreeQuery); - - ArgumentCaptor indexArgumentCaptor = ArgumentCaptor.forClass(IndexDefinition.class); - verify(indexOperations).ensureIndex(indexArgumentCaptor.capture()); - - IndexDefinition indexDefinition = indexArgumentCaptor.getValue(); - assertThat(indexDefinition.getIndexOptions()).isEmpty(); - } - - interface SampleRepository { - - Object findAllBy(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBeanUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBeanUnitTests.java deleted file mode 100644 index 18ab60ba89..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBeanUnitTests.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.support; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.repository.ContactRepository; -import org.springframework.data.repository.core.support.RepositoryFactorySupport; -import org.springframework.test.util.ReflectionTestUtils; - -/** - * Unit tests for {@link MongoRepositoryFactoryBean}. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -public class MongoRepositoryFactoryBeanUnitTests { - - @Mock MongoOperations operations; - @Mock MongoConverter converter; - @Mock @SuppressWarnings("rawtypes") MappingContext context; - - @Test - @SuppressWarnings("rawtypes") - public void addsIndexEnsuringQueryCreationListenerIfConfigured() { - - MongoRepositoryFactoryBean factory = new MongoRepositoryFactoryBean(ContactRepository.class); - factory.setCreateIndexesForQueryMethods(true); - - List listeners = getListenersFromFactory(factory); - assertThat(listeners.isEmpty()).isFalse(); - assertThat(listeners.stream().filter(IndexEnsuringQueryCreationListener.class::isInstance)).isNotEmpty(); - } - - @Test - @SuppressWarnings("rawtypes") - public void doesNotAddIndexEnsuringQueryCreationListenerByDefault() { - - List listeners = getListenersFromFactory(new MongoRepositoryFactoryBean(ContactRepository.class)); - assertThat(listeners.size()).isEqualTo(1); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private List getListenersFromFactory(MongoRepositoryFactoryBean factoryBean) { - - when(operations.getConverter()).thenReturn(converter); - when(converter.getMappingContext()).thenReturn(context); - - factoryBean.setLazyInit(true); - factoryBean.setMongoOperations(operations); - factoryBean.afterPropertiesSet(); - - RepositoryFactorySupport factory = factoryBean.createRepositoryFactory(); - return (List) ReflectionTestUtils.getField(factory, "queryPostProcessors"); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryUnitTests.java deleted file mode 100644 index 625f0c1995..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryUnitTests.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.support; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.io.Serializable; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.query.MongoEntityInformation; -import org.springframework.data.repository.Repository; - -/** - * Unit test for {@link MongoRepositoryFactory}. - * - * @author Oliver Gierke - */ -@ExtendWith(MockitoExtension.class) -public class MongoRepositoryFactoryUnitTests { - - @Mock MongoTemplate template; - - @Mock MongoConverter converter; - - @Mock @SuppressWarnings("rawtypes") MappingContext mappingContext; - - @Mock @SuppressWarnings("rawtypes") MongoPersistentEntity entity; - - @BeforeEach - @SuppressWarnings("unchecked") - public void setUp() { - when(template.getConverter()).thenReturn(converter); - when(converter.getMappingContext()).thenReturn(mappingContext); - } - - @Test - @SuppressWarnings("unchecked") - public void usesMappingMongoEntityInformationIfMappingContextSet() { - - when(mappingContext.getRequiredPersistentEntity(Person.class)).thenReturn(entity); - - MongoRepositoryFactory factory = new MongoRepositoryFactory(template); - MongoEntityInformation entityInformation = factory.getEntityInformation(Person.class); - assertThat(entityInformation instanceof MappingMongoEntityInformation).isTrue(); - } - - @Test // DATAMONGO-385 - @SuppressWarnings("unchecked") - public void createsRepositoryWithIdTypeLong() { - - when(mappingContext.getRequiredPersistentEntity(Person.class)).thenReturn(entity); - - MongoRepositoryFactory factory = new MongoRepositoryFactory(template); - MyPersonRepository repository = factory.getRepository(MyPersonRepository.class); - assertThat(repository).isNotNull(); - } - - interface MyPersonRepository extends Repository { - - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java deleted file mode 100644 index 6b46618fdb..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutorIntegrationTests.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2015-2021 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.repository.support; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.dao.PermissionDeniedDataAccessException; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.repository.Address; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.QAddress; -import org.springframework.data.mongodb.repository.QPerson; -import org.springframework.data.mongodb.repository.QUser; -import org.springframework.data.mongodb.repository.User; -import org.springframework.data.mongodb.repository.query.MongoEntityInformation; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.MongoException; -import com.mongodb.client.MongoDatabase; - -/** - * Integration test for {@link QuerydslMongoPredicateExecutor}. - * - * @author Thomas Darimont - * @author Mark Paluch - * @author Christoph Strobl - */ -@ContextConfiguration( - locations = "/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml") -@RunWith(SpringRunner.class) -public class QuerydslMongoPredicateExecutorIntegrationTests { - - @Autowired MongoOperations operations; - @Autowired MongoDatabaseFactory dbFactory; - - QuerydslMongoPredicateExecutor repository; - - Person dave, oliver, carter; - QPerson person; - - @Before - public void setup() { - - MongoRepositoryFactory factory = new MongoRepositoryFactory(operations); - MongoEntityInformation entityInformation = factory.getEntityInformation(Person.class); - repository = new QuerydslMongoPredicateExecutor<>(entityInformation, operations); - - operations.dropCollection(Person.class); - - dave = new Person("Dave", "Matthews", 42); - oliver = new Person("Oliver August", "Matthews", 4); - carter = new Person("Carter", "Beauford", 49); - - person = new QPerson("person"); - - operations.insertAll(Arrays.asList(oliver, dave, carter)); - } - - @Test // DATAMONGO-1146 - public void shouldSupportExistsWithPredicate() throws Exception { - - assertThat(repository.exists(person.firstname.eq("Dave"))).isTrue(); - assertThat(repository.exists(person.firstname.eq("Unknown"))).isFalse(); - } - - @Test // DATAMONGO-1167 - public void shouldSupportFindAllWithPredicateAndSort() { - - List users = repository.findAll(person.lastname.isNotNull(), Sort.by(Direction.ASC, "firstname")); - - assertThat(users).containsExactly(carter, dave, oliver); - } - - @Test // DATAMONGO-1690 - public void findOneWithPredicateReturnsResultCorrectly() { - assertThat(repository.findOne(person.firstname.eq(dave.getFirstname()))).contains(dave); - } - - @Test // DATAMONGO-1690 - public void findOneWithPredicateReturnsOptionalEmptyWhenNoDataFound() { - assertThat(repository.findOne(person.firstname.eq("batman"))).isNotPresent(); - } - - @Test // DATAMONGO-1690 - public void findOneWithPredicateThrowsExceptionForNonUniqueResults() { - assertThatExceptionOfType(IncorrectResultSizeDataAccessException.class) - .isThrownBy(() -> repository.findOne(person.firstname.contains("e"))); - } - - @Test // DATAMONGO-1848 - public void findUsingAndShouldWork() { - - assertThat(repository.findAll( - person.lastname.startsWith(oliver.getLastname()).and(person.firstname.startsWith(dave.getFirstname())))) - .containsExactly(dave); - } - - @Test // DATAMONGO-362, DATAMONGO-1848 - public void springDataMongodbQueryShouldAllowJoinOnDBref() { - - User user1 = new User(); - user1.setUsername("user-1"); - - User user2 = new User(); - user2.setUsername("user-2"); - - User user3 = new User(); - user3.setUsername("user-3"); - - operations.save(user1); - operations.save(user2); - operations.save(user3); - - Person person1 = new Person("Max", "The Mighty"); - person1.setCoworker(user1); - - Person person2 = new Person("Jack", "The Ripper"); - person2.setCoworker(user2); - - Person person3 = new Person("Bob", "The Builder"); - person3.setCoworker(user3); - - operations.save(person1); - operations.save(person2); - operations.save(person3); - - List result = new SpringDataMongodbQuery<>(operations, Person.class).where() - .join(person.coworker, QUser.user).on(QUser.user.username.eq("user-2")).fetch(); - - assertThat(result).containsExactly(person2); - } - - @Test // DATAMONGO-362, DATAMONGO-1848 - public void springDataMongodbQueryShouldReturnEmptyOnJoinWithNoResults() { - - User user1 = new User(); - user1.setUsername("user-1"); - - User user2 = new User(); - user2.setUsername("user-2"); - - operations.save(user1); - operations.save(user2); - - Person person1 = new Person("Max", "The Mighty"); - person1.setCoworker(user1); - - Person person2 = new Person("Jack", "The Ripper"); - person2.setCoworker(user2); - - operations.save(person1); - operations.save(person2); - - List result = new SpringDataMongodbQuery<>(operations, Person.class).where() - .join(person.coworker, QUser.user).on(QUser.user.username.eq("does-not-exist")).fetch(); - - assertThat(result).isEmpty(); - } - - @Test // DATAMONGO-595, DATAMONGO-1848 - public void springDataMongodbQueryShouldAllowElemMatchOnArrays() { - - Address adr1 = new Address("Hauptplatz", "4020", "Linz"); - Address adr2 = new Address("Stephansplatz", "1010", "Wien"); - Address adr3 = new Address("Tower of London", "EC3N 4AB", "London"); - - Person person1 = new Person("Max", "The Mighty"); - person1.setShippingAddresses(new LinkedHashSet<>(Arrays.asList(adr1, adr2))); - - Person person2 = new Person("Jack", "The Ripper"); - person2.setShippingAddresses(new LinkedHashSet<>(Arrays.asList(adr2, adr3))); - - operations.save(person1); - operations.save(person2); - - List result = new SpringDataMongodbQuery<>(operations, Person.class).where() - .anyEmbedded(person.shippingAddresses, QAddress.address).on(QAddress.address.city.eq("London")).fetch(); - - assertThat(result).containsExactly(person2); - } - - @Test(expected = PermissionDeniedDataAccessException.class) - // DATAMONGO-1434, DATAMONGO-1848 - public void translatesExceptionsCorrectly() { - - MongoOperations ops = new MongoTemplate(dbFactory) { - - @Override - protected MongoDatabase doGetDatabase() { - throw new MongoException(18, "Authentication Failed"); - } - }; - - MongoRepositoryFactory factory = new MongoRepositoryFactory(ops); - MongoEntityInformation entityInformation = factory.getEntityInformation(Person.class); - repository = new QuerydslMongoPredicateExecutor<>(entityInformation, ops); - - repository.findOne(person.firstname.contains("batman")); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupportTests.java deleted file mode 100644 index fc2f5332cd..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/QuerydslRepositorySupportTests.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.support; - -import static org.assertj.core.api.Assertions.*; - -import lombok.Data; - -import java.util.Arrays; - -import org.bson.types.ObjectId; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.DirectFieldAccessor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.FieldType; -import org.springframework.data.mongodb.core.mapping.MongoId; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.QPerson; -import org.springframework.data.mongodb.repository.User; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.StringUtils; - -/** - * Unit tests for {@link QuerydslRepositorySupport}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - */ -@RunWith(SpringRunner.class) -@ContextConfiguration("classpath:infrastructure.xml") -public class QuerydslRepositorySupportTests { - - @Autowired MongoOperations operations; - Person person; - QuerydslRepositorySupport repoSupport; - - @Before - public void setUp() { - - operations.remove(new Query(), Outer.class); - operations.remove(new Query(), Person.class); - - person = new Person("Dave", "Matthews"); - operations.save(person); - - repoSupport = new QuerydslRepositorySupport(operations) {}; - } - - @Test - public void providesMongoQuery() { - - QPerson p = QPerson.person; - QuerydslRepositorySupport support = new QuerydslRepositorySupport(operations) {}; - SpringDataMongodbQuery query = support.from(p).where(p.lastname.eq("Matthews")); - assertThat(query.fetchOne()).isEqualTo(person); - } - - @Test // DATAMONGO-1063 - public void shouldAllowAny() { - - person.setSkills(Arrays.asList("vocalist", "songwriter", "guitarist")); - - operations.save(person); - - QPerson p = QPerson.person; - - SpringDataMongodbQuery query = repoSupport.from(p).where(p.skills.any().in("guitarist")); - - assertThat(query.fetchOne()).isEqualTo(person); - } - - @Test // DATAMONGO-1394 - public void shouldAllowDbRefAgainstIdProperty() { - - User bart = new User(); - bart.setUsername("bart@simpson.com"); - operations.save(bart); - - person.setCoworker(bart); - operations.save(person); - - QPerson p = QPerson.person; - - SpringDataMongodbQuery queryUsingIdField = repoSupport.from(p).where(p.coworker.id.eq(bart.getId())); - SpringDataMongodbQuery queryUsingRefObject = repoSupport.from(p).where(p.coworker.eq(bart)); - - assertThat(queryUsingIdField.fetchOne()).isEqualTo(person); - assertThat(queryUsingIdField.fetchOne()).isEqualTo(queryUsingRefObject.fetchOne()); - } - - @Test // DATAMONGO-1998 - public void shouldLeaveStringIdThatIsNoValidObjectIdAsItIs() { - - Outer outer = new Outer(); - outer.id = "outer-1"; - outer.inner = new Inner(); - outer.inner.id = "inner-1"; - outer.inner.value = "go climb a rock"; - - operations.save(outer); - - QQuerydslRepositorySupportTests_Outer o = QQuerydslRepositorySupportTests_Outer.outer; - SpringDataMongodbQuery query = repoSupport.from(o).where(o.inner.id.eq(outer.inner.id)); - - assertThat(query.fetchOne()).isEqualTo(outer); - } - - @Test // DATAMONGO-1998 - public void shouldConvertStringIdThatIsAValidObjectIdIntoTheSuch() { - - Outer outer = new Outer(); - outer.id = new ObjectId().toHexString(); - outer.inner = new Inner(); - outer.inner.id = new ObjectId().toHexString(); - outer.inner.value = "eat sleep workout repeat"; - - operations.save(outer); - - QQuerydslRepositorySupportTests_Outer o = QQuerydslRepositorySupportTests_Outer.outer; - SpringDataMongodbQuery query = repoSupport.from(o).where(o.inner.id.eq(outer.inner.id)); - - assertThat(query.fetchOne()).isEqualTo(outer); - } - - @Test // DATAMONGO-1810, DATAMONGO-1848 - public void shouldFetchObjectsViaStringWhenUsingInOnDbRef() { - - User bart = new User(); - DirectFieldAccessor dfa = new DirectFieldAccessor(bart); - dfa.setPropertyValue("id", "bart"); - - bart.setUsername("bart@simpson.com"); - operations.save(bart); - - User lisa = new User(); - dfa = new DirectFieldAccessor(lisa); - dfa.setPropertyValue("id", "lisa"); - - lisa.setUsername("lisa@simposon.com"); - operations.save(lisa); - - person.setCoworker(bart); - operations.save(person); - - QPerson p = QPerson.person; - - SpringDataMongodbQuery queryUsingIdFieldWithinInClause = repoSupport.from(p) - .where(p.coworker.id.in(Arrays.asList(bart.getId(), lisa.getId()))); - - SpringDataMongodbQuery queryUsingRefObject = repoSupport.from(p).where(p.coworker.eq(bart)); - - assertThat(queryUsingIdFieldWithinInClause.fetchOne()).isEqualTo(person); - assertThat(queryUsingIdFieldWithinInClause.fetchOne()).isEqualTo(queryUsingRefObject.fetchOne()); - } - - @Test // DATAMONGO-1810, DATAMONGO-1848 - public void shouldFetchObjectsViaStringStoredAsObjectIdWhenUsingInOnDbRef() { - - User bart = new User(); - bart.setUsername("bart@simpson.com"); - operations.save(bart); - - User lisa = new User(); - lisa.setUsername("lisa@simposon.com"); - operations.save(lisa); - - person.setCoworker(bart); - operations.save(person); - - QPerson p = QPerson.person; - - SpringDataMongodbQuery queryUsingIdFieldWithinInClause = repoSupport.from(p) - .where(p.coworker.id.in(Arrays.asList(bart.getId(), lisa.getId()))); - - SpringDataMongodbQuery queryUsingRefObject = repoSupport.from(p).where(p.coworker.eq(bart)); - - assertThat(queryUsingIdFieldWithinInClause.fetchOne()).isEqualTo(person); - assertThat(queryUsingIdFieldWithinInClause.fetchOne()).isEqualTo(queryUsingRefObject.fetchOne()); - } - - @Test // DATAMONGO-1848, DATAMONGO-2010 - public void shouldConvertStringIdThatIsAValidObjectIdWhenUsedInInPredicateIntoTheSuch() { - - Outer outer = new Outer(); - outer.id = new ObjectId().toHexString(); - outer.inner = new Inner(); - outer.inner.id = new ObjectId().toHexString(); - outer.inner.value = "eat sleep workout repeat"; - - operations.save(outer); - - QQuerydslRepositorySupportTests_Outer o = QQuerydslRepositorySupportTests_Outer.outer; - SpringDataMongodbQuery query = repoSupport.from(o).where(o.inner.id.in(outer.inner.id, outer.inner.id)); - - assertThat(query.fetchOne()).isEqualTo(outer); - } - - @Test // DATAMONGO-1798 - public void shouldRetainIdPropertyTypeIfInvalidObjectId() { - - Outer outer = new Outer(); - outer.id = "foobar"; - - operations.save(outer); - - QQuerydslRepositorySupportTests_Outer o = QQuerydslRepositorySupportTests_Outer.outer; - SpringDataMongodbQuery query = repoSupport.from(o).where(o.id.eq(outer.id)); - - assertThat(query.fetchOne()).isEqualTo(outer); - } - - @Test // DATAMONGO-1798 - public void shouldUseStringForValidObjectIdHexStrings() { - - WithMongoId document = new WithMongoId(); - document.id = new ObjectId().toHexString(); - - operations.save(document); - - QQuerydslRepositorySupportTests_WithMongoId o = QQuerydslRepositorySupportTests_WithMongoId.withMongoId; - SpringDataMongodbQuery eqQuery = repoSupport.from(o).where(o.id.eq(document.id)); - - assertThat(eqQuery.fetchOne()).isEqualTo(document); - - SpringDataMongodbQuery inQuery = repoSupport.from(o).where(o.id.in(document.id)); - - assertThat(inQuery.fetchOne()).isEqualTo(document); - } - - @Test // DATAMONGO-2327 - public void toJsonShouldRenderQuery() { - - QPerson p = QPerson.person; - SpringDataMongodbQuery query = repoSupport.from(p).where(p.lastname.eq("Matthews")) - .orderBy(p.firstname.asc()).offset(1).limit(5); - - assertThat(StringUtils.trimAllWhitespace(query.toJson())).isEqualTo("{\"lastname\":\"Matthews\"}"); - } - - @Test // DATAMONGO-2327 - public void toStringShouldRenderQuery() { - - QPerson p = QPerson.person; - User user = new User(); - user.setId("id"); - SpringDataMongodbQuery query = repoSupport.from(p) - .where(p.lastname.eq("Matthews").and(p.coworker.eq(user))); - - assertThat(StringUtils.trimAllWhitespace(query.toString())) - .isEqualTo("find({\"lastname\":\"Matthews\",\"coworker\":{\"$ref\":\"user\",\"$id\":\"id\"}})"); - - query = query.orderBy(p.firstname.asc()); - assertThat(StringUtils.trimAllWhitespace(query.toString())).isEqualTo( - "find({\"lastname\":\"Matthews\",\"coworker\":{\"$ref\":\"user\",\"$id\":\"id\"}}).sort({\"firstname\":1})"); - - query = query.offset(1).limit(5); - assertThat(StringUtils.trimAllWhitespace(query.toString())).isEqualTo( - "find({\"lastname\":\"Matthews\",\"coworker\":{\"$ref\":\"user\",\"$id\":\"id\"}}).sort({\"firstname\":1}).skip(1).limit(5)"); - } - - @Data - @Document - public static class Outer { - - @Id String id; - Inner inner; - } - - @Data - public static class Inner { - - @Id String id; - String value; - } - - @Data - @Document - public static class WithMongoId { - - @MongoId(FieldType.STRING) String id; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java deleted file mode 100644 index e16a4d7b8c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/ReactiveQuerydslMongoPredicateExecutorTests.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright 2019-2021 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.repository.support; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.dao.PermissionDeniedDataAccessException; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.repository.Address; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.QAddress; -import org.springframework.data.mongodb.repository.QPerson; -import org.springframework.data.mongodb.repository.QUser; -import org.springframework.data.mongodb.repository.User; -import org.springframework.data.mongodb.repository.query.MongoEntityInformation; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.MongoException; -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * Tests for {@link ReactiveQuerydslMongoPredicateExecutor}. - * - * @author Mark Paluch - * @author Christoph Strobl - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class ReactiveQuerydslMongoPredicateExecutorTests { - - @Autowired ReactiveMongoOperations operations; - @Autowired ReactiveMongoDatabaseFactory dbFactory; - - ReactiveQuerydslMongoPredicateExecutor repository; - - Person dave, oliver, carter; - QPerson person; - - @Configuration - static class Config extends AbstractReactiveMongoConfiguration { - - @Override - public MongoClient reactiveMongoClient() { - return MongoTestUtils.reactiveClient(); - } - - @Override - protected String getDatabaseName() { - return "reactive"; - } - - @Override - protected Set> getInitialEntitySet() { - return Collections.singleton(Person.class); - } - } - - @BeforeClass - public static void cleanDb() { - - MongoClient client = MongoTestUtils.reactiveClient(); - - MongoTestUtils.createOrReplaceCollectionNow("reactive", "person", client); - MongoTestUtils.createOrReplaceCollectionNow("reactive", "user", client); - } - - @Before - public void setup() { - - ReactiveMongoRepositoryFactory factory = new ReactiveMongoRepositoryFactory(operations); - MongoEntityInformation entityInformation = factory.getEntityInformation(Person.class); - repository = new ReactiveQuerydslMongoPredicateExecutor<>(entityInformation, operations); - - dave = new Person("Dave", "Matthews", 42); - oliver = new Person("Oliver August", "Matthews", 4); - carter = new Person("Carter", "Beauford", 49); - - person = new QPerson("person"); - - Flux.merge(operations.insert(oliver), operations.insert(dave), operations.insert(carter)).then() // - .as(StepVerifier::create).verifyComplete(); - } - - @After - public void tearDown() { - operations.remove(new BasicQuery("{}"), "person").then().as(StepVerifier::create).verifyComplete(); - operations.remove(new BasicQuery("{}"), "uer").then().as(StepVerifier::create).verifyComplete(); - } - - @Test // DATAMONGO-2182 - public void shouldSupportExistsWithPredicate() { - - repository.exists(person.firstname.eq("Dave")) // - .as(StepVerifier::create) // - .expectNext(true) // - .verifyComplete(); - - repository.exists(person.firstname.eq("Unknown")) // - .as(StepVerifier::create) // - .expectNext(false) // - .verifyComplete(); - } - - @Test // DATAMONGO-2182 - public void shouldSupportCountWithPredicate() { - - repository.count(person.firstname.eq("Dave")) // - .as(StepVerifier::create) // - .expectNext(1L) // - .verifyComplete(); - - repository.count(person.firstname.eq("Unknown")) // - .as(StepVerifier::create) // - .expectNext(0L) // - .verifyComplete(); - } - - @Test // DATAMONGO-2182 - public void shouldSupportFindAllWithPredicateAndSort() { - - repository.findAll(person.lastname.isNotNull(), Sort.by(Direction.ASC, "firstname")) // - .as(StepVerifier::create) // - .expectNext(carter, dave, oliver) // - .verifyComplete(); - } - - @Test // DATAMONGO-2182 - public void findOneWithPredicateReturnsResultCorrectly() { - - repository.findOne(person.firstname.eq(dave.getFirstname())) // - .as(StepVerifier::create) // - .expectNext(dave) // - .verifyComplete(); - } - - @Test // DATAMONGO-2182 - public void findOneWithPredicateReturnsEmptyWhenNoDataFound() { - - repository.findOne(person.firstname.eq("batman")) // - .as(StepVerifier::create) // - .verifyComplete(); - } - - @Test // DATAMONGO-2182 - public void findOneWithPredicateThrowsExceptionForNonUniqueResults() { - - repository.findOne(person.firstname.contains("e")) // - .as(StepVerifier::create) // - .expectError(IncorrectResultSizeDataAccessException.class) // - .verify(); - } - - @Test // DATAMONGO-2182 - public void findUsingAndShouldWork() { - - repository - .findAll(person.lastname.startsWith(oliver.getLastname()).and(person.firstname.startsWith(dave.getFirstname()))) // - .as(StepVerifier::create) // - .expectNext(dave) // - .verifyComplete(); - } - - @Test // DATAMONGO-2182 - public void queryShouldTerminateWithUnsupportedOperationWithJoinOnDBref() { - - User user1 = new User(); - user1.setUsername("user-1"); - - User user2 = new User(); - user2.setUsername("user-2"); - - User user3 = new User(); - user3.setUsername("user-3"); - - Flux.merge(operations.save(user1), operations.save(user2), operations.save(user3)) // - .then() // - .as(StepVerifier::create) // - .verifyComplete(); // - - Person person1 = new Person("Max", "The Mighty"); - person1.setCoworker(user1); - - Person person2 = new Person("Jack", "The Ripper"); - person2.setCoworker(user2); - - Person person3 = new Person("Bob", "The Builder"); - person3.setCoworker(user3); - - operations.save(person1) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - operations.save(person2)// - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - operations.save(person3) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - - Flux result = new ReactiveSpringDataMongodbQuery<>(operations, Person.class).where() - .join(person.coworker, QUser.user).on(QUser.user.username.eq("user-2")).fetch(); - - result.as(StepVerifier::create) // - .expectError(UnsupportedOperationException.class) // - .verify(); - } - - @Test // DATAMONGO-2182 - public void queryShouldTerminateWithUnsupportedOperationOnJoinWithNoResults() { - - User user1 = new User(); - user1.setUsername("user-1"); - - User user2 = new User(); - user2.setUsername("user-2"); - - operations.insertAll(Arrays.asList(user1, user2)) // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - - Person person1 = new Person("Max", "The Mighty"); - person1.setCoworker(user1); - - Person person2 = new Person("Jack", "The Ripper"); - person2.setCoworker(user2); - - operations.save(person1) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - ; - operations.save(person2) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - ; - - Flux result = new ReactiveSpringDataMongodbQuery<>(operations, Person.class).where() - .join(person.coworker, QUser.user).on(QUser.user.username.eq("does-not-exist")).fetch(); - - result.as(StepVerifier::create) // - .expectError(UnsupportedOperationException.class) // - .verify(); - } - - @Test // DATAMONGO-2182 - public void springDataMongodbQueryShouldAllowElemMatchOnArrays() { - - Address adr1 = new Address("Hauptplatz", "4020", "Linz"); - Address adr2 = new Address("Stephansplatz", "1010", "Wien"); - Address adr3 = new Address("Tower of London", "EC3N 4AB", "London"); - - Person person1 = new Person("Max", "The Mighty"); - person1.setShippingAddresses(new LinkedHashSet<>(Arrays.asList(adr1, adr2))); - - Person person2 = new Person("Jack", "The Ripper"); - person2.setShippingAddresses(new LinkedHashSet<>(Arrays.asList(adr2, adr3))); - - operations.insertAll(Arrays.asList(person1, person2)) // - .as(StepVerifier::create) // - .expectNextCount(2) // - .verifyComplete(); - - Flux result = new ReactiveSpringDataMongodbQuery<>(operations, Person.class).where() - .anyEmbedded(person.shippingAddresses, QAddress.address).on(QAddress.address.city.eq("London")).fetch(); - - result.as(StepVerifier::create) // - .expectNext(person2) // - .verifyComplete(); - } - - @Test // DATAMONGO-2182, DATAMONGO-2265 - public void translatesExceptionsCorrectly() { - - ReactiveMongoOperations ops = new ReactiveMongoTemplate(dbFactory) { - - @Override - protected Mono doGetDatabase() { - return Mono.error(new MongoException(18, "Authentication Failed")); - } - }; - - ReactiveMongoRepositoryFactory factory = new ReactiveMongoRepositoryFactory(ops); - MongoEntityInformation entityInformation = factory.getEntityInformation(Person.class); - repository = new ReactiveQuerydslMongoPredicateExecutor<>(entityInformation, ops); - - repository.findOne(person.firstname.contains("batman")) // - .as(StepVerifier::create) // - .expectError(PermissionDeniedDataAccessException.class) // - .verify(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java deleted file mode 100755 index 135b6b3888..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryTests.java +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Copyright 2010-2021 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.repository.support; - -import static java.util.Arrays.*; -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.domain.ExampleMatcher.*; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.ExampleMatcher.*; -import org.springframework.data.geo.Point; -import org.springframework.data.mongodb.MongoTransactionManager; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.repository.Address; -import org.springframework.data.mongodb.repository.Person; -import org.springframework.data.mongodb.repository.Person.Sex; -import org.springframework.data.mongodb.repository.User; -import org.springframework.data.mongodb.repository.query.MongoEntityInformation; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; -import org.springframework.data.mongodb.test.util.MongoServerCondition; -import org.springframework.data.mongodb.test.util.MongoTemplateExtension; -import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.data.mongodb.test.util.Template; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.transaction.support.TransactionTemplate; - -/** - * @author A. B. M. Kowser - * @author Thomas Darimont - * @author Christoph Strobl - * @author Mark Paluch - * @author Jens Schauder - */ -@ExtendWith({ MongoTemplateExtension.class, MongoServerCondition.class }) -class SimpleMongoRepositoryTests { - - @Template(initialEntitySet = Person.class) // - private static MongoTestTemplate template; - - private Person oliver, dave, carter, boyd, stefan, leroi, alicia; - private List all; - - private MongoEntityInformation personEntityInformation = new CustomizedPersonInformation(); - private SimpleMongoRepository repository = new SimpleMongoRepository<>(personEntityInformation, - template); - - @BeforeEach - void setUp() { - - repository.deleteAll(); - - oliver = new Person("Oliver August", "Matthews", 4); - dave = new Person("Dave", "Matthews", 42); - carter = new Person("Carter", "Beauford", 49); - boyd = new Person("Boyd", "Tinsley", 45); - stefan = new Person("Stefan", "Lessard", 34); - leroi = new Person("Leroi", "Moore", 41); - alicia = new Person("Alicia", "Keys", 30, Sex.FEMALE); - - all = repository.saveAll(asList(oliver, dave, carter, boyd, stefan, leroi, alicia)); - } - - @Test - void findAllFromCustomCollectionName() { - assertThat(repository.findAll()).hasSameSizeAs(all); - } - - @Test - void findOneFromCustomCollectionName() { - assertThat(repository.findById(dave.getId())).contains(dave); - } - - @Test - void deleteFromCustomCollectionName() { - - repository.delete(dave); - - assertThat(repository.findAll()).hasSize(all.size() - 1).doesNotContain(dave); - } - - @Test - void deleteByIdFromCustomCollectionName() { - - repository.deleteById(dave.getId()); - - assertThat(repository.findAll()).hasSize(all.size() - 1).doesNotContain(dave); - } - - @Test // DATAMONGO-1054 - void shouldInsertSingle() { - - String randomId = UUID.randomUUID().toString(); - - Person person1 = new Person("First1" + randomId, "Last2" + randomId, 42); - person1 = repository.insert(person1); - - assertThat(repository.findById(person1.getId())).contains(person1); - } - - @Test // DATAMONGO-1054 - void shouldInsertMultipleFromList() { - - String randomId = UUID.randomUUID().toString(); - Map idToPerson = new HashMap(); - List persons = new ArrayList(); - - for (int i = 0; i < 10; i++) { - Person person = new Person("First" + i + randomId, "Last" + randomId + i, 42 + i); - idToPerson.put(person.getId(), person); - persons.add(person); - } - - List saved = repository.insert(persons); - - assertThat(saved).hasSameSizeAs(persons); - assertThatAllReferencePersonsWereStoredCorrectly(idToPerson, saved); - } - - @Test // DATAMONGO-1054 - void shouldInsertMutlipleFromSet() { - - String randomId = UUID.randomUUID().toString(); - Map idToPerson = new HashMap(); - Set persons = new HashSet(); - - for (int i = 0; i < 10; i++) { - Person person = new Person("First" + i + randomId, "Last" + i + randomId, 42 + i); - idToPerson.put(person.getId(), person); - persons.add(person); - } - - List saved = repository.insert(persons); - - assertThat(saved).hasSameSizeAs(persons); - assertThatAllReferencePersonsWereStoredCorrectly(idToPerson, saved); - } - - @Test // DATAMONGO-1245, DATAMONGO-1464 - void findByExampleShouldLookUpEntriesCorrectly() { - - Person sample = new Person(); - sample.setLastname("Matthews"); - trimDomainType(sample, "id", "createdAt", "email"); - - Page result = repository.findAll(Example.of(sample), PageRequest.of(0, 10)); - - assertThat(result.getContent()).hasSize(2).contains(dave, oliver); - assertThat(result.getTotalPages()).isEqualTo(1); - } - - @Test // DATAMONGO-1464 - void findByExampleMultiplePagesShouldLookUpEntriesCorrectly() { - - Person sample = new Person(); - sample.setLastname("Matthews"); - trimDomainType(sample, "id", "createdAt", "email"); - - Page result = repository.findAll(Example.of(sample), PageRequest.of(0, 1)); - - assertThat(result.getContent()).hasSize(1); - assertThat(result.getTotalPages()).isEqualTo(2); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldLookUpEntriesCorrectly() { - - Person sample = new Person(); - sample.setLastname("Matthews"); - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.findAll(Example.of(sample))).hasSize(2).contains(dave, oliver); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldLookUpEntriesCorrectlyWhenUsingNestedObject() { - - dave.setAddress(new Address("1600 Pennsylvania Ave NW", "20500", "Washington")); - repository.save(dave); - - oliver.setAddress(new Address("East Capitol St NE & First St SE", "20004", "Washington")); - repository.save(oliver); - - Person sample = new Person(); - sample.setAddress(dave.getAddress()); - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.findAll(Example.of(sample))).hasSize(1).contains(dave); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldLookUpEntriesCorrectlyWhenUsingPartialNestedObject() { - - dave.setAddress(new Address("1600 Pennsylvania Ave NW", "20500", "Washington")); - repository.save(dave); - - oliver.setAddress(new Address("East Capitol St NE & First St SE", "20004", "Washington")); - repository.save(oliver); - - Person sample = new Person(); - sample.setAddress(new Address(null, null, "Washington")); - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.findAll(Example.of(sample))).hasSize(2).contains(dave, oliver); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldNotFindEntriesWhenUsingPartialNestedObjectInStrictMode() { - - dave.setAddress(new Address("1600 Pennsylvania Ave NW", "20500", "Washington")); - repository.save(dave); - - Person sample = new Person(); - sample.setAddress(new Address(null, null, "Washington")); - trimDomainType(sample, "id", "createdAt", "email"); - - Example example = Example.of(sample, matching().withIncludeNullValues()); - - assertThat(repository.findAll(example)).isEmpty(); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldLookUpEntriesCorrectlyWhenUsingNestedObjectInStrictMode() { - - dave.setAddress(new Address("1600 Pennsylvania Ave NW", "20500", "Washington")); - repository.save(dave); - - Person sample = new Person(); - sample.setAddress(dave.getAddress()); - trimDomainType(sample, "id", "createdAt", "email"); - - Example example = Example.of(sample, matching().withIncludeNullValues()); - - assertThat(repository.findAll(example)).hasSize(1).contains(dave); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldRespectStringMatchMode() { - - Person sample = new Person(); - sample.setLastname("Mat"); - trimDomainType(sample, "id", "createdAt", "email"); - - Example example = Example.of(sample, matching().withStringMatcher(StringMatcher.STARTING)); - - assertThat(repository.findAll(example)).hasSize(2).contains(dave, oliver); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldResolveDbRefCorrectly() { - - User user = new User(); - user.setId("c0nf1ux"); - user.setUsername("conflux"); - template.save(user); - - Person megan = new Person("megan", "tarash"); - megan.setCreator(user); - - repository.save(megan); - - Person sample = new Person(); - sample.setCreator(user); - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.findAll(Example.of(sample))).hasSize(1).contains(megan); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldResolveLegacyCoordinatesCorrectly() { - - Person megan = new Person("megan", "tarash"); - megan.setLocation(new Point(41.85003D, -87.65005D)); - - repository.save(megan); - - Person sample = new Person(); - sample.setLocation(megan.getLocation()); - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.findAll(Example.of(sample))).hasSize(1).contains(megan); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldResolveGeoJsonCoordinatesCorrectly() { - - Person megan = new Person("megan", "tarash"); - megan.setLocation(new GeoJsonPoint(41.85003D, -87.65005D)); - - repository.save(megan); - - Person sample = new Person(); - sample.setLocation(megan.getLocation()); - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.findAll(Example.of(sample))).hasSize(1).contains(megan); - } - - @Test // DATAMONGO-1245 - void findAllByExampleShouldProcessInheritanceCorrectly() { - - PersonExtended reference = new PersonExtended(); - reference.setLastname("Matthews"); - - repository.save(reference); - - PersonExtended sample = new PersonExtended(); - sample.setLastname("Matthews"); - - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.findAll(Example.of(sample))).hasSize(1).contains(reference); - } - - @Test // DATAMONGO-1245 - void findOneByExampleShouldLookUpEntriesCorrectly() { - - Person sample = new Person(); - sample.setFirstname("Dave"); - sample.setLastname("Matthews"); - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.findOne(Example.of(sample))).isPresent().contains(dave); - } - - @Test // DATAMONGO-1245 - void existsByExampleShouldLookUpEntriesCorrectly() { - - Person sample = new Person(); - sample.setFirstname("Dave"); - sample.setLastname("Matthews"); - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.exists(Example.of(sample))).isTrue(); - } - - @Test // DATAMONGO-1245 - void countByExampleShouldLookUpEntriesCorrectly() { - - Person sample = new Person(); - sample.setLastname("Matthews"); - trimDomainType(sample, "id", "createdAt", "email"); - - assertThat(repository.count(Example.of(sample))).isEqualTo(2L); - } - - @Test // DATAMONGO-1896 - void saveAllUsesEntityCollection() { - - Person first = new PersonExtended(); - first.setEmail("foo@bar.com"); - ReflectionTestUtils.setField(first, "id", null); - - Person second = new PersonExtended(); - second.setEmail("bar@foo.com"); - ReflectionTestUtils.setField(second, "id", null); - - repository.deleteAll(); - - repository.saveAll(asList(first, second)); - - assertThat(repository.findAll()).containsExactlyInAnyOrder(first, second); - } - - @Test // DATAMONGO-2130 - @EnableIfReplicaSetAvailable - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - void countShouldBePossibleInTransaction() { - - MongoTransactionManager txmgr = new MongoTransactionManager(template.getMongoDbFactory()); - TransactionTemplate tt = new TransactionTemplate(txmgr); - tt.afterPropertiesSet(); - - long countPreTx = repository.count(); - - long count = tt.execute(status -> { - - Person sample = new Person(); - sample.setLastname("Matthews"); - - repository.save(sample); - - return repository.count(); - }); - - assertThat(count).isEqualTo(countPreTx + 1); - } - - @Test // DATAMONGO-2130 - @EnableIfReplicaSetAvailable - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - void existsShouldBePossibleInTransaction() { - - MongoTransactionManager txmgr = new MongoTransactionManager(template.getMongoDbFactory()); - TransactionTemplate tt = new TransactionTemplate(txmgr); - tt.afterPropertiesSet(); - - boolean exists = tt.execute(status -> { - - Person sample = new Person(); - sample.setLastname("Matthews"); - - repository.save(sample); - - return repository.existsById(sample.getId()); - }); - - assertThat(exists).isTrue(); - } - - @Test // DATAMONGO-2652 - void deleteAllByIds() { - - repository.deleteAllById(asList(dave.getId(), carter.getId())); - - assertThat(repository.findAll()) // - .hasSize(all.size() - 2).doesNotContain(dave, carter); - } - - private void assertThatAllReferencePersonsWereStoredCorrectly(Map references, List saved) { - - for (Person person : saved) { - Person reference = references.get(person.getId()); - assertThat(person).isEqualTo(reference); - } - } - - private void trimDomainType(Object source, String... attributes) { - - for (String attribute : attributes) { - ReflectionTestUtils.setField(source, attribute, null); - } - } - - private static class CustomizedPersonInformation implements MongoEntityInformation { - - @Override - public boolean isNew(Person entity) { - return entity.getId() == null; - } - - @Override - public String getId(Person entity) { - return entity.getId(); - } - - @Override - public Class getIdType() { - return String.class; - } - - @Override - public Class getJavaType() { - return Person.class; - } - - @Override - public String getCollectionName() { - return "customizedPerson"; - } - - @Override - public String getIdAttribute() { - return "id"; - } - - @Override - public Collation getCollation() { - return null; - } - } - - @Document - static class PersonExtended extends Person {} -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryUnitTests.java deleted file mode 100644 index cc59a57dbc..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryUnitTests.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2019-2021 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.repository.support; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.domain.Example; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.query.MongoEntityInformation; - -/** - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -public class SimpleMongoRepositoryUnitTests { - - SimpleMongoRepository repository; - @Mock MongoOperations mongoOperations; - @Mock MongoEntityInformation entityInformation; - - @BeforeEach - public void setUp() { - repository = new SimpleMongoRepository<>(entityInformation, mongoOperations); - } - - @Test // DATAMONGO-1854 - public void shouldAddDefaultCollationToCountForExampleIfPresent() { - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.count(Example.of(new TestDummy())); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).count(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - public void shouldAddDefaultCollationToExistsForExampleIfPresent() { - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.exists(Example.of(new TestDummy())); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).exists(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - public void shouldAddDefaultCollationToFindForExampleIfPresent() { - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.findAll(Example.of(new TestDummy())); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).find(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - public void shouldAddDefaultCollationToFindWithSortForExampleIfPresent() { - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.findAll(Example.of(new TestDummy()), Sort.by("nothing")); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).find(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - public void shouldAddDefaultCollationToFindWithPageableForExampleIfPresent() { - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.findAll(Example.of(new TestDummy()), PageRequest.of(1, 1, Sort.by("nothing"))); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).find(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - public void shouldAddDefaultCollationToFindOneForExampleIfPresent() { - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.findOne(Example.of(new TestDummy())); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).findOne(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - static class TestDummy { - - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryVersionedEntityTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryVersionedEntityTests.java deleted file mode 100644 index 1903107855..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepositoryVersionedEntityTests.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2019-2021 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.repository.support; - -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.OptimisticLockingFailureException; -import org.springframework.data.mongodb.MongoTransactionManager; -import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.repository.VersionedPerson; -import org.springframework.data.mongodb.repository.query.MongoEntityInformation; -import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.data.mongodb.test.util.ReplicaSet; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.transaction.support.TransactionTemplate; - -import com.mongodb.client.MongoClient; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration -public class SimpleMongoRepositoryVersionedEntityTests { - - @Configuration - static class Config extends AbstractMongoClientConfiguration { - - @Override - public MongoClient mongoClient() { - return MongoTestUtils.client(); - } - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Override - protected Set> getInitialEntitySet() throws ClassNotFoundException { - return new HashSet<>(Arrays.asList(VersionedPerson.class)); - } - } - - @Autowired private MongoTemplate template; - - private MongoEntityInformation personEntityInformation; - private SimpleMongoRepository repository; - - private VersionedPerson sarah; - - @BeforeEach - public void setUp() { - - MongoPersistentEntity entity = template.getConverter().getMappingContext() - .getRequiredPersistentEntity(VersionedPerson.class); - - personEntityInformation = new MappingMongoEntityInformation(entity); - repository = new SimpleMongoRepository<>(personEntityInformation, template); - repository.deleteAll(); - - sarah = repository.save(new VersionedPerson("Sarah", "Connor")); - } - - @Test // DATAMONGO-2195 - public void deleteWithMatchingVersion() { - - repository.delete(sarah); - - assertThat(template.count(query(where("id").is(sarah.getId())), VersionedPerson.class)).isZero(); - } - - @Test // DATAMONGO-2195 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - public void deleteWithMatchingVersionInTx() { - - assumeThat(ReplicaSet.required().runsAsReplicaSet()).isTrue(); - - long countBefore = repository.count(); - - initTxTemplate().execute(status -> { - - VersionedPerson t800 = repository.save(new VersionedPerson("T-800")); - repository.delete(t800); - - return Void.TYPE; - }); - - assertThat(repository.count()).isEqualTo(countBefore); - } - - @Test // DATAMONGO-2195 - public void deleteWithVersionMismatch() { - - sarah.setVersion(5L); - - assertThatExceptionOfType(OptimisticLockingFailureException.class).isThrownBy(() -> repository.delete(sarah)); - - assertThat(template.count(query(where("id").is(sarah.getId())), VersionedPerson.class)).isOne(); - } - - @Test // DATAMONGO-2195 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - public void deleteWithVersionMismatchInTx() { - - assumeThat(ReplicaSet.required().runsAsReplicaSet()).isTrue(); - - long countBefore = repository.count(); - - assertThatExceptionOfType(OptimisticLockingFailureException.class) - .isThrownBy(() -> initTxTemplate().execute(status -> { - - VersionedPerson t800 = repository.save(new VersionedPerson("T-800")); - t800.setVersion(5L); - repository.delete(t800); - - return Void.TYPE; - })); - - assertThat(repository.count()).isEqualTo(countBefore); - } - - @Test // DATAMONGO-2195 - public void deleteNonExisting() { - assertThatThrownBy(() -> repository.delete(new VersionedPerson("T-800"))) - .isInstanceOf(OptimisticLockingFailureException.class); - } - - @Test // DATAMONGO-2195 - @EnableIfMongoServerVersion(isGreaterThanEqual = "4.0") - public void deleteNonExistingInTx() { - - assumeThat(ReplicaSet.required().runsAsReplicaSet()).isTrue(); - - initTxTemplate().execute(status -> { - - assertThatThrownBy(() -> repository.delete(new VersionedPerson("T-800"))) - .isInstanceOf(OptimisticLockingFailureException.class); - - return Void.TYPE; - }); - } - - TransactionTemplate initTxTemplate() { - - MongoTransactionManager txmgr = new MongoTransactionManager(template.getMongoDbFactory()); - TransactionTemplate tt = new TransactionTemplate(txmgr); - tt.afterPropertiesSet(); - - return tt; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepositoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepositoryUnitTests.java deleted file mode 100644 index d5d7ed1180..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepositoryUnitTests.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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. - */ - -/* - * Copyright 2019-2021 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.repository.support; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Sort; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.query.MongoEntityInformation; - -/** - * @author Christoph Strobl - */ -@ExtendWith(MockitoExtension.class) -class SimpleReactiveMongoRepositoryUnitTests { - - private SimpleReactiveMongoRepository repository; - @Mock Mono mono; - @Mock Flux flux; - @Mock ReactiveMongoOperations mongoOperations; - @Mock MongoEntityInformation entityInformation; - - @BeforeEach - void setUp() { - repository = new SimpleReactiveMongoRepository<>(entityInformation, mongoOperations); - } - - @Test // DATAMONGO-1854 - void shouldAddDefaultCollationToCountForExampleIfPresent() { - - when(mongoOperations.count(any(), any(), any())).thenReturn(mono); - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.count(Example.of(new TestDummy())).subscribe(); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).count(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - void shouldAddDefaultCollationToExistsForExampleIfPresent() { - - when(mongoOperations.exists(any(), any(), any())).thenReturn(mono); - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.exists(Example.of(new TestDummy())).subscribe(); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).exists(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - void shouldAddDefaultCollationToFindForExampleIfPresent() { - - when(mongoOperations.find(any(), any(), any())).thenReturn(flux); - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.findAll(Example.of(new TestDummy())).subscribe(); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).find(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - void shouldAddDefaultCollationToFindWithSortForExampleIfPresent() { - - when(mongoOperations.find(any(), any(), any())).thenReturn(flux); - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.findAll(Example.of(new TestDummy()), Sort.by("nothing")).subscribe(); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).find(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - @Test // DATAMONGO-1854 - void shouldAddDefaultCollationToFindOneForExampleIfPresent() { - - when(mongoOperations.find(any(), any(), any())).thenReturn(flux); - - Collation collation = Collation.of("en_US"); - - when(entityInformation.getCollation()).thenReturn(collation); - repository.findOne(Example.of(new TestDummy())).subscribe(); - - ArgumentCaptor query = ArgumentCaptor.forClass(Query.class); - verify(mongoOperations).find(query.capture(), any(), any()); - - assertThat(query.getValue().getCollation()).contains(collation); - } - - private static class TestDummy { - - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepositoryVersionedEntityTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepositoryVersionedEntityTests.java deleted file mode 100644 index 2b4a3e8f11..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepositoryVersionedEntityTests.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2019-2021 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.repository.support; - -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; - -import reactor.test.StepVerifier; - -import java.util.Collections; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.OptimisticLockingFailureException; -import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.mongodb.repository.VersionedPerson; -import org.springframework.data.mongodb.repository.query.MongoEntityInformation; -import org.springframework.data.mongodb.test.util.MongoTestUtils; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.reactivestreams.client.MongoClient; - -/** - * @author Mark Paluch - */ -@RunWith(SpringRunner.class) -@ContextConfiguration -public class SimpleReactiveMongoRepositoryVersionedEntityTests { - - @Configuration - static class Config extends AbstractReactiveMongoConfiguration { - - @Override - public MongoClient reactiveMongoClient() { - return MongoTestUtils.reactiveClient(); - } - - @Override - protected String getDatabaseName() { - return "database"; - } - - @Override - protected Set> getInitialEntitySet() { - return Collections.singleton(VersionedPerson.class); - } - } - - @Autowired // - private ReactiveMongoTemplate template; - - private MongoEntityInformation personEntityInformation; - private SimpleReactiveMongoRepository repository; - - private VersionedPerson sarah; - - @Before - public void setUp() { - - MongoPersistentEntity entity = template.getConverter().getMappingContext() - .getRequiredPersistentEntity(VersionedPerson.class); - - personEntityInformation = new MappingMongoEntityInformation(entity); - repository = new SimpleReactiveMongoRepository<>(personEntityInformation, template); - repository.deleteAll().as(StepVerifier::create).verifyComplete(); - - sarah = repository.save(new VersionedPerson("Sarah", "Connor")).block(); - } - - @Test // DATAMONGO-2195 - public void deleteWithMatchingVersion() { - - repository.delete(sarah).as(StepVerifier::create).verifyComplete(); - - template.count(query(where("id").is(sarah.getId())), VersionedPerson.class) // - .as(StepVerifier::create) // - .expectNext(0L).verifyComplete(); - } - - @Test // DATAMONGO-2195 - public void deleteWithVersionMismatch() { - - sarah.setVersion(5L); - - repository.delete(sarah).as(StepVerifier::create).verifyError(OptimisticLockingFailureException.class); - - template.count(query(where("id").is(sarah.getId())), VersionedPerson.class) // - .as(StepVerifier::create) // - .expectNextCount(1) // - .verifyComplete(); - } - - @Test // DATAMONGO-2195 - public void deleteNonExisting() { - - repository.delete(new VersionedPerson("T-800")).as(StepVerifier::create) - .verifyError(OptimisticLockingFailureException.class); - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializerUnitTests.java deleted file mode 100644 index 6df4e25de8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializerUnitTests.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2011-2021 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.repository.support; - -import static com.querydsl.core.types.ExpressionUtils.path; -import static com.querydsl.core.types.ExpressionUtils.predicate; -import static com.querydsl.core.types.dsl.Expressions.*; -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collections; - -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.WritingConverter; -import org.springframework.data.mongodb.core.convert.DbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.mapping.Field; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.repository.Person.Sex; -import org.springframework.data.mongodb.repository.QAddress; -import org.springframework.data.mongodb.repository.QPerson; - -import com.querydsl.core.types.Ops; -import com.querydsl.core.types.Predicate; -import com.querydsl.core.types.PredicateOperation; -import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.core.types.dsl.BooleanOperation; -import com.querydsl.core.types.dsl.PathBuilder; -import com.querydsl.core.types.dsl.SimplePath; -import com.querydsl.core.types.dsl.StringPath; - -/** - * Unit tests for {@link SpringDataMongodbSerializer}. - * - * @author Oliver Gierke - * @author Christoph Strobl - * @author Mark Paluch - * @author Mikhail Kaduchka - * @author Enrique Leon Molina - */ -@ExtendWith(MockitoExtension.class) -public class SpringDataMongodbSerializerUnitTests { - - @Mock DbRefResolver dbFactory; - MongoConverter converter; - SpringDataMongodbSerializer serializer; - - @BeforeEach - public void setUp() { - - MongoMappingContext context = new MongoMappingContext(); - - this.converter = new MappingMongoConverter(dbFactory, context); - this.serializer = new SpringDataMongodbSerializer(converter); - } - - @Test - public void uses_idAsKeyForIdProperty() { - - StringPath path = QPerson.person.id; - assertThat(serializer.getKeyForPath(path, path.getMetadata())).isEqualTo("_id"); - } - - @Test - public void buildsNestedKeyCorrectly() { - - StringPath path = QPerson.person.address.street; - assertThat(serializer.getKeyForPath(path, path.getMetadata())).isEqualTo("street"); - } - - @Test - public void convertsComplexObjectOnSerializing() { - - Address address = new Address(); - address.street = "Foo"; - address.zipCode = "01234"; - - Document document = serializer.asDocument("foo", address); - - Object value = document.get("foo"); - assertThat(value).isNotNull().isInstanceOf(Document.class); - - Object reference = converter.convertToMongoType(address); - assertThat(value).isEqualTo(reference); - } - - @Test // DATAMONGO-376 - public void returnsEmptyStringIfNoPathExpressionIsGiven() { - - QAddress address = QPerson.person.shippingAddresses.any(); - assertThat(serializer.getKeyForPath(address, address.getMetadata())).isEmpty(); - } - - @Test // DATAMONGO-467, DATAMONGO-1798 - public void retainsIdPropertyType() { - - ObjectId id = new ObjectId(); - - PathBuilder
builder = new PathBuilder
(Address.class, "address"); - StringPath idPath = builder.getString("id"); - - Document result = (Document) serializer.visit((BooleanOperation) idPath.eq(id.toString()), null); - assertThat(result.get("_id")).isNotNull().isInstanceOf(String.class).isEqualTo(id.toString()); - } - - @Test // DATAMONGO-761 - public void looksUpKeyForNonPropertyPath() { - - PathBuilder
builder = new PathBuilder
(Address.class, "address"); - SimplePath firstElementPath = builder.getArray("foo", String[].class).get(0); - String path = serializer.getKeyForPath(firstElementPath, firstElementPath.getMetadata()); - - assertThat(path).isEqualTo("0"); - } - - @Test // DATAMONGO-1485 - public void takesCustomConversionForEnumsIntoAccount() { - - MongoMappingContext context = new MongoMappingContext(); - - MappingMongoConverter converter = new MappingMongoConverter(dbFactory, context); - converter.setCustomConversions(new MongoCustomConversions(Collections.singletonList(new SexTypeWriteConverter()))); - converter.afterPropertiesSet(); - - this.converter = converter; - this.serializer = new SpringDataMongodbSerializer(this.converter); - - Object mappedPredicate = serializer.handle(QPerson.person.sex.eq(Sex.FEMALE)); - - assertThat(mappedPredicate).isInstanceOf(Document.class); - assertThat(((Document) mappedPredicate).get("sex")).isEqualTo("f"); - } - - @Test // DATAMONGO-1848, DATAMONGO-1943 - public void shouldRemarshallListsAndDocuments() { - - BooleanExpression criteria = QPerson.person.lastname.isNotEmpty() - .and(QPerson.person.firstname.containsIgnoreCase("foo")).not(); - - assertThat(serializer.handle(criteria)).isEqualTo(Document.parse("{ \"$or\" : [ { \"lastname\" : { \"$not\" : { " - + "\"$ne\" : \"\"}}} , { \"firstname\" : { \"$not\" : { \"$regex\" : \".*\\\\Qfoo\\\\E.*\" , \"$options\" : \"i\"}}}]}")); - } - - @Test // DATAMONGO-2228 - public void retainsOpsInAndExpression() { - - PredicateOperation testExpression = predicate(Ops.AND, - predicate(Ops.OR, predicate(Ops.EQ, path(Object.class, "firstname"), constant("John")), - predicate(Ops.EQ, path(Object.class, "firstname"), constant("Sarah"))), - predicate(Ops.OR, predicate(Ops.EQ, path(Object.class, "lastname"), constant("Smith")), - predicate(Ops.EQ, path(Object.class, "lastname"), constant("Connor")))); - - assertThat(serializer.handle(testExpression)).isEqualTo(Document.parse( - "{\"$and\": [{\"$or\": [{\"firstname\": \"John\"}, {\"firstname\": \"Sarah\"}]}, {\"$or\": [{\"lastname\": \"Smith\"}, {\"lastname\": \"Connor\"}]}]}")); - } - - @Test // DATAMONGO-2475 - public void chainedOrsInSameDocument() { - - Predicate predicate = QPerson.person.firstname.eq("firstname_value") - .or(QPerson.person.lastname.eq("lastname_value")).or(QPerson.person.age.goe(30)).or(QPerson.person.age.loe(20)) - .or(QPerson.person.uniqueId.isNull()); - - assertThat(serializer.handle(predicate)).isEqualTo(Document.parse( - "{\"$or\": [{\"firstname\": \"firstname_value\"}, {\"lastname\": \"lastname_value\"}, {\"age\": {\"$gte\": 30}}, {\"age\": {\"$lte\": 20}}, {\"uniqueId\": {\"$exists\": false}}]}")); - } - - @Test // DATAMONGO-2475 - public void chainedNestedOrsInSameDocument() { - - Predicate predicate = QPerson.person.firstname.eq("firstname_value") - .or(QPerson.person.lastname.eq("lastname_value")).or(QPerson.person.address.street.eq("spring")); - - assertThat(serializer.handle(predicate)).isEqualTo(Document.parse( - "{\"$or\": [{\"firstname\": \"firstname_value\"}, {\"lastname\": \"lastname_value\"}, {\"add.street\": \"spring\"}]}")); - } - - @Test // DATAMONGO-2475 - public void chainedAndsInSameDocument() { - - Predicate predicate = QPerson.person.firstname.eq("firstname_value") - .and(QPerson.person.lastname.eq("lastname_value")).and(QPerson.person.age.goe(30)) - .and(QPerson.person.age.loe(20)).and(QPerson.person.uniqueId.isNull()); - - assertThat(serializer.handle(predicate)).isEqualTo(Document.parse( - "{\"$and\": [{\"firstname\": \"firstname_value\", \"lastname\": \"lastname_value\", \"age\": {\"$gte\": 30}, \"uniqueId\": {\"$exists\": false}}, {\"age\": {\"$lte\": 20}}]}")); - } - - @Test // DATAMONGO-2475 - void chainMultipleAndFlattensCorrectly() { - - Document p1doc = Document.parse("{ \"$or\" : [ { \"firstname\" : \"fn\"}, { \"lastname\" : \"ln\" } ] }"); - Document p2doc = Document - .parse("{ \"$or\" : [ { \"age\" : { \"$gte\" : 20 } }, { \"age\" : { \"$lte\" : 30} } ] }"); - Document p3doc = Document.parse("{ \"$or\" : [ { \"add.city\" : \"c\"}, { \"add.zipCode\" : \"0\" } ] }"); - Document expected = new Document("$and", Arrays.asList(p1doc, p2doc, p3doc)); - - Predicate predicate1 = QPerson.person.firstname.eq("fn").or(QPerson.person.lastname.eq("ln")); - Predicate predicate2 = QPerson.person.age.goe(20).or(QPerson.person.age.loe(30)); - Predicate predicate3 = QPerson.person.address.city.eq("c").or(QPerson.person.address.zipCode.eq("0")); - PredicateOperation testExpression = predicate(Ops.AND, predicate1, predicate2, predicate3); - - assertThat(serializer.handle(testExpression)).isEqualTo(expected); - } - - class Address { - String id; - String street; - @Field("zip_code") String zipCode; - @Field("bar") String[] foo; - } - - @WritingConverter - public class SexTypeWriteConverter implements Converter { - - @Override - public String convert(Sex source) { - - if (source == null) { - return null; - } - - switch (source) { - case MALE: - return "m"; - case FEMALE: - return "f"; - default: - throw new IllegalArgumentException("o_O"); - } - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AfterTransactionAssertion.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AfterTransactionAssertion.java deleted file mode 100644 index e382c0a001..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AfterTransactionAssertion.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2018-2021 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.test.util; - -import lombok.Data; - -import org.springframework.data.domain.Persistable; - -/** - * @author Christoph Strobl - * @currentRead Shadow's Edge - Brent Weeks - */ -@Data -public class AfterTransactionAssertion { - - private final T persistable; - private boolean expectToBePresent; - - public void isPresent() { - expectToBePresent = true; - } - - public void isNotPresent() { - expectToBePresent = false; - } - - public Object getId() { - return persistable.getId(); - } - - public boolean shouldBePresent() { - return expectToBePresent; - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AssertErrors.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AssertErrors.java deleted file mode 100644 index 6eb683a6cd..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/AssertErrors.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2017-2021 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.test.util; - -import org.assertj.core.error.BasicErrorMessageFactory; -import org.assertj.core.error.ErrorMessageFactory; -import org.assertj.core.internal.StandardComparisonStrategy; - -/** - * Utility class providing factory methods for {@link ErrorMessageFactory}. - * - * @author Mark Paluch - */ -class AssertErrors { - - /** - * Creates a new {@link ShouldHaveProperty}. - * - * @param actual the actual value in the failed assertion. - * @param key the key used in the failed assertion to compare the actual property key to. - * @param value the value used in the failed assertion to compare the actual property value to. - * @return the created {@link ErrorMessageFactory}. - */ - public static ErrorMessageFactory shouldHaveProperty(Object actual, String key, Object value) { - return new ShouldHaveProperty(actual, key, value); - } - - /** - * Creates a new {@link ShouldNotHaveProperty}. - * - * @param actual the actual value in the failed assertion. - * @param key the key used in the failed assertion to compare the actual property key to. - * @param value the value used in the failed assertion to compare the actual property value to. - * @return the created {@link ErrorMessageFactory}. - */ - public static ErrorMessageFactory shouldNotHaveProperty(Object actual, String key, Object value) { - return new ShouldNotHaveProperty(actual, key, value); - } - - private static class ShouldHaveProperty extends BasicErrorMessageFactory { - - private ShouldHaveProperty(Object actual, String key, Object value) { - super("\n" + // - "Expecting:\n" + // - " <%s>\n" + // - "to have property with key:\n" + // - " <%s>\n" + // - "and value:\n" + // - " <%s>\n" + // - "%s", actual, key, value, StandardComparisonStrategy.instance()); - } - } - - private static class ShouldNotHaveProperty extends BasicErrorMessageFactory { - - private ShouldNotHaveProperty(Object actual, String key, Object value) { - super("\n" + // - "Expecting:\n" + // - " <%s>\n" + // - "not to have property with key:\n" + // - " <%s>\n" + // - "and value:\n" + // - " <%s>\n" + // - "but actually found such property %s", actual, key, value, StandardComparisonStrategy.instance()); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/Assertions.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/Assertions.java deleted file mode 100644 index 0ce6b0bddc..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/Assertions.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017-2021 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.test.util; - -import org.bson.Document; - -/** - * The entry point for all MongoDB assertions. This class extends {@link org.assertj.core.api.Assertions} for - * convenience to statically import a single class. - * - * @author Mark Paluch - */ -public abstract class Assertions extends org.assertj.core.api.Assertions { - - private Assertions() { - // no instances allowed. - } - - /** - * Create assertion for {@link Document}. - * - * @param actual the actual value. - * @return the created assertion object. - */ - public static DocumentAssert assertThat(Document document) { - return new DocumentAssert(document); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/BasicDbListBuilder.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/BasicDbListBuilder.java deleted file mode 100644 index c5c16c68a7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/BasicDbListBuilder.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2015-2021 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.test.util; - -import com.mongodb.BasicDBList; - -/** - * @author Christoph Strobl - */ -public class BasicDbListBuilder { - - private final BasicDBList dbl; - - public BasicDbListBuilder() { - this.dbl = new BasicDBList(); - } - - public BasicDbListBuilder add(Object value) { - - this.dbl.add(value); - return this; - } - - public BasicDBList get() { - return this.dbl; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDB.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDB.java deleted file mode 100644 index 1dd3856049..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDB.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright 2014-2021 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.test.util; - -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.bson.Document; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; - -/** - * {@link CleanMongoDB} is a junit {@link TestRule} implementation to be used as for wiping data from MongoDB instance. - * MongoDB specific system databases like {@literal admin} and {@literal local} remain untouched. The rule will apply - * after the base {@link Statement}.
- * Use as {@link org.junit.ClassRule} to wipe data after finishing all tests within a class or as {@link org.junit.Rule} - * to do so after each {@link org.junit.Test}. - * - * @author Christoph Strobl - * @since 1.6 - */ -public class CleanMongoDB implements TestRule { - - private static final Logger LOGGER = LoggerFactory.getLogger(CleanMongoDB.class); - - /** - * Defines contents of MongoDB. - */ - public enum Struct { - DATABASE, COLLECTION, INDEX; - } - - @SuppressWarnings("serial") // - private Set preserveDatabases = new HashSet() { - { - add("admin"); - add("local"); - } - }; - - private Set dbNames = new HashSet(); - private Set collectionNames = new HashSet(); - private Set types = new HashSet(); - private MongoClient client; - - /** - * Create new instance using an internal {@link MongoClient}. - */ - public CleanMongoDB() { - this(null); - } - - /** - * Create new instance using an internal {@link MongoClient} connecting to specified instance running at host:port. - * - * @param host - * @param port - * @throws UnknownHostException - */ - public CleanMongoDB(String host, int port) throws UnknownHostException { - this(MongoTestUtils.client(host, port)); - } - - /** - * Create new instance using the given client. - * - * @param client - */ - public CleanMongoDB(MongoClient client) { - this.client = client; - } - - /** - * Removes everything by dropping every single {@link DB}. - * - * @return - */ - public static CleanMongoDB everything() { - - CleanMongoDB cleanMongoDB = new CleanMongoDB(); - cleanMongoDB.clean(Struct.DATABASE); - return cleanMongoDB; - } - - /** - * Removes everything from the databases with given name by dropping the according {@link DB}. - * - * @param dbNames - * @return - */ - public static CleanMongoDB databases(String... dbNames) { - - CleanMongoDB cleanMongoDB = new CleanMongoDB(); - cleanMongoDB.clean(Struct.DATABASE); - cleanMongoDB.useDatabases(dbNames); - return cleanMongoDB; - } - - /** - * Drops the {@link DBCollection} with given names from every single {@link DB} containing them. - * - * @param collectionNames - * @return - */ - public static CleanMongoDB collections(String... collectionNames) { - return collections("", Arrays.asList(collectionNames)); - } - - /** - * Drops the {@link DBCollection} with given names from the named {@link DB}. - * - * @param dbName - * @param collectionNames - * @return - */ - public static CleanMongoDB collections(String dbName, Collection collectionNames) { - - CleanMongoDB cleanMongoDB = new CleanMongoDB(); - cleanMongoDB.clean(Struct.COLLECTION); - cleanMongoDB.useCollections(dbName, collectionNames); - return cleanMongoDB; - } - - /** - * Drops all index structures from every single {@link DBCollection}. - * - * @return - */ - public static CleanMongoDB indexes() { - return indexes(Collections. emptySet()); - } - - /** - * Drops all index structures from every single {@link DBCollection}. - * - * @param collectionNames - * @return - */ - public static CleanMongoDB indexes(Collection collectionNames) { - - CleanMongoDB cleanMongoDB = new CleanMongoDB(); - cleanMongoDB.clean(Struct.INDEX); - cleanMongoDB.useCollections(collectionNames); - return cleanMongoDB; - } - - /** - * Define {@link Struct} to be cleaned. - * - * @param types - * @return - */ - public CleanMongoDB clean(Struct... types) { - - this.types.addAll(Arrays.asList(types)); - return this; - } - - /** - * Defines the {@link DB}s to be used.
- * Impact along with {@link CleanMongoDB#clean(Struct...)}: - *
    - *
  • {@link Struct#DATABASE}: Forces drop of named databases.
  • - *
  • {@link Struct#COLLECTION}: Forces drop of collections within named databases.
  • - *
  • {@link Struct#INDEX}: Removes index within collections of named databases.
  • - *
- * - * @param dbNames - * @return - */ - public CleanMongoDB useDatabases(String... dbNames) { - - this.dbNames.addAll(Arrays.asList(dbNames)); - return this; - } - - /** - * Excludes the given {@link DB}s from being processed. - * - * @param dbNames - * @return - */ - public CleanMongoDB preserveDatabases(String... dbNames) { - this.preserveDatabases.addAll(Arrays.asList(dbNames)); - return this; - } - - /** - * Defines the {@link DBCollection}s to be used.
- * Impact along with {@link CleanMongoDB#clean(Struct...)}: - *
    - *
  • {@link Struct#COLLECTION}: Forces drop of named collections.
  • - *
  • {@link Struct#INDEX}: Removes index within named collections.
  • - *
- * - * @param collectionNames - * @return - */ - public CleanMongoDB useCollections(String... collectionNames) { - return useCollections(Arrays.asList(collectionNames)); - } - - private CleanMongoDB useCollections(Collection collectionNames) { - return useCollections("", collectionNames); - } - - /** - * Defines the {@link DBCollection}s and {@link DB} to be used.
- * Impact along with {@link CleanMongoDB#clean(Struct...)}: - *
    - *
  • {@link Struct#COLLECTION}: Forces drop of named collections in given db.
  • - *
  • {@link Struct#INDEX}: Removes index within named collections in given db.
  • - *
- * - * @param collectionNames - * @return - */ - public CleanMongoDB useCollections(String db, Collection collectionNames) { - - if (StringUtils.hasText(db)) { - this.dbNames.add(db); - } - - if (!CollectionUtils.isEmpty(collectionNames)) { - this.collectionNames.addAll(collectionNames); - } - return this; - } - - Statement apply() { - return apply(null, null); - } - - /* - * (non-Javadoc) - * @see org.junit.rules.TestRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description) - */ - public Statement apply(Statement base, Description description) { - return new MongoCleanStatement(base); - } - - private void doClean() { - - Collection dbNamesToUse = initDbNames(); - - for (String dbName : dbNamesToUse) { - - if (isPreserved(dbName) || dropDbIfRequired(dbName)) { - continue; - } - - MongoDatabase db = client.getDatabase(dbName); - dropCollectionsOrIndexIfRequried(db, initCollectionNames(db)); - } - } - - private boolean dropDbIfRequired(String dbName) { - - if (!types.contains(Struct.DATABASE)) { - return false; - } - - client.getDatabase(dbName).drop(); - LOGGER.debug("Dropping DB '{}'. ", dbName); - return true; - } - - private void dropCollectionsOrIndexIfRequried(MongoDatabase db, Collection collectionsToUse) { - - Collection availableCollections = db.listCollectionNames().into(new LinkedHashSet<>()); - - for (String collectionName : collectionsToUse) { - - if (availableCollections.contains(collectionName)) { - - MongoCollection collection = db.getCollection(collectionName); - if (collection != null) { - - if (types.contains(Struct.COLLECTION)) { - collection.drop(); - LOGGER.debug("Dropping collection '{}' for DB '{}'. ", collectionName, db.getName()); - } else if (types.contains(Struct.INDEX)) { - collection.dropIndexes(); - LOGGER.debug("Dropping indexes in collection '{}' for DB '{}'. ", collectionName, db.getName()); - } - } - } - } - } - - private boolean isPreserved(String dbName) { - return preserveDatabases.contains(dbName.toLowerCase()); - } - - private Collection initDbNames() { - - Collection dbNamesToUse = dbNames; - if (dbNamesToUse.isEmpty()) { - dbNamesToUse = client.listDatabaseNames().into(new LinkedHashSet<>()); - } - return dbNamesToUse; - } - - private Collection initCollectionNames(MongoDatabase db) { - - Collection collectionsToUse = collectionNames; - if (CollectionUtils.isEmpty(collectionsToUse)) { - collectionsToUse = db.listCollectionNames().into(new LinkedHashSet<>()); - } - return collectionsToUse; - } - - /** - * @author Christoph Strobl - * @since 1.6 - */ - private class MongoCleanStatement extends Statement { - - private final Statement base; - - public MongoCleanStatement(Statement base) { - this.base = base; - } - - @Override - public void evaluate() throws Throwable { - - if (base != null) { - base.evaluate(); - } - - boolean isInternal = false; - if (client == null) { - client = MongoTestUtils.client(); - isInternal = true; - } - - doClean(); - - if (isInternal) { - client = null; - } - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDBJunitRunListener.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDBJunitRunListener.java deleted file mode 100644 index 15e830a5a7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDBJunitRunListener.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2014-2021 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.test.util; - -import org.junit.runner.Result; -import org.junit.runner.notification.RunListener; -import org.springframework.data.mongodb.test.util.CleanMongoDB.Struct; - -/** - * {@link RunListener} implementation to be used for wiping MongoDB index structures after all test runs have finished. - * - * @author Christoph Strobl - * @since 1.6 - */ -public class CleanMongoDBJunitRunListener extends RunListener { - - @Override - public void testRunFinished(Result result) throws Exception { - - super.testRunFinished(result); - try { - new CleanMongoDB().clean(Struct.INDEX).apply().evaluate(); - } catch (Throwable e) { - e.printStackTrace(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDBTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDBTests.java deleted file mode 100644 index 6639fad6fd..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/CleanMongoDBTests.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2014-2021 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.test.util; - -import static org.mockito.Mockito.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import org.bson.Document; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import org.springframework.data.mongodb.test.util.CleanMongoDB.Struct; - -import com.mongodb.client.ListDatabasesIterable; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; - -/** - * @author Christoph Strobl - * @author Mark Paluch - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -class CleanMongoDBTests { - - private CleanMongoDB cleaner; - - // JUnit internals - private @Mock Statement baseStatementMock; - private @Mock Description descriptionMock; - - // MongoClient in use - private @Mock MongoClient mongoClientMock; - - // Some Mock DBs - private @Mock MongoDatabase db1mock, db2mock, admin; - private @Mock MongoCollection db1collection1mock, db1collection2mock, db2collection1mock; - - @SuppressWarnings({ "serial", "unchecked" }) - @BeforeEach - void setUp() { - - // DB setup - - ListDatabasesIterable dbIterable = mock(ListDatabasesIterable.class); - when(dbIterable.into(any(Collection.class))).thenReturn(Arrays.asList("admin", "db1", "db2")); - when(mongoClientMock.listDatabaseNames()).thenReturn(dbIterable); - when(mongoClientMock.getDatabase(eq("db1"))).thenReturn(db1mock); - when(mongoClientMock.getDatabase(eq("db2"))).thenReturn(db2mock); - - // collections have to exist - ListDatabasesIterable collectionIterable = mock(ListDatabasesIterable.class); - when(collectionIterable.into(any(Collection.class))).thenReturn(Arrays.asList("db1collection1", "db1collection2")); - when(db1mock.listCollectionNames()).thenReturn(collectionIterable); - - ListDatabasesIterable collectionIterable2 = mock(ListDatabasesIterable.class); - when(collectionIterable2.into(any(Collection.class))).thenReturn(Collections.singletonList("db2collection1")); - when(db2mock.listCollectionNames()).thenReturn(collectionIterable2); - - // return collections according to names - when(db1mock.getCollection(eq("db1collection1"))).thenReturn(db1collection1mock); - when(db1mock.getCollection(eq("db1collection2"))).thenReturn(db1collection2mock); - when(db2mock.getCollection(eq("db2collection1"))).thenReturn(db2collection1mock); - - cleaner = new CleanMongoDB(mongoClientMock); - } - - @Test - void preservesSystemDBsCorrectlyWhenCleaningDatabase() throws Throwable { - - cleaner.clean(Struct.DATABASE); - - cleaner.apply(baseStatementMock, descriptionMock).evaluate(); - - verify(admin, never()).drop(); - } - - @Test - void preservesNamedDBsCorrectlyWhenCleaningDatabase() throws Throwable { - - cleaner.clean(Struct.DATABASE); - cleaner.preserveDatabases("db1"); - - cleaner.apply(baseStatementMock, descriptionMock).evaluate(); - - verify(db1mock, never()).drop(); - } - - @Test - void dropsAllDBsCorrectlyWhenCleaingDatabaseAndNotExplictDBNamePresent() throws Throwable { - - cleaner.clean(Struct.DATABASE); - - cleaner.apply(baseStatementMock, descriptionMock).evaluate(); - - verify(db1mock).drop(); - verify(db2mock).drop(); - } - - @Test - void dropsSpecifiedDBsCorrectlyWhenExplicitNameSet() throws Throwable { - - cleaner.clean(Struct.DATABASE); - cleaner.useDatabases("db2"); - - cleaner.apply(baseStatementMock, descriptionMock).evaluate(); - - verify(db2mock).drop(); - verify(db1mock, never()).drop(); - } - - @Test - void doesNotRemoveAnyDBwhenCleaningCollections() throws Throwable { - - cleaner.clean(Struct.COLLECTION); - - cleaner.apply(baseStatementMock, descriptionMock).evaluate(); - - verify(db1mock, never()).drop(); - verify(db2mock, never()).drop(); - verify(admin, never()).drop(); - } - - @Test - void doesNotDropCollectionsFromPreservedDBs() throws Throwable { - - cleaner.clean(Struct.COLLECTION); - cleaner.preserveDatabases("db1"); - - cleaner.apply(baseStatementMock, descriptionMock).evaluate(); - - verify(db1collection1mock, never()).drop(); - verify(db1collection2mock, never()).drop(); - verify(db2collection1mock, times(1)).drop(); - } - - @Test - void removesAllCollectionsFromAllDatabasesWhenNotLimitedToSpecificOnes() throws Throwable { - - cleaner.clean(Struct.COLLECTION); - - cleaner.apply(baseStatementMock, descriptionMock).evaluate(); - - verify(db1collection1mock, times(1)).drop(); - verify(db1collection2mock, times(1)).drop(); - verify(db2collection1mock, times(1)).drop(); - } - - @Test - void removesOnlyNamedCollectionsWhenSpecified() throws Throwable { - - cleaner.clean(Struct.COLLECTION); - cleaner.useCollections("db1collection2"); - - cleaner.apply(baseStatementMock, descriptionMock).evaluate(); - - verify(db1collection1mock, never()).drop(); - verify(db2collection1mock, never()).drop(); - verify(db1collection2mock, times(1)).drop(); - } - - @Test - void removesIndexesCorrectly() throws Throwable { - - cleaner.clean(Struct.INDEX); - - cleaner.apply(baseStatementMock, descriptionMock).evaluate(); - - verify(db1mock, never()).drop(); - verify(db2mock, never()).drop(); - verify(admin, never()).drop(); - - verify(db1collection1mock, times(1)).dropIndexes(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/Client.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/Client.java deleted file mode 100644 index 8fd5acc4a7..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/Client.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020-2021 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.test.util; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.junit.jupiter.api.extension.ExtendWith; - -/** - * Marks a field or method as to be autowired by JUnit's dependency injection facilities for injection of a MongoDB - * client instance. Depends on {@link MongoClientExtension}. - * - * @author Christoph Strobl - * @see com.mongodb.client.MongoClient - * @see com.mongodb.reactivestreams.client.MongoClient - * @see ReplSetClient - * @see MongoClientExtension - */ -@Target({ ElementType.FIELD, ElementType.PARAMETER }) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@ExtendWith(MongoClientExtension.class) -public @interface Client { - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/DocumentAssert.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/DocumentAssert.java deleted file mode 100644 index db33b29b82..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/DocumentAssert.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright 2017-2021 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.test.util; - -import static org.assertj.core.error.ElementsShouldBe.*; -import static org.assertj.core.error.ShouldContain.*; -import static org.assertj.core.error.ShouldContainKeys.*; -import static org.assertj.core.error.ShouldNotContain.*; -import static org.assertj.core.error.ShouldNotContainKeys.*; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.function.Consumer; - -import org.assertj.core.api.AbstractMapAssert; -import org.assertj.core.api.Condition; -import org.assertj.core.error.ShouldContainAnyOf; -import org.assertj.core.internal.Failures; -import org.bson.Document; -import org.bson.conversions.Bson; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; - -/** - * Assertions for Mongo's {@link Document}. Assertions based on keys/entries are translated to document paths allowing - * to assert nested elements. - * - *
- * 
- *  Document document = Document.parse("{ $set: { concreteInnerList: [ { foo: "bar", _class: … }] } }");
- *
- *  assertThat(mappedUpdate).containsKey("$set.concreteInnerList.[0].foo").doesNotContainKey("$set.concreteInnerList.[0].bar");
- * 
- * 
- * - * @author Mark Paluch - */ -public class DocumentAssert extends AbstractMapAssert, String, Object> { - - private final Document actual; - - DocumentAssert(Document actual) { - - super(actual, DocumentAssert.class); - this.actual = actual; - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#containsEntry(java.lang.Object, java.lang.Object) - */ - @Override - public DocumentAssert containsEntry(String key, Object value) { - - Assert.hasText(key, "The key to look for must not be empty!"); - - Lookup lookup = lookup(key); - - if (!lookup.isPathFound() || !ObjectUtils.nullSafeEquals(value, lookup.getValue())) { - throw Failures.instance().failure(info, AssertErrors.shouldHaveProperty(actual, key, value)); - } - - return myself; - } - - /** - * Verifies that the actual value is equal to the given one by accepting the expected {@link Document} in its - * JSON/BSON representation. - *

- * Example: - * - *

-	 *  // assertions will pass
-	 * assertThat(Document.parse("{foo: 1}").isEqualTo("{foo: 1}");
-	 * 
- * - * @param expectedBson the given value to compare the actual value to in BSON/JSON format. - * @return {@code this} assertion object. - * @throws AssertionError if the actual value is not equal to the given one. - * @see Document#parse(String) - */ - public DocumentAssert isEqualTo(String expectedBson) { - - isEqualTo(Document.parse(expectedBson)); - return myself; - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#doesNotContainEntry(java.lang.Object, java.lang.Object) - */ - @Override - public DocumentAssert doesNotContainEntry(String key, Object value) { - - Assert.hasText(key, "The key to look for must not be empty!"); - - Lookup lookup = lookup(key); - - if (lookup.isPathFound() && ObjectUtils.nullSafeEquals(value, lookup.getValue())) { - throw Failures.instance().failure(info, AssertErrors.shouldNotHaveProperty(actual, key, value)); - } - - return myself; - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#containsKey(java.lang.Object) - */ - @Override - public DocumentAssert containsKey(String key) { - return containsKeys(key); - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#containsKeys(java.lang.Object[]) - */ - @Override - public final DocumentAssert containsKeys(String... keys) { - - Set notFound = new LinkedHashSet<>(); - - for (String key : keys) { - - if (!lookup(key).isPathFound()) { - notFound.add(key); - } - } - - if (!notFound.isEmpty()) { - throw Failures.instance().failure(info, shouldContainKeys(actual, notFound)); - } - - return myself; - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#doesNotContainKey(java.lang.Object) - */ - @Override - public DocumentAssert doesNotContainKey(String key) { - return doesNotContainKeys(key); - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#doesNotContainKeys(java.lang.Object[]) - */ - @Override - public final DocumentAssert doesNotContainKeys(String... keys) { - - Set found = new LinkedHashSet<>(); - for (String key : keys) { - - if (lookup(key).isPathFound()) { - found.add(key); - } - } - if (!found.isEmpty()) { - throw Failures.instance().failure(info, shouldNotContainKeys(actual, found)); - } - - return myself; - } - - // override methods to annotate them with @SafeVarargs, we unfortunately can't do that in AbstractMapAssert as it is - // used in soft assertions which need to be able to proxy method - @SafeVarargs requiring method to be final prevents - // using proxies. - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#contains(java.util.Map.Entry[]) - */ - @SafeVarargs - @Override - public final DocumentAssert contains(Map.Entry... entries) { - - // if both actual and values are empty, then assertion passes. - if (actual.isEmpty() && entries.length == 0) { - return myself; - } - Set> notFound = new LinkedHashSet<>(); - for (Map.Entry entry : entries) { - if (!containsEntry(entry)) { - notFound.add(entry); - } - } - if (!notFound.isEmpty()) { - throw Failures.instance().failure(info, shouldContain(actual, entries, notFound)); - } - - return myself; - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#containsAnyOf(java.util.Map.Entry[]) - */ - @SafeVarargs - @Override - public final DocumentAssert containsAnyOf(Map.Entry... entries) { - - for (Map.Entry entry : entries) { - if (containsEntry(entry)) { - return myself; - } - } - - throw Failures.instance().failure(info, ShouldContainAnyOf.shouldContainAnyOf(actual, entries)); - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#containsOnly(java.util.Map.Entry[]) - */ - @SafeVarargs - @Override - public final DocumentAssert containsOnly(Map.Entry... entries) { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#doesNotContain(java.util.Map.Entry[]) - */ - @SafeVarargs - @Override - public final DocumentAssert doesNotContain(Map.Entry... entries) { - - Set> found = new LinkedHashSet<>(); - - for (Map.Entry entry : entries) { - if (containsEntry(entry)) { - found.add(entry); - } - } - if (!found.isEmpty()) { - throw Failures.instance().failure(info, shouldNotContain(actual, entries, found)); - } - - return myself; - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#containsExactly(java.util.Map.Entry[]) - */ - @SafeVarargs - @Override - public final DocumentAssert containsExactly(Map.Entry... entries) { - throw new UnsupportedOperationException(); - } - - private boolean containsEntry(Entry entry) { - - Lookup lookup = lookup(entry.getKey()); - - return lookup.isPathFound() && ObjectUtils.nullSafeEquals(entry.getValue(), lookup.getValue()); - } - - private Lookup lookup(String path) { - return lookup(actual, path); - } - - @SuppressWarnings("unchecked") - private static Lookup lookup(Bson source, String path) { - - Document lookupDocument = (Document) source; - String pathToUse = path.replace("\\.", "."); - - if (lookupDocument.containsKey(pathToUse)) { - return Lookup.found((T) lookupDocument.get(pathToUse)); - } - - String[] fragments = path.split("(? it = Arrays.asList(fragments).iterator(); - - Object current = source; - while (it.hasNext()) { - - String key = it.next().replace("\\.", "."); - - if ((!(current instanceof Bson) && !(current instanceof Map)) && !key.startsWith("[")) { - return Lookup.notFound(); - } - - if (key.startsWith("[")) { - - String indexNumber = key.substring(1, key.indexOf("]")); - - if (current instanceof List) { - current = ((List) current).get(Integer.valueOf(indexNumber)); - } - - if (!it.hasNext()) { - return Lookup.found((T) current); - } - } else { - - if (current instanceof Document) { - - Document document = (Document) current; - - if (!it.hasNext() && !document.containsKey(key)) { - return Lookup.notFound(); - } - - current = document.get(key); - } - - else if (current instanceof Map) { - - Map document = (Map) current; - - if (!it.hasNext() && !document.containsKey(key)) { - return Lookup.notFound(); - } - - current = document.get(key); - } - - if (!it.hasNext()) { - return Lookup.found((T) current); - } - } - } - - return Lookup.notFound(); - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#hasEntrySatisfying(java.lang.Object, org.assertj.core.api.Condition) - */ - @Override - public DocumentAssert hasEntrySatisfying(String key, Condition valueCondition) { - - Lookup value = lookup(key); - - if (!value.isPathFound() || !valueCondition.matches(value.getValue())) { - throw Failures.instance().failure(info, elementsShouldBe(actual, value, valueCondition)); - } - - return myself; - } - - /* - * (non-Javadoc) - * @see org.assertj.core.api.AbstractMapAssert#hasEntrySatisfying(java.lang.Object, java.util.function.Consumer) - */ - @Override - public DocumentAssert hasEntrySatisfying(String key, Consumer valueRequirements) { - - containsKey(key); - - valueRequirements.accept(lookup(key).getValue()); - - return myself; - } - - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - @Getter - static class Lookup { - - private final T value; - private final boolean pathFound; - - /** - * Factory method to construct a lookup with a hit. - * - * @param value the actual value. - * @return the lookup object. - */ - static Lookup found(T value) { - return new Lookup<>(value, true); - } - - /** - * Factory method to construct a lookup that yielded no match. - * - * @return the lookup object. - */ - static Lookup notFound() { - return new Lookup<>(null, false); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfMongoServerVersion.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfMongoServerVersion.java deleted file mode 100644 index 96790b2f04..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfMongoServerVersion.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2019-2021 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.test.util; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.extension.ExtendWith; - -/** - * {@code @EnableIfMongoServerVersion} is used to signal that the annotated test class or test method is only - * enabled if the value of the specified version boundaries {@link #isGreaterThanEqual()} and - * {@link #isLessThan()} match the connected MongoDB server version. - * - * @author Christoph Strobl - * @since 3.0 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Documented -@Tag("version-specific") -@ExtendWith(MongoServerCondition.class) -public @interface EnableIfMongoServerVersion { - - /** - * Inclusive lower bound of MongoDB server range. - * - * @return {@code 0.0.0} by default. - */ - String isGreaterThanEqual() default "0.0.0"; - - /** - * Exclusive upper bound of MongoDB server range. - * - * @return {@code 9999.9999.9999} by default. - */ - String isLessThan() default "9999.9999.9999"; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfReplicaSetAvailable.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfReplicaSetAvailable.java deleted file mode 100644 index 156185bc5c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/EnableIfReplicaSetAvailable.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2019-2021 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.test.util; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.extension.ExtendWith; - -/** - * {@link EnableIfReplicaSetAvailable} marks a specific test class or method to be only executed against a server - * running in replicaSet mode. Intended to be used along with {@link MongoServerCondition}. - * - * @author Christoph Strobl - * @since 3.0 - */ -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Tag("replSet") -@ExtendWith(MongoServerCondition.class) -public @interface EnableIfReplicaSetAvailable { - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoClientExtension.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoClientExtension.java deleted file mode 100644 index 08d61539f6..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoClientExtension.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2020-2021 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.test.util; - -import static org.junit.platform.commons.util.AnnotationUtils.*; -import static org.junit.platform.commons.util.ReflectionUtils.*; - -import java.lang.reflect.Field; -import java.util.function.Predicate; - -import org.junit.jupiter.api.extension.AfterAllCallback; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.Extension; -import org.junit.jupiter.api.extension.ExtensionConfigurationException; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.junit.jupiter.api.extension.ExtensionContext.Store; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.api.extension.ParameterResolutionException; -import org.junit.jupiter.api.extension.ParameterResolver; -import org.junit.platform.commons.util.ExceptionUtils; -import org.junit.platform.commons.util.ReflectionUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.ClassUtils; - -import com.mongodb.client.MongoClient; - -/** - * JUnit {@link Extension} providing parameter resolution for synchronous and reactive MongoDB client instances. - * - * @author Christoph Strobl - * @see Client - * @see ReplSetClient - */ -public class MongoClientExtension implements Extension, BeforeAllCallback, AfterAllCallback, ParameterResolver { - - private static final Logger LOGGER = LoggerFactory.getLogger(MongoClientExtension.class); - - private static final Namespace NAMESPACE = MongoExtensions.Client.NAMESPACE; - - private static final String SYNC_KEY = MongoExtensions.Client.SYNC_KEY; - private static final String REACTIVE_KEY = MongoExtensions.Client.REACTIVE_KEY; - private static final String SYNC_REPLSET_KEY = MongoExtensions.Client.SYNC_REPLSET_KEY; - private static final String REACTIVE_REPLSET_KEY = MongoExtensions.Client.REACTIVE_REPLSET_KEY; - - @Override - public void afterAll(ExtensionContext extensionContext) throws Exception { - - } - - @Override - public void beforeAll(ExtensionContext context) throws Exception { - injectFields(context, null, ReflectionUtils::isStatic); - } - - private void injectFields(ExtensionContext context, Object testInstance, Predicate predicate) { - - findAnnotatedFields(context.getRequiredTestClass(), Client.class, predicate).forEach(field -> { - assertValidFieldCandidate(field); - try { - makeAccessible(field).set(testInstance, getMongoClient(field.getType(), context, false)); - } catch (Throwable t) { - ExceptionUtils.throwAsUncheckedException(t); - } - }); - - findAnnotatedFields(context.getRequiredTestClass(), ReplSetClient.class, predicate).forEach(field -> { - assertValidFieldCandidate(field); - try { - makeAccessible(field).set(testInstance, getMongoClient(field.getType(), context, true)); - } catch (Throwable t) { - ExceptionUtils.throwAsUncheckedException(t); - } - }); - } - - protected Object getMongoClient(Class type, ExtensionContext extensionContext, boolean replSet) { - - Store store = extensionContext.getStore(NAMESPACE); - - if (ClassUtils.isAssignable(com.mongodb.client.MongoClient.class, type)) { - - LOGGER.debug("Obtaining sync client from store."); - return store.getOrComputeIfAbsent(replSet ? SYNC_REPLSET_KEY : SYNC_KEY, it -> syncClient(replSet), - SyncClientHolder.class).client; - } - - if (ClassUtils.isAssignable(com.mongodb.reactivestreams.client.MongoClient.class, type)) { - - LOGGER.debug("Obtaining reactive client from store."); - return store.getOrComputeIfAbsent(replSet ? REACTIVE_REPLSET_KEY : REACTIVE_KEY, key -> reactiveClient(replSet), - ReactiveClientHolder.class).client; - } - - throw new IllegalStateException("Damn - something went wrong."); - } - - private ReactiveClientHolder reactiveClient(boolean replSet) { - - LOGGER.debug("Creating new reactive {}client.", replSet ? "replica set " : ""); - return new ReactiveClientHolder(replSet ? MongoTestUtils.reactiveReplSetClient() : MongoTestUtils.reactiveClient()); - } - - private SyncClientHolder syncClient(boolean replSet) { - - LOGGER.debug("Creating new sync {}client.", replSet ? "replica set " : ""); - return new SyncClientHolder(replSet ? MongoTestUtils.replSetClient() : MongoTestUtils.client()); - } - - boolean holdsReplSetClient(ExtensionContext context) { - - Store store = context.getStore(NAMESPACE); - return store.get(SYNC_REPLSET_KEY) != null || store.get(REACTIVE_REPLSET_KEY) != null; - } - - private void assertValidFieldCandidate(Field field) { - - assertSupportedType("field", field.getType()); - } - - private void assertSupportedType(String target, Class type) { - - if (type != com.mongodb.client.MongoClient.class && type != com.mongodb.reactivestreams.client.MongoClient.class) { - throw new ExtensionConfigurationException("Can only resolve @MongoClient " + target + " of type " - + com.mongodb.client.MongoClient.class.getName() + " or " - + com.mongodb.reactivestreams.client.MongoClient.class.getName() + " but was: " + type.getName()); - } - } - - @Override - public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - return parameterContext.isAnnotated(Client.class) || parameterContext.isAnnotated(ReplSetClient.class); - } - - @Override - public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - - Class parameterType = parameterContext.getParameter().getType(); - boolean replSet = parameterContext.getParameter().getAnnotation(ReplSetClient.class) != null; - return getMongoClient(parameterType, extensionContext, replSet); - } - - static class SyncClientHolder implements Store.CloseableResource { - - final MongoClient client; - - SyncClientHolder(MongoClient client) { - this.client = client; - } - - @Override - public void close() { - try { - client.close(); - } catch (RuntimeException e) { - // so what? - } - } - } - - static class ReactiveClientHolder implements Store.CloseableResource { - - final com.mongodb.reactivestreams.client.MongoClient client; - - ReactiveClientHolder(com.mongodb.reactivestreams.client.MongoClient client) { - this.client = client; - } - - @Override - public void close() { - - try { - client.close(); - } catch (RuntimeException e) { - // so what? - } - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoExtensions.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoExtensions.java deleted file mode 100644 index 8cf6450e17..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoExtensions.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2020-2021 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.test.util; - -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; - -/** - * @author Christoph Strobl - */ -class MongoExtensions { - - static class Client { - - static final Namespace NAMESPACE = Namespace.create(MongoClientExtension.class); - static final String SYNC_KEY = "mongo.client.sync"; - static final String REACTIVE_KEY = "mongo.client.reactive"; - static final String SYNC_REPLSET_KEY = "mongo.client.replset.sync"; - static final String REACTIVE_REPLSET_KEY = "mongo.client.replset.reactive"; - } - - static class Termplate { - - static final Namespace NAMESPACE = Namespace.create(MongoTemplateExtension.class); - static final String SYNC = "mongo.template.sync"; - static final String REACTIVE = "mongo.template.reactive"; - } - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoServerCondition.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoServerCondition.java deleted file mode 100644 index a8ace3873b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoServerCondition.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2019-2021 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.test.util; - -import org.junit.jupiter.api.extension.ConditionEvaluationResult; -import org.junit.jupiter.api.extension.ExecutionCondition; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.data.util.Version; - -/** - * @author Christoph Strobl - */ -public class MongoServerCondition implements ExecutionCondition { - - private static final Namespace NAMESPACE = Namespace.create("mongodb", "server"); - - private static final Version ANY = new Version(9999, 9999, 9999); - private static final Version DEFAULT_HIGH = ANY; - private static final Version DEFAULT_LOW = new Version(0, 0, 0); - - @Override - public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { - - if (context.getTags().contains("replSet")) { - if (!serverIsPartOfReplicaSet(context)) { - return ConditionEvaluationResult.disabled("Disabled for servers not running in replicaSet mode."); - } - } - - if (context.getTags().contains("version-specific") && context.getElement().isPresent()) { - - EnableIfMongoServerVersion version = AnnotatedElementUtils.findMergedAnnotation(context.getElement().get(), - EnableIfMongoServerVersion.class); - - Version serverVersion = serverVersion(context); - - if (version != null && !serverVersion.equals(ANY)) { - - Version expectedMinVersion = Version.parse(version.isGreaterThanEqual()); - if (!expectedMinVersion.equals(ANY) && !expectedMinVersion.equals(DEFAULT_LOW)) { - if (serverVersion.isLessThan(expectedMinVersion)) { - return ConditionEvaluationResult.disabled(String - .format("Disabled for server version %s. Requires at least %s.", serverVersion, expectedMinVersion)); - } - } - - Version expectedMaxVersion = Version.parse(version.isLessThan()); - if (!expectedMaxVersion.equals(ANY) && !expectedMaxVersion.equals(DEFAULT_HIGH)) { - if (serverVersion.isGreaterThanOrEqualTo(expectedMaxVersion)) { - return ConditionEvaluationResult.disabled(String - .format("Disabled for server version %s. Only supported until %s.", serverVersion, expectedMaxVersion)); - } - } - } - } - - return ConditionEvaluationResult.enabled("Enabled by default"); - } - - private boolean serverIsPartOfReplicaSet(ExtensionContext context) { - - return context.getStore(NAMESPACE).getOrComputeIfAbsent("--replSet", (key) -> MongoTestUtils.serverIsReplSet(), - Boolean.class); - } - - private Version serverVersion(ExtensionContext context) { - - return context.getStore(NAMESPACE).getOrComputeIfAbsent(Version.class, (key) -> MongoTestUtils.serverVersion(), - Version.class); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTemplateExtension.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTemplateExtension.java deleted file mode 100644 index 93ce2aa1df..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTemplateExtension.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2020-2021 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.test.util; - -import java.lang.reflect.Field; -import java.util.List; -import java.util.function.Predicate; - -import org.junit.jupiter.api.extension.Extension; -import org.junit.jupiter.api.extension.ExtensionConfigurationException; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Store; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.api.extension.ParameterResolutionException; -import org.junit.platform.commons.util.AnnotationUtils; -import org.junit.platform.commons.util.ExceptionUtils; -import org.junit.platform.commons.util.ReflectionUtils; -import org.junit.platform.commons.util.StringUtils; - -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.test.util.MongoExtensions.Termplate; -import org.springframework.data.util.ParsingUtils; -import org.springframework.util.ClassUtils; - -/** - * JUnit {@link Extension} providing parameter resolution for synchronous and reactive MongoDB Template API objects. - * - * @author Christoph Strobl - * @see Template - * @see MongoTestTemplate - * @see ReactiveMongoTestTemplate - */ -public class MongoTemplateExtension extends MongoClientExtension { - - private static final String DEFAULT_DATABASE = "database"; - - @Override - public void beforeAll(ExtensionContext context) throws Exception { - - super.beforeAll(context); - - injectFields(context, null, ReflectionUtils::isStatic); - } - - @Override - public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - return super.supportsParameter(parameterContext, extensionContext) || parameterContext.isAnnotated(Template.class); - } - - @Override - public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - - if (parameterContext.getParameter().getAnnotation(Template.class) == null) { - return super.resolveParameter(parameterContext, extensionContext); - } - - Class parameterType = parameterContext.getParameter().getType(); - return getMongoTemplate(parameterType, parameterContext.getParameter().getAnnotation(Template.class), - extensionContext); - } - - private void injectFields(ExtensionContext context, Object testInstance, Predicate predicate) { - - AnnotationUtils.findAnnotatedFields(context.getRequiredTestClass(), Template.class, predicate).forEach(field -> { - - assertValidFieldCandidate(field); - - try { - - ReflectionUtils.makeAccessible(field).set(testInstance, - getMongoTemplate(field.getType(), field.getAnnotation(Template.class), context)); - } catch (Throwable t) { - ExceptionUtils.throwAsUncheckedException(t); - } - }); - } - - private void assertValidFieldCandidate(Field field) { - - assertSupportedType("field", field.getType()); - } - - private void assertSupportedType(String target, Class type) { - - if (!ClassUtils.isAssignable(MongoOperations.class, type) - && !ClassUtils.isAssignable(ReactiveMongoOperations.class, type)) { - throw new ExtensionConfigurationException( - String.format("Can only resolve @%s %s of type %s or %s but was: %s", Template.class.getSimpleName(), target, - MongoOperations.class.getName(), ReactiveMongoOperations.class.getName(), type.getName())); - } - } - - private Object getMongoTemplate(Class type, Template options, ExtensionContext extensionContext) { - - Store templateStore = extensionContext.getStore(MongoExtensions.Termplate.NAMESPACE); - - boolean replSetClient = holdsReplSetClient(extensionContext) || options.replicaSet(); - - String dbName = StringUtils.isNotBlank(options.database()) ? options.database() - : extensionContext.getTestClass().map(it -> { - List target = ParsingUtils.splitCamelCaseToLower(it.getSimpleName()); - return org.springframework.util.StringUtils.collectionToDelimitedString(target, "-"); - }).orElse(DEFAULT_DATABASE); - - if (ClassUtils.isAssignable(MongoOperations.class, type)) { - - String key = Termplate.SYNC + "-" + dbName; - return templateStore.getOrComputeIfAbsent(key, it -> { - - com.mongodb.client.MongoClient client = (com.mongodb.client.MongoClient) getMongoClient( - com.mongodb.client.MongoClient.class, extensionContext, replSetClient); - return new MongoTestTemplate(client, dbName, options.initialEntitySet()); - }); - } - - if (ClassUtils.isAssignable(ReactiveMongoOperations.class, type)) { - - String key = Termplate.REACTIVE + "-" + dbName; - return templateStore.getOrComputeIfAbsent(key, it -> { - - com.mongodb.reactivestreams.client.MongoClient client = (com.mongodb.reactivestreams.client.MongoClient) getMongoClient( - com.mongodb.reactivestreams.client.MongoClient.class, extensionContext, replSetClient); - return new ReactiveMongoTestTemplate(client, dbName, options.initialEntitySet()); - }); - } - - throw new IllegalStateException("Damn - something went wrong."); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplate.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplate.java deleted file mode 100644 index ff1363965d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplate.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2020-2021 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.test.util; - -import java.util.Arrays; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.bson.Document; -import org.springframework.context.ApplicationContext; -import org.springframework.data.mapping.context.PersistentEntities; -import org.springframework.data.mongodb.core.MongoTemplate; - -import com.mongodb.MongoWriteException; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; - -/** - * A {@link MongoTemplate} with configuration hooks and extension suitable for tests. - * - * @author Christoph Strobl - * @since 3.0 - */ -public class MongoTestTemplate extends MongoTemplate { - - private final MongoTestTemplateConfiguration cfg; - - public MongoTestTemplate(MongoClient client, String database, Class... initialEntities) { - this(cfg -> { - cfg.configureDatabaseFactory(it -> { - - it.client(client); - it.defaultDb(database); - }); - - cfg.configureMappingContext(it -> { - - it.autocreateIndex(false); - it.intitalEntitySet(initialEntities); - }); - }); - } - - public MongoTestTemplate(Consumer cfg) { - - this(new Supplier() { - @Override - public MongoTestTemplateConfiguration get() { - - MongoTestTemplateConfiguration config = new MongoTestTemplateConfiguration(); - cfg.accept(config); - return config; - } - }); - } - - public MongoTestTemplate(Supplier config) { - this(config.get()); - } - - public MongoTestTemplate(MongoTestTemplateConfiguration config) { - super(config.databaseFactory(), config.mongoConverter()); - - ApplicationContext applicationContext = config.getApplicationContext(); - if (applicationContext != null) { - setApplicationContext(applicationContext); - } - - this.cfg = config; - } - - public void flush() { - flush(PersistentEntities.of(getConverter().getMappingContext()).stream().map(it -> getCollectionName(it.getType())) - .collect(Collectors.toList())); - } - - public void flushDatabase() { - flush(getDb().listCollectionNames()); - } - - public void flush(Iterable collections) { - - for (String collection : collections) { - MongoCollection mongoCollection = getCollection(collection); - try { - mongoCollection.deleteMany(new Document()); - } catch (MongoWriteException e) { - mongoCollection.drop(); - } - } - } - - public void flush(Class... entities) { - flush(Arrays.asList(entities).stream().map(this::getCollectionName).collect(Collectors.toList())); - } - - public void flush(String... collections) { - flush(Arrays.asList(collections)); - } - - public void flush(Object... objects) { - - flush(Arrays.asList(objects).stream().map(it -> { - - if (it instanceof String) { - return (String) it; - } - if (it instanceof Class) { - return getCollectionName((Class) it); - } - return it.toString(); - }).collect(Collectors.toList())); - } - - public void dropDatabase() { - getDb().drop(); - } - - public void dropIndexes(String... collections) { - for (String collection : collections) { - getCollection(collection).dropIndexes(); - } - } - - public void dropIndexes(Class... entities) { - for (Class entity : entities) { - getCollection(getCollectionName(entity)).dropIndexes(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java deleted file mode 100644 index 0f90bd2b9c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2020-2021 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.test.util; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationListener; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.auditing.IsNewAwareAuditingHandler; -import org.springframework.data.convert.CustomConversions; -import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mongodb.MongoDatabaseFactory; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.MongoConverter; -import org.springframework.data.mongodb.core.convert.MongoCustomConversions; -import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener; -import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent; -import org.springframework.lang.Nullable; - -/** - * @author Christoph Strobl - * @since 3.0 - */ -public class MongoTestTemplateConfiguration { - - private final DatabaseFactoryConfigurer dbFactoryConfig = new DatabaseFactoryConfigurer(); - private final MappingContextConfigurer mappingContextConfigurer = new MappingContextConfigurer(); - private final MongoConverterConfigurer mongoConverterConfigurer = new MongoConverterConfigurer(); - private final AuditingConfigurer auditingConfigurer = new AuditingConfigurer(); - private final ApplicationContextConfigurer applicationContextConfigurer = new ApplicationContextConfigurer(); - - private MongoMappingContext mappingContext; - private MappingMongoConverter converter; - private ApplicationContext context; - - private com.mongodb.client.MongoClient syncClient; - private com.mongodb.reactivestreams.client.MongoClient reactiveClient; - private MongoDatabaseFactory syncFactory; - private SimpleReactiveMongoDatabaseFactory reactiveFactory; - - MongoConverter mongoConverter() { - - if (converter == null) { - - converter = new MappingMongoConverter(new DefaultDbRefResolver(databaseFactory()), mappingContext()); - - if (mongoConverterConfigurer.customConversions != null) { - converter.setCustomConversions(mongoConverterConfigurer.customConversions); - } - converter.afterPropertiesSet(); - } - - return converter; - } - - List> getApplicationEventListener() { - - ArrayList> listeners = new ArrayList<>(applicationContextConfigurer.listeners); - if (auditingConfigurer.hasAuditingHandler()) { - listeners.add(new AuditingEventListener(() -> auditingConfigurer.auditingHandlers(mappingContext()))); - } - return listeners; - } - - @Nullable - ApplicationContext getApplicationContext() { - - if (applicationContextConfigurer.applicationContext == null) { - return null; - } - - if (context != null) { - return context; - } - - context = applicationContextConfigurer.applicationContext; - - if (context instanceof ConfigurableApplicationContext) { - - ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) this.context; - getApplicationEventListener().forEach(configurableApplicationContext::addApplicationListener); - - configurableApplicationContext.refresh(); - } - return context; - } - - MongoMappingContext mappingContext() { - - if (mappingContext == null) { - - mappingContext = new MongoMappingContext(); - mappingContext.setInitialEntitySet(mappingContextConfigurer.initialEntitySet()); - mappingContext.setAutoIndexCreation(mappingContextConfigurer.autocreateIndex); - mappingContext.afterPropertiesSet(); - } - - return mappingContext; - } - - MongoDatabaseFactory databaseFactory() { - - if (syncFactory == null) { - syncFactory = new SimpleMongoClientDatabaseFactory(syncClient(), defaultDatabase()); - } - - return syncFactory; - } - - ReactiveMongoDatabaseFactory reactiveDatabaseFactory() { - - if (reactiveFactory == null) { - reactiveFactory = new SimpleReactiveMongoDatabaseFactory(reactiveClient(), defaultDatabase()); - } - - return reactiveFactory; - } - - public MongoTestTemplateConfiguration configureDatabaseFactory(Consumer dbFactory) { - - dbFactory.accept(dbFactoryConfig); - return this; - } - - public MongoTestTemplateConfiguration configureMappingContext( - Consumer mappingContextConfigurerConsumer) { - mappingContextConfigurerConsumer.accept(mappingContextConfigurer); - return this; - } - - public MongoTestTemplateConfiguration configureApplicationContext( - Consumer applicationContextConfigurerConsumer) { - - applicationContextConfigurerConsumer.accept(applicationContextConfigurer); - return this; - } - - public MongoTestTemplateConfiguration configureAuditing(Consumer auditingConfigurerConsumer) { - - auditingConfigurerConsumer.accept(auditingConfigurer); - return this; - } - - public MongoTestTemplateConfiguration configureConversion( - Consumer mongoConverterConfigurerConsumer) { - - mongoConverterConfigurerConsumer.accept(mongoConverterConfigurer); - return this; - } - - com.mongodb.client.MongoClient syncClient() { - - if (syncClient == null) { - syncClient = dbFactoryConfig.syncClient != null ? dbFactoryConfig.syncClient : MongoTestUtils.client(); - } - - return syncClient; - } - - com.mongodb.reactivestreams.client.MongoClient reactiveClient() { - - if (reactiveClient == null) { - reactiveClient = dbFactoryConfig.reactiveClient != null ? dbFactoryConfig.reactiveClient - : MongoTestUtils.reactiveClient(); - } - - return reactiveClient; - } - - String defaultDatabase() { - return dbFactoryConfig.defaultDatabase != null ? dbFactoryConfig.defaultDatabase : "test"; - } - - public static class DatabaseFactoryConfigurer { - - com.mongodb.client.MongoClient syncClient; - com.mongodb.reactivestreams.client.MongoClient reactiveClient; - String defaultDatabase; - - public void client(com.mongodb.client.MongoClient client) { - this.syncClient = client; - } - - public void client(com.mongodb.reactivestreams.client.MongoClient client) { - this.reactiveClient = client; - } - - public void defaultDb(String defaultDatabase) { - this.defaultDatabase = defaultDatabase; - } - } - - public static class MongoConverterConfigurer { - - CustomConversions customConversions; - - public void customConversions(CustomConversions customConversions) { - this.customConversions = customConversions; - } - - public void customConverters(Converter... converters) { - customConversions(new MongoCustomConversions(Arrays.asList(converters))); - } - } - - public static class MappingContextConfigurer { - - Set> intitalEntitySet; - boolean autocreateIndex = false; - - public void autocreateIndex(boolean autocreateIndex) { - this.autocreateIndex = autocreateIndex; - } - - public void intitalEntitySet(Set> intitalEntitySet) { - this.intitalEntitySet = intitalEntitySet; - } - - public void intitalEntitySet(Class... initialEntitySet) { - this.intitalEntitySet = new HashSet<>(Arrays.asList(initialEntitySet)); - } - - Set> initialEntitySet() { - return intitalEntitySet != null ? intitalEntitySet : Collections.emptySet(); - } - } - - public static class AuditingConfigurer { - - Function auditingHandlerFunction; - - public void auditingHandler(Function auditingHandlerFunction) { - this.auditingHandlerFunction = auditingHandlerFunction; - } - - IsNewAwareAuditingHandler auditingHandlers(MongoMappingContext mongoMappingContext) { - return auditingHandlerFunction.apply(mongoMappingContext); - } - - boolean hasAuditingHandler() { - return auditingHandlerFunction != null; - } - } - - public static class ApplicationContextConfigurer { - - List>> listeners = new ArrayList<>(); - ApplicationContext applicationContext; - - public void applicationContext(ApplicationContext context) { - this.applicationContext = context; - } - - public void addEventListener(ApplicationListener> listener) { - this.listeners.add(listener); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java deleted file mode 100644 index 5a41e8a68c..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestUtils.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2018-2021 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.test.util; - -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.time.Duration; -import java.util.List; - -import org.bson.Document; -import org.springframework.core.env.Environment; -import org.springframework.core.env.StandardEnvironment; -import org.springframework.data.mongodb.SpringDataMongoDB; -import org.springframework.data.util.Version; -import org.springframework.util.ObjectUtils; - -import com.mongodb.ConnectionString; -import com.mongodb.ReadPreference; -import com.mongodb.WriteConcern; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import com.mongodb.reactivestreams.client.MongoClients; -import reactor.util.retry.Retry; - -/** - * Utility to create (and reuse) imperative and reactive {@code MongoClient} instances. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -public class MongoTestUtils { - - private static final Environment ENV = new StandardEnvironment(); - private static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(10); - - public static final String CONNECTION_STRING = "mongodb://127.0.0.1:27017/?replicaSet=rs0&w=majority&uuidrepresentation=javaLegacy"; - - private static final String CONNECTION_STRING_PATTERN = "mongodb://%s:%s/?w=majority&uuidrepresentation=javaLegacy"; - - private static final Version ANY = new Version(9999, 9999, 9999); - - /** - * Create a new {@link com.mongodb.client.MongoClient} with defaults. - * - * @return new instance of {@link com.mongodb.client.MongoClient}. - */ - public static MongoClient client() { - return client("127.0.0.1", 27017); - } - - public static MongoClient client(String host, int port) { - - ConnectionString connectionString = new ConnectionString(String.format(CONNECTION_STRING_PATTERN, host, port)); - return com.mongodb.client.MongoClients.create(connectionString, SpringDataMongoDB.driverInformation()); - } - - /** - * Create a new {@link com.mongodb.reactivestreams.client.MongoClient} with defaults. - * - * @return new instance of {@link com.mongodb.reactivestreams.client.MongoClient}. - */ - public static com.mongodb.reactivestreams.client.MongoClient reactiveClient() { - return reactiveClient("127.0.0.1", 27017); - } - - public static com.mongodb.reactivestreams.client.MongoClient reactiveClient(String host, int port) { - - ConnectionString connectionString = new ConnectionString(String.format(CONNECTION_STRING_PATTERN, host, port)); - return MongoClients.create(connectionString, SpringDataMongoDB.driverInformation()); - } - - /** - * Create a {@link com.mongodb.client.MongoCollection} if it does not exist, or drop and recreate it if it does. - * - * @param dbName must not be {@literal null}. - * @param collectionName must not be {@literal null}. - * @param client must not be {@literal null}. - */ - public static MongoCollection createOrReplaceCollection(String dbName, String collectionName, - com.mongodb.client.MongoClient client) { - - MongoDatabase database = client.getDatabase(dbName).withWriteConcern(WriteConcern.MAJORITY) - .withReadPreference(ReadPreference.primary()); - - boolean collectionExists = database.listCollections().filter(new Document("name", collectionName)).first() != null; - - if (collectionExists) { - - database.getCollection(collectionName).drop(); - giveTheServerALittleTimeToThink(); - } - - database.createCollection(collectionName); - giveTheServerALittleTimeToThink(); - - return database.getCollection(collectionName); - } - - /** - * Create a {@link com.mongodb.client.MongoCollection} if it does not exist, or drop and recreate it if it does. - * - * @param dbName must not be {@literal null}. - * @param collectionName must not be {@literal null}. - * @param client must not be {@literal null}. - */ - public static Mono createOrReplaceCollection(String dbName, String collectionName, - com.mongodb.reactivestreams.client.MongoClient client) { - - com.mongodb.reactivestreams.client.MongoDatabase database = client.getDatabase(dbName) - .withWriteConcern(WriteConcern.MAJORITY).withReadPreference(ReadPreference.primary()); - - return Mono.from(database.getCollection(collectionName).drop()) // - .delayElement(getTimeout()) // server replication time - .then(Mono.from(database.createCollection(collectionName))) // - .delayElement(getTimeout()); // server replication time - } - - /** - * Create a {@link com.mongodb.client.MongoCollection} if it does not exist, or drop and recreate it if it does and - * verify operation result. - * - * @param dbName must not be {@literal null}. - * @param collectionName must not be {@literal null}. - * @param client must not be {@literal null}. - */ - public static void createOrReplaceCollectionNow(String dbName, String collectionName, - com.mongodb.reactivestreams.client.MongoClient client) { - - createOrReplaceCollection(dbName, collectionName, client) // - .as(StepVerifier::create) // - .verifyComplete(); - } - - /** - * Create a {@link com.mongodb.client.MongoCollection} if it does not exist, or drop and recreate it if it does and - * verify operation result. - * - * @param dbName must not be {@literal null}. - * @param collectionName must not be {@literal null}. - * @param client must not be {@literal null}. - */ - public static void dropCollectionNow(String dbName, String collectionName, - com.mongodb.reactivestreams.client.MongoClient client) { - - com.mongodb.reactivestreams.client.MongoDatabase database = client.getDatabase(dbName) - .withWriteConcern(WriteConcern.MAJORITY).withReadPreference(ReadPreference.primary()); - - Mono.from(database.getCollection(collectionName).drop()) // - .delayElement(getTimeout()).retryWhen(Retry.backoff(3, Duration.ofMillis(250))) // - .as(StepVerifier::create) // - .verifyComplete(); - } - - /** - * Remove all documents from the {@link MongoCollection} with given name in the according {@link MongoDatabase - * database}. - * - * @param dbName must not be {@literal null}. - * @param collectionName must not be {@literal null}. - * @param client must not be {@literal null}. - */ - public static void flushCollection(String dbName, String collectionName, - com.mongodb.reactivestreams.client.MongoClient client) { - - com.mongodb.reactivestreams.client.MongoDatabase database = client.getDatabase(dbName) - .withWriteConcern(WriteConcern.MAJORITY).withReadPreference(ReadPreference.primary()); - - Mono.from(database.getCollection(collectionName).deleteMany(new Document())) // - .delayElement(getTimeout()).then() // - .as(StepVerifier::create) // - .verifyComplete(); - } - - public static void flushCollection(String dbName, String collectionName, - com.mongodb.client.MongoClient client) { - - com.mongodb.client.MongoDatabase database = client.getDatabase(dbName) - .withWriteConcern(WriteConcern.MAJORITY).withReadPreference(ReadPreference.primary()); - - database.getCollection(collectionName).deleteMany(new Document()); - } - - /** - * Create a new {@link com.mongodb.client.MongoClient} with defaults suitable for replica set usage. - * - * @return new instance of {@link com.mongodb.client.MongoClient}. - */ - public static com.mongodb.client.MongoClient replSetClient() { - return com.mongodb.client.MongoClients.create(CONNECTION_STRING); - } - - /** - * Create a new {@link com.mongodb.reactivestreams.client.MongoClient} with defaults suitable for replica set usage. - * - * @return new instance of {@link com.mongodb.reactivestreams.client.MongoClient}. - */ - public static com.mongodb.reactivestreams.client.MongoClient reactiveReplSetClient() { - return MongoClients.create(CONNECTION_STRING); - } - - /** - * @return the server version extracted from buildInfo. - * @since 3.0 - */ - public static Version serverVersion() { - - try (MongoClient client = client()) { - - MongoDatabase database = client.getDatabase("test"); - Document result = database.runCommand(new Document("buildInfo", 1)); - - return Version.parse(result.get("version", String.class)); - } catch (Exception e) { - return ANY; - } - } - - /** - * @return check if the server is running as part of a replica set. - * @since 3.0 - */ - public static boolean serverIsReplSet() { - - try (MongoClient client = MongoTestUtils.client()) { - - return client.getDatabase("admin").runCommand(new Document("getCmdLineOpts", "1")).get("argv", List.class) - .contains("--replSet"); - } catch (Exception e) { - return false; - } - } - - public static Duration getTimeout() { - - return ObjectUtils.nullSafeEquals("jenkins", ENV.getProperty("user.name")) ? Duration.ofMillis(100) - : DEFAULT_TIMEOUT; - } - - private static void giveTheServerALittleTimeToThink() { - - try { - Thread.sleep(getTimeout().toMillis()); // server replication time - } catch (InterruptedException e) { - e.printStackTrace(); - } - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersion.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersion.java deleted file mode 100644 index 0ae2cb1fe8..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersion.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017-2021 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.test.util; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.core.annotation.AliasFor; - -/** - * {@link MongoVersion} allows specifying an version range of mongodb that is applicable for a specific test method. To - * be used along with {@link MongoVersionRule} or {@link MongoServerCondition}. - * - * @author Christoph Strobl - * @since 2.1 - * @deprecated Use {@link EnableIfMongoServerVersion} instead. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Documented -@EnableIfMongoServerVersion -@Deprecated -public @interface MongoVersion { - - /** - * Inclusive lower bound of MongoDB server range. - * - * @return {@code 0.0.0} by default. - */ - @AliasFor(annotation = EnableIfMongoServerVersion.class, attribute = "isGreaterThanEqual") - String asOf() default "0.0.0"; - - /** - * Exclusive upper bound of MongoDB server range. - * - * @return {@code 9999.9999.9999} by default. - */ - @AliasFor(annotation = EnableIfMongoServerVersion.class, attribute = "isLessThan") - String until() default "9999.9999.9999"; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersionRule.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersionRule.java deleted file mode 100644 index 20df6f7799..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoVersionRule.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2014-2021 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.test.util; - -import java.util.concurrent.atomic.AtomicReference; - -import org.bson.Document; -import org.junit.AssumptionViolatedException; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.springframework.data.util.Version; -import org.springframework.test.context.junit4.SpringRunner; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoDatabase; - -/** - * {@link TestRule} verifying server tests are executed against match a given version. This one can be used as - * {@link ClassRule} eg. in context depending tests run with {@link SpringRunner} when the context would fail to start - * in case of invalid version, or as simple {@link Rule} on specific tests. - * - * @author Christoph Strobl - * @author Mark Paluch - * @since 1.6 - * @deprecated Use {@link MongoServerCondition} instead. - */ -@Deprecated -public class MongoVersionRule implements TestRule { - - private static final Version ANY = new Version(9999, 9999, 9999); - private static final Version DEFAULT_HIGH = ANY; - private static final Version DEFAULT_LOW = new Version(0, 0, 0); - - public static MongoVersionRule REQUIRES_4_2 = MongoVersionRule - .atLeast(org.springframework.data.util.Version.parse("4.2")); - - private final AtomicReference currentVersion = new AtomicReference<>(null); - private final Version minVersion; - private final Version maxVersion; - - private String host = "127.0.0.1"; - private int port = 27017; - - public MongoVersionRule(Version min, Version max) { - - this.minVersion = min; - this.maxVersion = max; - } - - public static MongoVersionRule any() { - return new MongoVersionRule(ANY, ANY); - } - - public static MongoVersionRule atLeast(Version minVersion) { - return new MongoVersionRule(minVersion, DEFAULT_HIGH); - } - - public static MongoVersionRule atMost(Version maxVersion) { - return new MongoVersionRule(DEFAULT_LOW, maxVersion); - } - - public MongoVersionRule withServerRunningAt(String host, int port) { - - this.host = host; - this.port = port; - - return this; - } - - /** - * @see Version#isGreaterThan(Version) - */ - public boolean isGreaterThan(Version version) { - return getCurrentVersion().isGreaterThan(version); - } - - /** - * @see Version#isGreaterThanOrEqualTo(Version) - */ - public boolean isGreaterThanOrEqualTo(Version version) { - return getCurrentVersion().isGreaterThanOrEqualTo(version); - } - - /** - * @see Version#is(Version) - */ - public boolean is(Version version) { - return getCurrentVersion().equals(version); - } - - /** - * @see Version#isLessThan(Version) - */ - public boolean isLessThan(Version version) { - return getCurrentVersion().isLessThan(version); - } - - /** - * @see Version#isLessThanOrEqualTo(Version) - */ - public boolean isLessThanOrEqualTo(Version version) { - return getCurrentVersion().isGreaterThanOrEqualTo(version); - } - - @Override - public Statement apply(final Statement base, Description description) { - - return new Statement() { - - @Override - public void evaluate() throws Throwable { - - if (!getCurrentVersion().equals(ANY)) { - - Version minVersion = MongoVersionRule.this.minVersion.equals(ANY) ? DEFAULT_LOW - : MongoVersionRule.this.minVersion; - Version maxVersion = MongoVersionRule.this.maxVersion.equals(ANY) ? DEFAULT_HIGH - : MongoVersionRule.this.maxVersion; - - if (description.getAnnotation(MongoVersion.class) != null) { - MongoVersion version = description.getAnnotation(MongoVersion.class); - if (version != null) { - - Version expectedMinVersion = Version.parse(version.asOf()); - if (!expectedMinVersion.equals(ANY) && !expectedMinVersion.equals(DEFAULT_LOW)) { - minVersion = expectedMinVersion; - } - - Version expectedMaxVersion = Version.parse(version.until()); - if (!expectedMaxVersion.equals(ANY) && !expectedMaxVersion.equals(DEFAULT_HIGH)) { - maxVersion = expectedMaxVersion; - } - } - } - - validateVersion(minVersion, maxVersion); - } - - base.evaluate(); - } - }; - } - - private void validateVersion(Version min, Version max) { - - if (getCurrentVersion().isLessThan(min) || getCurrentVersion().isGreaterThanOrEqualTo(max)) { - - throw new AssumptionViolatedException( - String.format("Expected MongoDB server to be in range (%s, %s] but found %s", min, max, currentVersion)); - } - - } - - private Version getCurrentVersion() { - - if (currentVersion.get() == null) { - currentVersion.compareAndSet(null, fetchCurrentVersion()); - } - - return currentVersion.get(); - } - - private Version fetchCurrentVersion() { - - try { - - MongoClient client = MongoTestUtils.client(host, port); - MongoDatabase database = client.getDatabase("test"); - Document result = database.runCommand(new Document("buildInfo", 1)); - - return Version.parse(result.get("version", String.class)); - } catch (Exception e) { - return ANY; - } - } - - @Override - public String toString() { - return getCurrentVersion().toString(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReactiveMongoTestTemplate.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReactiveMongoTestTemplate.java deleted file mode 100644 index 9e7d2bbbfa..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReactiveMongoTestTemplate.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2020-2021 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.test.util; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.Arrays; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import org.bson.Document; -import org.reactivestreams.Publisher; - -import org.springframework.context.ApplicationContext; -import org.springframework.data.mapping.context.PersistentEntities; -import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; -import org.springframework.data.mongodb.core.ReactiveMongoTemplate; - -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoCollection; -import com.mongodb.reactivestreams.client.MongoDatabase; - -/** - * A {@link ReactiveMongoTemplate} with configuration hooks and extension suitable for tests. - * - * @author Christoph Strobl - * @author Mathieu Ouellet - * @since 3.0 - */ -public class ReactiveMongoTestTemplate extends ReactiveMongoTemplate { - - private final MongoTestTemplateConfiguration cfg; - - public ReactiveMongoTestTemplate(MongoClient client, String database, Class... initialEntities) { - this(cfg -> { - cfg.configureDatabaseFactory(it -> { - - it.client(client); - it.defaultDb(database); - }); - - cfg.configureMappingContext(it -> { - - it.autocreateIndex(false); - it.intitalEntitySet(initialEntities); - }); - }); - } - - public ReactiveMongoTestTemplate(Consumer cfg) { - - this(new Supplier() { - @Override - public MongoTestTemplateConfiguration get() { - - MongoTestTemplateConfiguration config = new MongoTestTemplateConfiguration(); - cfg.accept(config); - return config; - } - }); - } - - public ReactiveMongoTestTemplate(Supplier config) { - this(config.get()); - } - - public ReactiveMongoTestTemplate(MongoTestTemplateConfiguration config) { - super(config.reactiveDatabaseFactory(), config.mongoConverter()); - - ApplicationContext applicationContext = config.getApplicationContext(); - if (applicationContext != null) { - setApplicationContext(applicationContext); - } - - this.cfg = config; - } - - public ReactiveMongoDatabaseFactory getDatabaseFactory() { - return cfg.reactiveDatabaseFactory(); - } - - public Mono flush() { - return flush(Flux.fromStream( - PersistentEntities.of(getConverter().getMappingContext()).stream().map(it -> getCollectionName(it.getType())))); - } - - public Mono flushDatabase() { - return flush(getMongoDatabase().flatMapMany(MongoDatabase::listCollectionNames)); - } - - public Mono flush(Class... entities) { - return flush(Flux.fromStream(Arrays.asList(entities).stream().map(this::getCollectionName))); - } - - public Mono flush(String... collections) { - return flush(Flux.fromArray(collections)); - } - - public Mono flush(Publisher collectionNames) { - - return Flux.from(collectionNames) - .flatMap(collection -> getCollection(collection).flatMapMany(it -> it.deleteMany(new Document())).then() - .onErrorResume(it -> getCollection(collection).flatMapMany(MongoCollection::drop).then())) - .then(); - } - - public Mono flush(Object... objects) { - - return flush(Flux.fromStream(Arrays.asList(objects).stream().map(it -> { - - if (it instanceof String) { - return (String) it; - } - if (it instanceof Class) { - return getCollectionName((Class) it); - } - return it.toString(); - }))); - } - - public Mono dropDatabase() { - return getMongoDatabase().map(MongoDatabase::drop).then(); - } - - public Mono dropIndexes(String... collections) { - return Flux.fromArray(collections).flatMap(it -> getCollection(it).map(MongoCollection::dropIndexes).then()).then(); - } - - public Mono dropIndexes(Class... entities) { - return Flux.fromArray(entities) - .flatMap(it -> getCollection(getCollectionName(it)).map(MongoCollection::dropIndexes).then()).then(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplSetClient.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplSetClient.java deleted file mode 100644 index 8d601f293d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplSetClient.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2020-2021 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.test.util; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Marks a field or method as to be autowired by JUnit's dependency injection facilities for injection of a MongoDB - * client instance connected to a replica set. Depends on {@link MongoClientExtension}. - * - * @author Christoph Strobl - * @see com.mongodb.client.MongoClient - * @see com.mongodb.reactivestreams.client.MongoClient - * @see Client - * @see MongoClientExtension - */ -@Target({ ElementType.FIELD, ElementType.PARAMETER }) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface ReplSetClient { - -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplicaSet.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplicaSet.java deleted file mode 100644 index b4c8b82deb..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/ReplicaSet.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2018-2021 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.test.util; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import org.bson.Document; -import org.junit.AssumptionViolatedException; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.springframework.test.annotation.IfProfileValue; - -import com.mongodb.client.MongoClient; - -/** - * {@link TestRule} evaluating if MongoDB Server is running with {@code --replSet} flag. - * - * @author Christoph Strobl - * @deprecated Use {@link MongoServerCondition} with {@link EnableIfReplicaSetAvailable} instead. - */ -@Deprecated -public class ReplicaSet implements TestRule { - - boolean required = false; - AtomicReference runsAsReplicaSet = new AtomicReference<>(); - - private ReplicaSet(boolean required) { - this.required = required; - } - - /** - * A MongoDB server running with {@code --replSet} flag is required to execute tests. - * - * @return new instance of {@link ReplicaSet}. - */ - public static ReplicaSet required() { - return new ReplicaSet(true); - } - - /** - * A MongoDB server running with {@code --replSet} flag might be required to execute some tests. Those tests are - * marked with {@code @IfProfileValue(name="replSet", value="true")}. - * - * @return new instance of {@link ReplicaSet}. - */ - public static ReplicaSet none() { - return new ReplicaSet(false); - } - - @Override - public Statement apply(Statement base, Description description) { - - return new Statement() { - - @Override - public void evaluate() throws Throwable { - - if (!required) { - - IfProfileValue profileValue = description.getAnnotation(IfProfileValue.class); - if (profileValue == null || !profileValue.name().equalsIgnoreCase("replSet")) { - base.evaluate(); - return; - } - - if (!Boolean.valueOf(profileValue.value())) { - base.evaluate(); - return; - } - } - - if (!runsAsReplicaSet()) { - throw new AssumptionViolatedException("Not running in repl set mode"); - } - base.evaluate(); - } - }; - } - - public boolean runsAsReplicaSet() { - - if (runsAsReplicaSet.get() == null) { - - MongoClient client = MongoTestUtils.client(); - - boolean tmp = client.getDatabase("admin").runCommand(new Document("getCmdLineOpts", "1")).get("argv", List.class) - .contains("--replSet"); - runsAsReplicaSet.compareAndSet(null, tmp); - } - return runsAsReplicaSet.get(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/Template.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/Template.java deleted file mode 100644 index df05d59a1d..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/Template.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2020-2021 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.test.util; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.ExtensionContext; - -/** - * Annotation to inject {@link org.springframework.data.mongodb.core.MongoOperations} and - * {@link org.springframework.data.mongodb.core.ReactiveMongoOperations} parameters as method arguments and into - * {@code static} fields. - * - * @author Christoph Strobl - * @since 3.0 - * @see MongoTemplateExtension - */ -@Target({ ElementType.FIELD, ElementType.PARAMETER }) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@ExtendWith(MongoTemplateExtension.class) -public @interface Template { - - /** - * @return name of the database to use. Use empty String to generate the database name for the - * {@link ExtensionContext#getTestClass() test class}. - */ - String database() default ""; - - /** - * Pre-initialize the {@link org.springframework.data.mapping.context.MappingContext} with the given entities. - * - * @return empty by default. - */ - Class[] initialEntitySet() default {}; - - /** - * Use a {@link ReplSetClient} if {@literal true}. - * - * @return false by default. - */ - boolean replicaSet() default false; -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/BsonUtilsTest.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/BsonUtilsTest.java deleted file mode 100644 index 8210dd9a6f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/BsonUtilsTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2020-2021 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.util.json; - -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.bson.BsonDouble; -import org.bson.BsonInt32; -import org.bson.BsonInt64; -import org.bson.BsonObjectId; -import org.bson.BsonString; -import org.bson.Document; -import org.bson.types.ObjectId; -import org.junit.jupiter.api.Test; -import org.springframework.data.mongodb.util.BsonUtils; - -/** - * @author Christoph Strobl - */ -class BsonUtilsTest { - - @Test // DATAMONGO-625 - void simpleToBsonValue() { - - assertThat(BsonUtils.simpleToBsonValue(Long.valueOf(10))).isEqualTo(new BsonInt64(10)); - assertThat(BsonUtils.simpleToBsonValue(new Integer(10))).isEqualTo(new BsonInt32(10)); - assertThat(BsonUtils.simpleToBsonValue(Double.valueOf(0.1D))).isEqualTo(new BsonDouble(0.1D)); - assertThat(BsonUtils.simpleToBsonValue("value")).isEqualTo(new BsonString("value")); - } - - @Test // DATAMONGO-625 - void primitiveToBsonValue() { - assertThat(BsonUtils.simpleToBsonValue(10L)).isEqualTo(new BsonInt64(10)); - } - - @Test // DATAMONGO-625 - void objectIdToBsonValue() { - - ObjectId source = new ObjectId(); - assertThat(BsonUtils.simpleToBsonValue(source)).isEqualTo(new BsonObjectId(source)); - } - - @Test // DATAMONGO-625 - void bsonValueToBsonValue() { - - BsonObjectId source = new BsonObjectId(new ObjectId()); - assertThat(BsonUtils.simpleToBsonValue(source)).isSameAs(source); - } - - @Test // DATAMONGO-625 - void unsupportedToBsonValue() { - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> BsonUtils.simpleToBsonValue(new Object())); - } - - @Test // GH-3571 - void removeNullIdIfNull() { - - Document source = new Document("_id", null).append("value", "v-1"); - - assertThat(BsonUtils.removeNullId(source)).isTrue(); - assertThat(source).doesNotContainKey("_id").containsKey("value"); - } - - @Test // GH-3571 - void removeNullIdDoesNotTouchNonNullOn() { - - Document source = new Document("_id", "id-value").append("value", "v-1"); - - assertThat(BsonUtils.removeNullId(source)).isFalse(); - assertThat(source).containsKeys("_id", "value"); - } - - @Test // GH-3571 - void asCollectionDoesNotModifyCollection() { - - Object source = new ArrayList<>(0); - - assertThat(BsonUtils.asCollection(source)).isSameAs(source); - } - - @Test // GH-3571 - void asCollectionConvertsArrayToCollection() { - - Object source = new String[]{"one", "two"}; - - assertThat((Collection)BsonUtils.asCollection(source)).containsExactly("one", "two"); - } - - @Test // GH-3571 - void asCollectionConvertsWrapsNonIterable() { - - Object source = 100L; - - assertThat((Collection)BsonUtils.asCollection(source)).containsExactly(source); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java deleted file mode 100644 index 72ab2b454b..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright 2019-2021 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.util.json; - -import static org.assertj.core.api.Assertions.*; - -import lombok.AllArgsConstructor; -import lombok.Data; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import org.bson.Document; -import org.bson.codecs.DecoderContext; -import org.junit.jupiter.api.Test; -import org.springframework.data.spel.EvaluationContextProvider; -import org.springframework.data.spel.ExpressionDependencies; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.TypedValue; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; - -/** - * Unit tests for {@link ParameterBindingJsonReader}. - * - * @author Christoph Strobl - * @author Mark Paluch - */ -class ParameterBindingJsonReaderUnitTests { - - @Test - void bindUnquotedStringValue() { - - Document target = parse("{ 'lastname' : ?0 }", "kohlin"); - assertThat(target).isEqualTo(new Document("lastname", "kohlin")); - } - - @Test - void bindQuotedStringValue() { - - Document target = parse("{ 'lastname' : '?0' }", "kohlin"); - assertThat(target).isEqualTo(new Document("lastname", "kohlin")); - } - - @Test - void bindUnquotedIntegerValue() { - - Document target = parse("{ 'lastname' : ?0 } ", 100); - assertThat(target).isEqualTo(new Document("lastname", 100)); - } - - @Test - void bindMultiplePlacholders() { - - Document target = parse("{ 'lastname' : ?0, 'firstname' : '?1' }", "Kohlin", "Dalinar"); - assertThat(target).isEqualTo(Document.parse("{ 'lastname' : 'Kohlin', 'firstname' : 'Dalinar' }")); - } - - @Test - void bindQuotedIntegerValue() { - - Document target = parse("{ 'lastname' : '?0' }", 100); - assertThat(target).isEqualTo(new Document("lastname", "100")); - } - - @Test - void bindValueToRegex() { - - Document target = parse("{ 'lastname' : { '$regex' : '^(?0)'} }", "kohlin"); - assertThat(target).isEqualTo(Document.parse("{ 'lastname' : { '$regex' : '^(kohlin)'} }")); - } - - @Test - void bindValueToMultiRegex() { - - Document target = parse( - "{'$or' : [{'firstname': {'$regex': '.*?0.*', '$options': 'i'}}, {'lastname' : {'$regex': '.*?0xyz.*', '$options': 'i'}} ]}", - "calamity"); - assertThat(target).isEqualTo(Document.parse( - "{ \"$or\" : [ { \"firstname\" : { \"$regex\" : \".*calamity.*\" , \"$options\" : \"i\"}} , { \"lastname\" : { \"$regex\" : \".*calamityxyz.*\" , \"$options\" : \"i\"}}]}")); - } - - @Test - void bindMultipleValuesToSingleToken() { - - Document target = parse("{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}", 0, 1, 2, - 3, 4); - assertThat(target) - .isEqualTo(Document.parse("{$where: 'return this.date.getUTCMonth() == 2 && this.date.getUTCDay() == 3;'}")); - } - - @Test - void bindValueToDbRef() { - - Document target = parse("{ 'reference' : { $ref : 'reference', $id : ?0 }}", "kohlin"); - assertThat(target).isEqualTo(Document.parse("{ 'reference' : { $ref : 'reference', $id : 'kohlin' }}")); - } - - @Test - void bindToKey() { - - Document target = parse("{ ?0 : ?1 }", "firstname", "kaladin"); - assertThat(target).isEqualTo(Document.parse("{ 'firstname' : 'kaladin' }")); - } - - @Test - void bindListValue() { - - // - Document target = parse("{ 'lastname' : { $in : ?0 } }", Arrays.asList("Kohlin", "Davar")); - assertThat(target).isEqualTo(Document.parse("{ 'lastname' : { $in : ['Kohlin', 'Davar' ]} }")); - } - - @Test - void bindListOfBinaryValue() { - - // - byte[] value = "Kohlin".getBytes(StandardCharsets.UTF_8); - List args = Collections.singletonList(value); - - Document target = parse("{ 'lastname' : { $in : ?0 } }", args); - assertThat(target).isEqualTo(new Document("lastname", new Document("$in", args))); - } - - @Test - void bindExtendedExpression() { - - Document target = parse("{'id':?#{ [0] ? { $exists :true} : [1] }}", true, "firstname", "kaladin"); - assertThat(target).isEqualTo(Document.parse("{ \"id\" : { \"$exists\" : true}}")); - } - - // {'id':?#{ [0] ? { $exists :true} : [1] }} - - @Test - void bindDocumentValue() { - - // - Document target = parse("{ 'lastname' : ?0 }", new Document("$eq", "Kohlin")); - assertThat(target).isEqualTo(Document.parse("{ 'lastname' : { '$eq' : 'Kohlin' } }")); - } - - @Test - void arrayWithoutBinding() { - - // - Document target = parse("{ 'lastname' : { $in : [\"Kohlin\", \"Davar\"] } }"); - assertThat(target).isEqualTo(Document.parse("{ 'lastname' : { $in : ['Kohlin', 'Davar' ]} }")); - } - - @Test - void bindSpEL() { - - // "{ arg0 : ?#{[0]} }" - Document target = parse("{ arg0 : ?#{[0]} }", 100.01D); - assertThat(target).isEqualTo(new Document("arg0", 100.01D)); - } - - @Test // DATAMONGO-2315 - void bindDateAsDate() { - - Date date = new Date(); - Document target = parse("{ 'end_date' : { $gte : { $date : ?0 } } }", date); - - assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : " + date.getTime() + " } } } ")); - } - - @Test // DATAMONGO-2315 - void bindQuotedDateAsDate() { - - Date date = new Date(); - Document target = parse("{ 'end_date' : { $gte : { $date : '?0' } } }", date); - - assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : " + date.getTime() + " } } } ")); - } - - @Test // DATAMONGO-2315 - void bindStringAsDate() { - - Date date = new Date(); - Document target = parse("{ 'end_date' : { $gte : { $date : ?0 } } }", "2019-07-04T12:19:23.000Z"); - - assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : '2019-07-04T12:19:23.000Z' } } } ")); - } - - @Test // DATAMONGO-2315 - void bindNumberAsDate() { - - Long time = new Date().getTime(); - Document target = parse("{ 'end_date' : { $gte : { $date : ?0 } } }", time); - - assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : " + time + " } } } ")); - } - - @Test // DATAMONGO-2418 - void shouldNotAccessSpElEvaluationContextWhenNoSpElPresentInBindableTarget() { - - Object[] args = new Object[] { "value" }; - EvaluationContext evaluationContext = new StandardEvaluationContext() { - - @Override - public TypedValue getRootObject() { - throw new RuntimeException("o_O"); - } - }; - - ParameterBindingJsonReader reader = new ParameterBindingJsonReader("{ 'name':'?0' }", - new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); - Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); - - assertThat(target).isEqualTo(new Document("name", "value")); - } - - @Test // DATAMONGO-2476 - void bindUnquotedParameterInArray() { - - Document target = parse("{ 'name' : { $in : [?0] } }", "kohlin"); - assertThat(target).isEqualTo(new Document("name", new Document("$in", Collections.singletonList("kohlin")))); - } - - @Test // DATAMONGO-2476 - void bindMultipleUnquotedParameterInArray() { - - Document target = parse("{ 'name' : { $in : [?0,?1] } }", "dalinar", "kohlin"); - assertThat(target).isEqualTo(new Document("name", new Document("$in", Arrays.asList("dalinar", "kohlin")))); - } - - @Test // DATAMONGO-2476 - void bindUnquotedParameterInArrayWithSpaces() { - - Document target = parse("{ 'name' : { $in : [ ?0 ] } }", "kohlin"); - assertThat(target).isEqualTo(new Document("name", new Document("$in", Collections.singletonList("kohlin")))); - } - - @Test // DATAMONGO-2476 - void bindQuotedParameterInArray() { - - Document target = parse("{ 'name' : { $in : ['?0'] } }", "kohlin"); - assertThat(target).isEqualTo(new Document("name", new Document("$in", Collections.singletonList("kohlin")))); - } - - @Test // DATAMONGO-2476 - void bindQuotedMulitParameterInArray() { - - Document target = parse("{ 'name' : { $in : ['?0,?1'] } }", "dalinar", "kohlin"); - assertThat(target) - .isEqualTo(new Document("name", new Document("$in", Collections.singletonList("dalinar,kohlin")))); - } - - @Test // DATAMONGO-1894 - void discoversNoDependenciesInExpression() { - - String json = "{ $and : [?#{ [0] == null ? { '$where' : 'true' } : { 'v1' : { '$in' : {[0]} } } }]}"; - - ExpressionDependencies expressionDependencies = new ParameterBindingDocumentCodec() - .captureExpressionDependencies(json, it -> new Object(), new SpelExpressionParser()); - - assertThat(expressionDependencies).isEqualTo(ExpressionDependencies.none()); - } - - @Test // DATAMONGO-1894 - void discoversCorrectlyDependenciesInExpression() { - - String json = "{ hello: ?#{hasRole('foo')} }"; - - ExpressionDependencies expressionDependencies = new ParameterBindingDocumentCodec() - .captureExpressionDependencies(json, it -> new Object(), new SpelExpressionParser()); - - assertThat(expressionDependencies).isNotEmpty(); - assertThat(expressionDependencies.get()).hasSize(1); - } - - @Test // DATAMONGO-2523 - void bindSpelExpressionInArrayCorrectly/* closing bracket must not have leading whitespace! */() { - - Document target = parse("{ $and : [?#{ [0] == null ? { '$where' : 'true' } : { 'v1' : { '$in' : {[0]} } } }]}", 1); - - assertThat(target).isEqualTo(Document.parse("{\"$and\": [{\"v1\": {\"$in\": [1]}}]}")); - } - - @Test // DATAMONGO-2545 - void shouldABindArgumentsViaIndexInSpelExpressions() { - - Object[] args = new Object[] { "yess", "nooo" }; - StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT - .getEvaluationContext(args); - - ParameterBindingJsonReader reader = new ParameterBindingJsonReader( - "{ 'isBatman' : ?#{ T(" + this.getClass().getName() + ").isBatman() ? [0] : [1] }}", - new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); - Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); - - assertThat(target).isEqualTo(new Document("isBatman", "nooo")); - } - - @Test // DATAMONGO-2545 - void shouldAllowMethodArgumentPlaceholdersInSpelExpressions/*becuase this worked before*/() { - - Object[] args = new Object[] { "yess", "nooo" }; - StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT - .getEvaluationContext(args); - - ParameterBindingJsonReader reader = new ParameterBindingJsonReader( - "{ 'isBatman' : ?#{ T(" + this.getClass().getName() + ").isBatman() ? '?0' : '?1' }}", - new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); - Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); - - assertThat(target).isEqualTo(new Document("isBatman", "nooo")); - } - - @Test // DATAMONGO-2545 - void shouldAllowMethodArgumentPlaceholdersInQuotedSpelExpressions/*because this worked before*/() { - - Object[] args = new Object[] { "yess", "nooo" }; - StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT - .getEvaluationContext(args); - - ParameterBindingJsonReader reader = new ParameterBindingJsonReader( - "{ 'isBatman' : \"?#{ T(" + this.getClass().getName() + ").isBatman() ? '?0' : '?1' }\" }", - new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); - Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); - - assertThat(target).isEqualTo(new Document("isBatman", "nooo")); - } - - @Test // DATAMONGO-2545 - void evaluatesSpelExpressionDefiningEntireQuery() { - - Object[] args = new Object[] {}; - StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT - .getEvaluationContext(args); - evaluationContext.setRootObject(new DummySecurityObject(new DummyWithId("wonderwoman"))); - - String json = "?#{ T(" + this.getClass().getName() - + ").isBatman() ? {'_class': { '$eq' : 'region' }} : { '$and' : { {'_class': { '$eq' : 'region' } }, {'user.supervisor': principal.id } } } }"; - - ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, - new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); - Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); - - assertThat(target) - .isEqualTo(new Document("$and", Arrays.asList(new Document("_class", new Document("$eq", "region")), - new Document("user.supervisor", "wonderwoman")))); - } - - @Test // DATAMONGO-2571 - void shouldParseRegexCorrectly() { - - Document target = parse("{ $and: [{'fieldA': {$in: [/ABC.*/, /CDE.*F/]}}, {'fieldB': {$ne: null}}]}"); - assertThat(target) - .isEqualTo(Document.parse("{ $and: [{'fieldA': {$in: [/ABC.*/, /CDE.*F/]}}, {'fieldB': {$ne: null}}]}")); - } - - @Test // DATAMONGO-2571 - void shouldParseRegexWithPlaceholderCorrectly() { - - Document target = parse("{ $and: [{'fieldA': {$in: [/?0.*/, /CDE.*F/]}}, {'fieldB': {$ne: null}}]}", "ABC"); - assertThat(target) - .isEqualTo(Document.parse("{ $and: [{'fieldA': {$in: [/ABC.*/, /CDE.*F/]}}, {'fieldB': {$ne: null}}]}")); - } - - @Test // DATAMONGO-2633 - void shouldParseNestedArrays() { - - Document target = parse("{ 'stores.location' : { $geoWithin: { $centerSphere: [ [ ?0, 48.799029 ] , ?1 ] } } }", - 1.948516D, 0.004D); - assertThat(target).isEqualTo(Document - .parse("{ 'stores.location' : { $geoWithin: { $centerSphere: [ [ 1.948516, 48.799029 ] , 0.004 ] } } }")); - } - - private static Document parse(String json, Object... args) { - - ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args); - return new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); - } - - // DATAMONGO-2545 - public static boolean isBatman() { - return false; - } - - @Data - @AllArgsConstructor - public static class DummySecurityObject { - DummyWithId principal; - } - - @Data - @AllArgsConstructor - public static class DummyWithId { - String id; - } - -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/Entities.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/Entities.kt deleted file mode 100644 index b9b7d6b87c..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/Entities.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2019-2021 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 org.springframework.data.annotation.Id -import org.springframework.data.annotation.LastModifiedDate -import org.springframework.data.annotation.Version -import org.springframework.data.mongodb.core.mapping.Document -import java.time.Instant - -@Document("versioned-auditable") -data class KAuditableVersionedEntity( - @Id val id: String?, - val value: String, - @Version val version: Long?, - @LastModifiedDate val modificationDate: Instant? -) { - fun withValue(value: String) = copy(value = value) -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableAggregationOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableAggregationOperationExtensionsTests.kt deleted file mode 100644 index 6e09b3773e..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableAggregationOperationExtensionsTests.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test - -/** - * @author Sebastien Deleuze - * @author Mark Paluch - */ -class ExecutableAggregationOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `aggregateAndReturn(KClass) extension should call its Java counterpart`() { - - operation.aggregateAndReturn(First::class) - verify { operation.aggregateAndReturn(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `aggregateAndReturn() with reified type parameter extension should call its Java counterpart`() { - - operation.aggregateAndReturn() - verify { operation.aggregateAndReturn(First::class.java) } - } -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableFindOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableFindOperationExtensionsTests.kt deleted file mode 100644 index c708f3e6eb..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableFindOperationExtensionsTests.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test - -/** - * @author Sebastien Deleuze - * @author Mark Paluch - */ -class ExecutableFindOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - val operationWithProjection = mockk>(relaxed = true) - - val distinctWithProjection = mockk(relaxed = true) - - val findDistinct = mockk(relaxed = true) - - val executableFind = mockk>(relaxed = true) - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `ExecutableFindOperation#query(KClass) extension should call its Java counterpart`() { - - operation.query(First::class) - verify { operation.query(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `ExecutableFindOperation#query() with reified type parameter extension should call its Java counterpart`() { - - operation.query() - verify { operation.query(First::class.java) } - } - - @Test // DATAMONGO-1689, DATAMONGO-2086 - @Suppress("DEPRECATION") - fun `ExecutableFindOperation#FindOperationWithProjection#asType(KClass) extension should call its Java counterpart`() { - - operationWithProjection.asType(User::class) - verify { operationWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-1689, DATAMONGO-2086 - fun `ExecutableFindOperation#FindOperationWithProjection#asType() with reified type parameter extension should call its Java counterpart`() { - - operationWithProjection.asType() - verify { operationWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-1761, DATAMONGO-2086 - @Suppress("DEPRECATION") - fun `ExecutableFindOperation#DistinctWithProjection#asType(KClass) extension should call its Java counterpart`() { - - distinctWithProjection.asType(User::class) - verify { distinctWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-2086 - fun `ExecutableFindOperation#DistinctWithProjection#asType() with reified type parameter extension should call its Java counterpart`() { - - distinctWithProjection.asType() - verify { distinctWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-2417 - fun `ExecutableFindOperation#distrinct() using KProperty1 should call its Java counterpart`() { - - every { operation.query(KotlinUser::class.java) } returns executableFind - - operation.distinct(KotlinUser::username) - verify { - operation.query(KotlinUser::class.java) - executableFind.distinct("username") - } - } - - @Test // DATAMONGO-2417 - fun `ExecutableFindOperation#FindDistinct#field() using KProperty should call its Java counterpart`() { - - findDistinct.distinct(KotlinUser::username) - verify { findDistinct.distinct("username") } - } - - data class KotlinUser(val username: String) -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableInsertOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableInsertOperationExtensionsTests.kt deleted file mode 100644 index f10e8ee185..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableInsertOperationExtensionsTests.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test - -/** - * @author Sebastien Deleuze - * @author Mark Paluch - */ -class ExecutableInsertOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `insert(KClass) extension should call its Java counterpart`() { - - operation.insert(First::class) - verify { operation.insert(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `insert() with reified type parameter extension should call its Java counterpart`() { - - operation.insert() - verify { operation.insert(First::class.java) } - } - -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableMapReduceOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableMapReduceOperationExtensionsTests.kt deleted file mode 100644 index 76fdde3315..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableMapReduceOperationExtensionsTests.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2018-2021 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 example.first.First -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test - -/** - * @author Christoph Strobl - * @author Sebastien Deleuze - */ -class ExecutableMapReduceOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - val operationWithProjection = mockk>(relaxed = true) - - @Test // DATAMONGO-1929 - @Suppress("DEPRECATION") - fun `ExecutableMapReduceOperation#mapReduce(KClass) extension should call its Java counterpart`() { - - operation.mapReduce(First::class) - verify { operation.mapReduce(First::class.java) } - } - - @Test // DATAMONGO-1929 - fun `ExecutableMapReduceOperation#mapReduce() with reified type parameter extension should call its Java counterpart`() { - - operation.mapReduce() - verify { operation.mapReduce(First::class.java) } - } - - @Test // DATAMONGO-1929, DATAMONGO-2086 - @Suppress("DEPRECATION") - fun `ExecutableMapReduceOperation#MapReduceWithProjection#asType(KClass) extension should call its Java counterpart`() { - - operationWithProjection.asType(User::class) - verify { operationWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-1929, DATAMONGO-2086 - fun `ExecutableMapReduceOperation#MapReduceWithProjection#asType() with reified type parameter extension should call its Java counterpart`() { - - operationWithProjection.asType() - verify { operationWithProjection.`as`(User::class.java) } - } -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableRemoveOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableRemoveOperationExtensionsTests.kt deleted file mode 100644 index 235b9a8564..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableRemoveOperationExtensionsTests.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test - -/** - * @author Sebastien Deleuze - * @author Mark Paluch - */ -class ExecutableRemoveOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `remove(KClass) extension should call its Java counterpart`() { - - operation.remove(First::class) - verify { operation.remove(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `remove() with reified type parameter extension should call its Java counterpart`() { - - operation.remove() - verify { operation.remove(First::class.java) } - } - -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableUpdateOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableUpdateOperationExtensionsTests.kt deleted file mode 100644 index f7e74ae312..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ExecutableUpdateOperationExtensionsTests.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test - -/** - * Unit tests for `ExecutableUpdateOperationExtensions.kt`. - * - * @author Christoph Strobl - * @author Sebastien Deleuze - */ -class ExecutableUpdateOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - @Test // DATAMONGO-1719 - @Suppress("DEPRECATION") - fun `update(KClass) extension should call its Java counterpart`() { - - operation.update(First::class) - verify { operation.update(First::class.java) } - } - - @Test // DATAMONGO-1719 - fun `update() with reified type parameter extension should call its Java counterpart`() { - - operation.update() - verify { operation.update(First::class.java) } - } -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensionsTests.kt deleted file mode 100644 index 7e8eb71c24..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensionsTests.kt +++ /dev/null @@ -1,814 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import example.second.Second -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.springframework.data.mongodb.core.BulkOperations.BulkMode -import org.springframework.data.mongodb.core.aggregation.Aggregation -import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions -import org.springframework.data.mongodb.core.query.Criteria -import org.springframework.data.mongodb.core.query.NearQuery -import org.springframework.data.mongodb.core.query.Query -import org.springframework.data.mongodb.core.query.Update - -/** - * @author Sebastien Deleuze - * @author Mark Paluch - * @author Christoph Strobl - */ -class MongoOperationsExtensionsTests { - - val operations = mockk(relaxed = true) - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `getCollectionName(KClass) extension should call its Java counterpart`() { - - operations.getCollectionName(First::class) - verify { operations.getCollectionName(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `getCollectionName() with reified type parameter extension should call its Java counterpart`() { - - operations.getCollectionName() - verify { operations.getCollectionName(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `execute(CollectionCallback) with reified type parameter extension should call its Java counterpart`() { - - val collectionCallback = mockk>() - operations.execute(collectionCallback) - verify { operations.execute(First::class.java, collectionCallback) } - } - - @Test // DATAMONGO-1689 - fun `stream(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - operations.stream(query) - verify { operations.stream(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `stream(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - operations.stream(query, collectionName) - verify { operations.stream(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `createCollection(KClass) extension should call its Java counterpart`() { - - operations.createCollection(First::class) - verify { operations.createCollection(First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `createCollection(KClass, CollectionOptions) extension should call its Java counterpart`() { - - val collectionOptions = mockk() - operations.createCollection(First::class, collectionOptions) - verify { operations.createCollection(First::class.java, collectionOptions) } - } - - @Test // DATAMONGO-1689 - fun `createCollection() with reified type parameter extension should call its Java counterpart`() { - - operations.createCollection() - verify { operations.createCollection(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `createCollection(CollectionOptions) with reified type parameter extension should call its Java counterpart`() { - - val collectionOptions = mockk() - operations.createCollection(collectionOptions) - verify { operations.createCollection(First::class.java, collectionOptions) } - } - - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `collectionExists(KClass) extension should call its Java counterpart`() { - - operations.collectionExists(First::class) - verify { operations.collectionExists(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `collectionExists() with reified type parameter extension should call its Java counterpart`() { - - operations.collectionExists() - verify { operations.collectionExists(First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `dropCollection(KClass) extension should call its Java counterpart`() { - - operations.dropCollection(First::class) - verify { operations.dropCollection(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `dropCollection() with reified type parameter extension should call its Java counterpart`() { - - operations.dropCollection() - verify { operations.dropCollection(First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `indexOps(KClass) extension should call its Java counterpart`() { - - operations.indexOps(First::class) - verify { operations.indexOps(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `indexOps() with reified type parameter extension should call its Java counterpart`() { - - operations.indexOps() - verify { operations.indexOps(First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `bulkOps(BulkMode, KClass) extension should call its Java counterpart`() { - - val bulkMode = BulkMode.ORDERED - - operations.bulkOps(bulkMode, First::class) - verify { operations.bulkOps(bulkMode, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `bulkOps(BulkMode, KClass, String) extension should call its Java counterpart`() { - - val bulkMode = BulkMode.ORDERED - val collectionName = "foo" - - operations.bulkOps(bulkMode, First::class, collectionName) - verify { operations.bulkOps(bulkMode, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `bulkOps(BulkMode) with reified type parameter extension should call its Java counterpart`() { - - val bulkMode = BulkMode.ORDERED - - operations.bulkOps(bulkMode) - verify { operations.bulkOps(bulkMode, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `bulkOps(BulkMode, String) with reified type parameter extension should call its Java counterpart`() { - - val bulkMode = BulkMode.ORDERED - val collectionName = "foo" - - operations.bulkOps(bulkMode, collectionName) - verify { operations.bulkOps(bulkMode, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findAll() with reified type parameter extension should call its Java counterpart`() { - - operations.findAll() - verify { operations.findAll(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findAll(String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - - operations.findAll(collectionName) - verify { operations.findAll(First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `group(String, GroupBy) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val groupBy = mockk() - - operations.group(collectionName, groupBy) - verify { operations.group(collectionName, groupBy, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `group(Criteria, String, GroupBy) with reified type parameter extension should call its Java counterpart`() { - - val criteria = mockk() - val collectionName = "foo" - val groupBy = mockk() - - operations.group(criteria, collectionName, groupBy) - verify { operations.group(criteria, collectionName, groupBy, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `aggregate(Aggregation, KClass) with reified type parameter extension should call its Java counterpart`() { - - val aggregation = mockk() - - operations.aggregate(aggregation, Second::class) - verify { - operations.aggregate( - aggregation, - Second::class.java, - First::class.java - ) - } - } - - @Test // #3508 - fun `aggregate(Aggregation) with reified type parameter extension should call its Java counterpart`() { - - val aggregation = mockk() - - operations.aggregate(aggregation) - verify { - operations.aggregate( - aggregation, - Second::class.java, - First::class.java - ) - } - } - - @Test // DATAMONGO-1689 - fun `aggregate(Aggregation, String) with reified type parameter extension should call its Java counterpart`() { - - val aggregation = mockk() - val collectionName = "foo" - - operations.aggregate(aggregation, collectionName) - verify { operations.aggregate(aggregation, collectionName, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `aggregateStream(Aggregation, KClass) with reified type parameter extension should call its Java counterpart`() { - - val aggregation = mockk() - - operations.aggregateStream(aggregation, Second::class) - verify { - operations.aggregateStream( - aggregation, - Second::class.java, - First::class.java - ) - } - } - - @Test // #3508 - fun `aggregateStream(Aggregation) with reified type parameter extension should call its Java counterpart`() { - - val aggregation = mockk() - - operations.aggregateStream(aggregation) - verify { - operations.aggregateStream( - aggregation, - Second::class.java, - First::class.java - ) - } - } - - @Test // DATAMONGO-1689 - fun `aggregateStream(Aggregation, String) with reified type parameter extension should call its Java counterpart`() { - - val aggregation = mockk() - val collectionName = "foo" - - operations.aggregateStream(aggregation, collectionName) - verify { - operations.aggregateStream( - aggregation, - collectionName, - First::class.java - ) - } - } - - @Test // DATAMONGO-1689 - fun `mapReduce(String, String, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val mapFunction = "bar" - val reduceFunction = "baz" - - operations.mapReduce(collectionName, mapFunction, reduceFunction) - verify { operations.mapReduce(collectionName, mapFunction, reduceFunction, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `mapReduce(String, String, String, MapReduceOptions) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val mapFunction = "bar" - val reduceFunction = "baz" - val options = mockk() - - operations.mapReduce(collectionName, mapFunction, reduceFunction, options) - verify { operations.mapReduce(collectionName, mapFunction, reduceFunction, options, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `mapReduce(Query, String, String, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - val mapFunction = "bar" - val reduceFunction = "baz" - - operations.mapReduce(query, collectionName, mapFunction, reduceFunction) - verify { operations.mapReduce(query, collectionName, mapFunction, reduceFunction, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `mapReduce(Query, String, String, String, MapReduceOptions) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - val mapFunction = "bar" - val reduceFunction = "baz" - val options = mockk() - - operations.mapReduce(query, collectionName, mapFunction, reduceFunction, options) - verify { operations.mapReduce(query, collectionName, mapFunction, reduceFunction, options, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `geoNear(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = NearQuery.near(0.0, 0.0) - - operations.geoNear(query) - verify { operations.geoNear(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `geoNear(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val query = NearQuery.near(0.0, 0.0) - - operations.geoNear(query, collectionName) - verify { operations.geoNear(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findOne(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.findOne(query) - verify { operations.findOne(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findOne(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val query = mockk() - - operations.findOne(query, collectionName) - verify { operations.findOne(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `exists(Query, KClass) extension should call its Java counterpart`() { - - val query = mockk() - - operations.exists(query, First::class) - verify { operations.exists(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `exists(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.exists(query) - verify { operations.exists(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `find(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.find(query) - verify { operations.find(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `find(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val query = mockk() - - operations.find(query, collectionName) - verify { operations.find(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findById(Any) with reified type parameter extension should call its Java counterpart`() { - - val id = 1L - - operations.findById(id) - verify { operations.findById(id, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findById(Any, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val id = 1L - - operations.findById(id, collectionName) - verify { operations.findById(id, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findAndModify(Query, Update, FindAndModifyOptions) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val options = mockk() - - operations.findAndModify(query, update, options) - verify { operations.findAndModify(query, update, options, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findAndModify(Query, Update, FindAndModifyOptions, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val query = mockk() - val update = mockk() - val options = mockk() - - operations.findAndModify(query, update, options, collectionName) - verify { operations.findAndModify(query, update, options, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findAndRemove(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.findAndRemove(query) - verify { operations.findAndRemove(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findAndRemove(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.findAndRemove(query, collectionName) - verify { operations.findAndRemove(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `count() with reified type parameter extension should call its Java counterpart`() { - - operations.count() - verify { operations.count(any(), eq(First::class.java)) } - } - - @Test // DATAMONGO-1689 - fun `count(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.count(query) - verify { operations.count(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `count(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.count(query, collectionName) - verify { operations.count(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `count(Query, KClass) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.count(query, First::class) - verify { operations.count(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `count(Query, KClass, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.count(query, First::class, collectionName) - verify { operations.count(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `insert(Collection, KClass) extension should call its Java counterpart`() { - - val collection = listOf(First(), First()) - - operations.insert(collection, First::class) - verify { operations.insert(collection, First::class.java) } - } - - @Test // DATAMONGO-2208 - fun `insert(Collection) with reified type parameter extension should call its Java counterpart`() { - - val collection = listOf(First(), First()) - - operations.insert(collection) - verify { operations.insert(collection, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `upsert(Query, Update, KClass) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.upsert(query, update, First::class) - verify { operations.upsert(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `upsert(Query, Update, KClass, String) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.upsert(query, update, First::class, collectionName) - verify { operations.upsert(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `upsert(Query, Update) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.upsert(query, update) - verify { operations.upsert(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `upsert(Query, Update, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.upsert(query, update, collectionName) - verify { operations.upsert(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `updateFirst(Query, Update, KClass) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.updateFirst(query, update, First::class) - verify { operations.updateFirst(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `updateFirst(Query, Update, KClass, String) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.updateFirst(query, update, First::class, collectionName) - verify { operations.updateFirst(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `updateFirst(Query, Update) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.updateFirst(query, update) - verify { operations.updateFirst(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `updateFirst(Query, Update, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.updateFirst(query, update, collectionName) - verify { operations.updateFirst(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `updateMulti(Query, Update, KClass) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.updateMulti(query, update, First::class) - verify { operations.updateMulti(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `updateMulti(Query, Update, KClass, String) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.updateMulti(query, update, First::class, collectionName) - verify { operations.updateMulti(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `updateMulti(Query, Update) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.updateMulti(query, update) - verify { operations.updateMulti(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `updateMulti(Query, Update, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.updateMulti(query, update, collectionName) - verify { operations.updateMulti(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `remove(Query, KClass) extension should call its Java counterpart`() { - - val query = mockk() - - operations.remove(query, First::class) - verify { operations.remove(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `remove(Query, KClass, String) extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.remove(query, First::class, collectionName) - verify { operations.remove(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `remove(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.remove(query) - verify { operations.remove(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `remove(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.remove(query, collectionName) - verify { operations.remove(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findAllAndRemove(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.findAllAndRemove(query) - verify { operations.findAllAndRemove(query, First::class.java) } - } - - @Test // DATAMONGO-1761 - @Suppress("DEPRECATION") - fun `findDistinct(String, KClass) should call java counterpart`() { - - operations.findDistinct("field", First::class) - verify { operations.findDistinct("field", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - @Suppress("DEPRECATION") - fun `findDistinct(Query, String, KClass) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field", First::class) - verify { operations.findDistinct(query, "field", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - @Suppress("DEPRECATION") - fun `findDistinct(Query, String, String, KClass) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field", "collection", First::class) - verify { operations.findDistinct(query, "field", "collection", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - fun `findDistinctImplicit(Query, String) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field") - verify { operations.findDistinct(query, "field", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - fun `findDistinct(Query, String, String) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field", "collection") - verify { operations.findDistinct(query, "field", "collection", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - @Suppress("DEPRECATION") - fun `findDistinct(Query, String, KClass) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field", First::class) - verify { operations.findDistinct(query, "field", First::class.java, String::class.java) } - } -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveAggregationOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveAggregationOperationExtensionsTests.kt deleted file mode 100644 index 0bc661460f..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveAggregationOperationExtensionsTests.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import reactor.core.publisher.Flux - -/** - * @author Mark Paluch - * @author Sebastien Deleuze - */ -class ReactiveAggregationOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - @Test // DATAMONGO-1719 - @Suppress("DEPRECATION") - fun `aggregateAndReturn(KClass) extension should call its Java counterpart`() { - - operation.aggregateAndReturn(First::class) - verify { operation.aggregateAndReturn(First::class.java) } - } - - @Test // DATAMONGO-1719 - fun `aggregateAndReturn() with reified type parameter extension should call its Java counterpart`() { - - operation.aggregateAndReturn() - verify { operation.aggregateAndReturn(First::class.java) } - } - - @Test // DATAMONGO-2255 - fun terminatingAggregationOperationAllAsFlow() { - - val spec = mockk>() - every { spec.all() } returns Flux.just("foo", "bar", "baz") - - runBlocking { - assertThat(spec.flow().toList()).contains("foo", "bar", "baz") - } - - verify { - spec.all() - } - } - -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationExtensionsTests.kt deleted file mode 100644 index e88596f1f3..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationExtensionsTests.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2019-2021 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 example.first.First -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.bson.Document -import org.junit.Test -import reactor.core.publisher.Flux - -/** - * @author Christoph Strobl - * @soundtrack Rage Against The Machine - Take the Power Back - */ -class ReactiveChangeStreamOperationExtensionsTests { - - val operation = mockk(relaxed = true) - val changestream = mockk>(relaxed = true) - - @Test // DATAMONGO-2089 - fun `ReactiveChangeStreamOperation#changeStream() with reified type parameter extension should call its Java counterpart`() { - - operation.changeStream() - verify { operation.changeStream(First::class.java) } - } - - @Test // DATAMONGO-2089 - fun `TerminatingChangeStream#listen() flow extension`() { - - val doc1 = mockk>() - val doc2 = mockk>() - val doc3 = mockk>() - - val spec = mockk>() - every { spec.listen() } returns Flux.just(doc1, doc2, doc3) - - runBlocking { - assertThat(spec.flow().toList()).contains(doc1, doc2, doc3) - } - - verify { - spec.listen() - } - } - - data class Last(val id: String) -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveFindOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveFindOperationExtensionsTests.kt deleted file mode 100644 index 722229bab4..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveFindOperationExtensionsTests.kt +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.flow.take -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatExceptionOfType -import org.junit.Test -import org.springframework.data.geo.Distance -import org.springframework.data.geo.GeoResult -import reactor.core.publisher.Flux -import reactor.core.publisher.Mono - -/** - * @author Mark Paluch - * @author Sebastien Deleuze - */ -class ReactiveFindOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - val operationWithProjection = mockk>(relaxed = true) - - val distinctWithProjection = mockk(relaxed = true) - - val findDistinct = mockk(relaxed = true) - - val reactiveFind = mockk>(relaxed = true) - - @Test // DATAMONGO-1719 - @Suppress("DEPRECATION") - fun `ReactiveFind#query(KClass) extension should call its Java counterpart`() { - - operation.query(First::class) - verify { operation.query(First::class.java) } - } - - @Test // DATAMONGO-1719 - fun `ReactiveFind#query() with reified type parameter extension should call its Java counterpart`() { - - operation.query() - verify { operation.query(First::class.java) } - } - - @Test // DATAMONGO-1719, DATAMONGO-2086 - @Suppress("DEPRECATION") - fun `ReactiveFind#FindOperatorWithProjection#asType(KClass) extension should call its Java counterpart`() { - - operationWithProjection.asType(User::class) - verify { operationWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-1719, DATAMONGO-2086 - fun `ReactiveFind#FindOperatorWithProjection#asType() with reified type parameter extension should call its Java counterpart`() { - - operationWithProjection.asType() - verify { operationWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-1761, DATAMONGO-2086 - @Suppress("DEPRECATION") - fun `ReactiveFind#DistinctWithProjection#asType(KClass) extension should call its Java counterpart`() { - - distinctWithProjection.asType(User::class) - verify { distinctWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-2086 - fun `ReactiveFind#DistinctWithProjection#asType() with reified type parameter extension should call its Java counterpart`() { - - distinctWithProjection.asType() - verify { distinctWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-2417 - fun `ReactiveFind#distrinct() using KProperty1 should call its Java counterpart`() { - - every { operation.query(KotlinUser::class.java) } returns reactiveFind - - operation.distinct(KotlinUser::username) - verify { - operation.query(KotlinUser::class.java) - reactiveFind.distinct("username") - } - } - - @Test // DATAMONGO-2417 - fun `ReactiveFind#FindDistinct#field() using KProperty should call its Java counterpart`() { - - findDistinct.distinct(KotlinUser::username) - verify { findDistinct.distinct("username") } - } - - @Test // DATAMONGO-2209 - fun terminatingFindAwaitOneWithValue() { - - val find = mockk>() - every { find.one() } returns Mono.just("foo") - - runBlocking { - assertThat(find.awaitOne()).isEqualTo("foo") - } - - verify { - find.one() - } - } - - @Test // DATAMONGO-2247 - fun terminatingFindAwaitOneWithNull() { - - val find = mockk>() - every { find.one() } returns Mono.empty() - - assertThatExceptionOfType(NoSuchElementException::class.java).isThrownBy { - runBlocking { find.awaitOne() } - } - - verify { - find.one() - } - } - - @Test // DATAMONGO-2247 - fun terminatingFindAwaitOneOrNullWithValue() { - - val find = mockk>() - every { find.one() } returns Mono.just("foo") - - runBlocking { - assertThat(find.awaitOneOrNull()).isEqualTo("foo") - } - - verify { - find.one() - } - } - - @Test // DATAMONGO-2247 - fun terminatingFindAwaitOneOrNullWithNull() { - - val find = mockk>() - every { find.one() } returns Mono.empty() - - runBlocking { - assertThat(find.awaitOneOrNull()).isNull() - } - - verify { - find.one() - } - } - - @Test // DATAMONGO-2209 - fun terminatingFindAwaitFirstWithValue() { - - val find = mockk>() - every { find.first() } returns Mono.just("foo") - - runBlocking { - assertThat(find.awaitFirst()).isEqualTo("foo") - } - - verify { - find.first() - } - } - - @Test // DATAMONGO-2247 - fun terminatingFindAwaitFirstWithNull() { - - val find = mockk>() - every { find.first() } returns Mono.empty() - - assertThatExceptionOfType(NoSuchElementException::class.java).isThrownBy { - runBlocking { find.awaitFirst() } - } - - verify { - find.first() - } - } - - @Test // DATAMONGO-2247 - fun terminatingFindAwaitFirstOrNullWithValue() { - - val find = mockk>() - every { find.first() } returns Mono.just("foo") - - runBlocking { - assertThat(find.awaitFirstOrNull()).isEqualTo("foo") - } - - verify { - find.first() - } - } - - @Test // DATAMONGO-2247 - fun terminatingFindAwaitFirstOrNullWithNull() { - - val find = mockk>() - every { find.first() } returns Mono.empty() - - runBlocking { - assertThat(find.awaitFirstOrNull()).isNull() - } - - verify { - find.first() - } - } - - @Test // DATAMONGO-2209 - fun terminatingFindAwaitCount() { - - val find = mockk>() - every { find.count() } returns Mono.just(1) - - runBlocking { - assertThat(find.awaitCount()).isEqualTo(1) - } - - verify { - find.count() - } - } - - @Test // DATAMONGO-2209 - fun terminatingFindAwaitExists() { - - val find = mockk>() - every { find.exists() } returns Mono.just(true) - - runBlocking { - assertThat(find.awaitExists()).isTrue() - } - - verify { - find.exists() - } - } - - @Test // DATAMONGO-2255 - fun terminatingFindAllAsFlow() { - - val spec = mockk>() - every { spec.all() } returns Flux.just("foo", "bar", "baz") - - runBlocking { - assertThat(spec.flow().toList()).contains("foo", "bar", "baz") - } - - verify { - spec.all() - } - } - - @Test // DATAMONGO-2255 - fun terminatingFindTailAsFlow() { - - val spec = mockk>() - every { spec.tail() } returns Flux.just("foo", "bar", "baz").concatWith(Flux.never()) - - runBlocking { - assertThat(spec.tailAsFlow().take(3).toList()).contains("foo", "bar", "baz") - } - - verify { - spec.tail() - } - } - - @Test // DATAMONGO-2255 - fun terminatingFindNearAllAsFlow() { - - val spec = mockk>() - val foo = GeoResult("foo", Distance(0.0)) - val bar = GeoResult("bar", Distance(0.0)) - val baz = GeoResult("baz", Distance(0.0)) - every { spec.all() } returns Flux.just(foo, bar, baz) - - runBlocking { - assertThat(spec.flow().toList()).contains(foo, bar, baz) - } - - verify { - spec.all() - } - } - - @Test // DATAMONGO-2255 - fun terminatingDistinctAllAsFlow() { - - val spec = mockk>() - every { spec.all() } returns Flux.just("foo", "bar", "baz") - - runBlocking { - assertThat(spec.flow().toList()).contains("foo", "bar", "baz") - } - - verify { - spec.all() - } - } - - data class KotlinUser(val username: String) -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveInsertOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveInsertOperationExtensionsTests.kt deleted file mode 100644 index abc1eb42f6..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveInsertOperationExtensionsTests.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import reactor.core.publisher.Flux -import reactor.core.publisher.Mono - -/** - * @author Mark Paluch - * @author Sebastien Deleuze - */ -class ReactiveInsertOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - @Test // DATAMONGO-1719 - @Suppress("DEPRECATION") - fun `insert(KClass) extension should call its Java counterpart`() { - - operation.insert(First::class) - verify { operation.insert(First::class.java) } - } - - @Test // DATAMONGO-1719 - fun `insert() with reified type parameter extension should call its Java counterpart`() { - - operation.insert() - verify { operation.insert(First::class.java) } - } - - @Test // DATAMONGO-2209 - fun terminatingInsertOneAndAwait() { - - val insert = mockk>() - every { insert.one("foo") } returns Mono.just("foo") - - runBlocking { - assertThat(insert.oneAndAwait("foo")).isEqualTo("foo") - } - - verify { - insert.one("foo") - } - } - - @Test // DATAMONGO-2255 - fun terminatingInsertAllAsFlow() { - - val insert = mockk>() - val list = listOf("foo", "bar") - every { insert.all(any()) } returns Flux.fromIterable(list) - - runBlocking { - assertThat(insert.flow(list).toList()).containsAll(list) - } - - verify { - insert.all(list) - } - } -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMapReduceOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMapReduceOperationExtensionsTests.kt deleted file mode 100644 index fa74c458c8..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMapReduceOperationExtensionsTests.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2018-2021 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 example.first.First -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import reactor.core.publisher.Flux - -/** - * @author Christoph Strobl - * @author Sebastien Deleuze - */ -class ReactiveMapReduceOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - val operationWithProjection = mockk>(relaxed = true) - - @Test // DATAMONGO-1929 - @Suppress("DEPRECATION") - fun `ReactiveMapReduceOperation#mapReduce(KClass) extension should call its Java counterpart`() { - - operation.mapReduce(First::class) - verify { operation.mapReduce(First::class.java) } - } - - @Test // DATAMONGO-1929 - fun `ReactiveMapReduceOperation#mapReduce() with reified type parameter extension should call its Java counterpart`() { - - operation.mapReduce() - verify { operation.mapReduce(First::class.java) } - } - - @Test // DATAMONGO-1929, DATAMONGO-2086 - @Suppress("DEPRECATION") - fun `ReactiveMapReduceOperation#MapReduceWithProjection#asType(KClass) extension should call its Java counterpart`() { - - operationWithProjection.asType(User::class) - verify { operationWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-1929, DATAMONGO-2086 - fun `ReactiveMapReduceOperation#MapReduceWithProjection#asType() with reified type parameter extension should call its Java counterpart`() { - - operationWithProjection.asType() - verify { operationWithProjection.`as`(User::class.java) } - } - - @Test // DATAMONGO-2255 - fun terminatingMapReduceAllAsFlow() { - - val spec = mockk>() - every { spec.all() } returns Flux.just("foo", "bar", "baz") - - runBlocking { - assertThat(spec.flow().toList()).contains("foo", "bar", "baz") - } - - verify { - spec.all() - } - } -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensionsTests.kt deleted file mode 100644 index 7ad29572b8..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensionsTests.kt +++ /dev/null @@ -1,662 +0,0 @@ -/* - * Copyright 2017-2021 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 example.first.First -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.springframework.data.mongodb.core.aggregation.Aggregation -import org.springframework.data.mongodb.core.aggregation.TypedAggregation -import org.springframework.data.mongodb.core.query.NearQuery -import org.springframework.data.mongodb.core.query.Query -import org.springframework.data.mongodb.core.query.Update -import reactor.core.publisher.Mono - -/** - * @author Sebastien Deleuze - * @author Christoph Strobl - * @author Mark Paluch - * @author Wonwoo Lee - */ -class ReactiveMongoOperationsExtensionsTests { - - val operations = mockk(relaxed = true) - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `indexOps(KClass) extension should call its Java counterpart`() { - - operations.indexOps(First::class) - verify { operations.indexOps(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `indexOps() with reified type parameter extension should call its Java counterpart`() { - - operations.indexOps() - verify { operations.indexOps(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `execute(ReactiveCollectionCallback) with reified type parameter extension should call its Java counterpart`() { - - val collectionCallback = mockk>() - - operations.execute(collectionCallback) - verify { operations.execute(First::class.java, collectionCallback) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `createCollection(KClass) extension should call its Java counterpart`() { - - operations.createCollection(First::class) - verify { operations.createCollection(First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `createCollection(KClass, CollectionOptions) extension should call its Java counterpart`() { - - val collectionOptions = mockk() - - operations.createCollection(First::class, collectionOptions) - verify { operations.createCollection(First::class.java, collectionOptions) } - } - - @Test // DATAMONGO-1689 - fun `createCollection() with reified type parameter extension should call its Java counterpart`() { - - operations.createCollection() - verify { operations.createCollection(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `createCollection(CollectionOptions) with reified type parameter extension should call its Java counterpart`() { - - val collectionOptions = mockk() - - operations.createCollection(collectionOptions) - verify { operations.createCollection(First::class.java, collectionOptions) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `collectionExists(KClass) extension should call its Java counterpart`() { - - operations.collectionExists(First::class) - verify { operations.collectionExists(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `collectionExists() with reified type parameter extension should call its Java counterpart`() { - - operations.collectionExists() - verify { operations.collectionExists(First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `dropCollection(KClass) extension should call its Java counterpart`() { - - operations.dropCollection(First::class) - verify { operations.dropCollection(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `dropCollection() with reified type parameter extension should call its Java counterpart`() { - - operations.dropCollection() - verify { operations.dropCollection(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findAll() with reified type parameter extension should call its Java counterpart`() { - - operations.findAll() - verify { operations.findAll(First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findAll(String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - - operations.findAll(collectionName) - verify { operations.findAll(First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findOne(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.findOne(query) - verify { operations.findOne(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findOne(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val query = mockk() - - operations.findOne(query, collectionName) - verify { operations.findOne(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `exists(Query, KClass) extension should call its Java counterpart`() { - - val query = mockk() - - operations.exists(query, First::class) - verify { operations.exists(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `exists(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.exists(query) - verify { operations.exists(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `find(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.find(query) - verify { operations.find(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `find(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val query = mockk() - - operations.find(query, collectionName) - verify { operations.find(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findById(Any) with reified type parameter extension should call its Java counterpart`() { - - val id = 1L - - operations.findById(id) - verify { operations.findById(id, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findById(Any, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val id = 1L - - operations.findById(id, collectionName) - verify { operations.findById(id, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `geoNear(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = NearQuery.near(0.0, 0.0) - - operations.geoNear(query) - verify { operations.geoNear(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `geoNear(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val query = NearQuery.near(0.0, 0.0) - - operations.geoNear(query, collectionName) - verify { operations.geoNear(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findAndModify(Query, Update, FindAndModifyOptions) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val options = mockk() - - operations.findAndModify(query, update, options) - verify { operations.findAndModify(query, update, options, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findAndModify(Query, Update, FindAndModifyOptions, String) with reified type parameter extension should call its Java counterpart`() { - - val collectionName = "foo" - val query = mockk() - val update = mockk() - val options = mockk() - - operations.findAndModify(query, update, options, collectionName) - verify { operations.findAndModify(query, update, options, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findAndRemove(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.findAndRemove(query) - verify { operations.findAndRemove(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `findAndRemove(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.findAndRemove(query, collectionName) - verify { operations.findAndRemove(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `count() with reified type parameter extension should call its Java counterpart`() { - - operations.count() - verify { operations.count(any(), eq(First::class.java)) } - } - - @Test // DATAMONGO-1689 - fun `count(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.count(query) - verify { operations.count(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `count(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.count(query, collectionName) - verify { operations.count(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `count(Query, KClass) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.count(query, First::class) - verify { operations.count(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `count(Query, KClass, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.count(query, First::class, collectionName) - verify { operations.count(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `insert(Collection, KClass) extension should call its Java counterpart`() { - - val collection = listOf(First(), First()) - - operations.insert(collection, First::class) - verify { operations.insert(collection, First::class.java) } - } - - @Test // DATAMONGO-2208 - fun `insert(Collection) with reified type parameter extension should call its Java counterpart`() { - - val collection = listOf(First(), First()) - - operations.insert(collection) - verify { operations.insert(collection, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `insertAll(Mono, KClass) extension should call its Java counterpart`() { - - val collection = Mono.just(listOf(First(), First())) - - operations.insertAll(collection, First::class) - verify { operations.insertAll(collection, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `upsert(Query, Update, KClass) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.upsert(query, update, First::class) - verify { operations.upsert(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `upsert(Query, Update, KClass, String) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.upsert(query, update, First::class, collectionName) - verify { operations.upsert(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `upsert(Query, Update) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.upsert(query, update) - verify { operations.upsert(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `upsert(Query, Update, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.upsert(query, update, collectionName) - verify { operations.upsert(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `updateFirst(Query, Update, KClass) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.updateFirst(query, update, First::class) - verify { operations.updateFirst(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `updateFirst(Query, Update, KClass, String) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.updateFirst(query, update, First::class, collectionName) - verify { operations.updateFirst(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `updateFirst(Query, Update) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.updateFirst(query, update) - verify { operations.updateFirst(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `updateFirst(Query, Update, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.updateFirst(query, update, collectionName) - verify { operations.updateFirst(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `updateMulti(Query, Update, KClass) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.updateMulti(query, update, First::class) - verify { operations.updateMulti(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `updateMulti(Query, Update, KClass, String) extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.updateMulti(query, update, First::class, collectionName) - verify { operations.updateMulti(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `updateMulti(Query, Update) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - - operations.updateMulti(query, update) - verify { operations.updateMulti(query, update, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `updateMulti(Query, Update, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val update = mockk() - val collectionName = "foo" - - operations.updateMulti(query, update, collectionName) - verify { operations.updateMulti(query, update, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `remove(Query, KClass) extension should call its Java counterpart`() { - - val query = mockk() - - operations.remove(query, First::class) - verify { operations.remove(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - @Suppress("DEPRECATION") - fun `remove(Query, KClass, String) extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.remove(query, First::class, collectionName) - verify { operations.remove(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `remove(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.remove(query) - verify { operations.remove(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `remove(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.remove(query, collectionName) - verify { operations.remove(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1689 - fun `findAllAndRemove(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.findAllAndRemove(query) - verify { operations.findAllAndRemove(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `tail(Query) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - - operations.tail(query) - verify { operations.tail(query, First::class.java) } - } - - @Test // DATAMONGO-1689 - fun `tail(Query, String) with reified type parameter extension should call its Java counterpart`() { - - val query = mockk() - val collectionName = "foo" - - operations.tail(query, collectionName) - verify { operations.tail(query, First::class.java, collectionName) } - } - - @Test // DATAMONGO-1761 - @Suppress("DEPRECATION") - fun `findDistinct(String, KClass) should call java counterpart`() { - - operations.findDistinct("field", First::class) - verify { operations.findDistinct("field", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - @Suppress("DEPRECATION") - fun `findDistinct(Query, String, KClass) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field", First::class) - verify { operations.findDistinct(query, "field", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - @Suppress("DEPRECATION") - fun `findDistinct(Query, String, String, KClass) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field", "collection", First::class) - verify { operations.findDistinct(query, "field", "collection", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - fun `findDistinctImplicit(Query, String) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field") - verify { operations.findDistinct(query, "field", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - fun `findDistinct(Query, String, String) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field", "collection") - verify { operations.findDistinct(query, "field", "collection", First::class.java, String::class.java) } - } - - @Test // DATAMONGO-1761 - @Suppress("DEPRECATION") - fun `findDistinct(Query, String, KClass) should call java counterpart`() { - - val query = mockk() - - operations.findDistinct(query, "field", First::class) - verify { - operations.findDistinct( - query, - "field", - First::class.java, - String::class.java - ) - } - } - - @Test // #893 - fun `aggregate(TypedAggregation, String, KClass) should call java counterpart`() { - - val aggregation = mockk>() - - operations.aggregate(aggregation, "foo") - verify { operations.aggregate(aggregation, "foo", First::class.java) } - } - - @Test // #893 - fun `aggregate(TypedAggregation, KClass) should call java counterpart`() { - - val aggregation = mockk>() - - operations.aggregate(aggregation) - verify { operations.aggregate(aggregation, First::class.java) } - } - - @Test // #893 - fun `aggregate(Aggregation, KClass) should call java counterpart`() { - - val aggregation = mockk() - - operations.aggregate(aggregation) - verify { - operations.aggregate( - aggregation, - String::class.java, - First::class.java - ) - } - } - - @Test // #893 - fun `aggregate(Aggregation, String) should call java counterpart`() { - - val aggregation = mockk() - - operations.aggregate(aggregation, "foo") - verify { operations.aggregate(aggregation, "foo", First::class.java) } - } -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveRemoveOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveRemoveOperationExtensionsTests.kt deleted file mode 100644 index e637dd94ef..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveRemoveOperationExtensionsTests.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017-2021 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 com.mongodb.client.result.DeleteResult -import example.first.First -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import reactor.core.publisher.Flux -import reactor.core.publisher.Mono - -/** - * @author Mark Paluch - * @author Sebastien Deleuze - */ -class ReactiveRemoveOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - @Test // DATAMONGO-1719 - @Suppress("DEPRECATION") - fun `remove(KClass) extension should call its Java counterpart`() { - - operation.remove(First::class) - verify { operation.remove(First::class.java) } - } - - @Test // DATAMONGO-1719 - fun `remove() with reified type parameter extension should call its Java counterpart`() { - - operation.remove() - verify { operation.remove(First::class.java) } - } - - @Test // DATAMONGO-2209 - fun allAndAwait() { - - val remove = mockk>() - val result = mockk() - every { remove.all() } returns Mono.just(result) - - runBlocking { - assertThat(remove.allAndAwait()).isEqualTo(result) - } - - verify { - remove.all() - } - } - - @Test // DATAMONGO-2255 - fun terminatingRemoveFindAndRemoveAsFlow() { - - val spec = mockk>() - every { spec.findAndRemove() } returns Flux.just("foo", "bar", "baz") - - runBlocking { - assertThat(spec.findAndRemoveAsFlow().toList()).contains("foo", "bar", "baz") - } - - verify { - spec.findAndRemove() - } - } -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveUpdateOperationExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveUpdateOperationExtensionsTests.kt deleted file mode 100644 index 16eccc5b6a..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveUpdateOperationExtensionsTests.kt +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2017-2021 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 com.mongodb.client.result.UpdateResult -import example.first.First -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatExceptionOfType -import org.junit.Test -import reactor.core.publisher.Mono - -/** - * Unit tests for `ReactiveExecutableUpdateOperationExtensions.kt`. - * - * @author Mark Paluch - * @author Sebastien Deleuze - */ -class ReactiveUpdateOperationExtensionsTests { - - val operation = mockk(relaxed = true) - - @Test // DATAMONGO-1719 - @Suppress("DEPRECATION") - fun `update(KClass) extension should call its Java counterpart`() { - - operation.update(First::class) - verify { operation.update(First::class.java) } - } - - @Test // DATAMONGO-1719 - fun `update() with reified type parameter extension should call its Java counterpart`() { - - operation.update() - verify { operation.update(First::class.java) } - } - - @Test // DATAMONGO-2209 - fun findModifyAndAwaitWithValue() { - - val find = mockk>() - every { find.findAndModify() } returns Mono.just("foo") - - runBlocking { - assertThat(find.findModifyAndAwait()).isEqualTo("foo") - } - - verify { - find.findAndModify() - } - } - - @Test // DATAMONGO-2247 - fun findModifyAndAwaitWithNull() { - - val find = mockk>() - every { find.findAndModify() } returns Mono.empty() - - assertThatExceptionOfType(NoSuchElementException::class.java).isThrownBy { - runBlocking { find.findModifyAndAwait() } - } - - verify { - find.findAndModify() - } - } - - @Test // DATAMONGO-2247 - fun findModifyAndAwaitOrNullWithValue() { - - val find = mockk>() - every { find.findAndModify() } returns Mono.just("foo") - - runBlocking { - assertThat(find.findModifyAndAwaitOrNull()).isEqualTo("foo") - } - - verify { - find.findAndModify() - } - } - - @Test // DATAMONGO-2247 - fun findModifyAndAwaitOrNullWithNull() { - - val find = mockk>() - every { find.findAndModify() } returns Mono.empty() - - runBlocking { - assertThat(find.findModifyAndAwaitOrNull()).isNull() - } - - verify { - find.findAndModify() - } - } - - @Test // DATAMONGO-2209 - fun findReplaceAndAwaitWithValue() { - - val find = mockk>() - every { find.findAndReplace() } returns Mono.just("foo") - - runBlocking { - assertThat(find.findReplaceAndAwait()).isEqualTo("foo") - } - - verify { - find.findAndReplace() - } - } - - @Test // DATAMONGO-2247 - fun findReplaceAndAwaitWithNull() { - - val find = mockk>() - every { find.findAndReplace() } returns Mono.empty() - - assertThatExceptionOfType(NoSuchElementException::class.java).isThrownBy { - runBlocking { find.findReplaceAndAwait() } - } - - verify { - find.findAndReplace() - } - } - - @Test // DATAMONGO-2247 - fun findReplaceAndAwaitOrNullWithValue() { - - val find = mockk>() - every { find.findAndReplace() } returns Mono.just("foo") - - runBlocking { - assertThat(find.findReplaceAndAwaitOrNull()).isEqualTo("foo") - } - - verify { - find.findAndReplace() - } - } - - @Test // DATAMONGO-2247 - fun findReplaceAndAwaitOrNullWithNull() { - - val find = mockk>() - every { find.findAndReplace() } returns Mono.empty() - - runBlocking { - assertThat(find.findReplaceAndAwaitOrNull()).isNull() - } - - verify { - find.findAndReplace() - } - } - - @Test // DATAMONGO-2209 - fun allAndAwait() { - - val update = mockk>() - val result = mockk() - every { update.all() } returns Mono.just(result) - - runBlocking { - assertThat(update.allAndAwait()).isEqualTo(result) - } - - verify { - update.all() - } - } - - @Test // DATAMONGO-2209 - fun firstAndAwait() { - - val update = mockk>() - val result = mockk() - every { update.first() } returns Mono.just(result) - - runBlocking { - assertThat(update.firstAndAwait()).isEqualTo(result) - } - - verify { - update.first() - } - } - - @Test // DATAMONGO-2209 - fun upsertAndAwait() { - - val update = mockk>() - val result = mockk() - every { update.upsert() } returns Mono.just(result) - - runBlocking { - assertThat(update.upsertAndAwait()).isEqualTo(result) - } - - verify { - update.upsert() - } - } - - @Test // DATAMONGO-2209 - fun findAndReplaceWithProjectionAsType() { - - val update = mockk>() - val result = mockk>() - every { update.`as`(String::class.java) } returns result - - assertThat(update.asType()).isEqualTo(result) - - verify { - update.`as`(String::class.java) - } - } -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/CriteriaExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/CriteriaExtensionsTests.kt deleted file mode 100644 index 02753f16de..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/CriteriaExtensionsTests.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2017-2021 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.query - -import io.mockk.mockk -import io.mockk.verify -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test - -/** - * @author Sebastien Deleuze - * @author Tjeu Kayim - */ -class CriteriaExtensionsTests { - - val criteria = mockk(relaxed = true) - - @Test - fun `isEqualTo() extension should call its Java counterpart`() { - - val foo = "foo" - - criteria.isEqualTo(foo) - - verify(exactly = 1) { criteria.`is`(foo) } - } - - @Test - fun `isEqualTo() extension should support nullable value`() { - - criteria.isEqualTo(null) - - verify(exactly = 1) { criteria.`is`(null) } - } - - @Test - fun `inValues(varags) extension should call its Java counterpart`() { - - val foo = "foo" - val bar = "bar" - - criteria.inValues(foo, bar) - - verify(exactly = 1) { criteria.`in`(foo, bar) } - } - - @Test - fun `inValues(varags) extension should support nullable values`() { - - criteria.inValues(null, null) - - verify(exactly = 1) { criteria.`in`(null, null) } - } - - @Test - fun `inValues(Collection) extension should call its Java counterpart`() { - - val c = listOf("foo", "bar") - - criteria.inValues(c) - - verify(exactly = 1) { criteria.`in`(c) } - } - - @Test - fun `inValues(Collection) extension should support nullable values`() { - - val c = listOf("foo", null, "bar") - - criteria.inValues(c) - - verify(exactly = 1) { criteria.`in`(c) } - } - - @Test - fun `and(KProperty) extension should call its Java counterpart`() { - - criteria.and(Book::title) - - verify(exactly = 1) { criteria.and("title") } - } - - @Test - fun `and(KProperty) extension should support nested properties`() { - - criteria.and(Book::author / Author::name) - - verify(exactly = 1) { criteria.and("author.name") } - } - - @Test - fun `where(KProperty) should equal Criteria where()`() { - - class Book(val title: String) - - val typedCriteria = where(Book::title) - val classicCriteria = Criteria.where("title") - - assertThat(typedCriteria).isEqualTo(classicCriteria) - } - - @Test - fun `where(KProperty) should support nested properties`() { - - val typedCriteria = where(Book::author / Author::name) - val classicCriteria = Criteria.where("author.name") - - assertThat(typedCriteria).isEqualTo(classicCriteria) - } - - class Book(val title: String, val author: Author) - class Author(val name: String) -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/KPropertyPathTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/KPropertyPathTests.kt deleted file mode 100644 index 17347d8151..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/KPropertyPathTests.kt +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2018-2021 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.query - -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test - -/** - * Unit tests for [KPropertyPath] and its extensions. - * - * @author Tjeu Kayim - * @author Yoann de Martino - * @author Mark Paluch - */ -class KPropertyPathTests { - - @Test - fun `Convert normal KProperty to field name`() { - - val property = asString(Book::title) - - assertThat(property).isEqualTo("title") - } - - @Test - fun `Convert nested KProperty to field name`() { - - val property = asString(Book::author / Author::name) - - assertThat(property).isEqualTo("author.name") - } - - @Test - fun `Convert double nested KProperty to field name`() { - - class Entity(val book: Book) - - val property = asString(Entity::book / Book::author / Author::name) - - assertThat(property).isEqualTo("book.author.name") - } - - @Test - fun `Convert triple nested KProperty to field name`() { - - class Entity(val book: Book) - class AnotherEntity(val entity: Entity) - - val property = asString(AnotherEntity::entity / Entity::book / Book::author / Author::name) - - assertThat(property).isEqualTo("entity.book.author.name") - } - - @Test - fun `Convert simple KProperty to property path using toPath`() { - - class AnotherEntity(val entity: String) - - val property = (AnotherEntity::entity).toPath() - - assertThat(property).isEqualTo("entity") - } - - @Test - fun `Convert nested KProperty to field name using toPath()`() { - - val property = (Book::author / Author::name).toPath() - - assertThat(property).isEqualTo("author.name") - } - - @Test - fun `Convert triple nested KProperty to property path using toPath`() { - - class Entity(val book: Book) - class AnotherEntity(val entity: Entity) - - val property = (AnotherEntity::entity / Entity::book / Book::author / Author::name).toPath() - - assertThat(property).isEqualTo("entity.book.author.name") - } - - @Test // DATAMONGO-2661 - fun `Convert nullable KProperty to field name`() { - class Cat(val name: String) - class Owner(val cat: Cat?) - - val property = asString(Owner::cat / Cat::name) - assertThat(property).isEqualTo("cat.name") - } - - class Book(val title: String, val author: Author) - class Author(val name: String) -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt deleted file mode 100644 index 54969476d3..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright 2018-2021 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.query - -import org.assertj.core.api.Assertions.assertThat -import org.bson.BsonRegularExpression -import org.junit.Test -import org.springframework.data.geo.Circle -import org.springframework.data.geo.Point -import org.springframework.data.mongodb.core.geo.GeoJsonPoint -import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type -import java.util.regex.Pattern - -/** - * @author Tjeu Kayim - * @author Mark Paluch - */ -class TypedCriteriaExtensionsTests { - - @Test - fun `isEqualTo() should equal expected criteria`() { - - val typed = Book::title isEqualTo "Moby-Dick" - val expected = Criteria("title").isEqualTo("Moby-Dick") - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `ne() should equal expected criteria`() { - - val typed = Book::title ne "Moby-Dick" - val expected = Criteria("title").ne("Moby-Dick") - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `lt() should equal expected criteria`() { - - val typed = Book::price lt 100 - val expected = Criteria("price").lt(100) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `lte() should equal expected criteria`() { - - val typed = Book::price lte 100 - val expected = Criteria("price").lte(100) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `gt() should equal expected criteria`() { - - val typed = Book::price gt 100 - val expected = Criteria("price").gt(100) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `gte() should equal expected criteria`() { - - val typed = Book::price gte 100 - val expected = Criteria("price").gte(100) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `inValues(vararg) should equal expected criteria`() { - - val typed = Book::price.inValues(1, 2, 3) - val expected = Criteria("price").inValues(1, 2, 3) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `inValues(list) should equal expected criteria`() { - - val typed = Book::price inValues listOf(1, 2, 3) - val expected = Criteria("price").inValues(listOf(1, 2, 3)) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `nin(vararg) should equal expected criteria`() { - - val typed = Book::price.nin(1, 2, 3) - val expected = Criteria("price").nin(1, 2, 3) - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `nin(list) should equal expected criteria`() { - - val typed = Book::price nin listOf(1, 2, 3) - val expected = Criteria("price").nin(listOf(1, 2, 3)) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `mod() should equal expected criteria`() { - - val typed = Book::price.mod(2, 3) - val expected = Criteria("price").mod(2, 3) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `all(vararg) should equal expected criteria`() { - - val typed = Book::categories.all(1, 2, 3) - val expected = Criteria("categories").all(1, 2, 3) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `all(list) should equal expected criteria`() { - - val typed = Book::categories all listOf(1, 2, 3) - val expected = Criteria("categories").all(listOf(1, 2, 3)) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `size() should equal expected criteria`() { - - val typed = Book::categories size 4 - val expected = Criteria("categories").size(4) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `exists() should equal expected criteria`() { - - val typed = Book::title exists true - val expected = Criteria("title").exists(true) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `type(Int) should equal expected criteria`() { - - val typed = Book::title type 2 - val expected = Criteria("title").type(2) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `type(List) should equal expected criteria`() { - - val typed = Book::title type listOf(Type.STRING, Type.BOOLEAN) - val expected = Criteria("title").type(Type.STRING, Type.BOOLEAN) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `type(vararg) should equal expected criteria`() { - - val typed = Book::title.type(Type.STRING, Type.BOOLEAN) - val expected = Criteria("title").type(Type.STRING, Type.BOOLEAN) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `not() should equal expected criteria`() { - - val typed = Book::price.not().lt(123) - val expected = Criteria("price").not().lt(123) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `regex(string) should equal expected criteria`() { - - val typed = Book::title regex "ab+c" - val expected = Criteria("title").regex("ab+c") - assertEqualCriteriaByJson(typed, expected) - } - - @Test - fun `regex(string, options) should equal expected criteria`() { - - val typed = Book::title.regex("ab+c", "g") - val expected = Criteria("title").regex("ab+c", "g") - - assertEqualCriteriaByJson(typed, expected) - } - - @Test - fun `regex(Regex) should equal expected criteria`() { - - val typed = Book::title regex Regex("ab+c") - val expected = Criteria("title").regex(Pattern.compile("ab+c")) - - assertEqualCriteriaByJson(typed, expected) - } - - private fun assertEqualCriteriaByJson(typed: Criteria, expected: Criteria) { - assertThat(typed.criteriaObject.toJson()).isEqualTo(expected.criteriaObject.toJson()) - } - - @Test - fun `regex(Pattern) should equal expected criteria`() { - - val value = Pattern.compile("ab+c") - val typed = Book::title regex value - val expected = Criteria("title").regex(value) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `regex(BsonRegularExpression) should equal expected criteria`() { - - val expression = BsonRegularExpression("ab+c") - val typed = Book::title regex expression - val expected = Criteria("title").regex(expression) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `withinSphere() should equal expected criteria`() { - - val value = Circle(Point(928.76, 28.345), 65.243) - val typed = Building::location withinSphere value - val expected = Criteria("location").withinSphere(value) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `within() should equal expected criteria`() { - - val value = Circle(Point(5.43421, 12.456), 52.67) - val typed = Building::location within value - val expected = Criteria("location").within(value) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `near() should equal expected criteria`() { - - val value = Point(57.431, 71.345) - val typed = Building::location near value - val expected = Criteria("location").near(value) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `nearSphere() should equal expected criteria`() { - - val value = Point(5.4321, 12.345) - val typed = Building::location nearSphere value - val expected = Criteria("location").nearSphere(value) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `intersects() should equal expected criteria`() { - - val value = GeoJsonPoint(5.481573, 51.451726) - val typed = Building::location intersects value - val expected = Criteria("location").intersects(value) - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `maxDistance() should equal expected criteria`() { - - val typed = Building::location maxDistance 3.0 - val expected = Criteria("location").maxDistance(3.0) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `minDistance() should equal expected criteria`() { - - val typed = Building::location minDistance 3.0 - val expected = Criteria("location").minDistance(3.0) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `elemMatch() should equal expected criteria`() { - - val value = Criteria("price").lt(950) - val typed = Book::title elemMatch value - val expected = Criteria("title").elemMatch(value) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `elemMatch(TypedCriteria) should equal expected criteria`() { - - val typed = Book::title elemMatch (Book::price lt 950) - val expected = Criteria("title").elemMatch(Criteria("price").lt(950)) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `bits() should equal expected criteria`() { - - val typed = Book::title bits { allClear(123) } - val expected = Criteria("title").bits().allClear(123) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `One level nested should equal expected criteria`() { - - val typed = Book::author / Author::name isEqualTo "Herman Melville" - val expected = Criteria("author.name").isEqualTo("Herman Melville") - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `Two levels nested should equal expected criteria`() { - - data class Entity(val book: Book) - - val typed = Entity::book / Book::author / Author::name isEqualTo "Herman Melville" - val expected = Criteria("book.author.name").isEqualTo("Herman Melville") - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `typed criteria inside orOperator() should equal expected criteria`() { - - val typed = (Book::title isEqualTo "Moby-Dick").orOperator( - Book::price lt 1200, - Book::price gt 240 - ) - val expected = Criteria("title").isEqualTo("Moby-Dick") - .orOperator( - Criteria("price").lt(1200), - Criteria("price").gt(240) - ) - - assertThat(typed).isEqualTo(expected) - } - - @Test - fun `chaining gt & isEqualTo() should equal expected criteria`() { - - val typed = (Book::title isEqualTo "Moby-Dick") - .and(Book::price).lt(950) - val expected = Criteria("title").isEqualTo("Moby-Dick") - .and("price").lt(950) - - assertThat(typed).isEqualTo(expected) - } - - data class Book( - val title: String = "Moby-Dick", - val price: Int = 123, - val available: Boolean = true, - val categories: List = emptyList(), - val author: Author = Author() - ) - - data class Author( - val name: String = "Herman Melville" - ) - - data class Building( - val location: GeoJsonPoint - ) -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/repository/CoroutineRepositoryUnitTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/repository/CoroutineRepositoryUnitTests.kt deleted file mode 100644 index 69d718a8fb..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/repository/CoroutineRepositoryUnitTests.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2020-2021 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.repository - -import com.mongodb.client.result.DeleteResult -import io.mockk.every -import io.mockk.mockk -import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.data.annotation.Id -import org.springframework.data.mongodb.core.ReactiveMongoOperations -import org.springframework.data.mongodb.core.convert.MappingMongoConverter -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver -import org.springframework.data.mongodb.core.mapping.MongoMappingContext -import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory -import org.springframework.data.repository.kotlin.CoroutineCrudRepository -import reactor.core.publisher.Mono - -/** - * Unit tests for Kotlin Coroutine repositories. - * - * @author Mark Paluch - */ -class CoroutineRepositoryUnitTests { - - val operations = mockk(relaxed = true) - lateinit var repositoryFactory: ReactiveMongoRepositoryFactory - - @BeforeEach - fun before() { - - every { operations.getConverter() } returns MappingMongoConverter(NoOpDbRefResolver.INSTANCE, MongoMappingContext()) - repositoryFactory = ReactiveMongoRepositoryFactory(operations) - } - - @Test // DATAMONGO-2601 - fun `should discard result of suspended query method without result`() { - - every { operations.remove(any(), any(), any()) } returns Mono.just(DeleteResult.acknowledged(1)) - - val repository = repositoryFactory.getRepository(PersonRepository::class.java) - - runBlocking { - repository.deleteAllByName("foo") - } - } - - interface PersonRepository : CoroutineCrudRepository { - - suspend fun deleteAllByName(name: String) - } - - data class Person(@Id var id: Long, var name: String) -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/repository/KotlinRepositoryUnitTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/repository/KotlinRepositoryUnitTests.kt deleted file mode 100644 index fdd7936efb..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/repository/KotlinRepositoryUnitTests.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2020-2021 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.repository - -import io.mockk.every -import io.mockk.mockk -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.data.annotation.Id -import org.springframework.data.mongodb.core.MongoOperations -import org.springframework.data.mongodb.core.convert.MappingMongoConverter -import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver -import org.springframework.data.mongodb.core.mapping.MongoMappingContext -import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory -import org.springframework.data.repository.CrudRepository - -/** - * Unit tests for Kotlin repositories. - * - * @author Mark Paluch - */ -class KotlinRepositoryUnitTests { - - val operations = mockk(relaxed = true) - lateinit var repositoryFactory: MongoRepositoryFactory - - @BeforeEach - fun before() { - - every { operations.getConverter() } returns MappingMongoConverter(NoOpDbRefResolver.INSTANCE, MongoMappingContext()) - repositoryFactory = MongoRepositoryFactory(operations) - } - - @Test // DATAMONGO-2601 - fun should() { - - val repository = repositoryFactory.getRepository(PersonRepository::class.java) - - repository.deleteAllByName("foo") - } - - interface PersonRepository : CrudRepository { - - fun deleteAllByName(name: String) - } - - data class Person(@Id var id: Long, var name: String) -} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodCoroutineUnitTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodCoroutineUnitTests.kt deleted file mode 100644 index cc3c73b72c..0000000000 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethodCoroutineUnitTests.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2020-2021 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.repository.query - -import kotlinx.coroutines.flow.Flow -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.springframework.data.mongodb.core.mapping.MongoMappingContext -import org.springframework.data.mongodb.repository.Person -import org.springframework.data.projection.SpelAwareProxyProjectionFactory -import org.springframework.data.repository.core.support.DefaultRepositoryMetadata -import org.springframework.data.repository.kotlin.CoroutineCrudRepository -import kotlin.coroutines.Continuation - -/** - * Unit tests for [ReactiveMongoQueryMethod] using Coroutine repositories. - * - * @author Mark Paluch - */ -class ReactiveMongoQueryMethodCoroutineUnitTests { - - val projectionFactory = SpelAwareProxyProjectionFactory() - - interface PersonRepository : CoroutineCrudRepository { - - suspend fun findSuspendAllByName(): Flow - - fun findAllByName(): Flow - - suspend fun findSuspendByName(): List - } - - @Test // DATAMONGO-2562 - internal fun `should consider methods returning Flow as collection queries`() { - - val method = PersonRepository::class.java.getMethod("findAllByName") - val queryMethod = ReactiveMongoQueryMethod(method, DefaultRepositoryMetadata(PersonRepository::class.java), projectionFactory, MongoMappingContext()) - - assertThat(queryMethod.isCollectionQuery).isTrue() - } - - @Test // DATAMONGO-2562 - internal fun `should consider suspended methods returning Flow as collection queries`() { - - val method = PersonRepository::class.java.getMethod("findSuspendAllByName", Continuation::class.java) - val queryMethod = ReactiveMongoQueryMethod(method, DefaultRepositoryMetadata(PersonRepository::class.java), projectionFactory, MongoMappingContext()) - - assertThat(queryMethod.isCollectionQuery).isTrue() - } - - @Test // DATAMONGO-2630 - internal fun `should consider suspended methods returning List as collection queries`() { - - val method = PersonRepository::class.java.getMethod("findSuspendByName", Continuation::class.java) - val queryMethod = ReactiveMongoQueryMethod(method, DefaultRepositoryMetadata(PersonRepository::class.java), projectionFactory, MongoMappingContext()) - - assertThat(queryMethod.isCollectionQuery).isTrue() - } -} diff --git a/spring-data-mongodb/src/test/resources/META-INF/mongo-named-queries.properties b/spring-data-mongodb/src/test/resources/META-INF/mongo-named-queries.properties deleted file mode 100644 index fe24cacd95..0000000000 --- a/spring-data-mongodb/src/test/resources/META-INF/mongo-named-queries.properties +++ /dev/null @@ -1 +0,0 @@ -Person.findByNamedQuery={'firstname' : ?0} \ No newline at end of file diff --git a/spring-data-mongodb/src/test/resources/geospatial.xml b/spring-data-mongodb/src/test/resources/geospatial.xml deleted file mode 100644 index 8937e8f753..0000000000 --- a/spring-data-mongodb/src/test/resources/geospatial.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/gridfs/another-resource.xml b/spring-data-mongodb/src/test/resources/gridfs/another-resource.xml deleted file mode 100644 index 7217ac4743..0000000000 --- a/spring-data-mongodb/src/test/resources/gridfs/another-resource.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/spring-data-mongodb/src/test/resources/gridfs/gridfs.xml b/spring-data-mongodb/src/test/resources/gridfs/gridfs.xml deleted file mode 100644 index a7691c97ae..0000000000 --- a/spring-data-mongodb/src/test/resources/gridfs/gridfs.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/gridfs/reactive-gridfs.xml b/spring-data-mongodb/src/test/resources/gridfs/reactive-gridfs.xml deleted file mode 100644 index bcba3dfb38..0000000000 --- a/spring-data-mongodb/src/test/resources/gridfs/reactive-gridfs.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/groupReduce.js b/spring-data-mongodb/src/test/resources/groupReduce.js deleted file mode 100644 index 4c08bc87d5..0000000000 --- a/spring-data-mongodb/src/test/resources/groupReduce.js +++ /dev/null @@ -1 +0,0 @@ -function(doc, prev) { prev.count += 1 } \ No newline at end of file diff --git a/spring-data-mongodb/src/test/resources/infrastructure.xml b/spring-data-mongodb/src/test/resources/infrastructure.xml deleted file mode 100644 index 500c44e2bf..0000000000 --- a/spring-data-mongodb/src/test/resources/infrastructure.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/keyFunction.js b/spring-data-mongodb/src/test/resources/keyFunction.js deleted file mode 100644 index 3acc63159e..0000000000 --- a/spring-data-mongodb/src/test/resources/keyFunction.js +++ /dev/null @@ -1 +0,0 @@ -function(doc) { return { x : doc.x }; } \ No newline at end of file diff --git a/spring-data-mongodb/src/test/resources/logback.xml b/spring-data-mongodb/src/test/resources/logback.xml deleted file mode 100644 index a36841c97c..0000000000 --- a/spring-data-mongodb/src/test/resources/logback.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - %d %5p %40.40c:%4L - %m%n - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/logging.properties b/spring-data-mongodb/src/test/resources/logging.properties deleted file mode 100644 index 2f5ec24dd9..0000000000 --- a/spring-data-mongodb/src/test/resources/logging.properties +++ /dev/null @@ -1 +0,0 @@ -handlers = org.slf4j.bridge.SLF4JBridgeHandler \ No newline at end of file diff --git a/spring-data-mongodb/src/test/resources/map.js b/spring-data-mongodb/src/test/resources/map.js deleted file mode 100644 index f7a20609bf..0000000000 --- a/spring-data-mongodb/src/test/resources/map.js +++ /dev/null @@ -1,5 +0,0 @@ -function () { - for (var i = 0; i < this.x.length; i++) { - emit(this.x[i], 1); - } -} diff --git a/spring-data-mongodb/src/test/resources/namespace/converter-custom-fieldnamingstrategy.xml b/spring-data-mongodb/src/test/resources/namespace/converter-custom-fieldnamingstrategy.xml deleted file mode 100644 index a16849877b..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/converter-custom-fieldnamingstrategy.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-data-mongodb/src/test/resources/namespace/converter-custom-typeMapper.xml b/spring-data-mongodb/src/test/resources/namespace/converter-custom-typeMapper.xml deleted file mode 100644 index 580fe87272..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/converter-custom-typeMapper.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/converter-default.xml b/spring-data-mongodb/src/test/resources/namespace/converter-default.xml deleted file mode 100644 index 9da3af36d7..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/converter-default.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/converter-invalid.xml b/spring-data-mongodb/src/test/resources/namespace/converter-invalid.xml deleted file mode 100644 index e69006a0f7..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/converter-invalid.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/converter-nested-bean-definition.xml b/spring-data-mongodb/src/test/resources/namespace/converter-nested-bean-definition.xml deleted file mode 100644 index 7fef48d5dd..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/converter-nested-bean-definition.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/converter-validation-disabled.xml b/spring-data-mongodb/src/test/resources/namespace/converter-validation-disabled.xml deleted file mode 100644 index 8d7415f46b..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/converter-validation-disabled.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/converter-validation-enabled.xml b/spring-data-mongodb/src/test/resources/namespace/converter-validation-enabled.xml deleted file mode 100644 index fe6df5bae2..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/converter-validation-enabled.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/converter.xml b/spring-data-mongodb/src/test/resources/namespace/converter.xml deleted file mode 100644 index 52238f79e1..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/converter.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/db-factory-bean-custom-write-concern.xml b/spring-data-mongodb/src/test/resources/namespace/db-factory-bean-custom-write-concern.xml deleted file mode 100644 index 66dba8540a..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/db-factory-bean-custom-write-concern.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/db-factory-bean.xml b/spring-data-mongodb/src/test/resources/namespace/db-factory-bean.xml deleted file mode 100644 index 6f8c1ae0cf..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/db-factory-bean.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/mongo-bean.xml b/spring-data-mongodb/src/test/resources/namespace/mongo-bean.xml deleted file mode 100644 index 478f95daf0..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/mongo-bean.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/mongo-client-uri-and-id.xml b/spring-data-mongodb/src/test/resources/namespace/mongo-client-uri-and-id.xml deleted file mode 100644 index 4bd9158356..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/mongo-client-uri-and-id.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/mongo-client-uri.xml b/spring-data-mongodb/src/test/resources/namespace/mongo-client-uri.xml deleted file mode 100644 index e12b585237..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/mongo-client-uri.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/mongo-uri-and-id.xml b/spring-data-mongodb/src/test/resources/namespace/mongo-uri-and-id.xml deleted file mode 100644 index 4bd9158356..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/mongo-uri-and-id.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/mongo-uri-no-credentials.xml b/spring-data-mongodb/src/test/resources/namespace/mongo-uri-no-credentials.xml deleted file mode 100644 index 5969d84e9f..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/mongo-uri-no-credentials.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/mongo-uri-write-concern-and-details.xml b/spring-data-mongodb/src/test/resources/namespace/mongo-uri-write-concern-and-details.xml deleted file mode 100644 index a279bd83e4..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/mongo-uri-write-concern-and-details.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml b/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml deleted file mode 100644 index 0659bfb973..0000000000 --- a/spring-data-mongodb/src/test/resources/namespace/mongoClient-bean.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/MongoBeanPropertyDocumentMapper-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/MongoBeanPropertyDocumentMapper-context.xml deleted file mode 100644 index 76f271b4da..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/MongoBeanPropertyDocumentMapper-context.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoClientNamespaceTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoClientNamespaceTests-context.xml deleted file mode 100644 index 1bd3aa2a05..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoClientNamespaceTests-context.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoDbFactoryNoDatabaseRunningTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoDbFactoryNoDatabaseRunningTests-context.xml deleted file mode 100644 index dc86edce2f..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoDbFactoryNoDatabaseRunningTests-context.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests-context.xml deleted file mode 100644 index 5575248498..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceReplicaSetTests-context.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml deleted file mode 100644 index 172c005a72..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/MongoNamespaceTests-context.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/auditing.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/auditing.xml deleted file mode 100644 index 8466692f83..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/auditing.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/mongo.properties b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/mongo.properties deleted file mode 100644 index a79c2c685c..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/config/mongo.properties +++ /dev/null @@ -1,15 +0,0 @@ -mongo.host=127.0.0.1 -mongo.port=27017 -mongo.connectionsPerHost=8 -mongo.connectTimeout=1000 -mongo.maxWaitTime=1500 -mongo.autoConnectRetry=true -mongo.socketTimeout=1500 -mongo.threadsAllowedToBlockForConnectionMultiplier=4 -mongo.socketKeepAlive=true -mongo.fsync=true - -mongoSsl.ssl=true -replicaSetName=rs0 -credential=jon:warg@snow?uri.authMechanism=PLAIN - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests-context.xml deleted file mode 100644 index e3635826c7..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreatorIntegrationTests-context.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/mapping/event/ValidatingMongoEventListenerTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/mapping/event/ValidatingMongoEventListenerTests-context.xml deleted file mode 100644 index 66d9aed368..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/core/mapping/event/ValidatingMongoEventListenerTests-context.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests-context.xml deleted file mode 100644 index e9c04b79c9..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests-context.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml deleted file mode 100644 index 264d474b56..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/PersonRepositoryIntegrationTests-context.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests-context.xml deleted file mode 100644 index 2e88cda928..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests-context.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml deleted file mode 100644 index b70efb607c..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/lazy/AllowNestedMongoRepositoriesRepositoryConfigTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/lazy/AllowNestedMongoRepositoriesRepositoryConfigTests-context.xml deleted file mode 100644 index 4817cacf03..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/config/lazy/AllowNestedMongoRepositoriesRepositoryConfigTests-context.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/mongo.properties b/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/mongo.properties deleted file mode 100644 index d784d5f1ed..0000000000 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/mongodb/repository/mongo.properties +++ /dev/null @@ -1 +0,0 @@ -mongo.create-query-indexes=true diff --git a/spring-data-mongodb/src/test/resources/reactive-infrastructure.xml b/spring-data-mongodb/src/test/resources/reactive-infrastructure.xml deleted file mode 100644 index 896bb26812..0000000000 --- a/spring-data-mongodb/src/test/resources/reactive-infrastructure.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/reduce.js b/spring-data-mongodb/src/test/resources/reduce.js deleted file mode 100644 index 3552a50d83..0000000000 --- a/spring-data-mongodb/src/test/resources/reduce.js +++ /dev/null @@ -1,6 +0,0 @@ -function (key, values) { - var sum = 0; - for (var i = 0; i < values.length; i++) - sum += values[i]; - return sum; -} \ No newline at end of file diff --git a/spring-data-mongodb/src/test/resources/replicaSet.properties b/spring-data-mongodb/src/test/resources/replicaSet.properties deleted file mode 100644 index a4e554b6fa..0000000000 --- a/spring-data-mongodb/src/test/resources/replicaSet.properties +++ /dev/null @@ -1 +0,0 @@ -mongo.hosts=192.168.174.130:27017,192.168.174.130:27018,192.168.174.130:27019 \ No newline at end of file diff --git a/spring-data-mongodb/src/test/resources/server-jmx.xml b/spring-data-mongodb/src/test/resources/server-jmx.xml deleted file mode 100644 index 54f985f4cb..0000000000 --- a/spring-data-mongodb/src/test/resources/server-jmx.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/spring-data-mongodb/src/test/resources/template-mapping.xml b/spring-data-mongodb/src/test/resources/template-mapping.xml deleted file mode 100644 index 5f571f7241..0000000000 --- a/spring-data-mongodb/src/test/resources/template-mapping.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/resources/zips.json b/spring-data-mongodb/src/test/resources/zips.json deleted file mode 100644 index b84c090c2a..0000000000 --- a/spring-data-mongodb/src/test/resources/zips.json +++ /dev/null @@ -1,29467 +0,0 @@ -{"city": "ACMAR", "loc": [-86.51557, 33.584132], "pop": 6055, "state": "AL", "_id": "35004"} -{"city": "ADAMSVILLE", "loc": [-86.959727, 33.588437], "pop": 10616, "state": "AL", "_id": "35005"} -{"city": "ADGER", "loc": [-87.167455, 33.434277], "pop": 3205, "state": "AL", "_id": "35006"} -{"city": "KEYSTONE", "loc": [-86.812861, 33.236868], "pop": 14218, "state": "AL", "_id": "35007"} -{"city": "NEW SITE", "loc": [-85.951086, 32.941445], "pop": 19942, "state": "AL", "_id": "35010"} -{"city": "ALPINE", "loc": [-86.208934, 33.331165], "pop": 3062, "state": "AL", "_id": "35014"} -{"city": "ARAB", "loc": [-86.489638, 34.328339], "pop": 13650, "state": "AL", "_id": "35016"} -{"city": "BAILEYTON", "loc": [-86.621299, 34.268298], "pop": 1781, "state": "AL", "_id": "35019"} -{"city": "BESSEMER", "loc": [-86.947547, 33.409002], "pop": 40549, "state": "AL", "_id": "35020"} -{"city": "HUEYTOWN", "loc": [-86.999607, 33.414625], "pop": 39677, "state": "AL", "_id": "35023"} -{"city": "BLOUNTSVILLE", "loc": [-86.568628, 34.092937], "pop": 9058, "state": "AL", "_id": "35031"} -{"city": "BREMEN", "loc": [-87.004281, 33.973664], "pop": 3448, "state": "AL", "_id": "35033"} -{"city": "BRENT", "loc": [-87.211387, 32.93567], "pop": 3791, "state": "AL", "_id": "35034"} -{"city": "BRIERFIELD", "loc": [-86.951672, 33.042747], "pop": 1282, "state": "AL", "_id": "35035"} -{"city": "CALERA", "loc": [-86.755987, 33.1098], "pop": 4675, "state": "AL", "_id": "35040"} -{"city": "CENTREVILLE", "loc": [-87.11924, 32.950324], "pop": 4902, "state": "AL", "_id": "35042"} -{"city": "CHELSEA", "loc": [-86.614132, 33.371582], "pop": 4781, "state": "AL", "_id": "35043"} -{"city": "COOSA PINES", "loc": [-86.337622, 33.266928], "pop": 7985, "state": "AL", "_id": "35044"} -{"city": "CLANTON", "loc": [-86.642472, 32.835532], "pop": 13990, "state": "AL", "_id": "35045"} -{"city": "CLEVELAND", "loc": [-86.559355, 33.992106], "pop": 2369, "state": "AL", "_id": "35049"} -{"city": "COLUMBIANA", "loc": [-86.616145, 33.176964], "pop": 4486, "state": "AL", "_id": "35051"} -{"city": "CRANE HILL", "loc": [-87.048395, 34.082117], "pop": 2270, "state": "AL", "_id": "35053"} -{"city": "CROPWELL", "loc": [-86.280026, 33.506448], "pop": 4171, "state": "AL", "_id": "35054"} -{"city": "CULLMAN", "loc": [-86.829777, 34.176146], "pop": 31708, "state": "AL", "_id": "35055"} -{"city": "DOLOMITE", "loc": [-86.956435, 33.465424], "pop": 1476, "state": "AL", "_id": "35061"} -{"city": "DORA", "loc": [-87.040148, 33.734947], "pop": 11017, "state": "AL", "_id": "35062"} -{"city": "EMPIRE", "loc": [-87.016139, 33.825589], "pop": 2429, "state": "AL", "_id": "35063"} -{"city": "FAIRFIELD", "loc": [-86.918262, 33.473494], "pop": 12106, "state": "AL", "_id": "35064"} -{"city": "COALBURG", "loc": [-86.813614, 33.611283], "pop": 5909, "state": "AL", "_id": "35068"} -{"city": "GARDENDALE", "loc": [-86.822481, 33.71891], "pop": 17968, "state": "AL", "_id": "35071"} -{"city": "GOODWATER", "loc": [-86.078149, 33.074642], "pop": 3813, "state": "AL", "_id": "35072"} -{"city": "ALDEN", "loc": [-86.948221, 33.63356], "pop": 4429, "state": "AL", "_id": "35073"} -{"city": "HANCEVILLE", "loc": [-86.784844, 34.051569], "pop": 10186, "state": "AL", "_id": "35077"} -{"city": "HARPERSVILLE", "loc": [-86.429441, 33.36746], "pop": 4905, "state": "AL", "_id": "35078"} -{"city": "HAYDEN", "loc": [-86.81767, 33.885806], "pop": 6533, "state": "AL", "_id": "35079"} -{"city": "HELENA", "loc": [-86.813786, 33.316978], "pop": 9938, "state": "AL", "_id": "35080"} -{"city": "HOLLY POND", "loc": [-86.617441, 34.190085], "pop": 3838, "state": "AL", "_id": "35083"} -{"city": "JEMISON", "loc": [-86.718052, 32.980539], "pop": 7202, "state": "AL", "_id": "35085"} -{"city": "JOPPA", "loc": [-86.551939, 34.283739], "pop": 987, "state": "AL", "_id": "35087"} -{"city": "KELLYTON", "loc": [-86.048397, 32.979068], "pop": 1584, "state": "AL", "_id": "35089"} -{"city": "KIMBERLY", "loc": [-86.808417, 33.768355], "pop": 1045, "state": "AL", "_id": "35091"} -{"city": "LEEDS", "loc": [-86.574824, 33.528333], "pop": 10421, "state": "AL", "_id": "35094"} -{"city": "LINCOLN", "loc": [-86.111152, 33.605913], "pop": 5033, "state": "AL", "_id": "35096"} -{"city": "LOGAN", "loc": [-87.038115, 34.184079], "pop": 2379, "state": "AL", "_id": "35098"} -{"city": "MC CALLA", "loc": [-87.102379, 33.284546], "pop": 8147, "state": "AL", "_id": "35111"} -{"city": "MAYLENE", "loc": [-86.872745, 33.231694], "pop": 3727, "state": "AL", "_id": "35114"} -{"city": "MONTEVALLO", "loc": [-86.862228, 33.124765], "pop": 11638, "state": "AL", "_id": "35115"} -{"city": "MORRIS", "loc": [-86.772551, 33.739172], "pop": 3622, "state": "AL", "_id": "35116"} -{"city": "MOUNT OLIVE", "loc": [-86.87171, 33.67678], "pop": 3841, "state": "AL", "_id": "35117"} -{"city": "SYLVAN SPRINGS", "loc": [-87.043998, 33.540696], "pop": 3948, "state": "AL", "_id": "35118"} -{"city": "ODENVILLE", "loc": [-86.408952, 33.675611], "pop": 1123, "state": "AL", "_id": "35120"} -{"city": "ONEONTA", "loc": [-86.474118, 33.925858], "pop": 8956, "state": "AL", "_id": "35121"} -{"city": "INDIAN SPRINGS", "loc": [-86.806176, 33.31046], "pop": 7412, "state": "AL", "_id": "35124"} -{"city": "PELL CITY", "loc": [-86.343151, 33.597889], "pop": 17981, "state": "AL", "_id": "35125"} -{"city": "DIXIANA", "loc": [-86.656542, 33.708131], "pop": 17068, "state": "AL", "_id": "35126"} -{"city": "PLEASANT GROVE", "loc": [-86.976586, 33.488336], "pop": 8458, "state": "AL", "_id": "35127"} -{"city": "QUINTON", "loc": [-87.10066, 33.656065], "pop": 2198, "state": "AL", "_id": "35130"} -{"city": "RAGLAND", "loc": [-86.1619, 33.736677], "pop": 2797, "state": "AL", "_id": "35131"} -{"city": "REMLAP", "loc": [-86.641662, 33.846204], "pop": 6013, "state": "AL", "_id": "35133"} -{"city": "RIVERSIDE", "loc": [-86.198341, 33.608832], "pop": 1004, "state": "AL", "_id": "35135"} -{"city": "ROCKFORD", "loc": [-86.240006, 32.877957], "pop": 1819, "state": "AL", "_id": "35136"} -{"city": "SHELBY", "loc": [-86.553606, 33.078483], "pop": 1846, "state": "AL", "_id": "35143"} -{"city": "SPRINGVILLE", "loc": [-86.439407, 33.738647], "pop": 8723, "state": "AL", "_id": "35146"} -{"city": "STERRETT", "loc": [-86.491732, 33.446103], "pop": 617, "state": "AL", "_id": "35147"} -{"city": "SUMITON", "loc": [-87.044545, 33.768005], "pop": 3066, "state": "AL", "_id": "35148"} -{"city": "SYLACAUGA", "loc": [-86.271257, 33.171675], "pop": 24424, "state": "AL", "_id": "35150"} -{"city": "TALLADEGA", "loc": [-86.113352, 33.435445], "pop": 29778, "state": "AL", "_id": "35160"} -{"city": "THORSBY", "loc": [-86.746724, 32.923755], "pop": 4131, "state": "AL", "_id": "35171"} -{"city": "TRAFFORD", "loc": [-86.743414, 33.819038], "pop": 909, "state": "AL", "_id": "35172"} -{"city": "TRUSSVILLE", "loc": [-86.598068, 33.633932], "pop": 13367, "state": "AL", "_id": "35173"} -{"city": "UNION GROVE", "loc": [-86.462793, 34.409345], "pop": 4921, "state": "AL", "_id": "35175"} -{"city": "VANDIVER", "loc": [-86.501278, 33.480704], "pop": 1066, "state": "AL", "_id": "35176"} -{"city": "VINCENT", "loc": [-86.399425, 33.401049], "pop": 295, "state": "AL", "_id": "35178"} -{"city": "VINEMONT", "loc": [-86.912512, 34.262121], "pop": 8852, "state": "AL", "_id": "35179"} -{"city": "WARRIOR", "loc": [-86.819849, 33.852862], "pop": 530, "state": "AL", "_id": "35180"} -{"city": "WEOGUFKA", "loc": [-86.304203, 33.02381], "pop": 1249, "state": "AL", "_id": "35183"} -{"city": "WEST BLOCTON", "loc": [-87.13694, 33.142431], "pop": 5276, "state": "AL", "_id": "35184"} -{"city": "WILSONVILLE", "loc": [-86.529894, 33.229255], "pop": 5224, "state": "AL", "_id": "35186"} -{"city": "WOODSTOCK", "loc": [-87.163469, 33.169808], "pop": 691, "state": "AL", "_id": "35188"} -{"city": "BIRMINGHAM", "loc": [-86.806626, 33.520994], "pop": 4064, "state": "AL", "_id": "35203"} -{"city": "BIRMINGHAM", "loc": [-86.837198, 33.51795], "pop": 18193, "state": "AL", "_id": "35204"} -{"city": "BIRMINGHAM", "loc": [-86.805937, 33.495144], "pop": 23024, "state": "AL", "_id": "35205"} -{"city": "BIRMINGHAM", "loc": [-86.719854, 33.567797], "pop": 22050, "state": "AL", "_id": "35206"} -{"city": "BIRMINGHAM", "loc": [-86.815344, 33.559383], "pop": 13901, "state": "AL", "_id": "35207"} -{"city": "BIRMINGHAM", "loc": [-86.879884, 33.497658], "pop": 19328, "state": "AL", "_id": "35208"} -{"city": "HOMEWOOD", "loc": [-86.806738, 33.469624], "pop": 24973, "state": "AL", "_id": "35209"} -{"city": "IRONDALE", "loc": [-86.685697, 33.532797], "pop": 15047, "state": "AL", "_id": "35210"} -{"city": "BIRMINGHAM", "loc": [-86.85904, 33.481565], "pop": 35836, "state": "AL", "_id": "35211"} -{"city": "BIRMINGHAM", "loc": [-86.749524, 33.540883], "pop": 17865, "state": "AL", "_id": "35212"} -{"city": "CRESTLINE HEIGHT", "loc": [-86.742108, 33.508195], "pop": 13191, "state": "AL", "_id": "35213"} -{"city": "BIRMINGHAM", "loc": [-86.886989, 33.555445], "pop": 23293, "state": "AL", "_id": "35214"} -{"city": "CENTER POINT", "loc": [-86.693197, 33.635447], "pop": 43862, "state": "AL", "_id": "35215"} -{"city": "VESTAVIA HILLS", "loc": [-86.790425, 33.41531], "pop": 29224, "state": "AL", "_id": "35216"} -{"city": "BIRMINGHAM", "loc": [-86.764995, 33.5887], "pop": 17366, "state": "AL", "_id": "35217"} -{"city": "BIRMINGHAM", "loc": [-86.892993, 33.505972], "pop": 12137, "state": "AL", "_id": "35218"} -{"city": "BIRMINGHAM", "loc": [-86.893493, 33.452316], "pop": 5850, "state": "AL", "_id": "35221"} -{"city": "BIRMINGHAM", "loc": [-86.766579, 33.521859], "pop": 10035, "state": "AL", "_id": "35222"} -{"city": "MOUNTAIN BROOK", "loc": [-86.736584, 33.488726], "pop": 11117, "state": "AL", "_id": "35223"} -{"city": "BIRMINGHAM", "loc": [-86.934193, 33.519126], "pop": 7894, "state": "AL", "_id": "35224"} -{"city": "BLUFF PARK", "loc": [-86.831257, 33.403675], "pop": 23992, "state": "AL", "_id": "35226"} -{"city": "MIDFIELD", "loc": [-86.914703, 33.462446], "pop": 9294, "state": "AL", "_id": "35228"} -{"city": "BIRMINGHAM", "loc": [-86.800257, 33.506161], "pop": 842, "state": "AL", "_id": "35233"} -{"city": "BIRMINGHAM", "loc": [-86.80685, 33.53775], "pop": 10928, "state": "AL", "_id": "35234"} -{"city": "CENTER POINT", "loc": [-86.661051, 33.618045], "pop": 15873, "state": "AL", "_id": "35235"} -{"city": "SHOAL CREEK", "loc": [-86.705511, 33.401559], "pop": 16228, "state": "AL", "_id": "35242"} -{"city": "CAHABA HEIGHTS", "loc": [-86.743676, 33.446053], "pop": 13091, "state": "AL", "_id": "35243"} -{"city": "HOOVER", "loc": [-86.776381, 33.371776], "pop": 9758, "state": "AL", "_id": "35244"} -{"city": "TUSCALOOSA", "loc": [-87.562666, 33.196891], "pop": 42124, "state": "AL", "_id": "35401"} -{"city": "HOLT", "loc": [-87.488079, 33.210914], "pop": 21997, "state": "AL", "_id": "35404"} -{"city": "TUSCALOOSA", "loc": [-87.514435, 33.161704], "pop": 23663, "state": "AL", "_id": "35405"} -{"city": "TUSCALOOSA", "loc": [-87.536035, 33.272174], "pop": 12578, "state": "AL", "_id": "35406"} -{"city": "STEWART", "loc": [-87.708558, 32.872354], "pop": 1745, "state": "AL", "_id": "35441"} -{"city": "ALICEVILLE", "loc": [-88.166689, 33.122813], "pop": 5196, "state": "AL", "_id": "35442"} -{"city": "BOLIGEE", "loc": [-88.026652, 32.774646], "pop": 1073, "state": "AL", "_id": "35443"} -{"city": "BROOKWOOD", "loc": [-87.309025, 33.277523], "pop": 2319, "state": "AL", "_id": "35444"} -{"city": "BUHL", "loc": [-87.718886, 33.249448], "pop": 1662, "state": "AL", "_id": "35446"} -{"city": "CARROLLTON", "loc": [-88.132122, 33.248506], "pop": 5007, "state": "AL", "_id": "35447"} -{"city": "COKER", "loc": [-87.636528, 33.252612], "pop": 5747, "state": "AL", "_id": "35452"} -{"city": "COTTONDALE", "loc": [-87.387051, 33.176667], "pop": 4727, "state": "AL", "_id": "35453"} -{"city": "DUNCANVILLE", "loc": [-87.494977, 33.082815], "pop": 5514, "state": "AL", "_id": "35456"} -{"city": "ECHOLA", "loc": [-87.807202, 33.316559], "pop": 223, "state": "AL", "_id": "35457"} -{"city": "ELROD", "loc": [-87.801429, 33.343678], "pop": 809, "state": "AL", "_id": "35458"} -{"city": "EMELLE", "loc": [-88.305747, 32.754963], "pop": 491, "state": "AL", "_id": "35459"} -{"city": "EPES", "loc": [-88.161443, 32.763371], "pop": 1391, "state": "AL", "_id": "35460"} -{"city": "ETHELSVILLE", "loc": [-88.221987, 33.386816], "pop": 719, "state": "AL", "_id": "35461"} -{"city": "EUTAW", "loc": [-87.930297, 32.888871], "pop": 6586, "state": "AL", "_id": "35462"} -{"city": "FOSTERS", "loc": [-87.735688, 33.135859], "pop": 2100, "state": "AL", "_id": "35463"} -{"city": "GAINESVILLE", "loc": [-88.271558, 32.908364], "pop": 1051, "state": "AL", "_id": "35464"} -{"city": "GORDO", "loc": [-87.900504, 33.346917], "pop": 4333, "state": "AL", "_id": "35466"} -{"city": "KNOXVILLE", "loc": [-87.791855, 32.982423], "pop": 373, "state": "AL", "_id": "35469"} -{"city": "COATOPA", "loc": [-88.173592, 32.588509], "pop": 6055, "state": "AL", "_id": "35470"} -{"city": "CYPRESS", "loc": [-87.615134, 32.978853], "pop": 2659, "state": "AL", "_id": "35474"} -{"city": "NORTHPORT", "loc": [-87.591441, 33.283425], "pop": 20114, "state": "AL", "_id": "35476"} -{"city": "RALPH", "loc": [-87.744984, 33.062868], "pop": 927, "state": "AL", "_id": "35480"} -{"city": "REFORM", "loc": [-88.020277, 33.395945], "pop": 4062, "state": "AL", "_id": "35481"} -{"city": "VANCE", "loc": [-87.257362, 33.17498], "pop": 1234, "state": "AL", "_id": "35490"} -{"city": "JASPER", "loc": [-87.249144, 33.871672], "pop": 30600, "state": "AL", "_id": "35501"} -{"city": "ADDISON", "loc": [-87.194766, 34.205571], "pop": 3263, "state": "AL", "_id": "35540"} -{"city": "ARLEY", "loc": [-87.182761, 34.063234], "pop": 2645, "state": "AL", "_id": "35541"} -{"city": "BANKSTON", "loc": [-87.68965, 33.70083], "pop": 651, "state": "AL", "_id": "35542"} -{"city": "BEAR CREEK", "loc": [-87.686083, 34.213469], "pop": 3748, "state": "AL", "_id": "35543"} -{"city": "BEAVERTON", "loc": [-88.01567, 33.942877], "pop": 1014, "state": "AL", "_id": "35544"} -{"city": "BERRY", "loc": [-87.622563, 33.694485], "pop": 4887, "state": "AL", "_id": "35546"} -{"city": "BRILLIANT", "loc": [-87.756315, 34.037702], "pop": 2515, "state": "AL", "_id": "35548"} -{"city": "CARBON HILL", "loc": [-87.540328, 33.909252], "pop": 3597, "state": "AL", "_id": "35549"} -{"city": "CORDOVA", "loc": [-87.18406, 33.768033], "pop": 5273, "state": "AL", "_id": "35550"} -{"city": "DETROIT", "loc": [-88.16063, 34.010874], "pop": 938, "state": "AL", "_id": "35552"} -{"city": "DOUBLE SPRINGS", "loc": [-87.397431, 34.138682], "pop": 4797, "state": "AL", "_id": "35553"} -{"city": "ELDRIDGE", "loc": [-87.619397, 33.931546], "pop": 421, "state": "AL", "_id": "35554"} -{"city": "FAYETTE", "loc": [-87.834647, 33.697397], "pop": 10776, "state": "AL", "_id": "35555"} -{"city": "GUIN", "loc": [-87.90238, 33.967624], "pop": 3948, "state": "AL", "_id": "35563"} -{"city": "HACKLEBURG", "loc": [-87.860762, 34.267803], "pop": 2828, "state": "AL", "_id": "35564"} -{"city": "HALEYVILLE", "loc": [-87.593811, 34.231423], "pop": 9141, "state": "AL", "_id": "35565"} -{"city": "HAMILTON", "loc": [-88.008521, 34.153413], "pop": 11184, "state": "AL", "_id": "35570"} -{"city": "HODGES", "loc": [-87.959023, 34.341664], "pop": 798, "state": "AL", "_id": "35571"} -{"city": "HOUSTON", "loc": [-87.26183, 34.118042], "pop": 429, "state": "AL", "_id": "35572"} -{"city": "KENNEDY", "loc": [-88.05172, 33.574964], "pop": 3618, "state": "AL", "_id": "35574"} -{"city": "LYNN", "loc": [-87.539814, 34.052984], "pop": 1778, "state": "AL", "_id": "35575"} -{"city": "MILLPORT", "loc": [-88.200003, 33.54013], "pop": 2286, "state": "AL", "_id": "35576"} -{"city": "NAUVOO", "loc": [-87.432449, 33.929859], "pop": 5004, "state": "AL", "_id": "35578"} -{"city": "OAKMAN", "loc": [-87.368574, 33.700174], "pop": 3700, "state": "AL", "_id": "35579"} -{"city": "PARRISH", "loc": [-87.265773, 33.721307], "pop": 4775, "state": "AL", "_id": "35580"} -{"city": "PHIL CAMPBELL", "loc": [-87.715431, 34.347018], "pop": 5345, "state": "AL", "_id": "35581"} -{"city": "RED BAY", "loc": [-88.112914, 34.451259], "pop": 5159, "state": "AL", "_id": "35582"} -{"city": "SPRUCE PINE", "loc": [-87.712149, 34.384796], "pop": 465, "state": "AL", "_id": "35585"} -{"city": "SULLIGENT", "loc": [-88.150774, 33.8549], "pop": 4489, "state": "AL", "_id": "35586"} -{"city": "TOWNLEY", "loc": [-87.437248, 33.84702], "pop": 1819, "state": "AL", "_id": "35587"} -{"city": "VERNON", "loc": [-88.097919, 33.761285], "pop": 4752, "state": "AL", "_id": "35592"} -{"city": "VINA", "loc": [-88.077422, 34.37116], "pop": 888, "state": "AL", "_id": "35593"} -{"city": "WINFIELD", "loc": [-87.79716, 33.930256], "pop": 6750, "state": "AL", "_id": "35594"} -{"city": "DECATUR", "loc": [-86.98868, 34.589599], "pop": 36696, "state": "AL", "_id": "35601"} -{"city": "DECATUR", "loc": [-87.000389, 34.548417], "pop": 17861, "state": "AL", "_id": "35603"} -{"city": "ANDERSON", "loc": [-87.272327, 34.951777], "pop": 2039, "state": "AL", "_id": "35610"} -{"city": "ATHENS", "loc": [-86.970733, 34.803604], "pop": 35441, "state": "AL", "_id": "35611"} -{"city": "CHEROKEE", "loc": [-87.972445, 34.744188], "pop": 4811, "state": "AL", "_id": "35616"} -{"city": "COURTLAND", "loc": [-87.314255, 34.671866], "pop": 2733, "state": "AL", "_id": "35618"} -{"city": "DANVILLE", "loc": [-87.053187, 34.452189], "pop": 4614, "state": "AL", "_id": "35619"} -{"city": "ELKMONT", "loc": [-86.910452, 34.915241], "pop": 8013, "state": "AL", "_id": "35620"} -{"city": "EVA", "loc": [-86.704427, 34.347778], "pop": 3977, "state": "AL", "_id": "35621"} -{"city": "FALKVILLE", "loc": [-86.913086, 34.347653], "pop": 5392, "state": "AL", "_id": "35622"} -{"city": "FLORENCE", "loc": [-87.655985, 34.830547], "pop": 38725, "state": "AL", "_id": "35630"} -{"city": "FLORENCE", "loc": [-87.739778, 34.882471], "pop": 16478, "state": "AL", "_id": "35633"} -{"city": "HARTSELLE", "loc": [-86.924235, 34.448206], "pop": 17963, "state": "AL", "_id": "35640"} -{"city": "HILLSBORO", "loc": [-87.180379, 34.64763], "pop": 1967, "state": "AL", "_id": "35643"} -{"city": "KILLEN", "loc": [-87.508185, 34.901632], "pop": 11758, "state": "AL", "_id": "35645"} -{"city": "LEIGHTON", "loc": [-87.522133, 34.710982], "pop": 3046, "state": "AL", "_id": "35646"} -{"city": "LESTER", "loc": [-87.143396, 34.918364], "pop": 3108, "state": "AL", "_id": "35647"} -{"city": "LEXINGTON", "loc": [-87.393519, 34.955924], "pop": 2241, "state": "AL", "_id": "35648"} -{"city": "MOULTON", "loc": [-87.222385, 34.505836], "pop": 17288, "state": "AL", "_id": "35650"} -{"city": "MOUNT HOPE", "loc": [-87.451453, 34.462969], "pop": 1821, "state": "AL", "_id": "35651"} -{"city": "ROGERSVILLE", "loc": [-87.323671, 34.849544], "pop": 6521, "state": "AL", "_id": "35652"} -{"city": "RUSSELLVILLE", "loc": [-87.725726, 34.506568], "pop": 17767, "state": "AL", "_id": "35653"} -{"city": "SHEFFIELD", "loc": [-87.697057, 34.757829], "pop": 10685, "state": "AL", "_id": "35660"} -{"city": "MUSCLE SHOALS", "loc": [-87.630443, 34.756136], "pop": 11777, "state": "AL", "_id": "35661"} -{"city": "SOMERVILLE", "loc": [-86.800931, 34.499548], "pop": 5184, "state": "AL", "_id": "35670"} -{"city": "TANNER", "loc": [-86.989152, 34.713037], "pop": 2578, "state": "AL", "_id": "35671"} -{"city": "TOWN CREEK", "loc": [-87.426195, 34.649138], "pop": 9049, "state": "AL", "_id": "35672"} -{"city": "TRINITY", "loc": [-87.09127, 34.591771], "pop": 1758, "state": "AL", "_id": "35673"} -{"city": "TUSCUMBIA", "loc": [-87.683259, 34.687432], "pop": 17880, "state": "AL", "_id": "35674"} -{"city": "WATERLOO", "loc": [-87.962412, 34.93568], "pop": 1899, "state": "AL", "_id": "35677"} -{"city": "ARDMORE", "loc": [-86.834612, 34.980447], "pop": 1898, "state": "AL", "_id": "35739"} -{"city": "BRIDGEPORT", "loc": [-85.727681, 34.944638], "pop": 3988, "state": "AL", "_id": "35740"} -{"city": "BROWNSBORO", "loc": [-86.468703, 34.716733], "pop": 1793, "state": "AL", "_id": "35741"} -{"city": "DUTTON", "loc": [-85.906729, 34.604558], "pop": 2948, "state": "AL", "_id": "35744"} -{"city": "ESTILLFORK", "loc": [-86.171571, 34.913017], "pop": 718, "state": "AL", "_id": "35745"} -{"city": "FACKLER", "loc": [-85.984648, 34.82589], "pop": 396, "state": "AL", "_id": "35746"} -{"city": "GRANT", "loc": [-86.259041, 34.495902], "pop": 8345, "state": "AL", "_id": "35747"} -{"city": "GURLEY", "loc": [-86.394028, 34.713964], "pop": 4642, "state": "AL", "_id": "35748"} -{"city": "HARVEST", "loc": [-86.749929, 34.82732], "pop": 5701, "state": "AL", "_id": "35749"} -{"city": "HAZEL GREEN", "loc": [-86.593484, 34.949627], "pop": 7751, "state": "AL", "_id": "35750"} -{"city": "HOLLYTREE", "loc": [-86.233627, 34.798979], "pop": 260, "state": "AL", "_id": "35751"} -{"city": "HOLLYWOOD", "loc": [-85.953173, 34.730428], "pop": 2038, "state": "AL", "_id": "35752"} -{"city": "LACEYS SPRING", "loc": [-86.612869, 34.499647], "pop": 7040, "state": "AL", "_id": "35754"} -{"city": "LANGSTON", "loc": [-86.10563, 34.497831], "pop": 1041, "state": "AL", "_id": "35755"} -{"city": "TRIANA", "loc": [-86.750951, 34.713409], "pop": 24398, "state": "AL", "_id": "35758"} -{"city": "MERIDIANVILLE", "loc": [-86.578879, 34.861779], "pop": 2597, "state": "AL", "_id": "35759"} -{"city": "NEW HOPE", "loc": [-86.3961, 34.549445], "pop": 4075, "state": "AL", "_id": "35760"} -{"city": "NEW MARKET", "loc": [-86.448742, 34.899991], "pop": 5825, "state": "AL", "_id": "35761"} -{"city": "BIG COVE", "loc": [-86.466577, 34.612859], "pop": 3156, "state": "AL", "_id": "35763"} -{"city": "PAINT ROCK", "loc": [-86.332562, 34.667122], "pop": 553, "state": "AL", "_id": "35764"} -{"city": "PISGAH", "loc": [-85.803044, 34.688601], "pop": 3717, "state": "AL", "_id": "35765"} -{"city": "PRINCETON", "loc": [-86.250068, 34.840217], "pop": 273, "state": "AL", "_id": "35766"} -{"city": "HYTOP", "loc": [-86.051336, 34.67227], "pop": 16934, "state": "AL", "_id": "35768"} -{"city": "SECTION", "loc": [-85.994002, 34.543275], "pop": 2590, "state": "AL", "_id": "35771"} -{"city": "STEVENSON", "loc": [-85.850803, 34.876885], "pop": 5210, "state": "AL", "_id": "35772"} -{"city": "TONEY", "loc": [-86.699951, 34.911644], "pop": 5953, "state": "AL", "_id": "35773"} -{"city": "TRENTON", "loc": [-86.291264, 34.740065], "pop": 381, "state": "AL", "_id": "35774"} -{"city": "VALHERMOSO SPRIN", "loc": [-86.678, 34.538145], "pop": 667, "state": "AL", "_id": "35775"} -{"city": "WOODVILLE", "loc": [-86.22961, 34.668927], "pop": 2222, "state": "AL", "_id": "35776"} -{"city": "HUNTSVILLE", "loc": [-86.567318, 34.726866], "pop": 25513, "state": "AL", "_id": "35801"} -{"city": "HUNTSVILLE", "loc": [-86.560347, 34.667922], "pop": 21069, "state": "AL", "_id": "35802"} -{"city": "HUNTSVILLE", "loc": [-86.55096, 34.620506], "pop": 23380, "state": "AL", "_id": "35803"} -{"city": "HUNTSVILLE", "loc": [-86.616493, 34.705943], "pop": 24637, "state": "AL", "_id": "35805"} -{"city": "HUNTSVILLE", "loc": [-86.670411, 34.744765], "pop": 10121, "state": "AL", "_id": "35806"} -{"city": "HUNTSVILLE", "loc": [-86.653821, 34.684525], "pop": 4988, "state": "AL", "_id": "35808"} -{"city": "HUNTSVILLE", "loc": [-86.609063, 34.778378], "pop": 32896, "state": "AL", "_id": "35810"} -{"city": "HUNTSVILLE", "loc": [-86.543786, 34.778949], "pop": 19008, "state": "AL", "_id": "35811"} -{"city": "HUNTSVILLE", "loc": [-86.624948, 34.738864], "pop": 13736, "state": "AL", "_id": "35816"} -{"city": "HUNTSVILLE", "loc": [-86.729486, 34.658321], "pop": 770, "state": "AL", "_id": "35824"} -{"city": "SOUTHSIDE", "loc": [-86.010279, 33.997248], "pop": 44165, "state": "AL", "_id": "35901"} -{"city": "HOKES BLUFF", "loc": [-85.928724, 33.997057], "pop": 20057, "state": "AL", "_id": "35903"} -{"city": "GADSDEN", "loc": [-86.049479, 34.021694], "pop": 7002, "state": "AL", "_id": "35904"} -{"city": "GLENCOE", "loc": [-85.927586, 33.956787], "pop": 4256, "state": "AL", "_id": "35905"} -{"city": "ALBERTVILLE", "loc": [-86.206447, 34.273859], "pop": 21033, "state": "AL", "_id": "35950"} -{"city": "SNEAD", "loc": [-86.350879, 34.042916], "pop": 9472, "state": "AL", "_id": "35952"} -{"city": "ASHVILLE", "loc": [-86.255167, 33.837366], "pop": 5988, "state": "AL", "_id": "35953"} -{"city": "ATTALLA", "loc": [-86.096717, 34.029597], "pop": 11904, "state": "AL", "_id": "35954"} -{"city": "BOAZ", "loc": [-86.148003, 34.173686], "pop": 16955, "state": "AL", "_id": "35957"} -{"city": "BRYANT", "loc": [-85.632445, 34.945026], "pop": 1700, "state": "AL", "_id": "35958"} -{"city": "CEDAR BLUFF", "loc": [-85.602567, 34.241662], "pop": 3574, "state": "AL", "_id": "35959"} -{"city": "CENTRE", "loc": [-85.609229, 34.111592], "pop": 10294, "state": "AL", "_id": "35960"} -{"city": "COLLINSVILLE", "loc": [-85.861969, 34.267957], "pop": 3245, "state": "AL", "_id": "35961"} -{"city": "CROSSVILLE", "loc": [-86.030575, 34.258784], "pop": 4874, "state": "AL", "_id": "35962"} -{"city": "DAWSON", "loc": [-85.929161, 34.356838], "pop": 1531, "state": "AL", "_id": "35963"} -{"city": "FLAT ROCK", "loc": [-85.708372, 34.807598], "pop": 2141, "state": "AL", "_id": "35966"} -{"city": "FORT PAYNE", "loc": [-85.712394, 34.436792], "pop": 15893, "state": "AL", "_id": "35967"} -{"city": "FYFFE", "loc": [-85.928849, 34.437284], "pop": 3453, "state": "AL", "_id": "35971"} -{"city": "GALLANT", "loc": [-86.234606, 33.997586], "pop": 337, "state": "AL", "_id": "35972"} -{"city": "GAYLESVILLE", "loc": [-85.558893, 34.357324], "pop": 2291, "state": "AL", "_id": "35973"} -{"city": "GERALDINE", "loc": [-86.040393, 34.343864], "pop": 3291, "state": "AL", "_id": "35974"} -{"city": "GROVEOAK", "loc": [-86.040023, 34.435874], "pop": 1098, "state": "AL", "_id": "35975"} -{"city": "GUNTERSVILLE", "loc": [-86.305463, 34.32193], "pop": 11234, "state": "AL", "_id": "35976"} -{"city": "HENAGAR", "loc": [-85.727381, 34.618604], "pop": 2973, "state": "AL", "_id": "35978"} -{"city": "HIGDON", "loc": [-85.622507, 34.873247], "pop": 1324, "state": "AL", "_id": "35979"} -{"city": "HORTON", "loc": [-86.317318, 34.190033], "pop": 4195, "state": "AL", "_id": "35980"} -{"city": "IDER", "loc": [-85.641577, 34.735059], "pop": 5161, "state": "AL", "_id": "35981"} -{"city": "LEESBURG", "loc": [-85.77051, 34.191083], "pop": 3189, "state": "AL", "_id": "35983"} -{"city": "MENTONE", "loc": [-85.577728, 34.587236], "pop": 2099, "state": "AL", "_id": "35984"} -{"city": "RAINSVILLE", "loc": [-85.844605, 34.498884], "pop": 7498, "state": "AL", "_id": "35986"} -{"city": "STEELE", "loc": [-86.228929, 33.941679], "pop": 1830, "state": "AL", "_id": "35987"} -{"city": "SYLVANIA", "loc": [-85.792128, 34.558962], "pop": 2318, "state": "AL", "_id": "35988"} -{"city": "VALLEY HEAD", "loc": [-85.627208, 34.5697], "pop": 1211, "state": "AL", "_id": "35989"} -{"city": "AUTAUGAVILLE", "loc": [-86.714938, 32.462462], "pop": 2641, "state": "AL", "_id": "36003"} -{"city": "EUFAULA", "loc": [-85.239657, 31.786701], "pop": 2669, "state": "AL", "_id": "36004"} -{"city": "BANKS", "loc": [-85.799844, 31.836792], "pop": 1857, "state": "AL", "_id": "36005"} -{"city": "BILLINGSLEY", "loc": [-86.716301, 32.610578], "pop": 1869, "state": "AL", "_id": "36006"} -{"city": "BRANTLEY", "loc": [-86.274343, 31.570963], "pop": 2623, "state": "AL", "_id": "36009"} -{"city": "BRUNDIDGE", "loc": [-85.817668, 31.701324], "pop": 4320, "state": "AL", "_id": "36010"} -{"city": "CECIL", "loc": [-86.011241, 32.300891], "pop": 407, "state": "AL", "_id": "36013"} -{"city": "CLAYTON", "loc": [-85.450932, 31.887413], "pop": 3106, "state": "AL", "_id": "36016"} -{"city": "CLIO", "loc": [-85.590419, 31.68521], "pop": 3178, "state": "AL", "_id": "36017"} -{"city": "DEATSVILLE", "loc": [-86.338211, 32.613405], "pop": 5913, "state": "AL", "_id": "36022"} -{"city": "ECLECTIC", "loc": [-86.031614, 32.65419], "pop": 6105, "state": "AL", "_id": "36024"} -{"city": "ELMORE", "loc": [-86.316134, 32.545378], "pop": 3114, "state": "AL", "_id": "36025"} -{"city": "EQUALITY", "loc": [-86.105064, 32.813908], "pop": 1121, "state": "AL", "_id": "36026"} -{"city": "EUFAULA", "loc": [-85.165605, 31.905063], "pop": 14189, "state": "AL", "_id": "36027"} -{"city": "DOZIER", "loc": [-86.366315, 31.506614], "pop": 741, "state": "AL", "_id": "36028"} -{"city": "FITZPATRICK", "loc": [-85.888329, 32.151607], "pop": 736, "state": "AL", "_id": "36029"} -{"city": "FOREST HOME", "loc": [-86.786377, 31.85318], "pop": 1528, "state": "AL", "_id": "36030"} -{"city": "FORT DAVIS", "loc": [-85.700688, 32.28945], "pop": 891, "state": "AL", "_id": "36031"} -{"city": "FORT DEPOSIT", "loc": [-86.576142, 31.995979], "pop": 2435, "state": "AL", "_id": "36032"} -{"city": "GEORGIANA", "loc": [-86.733965, 31.628662], "pop": 3672, "state": "AL", "_id": "36033"} -{"city": "GLENWOOD", "loc": [-86.170972, 31.664143], "pop": 109, "state": "AL", "_id": "36034"} -{"city": "GOSHEN", "loc": [-86.104851, 31.82497], "pop": 2056, "state": "AL", "_id": "36035"} -{"city": "GRADY", "loc": [-86.129246, 32.019397], "pop": 1811, "state": "AL", "_id": "36036"} -{"city": "GREENVILLE", "loc": [-86.622919, 31.810036], "pop": 15476, "state": "AL", "_id": "36037"} -{"city": "GANTT", "loc": [-86.419836, 31.416909], "pop": 3298, "state": "AL", "_id": "36038"} -{"city": "HARDAWAY", "loc": [-85.883837, 32.312939], "pop": 354, "state": "AL", "_id": "36039"} -{"city": "HAYNEVILLE", "loc": [-86.654994, 32.195707], "pop": 5010, "state": "AL", "_id": "36040"} -{"city": "HIGHLAND HOME", "loc": [-86.297111, 31.928292], "pop": 3068, "state": "AL", "_id": "36041"} -{"city": "HONORAVILLE", "loc": [-86.398064, 31.878397], "pop": 838, "state": "AL", "_id": "36042"} -{"city": "HOPE HULL", "loc": [-86.393189, 32.224256], "pop": 2961, "state": "AL", "_id": "36043"} -{"city": "LAPINE", "loc": [-86.278534, 31.985423], "pop": 338, "state": "AL", "_id": "36046"} -{"city": "LETOHATCHEE", "loc": [-86.488013, 32.086199], "pop": 2348, "state": "AL", "_id": "36047"} -{"city": "LOUISVILLE", "loc": [-85.558062, 31.794262], "pop": 2279, "state": "AL", "_id": "36048"} -{"city": "LUVERNE", "loc": [-86.256601, 31.70739], "pop": 4274, "state": "AL", "_id": "36049"} -{"city": "MARBURY", "loc": [-86.510138, 32.639373], "pop": 3573, "state": "AL", "_id": "36051"} -{"city": "MATHEWS", "loc": [-86.041262, 32.128288], "pop": 836, "state": "AL", "_id": "36052"} -{"city": "MIDWAY", "loc": [-85.531523, 32.09552], "pop": 1873, "state": "AL", "_id": "36053"} -{"city": "MILLBROOK", "loc": [-86.364125, 32.499485], "pop": 9049, "state": "AL", "_id": "36054"} -{"city": "PEROTE", "loc": [-85.74773, 32.003459], "pop": 1792, "state": "AL", "_id": "36061"} -{"city": "PIKE ROAD", "loc": [-86.09589, 32.335705], "pop": 3398, "state": "AL", "_id": "36064"} -{"city": "PRATTVILLE", "loc": [-86.42997, 32.478695], "pop": 11059, "state": "AL", "_id": "36066"} -{"city": "PRATTVILLE", "loc": [-86.483072, 32.471501], "pop": 16536, "state": "AL", "_id": "36067"} -{"city": "RAMER", "loc": [-86.246329, 32.067745], "pop": 1712, "state": "AL", "_id": "36069"} -{"city": "RUTLEDGE", "loc": [-86.348464, 31.755867], "pop": 1976, "state": "AL", "_id": "36071"} -{"city": "SHORTER", "loc": [-85.91616, 32.383585], "pop": 1876, "state": "AL", "_id": "36075"} -{"city": "TALLASSEE", "loc": [-85.89781, 32.550997], "pop": 12046, "state": "AL", "_id": "36078"} -{"city": "TITUS", "loc": [-86.239334, 32.690019], "pop": 2683, "state": "AL", "_id": "36080"} -{"city": "TROY", "loc": [-85.965493, 31.794471], "pop": 19358, "state": "AL", "_id": "36081"} -{"city": "TUSKEGEE", "loc": [-85.68606, 32.431623], "pop": 10687, "state": "AL", "_id": "36083"} -{"city": "TUSKEGEE INSTITU", "loc": [-85.714848, 32.417699], "pop": 6915, "state": "AL", "_id": "36088"} -{"city": "UNION SPRINGS", "loc": [-85.678746, 32.166252], "pop": 7555, "state": "AL", "_id": "36089"} -{"city": "VERBENA", "loc": [-86.543753, 32.759565], "pop": 3702, "state": "AL", "_id": "36091"} -{"city": "WETUMPKA", "loc": [-86.188039, 32.54509], "pop": 13725, "state": "AL", "_id": "36092"} -{"city": "MONTGOMERY", "loc": [-86.308129, 32.373037], "pop": 17086, "state": "AL", "_id": "36104"} -{"city": "MONTGOMERY", "loc": [-86.310449, 32.32573], "pop": 16486, "state": "AL", "_id": "36105"} -{"city": "MONTGOMERY", "loc": [-86.267278, 32.354268], "pop": 15744, "state": "AL", "_id": "36106"} -{"city": "MONTGOMERY", "loc": [-86.279885, 32.380405], "pop": 10345, "state": "AL", "_id": "36107"} -{"city": "MONTGOMERY", "loc": [-86.352904, 32.341682], "pop": 30780, "state": "AL", "_id": "36108"} -{"city": "MONTGOMERY", "loc": [-86.243394, 32.383443], "pop": 25282, "state": "AL", "_id": "36109"} -{"city": "MONTGOMERY", "loc": [-86.274997, 32.421686], "pop": 12551, "state": "AL", "_id": "36110"} -{"city": "MONTGOMERY", "loc": [-86.271543, 32.337363], "pop": 11600, "state": "AL", "_id": "36111"} -{"city": "MAXWELL A F B", "loc": [-86.355848, 32.388133], "pop": 2788, "state": "AL", "_id": "36113"} -{"city": "GUNTER AFS", "loc": [-86.247327, 32.406814], "pop": 1348, "state": "AL", "_id": "36115"} -{"city": "MONTGOMERY", "loc": [-86.242056, 32.312943], "pop": 32314, "state": "AL", "_id": "36116"} -{"city": "MONTGOMERY", "loc": [-86.183299, 32.373568], "pop": 21623, "state": "AL", "_id": "36117"} -{"city": "ANNISTON", "loc": [-85.838152, 33.653896], "pop": 38370, "state": "AL", "_id": "36201"} -{"city": "OXFORD", "loc": [-85.83347, 33.596829], "pop": 12407, "state": "AL", "_id": "36203"} -{"city": "FORT MC CLELLAN", "loc": [-85.801467, 33.710168], "pop": 4128, "state": "AL", "_id": "36205"} -{"city": "ANNISTON", "loc": [-85.838904, 33.719124], "pop": 10915, "state": "AL", "_id": "36206"} -{"city": "ALEXANDRIA", "loc": [-85.892447, 33.780785], "pop": 5776, "state": "AL", "_id": "36250"} -{"city": "ASHLAND", "loc": [-85.828976, 33.247363], "pop": 4518, "state": "AL", "_id": "36251"} -{"city": "CRAGFORD", "loc": [-85.710797, 33.217148], "pop": 796, "state": "AL", "_id": "36255"} -{"city": "DAVISTON", "loc": [-85.753831, 33.033471], "pop": 2334, "state": "AL", "_id": "36256"} -{"city": "DELTA", "loc": [-85.679279, 33.457303], "pop": 1405, "state": "AL", "_id": "36258"} -{"city": "EASTABOGA", "loc": [-85.96075, 33.603132], "pop": 3999, "state": "AL", "_id": "36260"} -{"city": "FRUITHURST", "loc": [-85.43814, 33.771732], "pop": 1473, "state": "AL", "_id": "36262"} -{"city": "GRAHAM", "loc": [-85.334034, 33.462976], "pop": 374, "state": "AL", "_id": "36263"} -{"city": "HEFLIN", "loc": [-85.588471, 33.611515], "pop": 6577, "state": "AL", "_id": "36264"} -{"city": "JACKSONVILLE", "loc": [-85.77518, 33.830966], "pop": 16438, "state": "AL", "_id": "36265"} -{"city": "LINEVILLE", "loc": [-85.734609, 33.328613], "pop": 4345, "state": "AL", "_id": "36266"} -{"city": "MILLERVILLE", "loc": [-85.969001, 33.158959], "pop": 863, "state": "AL", "_id": "36267"} -{"city": "MUNFORD", "loc": [-85.936322, 33.540987], "pop": 4998, "state": "AL", "_id": "36268"} -{"city": "MUSCADINE", "loc": [-85.378907, 33.752913], "pop": 265, "state": "AL", "_id": "36269"} -{"city": "NEWELL", "loc": [-85.505925, 33.440172], "pop": 2407, "state": "AL", "_id": "36270"} -{"city": "OHATCHEE", "loc": [-86.025357, 33.778779], "pop": 3369, "state": "AL", "_id": "36271"} -{"city": "PIEDMONT", "loc": [-85.645997, 33.838946], "pop": 12921, "state": "AL", "_id": "36272"} -{"city": "RANBURNE", "loc": [-85.378604, 33.561627], "pop": 3696, "state": "AL", "_id": "36273"} -{"city": "ROCK MILLS", "loc": [-85.357854, 33.156443], "pop": 9430, "state": "AL", "_id": "36274"} -{"city": "WADLEY", "loc": [-85.551344, 33.149192], "pop": 1949, "state": "AL", "_id": "36276"} -{"city": "WEAVER", "loc": [-85.810666, 33.756286], "pop": 7453, "state": "AL", "_id": "36277"} -{"city": "WEDOWEE", "loc": [-85.473737, 33.301854], "pop": 3101, "state": "AL", "_id": "36278"} -{"city": "WELLINGTON", "loc": [-85.915325, 33.863843], "pop": 2137, "state": "AL", "_id": "36279"} -{"city": "WOODLAND", "loc": [-85.353768, 33.355453], "pop": 2291, "state": "AL", "_id": "36280"} -{"city": "TAYLOR", "loc": [-85.418036, 31.202888], "pop": 32689, "state": "AL", "_id": "36301"} -{"city": "NAPIER FIELD", "loc": [-85.412462, 31.255239], "pop": 32407, "state": "AL", "_id": "36303"} -{"city": "ABBEVILLE", "loc": [-85.279044, 31.575479], "pop": 5416, "state": "AL", "_id": "36310"} -{"city": "ARITON", "loc": [-85.707716, 31.582996], "pop": 1434, "state": "AL", "_id": "36311"} -{"city": "ASHFORD", "loc": [-85.253488, 31.1888], "pop": 5115, "state": "AL", "_id": "36312"} -{"city": "BLACK", "loc": [-85.745634, 31.01318], "pop": 183, "state": "AL", "_id": "36314"} -{"city": "CHANCELLOR", "loc": [-85.910897, 31.173379], "pop": 620, "state": "AL", "_id": "36316"} -{"city": "CLOPTON", "loc": [-85.482308, 31.602843], "pop": 157, "state": "AL", "_id": "36317"} -{"city": "COFFEE SPRINGS", "loc": [-85.918224, 31.138758], "pop": 750, "state": "AL", "_id": "36318"} -{"city": "COLUMBIA", "loc": [-85.145488, 31.335235], "pop": 2934, "state": "AL", "_id": "36319"} -{"city": "COTTONWOOD", "loc": [-85.375665, 31.098934], "pop": 8517, "state": "AL", "_id": "36320"} -{"city": "DALEVILLE", "loc": [-85.730473, 31.281091], "pop": 8885, "state": "AL", "_id": "36322"} -{"city": "ELBA", "loc": [-86.077728, 31.41373], "pop": 6662, "state": "AL", "_id": "36323"} -{"city": "ENTERPRISE", "loc": [-85.842111, 31.340789], "pop": 29102, "state": "AL", "_id": "36330"} -{"city": "GENEVA", "loc": [-85.884748, 31.041445], "pop": 5471, "state": "AL", "_id": "36340"} -{"city": "GORDON", "loc": [-85.123412, 31.10019], "pop": 2017, "state": "AL", "_id": "36343"} -{"city": "HARTFORD", "loc": [-85.719204, 31.08598], "pop": 4819, "state": "AL", "_id": "36344"} -{"city": "HEADLAND", "loc": [-85.332277, 31.353406], "pop": 4595, "state": "AL", "_id": "36345"} -{"city": "JACK", "loc": [-86.043083, 31.552392], "pop": 1517, "state": "AL", "_id": "36346"} -{"city": "MALVERN", "loc": [-85.522327, 31.157528], "pop": 1686, "state": "AL", "_id": "36349"} -{"city": "MIDLAND CITY", "loc": [-85.513033, 31.36716], "pop": 4854, "state": "AL", "_id": "36350"} -{"city": "NEW BROCKTON", "loc": [-85.940386, 31.36898], "pop": 1809, "state": "AL", "_id": "36351"} -{"city": "NEWTON", "loc": [-85.599229, 31.331064], "pop": 1660, "state": "AL", "_id": "36352"} -{"city": "NEWVILLE", "loc": [-85.328741, 31.440295], "pop": 1525, "state": "AL", "_id": "36353"} -{"city": "OZARK", "loc": [-85.643629, 31.439069], "pop": 19017, "state": "AL", "_id": "36360"} -{"city": "FORT RUCKER", "loc": [-85.721374, 31.348011], "pop": 7607, "state": "AL", "_id": "36362"} -{"city": "PANSEY", "loc": [-85.238549, 31.131778], "pop": 595, "state": "AL", "_id": "36370"} -{"city": "SHORTERVILLE", "loc": [-85.14584, 31.625627], "pop": 2503, "state": "AL", "_id": "36373"} -{"city": "SKIPPERVILLE", "loc": [-85.549578, 31.551588], "pop": 1048, "state": "AL", "_id": "36374"} -{"city": "SLOCOMB", "loc": [-85.582954, 31.095558], "pop": 4504, "state": "AL", "_id": "36375"} -{"city": "WEBB", "loc": [-85.254385, 31.265618], "pop": 1810, "state": "AL", "_id": "36376"} -{"city": "EVERGREEN", "loc": [-86.925771, 31.458009], "pop": 8556, "state": "AL", "_id": "36401"} -{"city": "ALLEN", "loc": [-87.66746, 31.624266], "pop": 0, "state": "AL", "_id": "36419"} -{"city": "ANDALUSIA", "loc": [-86.490468, 31.297142], "pop": 16920, "state": "AL", "_id": "36420"} -{"city": "BEATRICE", "loc": [-87.171912, 31.727324], "pop": 1620, "state": "AL", "_id": "36425"} -{"city": "EAST BREWTON", "loc": [-87.067325, 31.118926], "pop": 15479, "state": "AL", "_id": "36426"} -{"city": "CASTLEBERRY", "loc": [-87.026754, 31.326113], "pop": 2881, "state": "AL", "_id": "36432"} -{"city": "COY", "loc": [-87.392558, 31.888851], "pop": 1109, "state": "AL", "_id": "36435"} -{"city": "DICKINSON", "loc": [-87.656929, 31.717167], "pop": 272, "state": "AL", "_id": "36436"} -{"city": "FLOMATON", "loc": [-87.266365, 31.040233], "pop": 3585, "state": "AL", "_id": "36441"} -{"city": "FLORALA", "loc": [-86.338528, 31.017194], "pop": 3421, "state": "AL", "_id": "36442"} -{"city": "FRANKLIN", "loc": [-87.441237, 31.723815], "pop": 154, "state": "AL", "_id": "36444"} -{"city": "FRISCO CITY", "loc": [-87.381744, 31.423478], "pop": 6179, "state": "AL", "_id": "36445"} -{"city": "FULTON", "loc": [-87.708065, 31.790947], "pop": 2163, "state": "AL", "_id": "36446"} -{"city": "GROVE HILL", "loc": [-87.763571, 31.675502], "pop": 4089, "state": "AL", "_id": "36451"} -{"city": "KINSTON", "loc": [-86.069532, 31.262118], "pop": 3523, "state": "AL", "_id": "36453"} -{"city": "LENOX", "loc": [-87.19671, 31.327261], "pop": 515, "state": "AL", "_id": "36454"} -{"city": "MC KENZIE", "loc": [-86.735615, 31.54495], "pop": 1222, "state": "AL", "_id": "36456"} -{"city": "MONROEVILLE", "loc": [-87.340993, 31.51533], "pop": 10492, "state": "AL", "_id": "36460"} -{"city": "OPP", "loc": [-86.257105, 31.279861], "pop": 9901, "state": "AL", "_id": "36467"} -{"city": "PETERMAN", "loc": [-87.259984, 31.589953], "pop": 1826, "state": "AL", "_id": "36471"} -{"city": "RANGE", "loc": [-87.319739, 31.30169], "pop": 208, "state": "AL", "_id": "36473"} -{"city": "RED LEVEL", "loc": [-86.61209, 31.439931], "pop": 2005, "state": "AL", "_id": "36474"} -{"city": "REPTON", "loc": [-87.172039, 31.425569], "pop": 1750, "state": "AL", "_id": "36475"} -{"city": "SAMSON", "loc": [-86.067416, 31.104918], "pop": 4790, "state": "AL", "_id": "36477"} -{"city": "URIAH", "loc": [-87.570518, 31.313467], "pop": 1648, "state": "AL", "_id": "36480"} -{"city": "VREDENBURGH", "loc": [-87.416184, 31.73443], "pop": 2040, "state": "AL", "_id": "36481"} -{"city": "WHATLEY", "loc": [-87.656502, 31.639453], "pop": 790, "state": "AL", "_id": "36482"} -{"city": "WING", "loc": [-86.56119, 31.148127], "pop": 1583, "state": "AL", "_id": "36483"} -{"city": "ATMORE", "loc": [-87.487347, 31.057245], "pop": 15948, "state": "AL", "_id": "36502"} -{"city": "AXIS", "loc": [-88.04274, 31.006817], "pop": 140, "state": "AL", "_id": "36505"} -{"city": "BAY MINETTE", "loc": [-87.76444, 30.86354], "pop": 17816, "state": "AL", "_id": "36507"} -{"city": "BAYOU LA BATRE", "loc": [-88.250697, 30.407983], "pop": 4901, "state": "AL", "_id": "36509"} -{"city": "BIGBEE", "loc": [-88.165294, 31.589072], "pop": 264, "state": "AL", "_id": "36510"} -{"city": "BON SECOUR", "loc": [-87.721355, 30.328883], "pop": 2001, "state": "AL", "_id": "36511"} -{"city": "CARLTON", "loc": [-87.837793, 31.322449], "pop": 30, "state": "AL", "_id": "36515"} -{"city": "CHATOM", "loc": [-88.269887, 31.487638], "pop": 3094, "state": "AL", "_id": "36518"} -{"city": "CHUNCHULA", "loc": [-88.131126, 30.991178], "pop": 1322, "state": "AL", "_id": "36521"} -{"city": "CITRONELLE", "loc": [-88.254949, 31.042533], "pop": 7233, "state": "AL", "_id": "36522"} -{"city": "CODEN", "loc": [-88.168276, 30.418834], "pop": 3897, "state": "AL", "_id": "36523"} -{"city": "COFFEEVILLE", "loc": [-88.071542, 31.78428], "pop": 1374, "state": "AL", "_id": "36524"} -{"city": "CREOLA", "loc": [-88.017414, 30.901267], "pop": 2569, "state": "AL", "_id": "36525"} -{"city": "DAPHNE", "loc": [-87.889522, 30.61972], "pop": 14607, "state": "AL", "_id": "36526"} -{"city": "SPANISH FORT", "loc": [-87.88668, 30.695852], "pop": 5486, "state": "AL", "_id": "36527"} -{"city": "DAUPHIN ISLAND", "loc": [-88.109644, 30.252057], "pop": 824, "state": "AL", "_id": "36528"} -{"city": "DEER PARK", "loc": [-88.32731, 31.184892], "pop": 723, "state": "AL", "_id": "36529"} -{"city": "ELBERTA", "loc": [-87.58896, 30.394239], "pop": 2551, "state": "AL", "_id": "36530"} -{"city": "FAIRHOPE", "loc": [-87.883546, 30.50116], "pop": 16331, "state": "AL", "_id": "36532"} -{"city": "FOLEY", "loc": [-87.685737, 30.400664], "pop": 8520, "state": "AL", "_id": "36535"} -{"city": "FRANKVILLE", "loc": [-88.133185, 31.64684], "pop": 472, "state": "AL", "_id": "36538"} -{"city": "FRUITDALE", "loc": [-88.376597, 31.348843], "pop": 848, "state": "AL", "_id": "36539"} -{"city": "GAINESTOWN", "loc": [-87.682176, 31.425822], "pop": 301, "state": "AL", "_id": "36540"} -{"city": "GRAND BAY", "loc": [-88.32825, 30.498288], "pop": 10344, "state": "AL", "_id": "36541"} -{"city": "FORT MORGAN", "loc": [-87.712795, 30.268954], "pop": 4930, "state": "AL", "_id": "36542"} -{"city": "IRVINGTON", "loc": [-88.239563, 30.480241], "pop": 6181, "state": "AL", "_id": "36544"} -{"city": "JACKSON", "loc": [-87.867192, 31.513098], "pop": 9679, "state": "AL", "_id": "36545"} -{"city": "LEROY", "loc": [-87.968672, 31.491021], "pop": 1086, "state": "AL", "_id": "36548"} -{"city": "LILLIAN", "loc": [-87.474452, 30.39724], "pop": 3833, "state": "AL", "_id": "36549"} -{"city": "LITTLE RIVER", "loc": [-87.754796, 31.22459], "pop": 649, "state": "AL", "_id": "36550"} -{"city": "LOXLEY", "loc": [-87.75624, 30.617964], "pop": 2712, "state": "AL", "_id": "36551"} -{"city": "MC INTOSH", "loc": [-88.051102, 31.236108], "pop": 4211, "state": "AL", "_id": "36553"} -{"city": "MAGNOLIA SPRINGS", "loc": [-87.785781, 30.394497], "pop": 1088, "state": "AL", "_id": "36555"} -{"city": "MILLRY", "loc": [-88.355872, 31.626871], "pop": 2825, "state": "AL", "_id": "36558"} -{"city": "MOUNT VERNON", "loc": [-88.035044, 31.097375], "pop": 5053, "state": "AL", "_id": "36560"} -{"city": "ORANGE BEACH", "loc": [-87.555888, 30.290596], "pop": 2549, "state": "AL", "_id": "36561"} -{"city": "PERDIDO", "loc": [-87.628374, 30.98058], "pop": 986, "state": "AL", "_id": "36562"} -{"city": "ROBERTSDALE", "loc": [-87.637268, 30.561608], "pop": 3519, "state": "AL", "_id": "36567"} -{"city": "SAINT STEPHENS", "loc": [-88.052094, 31.533028], "pop": 688, "state": "AL", "_id": "36569"} -{"city": "SALITPA", "loc": [-87.959618, 31.654667], "pop": 896, "state": "AL", "_id": "36570"} -{"city": "SARALAND", "loc": [-88.093387, 30.833197], "pop": 13151, "state": "AL", "_id": "36571"} -{"city": "SATSUMA", "loc": [-88.053312, 30.851628], "pop": 6197, "state": "AL", "_id": "36572"} -{"city": "SEMINOLE", "loc": [-87.465938, 30.504806], "pop": 606, "state": "AL", "_id": "36574"} -{"city": "SEMMES", "loc": [-88.266705, 30.754408], "pop": 9329, "state": "AL", "_id": "36575"} -{"city": "SILVERHILL", "loc": [-87.745676, 30.522237], "pop": 8001, "state": "AL", "_id": "36576"} -{"city": "STOCKTON", "loc": [-87.863323, 31.01293], "pop": 1214, "state": "AL", "_id": "36579"} -{"city": "SUMMERDALE", "loc": [-87.699183, 30.475225], "pop": 881, "state": "AL", "_id": "36580"} -{"city": "THEODORE", "loc": [-88.180748, 30.544374], "pop": 20185, "state": "AL", "_id": "36582"} -{"city": "TIBBIE", "loc": [-88.266828, 31.373742], "pop": 327, "state": "AL", "_id": "36583"} -{"city": "VINEGAR BEND", "loc": [-88.386466, 31.25841], "pop": 475, "state": "AL", "_id": "36584"} -{"city": "WAGARVILLE", "loc": [-88.070519, 31.410205], "pop": 1681, "state": "AL", "_id": "36585"} -{"city": "WALKER SPRINGS", "loc": [-87.835355, 31.626166], "pop": 1001, "state": "AL", "_id": "36586"} -{"city": "WILMER", "loc": [-88.333196, 30.813745], "pop": 5905, "state": "AL", "_id": "36587"} -{"city": "MOBILE", "loc": [-88.045308, 30.688828], "pop": 1263, "state": "AL", "_id": "36602"} -{"city": "MOBILE", "loc": [-88.05622, 30.692141], "pop": 12162, "state": "AL", "_id": "36603"} -{"city": "MOBILE", "loc": [-88.067804, 30.681963], "pop": 11498, "state": "AL", "_id": "36604"} -{"city": "MOBILE", "loc": [-88.084646, 30.634117], "pop": 31621, "state": "AL", "_id": "36605"} -{"city": "MOBILE", "loc": [-88.100909, 30.672899], "pop": 18247, "state": "AL", "_id": "36606"} -{"city": "MOBILE", "loc": [-88.1029, 30.697486], "pop": 8610, "state": "AL", "_id": "36607"} -{"city": "MOBILE", "loc": [-88.187784, 30.69636], "pop": 37600, "state": "AL", "_id": "36608"} -{"city": "MOBILE", "loc": [-88.161806, 30.660527], "pop": 23687, "state": "AL", "_id": "36609"} -{"city": "PRICHARD", "loc": [-88.083761, 30.737846], "pop": 24919, "state": "AL", "_id": "36610"} -{"city": "CHICKASAW", "loc": [-88.084973, 30.766821], "pop": 6660, "state": "AL", "_id": "36611"} -{"city": "MOBILE", "loc": [-88.11311, 30.751844], "pop": 6096, "state": "AL", "_id": "36612"} -{"city": "EIGHT MILE", "loc": [-88.182311, 30.795074], "pop": 13517, "state": "AL", "_id": "36613"} -{"city": "BROOKLEY FIELD", "loc": [-88.068871, 30.631199], "pop": 864, "state": "AL", "_id": "36615"} -{"city": "MOBILE", "loc": [-88.091796, 30.714522], "pop": 17882, "state": "AL", "_id": "36617"} -{"city": "MOBILE", "loc": [-88.175753, 30.732178], "pop": 14887, "state": "AL", "_id": "36618"} -{"city": "MOBILE", "loc": [-88.194645, 30.592803], "pop": 12728, "state": "AL", "_id": "36619"} -{"city": "MOBILE", "loc": [-88.158843, 30.631076], "pop": 17704, "state": "AL", "_id": "36693"} -{"city": "MOBILE", "loc": [-88.229245, 30.647431], "pop": 21467, "state": "AL", "_id": "36695"} -{"city": "SELMA", "loc": [-87.024527, 32.419719], "pop": 26569, "state": "AL", "_id": "36701"} -{"city": "SELMA", "loc": [-87.013546, 32.415553], "pop": 12931, "state": "AL", "_id": "36703"} -{"city": "ALBERTA", "loc": [-87.342503, 32.144889], "pop": 1340, "state": "AL", "_id": "36720"} -{"city": "ARLINGTON", "loc": [-87.559691, 32.066784], "pop": 1098, "state": "AL", "_id": "36722"} -{"city": "CAMDEN", "loc": [-87.295049, 32.004732], "pop": 4948, "state": "AL", "_id": "36726"} -{"city": "CAMPBELL", "loc": [-88.006072, 31.963512], "pop": 392, "state": "AL", "_id": "36727"} -{"city": "CATHERINE", "loc": [-87.483623, 32.182], "pop": 366, "state": "AL", "_id": "36728"} -{"city": "DEMOPOLIS", "loc": [-87.839669, 32.490792], "pop": 9992, "state": "AL", "_id": "36732"} -{"city": "DIXONS MILLS", "loc": [-87.745356, 32.067209], "pop": 2635, "state": "AL", "_id": "36736"} -{"city": "FAUNSDALE", "loc": [-87.618079, 32.423259], "pop": 831, "state": "AL", "_id": "36738"} -{"city": "FORKLAND", "loc": [-87.881676, 32.644661], "pop": 2121, "state": "AL", "_id": "36740"} -{"city": "GALLION", "loc": [-87.698542, 32.529771], "pop": 599, "state": "AL", "_id": "36742"} -{"city": "GREENSBORO", "loc": [-87.590494, 32.716721], "pop": 7680, "state": "AL", "_id": "36744"} -{"city": "LINDEN", "loc": [-87.795358, 32.305343], "pop": 5438, "state": "AL", "_id": "36748"} -{"city": "JONES", "loc": [-86.889266, 32.608515], "pop": 428, "state": "AL", "_id": "36749"} -{"city": "MAPLESVILLE", "loc": [-86.871704, 32.804507], "pop": 1995, "state": "AL", "_id": "36750"} -{"city": "LOWER PEACH TREE", "loc": [-87.568224, 31.850365], "pop": 323, "state": "AL", "_id": "36751"} -{"city": "BURKVILLE", "loc": [-86.609868, 32.300292], "pop": 1478, "state": "AL", "_id": "36752"} -{"city": "MAGNOLIA", "loc": [-87.699815, 32.141931], "pop": 222, "state": "AL", "_id": "36754"} -{"city": "MARION", "loc": [-87.331437, 32.646301], "pop": 7284, "state": "AL", "_id": "36756"} -{"city": "PLANTERSVILLE", "loc": [-86.947531, 32.617608], "pop": 946, "state": "AL", "_id": "36758"} -{"city": "MARION JUNCTION", "loc": [-87.270181, 32.426589], "pop": 796, "state": "AL", "_id": "36759"} -{"city": "BOYS RANCH", "loc": [-86.92497, 32.095387], "pop": 1714, "state": "AL", "_id": "36761"} -{"city": "MORVIN", "loc": [-87.972897, 31.967305], "pop": 24, "state": "AL", "_id": "36762"} -{"city": "NEWBERN", "loc": [-87.561472, 32.551465], "pop": 1131, "state": "AL", "_id": "36765"} -{"city": "ORRVILLE", "loc": [-87.221368, 32.294859], "pop": 2680, "state": "AL", "_id": "36767"} -{"city": "PINE APPLE", "loc": [-87.004667, 31.926522], "pop": 1658, "state": "AL", "_id": "36768"} -{"city": "PINE HILL", "loc": [-87.577086, 31.973546], "pop": 2623, "state": "AL", "_id": "36769"} -{"city": "PRAIRIE", "loc": [-87.448723, 32.084762], "pop": 112, "state": "AL", "_id": "36771"} -{"city": "SAFFORD", "loc": [-87.369345, 32.300242], "pop": 715, "state": "AL", "_id": "36773"} -{"city": "SARDIS", "loc": [-86.991959, 32.284358], "pop": 1683, "state": "AL", "_id": "36775"} -{"city": "SAWYERVILLE", "loc": [-87.740201, 32.757796], "pop": 1684, "state": "AL", "_id": "36776"} -{"city": "SPROTT", "loc": [-87.141599, 32.654376], "pop": 1191, "state": "AL", "_id": "36779"} -{"city": "SWEET WATER", "loc": [-87.922755, 32.077272], "pop": 2444, "state": "AL", "_id": "36782"} -{"city": "THOMASTON", "loc": [-87.597438, 32.253776], "pop": 1527, "state": "AL", "_id": "36783"} -{"city": "THOMASVILLE", "loc": [-87.759842, 31.906728], "pop": 6229, "state": "AL", "_id": "36784"} -{"city": "BENTON", "loc": [-86.859867, 32.281924], "pop": 1396, "state": "AL", "_id": "36785"} -{"city": "UNIONTOWN", "loc": [-87.493398, 32.446966], "pop": 4173, "state": "AL", "_id": "36786"} -{"city": "STANTON", "loc": [-86.886848, 32.709631], "pop": 916, "state": "AL", "_id": "36790"} -{"city": "RANDOLPH", "loc": [-86.90701, 32.888834], "pop": 434, "state": "AL", "_id": "36792"} -{"city": "LAWLEY", "loc": [-86.956708, 32.864558], "pop": 337, "state": "AL", "_id": "36793"} -{"city": "OPELIKA", "loc": [-85.358629, 32.627771], "pop": 32808, "state": "AL", "_id": "36801"} -{"city": "AUBURN", "loc": [-85.489001, 32.602043], "pop": 38908, "state": "AL", "_id": "36830"} -{"city": "CAMP HILL", "loc": [-85.64738, 32.782749], "pop": 2422, "state": "AL", "_id": "36850"} -{"city": "CUSSETA", "loc": [-85.215791, 32.803194], "pop": 4597, "state": "AL", "_id": "36852"} -{"city": "DADEVILLE", "loc": [-85.770395, 32.82239], "pop": 7620, "state": "AL", "_id": "36853"} -{"city": "VALLEY", "loc": [-85.174911, 32.811349], "pop": 9504, "state": "AL", "_id": "36854"} -{"city": "FIVE POINTS", "loc": [-85.324264, 33.042408], "pop": 2400, "state": "AL", "_id": "36855"} -{"city": "HATCHECHUBBEE", "loc": [-85.302882, 32.284531], "pop": 772, "state": "AL", "_id": "36858"} -{"city": "HURTSBORO", "loc": [-85.395903, 32.245429], "pop": 1727, "state": "AL", "_id": "36860"} -{"city": "JACKSONS GAP", "loc": [-85.848662, 32.879698], "pop": 2236, "state": "AL", "_id": "36861"} -{"city": "LAFAYETTE", "loc": [-85.442563, 32.925237], "pop": 7045, "state": "AL", "_id": "36862"} -{"city": "LANETT", "loc": [-85.21608, 32.861609], "pop": 12083, "state": "AL", "_id": "36863"} -{"city": "NOTASULGA", "loc": [-85.68707, 32.543727], "pop": 3361, "state": "AL", "_id": "36866"} -{"city": "PHENIX CITY", "loc": [-85.095082, 32.407816], "pop": 13632, "state": "AL", "_id": "36867"} -{"city": "PHENIX CITY", "loc": [-85.018118, 32.470647], "pop": 28320, "state": "AL", "_id": "36869"} -{"city": "PITTSVIEW", "loc": [-85.131248, 32.173346], "pop": 1525, "state": "AL", "_id": "36871"} -{"city": "SALEM", "loc": [-85.183966, 32.621157], "pop": 2773, "state": "AL", "_id": "36874"} -{"city": "SEALE", "loc": [-85.167733, 32.305339], "pop": 1949, "state": "AL", "_id": "36875"} -{"city": "SMITHS", "loc": [-85.100027, 32.520072], "pop": 10605, "state": "AL", "_id": "36877"} -{"city": "WAVERLY", "loc": [-85.514372, 32.763167], "pop": 1689, "state": "AL", "_id": "36879"} -{"city": "BUTLER", "loc": [-88.206391, 32.082906], "pop": 4957, "state": "AL", "_id": "36904"} -{"city": "CUBA", "loc": [-88.361083, 32.410973], "pop": 1184, "state": "AL", "_id": "36907"} -{"city": "GILBERTOWN", "loc": [-88.326514, 31.866602], "pop": 1606, "state": "AL", "_id": "36908"} -{"city": "JACHIN", "loc": [-88.233356, 32.244174], "pop": 480, "state": "AL", "_id": "36910"} -{"city": "LISMAN", "loc": [-88.323419, 32.217722], "pop": 2313, "state": "AL", "_id": "36912"} -{"city": "NEEDHAM", "loc": [-88.345798, 32.012165], "pop": 618, "state": "AL", "_id": "36915"} -{"city": "PENNINGTON", "loc": [-88.0931, 32.226245], "pop": 1459, "state": "AL", "_id": "36916"} -{"city": "SILAS", "loc": [-88.309149, 31.766754], "pop": 2600, "state": "AL", "_id": "36919"} -{"city": "TOXEY", "loc": [-88.31794, 31.93153], "pop": 1980, "state": "AL", "_id": "36921"} -{"city": "WARD", "loc": [-88.297114, 32.334063], "pop": 274, "state": "AL", "_id": "36922"} -{"city": "YORK", "loc": [-88.268304, 32.472765], "pop": 5728, "state": "AL", "_id": "36925"} -{"city": "98791", "loc": [-176.310048, 51.938901], "pop": 5345, "state": "AK", "_id": "98791"} -{"city": "ANCHORAGE", "loc": [-149.876077, 61.211571], "pop": 14436, "state": "AK", "_id": "99501"} -{"city": "ANCHORAGE", "loc": [-150.093943, 61.096163], "pop": 15891, "state": "AK", "_id": "99502"} -{"city": "ANCHORAGE", "loc": [-149.893844, 61.189953], "pop": 12534, "state": "AK", "_id": "99503"} -{"city": "ANCHORAGE", "loc": [-149.74467, 61.203696], "pop": 32383, "state": "AK", "_id": "99504"} -{"city": "FORT RICHARDSON", "loc": [-149.675454, 61.275256], "pop": 7979, "state": "AK", "_id": "99505"} -{"city": "ELMENDORF AFB", "loc": [-149.812667, 61.251531], "pop": 7907, "state": "AK", "_id": "99506"} -{"city": "ANCHORAGE", "loc": [-149.828912, 61.153543], "pop": 20128, "state": "AK", "_id": "99507"} -{"city": "ANCHORAGE", "loc": [-149.810085, 61.205959], "pop": 29857, "state": "AK", "_id": "99508"} -{"city": "ANCHORAGE", "loc": [-149.897401, 61.119381], "pop": 17094, "state": "AK", "_id": "99515"} -{"city": "ANCHORAGE", "loc": [-149.779998, 61.10541], "pop": 18356, "state": "AK", "_id": "99516"} -{"city": "ANCHORAGE", "loc": [-149.936111, 61.190136], "pop": 15192, "state": "AK", "_id": "99517"} -{"city": "ANCHORAGE", "loc": [-149.886571, 61.154862], "pop": 8116, "state": "AK", "_id": "99518"} -{"city": "PORT HEIDEN", "loc": [-158.566367, 56.964333], "pop": 119, "state": "AK", "_id": "99549"} -{"city": "AKIACHAK", "loc": [-161.39233, 60.891854], "pop": 481, "state": "AK", "_id": "99551"} -{"city": "AKIAK", "loc": [-161.199325, 60.890632], "pop": 285, "state": "AK", "_id": "99552"} -{"city": "AKUTAN", "loc": [-165.785368, 54.143012], "pop": 589, "state": "AK", "_id": "99553"} -{"city": "ALAKANUK", "loc": [-164.60228, 62.746967], "pop": 1186, "state": "AK", "_id": "99554"} -{"city": "ALEKNAGIK", "loc": [-158.619882, 59.269688], "pop": 185, "state": "AK", "_id": "99555"} -{"city": "NIKOLAEVSK", "loc": [-151.732933, 59.788818], "pop": 1698, "state": "AK", "_id": "99556"} -{"city": "CHUATHBALUK", "loc": [-157.758502, 61.691648], "pop": 352, "state": "AK", "_id": "99557"} -{"city": "ANVIK", "loc": [-160.130441, 62.830913], "pop": 296, "state": "AK", "_id": "99558"} -{"city": "ATMAUTLUAK", "loc": [-161.824053, 60.832389], "pop": 7188, "state": "AK", "_id": "99559"} -{"city": "CHEFORNAK", "loc": [-164.210294, 60.153746], "pop": 320, "state": "AK", "_id": "99561"} -{"city": "CHEVAK", "loc": [-164.776457, 61.583982], "pop": 0, "state": "AK", "_id": "99563"} -{"city": "CHIGNIK", "loc": [-158.415696, 56.301639], "pop": 188, "state": "AK", "_id": "99564"} -{"city": "CHIGNIK LAGOON", "loc": [-158.673528, 56.251277], "pop": 186, "state": "AK", "_id": "99565"} -{"city": "CHUGIAK", "loc": [-149.453736, 61.409802], "pop": 6910, "state": "AK", "_id": "99567"} -{"city": "CLAM GULCH", "loc": [-151.422628, 60.201603], "pop": 133, "state": "AK", "_id": "99568"} -{"city": "CLARKS POINT", "loc": [-158.451241, 58.84921], "pop": 68, "state": "AK", "_id": "99569"} -{"city": "NELSON LAGOON", "loc": [-161.942941, 55.610952], "pop": 475, "state": "AK", "_id": "99571"} -{"city": "COOPER LANDING", "loc": [-149.823514, 60.476692], "pop": 252, "state": "AK", "_id": "99572"} -{"city": "COPPER CENTER", "loc": [-144.97793, 61.91581], "pop": 1389, "state": "AK", "_id": "99573"} -{"city": "CHENEGA BAY", "loc": [-147.943316, 60.102558], "pop": 96, "state": "AK", "_id": "99574"} -{"city": "CROOKED CREEK", "loc": [-158.002483, 61.818072], "pop": 1, "state": "AK", "_id": "99575"} -{"city": "KOLIGANEK", "loc": [-158.973533, 59.059279], "pop": 2711, "state": "AK", "_id": "99576"} -{"city": "EAGLE RIVER", "loc": [-149.508515, 61.311357], "pop": 18429, "state": "AK", "_id": "99577"} -{"city": "EEK", "loc": [-162.032341, 60.215058], "pop": 254, "state": "AK", "_id": "99578"} -{"city": "EGEGIK", "loc": [-157.342202, 58.206174], "pop": 122, "state": "AK", "_id": "99579"} -{"city": "EKWOK", "loc": [-157.478211, 59.362792], "pop": 77, "state": "AK", "_id": "99580"} -{"city": "EMMONAK", "loc": [-164.131298, 62.827404], "pop": 0, "state": "AK", "_id": "99581"} -{"city": "FALSE PASS", "loc": [-163.436845, 54.841028], "pop": 68, "state": "AK", "_id": "99583"} -{"city": "MARSHALL", "loc": [-161.7394, 61.837087], "pop": 530, "state": "AK", "_id": "99585"} -{"city": "SLANA", "loc": [-143.568393, 62.654744], "pop": 394, "state": "AK", "_id": "99586"} -{"city": "GLENNALLEN", "loc": [-145.661684, 62.103895], "pop": 1024, "state": "AK", "_id": "99588"} -{"city": "GOODNEWS BAY", "loc": [-161.587146, 59.085008], "pop": 305, "state": "AK", "_id": "99589"} -{"city": "GRAYLING", "loc": [-159.404907, 63.372013], "pop": 0, "state": "AK", "_id": "99590"} -{"city": "SAINT GEORGE ISL", "loc": [-169.547257, 56.60324], "pop": 138, "state": "AK", "_id": "99591"} -{"city": "HOLY CROSS", "loc": [-159.825092, 62.192584], "pop": 277, "state": "AK", "_id": "99602"} -{"city": "PORT GRAHAM", "loc": [-151.462644, 59.665495], "pop": 8186, "state": "AK", "_id": "99603"} -{"city": "HOOPER BAY", "loc": [-165.891045, 61.537157], "pop": 1443, "state": "AK", "_id": "99604"} -{"city": "KOKHANOK", "loc": [-155.462556, 59.564836], "pop": 362, "state": "AK", "_id": "99606"} -{"city": "KALSKAG", "loc": [-160.3261, 61.541006], "pop": 172, "state": "AK", "_id": "99607"} -{"city": "KASILOF", "loc": [-151.28958, 60.316365], "pop": 963, "state": "AK", "_id": "99610"} -{"city": "KENAI", "loc": [-151.254556, 60.614467], "pop": 10508, "state": "AK", "_id": "99611"} -{"city": "KING COVE", "loc": [-162.305561, 55.062848], "pop": 451, "state": "AK", "_id": "99612"} -{"city": "IGIUGIG", "loc": [-156.641603, 58.724264], "pop": 480, "state": "AK", "_id": "99613"} -{"city": "KIPNUK", "loc": [-164.101013, 59.923204], "pop": 470, "state": "AK", "_id": "99614"} -{"city": "AKHIOK", "loc": [-152.500169, 57.781967], "pop": 13309, "state": "AK", "_id": "99615"} -{"city": "KOTLIK", "loc": [-163.554153, 63.029471], "pop": 462, "state": "AK", "_id": "99620"} -{"city": "KWETHLUK", "loc": [-161.38849, 60.771814], "pop": 558, "state": "AK", "_id": "99621"} -{"city": "KWIGILLINGOK", "loc": [-162.984938, 59.881022], "pop": 572, "state": "AK", "_id": "99622"} -{"city": "LEVELOCK", "loc": [-154.976815, 59.371395], "pop": 204, "state": "AK", "_id": "99625"} -{"city": "LOWER KALSKAG", "loc": [-160.359966, 61.51377], "pop": 291, "state": "AK", "_id": "99626"} -{"city": "MC GRATH", "loc": [-155.585153, 62.967153], "pop": 618, "state": "AK", "_id": "99627"} -{"city": "MANOKOTAK", "loc": [-158.989699, 59.009559], "pop": 385, "state": "AK", "_id": "99628"} -{"city": "MEKORYUK", "loc": [-166.283583, 60.365679], "pop": 177, "state": "AK", "_id": "99630"} -{"city": "MOOSE PASS", "loc": [-149.255911, 60.85852], "pop": 1649, "state": "AK", "_id": "99631"} -{"city": "MOUNTAIN VILLAGE", "loc": [-163.883822, 62.158913], "pop": 788, "state": "AK", "_id": "99632"} -{"city": "NAKNEK", "loc": [-156.705405, 58.885699], "pop": 0, "state": "AK", "_id": "99633"} -{"city": "NAPAKIAK", "loc": [-161.738144, 60.663758], "pop": 328, "state": "AK", "_id": "99634"} -{"city": "NEW STUYAHOK", "loc": [-157.297205, 59.593533], "pop": 586, "state": "AK", "_id": "99636"} -{"city": "NIKOLSKI", "loc": [-168.788427, 52.988337], "pop": 42, "state": "AK", "_id": "99638"} -{"city": "NINILCHIK", "loc": [-151.639604, 60.010833], "pop": 767, "state": "AK", "_id": "99639"} -{"city": "NONDALTON", "loc": [-154.731675, 60.030837], "pop": 233, "state": "AK", "_id": "99640"} -{"city": "BUTTE", "loc": [-149.065323, 61.613814], "pop": 12358, "state": "AK", "_id": "99645"} -{"city": "PEDRO BAY", "loc": [-153.821856, 59.92238], "pop": 59, "state": "AK", "_id": "99647"} -{"city": "PERRYVILLE", "loc": [-159.259333, 55.945289], "pop": 143, "state": "AK", "_id": "99648"} -{"city": "PILOT POINT", "loc": [-157.449272, 57.595193], "pop": 63, "state": "AK", "_id": "99649"} -{"city": "PILOT STATION", "loc": [-162.874716, 61.946159], "pop": 463, "state": "AK", "_id": "99650"} -{"city": "PLATINUM", "loc": [-162.043201, 58.63364], "pop": 4, "state": "AK", "_id": "99651"} -{"city": "PORT ALSWORTH", "loc": [-154.433803, 60.636416], "pop": 7, "state": "AK", "_id": "99653"} -{"city": "WASILLA", "loc": [-149.395875, 61.592349], "pop": 10404, "state": "AK", "_id": "99654"} -{"city": "QUINHAGAK", "loc": [-161.874938, 59.738057], "pop": 501, "state": "AK", "_id": "99655"} -{"city": "RED DEVIL", "loc": [-157.195969, 61.735389], "pop": 159, "state": "AK", "_id": "99656"} -{"city": "RUSSIAN MISSION", "loc": [-161.558413, 61.591302], "pop": 0, "state": "AK", "_id": "99657"} -{"city": "SAINT MARYS", "loc": [-163.205263, 62.054106], "pop": 576, "state": "AK", "_id": "99658"} -{"city": "SAINT MICHAEL", "loc": [-162.109141, 63.47759], "pop": 295, "state": "AK", "_id": "99659"} -{"city": "SAINT PAUL ISLAN", "loc": [-170.293408, 57.178697], "pop": 763, "state": "AK", "_id": "99660"} -{"city": "SAND POINT", "loc": [-160.491435, 55.319236], "pop": 881, "state": "AK", "_id": "99661"} -{"city": "SCAMMON BAY", "loc": [-165.581945, 61.845019], "pop": 343, "state": "AK", "_id": "99662"} -{"city": "SEWARD", "loc": [-149.39849, 60.132874], "pop": 3937, "state": "AK", "_id": "99664"} -{"city": "SHAGELUK", "loc": [-159.52816, 62.661092], "pop": 139, "state": "AK", "_id": "99665"} -{"city": "SLEETMUTE", "loc": [-157.118284, 61.634555], "pop": 0, "state": "AK", "_id": "99668"} -{"city": "SOLDOTNA", "loc": [-151.13582, 60.481778], "pop": 9825, "state": "AK", "_id": "99669"} -{"city": "SOUTH NAKNEK", "loc": [-156.850289, 58.736221], "pop": 929, "state": "AK", "_id": "99670"} -{"city": "STEBBINS", "loc": [-162.227355, 63.478468], "pop": 400, "state": "AK", "_id": "99671"} -{"city": "STERLING", "loc": [-150.849792, 60.520373], "pop": 3814, "state": "AK", "_id": "99672"} -{"city": "TALKEETNA", "loc": [-150.110097, 62.260516], "pop": 1420, "state": "AK", "_id": "99676"} -{"city": "TULUKSAK", "loc": [-160.938924, 61.108848], "pop": 358, "state": "AK", "_id": "99679"} -{"city": "TUNUNAK", "loc": [-165.097464, 60.539322], "pop": 889, "state": "AK", "_id": "99681"} -{"city": "TYONEK", "loc": [-151.348495, 61.117929], "pop": 277, "state": "AK", "_id": "99682"} -{"city": "TRAPPER CREEK", "loc": [-150.284455, 61.441361], "pop": 20, "state": "AK", "_id": "99683"} -{"city": "UNALAKLEET", "loc": [-160.788365, 63.883478], "pop": 716, "state": "AK", "_id": "99684"} -{"city": "UNALASKA", "loc": [-166.519855, 53.887114], "pop": 3089, "state": "AK", "_id": "99685"} -{"city": "VALDEZ", "loc": [-146.195628, 60.895044], "pop": 7049, "state": "AK", "_id": "99686"} -{"city": "WASILLA", "loc": [-149.533003, 61.578032], "pop": 14215, "state": "AK", "_id": "99687"} -{"city": "WILLOW", "loc": [-150.188891, 61.771511], "pop": 1237, "state": "AK", "_id": "99688"} -{"city": "YAKUTAT", "loc": [-139.778858, 59.620211], "pop": 705, "state": "AK", "_id": "99689"} -{"city": "NIKOLAI", "loc": [-154.381247, 63.001603], "pop": 109, "state": "AK", "_id": "99691"} -{"city": "DUTCH HARBOR", "loc": [-167.510656, 53.362757], "pop": 3, "state": "AK", "_id": "99692"} -{"city": "COLDFOOT", "loc": [-147.710431, 64.840238], "pop": 19316, "state": "AK", "_id": "99701"} -{"city": "EIELSON AFB", "loc": [-147.08051, 64.67352], "pop": 5266, "state": "AK", "_id": "99702"} -{"city": "FORT WAINWRIGHT", "loc": [-147.655673, 64.828303], "pop": 6238, "state": "AK", "_id": "99703"} -{"city": "CLEAR", "loc": [-149.139885, 64.418121], "pop": 440, "state": "AK", "_id": "99704"} -{"city": "NORTH POLE", "loc": [-147.369353, 64.78049], "pop": 14672, "state": "AK", "_id": "99705"} -{"city": "FAIRBANKS", "loc": [-147.846917, 64.85437], "pop": 23238, "state": "AK", "_id": "99709"} -{"city": "FAIRBANKS", "loc": [-147.510479, 64.910879], "pop": 8141, "state": "AK", "_id": "99712"} -{"city": "SALCHA", "loc": [-146.952974, 64.50905], "pop": 890, "state": "AK", "_id": "99714"} -{"city": "ALLAKAKET", "loc": [-152.712155, 66.543197], "pop": 170, "state": "AK", "_id": "99720"} -{"city": "ANAKTUVUK PASS", "loc": [-151.679005, 68.11878], "pop": 260, "state": "AK", "_id": "99721"} -{"city": "ARCTIC VILLAGE", "loc": [-145.423115, 68.077395], "pop": 107, "state": "AK", "_id": "99722"} -{"city": "BARROW", "loc": [-156.817409, 71.234637], "pop": 3696, "state": "AK", "_id": "99723"} -{"city": "BEAVER", "loc": [-147.279803, 66.33883], "pop": 103, "state": "AK", "_id": "99724"} -{"city": "BETTLES FIELD", "loc": [-151.062414, 67.100495], "pop": 156, "state": "AK", "_id": "99726"} -{"city": "BUCKLAND", "loc": [-161.131676, 65.981052], "pop": 318, "state": "AK", "_id": "99727"} -{"city": "CANTWELL", "loc": [-148.89735, 63.395458], "pop": 210, "state": "AK", "_id": "99729"} -{"city": "CENTRAL", "loc": [-144.74886, 65.468058], "pop": 107, "state": "AK", "_id": "99730"} -{"city": "CIRCLE", "loc": [-144.08262, 65.824542], "pop": 73, "state": "AK", "_id": "99733"} -{"city": "PRUDHOE BAY", "loc": [-148.559636, 70.070057], "pop": 153, "state": "AK", "_id": "99734"} -{"city": "DEERING", "loc": [-162.711951, 66.062265], "pop": 167, "state": "AK", "_id": "99736"} -{"city": "DOT LAKE", "loc": [-145.613611, 64.005426], "pop": 4111, "state": "AK", "_id": "99737"} -{"city": "ELIM", "loc": [-162.260371, 64.621662], "pop": 264, "state": "AK", "_id": "99739"} -{"city": "FORT YUKON", "loc": [-145.306439, 66.520744], "pop": 662, "state": "AK", "_id": "99740"} -{"city": "GALENA", "loc": [-156.797701, 64.760784], "pop": 847, "state": "AK", "_id": "99741"} -{"city": "GAMBELL", "loc": [-171.701685, 63.776555], "pop": 525, "state": "AK", "_id": "99742"} -{"city": "HEALY", "loc": [-149.011128, 63.917123], "pop": 1057, "state": "AK", "_id": "99743"} -{"city": "ANDERSON", "loc": [-149.1718, 64.300693], "pop": 300, "state": "AK", "_id": "99744"} -{"city": "HUGHES", "loc": [-154.26443, 66.038246], "pop": 64, "state": "AK", "_id": "99745"} -{"city": "HUSLIA", "loc": [-156.291976, 65.689918], "pop": 207, "state": "AK", "_id": "99746"} -{"city": "KAKTOVIK", "loc": [-143.631329, 70.042889], "pop": 245, "state": "AK", "_id": "99747"} -{"city": "KALTAG", "loc": [-158.724251, 64.330452], "pop": 240, "state": "AK", "_id": "99748"} -{"city": "KIANA", "loc": [-158.152204, 67.18026], "pop": 349, "state": "AK", "_id": "99749"} -{"city": "KIVALINA", "loc": [-163.733617, 67.665859], "pop": 689, "state": "AK", "_id": "99750"} -{"city": "KOBUK", "loc": [-157.066864, 66.912253], "pop": 306, "state": "AK", "_id": "99751"} -{"city": "KOTZEBUE", "loc": [-162.126493, 66.846459], "pop": 3347, "state": "AK", "_id": "99752"} -{"city": "KOYUK", "loc": [-161.149957, 64.931668], "pop": 231, "state": "AK", "_id": "99753"} -{"city": "DENALI NATIONAL", "loc": [-149.539532, 63.516075], "pop": 27, "state": "AK", "_id": "99755"} -{"city": "MANLEY HOT SPRIN", "loc": [-150.573267, 65.02058], "pop": 122, "state": "AK", "_id": "99756"} -{"city": "LAKE MINCHUMINA", "loc": [-152.430081, 63.903884], "pop": 32, "state": "AK", "_id": "99757"} -{"city": "MINTO", "loc": [-149.691186, 65.058399], "pop": 228, "state": "AK", "_id": "99758"} -{"city": "POINT LAY", "loc": [-162.906148, 69.705626], "pop": 139, "state": "AK", "_id": "99759"} -{"city": "NENANA", "loc": [-149.086744, 64.557656], "pop": 393, "state": "AK", "_id": "99760"} -{"city": "NOATAK", "loc": [-160.509453, 66.97553], "pop": 395, "state": "AK", "_id": "99761"} -{"city": "GOLOVIN", "loc": [-165.310667, 64.505775], "pop": 3706, "state": "AK", "_id": "99762"} -{"city": "NOORVIK", "loc": [-161.044132, 66.836353], "pop": 534, "state": "AK", "_id": "99763"} -{"city": "NULATO", "loc": [-157.991353, 64.778024], "pop": 492, "state": "AK", "_id": "99765"} -{"city": "POINT HOPE", "loc": [-166.72618, 68.312058], "pop": 640, "state": "AK", "_id": "99766"} -{"city": "RAMPART", "loc": [-150.011201, 65.383627], "pop": 68, "state": "AK", "_id": "99767"} -{"city": "RUBY", "loc": [-155.503872, 64.720062], "pop": 172, "state": "AK", "_id": "99768"} -{"city": "SAVOONGA", "loc": [-170.470908, 63.679737], "pop": 519, "state": "AK", "_id": "99769"} -{"city": "SELAWIK", "loc": [-158.534287, 65.713537], "pop": 0, "state": "AK", "_id": "99770"} -{"city": "SHAKTOOLIK", "loc": [-161.174589, 64.375498], "pop": 183, "state": "AK", "_id": "99771"} -{"city": "SHISHMAREF", "loc": [-166.137276, 66.230562], "pop": 456, "state": "AK", "_id": "99772"} -{"city": "SHUNGNAK", "loc": [-157.613496, 66.958141], "pop": 0, "state": "AK", "_id": "99773"} -{"city": "STEVENS VILLAGE", "loc": [-149.118286, 65.995894], "pop": 110, "state": "AK", "_id": "99774"} -{"city": "TANANA", "loc": [-152.103747, 65.156483], "pop": 345, "state": "AK", "_id": "99777"} -{"city": "TELLER", "loc": [-166.3833, 65.240164], "pop": 260, "state": "AK", "_id": "99778"} -{"city": "BORDER", "loc": [-142.523046, 63.435022], "pop": 1805, "state": "AK", "_id": "99780"} -{"city": "VENETIE", "loc": [-146.413723, 67.010446], "pop": 184, "state": "AK", "_id": "99781"} -{"city": "WAINWRIGHT", "loc": [-160.012532, 70.620064], "pop": 492, "state": "AK", "_id": "99782"} -{"city": "WALES", "loc": [-168.520521, 65.688212], "pop": 341, "state": "AK", "_id": "99783"} -{"city": "WHITE MOUNTAIN", "loc": [-163.42185, 64.702791], "pop": 194, "state": "AK", "_id": "99784"} -{"city": "BREVIG MISSION", "loc": [-166.478578, 65.334187], "pop": 198, "state": "AK", "_id": "99785"} -{"city": "AMBLER", "loc": [-156.455652, 67.46951], "pop": 8, "state": "AK", "_id": "99786"} -{"city": "CHALKYITSIK", "loc": [-143.638121, 66.719], "pop": 99, "state": "AK", "_id": "99788"} -{"city": "NUIQSUT", "loc": [-150.997119, 70.192737], "pop": 354, "state": "AK", "_id": "99789"} -{"city": "JUNEAU", "loc": [-134.529429, 58.362767], "pop": 24947, "state": "AK", "_id": "99801"} -{"city": "ANGOON", "loc": [-134.371052, 57.569832], "pop": 1002, "state": "AK", "_id": "99820"} -{"city": "DOUGLAS", "loc": [-134.395041, 58.275597], "pop": 1802, "state": "AK", "_id": "99824"} -{"city": "GUSTAVUS", "loc": [-135.761542, 58.42835], "pop": 258, "state": "AK", "_id": "99826"} -{"city": "HAINES", "loc": [-135.542032, 59.251886], "pop": 2246, "state": "AK", "_id": "99827"} -{"city": "HOONAH", "loc": [-135.558435, 58.032237], "pop": 1670, "state": "AK", "_id": "99829"} -{"city": "PETERSBURG", "loc": [-133.160683, 56.827134], "pop": 4253, "state": "AK", "_id": "99833"} -{"city": "SITKA", "loc": [-135.316569, 57.051436], "pop": 8638, "state": "AK", "_id": "99835"} -{"city": "SKAGWAY", "loc": [-135.301794, 59.468471], "pop": 692, "state": "AK", "_id": "99840"} -{"city": "KETCHIKAN", "loc": [-131.683175, 55.372028], "pop": 13886, "state": "AK", "_id": "99901"} -{"city": "THORNE BAY", "loc": [-132.513815, 55.66086], "pop": 744, "state": "AK", "_id": "99919"} -{"city": "CRAIG", "loc": [-133.117081, 55.47317], "pop": 1398, "state": "AK", "_id": "99921"} -{"city": "HYDABURG", "loc": [-132.633175, 55.137406], "pop": 891, "state": "AK", "_id": "99922"} -{"city": "HYDER", "loc": [-130.124915, 55.925867], "pop": 116, "state": "AK", "_id": "99923"} -{"city": "KLAWOCK", "loc": [-133.055503, 55.552611], "pop": 851, "state": "AK", "_id": "99925"} -{"city": "METLAKATLA", "loc": [-131.579001, 55.121491], "pop": 1469, "state": "AK", "_id": "99926"} -{"city": "POINT BAKER", "loc": [-133.376372, 56.307858], "pop": 426, "state": "AK", "_id": "99927"} -{"city": "WRANGELL", "loc": [-132.352918, 56.433524], "pop": 2573, "state": "AK", "_id": "99929"} -{"city": "KETCHIKAN", "loc": [-133.18479, 55.942471], "pop": 422, "state": "AK", "_id": "99950"} -{"city": "PHOENIX", "loc": [-112.077428, 33.451095], "pop": 10633, "state": "AZ", "_id": "85003"} -{"city": "PHOENIX", "loc": [-112.068584, 33.455708], "pop": 4491, "state": "AZ", "_id": "85004"} -{"city": "PHOENIX", "loc": [-112.047357, 33.465016], "pop": 26747, "state": "AZ", "_id": "85006"} -{"city": "PHOENIX", "loc": [-112.089326, 33.452298], "pop": 13650, "state": "AZ", "_id": "85007"} -{"city": "PHOENIX", "loc": [-111.998381, 33.466457], "pop": 41733, "state": "AZ", "_id": "85008"} -{"city": "PHOENIX", "loc": [-112.128368, 33.456373], "pop": 41512, "state": "AZ", "_id": "85009"} -{"city": "PHOENIX", "loc": [-112.067816, 33.509744], "pop": 6141, "state": "AZ", "_id": "85012"} -{"city": "PHOENIX", "loc": [-112.082657, 33.508493], "pop": 18467, "state": "AZ", "_id": "85013"} -{"city": "PHOENIX", "loc": [-112.05557, 33.510263], "pop": 22646, "state": "AZ", "_id": "85014"} -{"city": "PHOENIX", "loc": [-112.101064, 33.508164], "pop": 32497, "state": "AZ", "_id": "85015"} -{"city": "PHOENIX", "loc": [-112.030496, 33.502117], "pop": 29527, "state": "AZ", "_id": "85016"} -{"city": "PHOENIX", "loc": [-112.121232, 33.515263], "pop": 27741, "state": "AZ", "_id": "85017"} -{"city": "PHOENIX", "loc": [-111.988259, 33.495796], "pop": 32926, "state": "AZ", "_id": "85018"} -{"city": "PHOENIX", "loc": [-112.141681, 33.512284], "pop": 21879, "state": "AZ", "_id": "85019"} -{"city": "PHOENIX", "loc": [-112.055888, 33.562281], "pop": 29043, "state": "AZ", "_id": "85020"} -{"city": "PHOENIX", "loc": [-112.092686, 33.559965], "pop": 31201, "state": "AZ", "_id": "85021"} -{"city": "PHOENIX", "loc": [-112.052008, 33.631513], "pop": 33573, "state": "AZ", "_id": "85022"} -{"city": "PHOENIX", "loc": [-112.111838, 33.632383], "pop": 54668, "state": "AZ", "_id": "85023"} -{"city": "PHOENIX", "loc": [-112.036956, 33.661664], "pop": 14090, "state": "AZ", "_id": "85024"} -{"city": "NEW RIVER STAGE", "loc": [-112.102723, 33.667157], "pop": 24843, "state": "AZ", "_id": "85027"} -{"city": "PHOENIX", "loc": [-112.008724, 33.585115], "pop": 22662, "state": "AZ", "_id": "85028"} -{"city": "PHOENIX", "loc": [-112.119913, 33.596133], "pop": 40764, "state": "AZ", "_id": "85029"} -{"city": "PHOENIX", "loc": [-112.16963, 33.493909], "pop": 21088, "state": "AZ", "_id": "85031"} -{"city": "PHOENIX", "loc": [-112.004369, 33.623807], "pop": 53113, "state": "AZ", "_id": "85032"} -{"city": "PHOENIX", "loc": [-112.213185, 33.494426], "pop": 41367, "state": "AZ", "_id": "85033"} -{"city": "PHOENIX", "loc": [-112.042135, 33.441251], "pop": 9824, "state": "AZ", "_id": "85034"} -{"city": "PHOENIX", "loc": [-112.183177, 33.472353], "pop": 35384, "state": "AZ", "_id": "85035"} -{"city": "PHOENIX", "loc": [-112.246763, 33.491278], "pop": 13924, "state": "AZ", "_id": "85037"} -{"city": "PHOENIX", "loc": [-112.288573, 33.495362], "pop": 7914, "state": "AZ", "_id": "85039"} -{"city": "PHOENIX", "loc": [-112.03126, 33.390475], "pop": 47527, "state": "AZ", "_id": "85040"} -{"city": "PHOENIX", "loc": [-112.095437, 33.388882], "pop": 29343, "state": "AZ", "_id": "85041"} -{"city": "PHOENIX", "loc": [-112.197245, 33.449056], "pop": 7054, "state": "AZ", "_id": "85043"} -{"city": "PHOENIX", "loc": [-111.9943, 33.329124], "pop": 32053, "state": "AZ", "_id": "85044"} -{"city": "PHOENIX", "loc": [-112.133168, 33.559113], "pop": 35671, "state": "AZ", "_id": "85051"} -{"city": "MESA", "loc": [-111.846931, 33.43174], "pop": 40017, "state": "AZ", "_id": "85201"} -{"city": "MESA", "loc": [-111.872429, 33.385095], "pop": 40729, "state": "AZ", "_id": "85202"} -{"city": "MESA", "loc": [-111.805697, 33.436952], "pop": 32853, "state": "AZ", "_id": "85203"} -{"city": "MESA", "loc": [-111.789554, 33.399168], "pop": 55180, "state": "AZ", "_id": "85204"} -{"city": "MESA", "loc": [-111.712939, 33.43685], "pop": 35676, "state": "AZ", "_id": "85205"} -{"city": "MESA", "loc": [-111.724223, 33.402603], "pop": 21274, "state": "AZ", "_id": "85206"} -{"city": "MESA", "loc": [-111.64256, 33.432073], "pop": 12547, "state": "AZ", "_id": "85207"} -{"city": "MESA", "loc": [-111.651297, 33.398416], "pop": 22113, "state": "AZ", "_id": "85208"} -{"city": "MESA", "loc": [-111.842757, 33.38867], "pop": 32467, "state": "AZ", "_id": "85210"} -{"city": "MESA", "loc": [-111.773114, 33.436688], "pop": 23500, "state": "AZ", "_id": "85213"} -{"city": "GOLD CANYON", "loc": [-111.51331, 33.360787], "pop": 14112, "state": "AZ", "_id": "85219"} -{"city": "APACHE JUNCTION", "loc": [-111.571818, 33.415211], "pop": 19342, "state": "AZ", "_id": "85220"} -{"city": "ELEVEN MILE CORN", "loc": [-111.756093, 32.892667], "pop": 26134, "state": "AZ", "_id": "85222"} -{"city": "CHANDLER", "loc": [-111.863156, 33.330091], "pop": 54023, "state": "AZ", "_id": "85224"} -{"city": "CHANDLER", "loc": [-111.823881, 33.310505], "pop": 15678, "state": "AZ", "_id": "85225"} -{"city": "CHANDLER", "loc": [-111.919827, 33.30917], "pop": 17639, "state": "AZ", "_id": "85226"} -{"city": "COOLIDGE", "loc": [-111.534378, 32.957399], "pop": 10698, "state": "AZ", "_id": "85228"} -{"city": "ELOY", "loc": [-111.583275, 32.750929], "pop": 10670, "state": "AZ", "_id": "85231"} -{"city": "FLORENCE", "loc": [-111.361234, 32.996881], "pop": 9888, "state": "AZ", "_id": "85232"} -{"city": "GILBERT", "loc": [-111.780876, 33.352746], "pop": 32606, "state": "AZ", "_id": "85234"} -{"city": "HIGLEY", "loc": [-111.696926, 33.302382], "pop": 3583, "state": "AZ", "_id": "85236"} -{"city": "KEARNY", "loc": [-110.91227, 33.059443], "pop": 2736, "state": "AZ", "_id": "85237"} -{"city": "MOBILE", "loc": [-112.075228, 32.987379], "pop": 5026, "state": "AZ", "_id": "85239"} -{"city": "WILLIAMS AFB", "loc": [-111.668801, 33.310289], "pop": 2574, "state": "AZ", "_id": "85240"} -{"city": "ARIZONA BOYS RAN", "loc": [-111.643596, 33.238577], "pop": 2569, "state": "AZ", "_id": "85242"} -{"city": "SACATON", "loc": [-111.775162, 33.097699], "pop": 6792, "state": "AZ", "_id": "85247"} -{"city": "SUN LAKES", "loc": [-111.866899, 33.223056], "pop": 9399, "state": "AZ", "_id": "85248"} -{"city": "CHANDLER", "loc": [-111.774486, 33.241384], "pop": 3871, "state": "AZ", "_id": "85249"} -{"city": "SCOTTSDALE", "loc": [-111.904926, 33.521767], "pop": 16133, "state": "AZ", "_id": "85250"} -{"city": "SCOTTSDALE", "loc": [-111.916697, 33.493559], "pop": 30869, "state": "AZ", "_id": "85251"} -{"city": "PARADISE VALLEY", "loc": [-111.956546, 33.549439], "pop": 15289, "state": "AZ", "_id": "85253"} -{"city": "SCOTTSDALE", "loc": [-111.955422, 33.616476], "pop": 37414, "state": "AZ", "_id": "85254"} -{"city": "SCOTTSDALE", "loc": [-111.889213, 33.696801], "pop": 2927, "state": "AZ", "_id": "85255"} -{"city": "SCOTTSDALE", "loc": [-111.85333, 33.485793], "pop": 3367, "state": "AZ", "_id": "85256"} -{"city": "SCOTTSDALE", "loc": [-111.915129, 33.46693], "pop": 30182, "state": "AZ", "_id": "85257"} -{"city": "SCOTTSDALE", "loc": [-111.893067, 33.564747], "pop": 20867, "state": "AZ", "_id": "85258"} -{"city": "SCOTTSDALE", "loc": [-111.840438, 33.587943], "pop": 7802, "state": "AZ", "_id": "85259"} -{"city": "SCOTTSDALE", "loc": [-111.88671, 33.601323], "pop": 17908, "state": "AZ", "_id": "85260"} -{"city": "SCOTTSDALE", "loc": [-111.779135, 33.77524], "pop": 1614, "state": "AZ", "_id": "85262"} -{"city": "FORT MCDOWELL", "loc": [-111.68062, 33.611807], "pop": 619, "state": "AZ", "_id": "85264"} -{"city": "FOUNTAIN HILLS", "loc": [-111.723685, 33.608489], "pop": 10030, "state": "AZ", "_id": "85268"} -{"city": "STANFIELD", "loc": [-111.965987, 32.882321], "pop": 644, "state": "AZ", "_id": "85272"} -{"city": "SUPERIOR", "loc": [-111.09846, 33.288716], "pop": 3901, "state": "AZ", "_id": "85273"} -{"city": "TEMPE", "loc": [-111.926144, 33.422675], "pop": 49218, "state": "AZ", "_id": "85281"} -{"city": "TEMPE", "loc": [-111.924896, 33.391669], "pop": 47890, "state": "AZ", "_id": "85282"} -{"city": "TEMPE", "loc": [-111.93122, 33.366524], "pop": 38332, "state": "AZ", "_id": "85283"} -{"city": "TEMPE", "loc": [-111.919696, 33.336302], "pop": 12320, "state": "AZ", "_id": "85284"} -{"city": "WINKELMAN", "loc": [-110.772682, 33.00572], "pop": 1977, "state": "AZ", "_id": "85292"} -{"city": "GLENDALE", "loc": [-112.176703, 33.531122], "pop": 46331, "state": "AZ", "_id": "85301"} -{"city": "GLENDALE", "loc": [-112.175289, 33.567487], "pop": 32094, "state": "AZ", "_id": "85302"} -{"city": "GLENDALE", "loc": [-112.214937, 33.526215], "pop": 16045, "state": "AZ", "_id": "85303"} -{"city": "GLENDALE", "loc": [-112.174575, 33.594289], "pop": 26463, "state": "AZ", "_id": "85304"} -{"city": "GLENDALE", "loc": [-112.248232, 33.529103], "pop": 1424, "state": "AZ", "_id": "85305"} -{"city": "GLENDALE", "loc": [-112.177563, 33.623882], "pop": 23493, "state": "AZ", "_id": "85306"} -{"city": "LUKE AFB", "loc": [-112.326735, 33.534879], "pop": 4120, "state": "AZ", "_id": "85307"} -{"city": "GLENDALE", "loc": [-112.169391, 33.653924], "pop": 31532, "state": "AZ", "_id": "85308"} -{"city": "LUKE AFB", "loc": [-112.356186, 33.539993], "pop": 3601, "state": "AZ", "_id": "85309"} -{"city": "GLENDALE", "loc": [-112.164131, 33.704726], "pop": 5369, "state": "AZ", "_id": "85310"} -{"city": "WHY", "loc": [-112.858681, 32.373485], "pop": 3288, "state": "AZ", "_id": "85321"} -{"city": "ARLINGTON", "loc": [-112.789058, 33.313317], "pop": 329, "state": "AZ", "_id": "85322"} -{"city": "AVONDALE", "loc": [-112.343754, 33.432114], "pop": 12321, "state": "AZ", "_id": "85323"} -{"city": "ROCK SPRINGS", "loc": [-112.130956, 34.073197], "pop": 1819, "state": "AZ", "_id": "85324"} -{"city": "BUCKEYE", "loc": [-112.607728, 33.38896], "pop": 13086, "state": "AZ", "_id": "85326"} -{"city": "CIBOLA", "loc": [-114.512204, 33.614886], "pop": 1285, "state": "AZ", "_id": "85328"} -{"city": "CAVE CREEK", "loc": [-112.015106, 33.821896], "pop": 13654, "state": "AZ", "_id": "85331"} -{"city": "CONGRESS", "loc": [-112.76801, 34.176425], "pop": 2314, "state": "AZ", "_id": "85332"} -{"city": "DATELAND", "loc": [-113.463126, 32.867886], "pop": 659, "state": "AZ", "_id": "85333"} -{"city": "EL MIRAGE", "loc": [-112.324147, 33.608153], "pop": 5234, "state": "AZ", "_id": "85335"} -{"city": "GILA BEND", "loc": [-112.746832, 32.93059], "pop": 2898, "state": "AZ", "_id": "85337"} -{"city": "GOODYEAR", "loc": [-112.383385, 33.436809], "pop": 5819, "state": "AZ", "_id": "85338"} -{"city": "LAVEEN", "loc": [-112.171618, 33.343572], "pop": 6187, "state": "AZ", "_id": "85339"} -{"city": "LITCHFIELD PARK", "loc": [-112.380497, 33.494127], "pop": 6349, "state": "AZ", "_id": "85340"} -{"city": "MORRISTOWN", "loc": [-112.548331, 33.772993], "pop": 2878, "state": "AZ", "_id": "85342"} -{"city": "PALO VERDE", "loc": [-112.646662, 33.34848], "pop": 669, "state": "AZ", "_id": "85343"} -{"city": "EMPIRE LANDING", "loc": [-114.266342, 34.0254], "pop": 11143, "state": "AZ", "_id": "85344"} -{"city": "PEORIA", "loc": [-112.234424, 33.576135], "pop": 37607, "state": "AZ", "_id": "85345"} -{"city": "ROLL", "loc": [-113.564287, 32.936635], "pop": 959, "state": "AZ", "_id": "85347"} -{"city": "SALOME", "loc": [-113.571459, 33.748141], "pop": 1279, "state": "AZ", "_id": "85348"} -{"city": "SOMERTON", "loc": [-114.7127, 32.563398], "pop": 15339, "state": "AZ", "_id": "85350"} -{"city": "SUN CITY", "loc": [-112.279701, 33.606104], "pop": 31102, "state": "AZ", "_id": "85351"} -{"city": "TOLLESON", "loc": [-112.277444, 33.434686], "pop": 9485, "state": "AZ", "_id": "85353"} -{"city": "TONOPAH", "loc": [-112.952785, 33.422797], "pop": 95, "state": "AZ", "_id": "85354"} -{"city": "WADDELL", "loc": [-112.43869, 33.567285], "pop": 2125, "state": "AZ", "_id": "85355"} -{"city": "WELLTON", "loc": [-114.176616, 32.749251], "pop": 4778, "state": "AZ", "_id": "85356"} -{"city": "WITTMANN", "loc": [-112.446578, 33.726425], "pop": 789, "state": "AZ", "_id": "85361"} -{"city": "YARNELL", "loc": [-112.62166, 34.414076], "pop": 455, "state": "AZ", "_id": "85362"} -{"city": "YOUNGTOWN", "loc": [-112.301305, 33.590751], "pop": 2351, "state": "AZ", "_id": "85363"} -{"city": "YUMA", "loc": [-114.642362, 32.701507], "pop": 57131, "state": "AZ", "_id": "85364"} -{"city": "YUMA PROVING GRO", "loc": [-114.548633, 32.671352], "pop": 28179, "state": "AZ", "_id": "85365"} -{"city": "SUN CITY", "loc": [-112.321397, 33.658756], "pop": 25878, "state": "AZ", "_id": "85373"} -{"city": "SURPRISE", "loc": [-112.33143, 33.630028], "pop": 5042, "state": "AZ", "_id": "85374"} -{"city": "SUN CITY WEST", "loc": [-112.255434, 33.662576], "pop": 5702, "state": "AZ", "_id": "85375"} -{"city": "PEORIA", "loc": [-112.223723, 33.604761], "pop": 9624, "state": "AZ", "_id": "85381"} -{"city": "PEORIA", "loc": [-112.207177, 33.63083], "pop": 1738, "state": "AZ", "_id": "85382"} -{"city": "WICKENBURG", "loc": [-112.738973, 33.911282], "pop": 7994, "state": "AZ", "_id": "85390"} -{"city": "GLOBE", "loc": [-110.789247, 33.402426], "pop": 13240, "state": "AZ", "_id": "85501"} -{"city": "BYLAS", "loc": [-110.11702, 33.126549], "pop": 1371, "state": "AZ", "_id": "85530"} -{"city": "CLIFTON", "loc": [-109.246199, 33.132343], "pop": 376, "state": "AZ", "_id": "85533"} -{"city": "FRANKLIN", "loc": [-109.129575, 32.793976], "pop": 2395, "state": "AZ", "_id": "85534"} -{"city": "EDEN", "loc": [-109.953682, 33.028629], "pop": 55, "state": "AZ", "_id": "85535"} -{"city": "MIAMI", "loc": [-110.881182, 33.431928], "pop": 4866, "state": "AZ", "_id": "85539"} -{"city": "MORENCI", "loc": [-109.311517, 33.043593], "pop": 5223, "state": "AZ", "_id": "85540"} -{"city": "PAYSON", "loc": [-111.287774, 34.219779], "pop": 13456, "state": "AZ", "_id": "85541"} -{"city": "PERIDOT", "loc": [-110.37252, 33.478874], "pop": 4878, "state": "AZ", "_id": "85542"} -{"city": "PIMA", "loc": [-109.856009, 32.909661], "pop": 2881, "state": "AZ", "_id": "85543"} -{"city": "STRAWBERRY", "loc": [-111.473483, 34.390915], "pop": 1903, "state": "AZ", "_id": "85544"} -{"city": "ROOSEVELT", "loc": [-110.974884, 33.635753], "pop": 65, "state": "AZ", "_id": "85545"} -{"city": "SAFFORD", "loc": [-109.626641, 32.829491], "pop": 2676, "state": "AZ", "_id": "85546"} -{"city": "SAN CARLOS", "loc": [-110.395323, 33.310643], "pop": 2307, "state": "AZ", "_id": "85550"} -{"city": "THATCHER", "loc": [-109.730245, 32.819902], "pop": 15375, "state": "AZ", "_id": "85552"} -{"city": "BENSON", "loc": [-110.294113, 31.98826], "pop": 6141, "state": "AZ", "_id": "85602"} -{"city": "BISBEE", "loc": [-109.911736, 31.408557], "pop": 8471, "state": "AZ", "_id": "85603"} -{"city": "COCHISE", "loc": [-109.92393, 32.097891], "pop": 290, "state": "AZ", "_id": "85606"} -{"city": "DOUGLAS", "loc": [-109.544698, 31.35111], "pop": 17350, "state": "AZ", "_id": "85607"} -{"city": "ELFRIDA", "loc": [-109.619277, 31.713891], "pop": 1655, "state": "AZ", "_id": "85610"} -{"city": "ELGIN", "loc": [-110.611403, 31.66002], "pop": 638, "state": "AZ", "_id": "85611"} -{"city": "FORT HUACHUCA", "loc": [-110.344131, 31.558735], "pop": 8710, "state": "AZ", "_id": "85613"} -{"city": "GREEN VALLEY", "loc": [-111.000253, 31.854271], "pop": 15530, "state": "AZ", "_id": "85614"} -{"city": "HEREFORD", "loc": [-110.204728, 31.403545], "pop": 1762, "state": "AZ", "_id": "85615"} -{"city": "HUACHUCA CITY", "loc": [-110.333414, 31.663896], "pop": 3639, "state": "AZ", "_id": "85616"} -{"city": "MC NEAL", "loc": [-109.630971, 31.502969], "pop": 3135, "state": "AZ", "_id": "85617"} -{"city": "MAMMOTH", "loc": [-110.643961, 32.723875], "pop": 1876, "state": "AZ", "_id": "85618"} -{"city": "NOGALES", "loc": [-110.943508, 31.376969], "pop": 25506, "state": "AZ", "_id": "85621"} -{"city": "ORACLE", "loc": [-110.796126, 32.600506], "pop": 3833, "state": "AZ", "_id": "85623"} -{"city": "PATAGONIA", "loc": [-110.696774, 31.535317], "pop": 1430, "state": "AZ", "_id": "85624"} -{"city": "PEARCE", "loc": [-109.795032, 31.966608], "pop": 2421, "state": "AZ", "_id": "85625"} -{"city": "SAHUARITA", "loc": [-111.000154, 31.945169], "pop": 2973, "state": "AZ", "_id": "85629"} -{"city": "SAINT DAVID", "loc": [-110.215377, 31.897251], "pop": 1928, "state": "AZ", "_id": "85630"} -{"city": "SAN MANUEL", "loc": [-110.656788, 32.695831], "pop": 6253, "state": "AZ", "_id": "85631"} -{"city": "PORTAL", "loc": [-109.367152, 32.208167], "pop": 1485, "state": "AZ", "_id": "85632"} -{"city": "PISINEMO", "loc": [-111.922207, 32.031572], "pop": 9003, "state": "AZ", "_id": "85634"} -{"city": "SIERRA VISTA", "loc": [-110.266565, 31.536467], "pop": 33700, "state": "AZ", "_id": "85635"} -{"city": "SONOITA", "loc": [-110.724418, 31.866154], "pop": 1399, "state": "AZ", "_id": "85637"} -{"city": "TOMBSTONE", "loc": [-110.058449, 31.721598], "pop": 1556, "state": "AZ", "_id": "85638"} -{"city": "AMADO", "loc": [-111.039693, 31.594194], "pop": 813, "state": "AZ", "_id": "85640"} -{"city": "VAIL", "loc": [-110.88375, 32.035926], "pop": 2843, "state": "AZ", "_id": "85641"} -{"city": "WILLCOX", "loc": [-109.863111, 32.372977], "pop": 7297, "state": "AZ", "_id": "85643"} -{"city": "AMADO", "loc": [-111.072233, 31.643759], "pop": 1157, "state": "AZ", "_id": "85645"} -{"city": "MARANA", "loc": [-111.273621, 32.404749], "pop": 7562, "state": "AZ", "_id": "85653"} -{"city": "TUCSON", "loc": [-110.969445, 32.213873], "pop": 5191, "state": "AZ", "_id": "85701"} -{"city": "CASAS ADOBES", "loc": [-110.984593, 32.329175], "pop": 24039, "state": "AZ", "_id": "85704"} -{"city": "TUCSON", "loc": [-110.984536, 32.269088], "pop": 52751, "state": "AZ", "_id": "85705"} -{"city": "TUCSON", "loc": [-110.945127, 32.139172], "pop": 52458, "state": "AZ", "_id": "85706"} -{"city": "TUCSON", "loc": [-110.869283, 32.179989], "pop": 6191, "state": "AZ", "_id": "85708"} -{"city": "TUCSON", "loc": [-110.824046, 32.213813], "pop": 52679, "state": "AZ", "_id": "85710"} -{"city": "TUCSON", "loc": [-110.882892, 32.212729], "pop": 40024, "state": "AZ", "_id": "85711"} -{"city": "TUCSON", "loc": [-110.886919, 32.250043], "pop": 28813, "state": "AZ", "_id": "85712"} -{"city": "TUCSON", "loc": [-110.973896, 32.194065], "pop": 40625, "state": "AZ", "_id": "85713"} -{"city": "TUCSON", "loc": [-110.971891, 32.170657], "pop": 16488, "state": "AZ", "_id": "85714"} -{"city": "TUCSON", "loc": [-110.834837, 32.269213], "pop": 33197, "state": "AZ", "_id": "85715"} -{"city": "TUCSON", "loc": [-110.922176, 32.246815], "pop": 32258, "state": "AZ", "_id": "85716"} -{"city": "TUCSON", "loc": [-110.917882, 32.311154], "pop": 22441, "state": "AZ", "_id": "85718"} -{"city": "TUCSON", "loc": [-110.949142, 32.247426], "pop": 39019, "state": "AZ", "_id": "85719"} -{"city": "TUCSON", "loc": [-110.81904, 32.180951], "pop": 33251, "state": "AZ", "_id": "85730"} -{"city": "TUCSON", "loc": [-111.260758, 32.057796], "pop": 2987, "state": "AZ", "_id": "85735"} -{"city": "TUCSON", "loc": [-111.317842, 31.667909], "pop": 1130, "state": "AZ", "_id": "85736"} -{"city": "ORO VALLEY", "loc": [-110.954463, 32.431679], "pop": 14077, "state": "AZ", "_id": "85737"} -{"city": "TUCSON", "loc": [-111.041873, 32.347215], "pop": 36400, "state": "AZ", "_id": "85741"} -{"city": "TUCSON", "loc": [-111.177071, 32.33655], "pop": 4507, "state": "AZ", "_id": "85743"} -{"city": "TUCSON", "loc": [-111.017907, 32.243359], "pop": 25143, "state": "AZ", "_id": "85745"} -{"city": "TUCSON", "loc": [-111.050569, 32.142244], "pop": 34683, "state": "AZ", "_id": "85746"} -{"city": "TUCSON", "loc": [-110.667337, 32.071142], "pop": 2286, "state": "AZ", "_id": "85747"} -{"city": "TUCSON", "loc": [-110.775765, 32.214981], "pop": 9675, "state": "AZ", "_id": "85748"} -{"city": "TUCSON", "loc": [-110.765829, 32.273285], "pop": 14254, "state": "AZ", "_id": "85749"} -{"city": "SHOW LOW", "loc": [-110.054633, 34.060117], "pop": 16493, "state": "AZ", "_id": "85901"} -{"city": "ALPINE", "loc": [-109.12829, 33.827878], "pop": 243, "state": "AZ", "_id": "85920"} -{"city": "BLUE", "loc": [-109.06849, 33.651245], "pop": 14, "state": "AZ", "_id": "85922"} -{"city": "CONCHO", "loc": [-109.674096, 34.445787], "pop": 949, "state": "AZ", "_id": "85924"} -{"city": "EAGAR", "loc": [-109.246933, 33.954571], "pop": 482, "state": "AZ", "_id": "85925"} -{"city": "HEBER", "loc": [-110.568647, 34.4163], "pop": 1856, "state": "AZ", "_id": "85928"} -{"city": "LAKESIDE", "loc": [-109.986878, 34.166224], "pop": 5350, "state": "AZ", "_id": "85929"} -{"city": "PINETOP", "loc": [-109.919668, 34.117459], "pop": 1938, "state": "AZ", "_id": "85935"} -{"city": "SAINT JOHNS", "loc": [-109.379617, 34.501008], "pop": 3844, "state": "AZ", "_id": "85936"} -{"city": "SNOWFLAKE", "loc": [-110.080742, 34.495859], "pop": 6678, "state": "AZ", "_id": "85937"} -{"city": "SPRINGERVILLE", "loc": [-109.304066, 34.119333], "pop": 6560, "state": "AZ", "_id": "85938"} -{"city": "FLAGSTAFF", "loc": [-111.661979, 35.185911], "pop": 30174, "state": "AZ", "_id": "86001"} -{"city": "FLAGSTAFF", "loc": [-111.574109, 35.225736], "pop": 26878, "state": "AZ", "_id": "86004"} -{"city": "COLORADO CITY", "loc": [-112.952427, 36.976266], "pop": 3065, "state": "AZ", "_id": "86021"} -{"city": "FREDONIA", "loc": [-112.497864, 36.904397], "pop": 1393, "state": "AZ", "_id": "86022"} -{"city": "HOLBROOK", "loc": [-110.143412, 34.908451], "pop": 5567, "state": "AZ", "_id": "86025"} -{"city": "HOTEVILLA", "loc": [-110.566107, 36.211141], "pop": 271, "state": "AZ", "_id": "86030"} -{"city": "KAYENTA", "loc": [-110.265229, 36.688327], "pop": 7549, "state": "AZ", "_id": "86033"} -{"city": "KEAMS CANYON", "loc": [-110.284461, 35.808206], "pop": 3240, "state": "AZ", "_id": "86034"} -{"city": "LEUPP", "loc": [-110.992651, 35.336528], "pop": 2396, "state": "AZ", "_id": "86035"} -{"city": "MARBLE CANYON", "loc": [-111.558166, 36.956943], "pop": 564, "state": "AZ", "_id": "86036"} -{"city": "MORMON LAKE", "loc": [-111.454914, 34.916896], "pop": 55, "state": "AZ", "_id": "86038"} -{"city": "KYKOTSMOVI VILLA", "loc": [-110.368805, 35.579084], "pop": 167, "state": "AZ", "_id": "86039"} -{"city": "GREENEHAVEN", "loc": [-111.43847, 36.896625], "pop": 8428, "state": "AZ", "_id": "86040"} -{"city": "POLACCA", "loc": [-110.51114, 35.811812], "pop": 1723, "state": "AZ", "_id": "86042"} -{"city": "SECOND MESA", "loc": [-110.6472, 35.903782], "pop": 1653, "state": "AZ", "_id": "86043"} -{"city": "TONALEA", "loc": [-110.882042, 35.934555], "pop": 158, "state": "AZ", "_id": "86044"} -{"city": "TUBA CITY", "loc": [-111.268566, 36.103729], "pop": 10514, "state": "AZ", "_id": "86045"} -{"city": "WILLIAMS", "loc": [-112.17075, 35.543398], "pop": 6117, "state": "AZ", "_id": "86046"} -{"city": "WINSLOW", "loc": [-110.511382, 35.16078], "pop": 17429, "state": "AZ", "_id": "86047"} -{"city": "KAIBITO", "loc": [-111.136973, 36.484798], "pop": 6098, "state": "AZ", "_id": "86053"} -{"city": "SHONTO", "loc": [-110.647743, 36.61594], "pop": 2049, "state": "AZ", "_id": "86054"} -{"city": "PRESCOTT", "loc": [-113.022459, 34.629909], "pop": 915, "state": "AZ", "_id": "86301"} -{"city": "GROOM CREEK", "loc": [-112.473459, 34.558577], "pop": 36617, "state": "AZ", "_id": "86303"} -{"city": "PRESCOTT VALLEY", "loc": [-112.326378, 34.601934], "pop": 11396, "state": "AZ", "_id": "86314"} -{"city": "ASH FORK", "loc": [-112.502681, 35.214998], "pop": 563, "state": "AZ", "_id": "86320"} -{"city": "BAGDAD", "loc": [-113.175535, 34.578484], "pop": 1596, "state": "AZ", "_id": "86321"} -{"city": "CAMP VERDE", "loc": [-111.855131, 34.569733], "pop": 6250, "state": "AZ", "_id": "86322"} -{"city": "CHINO VALLEY", "loc": [-112.473099, 34.775739], "pop": 7285, "state": "AZ", "_id": "86323"} -{"city": "CLARKDALE", "loc": [-112.033417, 34.747793], "pop": 7574, "state": "AZ", "_id": "86324"} -{"city": "CORNVILLE", "loc": [-111.908556, 34.725593], "pop": 2612, "state": "AZ", "_id": "86325"} -{"city": "COTTONWOOD", "loc": [-112.009099, 34.705547], "pop": 8530, "state": "AZ", "_id": "86326"} -{"city": "DEWEY", "loc": [-112.256665, 34.536753], "pop": 3965, "state": "AZ", "_id": "86327"} -{"city": "KIRKLAND", "loc": [-112.896641, 34.454149], "pop": 186, "state": "AZ", "_id": "86332"} -{"city": "MAYER", "loc": [-112.129551, 34.365535], "pop": 3248, "state": "AZ", "_id": "86333"} -{"city": "PAULDEN", "loc": [-112.544105, 35.03129], "pop": 24, "state": "AZ", "_id": "86334"} -{"city": "RIMROCK", "loc": [-111.784222, 34.63799], "pop": 1743, "state": "AZ", "_id": "86335"} -{"city": "SEDONA", "loc": [-111.750627, 34.826645], "pop": 13225, "state": "AZ", "_id": "86336"} -{"city": "SELIGMAN", "loc": [-112.954805, 35.321246], "pop": 693, "state": "AZ", "_id": "86337"} -{"city": "CROWN KING", "loc": [-112.333971, 34.224062], "pop": 105, "state": "AZ", "_id": "86343"} -{"city": "KINGMAN", "loc": [-114.05689, 35.258379], "pop": 32002, "state": "AZ", "_id": "86401"} -{"city": "DESERT HILLS", "loc": [-114.308083, 34.492879], "pop": 26718, "state": "AZ", "_id": "86403"} -{"city": "HUALAPAI", "loc": [-113.295324, 35.540732], "pop": 2, "state": "AZ", "_id": "86412"} -{"city": "BULLHEAD CITY", "loc": [-114.588816, 35.014832], "pop": 3196, "state": "AZ", "_id": "86430"} -{"city": "LITTLEFIELD", "loc": [-113.913693, 36.866524], "pop": 87, "state": "AZ", "_id": "86432"} -{"city": "PEACH SPRINGS", "loc": [-113.420199, 35.537795], "pop": 798, "state": "AZ", "_id": "86434"} -{"city": "SUPAI", "loc": [-112.693212, 36.224157], "pop": 423, "state": "AZ", "_id": "86435"} -{"city": "TOPOCK", "loc": [-114.481666, 34.778388], "pop": 912, "state": "AZ", "_id": "86436"} -{"city": "MOHAVE VALLEY", "loc": [-114.595115, 34.892942], "pop": 4139, "state": "AZ", "_id": "86440"} -{"city": "DOLAN SPRINGS", "loc": [-114.547771, 35.774789], "pop": 68, "state": "AZ", "_id": "86441"} -{"city": "BULLHEAD CITY", "loc": [-114.594737, 35.106001], "pop": 22394, "state": "AZ", "_id": "86442"} -{"city": "MEADVIEW", "loc": [-114.327696, 35.813733], "pop": 118, "state": "AZ", "_id": "86444"} -{"city": "CHAMBERS", "loc": [-109.37389, 35.143044], "pop": 1085, "state": "AZ", "_id": "86502"} -{"city": "CHINLE", "loc": [-109.603693, 36.130367], "pop": 10679, "state": "AZ", "_id": "86503"} -{"city": "GANADO", "loc": [-109.283168, 35.651844], "pop": 23428, "state": "AZ", "_id": "86505"} -{"city": "LUKACHUKAI", "loc": [-109.244614, 36.418111], "pop": 1665, "state": "AZ", "_id": "86507"} -{"city": "NAVAJO", "loc": [-109.396217, 34.817202], "pop": 41, "state": "AZ", "_id": "86509"} -{"city": "PINON", "loc": [-110.221077, 36.100243], "pop": 5911, "state": "AZ", "_id": "86510"} -{"city": "TEEC NOS POS", "loc": [-109.359039, 36.779694], "pop": 4941, "state": "AZ", "_id": "86514"} -{"city": "DENNEHOTSO", "loc": [-109.861001, 36.777286], "pop": 1693, "state": "AZ", "_id": "86535"} -{"city": "MANY FARMS", "loc": [-109.634021, 36.408259], "pop": 4172, "state": "AZ", "_id": "86538"} -{"city": "TSAILE", "loc": [-109.217627, 36.307075], "pop": 1593, "state": "AZ", "_id": "86556"} -{"city": "NORTH CEDAR", "loc": [-91.995812, 34.215405], "pop": 23095, "state": "AR", "_id": "71601"} -{"city": "DOLLARWAY", "loc": [-92.089718, 34.257001], "pop": 15547, "state": "AR", "_id": "71602"} -{"city": "PINE BLUFF", "loc": [-92.044786, 34.189745], "pop": 36473, "state": "AR", "_id": "71603"} -{"city": "ARKANSAS CITY", "loc": [-91.232529, 33.614328], "pop": 7, "state": "AR", "_id": "71630"} -{"city": "BANKS", "loc": [-92.260386, 33.549665], "pop": 514, "state": "AR", "_id": "71631"} -{"city": "NORTH", "loc": [-91.959152, 33.152369], "pop": 14645, "state": "AR", "_id": "71635"} -{"city": "DERMOTT", "loc": [-91.439391, 33.524054], "pop": 5880, "state": "AR", "_id": "71638"} -{"city": "DUMAS", "loc": [-91.486056, 33.892102], "pop": 7033, "state": "AR", "_id": "71639"} -{"city": "EUDORA", "loc": [-91.271552, 33.12135], "pop": 4860, "state": "AR", "_id": "71640"} -{"city": "FOUNTAIN HILL", "loc": [-91.835627, 33.342951], "pop": 704, "state": "AR", "_id": "71642"} -{"city": "GOULD", "loc": [-91.576798, 34.034503], "pop": 3765, "state": "AR", "_id": "71643"} -{"city": "TAMO", "loc": [-91.67082, 34.030484], "pop": 3768, "state": "AR", "_id": "71644"} -{"city": "HAMBURG", "loc": [-91.802265, 33.2058], "pop": 5422, "state": "AR", "_id": "71646"} -{"city": "INGALLS", "loc": [-92.127714, 33.422207], "pop": 2150, "state": "AR", "_id": "71647"} -{"city": "JERSEY", "loc": [-92.296613, 33.388914], "pop": 301, "state": "AR", "_id": "71651"} -{"city": "KINGSLAND", "loc": [-92.301415, 33.86002], "pop": 993, "state": "AR", "_id": "71652"} -{"city": "LAKE VILLAGE", "loc": [-91.282487, 33.327408], "pop": 5501, "state": "AR", "_id": "71653"} -{"city": "MC GEHEE", "loc": [-91.392781, 33.62971], "pop": 6646, "state": "AR", "_id": "71654"} -{"city": "MONTICELLO", "loc": [-91.794845, 33.624951], "pop": 14127, "state": "AR", "_id": "71655"} -{"city": "MONTROSE", "loc": [-91.522776, 33.307516], "pop": 948, "state": "AR", "_id": "71658"} -{"city": "NEW EDINBURG", "loc": [-92.193909, 33.758846], "pop": 1105, "state": "AR", "_id": "71660"} -{"city": "PARKDALE", "loc": [-91.542793, 33.121267], "pop": 560, "state": "AR", "_id": "71661"} -{"city": "PICKENS", "loc": [-91.39157, 33.807923], "pop": 1228, "state": "AR", "_id": "71662"} -{"city": "PORTLAND", "loc": [-91.513935, 33.231773], "pop": 773, "state": "AR", "_id": "71663"} -{"city": "RISON", "loc": [-92.118762, 33.945325], "pop": 5669, "state": "AR", "_id": "71665"} -{"city": "ROHWER", "loc": [-91.205124, 33.616003], "pop": 656, "state": "AR", "_id": "71666"} -{"city": "STAR CITY", "loc": [-91.865343, 33.940528], "pop": 5913, "state": "AR", "_id": "71667"} -{"city": "REED", "loc": [-91.520287, 33.67479], "pop": 739, "state": "AR", "_id": "71670"} -{"city": "WARREN", "loc": [-92.077824, 33.613983], "pop": 8828, "state": "AR", "_id": "71671"} -{"city": "WATSON", "loc": [-91.281455, 33.890737], "pop": 1003, "state": "AR", "_id": "71674"} -{"city": "WILMAR", "loc": [-91.925697, 33.621296], "pop": 1293, "state": "AR", "_id": "71675"} -{"city": "WILMOT", "loc": [-91.572283, 33.057557], "pop": 1267, "state": "AR", "_id": "71676"} -{"city": "WINCHESTER", "loc": [-91.543059, 33.752039], "pop": 687, "state": "AR", "_id": "71677"} -{"city": "YORKTOWN", "loc": [-91.796472, 34.017166], "pop": 868, "state": "AR", "_id": "71678"} -{"city": "EAST CAMDEN", "loc": [-92.833386, 33.575866], "pop": 22640, "state": "AR", "_id": "71701"} -{"city": "BEARDEN", "loc": [-92.61802, 33.729797], "pop": 1945, "state": "AR", "_id": "71720"} -{"city": "BLUFF CITY", "loc": [-93.186811, 33.698191], "pop": 546, "state": "AR", "_id": "71722"} -{"city": "CARTHAGE", "loc": [-92.62623, 34.063625], "pop": 1255, "state": "AR", "_id": "71725"} -{"city": "READER", "loc": [-93.018716, 33.700148], "pop": 1416, "state": "AR", "_id": "71726"} -{"city": "EL DORADO", "loc": [-92.662856, 33.20735], "pop": 35308, "state": "AR", "_id": "71730"} -{"city": "EMERSON", "loc": [-93.198699, 33.089054], "pop": 1888, "state": "AR", "_id": "71740"} -{"city": "FORDYCE", "loc": [-92.422475, 33.817648], "pop": 6001, "state": "AR", "_id": "71742"} -{"city": "GURDON", "loc": [-93.141691, 33.912493], "pop": 4180, "state": "AR", "_id": "71743"} -{"city": "HAMPTON", "loc": [-92.52951, 33.537613], "pop": 3505, "state": "AR", "_id": "71744"} -{"city": "HARRELL", "loc": [-92.391243, 33.510865], "pop": 846, "state": "AR", "_id": "71745"} -{"city": "HUTTIG", "loc": [-92.194153, 33.045888], "pop": 1287, "state": "AR", "_id": "71747"} -{"city": "IVAN", "loc": [-92.439406, 33.902984], "pop": 353, "state": "AR", "_id": "71748"} -{"city": "JUNCTION CITY", "loc": [-92.684254, 33.043965], "pop": 1553, "state": "AR", "_id": "71749"} -{"city": "LOUANN", "loc": [-92.782759, 33.411744], "pop": 1667, "state": "AR", "_id": "71751"} -{"city": "MC NEIL", "loc": [-93.193006, 33.36222], "pop": 2020, "state": "AR", "_id": "71752"} -{"city": "MAGNOLIA", "loc": [-93.239153, 33.264678], "pop": 16379, "state": "AR", "_id": "71753"} -{"city": "MOUNT HOLLY", "loc": [-92.944265, 33.308534], "pop": 514, "state": "AR", "_id": "71758"} -{"city": "NORPHLET", "loc": [-92.657619, 33.309619], "pop": 1765, "state": "AR", "_id": "71759"} -{"city": "SMACKOVER", "loc": [-92.74425, 33.339831], "pop": 3554, "state": "AR", "_id": "71762"} -{"city": "MANNING", "loc": [-92.810984, 33.902989], "pop": 2005, "state": "AR", "_id": "71763"} -{"city": "STEPHENS", "loc": [-93.021391, 33.455044], "pop": 2906, "state": "AR", "_id": "71764"} -{"city": "STRONG", "loc": [-92.362104, 33.119526], "pop": 2738, "state": "AR", "_id": "71765"} -{"city": "THORNTON", "loc": [-92.468299, 33.767694], "pop": 1272, "state": "AR", "_id": "71766"} -{"city": "TINSMAN", "loc": [-92.382192, 33.643436], "pop": 203, "state": "AR", "_id": "71767"} -{"city": "VILLAGE", "loc": [-93.046404, 33.281849], "pop": 882, "state": "AR", "_id": "71769"} -{"city": "WALDO", "loc": [-93.294915, 33.360017], "pop": 2862, "state": "AR", "_id": "71770"} -{"city": "PERRYTOWN", "loc": [-93.590305, 33.656645], "pop": 15955, "state": "AR", "_id": "71801"} -{"city": "ASHDOWN", "loc": [-94.135102, 33.678711], "pop": 8514, "state": "AR", "_id": "71822"} -{"city": "BLEVINS", "loc": [-93.536035, 33.875468], "pop": 831, "state": "AR", "_id": "71825"} -{"city": "BRADLEY", "loc": [-93.627496, 33.106963], "pop": 2623, "state": "AR", "_id": "71826"} -{"city": "BUCKNER", "loc": [-93.446998, 33.375323], "pop": 1364, "state": "AR", "_id": "71827"} -{"city": "CALE", "loc": [-93.26497, 33.616403], "pop": 230, "state": "AR", "_id": "71828"} -{"city": "COLUMBUS", "loc": [-93.85588, 33.745108], "pop": 739, "state": "AR", "_id": "71831"} -{"city": "DE QUEEN", "loc": [-94.338559, 34.044206], "pop": 7593, "state": "AR", "_id": "71832"} -{"city": "DIERKS", "loc": [-94.015243, 34.13232], "pop": 2284, "state": "AR", "_id": "71833"} -{"city": "DODDRIDGE", "loc": [-93.954307, 33.105448], "pop": 1435, "state": "AR", "_id": "71834"} -{"city": "EMMET", "loc": [-93.423242, 33.692896], "pop": 1141, "state": "AR", "_id": "71835"} -{"city": "FOREMAN", "loc": [-94.388108, 33.71759], "pop": 2740, "state": "AR", "_id": "71836"} -{"city": "FOUKE", "loc": [-93.900953, 33.302476], "pop": 5895, "state": "AR", "_id": "71837"} -{"city": "FULTON", "loc": [-93.808562, 33.629876], "pop": 925, "state": "AR", "_id": "71838"} -{"city": "GARLAND CITY", "loc": [-93.731624, 33.335161], "pop": 666, "state": "AR", "_id": "71839"} -{"city": "GILLHAM", "loc": [-94.316497, 34.157003], "pop": 853, "state": "AR", "_id": "71841"} -{"city": "HORATIO", "loc": [-94.295942, 33.939221], "pop": 2952, "state": "AR", "_id": "71842"} -{"city": "LEWISVILLE", "loc": [-93.595258, 33.373603], "pop": 2550, "state": "AR", "_id": "71845"} -{"city": "LOCKESBURG", "loc": [-94.127588, 33.930553], "pop": 2239, "state": "AR", "_id": "71846"} -{"city": "MC CASKILL", "loc": [-93.626605, 33.923042], "pop": 658, "state": "AR", "_id": "71847"} -{"city": "MINERAL SPRINGS", "loc": [-93.918828, 33.863852], "pop": 2732, "state": "AR", "_id": "71851"} -{"city": "NASHVILLE", "loc": [-93.870709, 33.957646], "pop": 7625, "state": "AR", "_id": "71852"} -{"city": "OGDEN", "loc": [-94.027826, 33.585706], "pop": 779, "state": "AR", "_id": "71853"} -{"city": "OZAN", "loc": [-93.771443, 33.902775], "pop": 1695, "state": "AR", "_id": "71855"} -{"city": "PRESCOTT", "loc": [-93.372544, 33.804029], "pop": 5732, "state": "AR", "_id": "71857"} -{"city": "ROSSTON", "loc": [-93.303891, 33.561693], "pop": 1561, "state": "AR", "_id": "71858"} -{"city": "SARATOGA", "loc": [-93.876723, 33.759883], "pop": 281, "state": "AR", "_id": "71859"} -{"city": "STAMPS", "loc": [-93.501307, 33.356877], "pop": 3106, "state": "AR", "_id": "71860"} -{"city": "TAYLOR", "loc": [-93.446019, 33.107957], "pop": 1660, "state": "AR", "_id": "71861"} -{"city": "WASHINGTON", "loc": [-93.673529, 33.754596], "pop": 821, "state": "AR", "_id": "71862"} -{"city": "WILLISVILLE", "loc": [-93.31212, 33.484731], "pop": 888, "state": "AR", "_id": "71864"} -{"city": "WILTON", "loc": [-94.135746, 33.734794], "pop": 983, "state": "AR", "_id": "71865"} -{"city": "WINTHROP", "loc": [-94.395174, 33.858321], "pop": 950, "state": "AR", "_id": "71866"} -{"city": "LAKE CATHERINE", "loc": [-93.026024, 34.501475], "pop": 27402, "state": "AR", "_id": "71901"} -{"city": "HOT SPRINGS VILL", "loc": [-93.006386, 34.65862], "pop": 8268, "state": "AR", "_id": "71909"} -{"city": "LAKE HAMILTON", "loc": [-93.109177, 34.473304], "pop": 31048, "state": "AR", "_id": "71913"} -{"city": "AMITY", "loc": [-93.420551, 34.259362], "pop": 1569, "state": "AR", "_id": "71921"} -{"city": "ANTOINE", "loc": [-93.437169, 34.028296], "pop": 474, "state": "AR", "_id": "71922"} -{"city": "ARKADELPHIA", "loc": [-93.068989, 34.11525], "pop": 14961, "state": "AR", "_id": "71923"} -{"city": "BISMARCK", "loc": [-93.187236, 34.311033], "pop": 1291, "state": "AR", "_id": "71929"} -{"city": "BLAKELY", "loc": [-93.063509, 34.703957], "pop": 271, "state": "AR", "_id": "71931"} -{"city": "BONNERDALE", "loc": [-93.319415, 34.349751], "pop": 1037, "state": "AR", "_id": "71933"} -{"city": "CADDO GAP", "loc": [-93.586376, 34.397579], "pop": 2257, "state": "AR", "_id": "71935"} -{"city": "COVE", "loc": [-94.39234, 34.419159], "pop": 1681, "state": "AR", "_id": "71937"} -{"city": "DELIGHT", "loc": [-93.524666, 34.023772], "pop": 1529, "state": "AR", "_id": "71940"} -{"city": "DONALDSON", "loc": [-92.909384, 34.221221], "pop": 748, "state": "AR", "_id": "71941"} -{"city": "FRIENDSHIP", "loc": [-92.980393, 34.241225], "pop": 626, "state": "AR", "_id": "71942"} -{"city": "GLENWOOD", "loc": [-93.555881, 34.319207], "pop": 2720, "state": "AR", "_id": "71943"} -{"city": "GRANNIS", "loc": [-94.3255, 34.237021], "pop": 751, "state": "AR", "_id": "71944"} -{"city": "HATFIELD", "loc": [-94.371397, 34.487703], "pop": 1048, "state": "AR", "_id": "71945"} -{"city": "JESSIEVILLE", "loc": [-93.196318, 34.693729], "pop": 931, "state": "AR", "_id": "71949"} -{"city": "KIRBY", "loc": [-93.616716, 34.255153], "pop": 973, "state": "AR", "_id": "71950"} -{"city": "LANGLEY", "loc": [-93.850942, 34.314035], "pop": 215, "state": "AR", "_id": "71952"} -{"city": "MENA", "loc": [-94.220984, 34.581435], "pop": 12505, "state": "AR", "_id": "71953"} -{"city": "BUCKVILLE", "loc": [-93.160041, 34.577182], "pop": 1857, "state": "AR", "_id": "71956"} -{"city": "MOUNT IDA", "loc": [-93.574904, 34.561194], "pop": 3052, "state": "AR", "_id": "71957"} -{"city": "MURFREESBORO", "loc": [-93.710903, 34.101734], "pop": 3775, "state": "AR", "_id": "71958"} -{"city": "NEWHOPE", "loc": [-93.890487, 34.227347], "pop": 400, "state": "AR", "_id": "71959"} -{"city": "NORMAN", "loc": [-93.674304, 34.459633], "pop": 688, "state": "AR", "_id": "71960"} -{"city": "ODEN", "loc": [-93.821103, 34.611292], "pop": 710, "state": "AR", "_id": "71961"} -{"city": "OKOLONA", "loc": [-93.289679, 34.055138], "pop": 985, "state": "AR", "_id": "71962"} -{"city": "PEARCY", "loc": [-93.242015, 34.435092], "pop": 3215, "state": "AR", "_id": "71964"} -{"city": "PENCIL BLUFF", "loc": [-93.742947, 34.639946], "pop": 323, "state": "AR", "_id": "71965"} -{"city": "ROYAL", "loc": [-93.289734, 34.515049], "pop": 1099, "state": "AR", "_id": "71968"} -{"city": "SIMS", "loc": [-93.674071, 34.646181], "pop": 461, "state": "AR", "_id": "71969"} -{"city": "STORY", "loc": [-93.537587, 34.668695], "pop": 350, "state": "AR", "_id": "71970"} -{"city": "UMPIRE", "loc": [-94.031377, 34.292129], "pop": 647, "state": "AR", "_id": "71971"} -{"city": "VANDERVOORT", "loc": [-94.369788, 34.379535], "pop": 323, "state": "AR", "_id": "71972"} -{"city": "WICKES", "loc": [-94.340257, 34.308806], "pop": 1273, "state": "AR", "_id": "71973"} -{"city": "ADONA", "loc": [-92.903325, 35.046956], "pop": 494, "state": "AR", "_id": "72001"} -{"city": "ALEXANDER", "loc": [-92.472673, 34.631266], "pop": 7984, "state": "AR", "_id": "72002"} -{"city": "ALMYRA", "loc": [-91.430992, 34.41459], "pop": 626, "state": "AR", "_id": "72003"} -{"city": "ALTHEIMER", "loc": [-91.828916, 34.306168], "pop": 1929, "state": "AR", "_id": "72004"} -{"city": "AMAGON", "loc": [-91.079636, 35.561559], "pop": 457, "state": "AR", "_id": "72005"} -{"city": "AUGUSTA", "loc": [-91.352653, 35.278806], "pop": 3702, "state": "AR", "_id": "72006"} -{"city": "AUSTIN", "loc": [-91.959398, 35.028369], "pop": 1741, "state": "AR", "_id": "72007"} -{"city": "BALD KNOB", "loc": [-91.550197, 35.311331], "pop": 5132, "state": "AR", "_id": "72010"} -{"city": "BAUXITE", "loc": [-92.360531, 34.545537], "pop": 6956, "state": "AR", "_id": "72011"} -{"city": "BEEBE", "loc": [-91.907449, 35.093743], "pop": 7862, "state": "AR", "_id": "72012"} -{"city": "BEE BRANCH", "loc": [-92.408523, 35.423367], "pop": 1344, "state": "AR", "_id": "72013"} -{"city": "BEEDEVILLE", "loc": [-91.106371, 35.420267], "pop": 518, "state": "AR", "_id": "72014"} -{"city": "BENTON", "loc": [-92.595241, 34.580087], "pop": 36525, "state": "AR", "_id": "72015"} -{"city": "BIGELOW", "loc": [-92.630842, 34.984659], "pop": 2001, "state": "AR", "_id": "72016"} -{"city": "BISCOE", "loc": [-91.490028, 34.833706], "pop": 1348, "state": "AR", "_id": "72017"} -{"city": "BRADFORD", "loc": [-91.518973, 35.427655], "pop": 3546, "state": "AR", "_id": "72020"} -{"city": "BRINKLEY", "loc": [-91.188596, 34.878124], "pop": 6313, "state": "AR", "_id": "72021"} -{"city": "BRYANT", "loc": [-92.492015, 34.606786], "pop": 6065, "state": "AR", "_id": "72022"} -{"city": "CABOT", "loc": [-92.031771, 34.945724], "pop": 21197, "state": "AR", "_id": "72023"} -{"city": "CARLISLE", "loc": [-91.745929, 34.793291], "pop": 2904, "state": "AR", "_id": "72024"} -{"city": "CASA", "loc": [-93.047005, 35.032168], "pop": 649, "state": "AR", "_id": "72025"} -{"city": "CASSCOE", "loc": [-91.324798, 34.473625], "pop": 1033, "state": "AR", "_id": "72026"} -{"city": "CENTER RIDGE", "loc": [-92.558167, 35.398063], "pop": 981, "state": "AR", "_id": "72027"} -{"city": "CHOCTAW", "loc": [-92.426412, 35.523528], "pop": 1091, "state": "AR", "_id": "72028"} -{"city": "CLARENDON", "loc": [-91.256426, 34.660138], "pop": 3211, "state": "AR", "_id": "72029"} -{"city": "CLEVELAND", "loc": [-92.712821, 35.358328], "pop": 1244, "state": "AR", "_id": "72030"} -{"city": "CLINTON", "loc": [-92.475825, 35.604537], "pop": 4098, "state": "AR", "_id": "72031"} -{"city": "CONWAY", "loc": [-92.423574, 35.084199], "pop": 43236, "state": "AR", "_id": "72032"} -{"city": "COTTON PLANT", "loc": [-91.229043, 35.017827], "pop": 1789, "state": "AR", "_id": "72036"} -{"city": "CROCKETTS BLUFF", "loc": [-91.232079, 34.425272], "pop": 132, "state": "AR", "_id": "72038"} -{"city": "TWIN GROVES", "loc": [-92.432952, 35.291042], "pop": 1605, "state": "AR", "_id": "72039"} -{"city": "DES ARC", "loc": [-91.511342, 34.97507], "pop": 3446, "state": "AR", "_id": "72040"} -{"city": "DE VALLS BLUFF", "loc": [-91.498218, 34.744704], "pop": 1854, "state": "AR", "_id": "72041"} -{"city": "DE WITT", "loc": [-91.333627, 34.285312], "pop": 5320, "state": "AR", "_id": "72042"} -{"city": "EDGEMONT", "loc": [-92.199471, 35.623006], "pop": 487, "state": "AR", "_id": "72044"} -{"city": "EL PASO", "loc": [-92.090076, 35.114176], "pop": 580, "state": "AR", "_id": "72045"} -{"city": "ENGLAND", "loc": [-91.948422, 34.557458], "pop": 5254, "state": "AR", "_id": "72046"} -{"city": "ENOLA", "loc": [-92.212328, 35.208705], "pop": 500, "state": "AR", "_id": "72047"} -{"city": "ETHEL", "loc": [-91.139839, 34.243525], "pop": 27, "state": "AR", "_id": "72048"} -{"city": "FOX", "loc": [-92.304266, 35.768208], "pop": 960, "state": "AR", "_id": "72051"} -{"city": "GARNER", "loc": [-91.742884, 35.130305], "pop": 618, "state": "AR", "_id": "72052"} -{"city": "GILLETT", "loc": [-91.380048, 34.121838], "pop": 925, "state": "AR", "_id": "72055"} -{"city": "GRAPEVINE", "loc": [-92.310944, 34.130893], "pop": 591, "state": "AR", "_id": "72057"} -{"city": "GREENBRIER", "loc": [-92.357792, 35.229547], "pop": 5532, "state": "AR", "_id": "72058"} -{"city": "GRIFFITHVILLE", "loc": [-91.624234, 35.114383], "pop": 582, "state": "AR", "_id": "72060"} -{"city": "GUY", "loc": [-92.331539, 35.318487], "pop": 1026, "state": "AR", "_id": "72061"} -{"city": "HATTIEVILLE", "loc": [-92.778317, 35.290713], "pop": 360, "state": "AR", "_id": "72063"} -{"city": "HAZEN", "loc": [-91.576699, 34.783833], "pop": 1841, "state": "AR", "_id": "72064"} -{"city": "HENSLEY", "loc": [-92.214019, 34.591133], "pop": 3696, "state": "AR", "_id": "72065"} -{"city": "HICKORY PLAINS", "loc": [-91.750844, 34.978642], "pop": 522, "state": "AR", "_id": "72066"} -{"city": "GREERS FERRY", "loc": [-92.182284, 35.549812], "pop": 1802, "state": "AR", "_id": "72067"} -{"city": "HIGGINSON", "loc": [-91.71828, 35.188254], "pop": 786, "state": "AR", "_id": "72068"} -{"city": "HOLLY GROVE", "loc": [-91.184376, 34.599294], "pop": 1417, "state": "AR", "_id": "72069"} -{"city": "HOUSTON", "loc": [-92.691319, 35.036209], "pop": 642, "state": "AR", "_id": "72070"} -{"city": "HUMNOKE", "loc": [-91.756616, 34.541896], "pop": 992, "state": "AR", "_id": "72072"} -{"city": "HUMPHREY", "loc": [-91.67896, 34.403387], "pop": 851, "state": "AR", "_id": "72073"} -{"city": "GRAVEL RIDGE", "loc": [-92.130435, 34.881985], "pop": 37428, "state": "AR", "_id": "72076"} -{"city": "JEFFERSON", "loc": [-92.148574, 34.355285], "pop": 1682, "state": "AR", "_id": "72079"} -{"city": "JERUSALEM", "loc": [-92.813886, 35.37975], "pop": 765, "state": "AR", "_id": "72080"} -{"city": "JUDSONIA", "loc": [-91.649087, 35.324954], "pop": 7033, "state": "AR", "_id": "72081"} -{"city": "KENSETT", "loc": [-91.669672, 35.229939], "pop": 2123, "state": "AR", "_id": "72082"} -{"city": "KEO", "loc": [-92.007844, 34.604072], "pop": 278, "state": "AR", "_id": "72083"} -{"city": "LEOLA", "loc": [-92.597865, 34.185605], "pop": 769, "state": "AR", "_id": "72084"} -{"city": "LONOKE", "loc": [-91.921367, 34.783162], "pop": 6733, "state": "AR", "_id": "72086"} -{"city": "LONSDALE", "loc": [-92.834034, 34.556203], "pop": 456, "state": "AR", "_id": "72087"} -{"city": "MC CRORY", "loc": [-91.179327, 35.247292], "pop": 4029, "state": "AR", "_id": "72101"} -{"city": "MC RAE", "loc": [-91.821551, 35.11322], "pop": 1095, "state": "AR", "_id": "72102"} -{"city": "SHANNON HILLS", "loc": [-92.384618, 34.621756], "pop": 8853, "state": "AR", "_id": "72103"} -{"city": "MALVERN", "loc": [-92.829162, 34.355715], "pop": 20257, "state": "AR", "_id": "72104"} -{"city": "JONES MILLS", "loc": [-92.861975, 34.453624], "pop": 1896, "state": "AR", "_id": "72105"} -{"city": "MAYFLOWER", "loc": [-92.400102, 34.966853], "pop": 3593, "state": "AR", "_id": "72106"} -{"city": "MORRILTON", "loc": [-92.735436, 35.169227], "pop": 12976, "state": "AR", "_id": "72110"} -{"city": "MOUNT VERNON", "loc": [-92.137334, 35.260613], "pop": 534, "state": "AR", "_id": "72111"} -{"city": "NEWPORT", "loc": [-91.257064, 35.598823], "pop": 13502, "state": "AR", "_id": "72112"} -{"city": "MAUMELLE", "loc": [-92.405892, 34.849085], "pop": 4806, "state": "AR", "_id": "72113"} -{"city": "NORTH LITTLE ROC", "loc": [-92.265376, 34.766974], "pop": 15485, "state": "AR", "_id": "72114"} -{"city": "SHERWOOD", "loc": [-92.237359, 34.807629], "pop": 29871, "state": "AR", "_id": "72116"} -{"city": "NORTH LITTLE ROC", "loc": [-92.194604, 34.776305], "pop": 12736, "state": "AR", "_id": "72117"} -{"city": "CAMP JOSEPH T RO", "loc": [-92.307875, 34.821598], "pop": 26442, "state": "AR", "_id": "72118"} -{"city": "NORTH LITTLE ROC", "loc": [-92.214169, 34.859292], "pop": 15389, "state": "AR", "_id": "72120"} -{"city": "PANGBURN", "loc": [-91.795971, 35.421583], "pop": 1996, "state": "AR", "_id": "72121"} -{"city": "PARON", "loc": [-92.748176, 34.785289], "pop": 484, "state": "AR", "_id": "72122"} -{"city": "PERRY", "loc": [-92.787976, 35.042732], "pop": 648, "state": "AR", "_id": "72125"} -{"city": "PERRYVILLE", "loc": [-92.847171, 34.970096], "pop": 3851, "state": "AR", "_id": "72126"} -{"city": "PLUMERVILLE", "loc": [-92.620435, 35.157466], "pop": 1940, "state": "AR", "_id": "72127"} -{"city": "POYEN", "loc": [-92.599037, 34.350828], "pop": 1753, "state": "AR", "_id": "72128"} -{"city": "PRATTSVILLE", "loc": [-92.513105, 34.307865], "pop": 860, "state": "AR", "_id": "72129"} -{"city": "PRIM", "loc": [-92.134596, 35.685733], "pop": 74, "state": "AR", "_id": "72130"} -{"city": "QUITMAN", "loc": [-92.133334, 35.404988], "pop": 3043, "state": "AR", "_id": "72131"} -{"city": "REDFIELD", "loc": [-92.17579, 34.452647], "pop": 1888, "state": "AR", "_id": "72132"} -{"city": "REYDELL", "loc": [-91.550395, 34.143281], "pop": 150, "state": "AR", "_id": "72133"} -{"city": "ROE", "loc": [-91.377058, 34.628592], "pop": 392, "state": "AR", "_id": "72134"} -{"city": "ROLAND", "loc": [-92.519152, 34.88287], "pop": 1824, "state": "AR", "_id": "72135"} -{"city": "ROMANCE", "loc": [-92.06985, 35.215462], "pop": 576, "state": "AR", "_id": "72136"} -{"city": "ROSE BUD", "loc": [-92.061988, 35.321447], "pop": 891, "state": "AR", "_id": "72137"} -{"city": "SAINT CHARLES", "loc": [-91.16808, 34.335296], "pop": 607, "state": "AR", "_id": "72140"} -{"city": "SCOTLAND", "loc": [-92.586652, 35.507956], "pop": 1276, "state": "AR", "_id": "72141"} -{"city": "SCOTT", "loc": [-92.11566, 34.694215], "pop": 1027, "state": "AR", "_id": "72142"} -{"city": "GEORGETOWN", "loc": [-91.762931, 35.253512], "pop": 22586, "state": "AR", "_id": "72143"} -{"city": "SHERIDAN", "loc": [-92.365713, 34.316527], "pop": 9714, "state": "AR", "_id": "72150"} -{"city": "SHERRILL", "loc": [-91.993285, 34.358148], "pop": 615, "state": "AR", "_id": "72152"} -{"city": "SHIRLEY", "loc": [-92.29751, 35.573259], "pop": 4841, "state": "AR", "_id": "72153"} -{"city": "SOLGOHACHIA", "loc": [-92.675387, 35.270056], "pop": 133, "state": "AR", "_id": "72156"} -{"city": "SPRINGFIELD", "loc": [-92.54567, 35.274879], "pop": 752, "state": "AR", "_id": "72157"} -{"city": "STUTTGART", "loc": [-91.548742, 34.485358], "pop": 11801, "state": "AR", "_id": "72160"} -{"city": "THIDA", "loc": [-91.461823, 35.553556], "pop": 616, "state": "AR", "_id": "72165"} -{"city": "TICHNOR", "loc": [-91.243684, 34.089311], "pop": 331, "state": "AR", "_id": "72166"} -{"city": "TRASKWOOD", "loc": [-92.654734, 34.450791], "pop": 606, "state": "AR", "_id": "72167"} -{"city": "TUCKER", "loc": [-91.916265, 34.441442], "pop": 2170, "state": "AR", "_id": "72168"} -{"city": "ULM", "loc": [-91.51343, 34.579938], "pop": 507, "state": "AR", "_id": "72170"} -{"city": "VILONIA", "loc": [-92.183235, 35.071895], "pop": 2492, "state": "AR", "_id": "72173"} -{"city": "WABBASEKA", "loc": [-91.754948, 34.393552], "pop": 1021, "state": "AR", "_id": "72175"} -{"city": "WARD", "loc": [-91.900383, 34.95316], "pop": 3503, "state": "AR", "_id": "72176"} -{"city": "WILBURN", "loc": [-91.907248, 35.454547], "pop": 1924, "state": "AR", "_id": "72179"} -{"city": "WOOSTER", "loc": [-92.450998, 35.197308], "pop": 1264, "state": "AR", "_id": "72181"} -{"city": "WRIGHT", "loc": [-92.06308, 34.437656], "pop": 302, "state": "AR", "_id": "72182"} -{"city": "LITTLE ROCK", "loc": [-92.281939, 34.748342], "pop": 539, "state": "AR", "_id": "72201"} -{"city": "LITTLE ROCK", "loc": [-92.274067, 34.736322], "pop": 11686, "state": "AR", "_id": "72202"} -{"city": "LITTLE ROCK", "loc": [-92.344041, 34.726904], "pop": 33104, "state": "AR", "_id": "72204"} -{"city": "LITTLE ROCK", "loc": [-92.345512, 34.750971], "pop": 25156, "state": "AR", "_id": "72205"} -{"city": "LITTLE ROCK", "loc": [-92.277606, 34.683599], "pop": 27367, "state": "AR", "_id": "72206"} -{"city": "LITTLE ROCK", "loc": [-92.356481, 34.772121], "pop": 25217, "state": "AR", "_id": "72207"} -{"city": "FERNDALE", "loc": [-92.585581, 34.781575], "pop": 458, "state": "AR", "_id": "72208"} -{"city": "LITTLE ROCK", "loc": [-92.352919, 34.672509], "pop": 35211, "state": "AR", "_id": "72209"} -{"city": "LITTLE ROCK", "loc": [-92.465981, 34.707625], "pop": 4426, "state": "AR", "_id": "72210"} -{"city": "LITTLE ROCK", "loc": [-92.431485, 34.758819], "pop": 14006, "state": "AR", "_id": "72211"} -{"city": "LITTLE ROCK", "loc": [-92.422232, 34.787076], "pop": 16183, "state": "AR", "_id": "72212"} -{"city": "WEST MEMPHIS", "loc": [-90.17792, 35.148442], "pop": 28720, "state": "AR", "_id": "72301"} -{"city": "ARMOREL", "loc": [-89.758118, 35.942843], "pop": 444, "state": "AR", "_id": "72310"} -{"city": "AUBREY", "loc": [-90.911331, 34.702727], "pop": 901, "state": "AR", "_id": "72311"} -{"city": "BASSETT", "loc": [-90.192155, 35.513808], "pop": 1147, "state": "AR", "_id": "72313"} -{"city": "BIRDEYE", "loc": [-90.67074, 35.409571], "pop": 505, "state": "AR", "_id": "72314"} -{"city": "BLYTHEVILLE A F", "loc": [-89.922497, 35.934482], "pop": 30944, "state": "AR", "_id": "72315"} -{"city": "BRICKEYS", "loc": [-90.537738, 34.854814], "pop": 458, "state": "AR", "_id": "72320"} -{"city": "BURDETTE", "loc": [-89.942312, 35.814769], "pop": 329, "state": "AR", "_id": "72321"} -{"city": "CHERRY VALLEY", "loc": [-90.761329, 35.399122], "pop": 1273, "state": "AR", "_id": "72324"} -{"city": "COLT", "loc": [-90.787204, 35.096062], "pop": 3755, "state": "AR", "_id": "72326"} -{"city": "CRAWFORDSVILLE", "loc": [-90.335107, 35.211873], "pop": 1834, "state": "AR", "_id": "72327"} -{"city": "CRUMROD", "loc": [-90.949535, 34.216167], "pop": 514, "state": "AR", "_id": "72328"} -{"city": "DRIVER", "loc": [-89.960526, 35.559714], "pop": 25, "state": "AR", "_id": "72329"} -{"city": "DYESS", "loc": [-90.215716, 35.595394], "pop": 912, "state": "AR", "_id": "72330"} -{"city": "EARLE", "loc": [-90.450261, 35.27989], "pop": 5009, "state": "AR", "_id": "72331"} -{"city": "EDMONDSON", "loc": [-90.328672, 35.022309], "pop": 2435, "state": "AR", "_id": "72332"} -{"city": "ELAINE", "loc": [-90.873485, 34.308668], "pop": 1366, "state": "AR", "_id": "72333"} -{"city": "FORREST CITY", "loc": [-90.788572, 35.009129], "pop": 17751, "state": "AR", "_id": "72335"} -{"city": "FRENCHMANS BAYOU", "loc": [-90.055933, 35.478419], "pop": 34, "state": "AR", "_id": "72338"} -{"city": "GILMORE", "loc": [-90.264664, 35.386408], "pop": 1852, "state": "AR", "_id": "72339"} -{"city": "GOODWIN", "loc": [-90.983518, 34.959149], "pop": 597, "state": "AR", "_id": "72340"} -{"city": "HAYNES", "loc": [-90.766621, 34.883752], "pop": 552, "state": "AR", "_id": "72341"} -{"city": "HELENA", "loc": [-90.629784, 34.532491], "pop": 9515, "state": "AR", "_id": "72342"} -{"city": "HETH", "loc": [-90.458554, 35.097335], "pop": 859, "state": "AR", "_id": "72346"} -{"city": "HICKORY RIDGE", "loc": [-90.982283, 35.399457], "pop": 754, "state": "AR", "_id": "72347"} -{"city": "HUGHES", "loc": [-90.474142, 34.945416], "pop": 2477, "state": "AR", "_id": "72348"} -{"city": "JOINER", "loc": [-90.14779, 35.505239], "pop": 986, "state": "AR", "_id": "72350"} -{"city": "KEISER", "loc": [-90.096171, 35.678626], "pop": 980, "state": "AR", "_id": "72351"} -{"city": "LEPANTO", "loc": [-90.335883, 35.606909], "pop": 2023, "state": "AR", "_id": "72354"} -{"city": "LEXA", "loc": [-90.78532, 34.672694], "pop": 903, "state": "AR", "_id": "72355"} -{"city": "LUXORA", "loc": [-89.92204, 35.760786], "pop": 1593, "state": "AR", "_id": "72358"} -{"city": "MARIANNA", "loc": [-90.77852, 34.775929], "pop": 8668, "state": "AR", "_id": "72360"} -{"city": "MARION", "loc": [-90.198908, 35.2077], "pop": 7891, "state": "AR", "_id": "72364"} -{"city": "MARKED TREE", "loc": [-90.419387, 35.534933], "pop": 5144, "state": "AR", "_id": "72365"} -{"city": "MARVELL", "loc": [-90.941322, 34.548541], "pop": 2733, "state": "AR", "_id": "72366"} -{"city": "MELLWOOD", "loc": [-91.008589, 34.220032], "pop": 61, "state": "AR", "_id": "72367"} -{"city": "MORO", "loc": [-91.006026, 34.802953], "pop": 1571, "state": "AR", "_id": "72368"} -{"city": "ONEIDA", "loc": [-90.826045, 34.405267], "pop": 918, "state": "AR", "_id": "72369"} -{"city": "OSCEOLA", "loc": [-89.979764, 35.701864], "pop": 10119, "state": "AR", "_id": "72370"} -{"city": "PALESTINE", "loc": [-90.904929, 34.966346], "pop": 1371, "state": "AR", "_id": "72372"} -{"city": "PARKIN", "loc": [-90.556417, 35.258557], "pop": 2437, "state": "AR", "_id": "72373"} -{"city": "POPLAR GROVE", "loc": [-90.881274, 34.539397], "pop": 1466, "state": "AR", "_id": "72374"} -{"city": "PROCTOR", "loc": [-90.255026, 35.083396], "pop": 953, "state": "AR", "_id": "72376"} -{"city": "SNOW LAKE", "loc": [-91.007083, 34.066466], "pop": 225, "state": "AR", "_id": "72379"} -{"city": "TOMATO", "loc": [-89.787092, 35.806666], "pop": 0, "state": "AR", "_id": "72381"} -{"city": "TURRELL", "loc": [-90.262979, 35.296936], "pop": 1308, "state": "AR", "_id": "72384"} -{"city": "TYRONZA", "loc": [-90.351944, 35.486477], "pop": 1230, "state": "AR", "_id": "72386"} -{"city": "WEST HELENA", "loc": [-90.654531, 34.549635], "pop": 12265, "state": "AR", "_id": "72390"} -{"city": "WHEATLEY", "loc": [-91.108645, 34.920703], "pop": 542, "state": "AR", "_id": "72392"} -{"city": "WIDENER", "loc": [-90.629313, 35.059167], "pop": 1145, "state": "AR", "_id": "72394"} -{"city": "WILSON", "loc": [-90.042749, 35.566004], "pop": 1185, "state": "AR", "_id": "72395"} -{"city": "WYNNE", "loc": [-90.79303, 35.233036], "pop": 13908, "state": "AR", "_id": "72396"} -{"city": "FAIR OAKS", "loc": [-91.014304, 35.236817], "pop": 285, "state": "AR", "_id": "72397"} -{"city": "JONESBORO", "loc": [-90.696526, 35.833016], "pop": 53532, "state": "AR", "_id": "72401"} -{"city": "ALICIA", "loc": [-91.081003, 35.942327], "pop": 725, "state": "AR", "_id": "72410"} -{"city": "BAY", "loc": [-90.550658, 35.74559], "pop": 2527, "state": "AR", "_id": "72411"} -{"city": "BEECH GROVE", "loc": [-90.618084, 36.183172], "pop": 292, "state": "AR", "_id": "72412"} -{"city": "BIGGERS", "loc": [-90.784467, 36.342978], "pop": 1060, "state": "AR", "_id": "72413"} -{"city": "BLACK OAK", "loc": [-90.354227, 35.835851], "pop": 518, "state": "AR", "_id": "72414"} -{"city": "BLACK ROCK", "loc": [-91.117629, 36.114874], "pop": 1190, "state": "AR", "_id": "72415"} -{"city": "BONO", "loc": [-90.784753, 35.908592], "pop": 3030, "state": "AR", "_id": "72416"} -{"city": "BROOKLAND", "loc": [-90.576228, 35.91647], "pop": 1896, "state": "AR", "_id": "72417"} -{"city": "CARAWAY", "loc": [-90.335797, 35.758951], "pop": 2214, "state": "AR", "_id": "72419"} -{"city": "CASH", "loc": [-90.941007, 35.830145], "pop": 628, "state": "AR", "_id": "72421"} -{"city": "CORNING", "loc": [-90.58702, 36.415532], "pop": 5469, "state": "AR", "_id": "72422"} -{"city": "DATTO", "loc": [-90.723117, 36.389685], "pop": 308, "state": "AR", "_id": "72424"} -{"city": "DELAPLAINE", "loc": [-90.734482, 36.199485], "pop": 719, "state": "AR", "_id": "72425"} -{"city": "DELL", "loc": [-90.035449, 35.850118], "pop": 832, "state": "AR", "_id": "72426"} -{"city": "ETOWAH", "loc": [-90.218172, 35.714707], "pop": 1330, "state": "AR", "_id": "72428"} -{"city": "FISHER", "loc": [-90.955001, 35.513956], "pop": 907, "state": "AR", "_id": "72429"} -{"city": "GREENWAY", "loc": [-90.225257, 36.334766], "pop": 529, "state": "AR", "_id": "72430"} -{"city": "HARRISBURG", "loc": [-90.703752, 35.572222], "pop": 5961, "state": "AR", "_id": "72432"} -{"city": "HOXIE", "loc": [-90.971504, 36.032573], "pop": 3436, "state": "AR", "_id": "72433"} -{"city": "IMBODEN", "loc": [-91.185442, 36.19763], "pop": 889, "state": "AR", "_id": "72434"} -{"city": "KNOBEL", "loc": [-90.599048, 36.317647], "pop": 518, "state": "AR", "_id": "72435"} -{"city": "LAFE", "loc": [-90.506973, 36.21547], "pop": 1007, "state": "AR", "_id": "72436"} -{"city": "LAKE CITY", "loc": [-90.442327, 35.81715], "pop": 2056, "state": "AR", "_id": "72437"} -{"city": "LEACHVILLE", "loc": [-90.195488, 35.933216], "pop": 2810, "state": "AR", "_id": "72438"} -{"city": "LYNN", "loc": [-91.254588, 36.016639], "pop": 1032, "state": "AR", "_id": "72440"} -{"city": "MC DOUGAL", "loc": [-90.394093, 36.379909], "pop": 1552, "state": "AR", "_id": "72441"} -{"city": "ROSELAND", "loc": [-90.180252, 35.874885], "pop": 3855, "state": "AR", "_id": "72442"} -{"city": "MARMADUKE", "loc": [-90.38368, 36.195117], "pop": 2082, "state": "AR", "_id": "72443"} -{"city": "MAYNARD", "loc": [-90.874921, 36.438563], "pop": 1616, "state": "AR", "_id": "72444"} -{"city": "MINTURN", "loc": [-91.033704, 35.976096], "pop": 364, "state": "AR", "_id": "72445"} -{"city": "MONETTE", "loc": [-90.343658, 35.900168], "pop": 2008, "state": "AR", "_id": "72447"} -{"city": "O KEAN", "loc": [-90.824069, 36.179672], "pop": 420, "state": "AR", "_id": "72449"} -{"city": "PARAGOULD", "loc": [-90.525093, 36.060033], "pop": 27704, "state": "AR", "_id": "72450"} -{"city": "PEACH ORCHARD", "loc": [-90.670217, 36.283045], "pop": 332, "state": "AR", "_id": "72453"} -{"city": "PIGGOTT", "loc": [-90.19261, 36.386993], "pop": 4818, "state": "AR", "_id": "72454"} -{"city": "POCAHONTAS", "loc": [-90.996782, 36.282876], "pop": 12280, "state": "AR", "_id": "72455"} -{"city": "POLLARD", "loc": [-90.275123, 36.431725], "pop": 553, "state": "AR", "_id": "72456"} -{"city": "PORTIA", "loc": [-91.068004, 36.080699], "pop": 646, "state": "AR", "_id": "72457"} -{"city": "POWHATAN", "loc": [-91.148214, 36.080044], "pop": 476, "state": "AR", "_id": "72458"} -{"city": "RAVENDEN", "loc": [-91.25934, 36.202863], "pop": 1137, "state": "AR", "_id": "72459"} -{"city": "RAVENDEN SPRINGS", "loc": [-91.209433, 36.310384], "pop": 944, "state": "AR", "_id": "72460"} -{"city": "RECTOR", "loc": [-90.270201, 36.267177], "pop": 3515, "state": "AR", "_id": "72461"} -{"city": "SAINT FRANCIS", "loc": [-90.144991, 36.454771], "pop": 227, "state": "AR", "_id": "72464"} -{"city": "SEDGWICK", "loc": [-90.880099, 35.985965], "pop": 503, "state": "AR", "_id": "72465"} -{"city": "SMITHVILLE", "loc": [-91.274456, 36.090815], "pop": 427, "state": "AR", "_id": "72466"} -{"city": "STATE UNIVERSITY", "loc": [-90.454245, 35.914894], "pop": 544, "state": "AR", "_id": "72467"} -{"city": "CALAMINE", "loc": [-91.375398, 35.972622], "pop": 1078, "state": "AR", "_id": "72469"} -{"city": "SUCCESS", "loc": [-90.728128, 36.453615], "pop": 286, "state": "AR", "_id": "72470"} -{"city": "SWIFTON", "loc": [-91.126358, 35.827366], "pop": 1172, "state": "AR", "_id": "72471"} -{"city": "PAYNEWAY", "loc": [-90.518738, 35.668914], "pop": 8179, "state": "AR", "_id": "72472"} -{"city": "TUCKERMAN", "loc": [-91.200347, 35.730678], "pop": 2564, "state": "AR", "_id": "72473"} -{"city": "COLLEGE CITY", "loc": [-90.952809, 36.077814], "pop": 5794, "state": "AR", "_id": "72476"} -{"city": "WARM SPRINGS", "loc": [-91.038707, 36.467531], "pop": 238, "state": "AR", "_id": "72478"} -{"city": "WEINER", "loc": [-90.928928, 35.629052], "pop": 1223, "state": "AR", "_id": "72479"} -{"city": "WILLIFORD", "loc": [-91.379226, 36.245335], "pop": 349, "state": "AR", "_id": "72482"} -{"city": "BATESVILLE", "loc": [-91.63519, 35.782614], "pop": 19976, "state": "AR", "_id": "72501"} -{"city": "HORSESHOE BEND", "loc": [-91.755334, 36.202502], "pop": 3088, "state": "AR", "_id": "72512"} -{"city": "AGNOS", "loc": [-91.610746, 36.219732], "pop": 1433, "state": "AR", "_id": "72513"} -{"city": "BEXAR", "loc": [-91.985998, 36.306103], "pop": 325, "state": "AR", "_id": "72515"} -{"city": "BOSWELL", "loc": [-92.044307, 36.01084], "pop": 321, "state": "AR", "_id": "72516"} -{"city": "BROCKWELL", "loc": [-91.951308, 36.135755], "pop": 548, "state": "AR", "_id": "72517"} -{"city": "JORDAN", "loc": [-92.145494, 36.144771], "pop": 2019, "state": "AR", "_id": "72519"} -{"city": "CAMP", "loc": [-91.726166, 36.402531], "pop": 401, "state": "AR", "_id": "72520"} -{"city": "CAVE CITY", "loc": [-91.544432, 35.951684], "pop": 2111, "state": "AR", "_id": "72521"} -{"city": "CHARLOTTE", "loc": [-91.396217, 35.833393], "pop": 797, "state": "AR", "_id": "72522"} -{"city": "CONCORD", "loc": [-91.833339, 35.641355], "pop": 1168, "state": "AR", "_id": "72523"} -{"city": "CORD", "loc": [-91.337455, 35.81844], "pop": 205, "state": "AR", "_id": "72524"} -{"city": "CUSHMAN", "loc": [-91.776455, 35.869663], "pop": 336, "state": "AR", "_id": "72526"} -{"city": "DESHA", "loc": [-91.678287, 35.731524], "pop": 876, "state": "AR", "_id": "72527"} -{"city": "DOLPH", "loc": [-92.11766, 36.222878], "pop": 348, "state": "AR", "_id": "72528"} -{"city": "CHEROKEE VILLAGE", "loc": [-91.528075, 36.30114], "pop": 4523, "state": "AR", "_id": "72529"} -{"city": "DRASCO", "loc": [-91.939818, 35.661606], "pop": 886, "state": "AR", "_id": "72530"} -{"city": "ELIZABETH", "loc": [-92.093366, 36.323776], "pop": 390, "state": "AR", "_id": "72531"} -{"city": "EVENING SHADE", "loc": [-91.598014, 36.085532], "pop": 1356, "state": "AR", "_id": "72532"} -{"city": "FIFTY SIX", "loc": [-92.218169, 35.991958], "pop": 346, "state": "AR", "_id": "72533"} -{"city": "FLORAL", "loc": [-91.734101, 35.602337], "pop": 940, "state": "AR", "_id": "72534"} -{"city": "FRANKLIN", "loc": [-91.809592, 36.129093], "pop": 364, "state": "AR", "_id": "72536"} -{"city": "GAMALIEL", "loc": [-92.228447, 36.461794], "pop": 301, "state": "AR", "_id": "72537"} -{"city": "GEPP", "loc": [-92.099507, 36.436443], "pop": 351, "state": "AR", "_id": "72538"} -{"city": "GLENCOE", "loc": [-91.769483, 36.288962], "pop": 514, "state": "AR", "_id": "72539"} -{"city": "GUION", "loc": [-91.934287, 35.960135], "pop": 226, "state": "AR", "_id": "72540"} -{"city": "HARDY", "loc": [-91.411027, 36.322746], "pop": 2473, "state": "AR", "_id": "72542"} -{"city": "HEBER SPRINGS", "loc": [-92.039211, 35.510278], "pop": 8709, "state": "AR", "_id": "72543"} -{"city": "HENDERSON", "loc": [-92.269253, 36.356829], "pop": 3441, "state": "AR", "_id": "72544"} -{"city": "IDA", "loc": [-91.930081, 35.594326], "pop": 539, "state": "AR", "_id": "72546"} -{"city": "LOCUST GROVE", "loc": [-91.741443, 35.717705], "pop": 750, "state": "AR", "_id": "72550"} -{"city": "MAGNESS", "loc": [-91.485041, 35.710062], "pop": 413, "state": "AR", "_id": "72553"} -{"city": "MAMMOTH SPRING", "loc": [-91.575712, 36.407648], "pop": 4075, "state": "AR", "_id": "72554"} -{"city": "MARCELLA", "loc": [-91.941891, 35.749654], "pop": 535, "state": "AR", "_id": "72555"} -{"city": "ZION", "loc": [-91.905196, 36.059958], "pop": 1880, "state": "AR", "_id": "72556"} -{"city": "MOKO", "loc": [-91.858663, 36.468902], "pop": 321, "state": "AR", "_id": "72557"} -{"city": "HANOVER", "loc": [-92.114762, 35.865769], "pop": 5553, "state": "AR", "_id": "72560"} -{"city": "MOUNT PLEASANT", "loc": [-91.785013, 35.975909], "pop": 1264, "state": "AR", "_id": "72561"} -{"city": "NEWARK", "loc": [-91.439395, 35.71183], "pop": 1816, "state": "AR", "_id": "72562"} -{"city": "OIL TROUGH", "loc": [-91.470305, 35.613147], "pop": 674, "state": "AR", "_id": "72564"} -{"city": "OXFORD", "loc": [-91.925849, 36.211408], "pop": 738, "state": "AR", "_id": "72565"} -{"city": "PINEVILLE", "loc": [-92.107299, 36.167704], "pop": 356, "state": "AR", "_id": "72566"} -{"city": "PLEASANT GROVE", "loc": [-91.931905, 35.842349], "pop": 772, "state": "AR", "_id": "72567"} -{"city": "PLEASANT PLAINS", "loc": [-91.632043, 35.589301], "pop": 1790, "state": "AR", "_id": "72568"} -{"city": "POUGHKEEPSIE", "loc": [-91.451575, 36.071526], "pop": 601, "state": "AR", "_id": "72569"} -{"city": "ROSIE", "loc": [-91.534003, 35.663816], "pop": 438, "state": "AR", "_id": "72571"} -{"city": "SAFFELL", "loc": [-91.297753, 35.917957], "pop": 379, "state": "AR", "_id": "72572"} -{"city": "SAGE", "loc": [-91.824569, 36.042476], "pop": 256, "state": "AR", "_id": "72573"} -{"city": "SALADO", "loc": [-91.597924, 35.703493], "pop": 818, "state": "AR", "_id": "72575"} -{"city": "BYRON", "loc": [-91.836321, 36.365401], "pop": 2561, "state": "AR", "_id": "72576"} -{"city": "SIDNEY", "loc": [-91.634593, 35.991554], "pop": 743, "state": "AR", "_id": "72577"} -{"city": "STURKIE", "loc": [-91.989917, 36.463869], "pop": 168, "state": "AR", "_id": "72578"} -{"city": "SULPHUR ROCK", "loc": [-91.507333, 35.754466], "pop": 756, "state": "AR", "_id": "72579"} -{"city": "TUMBLING SHOALS", "loc": [-91.970359, 35.546985], "pop": 768, "state": "AR", "_id": "72581"} -{"city": "VIOLA", "loc": [-91.993198, 36.392429], "pop": 823, "state": "AR", "_id": "72583"} -{"city": "VIOLET HILL", "loc": [-91.847064, 36.162966], "pop": 220, "state": "AR", "_id": "72584"} -{"city": "WIDEMAN", "loc": [-92.001796, 36.198739], "pop": 89, "state": "AR", "_id": "72585"} -{"city": "WISEMAN", "loc": [-91.849499, 36.228022], "pop": 26, "state": "AR", "_id": "72587"} -{"city": "HARRISON", "loc": [-93.106162, 36.241707], "pop": 23009, "state": "AR", "_id": "72601"} -{"city": "ALCO", "loc": [-92.380753, 35.894511], "pop": 172, "state": "AR", "_id": "72610"} -{"city": "ALPENA", "loc": [-93.2792, 36.299768], "pop": 780, "state": "AR", "_id": "72611"} -{"city": "BASS", "loc": [-92.999832, 35.892225], "pop": 261, "state": "AR", "_id": "72612"} -{"city": "BERRYVILLE", "loc": [-93.558725, 36.351908], "pop": 6821, "state": "AR", "_id": "72616"} -{"city": "BIG FLAT", "loc": [-92.391681, 36.006824], "pop": 244, "state": "AR", "_id": "72617"} -{"city": "BRUNO", "loc": [-92.763651, 36.124617], "pop": 236, "state": "AR", "_id": "72618"} -{"city": "BULL SHOALS", "loc": [-92.593765, 36.370958], "pop": 1967, "state": "AR", "_id": "72619"} -{"city": "CLARKRIDGE", "loc": [-92.35159, 36.443311], "pop": 1031, "state": "AR", "_id": "72623"} -{"city": "COMPTON", "loc": [-93.309903, 36.097941], "pop": 233, "state": "AR", "_id": "72624"} -{"city": "COTTER", "loc": [-92.532787, 36.280378], "pop": 1044, "state": "AR", "_id": "72626"} -{"city": "DEER", "loc": [-93.317388, 35.852982], "pop": 1378, "state": "AR", "_id": "72628"} -{"city": "DENNARD", "loc": [-92.557456, 35.725236], "pop": 1370, "state": "AR", "_id": "72629"} -{"city": "EUREKA SPRINGS", "loc": [-93.737915, 36.417465], "pop": 5444, "state": "AR", "_id": "72632"} -{"city": "EVERTON", "loc": [-92.914989, 36.153366], "pop": 436, "state": "AR", "_id": "72633"} -{"city": "FLIPPIN", "loc": [-92.577995, 36.268219], "pop": 2784, "state": "AR", "_id": "72634"} -{"city": "GASSVILLE", "loc": [-92.473637, 36.317525], "pop": 3568, "state": "AR", "_id": "72635"} -{"city": "GREEN FOREST", "loc": [-93.405875, 36.322408], "pop": 5430, "state": "AR", "_id": "72638"} -{"city": "HARRIET", "loc": [-92.50055, 35.974187], "pop": 950, "state": "AR", "_id": "72639"} -{"city": "HASTY", "loc": [-93.045966, 36.015229], "pop": 219, "state": "AR", "_id": "72640"} -{"city": "JASPER", "loc": [-93.204771, 36.003798], "pop": 1632, "state": "AR", "_id": "72641"} -{"city": "LAKEVIEW", "loc": [-92.504416, 36.397308], "pop": 2815, "state": "AR", "_id": "72642"} -{"city": "LEAD HILL", "loc": [-92.930194, 36.430886], "pop": 1818, "state": "AR", "_id": "72644"} -{"city": "LESLIE", "loc": [-92.566293, 35.82716], "pop": 1768, "state": "AR", "_id": "72645"} -{"city": "DOGPATCH", "loc": [-93.144822, 36.095121], "pop": 608, "state": "AR", "_id": "72648"} -{"city": "MARSHALL", "loc": [-92.640203, 35.926697], "pop": 2882, "state": "AR", "_id": "72650"} -{"city": "MIDWAY", "loc": [-92.431398, 36.290454], "pop": 408, "state": "AR", "_id": "72651"} -{"city": "MOUNTAIN HOME", "loc": [-92.375337, 36.331153], "pop": 16131, "state": "AR", "_id": "72653"} -{"city": "MOUNT JUDEA", "loc": [-93.082391, 35.834674], "pop": 665, "state": "AR", "_id": "72655"} -{"city": "TIMBO", "loc": [-92.252536, 35.896338], "pop": 596, "state": "AR", "_id": "72657"} -{"city": "NORFORK", "loc": [-92.273011, 36.206571], "pop": 1824, "state": "AR", "_id": "72658"} -{"city": "OAK GROVE", "loc": [-93.432129, 36.461347], "pop": 956, "state": "AR", "_id": "72660"} -{"city": "OAKLAND", "loc": [-92.583187, 36.444227], "pop": 544, "state": "AR", "_id": "72661"} -{"city": "OMAHA", "loc": [-93.188753, 36.46123], "pop": 1251, "state": "AR", "_id": "72662"} -{"city": "ONIA", "loc": [-92.345859, 35.940339], "pop": 444, "state": "AR", "_id": "72663"} -{"city": "PARTHENON", "loc": [-93.267776, 35.940836], "pop": 518, "state": "AR", "_id": "72666"} -{"city": "PEEL", "loc": [-92.776143, 36.444374], "pop": 731, "state": "AR", "_id": "72668"} -{"city": "PINDALL", "loc": [-92.886409, 36.067479], "pop": 499, "state": "AR", "_id": "72669"} -{"city": "PONCA", "loc": [-93.401985, 36.066225], "pop": 435, "state": "AR", "_id": "72670"} -{"city": "SAINT JOE", "loc": [-92.792828, 35.987707], "pop": 1196, "state": "AR", "_id": "72675"} -{"city": "TILLY", "loc": [-92.844209, 35.6976], "pop": 118, "state": "AR", "_id": "72679"} -{"city": "NEWNATA", "loc": [-92.337929, 35.855318], "pop": 397, "state": "AR", "_id": "72680"} -{"city": "VALLEY SPRINGS", "loc": [-92.979937, 36.146823], "pop": 1050, "state": "AR", "_id": "72682"} -{"city": "VENDOR", "loc": [-93.100815, 35.948062], "pop": 784, "state": "AR", "_id": "72683"} -{"city": "WESTERN GROVE", "loc": [-92.971649, 36.082994], "pop": 933, "state": "AR", "_id": "72685"} -{"city": "WITTS SPRINGS", "loc": [-92.815518, 35.785514], "pop": 546, "state": "AR", "_id": "72686"} -{"city": "YELLVILLE", "loc": [-92.724472, 36.225322], "pop": 5695, "state": "AR", "_id": "72687"} -{"city": "FAYETTEVILLE", "loc": [-94.153395, 36.052045], "pop": 28372, "state": "AR", "_id": "72701"} -{"city": "FAYETTEVILLE", "loc": [-94.17163, 36.099183], "pop": 24649, "state": "AR", "_id": "72703"} -{"city": "BENTONVILLE", "loc": [-94.22242, 36.357703], "pop": 14439, "state": "AR", "_id": "72712"} -{"city": "BELLA VISTA", "loc": [-94.251969, 36.465086], "pop": 8645, "state": "AR", "_id": "72714"} -{"city": "WAL-MART INC", "loc": [-94.181483, 36.326554], "pop": 459, "state": "AR", "_id": "72716"} -{"city": "CANEHILL", "loc": [-94.3862, 35.910992], "pop": 781, "state": "AR", "_id": "72717"} -{"city": "CAVE SPRINGS", "loc": [-94.207836, 36.266771], "pop": 1629, "state": "AR", "_id": "72718"} -{"city": "CENTERTON", "loc": [-94.30891, 36.366993], "pop": 1797, "state": "AR", "_id": "72719"} -{"city": "COMBS", "loc": [-93.825218, 35.848297], "pop": 491, "state": "AR", "_id": "72721"} -{"city": "DECATUR", "loc": [-94.453411, 36.334749], "pop": 1633, "state": "AR", "_id": "72722"} -{"city": "ELKINS", "loc": [-94.007323, 36.017719], "pop": 1767, "state": "AR", "_id": "72727"} -{"city": "EVANSVILLE", "loc": [-94.478936, 35.819345], "pop": 380, "state": "AR", "_id": "72729"} -{"city": "FARMINGTON", "loc": [-94.253871, 36.043635], "pop": 3495, "state": "AR", "_id": "72730"} -{"city": "GARFIELD", "loc": [-93.951197, 36.428817], "pop": 1389, "state": "AR", "_id": "72732"} -{"city": "GATEWAY", "loc": [-93.935016, 36.485751], "pop": 581, "state": "AR", "_id": "72733"} -{"city": "GENTRY", "loc": [-94.475131, 36.26516], "pop": 5145, "state": "AR", "_id": "72734"} -{"city": "GOSHEN", "loc": [-93.987262, 36.107523], "pop": 1639, "state": "AR", "_id": "72735"} -{"city": "GRAVETTE", "loc": [-94.477862, 36.415513], "pop": 3508, "state": "AR", "_id": "72736"} -{"city": "HINDSVILLE", "loc": [-93.863339, 36.142206], "pop": 1024, "state": "AR", "_id": "72738"} -{"city": "HIWASSE", "loc": [-94.338993, 36.441589], "pop": 1188, "state": "AR", "_id": "72739"} -{"city": "HUNTSVILLE", "loc": [-93.727893, 36.104348], "pop": 7021, "state": "AR", "_id": "72740"} -{"city": "KINGSTON", "loc": [-93.504357, 36.04845], "pop": 566, "state": "AR", "_id": "72742"} -{"city": "LINCOLN", "loc": [-94.42724, 35.956931], "pop": 3129, "state": "AR", "_id": "72744"} -{"city": "LOWELL", "loc": [-94.082725, 36.243318], "pop": 5077, "state": "AR", "_id": "72745"} -{"city": "MAYSVILLE", "loc": [-94.581278, 36.401062], "pop": 214, "state": "AR", "_id": "72747"} -{"city": "MORROW", "loc": [-94.425793, 35.85373], "pop": 615, "state": "AR", "_id": "72749"} -{"city": "PEA RIDGE", "loc": [-94.118026, 36.453884], "pop": 3584, "state": "AR", "_id": "72751"} -{"city": "PETTIGREW", "loc": [-93.618079, 35.834562], "pop": 422, "state": "AR", "_id": "72752"} -{"city": "PRAIRIE GROVE", "loc": [-94.316861, 35.991809], "pop": 4105, "state": "AR", "_id": "72753"} -{"city": "ROGERS", "loc": [-94.114784, 36.336316], "pop": 34081, "state": "AR", "_id": "72756"} -{"city": "SAINT PAUL", "loc": [-93.734743, 35.849576], "pop": 608, "state": "AR", "_id": "72760"} -{"city": "SILOAM SPRINGS", "loc": [-94.528036, 36.179969], "pop": 11677, "state": "AR", "_id": "72761"} -{"city": "SPRINGDALE", "loc": [-94.176216, 36.183521], "pop": 20104, "state": "AR", "_id": "72762"} -{"city": "BETHEL HEIGHTS", "loc": [-94.104682, 36.177918], "pop": 17792, "state": "AR", "_id": "72764"} -{"city": "SULPHUR SPRINGS", "loc": [-94.452069, 36.479434], "pop": 982, "state": "AR", "_id": "72768"} -{"city": "SUMMERS", "loc": [-94.500027, 36.013827], "pop": 1013, "state": "AR", "_id": "72769"} -{"city": "WESLEY", "loc": [-93.911478, 35.957264], "pop": 1307, "state": "AR", "_id": "72773"} -{"city": "WEST FORK", "loc": [-94.230375, 35.908153], "pop": 4473, "state": "AR", "_id": "72774"} -{"city": "WITTER", "loc": [-93.621, 35.935583], "pop": 179, "state": "AR", "_id": "72776"} -{"city": "RUSSELLVILLE", "loc": [-93.131476, 35.284208], "pop": 25169, "state": "AR", "_id": "72801"} -{"city": "ALIX", "loc": [-93.726416, 35.430767], "pop": 488, "state": "AR", "_id": "72820"} -{"city": "ALTUS", "loc": [-93.811494, 35.404873], "pop": 2292, "state": "AR", "_id": "72821"} -{"city": "ATKINS", "loc": [-92.950696, 35.244948], "pop": 4835, "state": "AR", "_id": "72823"} -{"city": "BELLEVILLE", "loc": [-93.451817, 35.103762], "pop": 988, "state": "AR", "_id": "72824"} -{"city": "BLUE MOUNTAIN", "loc": [-93.716797, 35.130386], "pop": 200, "state": "AR", "_id": "72826"} -{"city": "BLUFFTON", "loc": [-93.591946, 34.901635], "pop": 208, "state": "AR", "_id": "72827"} -{"city": "BRIGGSVILLE", "loc": [-93.515748, 34.916065], "pop": 164, "state": "AR", "_id": "72828"} -{"city": "CLARKSVILLE", "loc": [-93.491056, 35.490763], "pop": 11304, "state": "AR", "_id": "72830"} -{"city": "COAL HILL", "loc": [-93.67203, 35.437117], "pop": 1179, "state": "AR", "_id": "72832"} -{"city": "DANVILLE", "loc": [-93.392933, 35.049541], "pop": 3292, "state": "AR", "_id": "72833"} -{"city": "DARDANELLE", "loc": [-93.187316, 35.195507], "pop": 8281, "state": "AR", "_id": "72834"} -{"city": "DELAWARE", "loc": [-93.346152, 35.278005], "pop": 645, "state": "AR", "_id": "72835"} -{"city": "DOVER", "loc": [-93.135526, 35.407356], "pop": 5687, "state": "AR", "_id": "72837"} -{"city": "GRAVELLY", "loc": [-93.680249, 34.888123], "pop": 150, "state": "AR", "_id": "72838"} -{"city": "HAGARVILLE", "loc": [-93.344256, 35.523291], "pop": 584, "state": "AR", "_id": "72839"} -{"city": "HARTMAN", "loc": [-93.614196, 35.443583], "pop": 846, "state": "AR", "_id": "72840"} -{"city": "HARVEY", "loc": [-93.752538, 34.867912], "pop": 243, "state": "AR", "_id": "72841"} -{"city": "WAVELAND", "loc": [-93.576719, 35.111845], "pop": 1294, "state": "AR", "_id": "72842"} -{"city": "HECTOR", "loc": [-92.965559, 35.40574], "pop": 5067, "state": "AR", "_id": "72843"} -{"city": "KNOXVILLE", "loc": [-93.361797, 35.374897], "pop": 848, "state": "AR", "_id": "72845"} -{"city": "LAMAR", "loc": [-93.355157, 35.434868], "pop": 2905, "state": "AR", "_id": "72846"} -{"city": "LONDON", "loc": [-93.238907, 35.337017], "pop": 2396, "state": "AR", "_id": "72847"} -{"city": "NEW BLAINE", "loc": [-93.44457, 35.318912], "pop": 891, "state": "AR", "_id": "72851"} -{"city": "OARK", "loc": [-93.558531, 35.709454], "pop": 221, "state": "AR", "_id": "72852"} -{"city": "OLA", "loc": [-93.21356, 35.03091], "pop": 1823, "state": "AR", "_id": "72853"} -{"city": "OZONE", "loc": [-93.43111, 35.657478], "pop": 334, "state": "AR", "_id": "72854"} -{"city": "PARIS", "loc": [-93.726469, 35.294149], "pop": 5718, "state": "AR", "_id": "72855"} -{"city": "PELSOR", "loc": [-93.016088, 35.691158], "pop": 117, "state": "AR", "_id": "72856"} -{"city": "PLAINVIEW", "loc": [-93.309897, 34.966793], "pop": 1260, "state": "AR", "_id": "72857"} -{"city": "POTTSVILLE", "loc": [-93.056386, 35.239785], "pop": 2494, "state": "AR", "_id": "72858"} -{"city": "ROVER", "loc": [-93.401728, 34.947522], "pop": 299, "state": "AR", "_id": "72860"} -{"city": "SCRANTON", "loc": [-93.539362, 35.330816], "pop": 1683, "state": "AR", "_id": "72863"} -{"city": "SUBIACO", "loc": [-93.63869, 35.31346], "pop": 1190, "state": "AR", "_id": "72865"} -{"city": "FORT SMITH", "loc": [-94.411035, 35.365272], "pop": 21722, "state": "AR", "_id": "72901"} -{"city": "FORT SMITH", "loc": [-94.378361, 35.342673], "pop": 32809, "state": "AR", "_id": "72903"} -{"city": "FORT SMITH", "loc": [-94.38723, 35.405122], "pop": 18559, "state": "AR", "_id": "72904"} -{"city": "FORT CHAFFEE", "loc": [-94.340521, 35.297366], "pop": 224, "state": "AR", "_id": "72905"} -{"city": "FORT SMITH", "loc": [-94.370308, 35.250175], "pop": 3494, "state": "AR", "_id": "72916"} -{"city": "ALMA", "loc": [-94.207337, 35.500043], "pop": 7443, "state": "AR", "_id": "72921"} -{"city": "BARLING", "loc": [-94.308226, 35.332963], "pop": 3857, "state": "AR", "_id": "72923"} -{"city": "BATES", "loc": [-94.393155, 34.909295], "pop": 213, "state": "AR", "_id": "72924"} -{"city": "BOLES", "loc": [-94.062864, 34.765363], "pop": 687, "state": "AR", "_id": "72926"} -{"city": "BOONEVILLE", "loc": [-93.927433, 35.136385], "pop": 7502, "state": "AR", "_id": "72927"} -{"city": "BRANCH", "loc": [-93.94537, 35.297055], "pop": 597, "state": "AR", "_id": "72928"} -{"city": "CECIL", "loc": [-93.942932, 35.434176], "pop": 271, "state": "AR", "_id": "72930"} -{"city": "CEDARVILLE", "loc": [-94.344618, 35.590231], "pop": 1605, "state": "AR", "_id": "72932"} -{"city": "CHARLESTON", "loc": [-94.03368, 35.311502], "pop": 3173, "state": "AR", "_id": "72933"} -{"city": "CHESTER", "loc": [-94.202026, 35.689776], "pop": 1004, "state": "AR", "_id": "72934"} -{"city": "GREENWOOD", "loc": [-94.253027, 35.195476], "pop": 8385, "state": "AR", "_id": "72936"} -{"city": "HACKETT", "loc": [-94.398328, 35.194476], "pop": 1587, "state": "AR", "_id": "72937"} -{"city": "HARTFORD", "loc": [-94.381876, 35.022233], "pop": 1073, "state": "AR", "_id": "72938"} -{"city": "HUNTINGTON", "loc": [-94.331283, 35.096267], "pop": 2608, "state": "AR", "_id": "72940"} -{"city": "CENTRAL CITY", "loc": [-94.165637, 35.337513], "pop": 4138, "state": "AR", "_id": "72941"} -{"city": "MAGAZINE", "loc": [-93.800336, 35.15873], "pop": 1336, "state": "AR", "_id": "72943"} -{"city": "MANSFIELD", "loc": [-94.22036, 35.0432], "pop": 2607, "state": "AR", "_id": "72944"} -{"city": "MOUNTAINBURG", "loc": [-94.109094, 35.570027], "pop": 4762, "state": "AR", "_id": "72946"} -{"city": "MULBERRY", "loc": [-93.988505, 35.517246], "pop": 738, "state": "AR", "_id": "72947"} -{"city": "NATURAL DAM", "loc": [-94.412377, 35.674259], "pop": 497, "state": "AR", "_id": "72948"} -{"city": "OZARK", "loc": [-93.837423, 35.524645], "pop": 7338, "state": "AR", "_id": "72949"} -{"city": "PARKS", "loc": [-93.950913, 34.800333], "pop": 471, "state": "AR", "_id": "72950"} -{"city": "RATCLIFF", "loc": [-93.842149, 35.276032], "pop": 1392, "state": "AR", "_id": "72951"} -{"city": "RUDY", "loc": [-94.237376, 35.539805], "pop": 1753, "state": "AR", "_id": "72952"} -{"city": "UNIONTOWN", "loc": [-94.43487, 35.574822], "pop": 710, "state": "AR", "_id": "72955"} -{"city": "VAN BUREN", "loc": [-94.327762, 35.453989], "pop": 24719, "state": "AR", "_id": "72956"} -{"city": "WALDRON", "loc": [-94.077243, 34.902642], "pop": 6884, "state": "AR", "_id": "72958"} -{"city": "WINSLOW", "loc": [-94.118657, 35.831206], "pop": 2566, "state": "AR", "_id": "72959"} -{"city": "TEXARKANA", "loc": [-94.011281, 33.432644], "pop": 30471, "state": "AR", "_id": "75502"} -{"city": "LOS ANGELES", "loc": [-118.247896, 33.973093], "pop": 51841, "state": "CA", "_id": "90001"} -{"city": "LOS ANGELES", "loc": [-118.246213, 33.94969], "pop": 40629, "state": "CA", "_id": "90002"} -{"city": "LOS ANGELES", "loc": [-118.272739, 33.965335], "pop": 53938, "state": "CA", "_id": "90003"} -{"city": "LOS ANGELES", "loc": [-118.302863, 34.076163], "pop": 64062, "state": "CA", "_id": "90004"} -{"city": "LOS ANGELES", "loc": [-118.301197, 34.058508], "pop": 35864, "state": "CA", "_id": "90005"} -{"city": "LOS ANGELES", "loc": [-118.291687, 34.049323], "pop": 63389, "state": "CA", "_id": "90006"} -{"city": "LOS ANGELES", "loc": [-118.287095, 34.029442], "pop": 46985, "state": "CA", "_id": "90007"} -{"city": "LOS ANGELES", "loc": [-118.341123, 34.011643], "pop": 33073, "state": "CA", "_id": "90008"} -{"city": "LOS ANGELES", "loc": [-118.302664, 34.060633], "pop": 5335, "state": "CA", "_id": "90010"} -{"city": "LOS ANGELES", "loc": [-118.258189, 34.007856], "pop": 96074, "state": "CA", "_id": "90011"} -{"city": "LOS ANGELES", "loc": [-118.238479, 34.061396], "pop": 28518, "state": "CA", "_id": "90012"} -{"city": "LOS ANGELES", "loc": [-118.243366, 34.044841], "pop": 5653, "state": "CA", "_id": "90013"} -{"city": "LOS ANGELES", "loc": [-118.250937, 34.044272], "pop": 2715, "state": "CA", "_id": "90014"} -{"city": "LOS ANGELES", "loc": [-118.271613, 34.043439], "pop": 18880, "state": "CA", "_id": "90015"} -{"city": "LOS ANGELES", "loc": [-118.352787, 34.029826], "pop": 43669, "state": "CA", "_id": "90016"} -{"city": "LOS ANGELES", "loc": [-118.266582, 34.055864], "pop": 21790, "state": "CA", "_id": "90017"} -{"city": "LOS ANGELES", "loc": [-118.315173, 34.028972], "pop": 48267, "state": "CA", "_id": "90018"} -{"city": "LOS ANGELES", "loc": [-118.33426, 34.048158], "pop": 64996, "state": "CA", "_id": "90019"} -{"city": "LOS ANGELES", "loc": [-118.302211, 34.066535], "pop": 34926, "state": "CA", "_id": "90020"} -{"city": "LOS ANGELES", "loc": [-118.244698, 34.033303], "pop": 2869, "state": "CA", "_id": "90021"} -{"city": "EAST LOS ANGELES", "loc": [-118.155319, 34.023638], "pop": 65065, "state": "CA", "_id": "90022"} -{"city": "LOS ANGELES", "loc": [-118.197498, 34.024478], "pop": 47136, "state": "CA", "_id": "90023"} -{"city": "LOS ANGELES", "loc": [-118.440796, 34.063691], "pop": 38370, "state": "CA", "_id": "90024"} -{"city": "LOS ANGELES", "loc": [-118.448717, 34.044662], "pop": 37746, "state": "CA", "_id": "90025"} -{"city": "LOS ANGELES", "loc": [-118.264641, 34.076629], "pop": 74751, "state": "CA", "_id": "90026"} -{"city": "LOS ANGELES", "loc": [-118.292516, 34.104031], "pop": 50484, "state": "CA", "_id": "90027"} -{"city": "LOS ANGELES", "loc": [-118.325363, 34.100549], "pop": 30152, "state": "CA", "_id": "90028"} -{"city": "LOS ANGELES", "loc": [-118.294393, 34.089982], "pop": 41120, "state": "CA", "_id": "90029"} -{"city": "LOS ANGELES", "loc": [-118.211279, 34.078349], "pop": 39706, "state": "CA", "_id": "90031"} -{"city": "LOS ANGELES", "loc": [-118.175323, 34.081785], "pop": 46602, "state": "CA", "_id": "90032"} -{"city": "LOS ANGELES", "loc": [-118.208442, 34.048676], "pop": 57469, "state": "CA", "_id": "90033"} -{"city": "LOS ANGELES", "loc": [-118.400482, 34.028977], "pop": 53930, "state": "CA", "_id": "90034"} -{"city": "LOS ANGELES", "loc": [-118.380615, 34.053096], "pop": 25723, "state": "CA", "_id": "90035"} -{"city": "LOS ANGELES", "loc": [-118.349175, 34.069888], "pop": 29887, "state": "CA", "_id": "90036"} -{"city": "LOS ANGELES", "loc": [-118.286284, 34.002982], "pop": 56922, "state": "CA", "_id": "90037"} -{"city": "LOS ANGELES", "loc": [-118.321489, 34.089769], "pop": 33001, "state": "CA", "_id": "90038"} -{"city": "LOS ANGELES", "loc": [-118.259428, 34.112089], "pop": 29189, "state": "CA", "_id": "90039"} -{"city": "CITY OF COMMERCE", "loc": [-118.151352, 33.99471], "pop": 9689, "state": "CA", "_id": "90040"} -{"city": "LOS ANGELES", "loc": [-118.208205, 34.133932], "pop": 26864, "state": "CA", "_id": "90041"} -{"city": "LOS ANGELES", "loc": [-118.192902, 34.114527], "pop": 60003, "state": "CA", "_id": "90042"} -{"city": "LOS ANGELES", "loc": [-118.33211, 33.987099], "pop": 45397, "state": "CA", "_id": "90043"} -{"city": "LOS ANGELES", "loc": [-118.290119, 33.955089], "pop": 83958, "state": "CA", "_id": "90044"} -{"city": "LOS ANGELES", "loc": [-118.394128, 33.963075], "pop": 36480, "state": "CA", "_id": "90045"} -{"city": "COLE", "loc": [-118.357979, 34.09743], "pop": 48423, "state": "CA", "_id": "90046"} -{"city": "LOS ANGELES", "loc": [-118.307304, 33.956896], "pop": 47975, "state": "CA", "_id": "90047"} -{"city": "LOS ANGELES", "loc": [-118.371969, 34.073656], "pop": 20863, "state": "CA", "_id": "90048"} -{"city": "LOS ANGELES", "loc": [-118.473967, 34.066], "pop": 35507, "state": "CA", "_id": "90049"} -{"city": "LOS ANGELES", "loc": [-118.370703, 33.985329], "pop": 8108, "state": "CA", "_id": "90056"} -{"city": "LOS ANGELES", "loc": [-118.276262, 34.062172], "pop": 39017, "state": "CA", "_id": "90057"} -{"city": "VERNON", "loc": [-118.235365, 33.997344], "pop": 4090, "state": "CA", "_id": "90058"} -{"city": "LOS ANGELES", "loc": [-118.24628, 33.929331], "pop": 34536, "state": "CA", "_id": "90059"} -{"city": "LOS ANGELES", "loc": [-118.271638, 33.924493], "pop": 21856, "state": "CA", "_id": "90061"} -{"city": "LOS ANGELES", "loc": [-118.307277, 34.00324], "pop": 27517, "state": "CA", "_id": "90062"} -{"city": "HAZARD", "loc": [-118.185432, 34.044017], "pop": 61123, "state": "CA", "_id": "90063"} -{"city": "LOS ANGELES", "loc": [-118.425911, 34.035279], "pop": 23530, "state": "CA", "_id": "90064"} -{"city": "LOS ANGELES", "loc": [-118.226637, 34.107307], "pop": 45578, "state": "CA", "_id": "90065"} -{"city": "LOS ANGELES", "loc": [-118.429769, 34.002956], "pop": 53095, "state": "CA", "_id": "90066"} -{"city": "LOS ANGELES", "loc": [-118.409479, 34.055146], "pop": 2731, "state": "CA", "_id": "90067"} -{"city": "LOS ANGELES", "loc": [-118.330476, 34.115625], "pop": 25615, "state": "CA", "_id": "90068"} -{"city": "WEST HOLLYWOOD", "loc": [-118.378753, 34.090573], "pop": 20587, "state": "CA", "_id": "90069"} -{"city": "LOS ANGELES", "loc": [-118.257127, 34.052043], "pop": 15, "state": "CA", "_id": "90071"} -{"city": "LOS ANGELES", "loc": [-118.450155, 34.111245], "pop": 7989, "state": "CA", "_id": "90077"} -{"city": "BELL GARDENS", "loc": [-118.17205, 33.969177], "pop": 99568, "state": "CA", "_id": "90201"} -{"city": "BEVERLY HILLS", "loc": [-118.406477, 34.090107], "pop": 20700, "state": "CA", "_id": "90210"} -{"city": "BEVERLY HILLS", "loc": [-118.383007, 34.065217], "pop": 7746, "state": "CA", "_id": "90211"} -{"city": "BEVERLY HILLS", "loc": [-118.399544, 34.061855], "pop": 10725, "state": "CA", "_id": "90212"} -{"city": "RANCHO DOMINGUEZ", "loc": [-118.239044, 33.890654], "pop": 47631, "state": "CA", "_id": "90220"} -{"city": "EAST RANCHO DOMI", "loc": [-118.203724, 33.897324], "pop": 47844, "state": "CA", "_id": "90221"} -{"city": "ROSEWOOD", "loc": [-118.234034, 33.911535], "pop": 28754, "state": "CA", "_id": "90222"} -{"city": "CULVER CITY", "loc": [-118.399115, 33.994927], "pop": 32207, "state": "CA", "_id": "90230"} -{"city": "CULVER CITY", "loc": [-118.397332, 34.016809], "pop": 16138, "state": "CA", "_id": "90232"} -{"city": "DOWNEY", "loc": [-118.117381, 33.958135], "pop": 20273, "state": "CA", "_id": "90240"} -{"city": "DOWNEY", "loc": [-118.13062, 33.941612], "pop": 34348, "state": "CA", "_id": "90241"} -{"city": "DOWNEY", "loc": [-118.139465, 33.921789], "pop": 36988, "state": "CA", "_id": "90242"} -{"city": "EL SEGUNDO", "loc": [-118.411924, 33.924275], "pop": 15162, "state": "CA", "_id": "90245"} -{"city": "GARDENA", "loc": [-118.296122, 33.892463], "pop": 42072, "state": "CA", "_id": "90247"} -{"city": "GARDENA", "loc": [-118.289312, 33.874506], "pop": 9529, "state": "CA", "_id": "90248"} -{"city": "GARDENA", "loc": [-118.319854, 33.899762], "pop": 25806, "state": "CA", "_id": "90249"} -{"city": "HOLLY PARK", "loc": [-118.346978, 33.913214], "pop": 78511, "state": "CA", "_id": "90250"} -{"city": "HERMOSA BEACH", "loc": [-118.395511, 33.864309], "pop": 18289, "state": "CA", "_id": "90254"} -{"city": "HUNTINGTON PARK", "loc": [-118.216053, 33.976879], "pop": 72139, "state": "CA", "_id": "90255"} -{"city": "LAWNDALE", "loc": [-118.351014, 33.887908], "pop": 29576, "state": "CA", "_id": "90260"} -{"city": "LYNWOOD", "loc": [-118.201289, 33.924076], "pop": 61606, "state": "CA", "_id": "90262"} -{"city": "MALIBU", "loc": [-118.735149, 34.040184], "pop": 17850, "state": "CA", "_id": "90265"} -{"city": "MANHATTAN BEACH", "loc": [-118.399562, 33.889594], "pop": 31984, "state": "CA", "_id": "90266"} -{"city": "MAYWOOD", "loc": [-118.187685, 33.988992], "pop": 27801, "state": "CA", "_id": "90270"} -{"city": "PACIFIC PALISADE", "loc": [-118.536874, 34.054097], "pop": 20984, "state": "CA", "_id": "90272"} -{"city": "PALOS VERDES EST", "loc": [-118.374763, 33.770094], "pop": 60381, "state": "CA", "_id": "90274"} -{"city": "REDONDO BEACH", "loc": [-118.383221, 33.830656], "pop": 33102, "state": "CA", "_id": "90277"} -{"city": "REDONDO BEACH", "loc": [-118.371459, 33.870654], "pop": 34873, "state": "CA", "_id": "90278"} -{"city": "SOUTH GATE", "loc": [-118.201349, 33.94617], "pop": 87026, "state": "CA", "_id": "90280"} -{"city": "TOPANGA", "loc": [-118.602268, 34.10759], "pop": 5430, "state": "CA", "_id": "90290"} -{"city": "VENICE", "loc": [-118.463463, 33.993772], "pop": 32987, "state": "CA", "_id": "90291"} -{"city": "MARINA DEL REY", "loc": [-118.452475, 33.977876], "pop": 16572, "state": "CA", "_id": "90292"} -{"city": "PLAYA DEL REY", "loc": [-118.437314, 33.957745], "pop": 11477, "state": "CA", "_id": "90293"} -{"city": "INGLEWOOD", "loc": [-118.355575, 33.955048], "pop": 36481, "state": "CA", "_id": "90301"} -{"city": "INGLEWOOD", "loc": [-118.354805, 33.974496], "pop": 28681, "state": "CA", "_id": "90302"} -{"city": "INGLEWOOD", "loc": [-118.332058, 33.937691], "pop": 26418, "state": "CA", "_id": "90303"} -{"city": "LENNOX", "loc": [-118.355562, 33.938514], "pop": 28216, "state": "CA", "_id": "90304"} -{"city": "INGLEWOOD", "loc": [-118.32585, 33.958304], "pop": 14370, "state": "CA", "_id": "90305"} -{"city": "SANTA MONICA", "loc": [-118.490708, 34.017628], "pop": 4813, "state": "CA", "_id": "90401"} -{"city": "SANTA MONICA", "loc": [-118.503011, 34.034875], "pop": 14628, "state": "CA", "_id": "90402"} -{"city": "SANTA MONICA", "loc": [-118.49241, 34.028658], "pop": 22303, "state": "CA", "_id": "90403"} -{"city": "SANTA MONICA", "loc": [-118.4733, 34.026828], "pop": 22480, "state": "CA", "_id": "90404"} -{"city": "SANTA MONICA", "loc": [-118.471708, 34.01001], "pop": 26081, "state": "CA", "_id": "90405"} -{"city": "TORRANCE", "loc": [-118.31183, 33.826817], "pop": 37691, "state": "CA", "_id": "90501"} -{"city": "TORRANCE", "loc": [-118.292039, 33.828555], "pop": 15963, "state": "CA", "_id": "90502"} -{"city": "TORRANCE", "loc": [-118.354236, 33.839709], "pop": 40351, "state": "CA", "_id": "90503"} -{"city": "TORRANCE", "loc": [-118.329517, 33.870815], "pop": 30245, "state": "CA", "_id": "90504"} -{"city": "TORRANCE", "loc": [-118.350733, 33.810635], "pop": 33933, "state": "CA", "_id": "90505"} -{"city": "TORRANCE", "loc": [-118.329543, 33.885367], "pop": 0, "state": "CA", "_id": "90506"} -{"city": "WHITTIER", "loc": [-118.037139, 34.001119], "pop": 30501, "state": "CA", "_id": "90601"} -{"city": "WHITTIER", "loc": [-118.033703, 33.96931], "pop": 23508, "state": "CA", "_id": "90602"} -{"city": "WHITTIER", "loc": [-117.992685, 33.943199], "pop": 18063, "state": "CA", "_id": "90603"} -{"city": "WHITTIER", "loc": [-118.012075, 33.929931], "pop": 36371, "state": "CA", "_id": "90604"} -{"city": "WHITTIER", "loc": [-118.035568, 33.941338], "pop": 34035, "state": "CA", "_id": "90605"} -{"city": "LOS NIETOS", "loc": [-118.065639, 33.977019], "pop": 30185, "state": "CA", "_id": "90606"} -{"city": "BUENA PARK", "loc": [-118.011359, 33.840607], "pop": 42966, "state": "CA", "_id": "90620"} -{"city": "BUENA PARK", "loc": [-117.994257, 33.873136], "pop": 27502, "state": "CA", "_id": "90621"} -{"city": "CERRITOS", "loc": [-118.040618, 33.849017], "pop": 15066, "state": "CA", "_id": "90623"} -{"city": "CYPRESS", "loc": [-118.038696, 33.818613], "pop": 43055, "state": "CA", "_id": "90630"} -{"city": "LA HABRA HEIGHTS", "loc": [-117.949703, 33.932173], "pop": 59113, "state": "CA", "_id": "90631"} -{"city": "LA MIRADA", "loc": [-118.010081, 33.906681], "pop": 40452, "state": "CA", "_id": "90638"} -{"city": "MONTEBELLO", "loc": [-118.112986, 34.013342], "pop": 59068, "state": "CA", "_id": "90640"} -{"city": "NORWALK", "loc": [-118.081767, 33.90564], "pop": 94188, "state": "CA", "_id": "90650"} -{"city": "PICO RIVERA", "loc": [-118.088269, 33.98857], "pop": 58891, "state": "CA", "_id": "90660"} -{"city": "SANTA FE SPRINGS", "loc": [-118.083801, 33.946407], "pop": 14417, "state": "CA", "_id": "90670"} -{"city": "STANTON", "loc": [-117.994709, 33.803029], "pop": 25160, "state": "CA", "_id": "90680"} -{"city": "CERRITOS", "loc": [-118.068046, 33.866568], "pop": 69130, "state": "CA", "_id": "90701"} -{"city": "AVALON", "loc": [-118.343706, 33.331963], "pop": 3445, "state": "CA", "_id": "90704"} -{"city": "BELLFLOWER", "loc": [-118.126527, 33.886676], "pop": 61650, "state": "CA", "_id": "90706"} -{"city": "HARBOR CITY", "loc": [-118.299114, 33.797006], "pop": 24418, "state": "CA", "_id": "90710"} -{"city": "LAKEWOOD", "loc": [-118.145727, 33.851201], "pop": 28992, "state": "CA", "_id": "90712"} -{"city": "LAKEWOOD", "loc": [-118.111464, 33.847302], "pop": 26646, "state": "CA", "_id": "90713"} -{"city": "LAKEWOOD", "loc": [-118.076707, 33.840453], "pop": 17983, "state": "CA", "_id": "90715"} -{"city": "HAWAIIAN GARDENS", "loc": [-118.072964, 33.82958], "pop": 13921, "state": "CA", "_id": "90716"} -{"city": "RANCHO PALOS VER", "loc": [-118.31693, 33.793339], "pop": 19635, "state": "CA", "_id": "90717"} -{"city": "ROSSMOOR", "loc": [-118.069891, 33.795291], "pop": 21695, "state": "CA", "_id": "90720"} -{"city": "PARAMOUNT", "loc": [-118.163152, 33.896867], "pop": 46679, "state": "CA", "_id": "90723"} -{"city": "SAN PEDRO", "loc": [-118.291425, 33.733894], "pop": 58821, "state": "CA", "_id": "90731"} -{"city": "RANCHO PALOS VER", "loc": [-118.310597, 33.744947], "pop": 26244, "state": "CA", "_id": "90732"} -{"city": "SEAL BEACH", "loc": [-118.08428, 33.760971], "pop": 24537, "state": "CA", "_id": "90740"} -{"city": "WILMINGTON", "loc": [-118.264451, 33.785475], "pop": 49178, "state": "CA", "_id": "90744"} -{"city": "CARSON", "loc": [-118.268352, 33.822968], "pop": 50251, "state": "CA", "_id": "90745"} -{"city": "CARSON", "loc": [-118.255449, 33.858444], "pop": 25970, "state": "CA", "_id": "90746"} -{"city": "LONG BEACH", "loc": [-118.182025, 33.770553], "pop": 33906, "state": "CA", "_id": "90802"} -{"city": "LONG BEACH", "loc": [-118.134073, 33.761932], "pop": 32492, "state": "CA", "_id": "90803"} -{"city": "SIGNAL HILL", "loc": [-118.155187, 33.782993], "pop": 36092, "state": "CA", "_id": "90804"} -{"city": "LONG BEACH", "loc": [-118.180102, 33.863457], "pop": 74011, "state": "CA", "_id": "90805"} -{"city": "SIGNAL HILL", "loc": [-118.187443, 33.799319], "pop": 44982, "state": "CA", "_id": "90806"} -{"city": "SIGNAL HILL", "loc": [-118.18092, 33.830712], "pop": 28037, "state": "CA", "_id": "90807"} -{"city": "LONG BEACH", "loc": [-118.110299, 33.824145], "pop": 37809, "state": "CA", "_id": "90808"} -{"city": "CARSON", "loc": [-118.215006, 33.810985], "pop": 36694, "state": "CA", "_id": "90810"} -{"city": "LONG BEACH", "loc": [-118.183488, 33.78202], "pop": 58109, "state": "CA", "_id": "90813"} -{"city": "LONG BEACH", "loc": [-118.147988, 33.771576], "pop": 17359, "state": "CA", "_id": "90814"} -{"city": "LONG BEACH", "loc": [-118.119249, 33.793908], "pop": 38603, "state": "CA", "_id": "90815"} -{"city": "LONG BEACH", "loc": [-118.239257, 33.744415], "pop": 7362, "state": "CA", "_id": "90822"} -{"city": "ALTADENA", "loc": [-118.13919, 34.191187], "pop": 36013, "state": "CA", "_id": "91001"} -{"city": "ARCADIA", "loc": [-118.026374, 34.132389], "pop": 30550, "state": "CA", "_id": "91006"} -{"city": "ARCADIA", "loc": [-118.051483, 34.124271], "pop": 25675, "state": "CA", "_id": "91007"} -{"city": "BRADBURY", "loc": [-117.967005, 34.137445], "pop": 26462, "state": "CA", "_id": "91010"} -{"city": "FLINTRIDGE", "loc": [-118.199008, 34.209282], "pop": 19699, "state": "CA", "_id": "91011"} -{"city": "MONROVIA", "loc": [-118.001376, 34.143959], "pop": 39015, "state": "CA", "_id": "91016"} -{"city": "MONTROSE", "loc": [-118.230529, 34.211425], "pop": 6536, "state": "CA", "_id": "91020"} -{"city": "SIERRA MADRE", "loc": [-118.051916, 34.165101], "pop": 10560, "state": "CA", "_id": "91024"} -{"city": "SOUTH PASADENA", "loc": [-118.154696, 34.110939], "pop": 23936, "state": "CA", "_id": "91030"} -{"city": "SHADOW HILLS", "loc": [-118.321359, 34.261025], "pop": 18303, "state": "CA", "_id": "91040"} -{"city": "TUJUNGA", "loc": [-118.284856, 34.254389], "pop": 24853, "state": "CA", "_id": "91042"} -{"city": "PASADENA", "loc": [-118.139119, 34.146762], "pop": 16045, "state": "CA", "_id": "91101"} -{"city": "PASADENA", "loc": [-118.155119, 34.166906], "pop": 26641, "state": "CA", "_id": "91103"} -{"city": "PASADENA", "loc": [-118.12609, 34.167776], "pop": 37973, "state": "CA", "_id": "91104"} -{"city": "PASADENA", "loc": [-118.163577, 34.135455], "pop": 11165, "state": "CA", "_id": "91105"} -{"city": "PASADENA", "loc": [-118.126647, 34.143527], "pop": 23854, "state": "CA", "_id": "91106"} -{"city": "PASADENA", "loc": [-118.088905, 34.150997], "pop": 31390, "state": "CA", "_id": "91107"} -{"city": "SAN MARINO", "loc": [-118.111745, 34.120698], "pop": 12953, "state": "CA", "_id": "91108"} -{"city": "GLENDALE", "loc": [-118.289892, 34.171606], "pop": 22495, "state": "CA", "_id": "91201"} -{"city": "GLENDALE", "loc": [-118.265649, 34.165235], "pop": 20331, "state": "CA", "_id": "91202"} -{"city": "GLENDALE", "loc": [-118.263614, 34.151718], "pop": 12714, "state": "CA", "_id": "91203"} -{"city": "GLENDALE", "loc": [-118.259947, 34.137871], "pop": 15541, "state": "CA", "_id": "91204"} -{"city": "GLENDALE", "loc": [-118.24245, 34.137798], "pop": 38428, "state": "CA", "_id": "91205"} -{"city": "GLENDALE", "loc": [-118.232217, 34.155605], "pop": 30415, "state": "CA", "_id": "91206"} -{"city": "GLENDALE", "loc": [-118.245086, 34.164856], "pop": 8911, "state": "CA", "_id": "91207"} -{"city": "GLENDALE", "loc": [-118.234966, 34.19212], "pop": 14831, "state": "CA", "_id": "91208"} -{"city": "LA CRESCENTA", "loc": [-118.245687, 34.231619], "pop": 27249, "state": "CA", "_id": "91214"} -{"city": "OAK PARK", "loc": [-118.75718, 34.157163], "pop": 35520, "state": "CA", "_id": "91301"} -{"city": "CALABASAS", "loc": [-118.664103, 34.141854], "pop": 12690, "state": "CA", "_id": "91302"} -{"city": "CANOGA PARK", "loc": [-118.59828, 34.199257], "pop": 19656, "state": "CA", "_id": "91303"} -{"city": "CANOGA PARK", "loc": [-118.611059, 34.219671], "pop": 43675, "state": "CA", "_id": "91304"} -{"city": "WINNETKA", "loc": [-118.57492, 34.2092], "pop": 39261, "state": "CA", "_id": "91306"} -{"city": "WEST HILLS", "loc": [-118.638892, 34.196343], "pop": 22049, "state": "CA", "_id": "91307"} -{"city": "CHATSWORTH", "loc": [-118.591357, 34.258253], "pop": 37225, "state": "CA", "_id": "91311"} -{"city": "ENCINO", "loc": [-118.517542, 34.165479], "pop": 24538, "state": "CA", "_id": "91316"} -{"city": "NEWBURY PARK", "loc": [-118.935798, 34.177393], "pop": 31941, "state": "CA", "_id": "91320"} -{"city": "NEWHALL", "loc": [-118.523005, 34.379519], "pop": 23520, "state": "CA", "_id": "91321"} -{"city": "NORTHRIDGE", "loc": [-118.546595, 34.236743], "pop": 23252, "state": "CA", "_id": "91324"} -{"city": "NORTHRIDGE", "loc": [-118.51884, 34.235332], "pop": 28071, "state": "CA", "_id": "91325"} -{"city": "PORTER RANCH", "loc": [-118.541235, 34.274191], "pop": 23466, "state": "CA", "_id": "91326"} -{"city": "CALIFORNIA STATE", "loc": [-118.528634, 34.23805], "pop": 1604, "state": "CA", "_id": "91330"} -{"city": "ARLETA", "loc": [-118.420692, 34.258081], "pop": 88114, "state": "CA", "_id": "91331"} -{"city": "RESEDA", "loc": [-118.539071, 34.200663], "pop": 62117, "state": "CA", "_id": "91335"} -{"city": "SAN FERNANDO", "loc": [-118.435242, 34.287509], "pop": 33379, "state": "CA", "_id": "91340"} -{"city": "SYLMAR", "loc": [-118.432181, 34.30538], "pop": 68612, "state": "CA", "_id": "91342"} -{"city": "NORTH HILLS", "loc": [-118.47582, 34.236636], "pop": 48751, "state": "CA", "_id": "91343"} -{"city": "GRANADA HILLS", "loc": [-118.4992, 34.277068], "pop": 45729, "state": "CA", "_id": "91344"} -{"city": "MISSION HILLS", "loc": [-118.458659, 34.261873], "pop": 14886, "state": "CA", "_id": "91345"} -{"city": "AGUA DULCE", "loc": [-118.488955, 34.459742], "pop": 31741, "state": "CA", "_id": "91350"} -{"city": "CANYON COUNTRY", "loc": [-118.449011, 34.426203], "pop": 47273, "state": "CA", "_id": "91351"} -{"city": "SUN VALLEY", "loc": [-118.369853, 34.220907], "pop": 43722, "state": "CA", "_id": "91352"} -{"city": "VALENCIA", "loc": [-118.537437, 34.446608], "pop": 7918, "state": "CA", "_id": "91354"} -{"city": "VALENCIA", "loc": [-118.55352, 34.398456], "pop": 23550, "state": "CA", "_id": "91355"} -{"city": "TARZANA", "loc": [-118.541354, 34.16708], "pop": 25316, "state": "CA", "_id": "91356"} -{"city": "THOUSAND OAKS", "loc": [-118.873908, 34.209179], "pop": 41654, "state": "CA", "_id": "91360"} -{"city": "WESTLAKE VILLAGE", "loc": [-118.83832, 34.147233], "pop": 18608, "state": "CA", "_id": "91361"} -{"city": "WESTLAKE VILLAGE", "loc": [-118.830601, 34.191884], "pop": 26572, "state": "CA", "_id": "91362"} -{"city": "WOODLAND HILLS", "loc": [-118.600019, 34.155652], "pop": 25638, "state": "CA", "_id": "91364"} -{"city": "WOODLAND HILLS", "loc": [-118.615891, 34.176689], "pop": 34253, "state": "CA", "_id": "91367"} -{"city": "NEWHALL", "loc": [-118.582134, 34.381067], "pop": 1677, "state": "CA", "_id": "91381"} -{"city": "CASTAIC", "loc": [-118.62537, 34.482695], "pop": 19440, "state": "CA", "_id": "91384"} -{"city": "VAN NUYS", "loc": [-118.432375, 34.180152], "pop": 35990, "state": "CA", "_id": "91401"} -{"city": "PANORAMA CITY", "loc": [-118.447009, 34.226158], "pop": 52577, "state": "CA", "_id": "91402"} -{"city": "SHERMAN OAKS", "loc": [-118.460325, 34.151407], "pop": 20046, "state": "CA", "_id": "91403"} -{"city": "VAN NUYS", "loc": [-118.445636, 34.200068], "pop": 39669, "state": "CA", "_id": "91405"} -{"city": "VAN NUYS", "loc": [-118.486821, 34.200568], "pop": 46124, "state": "CA", "_id": "91406"} -{"city": "VAN NUYS", "loc": [-118.457396, 34.178133], "pop": 21616, "state": "CA", "_id": "91411"} -{"city": "SHERMAN OAKS", "loc": [-118.432208, 34.152565], "pop": 27747, "state": "CA", "_id": "91423"} -{"city": "ENCINO", "loc": [-118.488238, 34.15098], "pop": 13605, "state": "CA", "_id": "91436"} -{"city": "BURBANK", "loc": [-118.300898, 34.186238], "pop": 15991, "state": "CA", "_id": "91501"} -{"city": "BURBANK", "loc": [-118.305912, 34.174487], "pop": 9833, "state": "CA", "_id": "91502"} -{"city": "BURBANK", "loc": [-118.326401, 34.200097], "pop": 22656, "state": "CA", "_id": "91504"} -{"city": "BURBANK", "loc": [-118.344175, 34.168998], "pop": 27676, "state": "CA", "_id": "91505"} -{"city": "BURBANK", "loc": [-118.323148, 34.171746], "pop": 18336, "state": "CA", "_id": "91506"} -{"city": "NORTH HOLLYWOOD", "loc": [-118.371274, 34.16867], "pop": 33882, "state": "CA", "_id": "91601"} -{"city": "TOLUCA LAKE", "loc": [-118.367606, 34.151095], "pop": 14301, "state": "CA", "_id": "91602"} -{"city": "STUDIO CITY", "loc": [-118.391311, 34.143025], "pop": 24354, "state": "CA", "_id": "91604"} -{"city": "NORTH HOLLYWOOD", "loc": [-118.400069, 34.205747], "pop": 50050, "state": "CA", "_id": "91605"} -{"city": "NORTH HOLLYWOOD", "loc": [-118.386538, 34.187182], "pop": 39737, "state": "CA", "_id": "91606"} -{"city": "VALLEY VILLAGE", "loc": [-118.398905, 34.167217], "pop": 28021, "state": "CA", "_id": "91607"} -{"city": "ALTA LOMA", "loc": [-117.599149, 34.133922], "pop": 31633, "state": "CA", "_id": "91701"} -{"city": "AZUSA", "loc": [-117.903083, 34.12476], "pop": 52261, "state": "CA", "_id": "91702"} -{"city": "IRWINDALE", "loc": [-117.969539, 34.084245], "pop": 69464, "state": "CA", "_id": "91706"} -{"city": "CHINO HILLS", "loc": [-117.730791, 33.979735], "pop": 37965, "state": "CA", "_id": "91709"} -{"city": "CHINO", "loc": [-117.684401, 34.012532], "pop": 69244, "state": "CA", "_id": "91710"} -{"city": "CLAREMONT", "loc": [-117.718293, 34.109167], "pop": 34096, "state": "CA", "_id": "91711"} -{"city": "CORONA", "loc": [-117.531916, 33.861839], "pop": 42717, "state": "CA", "_id": "91719"} -{"city": "CORONA", "loc": [-117.594288, 33.876995], "pop": 55741, "state": "CA", "_id": "91720"} -{"city": "COVINA", "loc": [-117.906544, 34.097162], "pop": 31703, "state": "CA", "_id": "91722"} -{"city": "COVINA", "loc": [-117.884285, 34.08596], "pop": 15590, "state": "CA", "_id": "91723"} -{"city": "COVINA", "loc": [-117.855982, 34.093752], "pop": 23462, "state": "CA", "_id": "91724"} -{"city": "RANCHO CUCAMONGA", "loc": [-117.59408, 34.107039], "pop": 41087, "state": "CA", "_id": "91730"} -{"city": "EL MONTE", "loc": [-118.037108, 34.079142], "pop": 26178, "state": "CA", "_id": "91731"} -{"city": "EL MONTE", "loc": [-118.01492, 34.070533], "pop": 58059, "state": "CA", "_id": "91732"} -{"city": "SOUTH EL MONTE", "loc": [-118.044381, 34.055676], "pop": 43691, "state": "CA", "_id": "91733"} -{"city": "ALTA LOMA", "loc": [-117.579295, 34.144883], "pop": 19708, "state": "CA", "_id": "91737"} -{"city": "ETIWANDA", "loc": [-117.519329, 34.119873], "pop": 13553, "state": "CA", "_id": "91739"} -{"city": "GLENDORA", "loc": [-117.855155, 34.128663], "pop": 48836, "state": "CA", "_id": "91740"} -{"city": "INDUSTRY", "loc": [-117.934098, 34.029428], "pop": 77114, "state": "CA", "_id": "91744"} -{"city": "HACIENDA HEIGHTS", "loc": [-117.965205, 33.997741], "pop": 52182, "state": "CA", "_id": "91745"} -{"city": "BASSETT", "loc": [-117.980026, 34.050963], "pop": 30330, "state": "CA", "_id": "91746"} -{"city": "ROWLAND HEIGHTS", "loc": [-117.896946, 33.981777], "pop": 40511, "state": "CA", "_id": "91748"} -{"city": "LA VERNE", "loc": [-117.77077, 34.115905], "pop": 33621, "state": "CA", "_id": "91750"} -{"city": "MIRA LOMA", "loc": [-117.523574, 33.993845], "pop": 17368, "state": "CA", "_id": "91752"} -{"city": "MONTEREY PARK", "loc": [-118.127144, 34.053409], "pop": 62133, "state": "CA", "_id": "91754"} -{"city": "MT BALDY", "loc": [-117.580219, 34.218082], "pop": 430, "state": "CA", "_id": "91759"} -{"city": "NORCO", "loc": [-117.557866, 33.927983], "pop": 23585, "state": "CA", "_id": "91760"} -{"city": "ONTARIO", "loc": [-117.618662, 34.031647], "pop": 47921, "state": "CA", "_id": "91761"} -{"city": "ONTARIO", "loc": [-117.66647, 34.058415], "pop": 47653, "state": "CA", "_id": "91762"} -{"city": "MONTCLAIR", "loc": [-117.698669, 34.073298], "pop": 25862, "state": "CA", "_id": "91763"} -{"city": "ONTARIO", "loc": [-117.625402, 34.076308], "pop": 41958, "state": "CA", "_id": "91764"} -{"city": "DIAMOND BAR", "loc": [-117.809822, 34.006585], "pop": 41920, "state": "CA", "_id": "91765"} -{"city": "PHILLIPS RANCH", "loc": [-117.752086, 34.043268], "pop": 64056, "state": "CA", "_id": "91766"} -{"city": "POMONA", "loc": [-117.736171, 34.081187], "pop": 41420, "state": "CA", "_id": "91767"} -{"city": "POMONA", "loc": [-117.776312, 34.066168], "pop": 31007, "state": "CA", "_id": "91768"} -{"city": "ROSEMEAD", "loc": [-118.08529, 34.065767], "pop": 59898, "state": "CA", "_id": "91770"} -{"city": "SAN DIMAS", "loc": [-117.81694, 34.102263], "pop": 32453, "state": "CA", "_id": "91773"} -{"city": "SAN GABRIEL", "loc": [-118.085658, 34.115486], "pop": 21426, "state": "CA", "_id": "91775"} -{"city": "SAN GABRIEL", "loc": [-118.095471, 34.089027], "pop": 34995, "state": "CA", "_id": "91776"} -{"city": "TEMPLE CITY", "loc": [-118.053652, 34.101586], "pop": 31297, "state": "CA", "_id": "91780"} -{"city": "UPLAND", "loc": [-117.658336, 34.114432], "pop": 66548, "state": "CA", "_id": "91786"} -{"city": "DIAMOND BAR", "loc": [-117.857828, 34.016625], "pop": 42206, "state": "CA", "_id": "91789"} -{"city": "WEST COVINA", "loc": [-117.936643, 34.067336], "pop": 38113, "state": "CA", "_id": "91790"} -{"city": "WEST COVINA", "loc": [-117.897767, 34.065267], "pop": 25685, "state": "CA", "_id": "91791"} -{"city": "WEST COVINA", "loc": [-117.897459, 34.022852], "pop": 30496, "state": "CA", "_id": "91792"} -{"city": "ALHAMBRA", "loc": [-118.129288, 34.091436], "pop": 51148, "state": "CA", "_id": "91801"} -{"city": "ALHAMBRA", "loc": [-118.143354, 34.074514], "pop": 30228, "state": "CA", "_id": "91803"} -{"city": "ALPINE", "loc": [-116.754328, 32.828161], "pop": 12566, "state": "CA", "_id": "91901"} -{"city": "BONITA", "loc": [-117.022065, 32.667143], "pop": 16579, "state": "CA", "_id": "91902"} -{"city": "BOULEVARD", "loc": [-116.319982, 32.671934], "pop": 1163, "state": "CA", "_id": "91905"} -{"city": "CAMPO", "loc": [-116.490459, 32.660491], "pop": 2657, "state": "CA", "_id": "91906"} -{"city": "CHULA VISTA", "loc": [-117.06756, 32.637139], "pop": 56320, "state": "CA", "_id": "91910"} -{"city": "CHULA VISTA", "loc": [-117.056459, 32.608428], "pop": 65952, "state": "CA", "_id": "91911"} -{"city": "CHULA VISTA", "loc": [-116.985237, 32.651296], "pop": 10079, "state": "CA", "_id": "91913"} -{"city": "CHULA VISTA", "loc": [-116.96517, 32.65875], "pop": 0, "state": "CA", "_id": "91914"} -{"city": "CHULA VISTA", "loc": [-116.940807, 32.631513], "pop": 12, "state": "CA", "_id": "91915"} -{"city": "DESCANSO", "loc": [-116.602732, 32.872971], "pop": 1826, "state": "CA", "_id": "91916"} -{"city": "DULZURA", "loc": [-116.728523, 32.615172], "pop": 352, "state": "CA", "_id": "91917"} -{"city": "IMPERIAL BEACH", "loc": [-117.11478, 32.578289], "pop": 26567, "state": "CA", "_id": "91932"} -{"city": "JACUMBA", "loc": [-116.195184, 32.624934], "pop": 599, "state": "CA", "_id": "91934"} -{"city": "JAMUL", "loc": [-116.832332, 32.716289], "pop": 7879, "state": "CA", "_id": "91935"} -{"city": "LA MESA", "loc": [-117.011541, 32.760431], "pop": 42536, "state": "CA", "_id": "91941"} -{"city": "LA MESA", "loc": [-117.018879, 32.783506], "pop": 23944, "state": "CA", "_id": "91942"} -{"city": "LEMON GROVE", "loc": [-117.032646, 32.73323], "pop": 24268, "state": "CA", "_id": "91945"} -{"city": "NATIONAL CITY", "loc": [-117.089747, 32.674916], "pop": 52005, "state": "CA", "_id": "91950"} -{"city": "PINE VALLEY", "loc": [-116.512733, 32.835047], "pop": 1801, "state": "CA", "_id": "91962"} -{"city": "POTRERO", "loc": [-116.603748, 32.620477], "pop": 638, "state": "CA", "_id": "91963"} -{"city": "SPRING VALLEY", "loc": [-116.997644, 32.724014], "pop": 52403, "state": "CA", "_id": "91977"} -{"city": "SPRING VALLEY", "loc": [-116.959551, 32.732892], "pop": 7601, "state": "CA", "_id": "91978"} -{"city": "TECATE", "loc": [-116.606397, 32.592205], "pop": 230, "state": "CA", "_id": "91980"} -{"city": "BONSALL", "loc": [-117.18969, 33.294033], "pop": 2910, "state": "CA", "_id": "92003"} -{"city": "BORREGO SPRINGS", "loc": [-116.351394, 33.238649], "pop": 2633, "state": "CA", "_id": "92004"} -{"city": "CARDIFF BY THE S", "loc": [-117.274371, 33.025265], "pop": 10236, "state": "CA", "_id": "92007"} -{"city": "CARLSBAD", "loc": [-117.324998, 33.160241], "pop": 35651, "state": "CA", "_id": "92008"} -{"city": "CARLSBAD", "loc": [-117.261888, 33.095407], "pop": 27019, "state": "CA", "_id": "92009"} -{"city": "DEL MAR", "loc": [-117.237314, 32.971474], "pop": 19885, "state": "CA", "_id": "92014"} -{"city": "EL CAJON", "loc": [-116.919055, 32.777726], "pop": 35425, "state": "CA", "_id": "92019"} -{"city": "EL CAJON", "loc": [-116.966504, 32.792765], "pop": 55176, "state": "CA", "_id": "92020"} -{"city": "EL CAJON", "loc": [-116.922336, 32.817847], "pop": 51773, "state": "CA", "_id": "92021"} -{"city": "ENCINITAS", "loc": [-117.26891, 33.053469], "pop": 45995, "state": "CA", "_id": "92024"} -{"city": "ESCONDIDO", "loc": [-117.069987, 33.110117], "pop": 39345, "state": "CA", "_id": "92025"} -{"city": "ESCONDIDO", "loc": [-117.097808, 33.160513], "pop": 37176, "state": "CA", "_id": "92026"} -{"city": "ESCONDIDO", "loc": [-117.051966, 33.138824], "pop": 39305, "state": "CA", "_id": "92027"} -{"city": "FALLBROOK", "loc": [-117.228952, 33.369015], "pop": 35232, "state": "CA", "_id": "92028"} -{"city": "ESCONDIDO", "loc": [-117.112793, 33.089497], "pop": 18174, "state": "CA", "_id": "92029"} -{"city": "JULIAN", "loc": [-116.565812, 33.053355], "pop": 2552, "state": "CA", "_id": "92036"} -{"city": "LA JOLLA", "loc": [-117.25208, 32.845488], "pop": 40399, "state": "CA", "_id": "92037"} -{"city": "LAKESIDE", "loc": [-116.920089, 32.856181], "pop": 41054, "state": "CA", "_id": "92040"} -{"city": "OCEANSIDE", "loc": [-117.357294, 33.20723], "pop": 61760, "state": "CA", "_id": "92054"} -{"city": "MARINE CORP BASE", "loc": [-117.409452, 33.327929], "pop": 13643, "state": "CA", "_id": "92055"} -{"city": "OCEANSIDE", "loc": [-117.283089, 33.196784], "pop": 40161, "state": "CA", "_id": "92056"} -{"city": "OCEANSIDE", "loc": [-117.302484, 33.240654], "pop": 33178, "state": "CA", "_id": "92057"} -{"city": "PALA", "loc": [-117.071725, 33.377662], "pop": 1064, "state": "CA", "_id": "92059"} -{"city": "PAUMA VALLEY", "loc": [-116.959552, 33.306285], "pop": 1929, "state": "CA", "_id": "92061"} -{"city": "POWAY", "loc": [-117.040223, 32.975619], "pop": 43490, "state": "CA", "_id": "92064"} -{"city": "RAMONA", "loc": [-116.853548, 33.029349], "pop": 27744, "state": "CA", "_id": "92065"} -{"city": "RANCHITA", "loc": [-116.539121, 33.24055], "pop": 389, "state": "CA", "_id": "92066"} -{"city": "SAN LUIS REY", "loc": [-117.306403, 33.294367], "pop": 9471, "state": "CA", "_id": "92068"} -{"city": "SAN MARCOS", "loc": [-117.169716, 33.144386], "pop": 45382, "state": "CA", "_id": "92069"} -{"city": "SANTA YSABEL", "loc": [-116.69635, 33.147579], "pop": 1263, "state": "CA", "_id": "92070"} -{"city": "SANTEE", "loc": [-116.986154, 32.848636], "pop": 52816, "state": "CA", "_id": "92071"} -{"city": "SOLANA BEACH", "loc": [-117.2598, 32.993739], "pop": 12259, "state": "CA", "_id": "92075"} -{"city": "VALLEY CENTER", "loc": [-117.012232, 33.249046], "pop": 13196, "state": "CA", "_id": "92082"} -{"city": "VISTA", "loc": [-117.245854, 33.187296], "pop": 50641, "state": "CA", "_id": "92083"} -{"city": "VISTA", "loc": [-117.224285, 33.213118], "pop": 38088, "state": "CA", "_id": "92084"} -{"city": "WARNER SPRINGS", "loc": [-116.721385, 33.303666], "pop": 780, "state": "CA", "_id": "92086"} -{"city": "SAN DIEGO", "loc": [-117.159316, 32.71852], "pop": 20265, "state": "CA", "_id": "92101"} -{"city": "SAN DIEGO", "loc": [-117.121858, 32.713893], "pop": 45265, "state": "CA", "_id": "92102"} -{"city": "SAN DIEGO", "loc": [-117.163552, 32.746638], "pop": 31123, "state": "CA", "_id": "92103"} -{"city": "SAN DIEGO", "loc": [-117.127189, 32.745425], "pop": 44032, "state": "CA", "_id": "92104"} -{"city": "SAN DIEGO", "loc": [-117.094681, 32.7423], "pop": 63344, "state": "CA", "_id": "92105"} -{"city": "SAN DIEGO", "loc": [-117.226829, 32.72725], "pop": 27640, "state": "CA", "_id": "92106"} -{"city": "SAN DIEGO", "loc": [-117.243307, 32.742531], "pop": 25913, "state": "CA", "_id": "92107"} -{"city": "SAN DIEGO", "loc": [-117.133525, 32.778327], "pop": 8860, "state": "CA", "_id": "92108"} -{"city": "SAN DIEGO", "loc": [-117.240534, 32.796923], "pop": 44804, "state": "CA", "_id": "92109"} -{"city": "SAN DIEGO", "loc": [-117.202847, 32.763476], "pop": 26787, "state": "CA", "_id": "92110"} -{"city": "SAN DIEGO", "loc": [-117.17081, 32.797185], "pop": 45487, "state": "CA", "_id": "92111"} -{"city": "SAN DIEGO", "loc": [-117.115257, 32.697047], "pop": 44741, "state": "CA", "_id": "92113"} -{"city": "SAN DIEGO", "loc": [-117.05235, 32.705923], "pop": 62258, "state": "CA", "_id": "92114"} -{"city": "SAN DIEGO", "loc": [-117.072056, 32.760742], "pop": 51418, "state": "CA", "_id": "92115"} -{"city": "SAN DIEGO", "loc": [-117.124166, 32.762446], "pop": 32279, "state": "CA", "_id": "92116"} -{"city": "SAN DIEGO", "loc": [-117.196536, 32.823948], "pop": 49737, "state": "CA", "_id": "92117"} -{"city": "CORONADO", "loc": [-117.169823, 32.68069], "pop": 16670, "state": "CA", "_id": "92118"} -{"city": "SAN DIEGO", "loc": [-117.026065, 32.803587], "pop": 24135, "state": "CA", "_id": "92119"} -{"city": "SAN DIEGO", "loc": [-117.070708, 32.79581], "pop": 25375, "state": "CA", "_id": "92120"} -{"city": "SAN DIEGO", "loc": [-117.203503, 32.891894], "pop": 2286, "state": "CA", "_id": "92121"} -{"city": "SAN DIEGO", "loc": [-117.211507, 32.857736], "pop": 30192, "state": "CA", "_id": "92122"} -{"city": "SAN DIEGO", "loc": [-117.139248, 32.797297], "pop": 23541, "state": "CA", "_id": "92123"} -{"city": "SAN DIEGO", "loc": [-117.098613, 32.820113], "pop": 29171, "state": "CA", "_id": "92124"} -{"city": "SAN DIEGO", "loc": [-117.140227, 32.916136], "pop": 56676, "state": "CA", "_id": "92126"} -{"city": "SAN DIEGO", "loc": [-117.085596, 33.027854], "pop": 11077, "state": "CA", "_id": "92127"} -{"city": "SAN DIEGO", "loc": [-117.068982, 33.00666], "pop": 30437, "state": "CA", "_id": "92128"} -{"city": "SAN DIEGO", "loc": [-117.121308, 32.965185], "pop": 43092, "state": "CA", "_id": "92129"} -{"city": "SAN DIEGO", "loc": [-117.225201, 32.955533], "pop": 12681, "state": "CA", "_id": "92130"} -{"city": "SAN DIEGO", "loc": [-117.089758, 32.912343], "pop": 16649, "state": "CA", "_id": "92131"} -{"city": "SAN DIEGO", "loc": [-117.19202, 32.702482], "pop": 8122, "state": "CA", "_id": "92135"} -{"city": "SAN DIEGO", "loc": [-117.124678, 32.681585], "pop": 11750, "state": "CA", "_id": "92136"} -{"city": "SAN DIEGO", "loc": [-117.047375, 32.680612], "pop": 35577, "state": "CA", "_id": "92139"} -{"city": "SAN DIEGO", "loc": [-117.116518, 32.870365], "pop": 3089, "state": "CA", "_id": "92145"} -{"city": "SAN DIEGO", "loc": [-117.070725, 32.575276], "pop": 59925, "state": "CA", "_id": "92154"} -{"city": "SAN DIEGO", "loc": [-117.160335, 32.676144], "pop": 1570, "state": "CA", "_id": "92155"} -{"city": "SAN YSIDRO", "loc": [-117.042976, 32.562567], "pop": 30131, "state": "CA", "_id": "92173"} -{"city": "CHIRIACO SUMMIT", "loc": [-116.235729, 33.721899], "pop": 47118, "state": "CA", "_id": "92201"} -{"city": "INDIAN WELLS", "loc": [-116.338129, 33.716334], "pop": 2599, "state": "CA", "_id": "92210"} -{"city": "BANNING", "loc": [-116.889928, 33.92816], "pop": 22545, "state": "CA", "_id": "92220"} -{"city": "BEAUMONT", "loc": [-116.970079, 33.950429], "pop": 16176, "state": "CA", "_id": "92223"} -{"city": "LOST LAKE", "loc": [-114.597131, 33.605715], "pop": 13852, "state": "CA", "_id": "92225"} -{"city": "BRAWLEY", "loc": [-115.529613, 32.979181], "pop": 20199, "state": "CA", "_id": "92227"} -{"city": "CABAZON", "loc": [-116.773948, 33.908583], "pop": 1697, "state": "CA", "_id": "92230"} -{"city": "CALEXICO", "loc": [-115.502815, 32.683227], "pop": 22345, "state": "CA", "_id": "92231"} -{"city": "CALIPATRIA", "loc": [-115.511402, 33.166956], "pop": 4847, "state": "CA", "_id": "92233"} -{"city": "CATHEDRAL CITY", "loc": [-116.466497, 33.809839], "pop": 29640, "state": "CA", "_id": "92234"} -{"city": "COACHELLA", "loc": [-116.177231, 33.674965], "pop": 17147, "state": "CA", "_id": "92236"} -{"city": "EAGLE MOUNTAIN", "loc": [-115.052603, 33.604941], "pop": 4499, "state": "CA", "_id": "92239"} -{"city": "DESERT HOT SPRIN", "loc": [-116.366222, 33.904973], "pop": 686, "state": "CA", "_id": "92240"} -{"city": "BIG RIVER", "loc": [-114.33928, 34.149142], "pop": 976, "state": "CA", "_id": "92242"} -{"city": "EL CENTRO", "loc": [-115.566508, 32.789332], "pop": 39246, "state": "CA", "_id": "92243"} -{"city": "HEBER", "loc": [-115.428281, 32.698918], "pop": 206, "state": "CA", "_id": "92249"} -{"city": "HOLTVILLE", "loc": [-115.377456, 32.810387], "pop": 7060, "state": "CA", "_id": "92250"} -{"city": "IMPERIAL", "loc": [-115.572984, 32.846954], "pop": 6092, "state": "CA", "_id": "92251"} -{"city": "JOSHUA TREE", "loc": [-116.303763, 34.150163], "pop": 8227, "state": "CA", "_id": "92252"} -{"city": "LA QUINTA", "loc": [-116.308081, 33.668474], "pop": 9392, "state": "CA", "_id": "92253"} -{"city": "MORONGO VALLEY", "loc": [-116.565641, 34.060646], "pop": 2721, "state": "CA", "_id": "92256"} -{"city": "NILAND", "loc": [-115.696455, 33.378373], "pop": 875, "state": "CA", "_id": "92257"} -{"city": "PALM CITY", "loc": [-116.366442, 33.730842], "pop": 31975, "state": "CA", "_id": "92260"} -{"city": "PALM SPRINGS", "loc": [-116.53466, 33.841406], "pop": 22808, "state": "CA", "_id": "92262"} -{"city": "PALM SPRINGS", "loc": [-116.516958, 33.801828], "pop": 18733, "state": "CA", "_id": "92264"} -{"city": "PARKER DAM", "loc": [-114.155969, 34.297977], "pop": 141, "state": "CA", "_id": "92267"} -{"city": "RANCHO MIRAGE", "loc": [-116.422451, 33.764284], "pop": 9737, "state": "CA", "_id": "92270"} -{"city": "BLYTHE", "loc": [-116.495855, 33.951256], "pop": 18605, "state": "CA", "_id": "92272"} -{"city": "SALTON CITY", "loc": [-116.11584, 33.543418], "pop": 19112, "state": "CA", "_id": "92274"} -{"city": "THOUSAND PALMS", "loc": [-116.371305, 33.808158], "pop": 5557, "state": "CA", "_id": "92276"} -{"city": "TWENTYNINE PALMS", "loc": [-116.060133, 34.145509], "pop": 13371, "state": "CA", "_id": "92277"} -{"city": "TWENTYNINE PALMS", "loc": [-116.06041, 34.237969], "pop": 11412, "state": "CA", "_id": "92278"} -{"city": "VIDAL", "loc": [-114.565602, 34.156109], "pop": 40, "state": "CA", "_id": "92280"} -{"city": "WESTMORLAND", "loc": [-115.630723, 33.041058], "pop": 1902, "state": "CA", "_id": "92281"} -{"city": "WHITE WATER", "loc": [-116.693154, 33.927591], "pop": 420, "state": "CA", "_id": "92282"} -{"city": "FELICITY", "loc": [-114.636634, 32.832922], "pop": 3867, "state": "CA", "_id": "92283"} -{"city": "YUCCA VALLEY", "loc": [-116.431313, 34.155936], "pop": 22131, "state": "CA", "_id": "92284"} -{"city": "ADELANTO", "loc": [-117.424189, 34.584128], "pop": 7176, "state": "CA", "_id": "92301"} -{"city": "AMBOY", "loc": [-115.774907, 34.599012], "pop": 29, "state": "CA", "_id": "92304"} -{"city": "ANGELUS OAKS", "loc": [-116.948482, 34.153149], "pop": 238, "state": "CA", "_id": "92305"} -{"city": "APPLE VALLEY", "loc": [-117.2132, 34.529081], "pop": 26066, "state": "CA", "_id": "92307"} -{"city": "APPLE VALLEY", "loc": [-117.192684, 34.469814], "pop": 24973, "state": "CA", "_id": "92308"} -{"city": "BAKER", "loc": [-116.063754, 35.360573], "pop": 606, "state": "CA", "_id": "92309"} -{"city": "FORT IRWIN", "loc": [-116.644759, 35.40148], "pop": 6735, "state": "CA", "_id": "92310"} -{"city": "BARSTOW", "loc": [-117.038702, 34.89145], "pop": 33076, "state": "CA", "_id": "92311"} -{"city": "BIG BEAR CITY", "loc": [-116.920412, 34.242233], "pop": 18959, "state": "CA", "_id": "92314"} -{"city": "BLOOMINGTON", "loc": [-117.399295, 34.066198], "pop": 22916, "state": "CA", "_id": "92316"} -{"city": "CALIMESA", "loc": [-117.04304, 33.994586], "pop": 6345, "state": "CA", "_id": "92320"} -{"city": "GRAND TERRACE", "loc": [-117.318577, 34.057964], "pop": 53822, "state": "CA", "_id": "92324"} -{"city": "DAGGETT", "loc": [-116.887555, 34.86677], "pop": 701, "state": "CA", "_id": "92327"} -{"city": "DEATH VALLEY", "loc": [-116.893682, 36.467165], "pop": 440, "state": "CA", "_id": "92328"} -{"city": "ESSEX", "loc": [-115.280344, 34.560626], "pop": 214, "state": "CA", "_id": "92332"} -{"city": "FONTANA", "loc": [-117.455114, 34.079351], "pop": 81255, "state": "CA", "_id": "92335"} -{"city": "FONTANA", "loc": [-117.437759, 34.117276], "pop": 27957, "state": "CA", "_id": "92336"} -{"city": "LUDLOW", "loc": [-116.386202, 34.901779], "pop": 383, "state": "CA", "_id": "92338"} -{"city": "FOREST FALLS", "loc": [-116.914147, 34.08805], "pop": 842, "state": "CA", "_id": "92339"} -{"city": "HELENDALE", "loc": [-117.33666, 34.749859], "pop": 3612, "state": "CA", "_id": "92342"} -{"city": "HESPERIA", "loc": [-117.302527, 34.422215], "pop": 52170, "state": "CA", "_id": "92345"} -{"city": "EAST HIGHLAND", "loc": [-117.208717, 34.126969], "pop": 37484, "state": "CA", "_id": "92346"} -{"city": "HINKLEY", "loc": [-117.180867, 34.92788], "pop": 1905, "state": "CA", "_id": "92347"} -{"city": "KELSO", "loc": [-115.577574, 34.9678], "pop": 30, "state": "CA", "_id": "92351"} -{"city": "LOMA LINDA", "loc": [-117.251286, 34.052833], "pop": 17855, "state": "CA", "_id": "92354"} -{"city": "LUCERNE VALLEY", "loc": [-116.918857, 34.44695], "pop": 4353, "state": "CA", "_id": "92356"} -{"city": "LYTLE CREEK", "loc": [-117.518613, 34.255755], "pop": 625, "state": "CA", "_id": "92358"} -{"city": "MENTONE", "loc": [-117.112581, 34.077372], "pop": 5832, "state": "CA", "_id": "92359"} -{"city": "NEEDLES", "loc": [-114.587134, 34.782369], "pop": 6316, "state": "CA", "_id": "92363"} -{"city": "NIPTON", "loc": [-115.481436, 35.46761], "pop": 390, "state": "CA", "_id": "92364"} -{"city": "NEWBERRY SPRINGS", "loc": [-116.746373, 34.885001], "pop": 4296, "state": "CA", "_id": "92365"} -{"city": "ORO GRANDE", "loc": [-117.332733, 34.617832], "pop": 852, "state": "CA", "_id": "92368"} -{"city": "PHELAN", "loc": [-117.519604, 34.444901], "pop": 13508, "state": "CA", "_id": "92371"} -{"city": "PINON HILLS", "loc": [-117.640262, 34.442937], "pop": 1741, "state": "CA", "_id": "92372"} -{"city": "REDLANDS", "loc": [-117.180352, 34.039659], "pop": 29784, "state": "CA", "_id": "92373"} -{"city": "REDLANDS", "loc": [-117.167182, 34.064989], "pop": 36952, "state": "CA", "_id": "92374"} -{"city": "RIALTO", "loc": [-117.377133, 34.113155], "pop": 75341, "state": "CA", "_id": "92376"} -{"city": "SHOSHONE", "loc": [-116.264476, 35.899239], "pop": 353, "state": "CA", "_id": "92384"} -{"city": "TECOPA", "loc": [-115.829824, 35.897925], "pop": 48, "state": "CA", "_id": "92389"} -{"city": "SPRING VALLEY LA", "loc": [-117.319235, 34.503917], "pop": 51968, "state": "CA", "_id": "92392"} -{"city": "GEORGE AFB", "loc": [-117.363516, 34.577325], "pop": 6815, "state": "CA", "_id": "92394"} -{"city": "WRIGHTWOOD", "loc": [-117.6249, 34.362839], "pop": 4148, "state": "CA", "_id": "92397"} -{"city": "YUCAIPA", "loc": [-117.048925, 34.028197], "pop": 33298, "state": "CA", "_id": "92399"} -{"city": "SAN BERNARDINO", "loc": [-117.289753, 34.110521], "pop": 1193, "state": "CA", "_id": "92401"} -{"city": "SAN BERNARDINO", "loc": [-117.260572, 34.142577], "pop": 50792, "state": "CA", "_id": "92404"} -{"city": "MUSCOY", "loc": [-117.310765, 34.144101], "pop": 35583, "state": "CA", "_id": "92405"} -{"city": "SAN BERNARDINO", "loc": [-117.293697, 34.20928], "pop": 44927, "state": "CA", "_id": "92407"} -{"city": "SAN BERNARDINO", "loc": [-117.271059, 34.083127], "pop": 13378, "state": "CA", "_id": "92408"} -{"city": "SAN BERNARDINO", "loc": [-117.241291, 34.103685], "pop": 1746, "state": "CA", "_id": "92409"} -{"city": "SAN BERNARDINO", "loc": [-117.296789, 34.107729], "pop": 42522, "state": "CA", "_id": "92410"} -{"city": "SAN BERNARDINO", "loc": [-117.317158, 34.121414], "pop": 22994, "state": "CA", "_id": "92411"} -{"city": "RIVERSIDE", "loc": [-117.369421, 33.9924], "pop": 18478, "state": "CA", "_id": "92501"} -{"city": "RIVERSIDE", "loc": [-117.458862, 33.920808], "pop": 55552, "state": "CA", "_id": "92503"} -{"city": "RIVERSIDE", "loc": [-117.411948, 33.931458], "pop": 45308, "state": "CA", "_id": "92504"} -{"city": "RIVERSIDE", "loc": [-117.486687, 33.922769], "pop": 37420, "state": "CA", "_id": "92505"} -{"city": "RIVERSIDE", "loc": [-117.375696, 33.945485], "pop": 37294, "state": "CA", "_id": "92506"} -{"city": "RIVERSIDE", "loc": [-117.338874, 33.976086], "pop": 45844, "state": "CA", "_id": "92507"} -{"city": "RIVERSIDE", "loc": [-117.304264, 33.889676], "pop": 13582, "state": "CA", "_id": "92508"} -{"city": "RUBIDOUX", "loc": [-117.444896, 33.997355], "pop": 52456, "state": "CA", "_id": "92509"} -{"city": "LAKE ELSINORE", "loc": [-117.348535, 33.659816], "pop": 34619, "state": "CA", "_id": "92530"} -{"city": "LAKE ELSINORE", "loc": [-117.271278, 33.651662], "pop": 6796, "state": "CA", "_id": "92532"} -{"city": "AGUANGA", "loc": [-116.799693, 33.447306], "pop": 2309, "state": "CA", "_id": "92536"} -{"city": "ANZA", "loc": [-116.71355, 33.568812], "pop": 1860, "state": "CA", "_id": "92539"} -{"city": "HEMET", "loc": [-116.972974, 33.741613], "pop": 26985, "state": "CA", "_id": "92543"} -{"city": "HEMET", "loc": [-116.924306, 33.738978], "pop": 34483, "state": "CA", "_id": "92544"} -{"city": "HEMET", "loc": [-117.015078, 33.739857], "pop": 19513, "state": "CA", "_id": "92545"} -{"city": "HOMELAND", "loc": [-117.111845, 33.74528], "pop": 4290, "state": "CA", "_id": "92548"} -{"city": "IDYLLWILD", "loc": [-116.710665, 33.730433], "pop": 3975, "state": "CA", "_id": "92549"} -{"city": "MORENO VALLEY", "loc": [-117.235066, 33.915719], "pop": 71314, "state": "CA", "_id": "92553"} -{"city": "MORENO VALLEY", "loc": [-117.185105, 33.937659], "pop": 9784, "state": "CA", "_id": "92555"} -{"city": "MORENO VALLEY", "loc": [-117.245682, 33.955257], "pop": 37853, "state": "CA", "_id": "92557"} -{"city": "MOUNTAIN CENTER", "loc": [-116.581954, 33.531667], "pop": 1896, "state": "CA", "_id": "92561"} -{"city": "MURRIETA", "loc": [-117.273838, 33.563071], "pop": 1988, "state": "CA", "_id": "92562"} -{"city": "MURRIETA", "loc": [-117.178298, 33.56903], "pop": 22649, "state": "CA", "_id": "92563"} -{"city": "LAKEVIEW", "loc": [-117.118704, 33.807712], "pop": 6480, "state": "CA", "_id": "92567"} -{"city": "MEAD VALLEY", "loc": [-117.280005, 33.797535], "pop": 31870, "state": "CA", "_id": "92570"} -{"city": "PERRIS", "loc": [-117.217968, 33.810979], "pop": 12436, "state": "CA", "_id": "92571"} -{"city": "SAN JACINTO", "loc": [-116.981911, 33.788281], "pop": 3233, "state": "CA", "_id": "92582"} -{"city": "GILMAN HOT SPRIN", "loc": [-116.955201, 33.78604], "pop": 15506, "state": "CA", "_id": "92583"} -{"city": "MENIFEE", "loc": [-117.1743, 33.664744], "pop": 8347, "state": "CA", "_id": "92584"} -{"city": "ROMOLAND", "loc": [-117.171899, 33.744518], "pop": 5231, "state": "CA", "_id": "92585"} -{"city": "SUN CITY", "loc": [-117.196942, 33.704373], "pop": 15225, "state": "CA", "_id": "92586"} -{"city": "CANYON LAKE", "loc": [-117.252653, 33.688756], "pop": 12522, "state": "CA", "_id": "92587"} -{"city": "TEMECULA", "loc": [-117.182437, 33.490269], "pop": 2128, "state": "CA", "_id": "92590"} -{"city": "TEMECULA", "loc": [-117.128571, 33.521687], "pop": 13480, "state": "CA", "_id": "92591"} -{"city": "TEMECULA", "loc": [-117.095774, 33.498314], "pop": 15567, "state": "CA", "_id": "92592"} -{"city": "WILDOMAR", "loc": [-117.263953, 33.602115], "pop": 4758, "state": "CA", "_id": "92595"} -{"city": "WINCHESTER", "loc": [-117.088518, 33.624269], "pop": 1195, "state": "CA", "_id": "92596"} -{"city": "FOOTHILL RANCH", "loc": [-117.664995, 33.666822], "pop": 3294, "state": "CA", "_id": "92610"} -{"city": "BREA", "loc": [-117.886742, 33.922897], "pop": 45577, "state": "CA", "_id": "92621"} -{"city": "CAPISTRANO BEACH", "loc": [-117.662657, 33.459115], "pop": 7498, "state": "CA", "_id": "92624"} -{"city": "CORONA DEL MAR", "loc": [-117.874331, 33.602066], "pop": 5166, "state": "CA", "_id": "92625"} -{"city": "COSTA MESA", "loc": [-117.909623, 33.677711], "pop": 45411, "state": "CA", "_id": "92626"} -{"city": "COSTA MESA", "loc": [-117.917667, 33.647793], "pop": 52357, "state": "CA", "_id": "92627"} -{"city": "MONARCH BAY", "loc": [-117.700483, 33.476964], "pop": 25618, "state": "CA", "_id": "92629"} -{"city": "LAKE FOREST", "loc": [-117.68819, 33.640015], "pop": 51666, "state": "CA", "_id": "92630"} -{"city": "FULLERTON", "loc": [-117.89157, 33.880519], "pop": 28902, "state": "CA", "_id": "92631"} -{"city": "FULLERTON", "loc": [-117.928376, 33.865848], "pop": 21680, "state": "CA", "_id": "92632"} -{"city": "FULLERTON", "loc": [-117.961043, 33.873913], "pop": 42033, "state": "CA", "_id": "92633"} -{"city": "FULLERTON", "loc": [-117.927801, 33.901181], "pop": 10531, "state": "CA", "_id": "92635"} -{"city": "GARDEN GROVE", "loc": [-117.92906, 33.785826], "pop": 43908, "state": "CA", "_id": "92640"} -{"city": "GARDEN GROVE", "loc": [-117.975526, 33.786651], "pop": 29308, "state": "CA", "_id": "92641"} -{"city": "GARDEN GROVE", "loc": [-117.930193, 33.762641], "pop": 39473, "state": "CA", "_id": "92643"} -{"city": "GARDEN GROVE", "loc": [-117.96935, 33.765532], "pop": 20467, "state": "CA", "_id": "92644"} -{"city": "GARDEN GROVE", "loc": [-118.02639, 33.78324], "pop": 16398, "state": "CA", "_id": "92645"} -{"city": "HUNTINGTON BEACH", "loc": [-117.967771, 33.668448], "pop": 57915, "state": "CA", "_id": "92646"} -{"city": "HUNTINGTON BEACH", "loc": [-118.003035, 33.721018], "pop": 56565, "state": "CA", "_id": "92647"} -{"city": "HUNTINGTON BEACH", "loc": [-117.999012, 33.674577], "pop": 34997, "state": "CA", "_id": "92648"} -{"city": "HUNTINGTON BEACH", "loc": [-118.045142, 33.719111], "pop": 34065, "state": "CA", "_id": "92649"} -{"city": "LAGUNA NIGUEL", "loc": [-117.772351, 33.542927], "pop": 20315, "state": "CA", "_id": "92651"} -{"city": "LAGUNA HILLS", "loc": [-117.70854, 33.60028], "pop": 45283, "state": "CA", "_id": "92653"} -{"city": "MIDWAY CITY", "loc": [-117.983539, 33.744794], "pop": 6224, "state": "CA", "_id": "92655"} -{"city": "ALISO VIEJO", "loc": [-117.708906, 33.572367], "pop": 16455, "state": "CA", "_id": "92656"} -{"city": "NEWPORT BEACH", "loc": [-117.855317, 33.596382], "pop": 8254, "state": "CA", "_id": "92657"} -{"city": "NEWPORT BEACH", "loc": [-117.8757, 33.630027], "pop": 25390, "state": "CA", "_id": "92660"} -{"city": "NEWPORT BEACH", "loc": [-117.906237, 33.604429], "pop": 6123, "state": "CA", "_id": "92661"} -{"city": "NEWPORT BEACH", "loc": [-117.891732, 33.606459], "pop": 3472, "state": "CA", "_id": "92662"} -{"city": "NEWPORT BEACH", "loc": [-117.92788, 33.623084], "pop": 19826, "state": "CA", "_id": "92663"} -{"city": "ORANGE", "loc": [-117.844903, 33.83096], "pop": 16566, "state": "CA", "_id": "92665"} -{"city": "ORANGE", "loc": [-117.845461, 33.785258], "pop": 13811, "state": "CA", "_id": "92666"} -{"city": "VILLA PARK", "loc": [-117.828421, 33.81036], "pop": 40937, "state": "CA", "_id": "92667"} -{"city": "ORANGE", "loc": [-117.87532, 33.786481], "pop": 20139, "state": "CA", "_id": "92668"} -{"city": "ORANGE", "loc": [-117.800285, 33.791672], "pop": 31583, "state": "CA", "_id": "92669"} -{"city": "PLACENTIA", "loc": [-117.859837, 33.880323], "pop": 47174, "state": "CA", "_id": "92670"} -{"city": "SAN CLEMENTE", "loc": [-117.610139, 33.430809], "pop": 46719, "state": "CA", "_id": "92672"} -{"city": "MISSION VIEJO", "loc": [-117.657409, 33.511714], "pop": 28121, "state": "CA", "_id": "92675"} -{"city": "LAGUNA NIGUEL", "loc": [-117.705154, 33.522871], "pop": 40072, "state": "CA", "_id": "92677"} -{"city": "COTO DE CAZA", "loc": [-117.577709, 33.634576], "pop": 6067, "state": "CA", "_id": "92679"} -{"city": "TUSTIN", "loc": [-117.819193, 33.73713], "pop": 51150, "state": "CA", "_id": "92680"} -{"city": "WESTMINSTER", "loc": [-117.991312, 33.752756], "pop": 77965, "state": "CA", "_id": "92683"} -{"city": "YORBA LINDA", "loc": [-117.799619, 33.888361], "pop": 41141, "state": "CA", "_id": "92686"} -{"city": "YORBA LINDA", "loc": [-117.731162, 33.88238], "pop": 14429, "state": "CA", "_id": "92687"} -{"city": "RANCHO SANTA MAR", "loc": [-117.588388, 33.651822], "pop": 9489, "state": "CA", "_id": "92688"} -{"city": "MISSION VIEJO", "loc": [-117.664119, 33.617155], "pop": 48832, "state": "CA", "_id": "92691"} -{"city": "MISSION VIEJO", "loc": [-117.64245, 33.610872], "pop": 30777, "state": "CA", "_id": "92692"} -{"city": "SANTA ANA", "loc": [-117.857665, 33.75016], "pop": 63544, "state": "CA", "_id": "92701"} -{"city": "SANTA ANA", "loc": [-117.899589, 33.746613], "pop": 63104, "state": "CA", "_id": "92703"} -{"city": "SANTA ANA", "loc": [-117.904683, 33.726513], "pop": 77151, "state": "CA", "_id": "92704"} -{"city": "COWAN HEIGHTS", "loc": [-117.768902, 33.74866], "pop": 37045, "state": "CA", "_id": "92705"} -{"city": "SANTA ANA", "loc": [-117.881791, 33.764434], "pop": 30673, "state": "CA", "_id": "92706"} -{"city": "SANTA ANA HEIGHT", "loc": [-117.870346, 33.715938], "pop": 56450, "state": "CA", "_id": "92707"} -{"city": "FOUNTAIN VALLEY", "loc": [-117.952318, 33.710762], "pop": 54803, "state": "CA", "_id": "92708"} -{"city": "EL TORO MARINE C", "loc": [-117.715018, 33.681287], "pop": 8078, "state": "CA", "_id": "92709"} -{"city": "IRVINE", "loc": [-117.798928, 33.68764], "pop": 60654, "state": "CA", "_id": "92714"} -{"city": "IRVINE", "loc": [-117.821251, 33.650884], "pop": 30690, "state": "CA", "_id": "92715"} -{"city": "IRVINE", "loc": [-117.711476, 33.658179], "pop": 1, "state": "CA", "_id": "92718"} -{"city": "IRVINE", "loc": [-117.765533, 33.707495], "pop": 23474, "state": "CA", "_id": "92720"} -{"city": "ANAHEIM", "loc": [-117.954035, 33.842679], "pop": 47392, "state": "CA", "_id": "92801"} -{"city": "ANAHEIM", "loc": [-117.92219, 33.806909], "pop": 36262, "state": "CA", "_id": "92802"} -{"city": "ANAHEIM", "loc": [-117.974985, 33.81908], "pop": 63622, "state": "CA", "_id": "92804"} -{"city": "ANAHEIM", "loc": [-117.906263, 33.835332], "pop": 55489, "state": "CA", "_id": "92805"} -{"city": "ANAHEIM", "loc": [-117.875928, 33.837344], "pop": 27945, "state": "CA", "_id": "92806"} -{"city": "ANAHEIM", "loc": [-117.787657, 33.851583], "pop": 35411, "state": "CA", "_id": "92807"} -{"city": "ANAHEIM", "loc": [-117.748445, 33.857569], "pop": 6206, "state": "CA", "_id": "92808"} -{"city": "SAN BUENAVENTURA", "loc": [-119.28882, 34.290531], "pop": 31453, "state": "CA", "_id": "93001"} -{"city": "SAN BUENAVENTURA", "loc": [-119.2214, 34.270568], "pop": 44627, "state": "CA", "_id": "93003"} -{"city": "SAN BUENAVENTURA", "loc": [-119.168727, 34.278091], "pop": 23430, "state": "CA", "_id": "93004"} -{"city": "CAMARILLO", "loc": [-119.046361, 34.231328], "pop": 40173, "state": "CA", "_id": "93010"} -{"city": "CAMARILLO", "loc": [-118.986648, 34.22179], "pop": 23489, "state": "CA", "_id": "93012"} -{"city": "CARPINTERIA", "loc": [-119.518257, 34.403589], "pop": 16591, "state": "CA", "_id": "93013"} -{"city": "BARDSDALE", "loc": [-118.904071, 34.402557], "pop": 15311, "state": "CA", "_id": "93015"} -{"city": "MOORPARK", "loc": [-118.877139, 34.278421], "pop": 27011, "state": "CA", "_id": "93021"} -{"city": "OAK VIEW", "loc": [-119.298168, 34.402021], "pop": 5611, "state": "CA", "_id": "93022"} -{"city": "OJAI", "loc": [-119.256477, 34.44512], "pop": 22778, "state": "CA", "_id": "93023"} -{"city": "OXNARD", "loc": [-119.174952, 34.214142], "pop": 66240, "state": "CA", "_id": "93030"} -{"city": "OXNARD", "loc": [-119.171732, 34.168505], "pop": 66043, "state": "CA", "_id": "93033"} -{"city": "OXNARD", "loc": [-119.215975, 34.182177], "pop": 23778, "state": "CA", "_id": "93035"} -{"city": "PORT HUENEME", "loc": [-119.197317, 34.162572], "pop": 17337, "state": "CA", "_id": "93041"} -{"city": "POINT MUGU NAWC", "loc": [-119.09931, 34.123432], "pop": 1707, "state": "CA", "_id": "93042"} -{"city": "PORT HUENEME CBC", "loc": [-119.206008, 34.16212], "pop": 3389, "state": "CA", "_id": "93043"} -{"city": "SANTA PAULA", "loc": [-119.071328, 34.354718], "pop": 28319, "state": "CA", "_id": "93060"} -{"city": "SANTA SUSANA", "loc": [-118.699229, 34.279202], "pop": 47637, "state": "CA", "_id": "93063"} -{"city": "SIMI VALLEY", "loc": [-118.765349, 34.265589], "pop": 55528, "state": "CA", "_id": "93065"} -{"city": "SOMIS", "loc": [-119.011537, 34.279753], "pop": 3025, "state": "CA", "_id": "93066"} -{"city": "SUMMERLAND", "loc": [-119.596016, 34.424541], "pop": 1330, "state": "CA", "_id": "93067"} -{"city": "SANTA BARBARA", "loc": [-119.70782, 34.419668], "pop": 29235, "state": "CA", "_id": "93101"} -{"city": "SANTA BARBARA", "loc": [-119.683275, 34.429065], "pop": 18199, "state": "CA", "_id": "93103"} -{"city": "SANTA BARBARA", "loc": [-119.728538, 34.436915], "pop": 23284, "state": "CA", "_id": "93105"} -{"city": "MONTECITO", "loc": [-119.64255, 34.434258], "pop": 12923, "state": "CA", "_id": "93108"} -{"city": "SANTA BARBARA", "loc": [-119.7194, 34.403848], "pop": 11089, "state": "CA", "_id": "93109"} -{"city": "SANTA BARBARA", "loc": [-119.764668, 34.441814], "pop": 15352, "state": "CA", "_id": "93110"} -{"city": "SANTA BARBARA", "loc": [-119.802509, 34.445262], "pop": 17689, "state": "CA", "_id": "93111"} -{"city": "GOLETA", "loc": [-119.861245, 34.429631], "pop": 45988, "state": "CA", "_id": "93117"} -{"city": "ARMONA", "loc": [-119.705279, 36.309459], "pop": 752, "state": "CA", "_id": "93202"} -{"city": "ARVIN", "loc": [-118.8336, 35.196629], "pop": 10613, "state": "CA", "_id": "93203"} -{"city": "AVENAL", "loc": [-120.122716, 35.987667], "pop": 9882, "state": "CA", "_id": "93204"} -{"city": "BODFISH", "loc": [-118.484656, 35.587046], "pop": 1407, "state": "CA", "_id": "93205"} -{"city": "BUTTONWILLOW", "loc": [-119.465926, 35.403268], "pop": 1975, "state": "CA", "_id": "93206"} -{"city": "CALIFORNIA HOT S", "loc": [-118.646317, 35.892422], "pop": 436, "state": "CA", "_id": "93207"} -{"city": "COALINGA", "loc": [-120.348928, 36.162435], "pop": 9579, "state": "CA", "_id": "93210"} -{"city": "CORCORAN", "loc": [-119.560665, 36.086455], "pop": 16228, "state": "CA", "_id": "93212"} -{"city": "CUYAMA", "loc": [-119.661339, 34.933694], "pop": 808, "state": "CA", "_id": "93214"} -{"city": "DELANO", "loc": [-119.24594, 35.771511], "pop": 23803, "state": "CA", "_id": "93215"} -{"city": "DI GIORGIO", "loc": [-118.846755, 35.247604], "pop": 258, "state": "CA", "_id": "93217"} -{"city": "EARLIMART", "loc": [-119.253406, 35.854195], "pop": 11963, "state": "CA", "_id": "93219"} -{"city": "EXETER", "loc": [-119.12928, 36.304055], "pop": 11088, "state": "CA", "_id": "93221"} -{"city": "FARMERSVILLE", "loc": [-119.205357, 36.300169], "pop": 6432, "state": "CA", "_id": "93223"} -{"city": "FELLOWS", "loc": [-119.564757, 35.202579], "pop": 521, "state": "CA", "_id": "93224"} -{"city": "FRAZIER PARK", "loc": [-119.035488, 34.826463], "pop": 5257, "state": "CA", "_id": "93225"} -{"city": "GLENNVILLE", "loc": [-118.71693, 35.737677], "pop": 346, "state": "CA", "_id": "93226"} -{"city": "HANFORD", "loc": [-119.649094, 36.331419], "pop": 44686, "state": "CA", "_id": "93230"} -{"city": "HURON", "loc": [-120.101964, 36.237144], "pop": 7050, "state": "CA", "_id": "93234"} -{"city": "IVANHOE", "loc": [-119.218884, 36.385622], "pop": 3326, "state": "CA", "_id": "93235"} -{"city": "KERNVILLE", "loc": [-118.404723, 35.755005], "pop": 812, "state": "CA", "_id": "93238"} -{"city": "KETTLEMAN CITY", "loc": [-119.964361, 36.021501], "pop": 1762, "state": "CA", "_id": "93239"} -{"city": "MOUNTAIN MESA", "loc": [-118.441256, 35.617889], "pop": 6285, "state": "CA", "_id": "93240"} -{"city": "LAMONT", "loc": [-118.912419, 35.257059], "pop": 13471, "state": "CA", "_id": "93241"} -{"city": "LATON", "loc": [-119.715565, 36.437834], "pop": 2860, "state": "CA", "_id": "93242"} -{"city": "GORMAN", "loc": [-118.879126, 34.828862], "pop": 1103, "state": "CA", "_id": "93243"} -{"city": "LEMONCOVE", "loc": [-119.004986, 36.396084], "pop": 64, "state": "CA", "_id": "93244"} -{"city": "LEMOORE NAVAL AI", "loc": [-119.831017, 36.309535], "pop": 26170, "state": "CA", "_id": "93245"} -{"city": "LINDSAY", "loc": [-119.088427, 36.209551], "pop": 12983, "state": "CA", "_id": "93247"} -{"city": "LOST HILLS", "loc": [-119.721573, 35.613111], "pop": 2373, "state": "CA", "_id": "93249"} -{"city": "MC FARLAND", "loc": [-119.227156, 35.675779], "pop": 8494, "state": "CA", "_id": "93250"} -{"city": "MC KITTRICK", "loc": [-119.636627, 35.303097], "pop": 616, "state": "CA", "_id": "93251"} -{"city": "MARICOPA", "loc": [-119.407661, 35.038353], "pop": 1571, "state": "CA", "_id": "93252"} -{"city": "NEW CUYAMA", "loc": [-119.823806, 34.996709], "pop": 80, "state": "CA", "_id": "93254"} -{"city": "ONYX", "loc": [-118.190227, 35.713584], "pop": 380, "state": "CA", "_id": "93255"} -{"city": "PIXLEY", "loc": [-119.256427, 35.955286], "pop": 4768, "state": "CA", "_id": "93256"} -{"city": "PORTERVILLE", "loc": [-119.031549, 36.068636], "pop": 54599, "state": "CA", "_id": "93257"} -{"city": "POSEY", "loc": [-118.664286, 35.813496], "pop": 253, "state": "CA", "_id": "93260"} -{"city": "GIANT FOREST", "loc": [-118.772271, 36.573878], "pop": 132, "state": "CA", "_id": "93262"} -{"city": "SHAFTER", "loc": [-119.280075, 35.496994], "pop": 12270, "state": "CA", "_id": "93263"} -{"city": "SPRINGVILLE", "loc": [-118.796059, 36.136314], "pop": 3374, "state": "CA", "_id": "93265"} -{"city": "STRATFORD", "loc": [-119.823564, 36.178976], "pop": 1135, "state": "CA", "_id": "93266"} -{"city": "STRATHMORE", "loc": [-119.079163, 36.147237], "pop": 4774, "state": "CA", "_id": "93267"} -{"city": "TAFT", "loc": [-119.455674, 35.148164], "pop": 15046, "state": "CA", "_id": "93268"} -{"city": "TERRA BELLA", "loc": [-119.031239, 35.95698], "pop": 5066, "state": "CA", "_id": "93270"} -{"city": "THREE RIVERS", "loc": [-118.88754, 36.437686], "pop": 2245, "state": "CA", "_id": "93271"} -{"city": "TIPTON", "loc": [-119.30781, 36.054567], "pop": 2365, "state": "CA", "_id": "93272"} -{"city": "TULARE", "loc": [-119.33802, 36.202155], "pop": 45567, "state": "CA", "_id": "93274"} -{"city": "TUPMAN", "loc": [-119.341994, 35.288547], "pop": 280, "state": "CA", "_id": "93276"} -{"city": "VISALIA", "loc": [-119.306471, 36.311379], "pop": 51620, "state": "CA", "_id": "93277"} -{"city": "POND", "loc": [-119.344728, 35.593375], "pop": 13589, "state": "CA", "_id": "93280"} -{"city": "WELDON", "loc": [-118.285856, 35.639076], "pop": 2049, "state": "CA", "_id": "93283"} -{"city": "WOFFORD HEIGHTS", "loc": [-118.455877, 35.724556], "pop": 3480, "state": "CA", "_id": "93285"} -{"city": "WOODLAKE", "loc": [-119.091764, 36.431334], "pop": 8421, "state": "CA", "_id": "93286"} -{"city": "WOODY", "loc": [-118.843872, 35.70681], "pop": 72, "state": "CA", "_id": "93287"} -{"city": "VISALIA", "loc": [-119.301029, 36.355108], "pop": 46656, "state": "CA", "_id": "93291"} -{"city": "BAKERSFIELD", "loc": [-119.017063, 35.386611], "pop": 12822, "state": "CA", "_id": "93301"} -{"city": "BAKERSFIELD", "loc": [-119.021793, 35.339581], "pop": 41870, "state": "CA", "_id": "93304"} -{"city": "COLLEGE HEIGHTS", "loc": [-118.982042, 35.387772], "pop": 34046, "state": "CA", "_id": "93305"} -{"city": "BAKERSFIELD", "loc": [-118.939104, 35.386697], "pop": 46699, "state": "CA", "_id": "93306"} -{"city": "BAKERSFIELD", "loc": [-118.983851, 35.327484], "pop": 50585, "state": "CA", "_id": "93307"} -{"city": "BAKERSFIELD", "loc": [-119.043319, 35.424395], "pop": 39454, "state": "CA", "_id": "93308"} -{"city": "BAKERSFIELD", "loc": [-119.062713, 35.33839], "pop": 58179, "state": "CA", "_id": "93309"} -{"city": "BAKERSFIELD", "loc": [-119.105647, 35.303891], "pop": 10321, "state": "CA", "_id": "93311"} -{"city": "GREENACRES", "loc": [-119.15014, 35.382082], "pop": 15935, "state": "CA", "_id": "93312"} -{"city": "BAKERSFIELD", "loc": [-119.050936, 35.297391], "pop": 11417, "state": "CA", "_id": "93313"} -{"city": "SAN LUIS OBISPO", "loc": [-120.650933, 35.263453], "pop": 24638, "state": "CA", "_id": "93401"} -{"city": "LOS OSOS", "loc": [-120.833261, 35.317203], "pop": 14648, "state": "CA", "_id": "93402"} -{"city": "SAN LUIS OBISPO", "loc": [-120.681724, 35.290058], "pop": 31976, "state": "CA", "_id": "93405"} -{"city": "HALCYON", "loc": [-120.57289, 35.11449], "pop": 21992, "state": "CA", "_id": "93420"} -{"city": "ATASCADERO", "loc": [-120.663838, 35.475439], "pop": 27720, "state": "CA", "_id": "93422"} -{"city": "BRADLEY", "loc": [-120.972793, 35.809255], "pop": 862, "state": "CA", "_id": "93426"} -{"city": "BUELLTON", "loc": [-120.192233, 34.62093], "pop": 3883, "state": "CA", "_id": "93427"} -{"city": "CAMBRIA", "loc": [-121.084029, 35.556568], "pop": 5635, "state": "CA", "_id": "93428"} -{"city": "CAYUCOS", "loc": [-120.890791, 35.444606], "pop": 3384, "state": "CA", "_id": "93430"} -{"city": "CHOLAME", "loc": [-120.194827, 35.543847], "pop": 206, "state": "CA", "_id": "93431"} -{"city": "CRESTON", "loc": [-120.554238, 35.491543], "pop": 726, "state": "CA", "_id": "93432"} -{"city": "GROVER BEACH", "loc": [-120.617348, 35.120969], "pop": 11790, "state": "CA", "_id": "93433"} -{"city": "GUADALUPE", "loc": [-120.570329, 34.959989], "pop": 6064, "state": "CA", "_id": "93434"} -{"city": "LOMPOC", "loc": [-120.450605, 34.658349], "pop": 49960, "state": "CA", "_id": "93436"} -{"city": "LOMPOC", "loc": [-120.517096, 34.753215], "pop": 9846, "state": "CA", "_id": "93437"} -{"city": "MORRO BAY", "loc": [-120.844745, 35.37953], "pop": 10475, "state": "CA", "_id": "93442"} -{"city": "NIPOMO", "loc": [-120.489413, 35.029806], "pop": 11070, "state": "CA", "_id": "93444"} -{"city": "OCEANO", "loc": [-120.608044, 35.10187], "pop": 6249, "state": "CA", "_id": "93445"} -{"city": "ADELAIDE", "loc": [-120.670676, 35.635248], "pop": 29255, "state": "CA", "_id": "93446"} -{"city": "SHELL BEACH", "loc": [-120.651788, 35.149212], "pop": 7474, "state": "CA", "_id": "93449"} -{"city": "SAN ARDO", "loc": [-120.861191, 35.985685], "pop": 1684, "state": "CA", "_id": "93450"} -{"city": "PARKFIELD", "loc": [-120.696532, 35.753209], "pop": 1218, "state": "CA", "_id": "93451"} -{"city": "SAN SIMEON", "loc": [-121.144033, 35.666815], "pop": 500, "state": "CA", "_id": "93452"} -{"city": "CALIFORNIA VALLE", "loc": [-120.3202, 35.341254], "pop": 1237, "state": "CA", "_id": "93453"} -{"city": "SANTA MARIA", "loc": [-120.43245, 34.954538], "pop": 60187, "state": "CA", "_id": "93454"} -{"city": "ORCUTT", "loc": [-120.429128, 34.879786], "pop": 32891, "state": "CA", "_id": "93455"} -{"city": "SANTA YNEZ", "loc": [-120.071332, 34.623966], "pop": 5659, "state": "CA", "_id": "93460"} -{"city": "SHANDON", "loc": [-120.372047, 35.651273], "pop": 908, "state": "CA", "_id": "93461"} -{"city": "BALLARD", "loc": [-120.129286, 34.609931], "pop": 8327, "state": "CA", "_id": "93463"} -{"city": "TEMPLETON", "loc": [-120.710737, 35.555082], "pop": 5795, "state": "CA", "_id": "93465"} -{"city": "MOJAVE", "loc": [-118.173475, 35.047767], "pop": 4774, "state": "CA", "_id": "93501"} -{"city": "CALIFORNIA CITY", "loc": [-117.965142, 35.127783], "pop": 6086, "state": "CA", "_id": "93505"} -{"city": "ACTON", "loc": [-118.195929, 34.483541], "pop": 6139, "state": "CA", "_id": "93510"} -{"city": "BENTON", "loc": [-118.498526, 37.798099], "pop": 241, "state": "CA", "_id": "93512"} -{"city": "BIG PINE", "loc": [-118.291597, 37.167857], "pop": 1642, "state": "CA", "_id": "93513"} -{"city": "TOMS PLACE", "loc": [-118.44156, 37.386301], "pop": 14072, "state": "CA", "_id": "93514"} -{"city": "BORON", "loc": [-117.662921, 35.003748], "pop": 2904, "state": "CA", "_id": "93516"} -{"city": "BRIDGEPORT", "loc": [-119.208025, 38.256601], "pop": 697, "state": "CA", "_id": "93517"} -{"city": "HAVILAH", "loc": [-118.410264, 35.356798], "pop": 899, "state": "CA", "_id": "93518"} -{"city": "CANTIL", "loc": [-117.993868, 35.282558], "pop": 222, "state": "CA", "_id": "93519"} -{"city": "NORTH EDWARDS", "loc": [-117.915384, 34.930507], "pop": 8996, "state": "CA", "_id": "93523"} -{"city": "INDEPENDENCE", "loc": [-118.204808, 36.839578], "pop": 889, "state": "CA", "_id": "93526"} -{"city": "PEARSONVILLE", "loc": [-117.834844, 35.674498], "pop": 2633, "state": "CA", "_id": "93527"} -{"city": "JOHANNESBURG", "loc": [-117.637325, 35.370655], "pop": 306, "state": "CA", "_id": "93528"} -{"city": "JUNE LAKE", "loc": [-119.082492, 37.777324], "pop": 609, "state": "CA", "_id": "93529"} -{"city": "KEENE", "loc": [-118.607563, 35.237502], "pop": 455, "state": "CA", "_id": "93531"} -{"city": "ELIZABETH LAKE", "loc": [-118.444719, 34.668297], "pop": 2337, "state": "CA", "_id": "93532"} -{"city": "LANCASTER", "loc": [-118.149129, 34.690888], "pop": 32929, "state": "CA", "_id": "93534"} -{"city": "HI VISTA", "loc": [-118.063245, 34.684751], "pop": 49751, "state": "CA", "_id": "93535"} -{"city": "QUARTZ HILL", "loc": [-118.213336, 34.673619], "pop": 39987, "state": "CA", "_id": "93536"} -{"city": "LEE VINING", "loc": [-119.123413, 37.988988], "pop": 415, "state": "CA", "_id": "93541"} -{"city": "JUNIPER HILLS", "loc": [-117.957405, 34.547372], "pop": 10046, "state": "CA", "_id": "93543"} -{"city": "CRYSTALAIRE", "loc": [-117.798841, 34.495914], "pop": 1204, "state": "CA", "_id": "93544"} -{"city": "LONE PINE", "loc": [-118.057824, 36.579781], "pop": 2257, "state": "CA", "_id": "93545"} -{"city": "CROWLEY LAKE", "loc": [-118.976383, 37.642361], "pop": 4832, "state": "CA", "_id": "93546"} -{"city": "LAKE LOS ANGELES", "loc": [-118.061306, 34.571483], "pop": 71024, "state": "CA", "_id": "93550"} -{"city": "LEONA VALLEY", "loc": [-118.181207, 34.601404], "pop": 20768, "state": "CA", "_id": "93551"} -{"city": "JUNIPER HILLS", "loc": [-117.902893, 34.491124], "pop": 1106, "state": "CA", "_id": "93553"} -{"city": "RANDSBURG", "loc": [-117.726396, 35.352176], "pop": 190, "state": "CA", "_id": "93554"} -{"city": "CHINA LAKE NWC", "loc": [-117.679733, 35.631376], "pop": 34246, "state": "CA", "_id": "93555"} -{"city": "WILLOW SPRINGS", "loc": [-118.19636, 34.863117], "pop": 9898, "state": "CA", "_id": "93560"} -{"city": "BEAR VALLEY SPRI", "loc": [-118.522227, 35.129776], "pop": 24466, "state": "CA", "_id": "93561"} -{"city": "ARGUS", "loc": [-117.382992, 35.760817], "pop": 3189, "state": "CA", "_id": "93562"} -{"city": "VALYERMO", "loc": [-117.8271, 34.339014], "pop": 1456, "state": "CA", "_id": "93563"} -{"city": "AHWAHNEE", "loc": [-119.723251, 37.407631], "pop": 327, "state": "CA", "_id": "93601"} -{"city": "AUBERRY", "loc": [-119.457202, 37.072635], "pop": 3179, "state": "CA", "_id": "93602"} -{"city": "BASS LAKE", "loc": [-119.556839, 37.324359], "pop": 628, "state": "CA", "_id": "93604"} -{"city": "CANTUA CREEK", "loc": [-120.335252, 36.492123], "pop": 1622, "state": "CA", "_id": "93608"} -{"city": "CARUTHERS", "loc": [-119.844581, 36.535847], "pop": 4558, "state": "CA", "_id": "93609"} -{"city": "CHOWCHILLA", "loc": [-120.269077, 37.101371], "pop": 10745, "state": "CA", "_id": "93610"} -{"city": "CLOVIS", "loc": [-119.689757, 36.823146], "pop": 59963, "state": "CA", "_id": "93612"} -{"city": "COARSEGOLD", "loc": [-119.745545, 37.221378], "pop": 6950, "state": "CA", "_id": "93614"} -{"city": "CUTLER", "loc": [-119.287023, 36.524266], "pop": 4901, "state": "CA", "_id": "93615"} -{"city": "DEL REY", "loc": [-119.59291, 36.654306], "pop": 1923, "state": "CA", "_id": "93616"} -{"city": "DINUBA", "loc": [-119.39087, 36.534931], "pop": 20012, "state": "CA", "_id": "93618"} -{"city": "DOS PALOS", "loc": [-120.633348, 37.00253], "pop": 9311, "state": "CA", "_id": "93620"} -{"city": "DUNLAP", "loc": [-119.089931, 36.744635], "pop": 94, "state": "CA", "_id": "93621"} -{"city": "FIREBAUGH", "loc": [-120.470048, 36.8651], "pop": 7435, "state": "CA", "_id": "93622"} -{"city": "FISH CAMP", "loc": [-119.642005, 37.51692], "pop": 132, "state": "CA", "_id": "93623"} -{"city": "FOWLER", "loc": [-119.671025, 36.628153], "pop": 5290, "state": "CA", "_id": "93625"} -{"city": "FRIANT", "loc": [-119.696501, 37.002416], "pop": 871, "state": "CA", "_id": "93626"} -{"city": "HELM", "loc": [-120.093598, 36.499231], "pop": 832, "state": "CA", "_id": "93627"} -{"city": "KERMAN", "loc": [-120.072444, 36.730576], "pop": 11224, "state": "CA", "_id": "93630"} -{"city": "KINGSBURG", "loc": [-119.543298, 36.508047], "pop": 12263, "state": "CA", "_id": "93631"} -{"city": "KINGS CANYON NAT", "loc": [-119.068201, 36.780601], "pop": 472, "state": "CA", "_id": "93633"} -{"city": "LOS BANOS", "loc": [-120.854387, 37.06266], "pop": 18199, "state": "CA", "_id": "93635"} -{"city": "MADERA", "loc": [-120.081966, 36.94026], "pop": 20440, "state": "CA", "_id": "93637"} -{"city": "MADERA", "loc": [-120.012778, 36.968726], "pop": 36525, "state": "CA", "_id": "93638"} -{"city": "MENDOTA", "loc": [-120.409287, 36.742365], "pop": 8839, "state": "CA", "_id": "93640"} -{"city": "MIRAMONTE", "loc": [-119.047718, 36.68938], "pop": 607, "state": "CA", "_id": "93641"} -{"city": "NORTH FORK", "loc": [-119.514324, 37.212531], "pop": 2541, "state": "CA", "_id": "93643"} -{"city": "OAKHURST", "loc": [-119.644854, 37.347561], "pop": 8190, "state": "CA", "_id": "93644"} -{"city": "O NEALS", "loc": [-119.745369, 37.086874], "pop": 24, "state": "CA", "_id": "93645"} -{"city": "ORANGE COVE", "loc": [-119.313502, 36.624283], "pop": 6374, "state": "CA", "_id": "93646"} -{"city": "OROSI", "loc": [-119.281522, 36.546368], "pop": 7545, "state": "CA", "_id": "93647"} -{"city": "PARLIER", "loc": [-119.537482, 36.610265], "pop": 9076, "state": "CA", "_id": "93648"} -{"city": "PINEDALE", "loc": [-119.800359, 36.841107], "pop": 4164, "state": "CA", "_id": "93650"} -{"city": "PRATHER", "loc": [-119.526771, 36.993799], "pop": 1446, "state": "CA", "_id": "93651"} -{"city": "RAISIN", "loc": [-119.903158, 36.598928], "pop": 381, "state": "CA", "_id": "93652"} -{"city": "RAYMOND", "loc": [-119.876567, 37.27898], "pop": 491, "state": "CA", "_id": "93653"} -{"city": "REEDLEY", "loc": [-119.437785, 36.604406], "pop": 22370, "state": "CA", "_id": "93654"} -{"city": "RIVERDALE", "loc": [-119.871953, 36.429525], "pop": 4386, "state": "CA", "_id": "93656"} -{"city": "SANGER", "loc": [-119.547796, 36.7243], "pop": 27201, "state": "CA", "_id": "93657"} -{"city": "SAN JOAQUIN", "loc": [-120.188934, 36.605869], "pop": 2727, "state": "CA", "_id": "93660"} -{"city": "SELMA", "loc": [-119.617026, 36.569524], "pop": 21798, "state": "CA", "_id": "93662"} -{"city": "SHAVER LAKE", "loc": [-119.273031, 37.139695], "pop": 925, "state": "CA", "_id": "93664"} -{"city": "TOLLHOUSE", "loc": [-119.391415, 36.99434], "pop": 1820, "state": "CA", "_id": "93667"} -{"city": "TRANQUILLITY", "loc": [-120.261655, 36.658376], "pop": 1297, "state": "CA", "_id": "93668"} -{"city": "WISHON", "loc": [-119.557014, 37.281028], "pop": 474, "state": "CA", "_id": "93669"} -{"city": "SQUAW VALLEY", "loc": [-119.181449, 36.707146], "pop": 2731, "state": "CA", "_id": "93675"} -{"city": "FRESNO", "loc": [-119.786705, 36.748727], "pop": 15024, "state": "CA", "_id": "93701"} -{"city": "FRESNO", "loc": [-119.753215, 36.739954], "pop": 44477, "state": "CA", "_id": "93702"} -{"city": "FRESNO", "loc": [-119.759401, 36.768445], "pop": 30457, "state": "CA", "_id": "93703"} -{"city": "FIG GARDEN VILLA", "loc": [-119.799745, 36.798781], "pop": 26496, "state": "CA", "_id": "93704"} -{"city": "FRESNO", "loc": [-119.828617, 36.786285], "pop": 34114, "state": "CA", "_id": "93705"} -{"city": "EASTON", "loc": [-119.820408, 36.700589], "pop": 33682, "state": "CA", "_id": "93706"} -{"city": "FRESNO", "loc": [-119.76205, 36.823643], "pop": 29719, "state": "CA", "_id": "93710"} -{"city": "FRESNO", "loc": [-119.831896, 36.830297], "pop": 29809, "state": "CA", "_id": "93711"} -{"city": "FRESNO", "loc": [-119.765522, 36.857944], "pop": 21498, "state": "CA", "_id": "93720"} -{"city": "FRESNO", "loc": [-119.784273, 36.737714], "pop": 6156, "state": "CA", "_id": "93721"} -{"city": "FRESNO", "loc": [-119.880119, 36.791779], "pop": 33523, "state": "CA", "_id": "93722"} -{"city": "CALWA", "loc": [-119.742477, 36.675312], "pop": 19698, "state": "CA", "_id": "93725"} -{"city": "FRESNO", "loc": [-119.760445, 36.794943], "pop": 36325, "state": "CA", "_id": "93726"} -{"city": "FRESNO", "loc": [-119.706055, 36.752796], "pop": 51417, "state": "CA", "_id": "93727"} -{"city": "FRESNO", "loc": [-119.811314, 36.758095], "pop": 15386, "state": "CA", "_id": "93728"} -{"city": "SALINAS", "loc": [-121.659589, 36.667693], "pop": 25605, "state": "CA", "_id": "93901"} -{"city": "SALINAS", "loc": [-121.617606, 36.681143], "pop": 41956, "state": "CA", "_id": "93905"} -{"city": "SALINAS", "loc": [-121.643805, 36.710339], "pop": 39534, "state": "CA", "_id": "93906"} -{"city": "PRUNEDALE", "loc": [-121.665588, 36.765385], "pop": 21061, "state": "CA", "_id": "93907"} -{"city": "SALINAS", "loc": [-121.672861, 36.601122], "pop": 15610, "state": "CA", "_id": "93908"} -{"city": "BIG SUR", "loc": [-121.700897, 36.245798], "pop": 1669, "state": "CA", "_id": "93920"} -{"city": "CARMEL", "loc": [-121.894875, 36.545693], "pop": 15293, "state": "CA", "_id": "93923"} -{"city": "CARMEL VALLEY", "loc": [-121.724356, 36.478709], "pop": 6066, "state": "CA", "_id": "93924"} -{"city": "CHUALAR", "loc": [-121.431964, 36.595042], "pop": 12, "state": "CA", "_id": "93925"} -{"city": "GONZALES", "loc": [-121.410347, 36.490038], "pop": 12842, "state": "CA", "_id": "93926"} -{"city": "GREENFIELD", "loc": [-121.24507, 36.320178], "pop": 8728, "state": "CA", "_id": "93927"} -{"city": "KING CITY", "loc": [-121.127329, 36.202776], "pop": 11299, "state": "CA", "_id": "93930"} -{"city": "LOCKWOOD", "loc": [-121.205946, 35.989287], "pop": 939, "state": "CA", "_id": "93932"} -{"city": "MARINA", "loc": [-121.793383, 36.684922], "pop": 16973, "state": "CA", "_id": "93933"} -{"city": "DEL REY OAKS", "loc": [-121.8848, 36.595642], "pop": 35326, "state": "CA", "_id": "93940"} -{"city": "FORT ORD", "loc": [-121.804999, 36.644627], "pop": 25009, "state": "CA", "_id": "93941"} -{"city": "PACIFIC GROVE", "loc": [-121.921957, 36.616737], "pop": 16040, "state": "CA", "_id": "93950"} -{"city": "PEBBLE BEACH", "loc": [-121.942044, 36.590735], "pop": 5061, "state": "CA", "_id": "93953"} -{"city": "SAND CITY", "loc": [-121.835724, 36.609208], "pop": 23514, "state": "CA", "_id": "93955"} -{"city": "SOLEDAD", "loc": [-121.324286, 36.41964], "pop": 9046, "state": "CA", "_id": "93960"} -{"city": "BELMONT", "loc": [-122.292671, 37.517433], "pop": 24960, "state": "CA", "_id": "94002"} -{"city": "BRISBANE", "loc": [-122.400118, 37.681104], "pop": 2952, "state": "CA", "_id": "94005"} -{"city": "HILLSBOROUGH", "loc": [-122.362952, 37.575884], "pop": 38444, "state": "CA", "_id": "94010"} -{"city": "COLMA", "loc": [-122.452679, 37.698187], "pop": 40406, "state": "CA", "_id": "94014"} -{"city": "DALY CITY", "loc": [-122.478015, 37.678696], "pop": 57354, "state": "CA", "_id": "94015"} -{"city": "HALF MOON BAY", "loc": [-122.445929, 37.479057], "pop": 14073, "state": "CA", "_id": "94019"} -{"city": "LA HONDA", "loc": [-122.293889, 37.272285], "pop": 1557, "state": "CA", "_id": "94020"} -{"city": "LOMA MAR", "loc": [-122.281996, 37.254437], "pop": 237, "state": "CA", "_id": "94021"} -{"city": "LOS ALTOS", "loc": [-122.125754, 37.381432], "pop": 17366, "state": "CA", "_id": "94022"} -{"city": "LOS ALTOS", "loc": [-122.086205, 37.354745], "pop": 20795, "state": "CA", "_id": "94024"} -{"city": "WEST MENLO PARK", "loc": [-122.179136, 37.453401], "pop": 38383, "state": "CA", "_id": "94025"} -{"city": "ATHERTON", "loc": [-122.200198, 37.456255], "pop": 7312, "state": "CA", "_id": "94027"} -{"city": "LADERA", "loc": [-122.208131, 37.378859], "pop": 6379, "state": "CA", "_id": "94028"} -{"city": "MILLBRAE", "loc": [-122.401985, 37.600382], "pop": 20508, "state": "CA", "_id": "94030"} -{"city": "MOFFETT FIELD", "loc": [-122.051944, 37.41001], "pop": 790, "state": "CA", "_id": "94035"} -{"city": "MOSS BEACH", "loc": [-122.50683, 37.531039], "pop": 5415, "state": "CA", "_id": "94038"} -{"city": "MOUNTAIN VIEW", "loc": [-122.087983, 37.385532], "pop": 26969, "state": "CA", "_id": "94040"} -{"city": "MOUNTAIN VIEW", "loc": [-122.078341, 37.389347], "pop": 13438, "state": "CA", "_id": "94041"} -{"city": "MOUNTAIN VIEW", "loc": [-122.077468, 37.405567], "pop": 28592, "state": "CA", "_id": "94043"} -{"city": "PACIFICA", "loc": [-122.481607, 37.619559], "pop": 37596, "state": "CA", "_id": "94044"} -{"city": "PESCADERO", "loc": [-122.364876, 37.206518], "pop": 670, "state": "CA", "_id": "94060"} -{"city": "REDWOOD CITY", "loc": [-122.230406, 37.464661], "pop": 33316, "state": "CA", "_id": "94061"} -{"city": "WOODSIDE", "loc": [-122.255879, 37.452119], "pop": 24947, "state": "CA", "_id": "94062"} -{"city": "REDWOOD CITY", "loc": [-122.209134, 37.481544], "pop": 28251, "state": "CA", "_id": "94063"} -{"city": "REDWOOD CITY", "loc": [-122.248564, 37.533128], "pop": 2285, "state": "CA", "_id": "94065"} -{"city": "SAN BRUNO", "loc": [-122.429021, 37.624742], "pop": 38678, "state": "CA", "_id": "94066"} -{"city": "SAN CARLOS", "loc": [-122.267356, 37.496859], "pop": 27599, "state": "CA", "_id": "94070"} -{"city": "SAN GREGORIO", "loc": [-122.355552, 37.325513], "pop": 312, "state": "CA", "_id": "94074"} -{"city": "SOUTH SAN FRANCI", "loc": [-122.4347, 37.65382], "pop": 54610, "state": "CA", "_id": "94080"} -{"city": "SUNNYVALE", "loc": [-122.023771, 37.376407], "pop": 56215, "state": "CA", "_id": "94086"} -{"city": "SUNNYVALE", "loc": [-122.034859, 37.350214], "pop": 47813, "state": "CA", "_id": "94087"} -{"city": "SUNNYVALE", "loc": [-122.000637, 37.398255], "pop": 13522, "state": "CA", "_id": "94089"} -{"city": "SAN FRANCISCO", "loc": [-122.416728, 37.781334], "pop": 26908, "state": "CA", "_id": "94102"} -{"city": "SAN FRANCISCO", "loc": [-122.414664, 37.77254], "pop": 17867, "state": "CA", "_id": "94103"} -{"city": "SAN FRANCISCO", "loc": [-122.401826, 37.791487], "pop": 760, "state": "CA", "_id": "94104"} -{"city": "SAN FRANCISCO", "loc": [-122.389229, 37.786427], "pop": 2054, "state": "CA", "_id": "94105"} -{"city": "SAN FRANCISCO", "loc": [-122.397099, 37.762147], "pop": 12143, "state": "CA", "_id": "94107"} -{"city": "SAN FRANCISCO", "loc": [-122.40791, 37.792931], "pop": 14143, "state": "CA", "_id": "94108"} -{"city": "SAN FRANCISCO", "loc": [-122.418579, 37.791687], "pop": 49396, "state": "CA", "_id": "94109"} -{"city": "SAN FRANCISCO", "loc": [-122.415344, 37.750858], "pop": 70770, "state": "CA", "_id": "94110"} -{"city": "SAN FRANCISCO", "loc": [-122.400147, 37.797376], "pop": 3122, "state": "CA", "_id": "94111"} -{"city": "SAN FRANCISCO", "loc": [-122.441081, 37.71954], "pop": 64320, "state": "CA", "_id": "94112"} -{"city": "SAN FRANCISCO", "loc": [-122.432977, 37.758716], "pop": 30698, "state": "CA", "_id": "94114"} -{"city": "SAN FRANCISCO", "loc": [-122.435835, 37.785607], "pop": 28859, "state": "CA", "_id": "94115"} -{"city": "SAN FRANCISCO", "loc": [-122.486296, 37.744144], "pop": 39970, "state": "CA", "_id": "94116"} -{"city": "SAN FRANCISCO", "loc": [-122.441272, 37.771234], "pop": 38127, "state": "CA", "_id": "94117"} -{"city": "SAN FRANCISCO", "loc": [-122.461414, 37.781174], "pop": 38499, "state": "CA", "_id": "94118"} -{"city": "SAN FRANCISCO", "loc": [-122.489178, 37.778616], "pop": 40430, "state": "CA", "_id": "94121"} -{"city": "SAN FRANCISCO", "loc": [-122.483647, 37.759326], "pop": 52318, "state": "CA", "_id": "94122"} -{"city": "SAN FRANCISCO", "loc": [-122.434163, 37.799865], "pop": 23280, "state": "CA", "_id": "94123"} -{"city": "SAN FRANCISCO", "loc": [-122.388649, 37.730888], "pop": 27239, "state": "CA", "_id": "94124"} -{"city": "SAN FRANCISCO", "loc": [-122.457116, 37.735385], "pop": 17906, "state": "CA", "_id": "94127"} -{"city": "SAN FRANCISCO", "loc": [-122.464958, 37.800507], "pop": 4715, "state": "CA", "_id": "94129"} -{"city": "SAN FRANCISCO", "loc": [-122.369319, 37.823128], "pop": 4533, "state": "CA", "_id": "94130"} -{"city": "SAN FRANCISCO", "loc": [-122.438335, 37.745032], "pop": 30521, "state": "CA", "_id": "94131"} -{"city": "SAN FRANCISCO", "loc": [-122.47545, 37.721118], "pop": 23632, "state": "CA", "_id": "94132"} -{"city": "SAN FRANCISCO", "loc": [-122.409081, 37.800175], "pop": 27148, "state": "CA", "_id": "94133"} -{"city": "SAN FRANCISCO", "loc": [-122.409577, 37.718968], "pop": 34635, "state": "CA", "_id": "94134"} -{"city": "PALO ALTO", "loc": [-122.149685, 37.444324], "pop": 15965, "state": "CA", "_id": "94301"} -{"city": "EAST PALO ALTO", "loc": [-122.131902, 37.455641], "pop": 35680, "state": "CA", "_id": "94303"} -{"city": "PALO ALTO", "loc": [-122.184234, 37.433424], "pop": 1835, "state": "CA", "_id": "94304"} -{"city": "STANFORD", "loc": [-122.161867, 37.423573], "pop": 18097, "state": "CA", "_id": "94305"} -{"city": "PALO ALTO", "loc": [-122.127375, 37.418009], "pop": 24309, "state": "CA", "_id": "94306"} -{"city": "RUSSIAN RIVER", "loc": [-122.320262, 37.572271], "pop": 28190, "state": "CA", "_id": "94401"} -{"city": "SAN MATEO", "loc": [-122.32762, 37.550685], "pop": 23838, "state": "CA", "_id": "94402"} -{"city": "SAN MATEO", "loc": [-122.299796, 37.539495], "pop": 35630, "state": "CA", "_id": "94403"} -{"city": "FOSTER CITY", "loc": [-122.263577, 37.551614], "pop": 33745, "state": "CA", "_id": "94404"} -{"city": "COAST GUARD ISLA", "loc": [-122.260516, 37.764783], "pop": 76110, "state": "CA", "_id": "94501"} -{"city": "ALAMO", "loc": [-122.022868, 37.853695], "pop": 8569, "state": "CA", "_id": "94507"} -{"city": "ANGWIN", "loc": [-122.447732, 38.576906], "pop": 4067, "state": "CA", "_id": "94508"} -{"city": "ANTIOCH", "loc": [-121.808906, 37.993917], "pop": 62830, "state": "CA", "_id": "94509"} -{"city": "BENICIA", "loc": [-122.161392, 38.068459], "pop": 24545, "state": "CA", "_id": "94510"} -{"city": "BIRDS LANDING", "loc": [-121.844318, 38.150402], "pop": 32, "state": "CA", "_id": "94512"} -{"city": "BRENTWOOD", "loc": [-121.689427, 37.932415], "pop": 12372, "state": "CA", "_id": "94513"} -{"city": "BYRON", "loc": [-121.602211, 37.902616], "pop": 5745, "state": "CA", "_id": "94514"} -{"city": "CALISTOGA", "loc": [-122.581384, 38.582305], "pop": 5758, "state": "CA", "_id": "94515"} -{"city": "CLAYTON", "loc": [-121.909967, 37.915442], "pop": 10353, "state": "CA", "_id": "94517"} -{"city": "CONCORD", "loc": [-122.026296, 37.950434], "pop": 25516, "state": "CA", "_id": "94518"} -{"city": "CONCORD", "loc": [-122.011948, 37.984082], "pop": 19032, "state": "CA", "_id": "94519"} -{"city": "CONCORD", "loc": [-122.036178, 37.982259], "pop": 31474, "state": "CA", "_id": "94520"} -{"city": "CONCORD", "loc": [-121.974955, 37.957503], "pop": 39005, "state": "CA", "_id": "94521"} -{"city": "PLEASANT HILL", "loc": [-122.07371, 37.954002], "pop": 31046, "state": "CA", "_id": "94523"} -{"city": "CROCKETT", "loc": [-122.217659, 38.051865], "pop": 3228, "state": "CA", "_id": "94525"} -{"city": "DANVILLE", "loc": [-121.96598, 37.813985], "pop": 40613, "state": "CA", "_id": "94526"} -{"city": "DIABLO", "loc": [-121.960951, 37.83883], "pop": 791, "state": "CA", "_id": "94528"} -{"city": "EL CERRITO", "loc": [-122.298521, 37.915633], "pop": 28146, "state": "CA", "_id": "94530"} -{"city": "FAIRFIELD", "loc": [-122.03565, 38.267084], "pop": 65455, "state": "CA", "_id": "94533"} -{"city": "TRAVIS AFB", "loc": [-121.946317, 38.274313], "pop": 9874, "state": "CA", "_id": "94535"} -{"city": "FREMONT", "loc": [-121.999935, 37.560493], "pop": 58580, "state": "CA", "_id": "94536"} -{"city": "FREMONT", "loc": [-121.971215, 37.530815], "pop": 45430, "state": "CA", "_id": "94538"} -{"city": "FREMONT", "loc": [-121.928733, 37.517579], "pop": 39927, "state": "CA", "_id": "94539"} -{"city": "HAYWARD", "loc": [-122.089418, 37.674048], "pop": 48964, "state": "CA", "_id": "94541"} -{"city": "HAYWARD", "loc": [-122.047236, 37.658566], "pop": 11165, "state": "CA", "_id": "94542"} -{"city": "HAYWARD", "loc": [-122.067029, 37.637443], "pop": 58348, "state": "CA", "_id": "94544"} -{"city": "HAYWARD", "loc": [-122.0971, 37.633245], "pop": 23760, "state": "CA", "_id": "94545"} -{"city": "CASTRO VALLEY", "loc": [-122.078183, 37.701527], "pop": 37808, "state": "CA", "_id": "94546"} -{"city": "HERCULES", "loc": [-122.263702, 38.006649], "pop": 16376, "state": "CA", "_id": "94547"} -{"city": "KNIGHTSEN", "loc": [-121.672149, 37.9818], "pop": 118, "state": "CA", "_id": "94548"} -{"city": "LAFAYETTE", "loc": [-122.11194, 37.896105], "pop": 25979, "state": "CA", "_id": "94549"} -{"city": "LIVERMORE", "loc": [-121.762983, 37.68299], "pop": 59709, "state": "CA", "_id": "94550"} -{"city": "CASTRO VALLEY", "loc": [-122.038113, 37.713107], "pop": 7936, "state": "CA", "_id": "94552"} -{"city": "PACHECO", "loc": [-122.111693, 37.993246], "pop": 45532, "state": "CA", "_id": "94553"} -{"city": "FREMONT", "loc": [-122.046925, 37.573458], "pop": 29437, "state": "CA", "_id": "94555"} -{"city": "MORAGA", "loc": [-122.124185, 37.843653], "pop": 15988, "state": "CA", "_id": "94556"} -{"city": "SPANISH FLAT", "loc": [-122.305518, 38.328137], "pop": 57901, "state": "CA", "_id": "94558"} -{"city": "NAPA", "loc": [-122.284086, 38.290362], "pop": 23606, "state": "CA", "_id": "94559"} -{"city": "NEWARK", "loc": [-122.031956, 37.536812], "pop": 37861, "state": "CA", "_id": "94560"} -{"city": "OAKLEY", "loc": [-121.703623, 37.994034], "pop": 20920, "state": "CA", "_id": "94561"} -{"city": "ORINDA", "loc": [-122.172848, 37.878659], "pop": 16883, "state": "CA", "_id": "94563"} -{"city": "PINOLE", "loc": [-122.287477, 37.996903], "pop": 16920, "state": "CA", "_id": "94564"} -{"city": "SHORE ACRES", "loc": [-121.908178, 38.016887], "pop": 64053, "state": "CA", "_id": "94565"} -{"city": "PLEASANTON", "loc": [-121.8755, 37.665804], "pop": 32953, "state": "CA", "_id": "94566"} -{"city": "POPE VALLEY", "loc": [-122.472244, 38.678192], "pop": 286, "state": "CA", "_id": "94567"} -{"city": "DUBLIN", "loc": [-121.922589, 37.716597], "pop": 23275, "state": "CA", "_id": "94568"} -{"city": "PORT COSTA", "loc": [-122.186649, 38.046013], "pop": 228, "state": "CA", "_id": "94569"} -{"city": "RIO VISTA", "loc": [-121.701635, 38.163734], "pop": 4516, "state": "CA", "_id": "94571"} -{"city": "RODEO", "loc": [-122.258139, 38.03069], "pop": 7827, "state": "CA", "_id": "94572"} -{"city": "SAINT HELENA", "loc": [-122.461921, 38.513776], "pop": 9388, "state": "CA", "_id": "94574"} -{"city": "SAN LEANDRO", "loc": [-122.158705, 37.720467], "pop": 36779, "state": "CA", "_id": "94577"} -{"city": "SAN LEANDRO", "loc": [-122.123969, 37.702377], "pop": 31780, "state": "CA", "_id": "94578"} -{"city": "SAN LEANDRO", "loc": [-122.150659, 37.689209], "pop": 15754, "state": "CA", "_id": "94579"} -{"city": "SAN LORENZO", "loc": [-122.129547, 37.678671], "pop": 23010, "state": "CA", "_id": "94580"} -{"city": "SAN RAMON", "loc": [-121.952224, 37.756188], "pop": 35449, "state": "CA", "_id": "94583"} -{"city": "SUISUN CITY", "loc": [-122.042003, 38.240834], "pop": 31081, "state": "CA", "_id": "94585"} -{"city": "SUNOL", "loc": [-121.898636, 37.609403], "pop": 953, "state": "CA", "_id": "94586"} -{"city": "UNION CITY", "loc": [-122.049702, 37.589458], "pop": 52869, "state": "CA", "_id": "94587"} -{"city": "PLEASANTON", "loc": [-121.8957, 37.687311], "pop": 19032, "state": "CA", "_id": "94588"} -{"city": "AMERICAN CANYON", "loc": [-122.249333, 38.148345], "pop": 37599, "state": "CA", "_id": "94589"} -{"city": "VALLEJO", "loc": [-122.247367, 38.105302], "pop": 35516, "state": "CA", "_id": "94590"} -{"city": "VALLEJO", "loc": [-122.212354, 38.09853], "pop": 43336, "state": "CA", "_id": "94591"} -{"city": "MARE ISLAND", "loc": [-122.27273, 38.094654], "pop": 3589, "state": "CA", "_id": "94592"} -{"city": "WALNUT CREEK", "loc": [-122.070259, 37.875317], "pop": 16346, "state": "CA", "_id": "94595"} -{"city": "WALNUT CREEK", "loc": [-122.054909, 37.905279], "pop": 38092, "state": "CA", "_id": "94596"} -{"city": "WALNUT CREEK", "loc": [-122.025879, 37.919424], "pop": 24174, "state": "CA", "_id": "94598"} -{"city": "YOUNTVILLE", "loc": [-122.358506, 38.403813], "pop": 1876, "state": "CA", "_id": "94599"} -{"city": "OAKLAND", "loc": [-122.216587, 37.780595], "pop": 47715, "state": "CA", "_id": "94601"} -{"city": "OAKLAND", "loc": [-122.210368, 37.801133], "pop": 28629, "state": "CA", "_id": "94602"} -{"city": "OAKLAND", "loc": [-122.171017, 37.740239], "pop": 27303, "state": "CA", "_id": "94603"} -{"city": "OAKLAND", "loc": [-122.163326, 37.764132], "pop": 38511, "state": "CA", "_id": "94605"} -{"city": "OAKLAND", "loc": [-122.24292, 37.79565], "pop": 38555, "state": "CA", "_id": "94606"} -{"city": "OAKLAND", "loc": [-122.285051, 37.807084], "pop": 21294, "state": "CA", "_id": "94607"} -{"city": "EMERYVILLE", "loc": [-122.280363, 37.836466], "pop": 22318, "state": "CA", "_id": "94608"} -{"city": "OAKLAND", "loc": [-122.26367, 37.836096], "pop": 20263, "state": "CA", "_id": "94609"} -{"city": "OAKLAND", "loc": [-122.244322, 37.812636], "pop": 29637, "state": "CA", "_id": "94610"} -{"city": "PIEDMONT", "loc": [-122.22683, 37.828157], "pop": 34238, "state": "CA", "_id": "94611"} -{"city": "OAKLAND", "loc": [-122.266774, 37.808473], "pop": 10763, "state": "CA", "_id": "94612"} -{"city": "OAKLAND", "loc": [-122.181585, 37.782427], "pop": 627, "state": "CA", "_id": "94613"} -{"city": "PIEDMONT", "loc": [-122.24191, 37.84368], "pop": 15763, "state": "CA", "_id": "94618"} -{"city": "OAKLAND", "loc": [-122.18838, 37.787786], "pop": 24501, "state": "CA", "_id": "94619"} -{"city": "OAKLAND", "loc": [-122.185335, 37.758924], "pop": 26689, "state": "CA", "_id": "94621"} -{"city": "BERKELEY", "loc": [-122.285126, 37.865611], "pop": 15004, "state": "CA", "_id": "94702"} -{"city": "BERKELEY", "loc": [-122.274914, 37.863028], "pop": 18554, "state": "CA", "_id": "94703"} -{"city": "BERKELEY", "loc": [-122.257048, 37.866428], "pop": 23551, "state": "CA", "_id": "94704"} -{"city": "BERKELEY", "loc": [-122.249964, 37.85711], "pop": 11833, "state": "CA", "_id": "94705"} -{"city": "ALBANY", "loc": [-122.295394, 37.890045], "pop": 17333, "state": "CA", "_id": "94706"} -{"city": "KENSINGTON", "loc": [-122.276517, 37.893118], "pop": 9152, "state": "CA", "_id": "94707"} -{"city": "KENSINGTON", "loc": [-122.25976, 37.890829], "pop": 8874, "state": "CA", "_id": "94708"} -{"city": "BERKELEY", "loc": [-122.265461, 37.878397], "pop": 9927, "state": "CA", "_id": "94709"} -{"city": "BERKELEY", "loc": [-122.295929, 37.869603], "pop": 6891, "state": "CA", "_id": "94710"} -{"city": "RICHMOND", "loc": [-122.36201, 37.940039], "pop": 23948, "state": "CA", "_id": "94801"} -{"city": "EL SOBRANTE", "loc": [-122.290092, 37.969287], "pop": 22238, "state": "CA", "_id": "94803"} -{"city": "RICHMOND", "loc": [-122.33421, 37.926523], "pop": 33990, "state": "CA", "_id": "94804"} -{"city": "RICHMOND", "loc": [-122.323756, 37.941719], "pop": 12342, "state": "CA", "_id": "94805"} -{"city": "SAN PABLO", "loc": [-122.336929, 37.972374], "pop": 47668, "state": "CA", "_id": "94806"} -{"city": "SAN RAFAEL", "loc": [-122.510502, 37.969144], "pop": 41550, "state": "CA", "_id": "94901"} -{"city": "CIVIC CENTER", "loc": [-122.54521, 38.015044], "pop": 25563, "state": "CA", "_id": "94903"} -{"city": "KENTFIELD", "loc": [-122.535501, 37.950599], "pop": 11820, "state": "CA", "_id": "94904"} -{"city": "BELVEDERE", "loc": [-122.472627, 37.889885], "pop": 10993, "state": "CA", "_id": "94920"} -{"city": "BODEGA", "loc": [-122.951364, 38.339264], "pop": 93, "state": "CA", "_id": "94922"} -{"city": "BODEGA BAY", "loc": [-123.037308, 38.330921], "pop": 1427, "state": "CA", "_id": "94923"} -{"city": "BOLINAS", "loc": [-122.694655, 37.907875], "pop": 1555, "state": "CA", "_id": "94924"} -{"city": "CORTE MADERA", "loc": [-122.513202, 37.922256], "pop": 7974, "state": "CA", "_id": "94925"} -{"city": "ROHNERT PARK", "loc": [-122.69408, 38.347027], "pop": 35730, "state": "CA", "_id": "94928"} -{"city": "FAIRFAX", "loc": [-122.593711, 37.988289], "pop": 8051, "state": "CA", "_id": "94930"} -{"city": "COTATI", "loc": [-122.704831, 38.325918], "pop": 6849, "state": "CA", "_id": "94931"} -{"city": "FOREST KNOLLS", "loc": [-122.69074, 38.012178], "pop": 732, "state": "CA", "_id": "94933"} -{"city": "INVERNESS", "loc": [-122.856774, 38.083514], "pop": 1716, "state": "CA", "_id": "94937"} -{"city": "LAGUNITAS", "loc": [-122.701576, 38.013929], "pop": 276, "state": "CA", "_id": "94938"} -{"city": "LARKSPUR", "loc": [-122.536202, 37.936743], "pop": 5884, "state": "CA", "_id": "94939"} -{"city": "MARSHALL", "loc": [-122.890011, 38.176221], "pop": 55, "state": "CA", "_id": "94940"} -{"city": "MILL VALLEY", "loc": [-122.533885, 37.895757], "pop": 27746, "state": "CA", "_id": "94941"} -{"city": "NOVATO", "loc": [-122.571416, 38.1163], "pop": 15535, "state": "CA", "_id": "94945"} -{"city": "NICASIO", "loc": [-122.696402, 38.054622], "pop": 665, "state": "CA", "_id": "94946"} -{"city": "NOVATO", "loc": [-122.583691, 38.097258], "pop": 22759, "state": "CA", "_id": "94947"} -{"city": "NOVATO", "loc": [-122.540408, 38.061837], "pop": 16219, "state": "CA", "_id": "94949"} -{"city": "PENNGROVE", "loc": [-122.671772, 38.315948], "pop": 3886, "state": "CA", "_id": "94951"} -{"city": "PETALUMA", "loc": [-122.677727, 38.240349], "pop": 29724, "state": "CA", "_id": "94952"} -{"city": "PETALUMA", "loc": [-122.615536, 38.250739], "pop": 27667, "state": "CA", "_id": "94954"} -{"city": "POINT REYES STAT", "loc": [-122.81621, 38.064794], "pop": 951, "state": "CA", "_id": "94956"} -{"city": "SAN ANSELMO", "loc": [-122.571062, 37.984579], "pop": 15178, "state": "CA", "_id": "94960"} -{"city": "SAN GERONIMO", "loc": [-122.67784, 38.017155], "pop": 802, "state": "CA", "_id": "94963"} -{"city": "SAUSALITO", "loc": [-122.494555, 37.860147], "pop": 10032, "state": "CA", "_id": "94965"} -{"city": "STINSON BEACH", "loc": [-122.639305, 37.901992], "pop": 630, "state": "CA", "_id": "94970"} -{"city": "VALLEY FORD", "loc": [-122.924457, 38.315729], "pop": 114, "state": "CA", "_id": "94972"} -{"city": "WOODACRE", "loc": [-122.638247, 38.006933], "pop": 1524, "state": "CA", "_id": "94973"} -{"city": "ALVISO", "loc": [-121.968597, 37.427659], "pop": 2179, "state": "CA", "_id": "95002"} -{"city": "APTOS", "loc": [-121.891979, 36.978477], "pop": 23964, "state": "CA", "_id": "95003"} -{"city": "AROMAS", "loc": [-121.639781, 36.878522], "pop": 2713, "state": "CA", "_id": "95004"} -{"city": "BEN LOMOND", "loc": [-122.083869, 37.086183], "pop": 7702, "state": "CA", "_id": "95005"} -{"city": "BOULDER CREEK", "loc": [-122.133053, 37.149934], "pop": 9434, "state": "CA", "_id": "95006"} -{"city": "CAMPBELL", "loc": [-121.95539, 37.280007], "pop": 41821, "state": "CA", "_id": "95008"} -{"city": "CAPITOLA", "loc": [-121.952145, 36.977359], "pop": 9337, "state": "CA", "_id": "95010"} -{"city": "CASTROVILLE", "loc": [-121.747368, 36.77142], "pop": 7168, "state": "CA", "_id": "95012"} -{"city": "COYOTE", "loc": [-121.746021, 37.216721], "pop": 316, "state": "CA", "_id": "95013"} -{"city": "MONTE VISTA", "loc": [-122.038604, 37.317363], "pop": 47598, "state": "CA", "_id": "95014"} -{"city": "DAVENPORT", "loc": [-122.225735, 37.06306], "pop": 42, "state": "CA", "_id": "95017"} -{"city": "FELTON", "loc": [-122.062162, 37.063124], "pop": 8194, "state": "CA", "_id": "95018"} -{"city": "FREEDOM", "loc": [-121.776761, 36.936483], "pop": 5075, "state": "CA", "_id": "95019"} -{"city": "GILROY", "loc": [-121.57825, 37.016005], "pop": 39878, "state": "CA", "_id": "95020"} -{"city": "HOLLISTER", "loc": [-121.387101, 36.848404], "pop": 31243, "state": "CA", "_id": "95023"} -{"city": "MONTE SERENO", "loc": [-121.978684, 37.211677], "pop": 25881, "state": "CA", "_id": "95030"} -{"city": "LOS GATOS", "loc": [-121.950088, 37.231571], "pop": 18189, "state": "CA", "_id": "95032"} -{"city": "MILPITAS", "loc": [-121.892885, 37.436491], "pop": 50907, "state": "CA", "_id": "95035"} -{"city": "MORGAN HILL", "loc": [-121.64636, 37.129171], "pop": 31309, "state": "CA", "_id": "95037"} -{"city": "PAICINES", "loc": [-121.032853, 36.49478], "pop": 636, "state": "CA", "_id": "95043"} -{"city": "SAN JUAN BAUTIST", "loc": [-121.532721, 36.849856], "pop": 3657, "state": "CA", "_id": "95045"} -{"city": "SAN MARTIN", "loc": [-121.599901, 37.091118], "pop": 5563, "state": "CA", "_id": "95046"} -{"city": "SANTA CLARA", "loc": [-121.954079, 37.34732], "pop": 33310, "state": "CA", "_id": "95050"} -{"city": "SANTA CLARA", "loc": [-121.983848, 37.346992], "pop": 49570, "state": "CA", "_id": "95051"} -{"city": "SANTA CLARA", "loc": [-121.95394, 37.394673], "pop": 10370, "state": "CA", "_id": "95054"} -{"city": "SCOTTS VALLEY", "loc": [-122.043612, 36.982946], "pop": 40334, "state": "CA", "_id": "95060"} -{"city": "SANTA CRUZ", "loc": [-121.988055, 36.972101], "pop": 34287, "state": "CA", "_id": "95062"} -{"city": "SANTA CRUZ", "loc": [-122.057803, 36.995851], "pop": 4658, "state": "CA", "_id": "95064"} -{"city": "SANTA CRUZ", "loc": [-121.982557, 37.003319], "pop": 8130, "state": "CA", "_id": "95065"} -{"city": "SCOTTS VALLEY", "loc": [-122.014961, 37.057841], "pop": 10636, "state": "CA", "_id": "95066"} -{"city": "SARATOGA", "loc": [-122.018238, 37.272871], "pop": 28909, "state": "CA", "_id": "95070"} -{"city": "SOQUEL", "loc": [-121.950255, 37.003525], "pop": 8143, "state": "CA", "_id": "95073"} -{"city": "LA SELVA BEACH", "loc": [-121.763437, 36.920515], "pop": 68295, "state": "CA", "_id": "95076"} -{"city": "SAN JOSE", "loc": [-121.890299, 37.32966], "pop": 17437, "state": "CA", "_id": "95110"} -{"city": "SAN JOSE", "loc": [-121.824038, 37.282276], "pop": 48286, "state": "CA", "_id": "95111"} -{"city": "SAN JOSE", "loc": [-121.880414, 37.341388], "pop": 46470, "state": "CA", "_id": "95112"} -{"city": "SAN JOSE", "loc": [-121.887227, 37.335188], "pop": 1265, "state": "CA", "_id": "95113"} -{"city": "SAN JOSE", "loc": [-121.850221, 37.351342], "pop": 46754, "state": "CA", "_id": "95116"} -{"city": "SAN JOSE", "loc": [-121.962126, 37.308896], "pop": 27414, "state": "CA", "_id": "95117"} -{"city": "SAN JOSE", "loc": [-121.889845, 37.256162], "pop": 30591, "state": "CA", "_id": "95118"} -{"city": "SAN JOSE", "loc": [-121.790067, 37.230135], "pop": 9823, "state": "CA", "_id": "95119"} -{"city": "SAN JOSE", "loc": [-121.861547, 37.217538], "pop": 34577, "state": "CA", "_id": "95120"} -{"city": "SAN JOSE", "loc": [-121.811939, 37.30593], "pop": 32572, "state": "CA", "_id": "95121"} -{"city": "SAN JOSE", "loc": [-121.833949, 37.329313], "pop": 52543, "state": "CA", "_id": "95122"} -{"city": "SAN JOSE", "loc": [-121.830502, 37.244594], "pop": 55146, "state": "CA", "_id": "95123"} -{"city": "SAN JOSE", "loc": [-121.920831, 37.256844], "pop": 44595, "state": "CA", "_id": "95124"} -{"city": "SAN JOSE", "loc": [-121.895476, 37.296187], "pop": 42573, "state": "CA", "_id": "95125"} -{"city": "SAN JOSE", "loc": [-121.917398, 37.322482], "pop": 24778, "state": "CA", "_id": "95126"} -{"city": "SAN JOSE", "loc": [-121.819516, 37.3664], "pop": 50371, "state": "CA", "_id": "95127"} -{"city": "SAN JOSE", "loc": [-121.934364, 37.314657], "pop": 28275, "state": "CA", "_id": "95128"} -{"city": "SAN JOSE", "loc": [-122.000494, 37.306537], "pop": 33953, "state": "CA", "_id": "95129"} -{"city": "SAN JOSE", "loc": [-121.979182, 37.288628], "pop": 13765, "state": "CA", "_id": "95130"} -{"city": "SAN JOSE", "loc": [-121.879977, 37.386368], "pop": 18425, "state": "CA", "_id": "95131"} -{"city": "SAN JOSE", "loc": [-121.860336, 37.40408], "pop": 37995, "state": "CA", "_id": "95132"} -{"city": "SAN JOSE", "loc": [-121.855959, 37.372875], "pop": 24136, "state": "CA", "_id": "95133"} -{"city": "SAN JOSE", "loc": [-121.943399, 37.413999], "pop": 4324, "state": "CA", "_id": "95134"} -{"city": "SAN JOSE", "loc": [-121.757228, 37.297539], "pop": 9104, "state": "CA", "_id": "95135"} -{"city": "SAN JOSE", "loc": [-121.847625, 37.268423], "pop": 31200, "state": "CA", "_id": "95136"} -{"city": "SAN JOSE", "loc": [-121.778641, 37.246259], "pop": 5956, "state": "CA", "_id": "95138"} -{"city": "SAN JOSE", "loc": [-121.766867, 37.225162], "pop": 6912, "state": "CA", "_id": "95139"} -{"city": "MOUNT HAMILTON", "loc": [-121.639948, 37.316087], "pop": 37, "state": "CA", "_id": "95140"} -{"city": "SAN JOSE", "loc": [-121.755808, 37.169912], "pop": 0, "state": "CA", "_id": "95141"} -{"city": "SAN JOSE", "loc": [-121.792111, 37.329765], "pop": 37413, "state": "CA", "_id": "95148"} -{"city": "STOCKTON", "loc": [-121.287087, 37.960632], "pop": 7753, "state": "CA", "_id": "95202"} -{"city": "STOCKTON", "loc": [-121.307688, 37.956515], "pop": 17847, "state": "CA", "_id": "95203"} -{"city": "STOCKTON", "loc": [-121.315364, 37.974302], "pop": 28860, "state": "CA", "_id": "95204"} -{"city": "STOCKTON", "loc": [-121.259241, 37.960986], "pop": 31314, "state": "CA", "_id": "95205"} -{"city": "STOCKTON", "loc": [-121.287169, 37.931643], "pop": 33154, "state": "CA", "_id": "95206"} -{"city": "STOCKTON", "loc": [-121.32056, 38.002025], "pop": 50167, "state": "CA", "_id": "95207"} -{"city": "STOCKTON", "loc": [-121.343292, 38.033105], "pop": 26289, "state": "CA", "_id": "95209"} -{"city": "STOCKTON", "loc": [-121.297229, 38.024997], "pop": 33763, "state": "CA", "_id": "95210"} -{"city": "UNIV OF THE PACI", "loc": [-121.310336, 37.980364], "pop": 1722, "state": "CA", "_id": "95211"} -{"city": "STOCKTON", "loc": [-121.246018, 38.034428], "pop": 5584, "state": "CA", "_id": "95212"} -{"city": "STOCKTON", "loc": [-121.215295, 37.968545], "pop": 18533, "state": "CA", "_id": "95215"} -{"city": "STOCKTON", "loc": [-121.363712, 38.010233], "pop": 13994, "state": "CA", "_id": "95219"} -{"city": "ACAMPO", "loc": [-121.218576, 38.200413], "pop": 7734, "state": "CA", "_id": "95220"} -{"city": "ANGELS CAMP", "loc": [-120.55437, 38.064011], "pop": 3530, "state": "CA", "_id": "95222"} -{"city": "BEAR VALLEY", "loc": [-120.342231, 38.24175], "pop": 6205, "state": "CA", "_id": "95223"} -{"city": "COPPEROPOLIS", "loc": [-120.638374, 37.937246], "pop": 1336, "state": "CA", "_id": "95228"} -{"city": "FARMINGTON", "loc": [-120.852343, 37.944771], "pop": 141, "state": "CA", "_id": "95230"} -{"city": "FRENCH CAMP", "loc": [-121.282704, 37.877982], "pop": 3673, "state": "CA", "_id": "95231"} -{"city": "GLENCOE", "loc": [-120.594546, 38.351557], "pop": 189, "state": "CA", "_id": "95232"} -{"city": "LINDEN", "loc": [-121.074442, 38.021869], "pop": 3656, "state": "CA", "_id": "95236"} -{"city": "LOCKEFORD", "loc": [-121.135611, 38.162436], "pop": 2847, "state": "CA", "_id": "95237"} -{"city": "LODI", "loc": [-121.263034, 38.123579], "pop": 42726, "state": "CA", "_id": "95240"} -{"city": "LODI", "loc": [-121.311814, 38.132989], "pop": 20669, "state": "CA", "_id": "95242"} -{"city": "MOKELUMNE HILL", "loc": [-120.567705, 38.328918], "pop": 3507, "state": "CA", "_id": "95245"} -{"city": "MOUNTAIN RANCH", "loc": [-120.548137, 38.220363], "pop": 2323, "state": "CA", "_id": "95246"} -{"city": "MURPHYS", "loc": [-120.461772, 38.126896], "pop": 2691, "state": "CA", "_id": "95247"} -{"city": "SAN ANDREAS", "loc": [-120.668703, 38.186732], "pop": 3081, "state": "CA", "_id": "95249"} -{"city": "VALLECITO", "loc": [-120.467879, 38.101472], "pop": 200, "state": "CA", "_id": "95251"} -{"city": "VALLEY SPRINGS", "loc": [-120.859742, 38.154355], "pop": 7592, "state": "CA", "_id": "95252"} -{"city": "WEST POINT", "loc": [-120.515862, 38.406201], "pop": 1513, "state": "CA", "_id": "95255"} -{"city": "WILSEYVILLE", "loc": [-120.442356, 38.384566], "pop": 32, "state": "CA", "_id": "95257"} -{"city": "WOODBRIDGE", "loc": [-121.308632, 38.155124], "pop": 2241, "state": "CA", "_id": "95258"} -{"city": "ATWATER", "loc": [-120.600837, 37.353154], "pop": 24928, "state": "CA", "_id": "95301"} -{"city": "BALLICO", "loc": [-120.700152, 37.452455], "pop": 1296, "state": "CA", "_id": "95303"} -{"city": "CATHEYS VALLEY", "loc": [-120.069017, 37.441409], "pop": 1033, "state": "CA", "_id": "95306"} -{"city": "CERES", "loc": [-120.949936, 37.588097], "pop": 29037, "state": "CA", "_id": "95307"} -{"city": "CHINESE CAMP", "loc": [-120.406673, 37.856829], "pop": 15, "state": "CA", "_id": "95309"} -{"city": "COLUMBIA", "loc": [-120.405552, 38.03975], "pop": 1512, "state": "CA", "_id": "95310"} -{"city": "COULTERVILLE", "loc": [-119.985813, 37.738642], "pop": 2384, "state": "CA", "_id": "95311"} -{"city": "CROWS LANDING", "loc": [-121.019893, 37.438956], "pop": 1896, "state": "CA", "_id": "95313"} -{"city": "DELHI", "loc": [-120.775489, 37.428629], "pop": 6151, "state": "CA", "_id": "95315"} -{"city": "DENAIR", "loc": [-120.796474, 37.538419], "pop": 5878, "state": "CA", "_id": "95316"} -{"city": "EL NIDO", "loc": [-120.483235, 37.13452], "pop": 1024, "state": "CA", "_id": "95317"} -{"city": "ESCALON", "loc": [-120.990044, 37.804428], "pop": 9405, "state": "CA", "_id": "95320"} -{"city": "GROVELAND", "loc": [-120.191809, 37.840899], "pop": 3616, "state": "CA", "_id": "95321"} -{"city": "GUSTINE", "loc": [-121.003965, 37.242456], "pop": 6083, "state": "CA", "_id": "95322"} -{"city": "HICKMAN", "loc": [-120.71722, 37.619989], "pop": 1405, "state": "CA", "_id": "95323"} -{"city": "HILMAR", "loc": [-120.856144, 37.408377], "pop": 6676, "state": "CA", "_id": "95324"} -{"city": "HORNITOS", "loc": [-120.226443, 37.492226], "pop": 49, "state": "CA", "_id": "95325"} -{"city": "HUGHSON", "loc": [-120.865281, 37.594364], "pop": 6383, "state": "CA", "_id": "95326"} -{"city": "JAMESTOWN", "loc": [-120.494567, 37.906544], "pop": 8359, "state": "CA", "_id": "95327"} -{"city": "LA GRANGE", "loc": [-120.358131, 37.678915], "pop": 1305, "state": "CA", "_id": "95329"} -{"city": "LATHROP", "loc": [-121.282652, 37.820897], "pop": 8426, "state": "CA", "_id": "95330"} -{"city": "LE GRAND", "loc": [-120.251737, 37.234175], "pop": 1810, "state": "CA", "_id": "95333"} -{"city": "LIVINGSTON", "loc": [-120.716156, 37.376168], "pop": 10994, "state": "CA", "_id": "95334"} -{"city": "COLD SPRINGS", "loc": [-120.001159, 38.20336], "pop": 201, "state": "CA", "_id": "95335"} -{"city": "MANTECA", "loc": [-121.21856, 37.80875], "pop": 51728, "state": "CA", "_id": "95336"} -{"city": "MARIPOSA", "loc": [-119.892496, 37.503918], "pop": 9323, "state": "CA", "_id": "95338"} -{"city": "RED TOP", "loc": [-120.461668, 37.300724], "pop": 59918, "state": "CA", "_id": "95340"} -{"city": "MIDPINES", "loc": [-119.946548, 37.571009], "pop": 388, "state": "CA", "_id": "95345"} -{"city": "MI WUK VILLAGE", "loc": [-120.131762, 38.105576], "pop": 701, "state": "CA", "_id": "95346"} -{"city": "MERCED", "loc": [-120.500897, 37.326964], "pop": 19719, "state": "CA", "_id": "95348"} -{"city": "MODESTO", "loc": [-121.011303, 37.674649], "pop": 50618, "state": "CA", "_id": "95350"} -{"city": "MODESTO", "loc": [-121.006033, 37.625022], "pop": 69275, "state": "CA", "_id": "95351"} -{"city": "MODESTO", "loc": [-120.968323, 37.644526], "pop": 26630, "state": "CA", "_id": "95354"} -{"city": "MODESTO", "loc": [-120.954658, 37.673515], "pop": 43734, "state": "CA", "_id": "95355"} -{"city": "MODESTO", "loc": [-121.027051, 37.699431], "pop": 26202, "state": "CA", "_id": "95356"} -{"city": "NEWMAN", "loc": [-121.025943, 37.317591], "pop": 5313, "state": "CA", "_id": "95360"} -{"city": "KNIGHTS FERRY", "loc": [-120.849474, 37.775323], "pop": 20919, "state": "CA", "_id": "95361"} -{"city": "PATTERSON", "loc": [-121.140732, 37.490592], "pop": 13437, "state": "CA", "_id": "95363"} -{"city": "PINECREST", "loc": [-119.865195, 38.341062], "pop": 13, "state": "CA", "_id": "95364"} -{"city": "RIPON", "loc": [-121.122909, 37.753286], "pop": 10879, "state": "CA", "_id": "95366"} -{"city": "RIVERBANK", "loc": [-120.943019, 37.732809], "pop": 9732, "state": "CA", "_id": "95367"} -{"city": "SALIDA", "loc": [-121.090484, 37.70713], "pop": 3255, "state": "CA", "_id": "95368"} -{"city": "SNELLING", "loc": [-120.481615, 37.514359], "pop": 1335, "state": "CA", "_id": "95369"} -{"city": "SONORA", "loc": [-120.338498, 37.995692], "pop": 23398, "state": "CA", "_id": "95370"} -{"city": "SOULSBYVILLE", "loc": [-120.259892, 37.992794], "pop": 1566, "state": "CA", "_id": "95372"} -{"city": "STEVINSON", "loc": [-120.869856, 37.32575], "pop": 1564, "state": "CA", "_id": "95374"} -{"city": "TRACY", "loc": [-121.419723, 37.742116], "pop": 47291, "state": "CA", "_id": "95376"} -{"city": "TUOLUMNE", "loc": [-120.237496, 37.971339], "pop": 3402, "state": "CA", "_id": "95379"} -{"city": "TURLOCK", "loc": [-120.850511, 37.503605], "pop": 50025, "state": "CA", "_id": "95380"} -{"city": "TWAIN HARTE", "loc": [-120.2155, 38.044722], "pop": 5133, "state": "CA", "_id": "95383"} -{"city": "WATERFORD", "loc": [-120.753862, 37.650988], "pop": 6505, "state": "CA", "_id": "95386"} -{"city": "WINTON", "loc": [-120.6132, 37.39233], "pop": 10362, "state": "CA", "_id": "95388"} -{"city": "SANTA ROSA", "loc": [-122.751722, 38.443123], "pop": 30549, "state": "CA", "_id": "95401"} -{"city": "SANTA ROSA", "loc": [-122.748528, 38.477273], "pop": 30874, "state": "CA", "_id": "95403"} -{"city": "SANTA ROSA", "loc": [-122.689524, 38.449556], "pop": 31216, "state": "CA", "_id": "95404"} -{"city": "SANTA ROSA", "loc": [-122.66988, 38.438279], "pop": 20776, "state": "CA", "_id": "95405"} -{"city": "SANTA ROSA", "loc": [-122.727896, 38.410462], "pop": 22086, "state": "CA", "_id": "95407"} -{"city": "SANTA ROSA", "loc": [-122.642125, 38.461242], "pop": 22897, "state": "CA", "_id": "95409"} -{"city": "ALBION", "loc": [-123.721366, 39.215934], "pop": 869, "state": "CA", "_id": "95410"} -{"city": "95411", "loc": [-123.610187, 40.161039], "pop": 133, "state": "CA", "_id": "95411"} -{"city": "ANNAPOLIS", "loc": [-123.314214, 38.714485], "pop": 24, "state": "CA", "_id": "95412"} -{"city": "95414", "loc": [-123.649246, 40.290394], "pop": 226, "state": "CA", "_id": "95414"} -{"city": "BOONVILLE", "loc": [-123.401954, 39.035007], "pop": 1744, "state": "CA", "_id": "95415"} -{"city": "BRANSCOMB", "loc": [-123.554982, 39.700531], "pop": 708, "state": "CA", "_id": "95417"} -{"city": "CASPAR", "loc": [-123.798489, 39.365102], "pop": 349, "state": "CA", "_id": "95420"} -{"city": "CAZADERO", "loc": [-123.182896, 38.566335], "pop": 2367, "state": "CA", "_id": "95421"} -{"city": "CLEARLAKE", "loc": [-122.636089, 38.957138], "pop": 12157, "state": "CA", "_id": "95422"} -{"city": "CLEARLAKE OAKS", "loc": [-122.66873, 39.034838], "pop": 3400, "state": "CA", "_id": "95423"} -{"city": "CLOVERDALE", "loc": [-123.011725, 38.799396], "pop": 7695, "state": "CA", "_id": "95425"} -{"city": "COMPTCHE", "loc": [-123.585652, 39.249919], "pop": 538, "state": "CA", "_id": "95427"} -{"city": "COVELO", "loc": [-123.215015, 39.801864], "pop": 2182, "state": "CA", "_id": "95428"} -{"city": "DOS RIOS", "loc": [-123.298294, 39.709298], "pop": 14, "state": "CA", "_id": "95429"} -{"city": "ELK", "loc": [-123.723961, 39.160402], "pop": 199, "state": "CA", "_id": "95432"} -{"city": "FORESTVILLE", "loc": [-122.896683, 38.489442], "pop": 7252, "state": "CA", "_id": "95436"} -{"city": "FORT BRAGG", "loc": [-123.78835, 39.437452], "pop": 13535, "state": "CA", "_id": "95437"} -{"city": "FULTON", "loc": [-122.77608, 38.494732], "pop": 569, "state": "CA", "_id": "95439"} -{"city": "95440", "loc": [-123.842409, 40.101665], "pop": 2876, "state": "CA", "_id": "95440"} -{"city": "GEYSERVILLE", "loc": [-122.888921, 38.700398], "pop": 2716, "state": "CA", "_id": "95441"} -{"city": "GLEN ELLEN", "loc": [-122.521002, 38.362538], "pop": 5055, "state": "CA", "_id": "95442"} -{"city": "GLENHAVEN", "loc": [-122.522035, 38.99008], "pop": 94, "state": "CA", "_id": "95443"} -{"city": "GRATON", "loc": [-122.866766, 38.434971], "pop": 259, "state": "CA", "_id": "95444"} -{"city": "GUALALA", "loc": [-123.553975, 38.803619], "pop": 1806, "state": "CA", "_id": "95445"} -{"city": "GUERNEVILLE", "loc": [-122.994416, 38.509573], "pop": 5060, "state": "CA", "_id": "95446"} -{"city": "HEALDSBURG", "loc": [-122.856529, 38.61553], "pop": 16884, "state": "CA", "_id": "95448"} -{"city": "HOPLAND", "loc": [-123.116956, 38.972056], "pop": 1648, "state": "CA", "_id": "95449"} -{"city": "JENNER", "loc": [-123.135379, 38.474093], "pop": 156, "state": "CA", "_id": "95450"} -{"city": "KELSEYVILLE", "loc": [-122.781707, 38.946164], "pop": 10064, "state": "CA", "_id": "95451"} -{"city": "KENWOOD", "loc": [-122.554679, 38.416794], "pop": 1411, "state": "CA", "_id": "95452"} -{"city": "LAKEPORT", "loc": [-122.915082, 39.055147], "pop": 10351, "state": "CA", "_id": "95453"} -{"city": "LAYTONVILLE", "loc": [-123.486862, 39.667811], "pop": 1815, "state": "CA", "_id": "95454"} -{"city": "95455", "loc": [-123.618247, 39.851774], "pop": 843, "state": "CA", "_id": "95455"} -{"city": "LITTLERIVER", "loc": [-123.753089, 39.271042], "pop": 1119, "state": "CA", "_id": "95456"} -{"city": "LOWER LAKE", "loc": [-122.575426, 38.860682], "pop": 4880, "state": "CA", "_id": "95457"} -{"city": "LUCERNE", "loc": [-122.785125, 39.083393], "pop": 2330, "state": "CA", "_id": "95458"} -{"city": "MANCHESTER", "loc": [-123.670962, 38.992088], "pop": 453, "state": "CA", "_id": "95459"} -{"city": "MENDOCINO", "loc": [-123.78846, 39.323212], "pop": 1876, "state": "CA", "_id": "95460"} -{"city": "MIDDLETOWN", "loc": [-122.64352, 38.782504], "pop": 2863, "state": "CA", "_id": "95461"} -{"city": "RUSSIAN RIVER MD", "loc": [-123.00447, 38.462668], "pop": 1999, "state": "CA", "_id": "95462"} -{"city": "NICE", "loc": [-122.842409, 39.122341], "pop": 2374, "state": "CA", "_id": "95464"} -{"city": "OCCIDENTAL", "loc": [-122.988194, 38.396911], "pop": 2094, "state": "CA", "_id": "95465"} -{"city": "PHILO", "loc": [-123.538218, 39.120005], "pop": 498, "state": "CA", "_id": "95466"} -{"city": "95467", "loc": [-123.814656, 39.962583], "pop": 328, "state": "CA", "_id": "95467"} -{"city": "POINT ARENA", "loc": [-123.660756, 38.915264], "pop": 1129, "state": "CA", "_id": "95468"} -{"city": "POTTER VALLEY", "loc": [-123.104181, 39.329639], "pop": 1970, "state": "CA", "_id": "95469"} -{"city": "REDWOOD VALLEY", "loc": [-123.213289, 39.269446], "pop": 5328, "state": "CA", "_id": "95470"} -{"city": "FREESTONE", "loc": [-122.838503, 38.391543], "pop": 27943, "state": "CA", "_id": "95472"} -{"city": "SONOMA", "loc": [-122.472843, 38.295659], "pop": 30443, "state": "CA", "_id": "95476"} -{"city": "UKIAH", "loc": [-123.200692, 39.151917], "pop": 28165, "state": "CA", "_id": "95482"} -{"city": "UPPER LAKE", "loc": [-122.896114, 39.160829], "pop": 1771, "state": "CA", "_id": "95485"} -{"city": "WESTPORT", "loc": [-123.76426, 39.644059], "pop": 369, "state": "CA", "_id": "95488"} -{"city": "95489", "loc": [-124.032628, 40.038453], "pop": 358, "state": "CA", "_id": "95489"} -{"city": "WILLITS", "loc": [-123.350271, 39.411426], "pop": 12864, "state": "CA", "_id": "95490"} -{"city": "WINDSOR", "loc": [-122.804375, 38.543182], "pop": 13717, "state": "CA", "_id": "95492"} -{"city": "WITTER SPRINGS", "loc": [-122.97105, 39.182118], "pop": 300, "state": "CA", "_id": "95493"} -{"city": "YORKVILLE", "loc": [-123.233867, 38.908945], "pop": 173, "state": "CA", "_id": "95494"} -{"city": "95495", "loc": [-123.381217, 40.203331], "pop": 367, "state": "CA", "_id": "95495"} -{"city": "THE SEA RANCH", "loc": [-123.467486, 38.725627], "pop": 576, "state": "CA", "_id": "95497"} -{"city": "EUREKA", "loc": [-124.155892, 40.776237], "pop": 45720, "state": "CA", "_id": "95501"} -{"city": "MC KINLEYVILLE", "loc": [-124.081069, 40.904901], "pop": 32283, "state": "CA", "_id": "95521"} -{"city": "BAYSIDE", "loc": [-124.027305, 40.822381], "pop": 1744, "state": "CA", "_id": "95524"} -{"city": "BLUE LAKE", "loc": [-123.896696, 40.928701], "pop": 328, "state": "CA", "_id": "95525"} -{"city": "RUTH", "loc": [-123.549351, 40.468566], "pop": 1076, "state": "CA", "_id": "95526"} -{"city": "BURNT RANCH", "loc": [-123.461248, 40.772238], "pop": 478, "state": "CA", "_id": "95527"} -{"city": "CARLOTTA", "loc": [-123.974287, 40.507027], "pop": 1054, "state": "CA", "_id": "95528"} -{"city": "CRESCENT CITY", "loc": [-124.178448, 41.785402], "pop": 19400, "state": "CA", "_id": "95531"} -{"city": "FERNDALE", "loc": [-124.252268, 40.574488], "pop": 2942, "state": "CA", "_id": "95536"} -{"city": "FORTUNA", "loc": [-124.140654, 40.584931], "pop": 10727, "state": "CA", "_id": "95540"} -{"city": "GASQUET", "loc": [-123.912467, 41.862768], "pop": 658, "state": "CA", "_id": "95543"} -{"city": "HOOPA", "loc": [-123.692754, 41.105937], "pop": 2702, "state": "CA", "_id": "95546"} -{"city": "HYDESVILLE", "loc": [-124.084489, 40.549576], "pop": 919, "state": "CA", "_id": "95547"} -{"city": "KLAMATH", "loc": [-124.033907, 41.542075], "pop": 1390, "state": "CA", "_id": "95548"} -{"city": "KNEELAND", "loc": [-123.946421, 40.71262], "pop": 265, "state": "CA", "_id": "95549"} -{"city": "KORBEL", "loc": [-123.859413, 40.824369], "pop": 187, "state": "CA", "_id": "95550"} -{"city": "LOLETA", "loc": [-124.228826, 40.6531], "pop": 1244, "state": "CA", "_id": "95551"} -{"city": "MAD RIVER", "loc": [-123.413994, 40.352352], "pop": 6, "state": "CA", "_id": "95552"} -{"city": "MYERS FLAT", "loc": [-123.822713, 40.194758], "pop": 3791, "state": "CA", "_id": "95554"} -{"city": "ORICK", "loc": [-124.050319, 41.30724], "pop": 314, "state": "CA", "_id": "95555"} -{"city": "ORLEANS", "loc": [-123.529564, 41.318092], "pop": 619, "state": "CA", "_id": "95556"} -{"city": "PETROLIA", "loc": [-124.251236, 40.300558], "pop": 347, "state": "CA", "_id": "95558"} -{"city": "REDWAY", "loc": [-123.844194, 40.110081], "pop": 148, "state": "CA", "_id": "95560"} -{"city": "RIO DELL", "loc": [-124.104386, 40.495147], "pop": 4066, "state": "CA", "_id": "95562"} -{"city": "SALYER", "loc": [-123.547833, 40.89095], "pop": 903, "state": "CA", "_id": "95563"} -{"city": "SAMOA", "loc": [-124.193571, 40.803712], "pop": 527, "state": "CA", "_id": "95564"} -{"city": "SCOTIA", "loc": [-124.039074, 40.454665], "pop": 321, "state": "CA", "_id": "95565"} -{"city": "SMITH RIVER", "loc": [-124.166115, 41.936218], "pop": 2012, "state": "CA", "_id": "95567"} -{"city": "SOMES BAR", "loc": [-123.475928, 41.43304], "pop": 195, "state": "CA", "_id": "95568"} -{"city": "REDCREST", "loc": [-123.94205, 40.406458], "pop": 208, "state": "CA", "_id": "95569"} -{"city": "WESTHAVEN", "loc": [-124.101253, 41.087949], "pop": 3143, "state": "CA", "_id": "95570"} -{"city": "WILLOW CREEK", "loc": [-123.631062, 40.938894], "pop": 1503, "state": "CA", "_id": "95573"} -{"city": "AUBURN", "loc": [-121.084347, 38.928311], "pop": 32535, "state": "CA", "_id": "95603"} -{"city": "BRYTE", "loc": [-121.526377, 38.592424], "pop": 11611, "state": "CA", "_id": "95605"} -{"city": "BROOKS", "loc": [-122.133391, 38.739277], "pop": 162, "state": "CA", "_id": "95606"} -{"city": "CAPAY", "loc": [-122.100993, 38.696964], "pop": 175, "state": "CA", "_id": "95607"} -{"city": "CARMICHAEL", "loc": [-121.328683, 38.628393], "pop": 55815, "state": "CA", "_id": "95608"} -{"city": "CITRUS HEIGHTS", "loc": [-121.269211, 38.694571], "pop": 41476, "state": "CA", "_id": "95610"} -{"city": "CLARKSBURG", "loc": [-121.556853, 38.395963], "pop": 1501, "state": "CA", "_id": "95612"} -{"city": "COOL", "loc": [-120.972963, 38.903633], "pop": 2325, "state": "CA", "_id": "95614"} -{"city": "COURTLAND", "loc": [-121.554297, 38.305756], "pop": 958, "state": "CA", "_id": "95615"} -{"city": "DAVIS", "loc": [-121.748495, 38.554817], "pop": 52224, "state": "CA", "_id": "95616"} -{"city": "EL MACERO", "loc": [-121.676722, 38.542151], "pop": 1126, "state": "CA", "_id": "95618"} -{"city": "DIAMOND SPRINGS", "loc": [-120.836071, 38.66302], "pop": 6747, "state": "CA", "_id": "95619"} -{"city": "LIBERTY FARMS", "loc": [-121.815825, 38.458691], "pop": 14536, "state": "CA", "_id": "95620"} -{"city": "CITRUS HEIGHTS", "loc": [-121.307501, 38.695155], "pop": 40540, "state": "CA", "_id": "95621"} -{"city": "ELK GROVE", "loc": [-121.359914, 38.412744], "pop": 23492, "state": "CA", "_id": "95624"} -{"city": "ELVERTA", "loc": [-121.431038, 38.716424], "pop": 6154, "state": "CA", "_id": "95626"} -{"city": "ESPARTO", "loc": [-122.021391, 38.694191], "pop": 2118, "state": "CA", "_id": "95627"} -{"city": "FAIR OAKS", "loc": [-121.261065, 38.655408], "pop": 40502, "state": "CA", "_id": "95628"} -{"city": "FIDDLETOWN", "loc": [-120.715926, 38.53141], "pop": 1147, "state": "CA", "_id": "95629"} -{"city": "EL DORADO HILLS", "loc": [-121.140927, 38.687885], "pop": 38587, "state": "CA", "_id": "95630"} -{"city": "FORESTHILL", "loc": [-120.861127, 39.00229], "pop": 4626, "state": "CA", "_id": "95631"} -{"city": "GALT", "loc": [-121.29383, 38.269846], "pop": 14173, "state": "CA", "_id": "95632"} -{"city": "GARDEN VALLEY", "loc": [-120.856672, 38.866495], "pop": 3628, "state": "CA", "_id": "95633"} -{"city": "GEORGETOWN", "loc": [-120.793388, 38.919892], "pop": 1634, "state": "CA", "_id": "95634"} -{"city": "GREENWOOD", "loc": [-120.916589, 38.936234], "pop": 226, "state": "CA", "_id": "95635"} -{"city": "GRIZZLY FLATS", "loc": [-120.542508, 38.628665], "pop": 237, "state": "CA", "_id": "95636"} -{"city": "GUINDA", "loc": [-122.189659, 38.816568], "pop": 198, "state": "CA", "_id": "95637"} -{"city": "HERALD", "loc": [-121.158898, 38.31282], "pop": 1009, "state": "CA", "_id": "95638"} -{"city": "IONE", "loc": [-120.943265, 38.351882], "pop": 8440, "state": "CA", "_id": "95640"} -{"city": "ISLETON", "loc": [-121.604858, 38.155392], "pop": 2273, "state": "CA", "_id": "95641"} -{"city": "JACKSON", "loc": [-120.754877, 38.357514], "pop": 5335, "state": "CA", "_id": "95642"} -{"city": "KELSEY", "loc": [-120.824056, 38.797636], "pop": 531, "state": "CA", "_id": "95643"} -{"city": "KNIGHTS LANDING", "loc": [-121.720003, 38.822032], "pop": 1907, "state": "CA", "_id": "95645"} -{"city": "LINCOLN", "loc": [-121.295541, 38.904035], "pop": 11935, "state": "CA", "_id": "95648"} -{"city": "LOOMIS", "loc": [-121.169826, 38.80711], "pop": 12973, "state": "CA", "_id": "95650"} -{"city": "LOTUS", "loc": [-120.928864, 38.801712], "pop": 1832, "state": "CA", "_id": "95651"} -{"city": "MCCLELLAN AFB", "loc": [-121.40311, 38.655416], "pop": 541, "state": "CA", "_id": "95652"} -{"city": "MADISON", "loc": [-121.972129, 38.680164], "pop": 523, "state": "CA", "_id": "95653"} -{"city": "MATHER AFB", "loc": [-121.282394, 38.549224], "pop": 4880, "state": "CA", "_id": "95655"} -{"city": "NEWCASTLE", "loc": [-121.142616, 38.872467], "pop": 5998, "state": "CA", "_id": "95658"} -{"city": "TROWBRIDGE", "loc": [-121.553043, 38.882726], "pop": 802, "state": "CA", "_id": "95659"} -{"city": "NORTH HIGHLANDS", "loc": [-121.374913, 38.68855], "pop": 42271, "state": "CA", "_id": "95660"} -{"city": "ROSEVILLE", "loc": [-121.233968, 38.734612], "pop": 29157, "state": "CA", "_id": "95661"} -{"city": "ORANGEVALE", "loc": [-121.222902, 38.682384], "pop": 31361, "state": "CA", "_id": "95662"} -{"city": "PENRYN", "loc": [-121.179149, 38.856654], "pop": 2048, "state": "CA", "_id": "95663"} -{"city": "PILOT HILL", "loc": [-121.029765, 38.826312], "pop": 1152, "state": "CA", "_id": "95664"} -{"city": "PINE GROVE", "loc": [-120.643713, 38.396783], "pop": 3066, "state": "CA", "_id": "95665"} -{"city": "PIONEER", "loc": [-120.531815, 38.460271], "pop": 4797, "state": "CA", "_id": "95666"} -{"city": "PLACERVILLE", "loc": [-120.804564, 38.719479], "pop": 30563, "state": "CA", "_id": "95667"} -{"city": "PLEASANT GROVE", "loc": [-121.507967, 38.787773], "pop": 1034, "state": "CA", "_id": "95668"} -{"city": "PLYMOUTH", "loc": [-120.870258, 38.48811], "pop": 969, "state": "CA", "_id": "95669"} -{"city": "GOLD RIVER", "loc": [-121.289434, 38.601897], "pop": 42461, "state": "CA", "_id": "95670"} -{"city": "RESCUE", "loc": [-120.994526, 38.719353], "pop": 2987, "state": "CA", "_id": "95672"} -{"city": "RIO LINDA", "loc": [-121.445152, 38.689311], "pop": 12756, "state": "CA", "_id": "95673"} -{"city": "RIO OSO", "loc": [-121.505305, 38.960998], "pop": 1102, "state": "CA", "_id": "95674"} -{"city": "ROCKLIN", "loc": [-121.243406, 38.791898], "pop": 19125, "state": "CA", "_id": "95677"} -{"city": "ROSEVILLE", "loc": [-121.302801, 38.750895], "pop": 28285, "state": "CA", "_id": "95678"} -{"city": "RUMSEY", "loc": [-122.253738, 38.8762], "pop": 228, "state": "CA", "_id": "95679"} -{"city": "SHERIDAN", "loc": [-121.362062, 38.99153], "pop": 1169, "state": "CA", "_id": "95681"} -{"city": "CAMERON PARK", "loc": [-120.975925, 38.665616], "pop": 20139, "state": "CA", "_id": "95682"} -{"city": "RANCHO MURIETA", "loc": [-121.094089, 38.507475], "pop": 3035, "state": "CA", "_id": "95683"} -{"city": "SOMERSET", "loc": [-120.666306, 38.607414], "pop": 2892, "state": "CA", "_id": "95684"} -{"city": "SUTTER CREEK", "loc": [-120.785454, 38.418569], "pop": 5677, "state": "CA", "_id": "95685"} -{"city": "VACAVILLE", "loc": [-121.962285, 38.341915], "pop": 50280, "state": "CA", "_id": "95687"} -{"city": "VACAVILLE", "loc": [-121.989912, 38.3812], "pop": 29052, "state": "CA", "_id": "95688"} -{"city": "VOLCANO", "loc": [-120.617761, 38.477319], "pop": 1004, "state": "CA", "_id": "95689"} -{"city": "WALNUT GROVE", "loc": [-121.524849, 38.236362], "pop": 1839, "state": "CA", "_id": "95690"} -{"city": "WEST SACRAMENTO", "loc": [-121.539671, 38.567979], "pop": 17301, "state": "CA", "_id": "95691"} -{"city": "WHEATLAND", "loc": [-121.422126, 39.019197], "pop": 2532, "state": "CA", "_id": "95692"} -{"city": "WILTON", "loc": [-121.225656, 38.412069], "pop": 4082, "state": "CA", "_id": "95693"} -{"city": "WINTERS", "loc": [-121.974544, 38.529462], "pop": 6253, "state": "CA", "_id": "95694"} -{"city": "WOODLAND", "loc": [-121.77932, 38.674294], "pop": 44587, "state": "CA", "_id": "95695"} -{"city": "ZAMORA", "loc": [-121.90654, 38.799896], "pop": 317, "state": "CA", "_id": "95698"} -{"city": "ALTA", "loc": [-120.773165, 39.228032], "pop": 751, "state": "CA", "_id": "95701"} -{"city": "APPLEGATE", "loc": [-120.990511, 38.995487], "pop": 1898, "state": "CA", "_id": "95703"} -{"city": "CAMINO", "loc": [-120.671979, 38.744938], "pop": 4394, "state": "CA", "_id": "95709"} -{"city": "IOWA HILL", "loc": [-120.960163, 39.076936], "pop": 7344, "state": "CA", "_id": "95713"} -{"city": "DUTCH FLAT", "loc": [-120.826224, 39.197788], "pop": 533, "state": "CA", "_id": "95714"} -{"city": "EMIGRANT GAP", "loc": [-120.662902, 39.286907], "pop": 36, "state": "CA", "_id": "95715"} -{"city": "GOLD RUN", "loc": [-120.856909, 39.175102], "pop": 79, "state": "CA", "_id": "95717"} -{"city": "KYBURZ", "loc": [-120.25529, 38.780036], "pop": 159, "state": "CA", "_id": "95720"} -{"city": "ECHO LAKE", "loc": [-120.070498, 38.810254], "pop": 17, "state": "CA", "_id": "95721"} -{"city": "MEADOW VISTA", "loc": [-121.029155, 39.003101], "pop": 3314, "state": "CA", "_id": "95722"} -{"city": "NORDEN", "loc": [-120.400876, 39.319566], "pop": 316, "state": "CA", "_id": "95724"} -{"city": "PACIFIC HOUSE", "loc": [-120.585114, 38.745581], "pop": 7722, "state": "CA", "_id": "95726"} -{"city": "SODA SPRINGS", "loc": [-120.465493, 39.338467], "pop": 96, "state": "CA", "_id": "95728"} -{"city": "TWIN BRIDGES", "loc": [-120.128851, 38.808615], "pop": 0, "state": "CA", "_id": "95735"} -{"city": "RANCHO CORDOVA", "loc": [-121.204019, 38.604313], "pop": 186, "state": "CA", "_id": "95742"} -{"city": "ELK GROVE", "loc": [-121.430706, 38.404238], "pop": 13455, "state": "CA", "_id": "95758"} -{"city": "SACRAMENTO", "loc": [-121.489404, 38.579792], "pop": 16414, "state": "CA", "_id": "95814"} -{"city": "SACRAMENTO", "loc": [-121.443543, 38.613303], "pop": 23491, "state": "CA", "_id": "95815"} -{"city": "SACRAMENTO", "loc": [-121.46753, 38.572788], "pop": 16211, "state": "CA", "_id": "95816"} -{"city": "SACRAMENTO", "loc": [-121.458324, 38.549785], "pop": 15767, "state": "CA", "_id": "95817"} -{"city": "SACRAMENTO", "loc": [-121.492884, 38.556778], "pop": 22214, "state": "CA", "_id": "95818"} -{"city": "SACRAMENTO", "loc": [-121.436634, 38.568293], "pop": 18333, "state": "CA", "_id": "95819"} -{"city": "SACRAMENTO", "loc": [-121.445139, 38.534694], "pop": 35354, "state": "CA", "_id": "95820"} -{"city": "SACRAMENTO", "loc": [-121.383807, 38.623889], "pop": 33040, "state": "CA", "_id": "95821"} -{"city": "SACRAMENTO", "loc": [-121.493541, 38.509139], "pop": 43943, "state": "CA", "_id": "95822"} -{"city": "SACRAMENTO", "loc": [-121.443846, 38.479711], "pop": 55103, "state": "CA", "_id": "95823"} -{"city": "SACRAMENTO", "loc": [-121.441883, 38.517843], "pop": 26507, "state": "CA", "_id": "95824"} -{"city": "SACRAMENTO", "loc": [-121.405677, 38.589226], "pop": 27116, "state": "CA", "_id": "95825"} -{"city": "SACRAMENTO", "loc": [-121.369265, 38.553868], "pop": 38107, "state": "CA", "_id": "95826"} -{"city": "SACRAMENTO", "loc": [-121.328593, 38.56623], "pop": 19471, "state": "CA", "_id": "95827"} -{"city": "SACRAMENTO", "loc": [-121.401504, 38.483718], "pop": 43489, "state": "CA", "_id": "95828"} -{"city": "SACRAMENTO", "loc": [-121.346631, 38.472564], "pop": 4610, "state": "CA", "_id": "95829"} -{"city": "SACRAMENTO", "loc": [-121.281453, 38.476556], "pop": 420, "state": "CA", "_id": "95830"} -{"city": "SACRAMENTO", "loc": [-121.529661, 38.496226], "pop": 39369, "state": "CA", "_id": "95831"} -{"city": "SACRAMENTO", "loc": [-121.482967, 38.475387], "pop": 7724, "state": "CA", "_id": "95832"} -{"city": "SACRAMENTO", "loc": [-121.494487, 38.616993], "pop": 29150, "state": "CA", "_id": "95833"} -{"city": "SACRAMENTO", "loc": [-121.492052, 38.633418], "pop": 6375, "state": "CA", "_id": "95834"} -{"city": "SACRAMENTO", "loc": [-121.483444, 38.662595], "pop": 373, "state": "CA", "_id": "95835"} -{"city": "SACRAMENTO", "loc": [-121.532259, 38.707346], "pop": 8, "state": "CA", "_id": "95836"} -{"city": "SACRAMENTO", "loc": [-121.60297, 38.681726], "pop": 259, "state": "CA", "_id": "95837"} -{"city": "SACRAMENTO", "loc": [-121.44396, 38.640566], "pop": 26996, "state": "CA", "_id": "95838"} -{"city": "SACRAMENTO", "loc": [-121.340608, 38.662699], "pop": 21161, "state": "CA", "_id": "95841"} -{"city": "SACRAMENTO", "loc": [-121.35046, 38.687385], "pop": 32169, "state": "CA", "_id": "95842"} -{"city": "SACRAMENTO", "loc": [-121.376889, 38.587768], "pop": 25105, "state": "CA", "_id": "95864"} -{"city": "MARYSVILLE", "loc": [-121.522467, 39.141653], "pop": 43785, "state": "CA", "_id": "95901"} -{"city": "ALLEGHANY", "loc": [-120.727176, 39.512698], "pop": 0, "state": "CA", "_id": "95910"} -{"city": "ARBUCKLE", "loc": [-122.027405, 39.013787], "pop": 3851, "state": "CA", "_id": "95912"} -{"city": "BANGOR", "loc": [-121.350499, 39.424862], "pop": 110, "state": "CA", "_id": "95914"} -{"city": "BELDEN", "loc": [-121.325924, 39.921746], "pop": 32, "state": "CA", "_id": "95915"} -{"city": "BERRY CREEK", "loc": [-121.385467, 39.638394], "pop": 1285, "state": "CA", "_id": "95916"} -{"city": "BIGGS", "loc": [-121.695873, 39.414918], "pop": 2784, "state": "CA", "_id": "95917"} -{"city": "BROWNS VALLEY", "loc": [-121.346482, 39.284428], "pop": 1297, "state": "CA", "_id": "95918"} -{"city": "BROWNSVILLE", "loc": [-121.261179, 39.452534], "pop": 1013, "state": "CA", "_id": "95919"} -{"city": "BUTTE CITY", "loc": [-121.978046, 39.456348], "pop": 548, "state": "CA", "_id": "95920"} -{"city": "CAMPTONVILLE", "loc": [-121.023066, 39.450784], "pop": 1090, "state": "CA", "_id": "95922"} -{"city": "CANYONDAM", "loc": [-121.156324, 40.207958], "pop": 39, "state": "CA", "_id": "95923"} -{"city": "COHASSET", "loc": [-121.851806, 39.756466], "pop": 55269, "state": "CA", "_id": "95926"} -{"city": "CHICO", "loc": [-121.81555, 39.729523], "pop": 27452, "state": "CA", "_id": "95928"} -{"city": "COLUSA", "loc": [-122.011563, 39.21311], "pop": 7042, "state": "CA", "_id": "95932"} -{"city": "CRESCENT MILLS", "loc": [-120.881993, 40.081915], "pop": 189, "state": "CA", "_id": "95934"} -{"city": "DOBBINS", "loc": [-121.234386, 39.371469], "pop": 1502, "state": "CA", "_id": "95935"} -{"city": "DOWNIEVILLE", "loc": [-120.677767, 39.570265], "pop": 46, "state": "CA", "_id": "95936"} -{"city": "DUNNIGAN", "loc": [-121.996577, 38.893671], "pop": 850, "state": "CA", "_id": "95937"} -{"city": "DURHAM", "loc": [-121.791983, 39.641599], "pop": 3327, "state": "CA", "_id": "95938"} -{"city": "ELK CREEK", "loc": [-122.557244, 39.598914], "pop": 497, "state": "CA", "_id": "95939"} -{"city": "FORBESTOWN", "loc": [-121.213443, 39.541712], "pop": 517, "state": "CA", "_id": "95941"} -{"city": "BUTTE MEADOWS", "loc": [-121.500205, 40.129931], "pop": 91, "state": "CA", "_id": "95942"} -{"city": "GLENN", "loc": [-122.038443, 39.606871], "pop": 1090, "state": "CA", "_id": "95943"} -{"city": "GOODYEARS BAR", "loc": [-120.820698, 39.572891], "pop": 377, "state": "CA", "_id": "95944"} -{"city": "GRASS VALLEY", "loc": [-121.037401, 39.207617], "pop": 21263, "state": "CA", "_id": "95945"} -{"city": "PENN VALLEY", "loc": [-121.193519, 39.218778], "pop": 7603, "state": "CA", "_id": "95946"} -{"city": "GREENVILLE", "loc": [-120.927299, 40.142404], "pop": 2690, "state": "CA", "_id": "95947"} -{"city": "GRIDLEY", "loc": [-121.689777, 39.358855], "pop": 9499, "state": "CA", "_id": "95948"} -{"city": "GRASS VALLEY", "loc": [-121.069357, 39.1029], "pop": 20973, "state": "CA", "_id": "95949"} -{"city": "LIVE OAK", "loc": [-121.66393, 39.266904], "pop": 6800, "state": "CA", "_id": "95953"} -{"city": "MAGALIA", "loc": [-121.597455, 39.831728], "pop": 10009, "state": "CA", "_id": "95954"} -{"city": "MAXWELL", "loc": [-122.195161, 39.292494], "pop": 1410, "state": "CA", "_id": "95955"} -{"city": "MEADOW VALLEY", "loc": [-121.05629, 39.918488], "pop": 91, "state": "CA", "_id": "95956"} -{"city": "MERIDIAN", "loc": [-121.88138, 39.116812], "pop": 907, "state": "CA", "_id": "95957"} -{"city": "NEVADA CITY", "loc": [-121.019634, 39.275395], "pop": 16670, "state": "CA", "_id": "95959"} -{"city": "NORTH SAN JUAN", "loc": [-121.13498, 39.354037], "pop": 228, "state": "CA", "_id": "95960"} -{"city": "OLIVEHURST", "loc": [-121.550059, 39.089483], "pop": 6418, "state": "CA", "_id": "95961"} -{"city": "OREGON HOUSE", "loc": [-121.209229, 39.314388], "pop": 0, "state": "CA", "_id": "95962"} -{"city": "ORLAND", "loc": [-122.157885, 39.744578], "pop": 14720, "state": "CA", "_id": "95963"} -{"city": "PULGA", "loc": [-121.578396, 39.532967], "pop": 16712, "state": "CA", "_id": "95965"} -{"city": "OROVILLE", "loc": [-121.502029, 39.491448], "pop": 27286, "state": "CA", "_id": "95966"} -{"city": "PALERMO", "loc": [-121.545389, 39.436148], "pop": 1843, "state": "CA", "_id": "95968"} -{"city": "PARADISE", "loc": [-121.603097, 39.759804], "pop": 26327, "state": "CA", "_id": "95969"} -{"city": "PRINCETON", "loc": [-122.030181, 39.428313], "pop": 563, "state": "CA", "_id": "95970"} -{"city": "QUINCY", "loc": [-120.928493, 39.940504], "pop": 6303, "state": "CA", "_id": "95971"} -{"city": "RACKERBY", "loc": [-121.336192, 39.4059], "pop": 260, "state": "CA", "_id": "95972"} -{"city": "ROUGH AND READY", "loc": [-121.150856, 39.228585], "pop": 1811, "state": "CA", "_id": "95975"} -{"city": "SMARTVILLE", "loc": [-121.266716, 39.204305], "pop": 807, "state": "CA", "_id": "95977"} -{"city": "STONYFORD", "loc": [-122.517811, 39.333652], "pop": 683, "state": "CA", "_id": "95979"} -{"city": "LA PORTE", "loc": [-121.074566, 39.604136], "pop": 242, "state": "CA", "_id": "95981"} -{"city": "SUTTER", "loc": [-121.756527, 39.168114], "pop": 3090, "state": "CA", "_id": "95982"} -{"city": "TAYLORSVILLE", "loc": [-120.801797, 40.064911], "pop": 177, "state": "CA", "_id": "95983"} -{"city": "TWAIN", "loc": [-121.150729, 40.002769], "pop": 211, "state": "CA", "_id": "95984"} -{"city": "WILLIAMS", "loc": [-122.162375, 39.148855], "pop": 3094, "state": "CA", "_id": "95987"} -{"city": "WILLOWS", "loc": [-122.199204, 39.523751], "pop": 8034, "state": "CA", "_id": "95988"} -{"city": "YUBA CITY", "loc": [-121.621599, 39.128619], "pop": 30201, "state": "CA", "_id": "95991"} -{"city": "YUBA CITY", "loc": [-121.655168, 39.128193], "pop": 19635, "state": "CA", "_id": "95993"} -{"city": "REDDING", "loc": [-122.411627, 40.560493], "pop": 30690, "state": "CA", "_id": "96001"} -{"city": "REDDING", "loc": [-122.333932, 40.548586], "pop": 29008, "state": "CA", "_id": "96002"} -{"city": "REDDING", "loc": [-122.352962, 40.627751], "pop": 30889, "state": "CA", "_id": "96003"} -{"city": "ADIN", "loc": [-120.943193, 41.21751], "pop": 355, "state": "CA", "_id": "96006"} -{"city": "ANDERSON", "loc": [-122.328218, 40.457432], "pop": 20309, "state": "CA", "_id": "96007"} -{"city": "BELLA VISTA", "loc": [-122.07245, 40.740945], "pop": 1218, "state": "CA", "_id": "96008"} -{"city": "BIG BAR", "loc": [-123.229006, 40.74796], "pop": 344, "state": "CA", "_id": "96010"} -{"city": "BURNEY", "loc": [-121.655036, 40.894927], "pop": 4666, "state": "CA", "_id": "96013"} -{"city": "CALLAHAN", "loc": [-122.764046, 41.383257], "pop": 196, "state": "CA", "_id": "96014"} -{"city": "CANBY", "loc": [-120.921769, 41.466358], "pop": 424, "state": "CA", "_id": "96015"} -{"city": "CASSEL", "loc": [-121.524497, 40.907832], "pop": 566, "state": "CA", "_id": "96016"} -{"city": "SHASTA LAKE", "loc": [-122.365395, 40.680262], "pop": 6405, "state": "CA", "_id": "96019"} -{"city": "CHESTER", "loc": [-121.227338, 40.297457], "pop": 2361, "state": "CA", "_id": "96020"} -{"city": "CORNING", "loc": [-122.195991, 39.929566], "pop": 12436, "state": "CA", "_id": "96021"} -{"city": "COTTONWOOD", "loc": [-122.337463, 40.369072], "pop": 9579, "state": "CA", "_id": "96022"} -{"city": "DOUGLAS CITY", "loc": [-122.923867, 40.634151], "pop": 727, "state": "CA", "_id": "96024"} -{"city": "DUNSMUIR", "loc": [-122.273397, 41.212439], "pop": 2683, "state": "CA", "_id": "96025"} -{"city": "SAWYERS BAR", "loc": [-122.914189, 41.468303], "pop": 2326, "state": "CA", "_id": "96027"} -{"city": "FALL RIVER MILLS", "loc": [-121.460562, 41.03931], "pop": 1843, "state": "CA", "_id": "96028"} -{"city": "FORKS OF SALMON", "loc": [-123.09781, 41.256978], "pop": 469, "state": "CA", "_id": "96031"} -{"city": "FORT JONES", "loc": [-122.883207, 41.617027], "pop": 2363, "state": "CA", "_id": "96032"} -{"city": "FRENCH GULCH", "loc": [-122.622868, 40.703517], "pop": 640, "state": "CA", "_id": "96033"} -{"city": "GAZELLE", "loc": [-122.537122, 41.510485], "pop": 162, "state": "CA", "_id": "96034"} -{"city": "GERBER", "loc": [-122.164937, 40.042997], "pop": 3337, "state": "CA", "_id": "96035"} -{"city": "GRENADA", "loc": [-122.525829, 41.612512], "pop": 703, "state": "CA", "_id": "96038"} -{"city": "HAPPY CAMP", "loc": [-123.388045, 41.801802], "pop": 1885, "state": "CA", "_id": "96039"} -{"city": "HAT CREEK", "loc": [-121.463687, 40.767673], "pop": 150, "state": "CA", "_id": "96040"} -{"city": "HAYFORK", "loc": [-123.163416, 40.550431], "pop": 2671, "state": "CA", "_id": "96041"} -{"city": "HORNBROOK", "loc": [-122.526528, 41.907738], "pop": 905, "state": "CA", "_id": "96044"} -{"city": "HORSE CREEK", "loc": [-123.013919, 41.833732], "pop": 379, "state": "CA", "_id": "96045"} -{"city": "IGO", "loc": [-122.654023, 40.431795], "pop": 205, "state": "CA", "_id": "96047"} -{"city": "HELENA", "loc": [-123.062671, 40.768187], "pop": 606, "state": "CA", "_id": "96048"} -{"city": "KLAMATH RIVER", "loc": [-122.819693, 41.863699], "pop": 174, "state": "CA", "_id": "96050"} -{"city": "LAKEHEAD", "loc": [-122.359281, 40.958775], "pop": 1709, "state": "CA", "_id": "96051"} -{"city": "LEWISTON", "loc": [-122.842591, 40.745986], "pop": 2461, "state": "CA", "_id": "96052"} -{"city": "LOS MOLINOS", "loc": [-122.099175, 40.049735], "pop": 3363, "state": "CA", "_id": "96055"} -{"city": "MCARTHUR", "loc": [-121.214896, 41.111407], "pop": 2797, "state": "CA", "_id": "96056"} -{"city": "MCCLOUD", "loc": [-122.13562, 41.252108], "pop": 1743, "state": "CA", "_id": "96057"} -{"city": "MACDOEL", "loc": [-121.944472, 41.883028], "pop": 1945, "state": "CA", "_id": "96058"} -{"city": "MANTON", "loc": [-121.836521, 40.433125], "pop": 344, "state": "CA", "_id": "96059"} -{"city": "MILLVILLE", "loc": [-122.111088, 40.565316], "pop": 1281, "state": "CA", "_id": "96062"} -{"city": "MINERAL", "loc": [-121.524807, 40.328826], "pop": 172, "state": "CA", "_id": "96063"} -{"city": "MONTAGUE", "loc": [-122.463799, 41.724294], "pop": 4246, "state": "CA", "_id": "96064"} -{"city": "MONTGOMERY CREEK", "loc": [-121.923313, 40.912378], "pop": 823, "state": "CA", "_id": "96065"} -{"city": "MOUNT SHASTA", "loc": [-122.324017, 41.317435], "pop": 6719, "state": "CA", "_id": "96067"} -{"city": "OAK RUN", "loc": [-122.040932, 40.68631], "pop": 1160, "state": "CA", "_id": "96069"} -{"city": "OLD STATION", "loc": [-121.458476, 40.62557], "pop": 213, "state": "CA", "_id": "96071"} -{"city": "PALO CEDRO", "loc": [-122.239805, 40.576661], "pop": 3905, "state": "CA", "_id": "96073"} -{"city": "PAYNES CREEK", "loc": [-121.764952, 40.351415], "pop": 773, "state": "CA", "_id": "96075"} -{"city": "WILDWOOD", "loc": [-122.918013, 40.316528], "pop": 119, "state": "CA", "_id": "96076"} -{"city": "RED BLUFF", "loc": [-122.238281, 40.179535], "pop": 25180, "state": "CA", "_id": "96080"} -{"city": "SCOTT BAR", "loc": [-122.988183, 41.77364], "pop": 21, "state": "CA", "_id": "96085"} -{"city": "SEIAD VALLEY", "loc": [-123.243762, 41.886589], "pop": 311, "state": "CA", "_id": "96086"} -{"city": "SHASTA", "loc": [-122.49685, 40.610896], "pop": 294, "state": "CA", "_id": "96087"} -{"city": "SHINGLETOWN", "loc": [-121.885668, 40.504959], "pop": 3681, "state": "CA", "_id": "96088"} -{"city": "TRINITY CENTER", "loc": [-122.723919, 41.061548], "pop": 362, "state": "CA", "_id": "96091"} -{"city": "WEAVERVILLE", "loc": [-122.935303, 40.731701], "pop": 3188, "state": "CA", "_id": "96093"} -{"city": "EDGEWOOD", "loc": [-122.384803, 41.439466], "pop": 5506, "state": "CA", "_id": "96094"} -{"city": "WHITMORE", "loc": [-121.877076, 40.65255], "pop": 593, "state": "CA", "_id": "96096"} -{"city": "YREKA", "loc": [-122.637604, 41.720558], "pop": 9151, "state": "CA", "_id": "96097"} -{"city": "ALTURAS", "loc": [-120.545584, 41.476742], "pop": 5566, "state": "CA", "_id": "96101"} -{"city": "CROMBERG", "loc": [-120.627397, 39.784745], "pop": 1774, "state": "CA", "_id": "96103"} -{"city": "CEDARVILLE", "loc": [-120.151551, 41.475871], "pop": 991, "state": "CA", "_id": "96104"} -{"city": "CHILCOOT", "loc": [-120.175212, 39.805683], "pop": 470, "state": "CA", "_id": "96105"} -{"city": "CLIO", "loc": [-120.560458, 39.74326], "pop": 84, "state": "CA", "_id": "96106"} -{"city": "COLEVILLE", "loc": [-119.482784, 38.502903], "pop": 1370, "state": "CA", "_id": "96107"} -{"city": "DAVIS CREEK", "loc": [-120.323549, 41.862555], "pop": 285, "state": "CA", "_id": "96108"} -{"city": "DOYLE", "loc": [-120.107693, 40.000799], "pop": 985, "state": "CA", "_id": "96109"} -{"city": "FLORISTON", "loc": [-120.025421, 39.445746], "pop": 169, "state": "CA", "_id": "96111"} -{"city": "FORT BIDWELL", "loc": [-120.161983, 41.864441], "pop": 226, "state": "CA", "_id": "96112"} -{"city": "HERLONG", "loc": [-120.171271, 40.148492], "pop": 1518, "state": "CA", "_id": "96113"} -{"city": "JANESVILLE", "loc": [-120.50982, 40.296325], "pop": 2655, "state": "CA", "_id": "96114"} -{"city": "LAKE CITY", "loc": [-120.181424, 41.668208], "pop": 234, "state": "CA", "_id": "96115"} -{"city": "LITCHFIELD", "loc": [-120.253975, 40.362788], "pop": 23, "state": "CA", "_id": "96117"} -{"city": "LOYALTON", "loc": [-120.229662, 39.662974], "pop": 1500, "state": "CA", "_id": "96118"} -{"city": "HOPE VALLEY", "loc": [-119.807275, 38.76473], "pop": 850, "state": "CA", "_id": "96120"} -{"city": "MILFORD", "loc": [-120.389508, 40.182763], "pop": 376, "state": "CA", "_id": "96121"} -{"city": "PORTOLA", "loc": [-120.466858, 39.810883], "pop": 3685, "state": "CA", "_id": "96122"} -{"city": "RAVENDALE", "loc": [-120.16001, 40.831705], "pop": 89, "state": "CA", "_id": "96123"} -{"city": "CALPINE", "loc": [-120.4442, 39.651699], "pop": 286, "state": "CA", "_id": "96124"} -{"city": "SIERRA CITY", "loc": [-120.624135, 39.559248], "pop": 311, "state": "CA", "_id": "96125"} -{"city": "SIERRAVILLE", "loc": [-120.347789, 39.594294], "pop": 355, "state": "CA", "_id": "96126"} -{"city": "STANDISH", "loc": [-120.406847, 40.350863], "pop": 340, "state": "CA", "_id": "96128"} -{"city": "SUSANVILLE", "loc": [-120.646442, 40.398282], "pop": 19347, "state": "CA", "_id": "96130"} -{"city": "TERMO", "loc": [-120.517378, 40.946667], "pop": 199, "state": "CA", "_id": "96132"} -{"city": "TOPAZ", "loc": [-119.512164, 38.64151], "pop": 87, "state": "CA", "_id": "96133"} -{"city": "TULELAKE", "loc": [-121.434688, 41.931621], "pop": 2613, "state": "CA", "_id": "96134"} -{"city": "VINTON", "loc": [-120.204994, 39.720719], "pop": 0, "state": "CA", "_id": "96135"} -{"city": "WENDEL", "loc": [-120.352156, 40.346233], "pop": 148, "state": "CA", "_id": "96136"} -{"city": "PENINSULA VILLAG", "loc": [-121.109224, 40.270359], "pop": 1843, "state": "CA", "_id": "96137"} -{"city": "CARNELIAN BAY", "loc": [-120.075328, 39.231937], "pop": 620, "state": "CA", "_id": "96140"} -{"city": "HOMEWOOD", "loc": [-120.179035, 39.078157], "pop": 283, "state": "CA", "_id": "96141"} -{"city": "TAHOMA", "loc": [-120.135747, 39.064406], "pop": 1029, "state": "CA", "_id": "96142"} -{"city": "KINGS BEACH", "loc": [-120.023287, 39.240119], "pop": 3247, "state": "CA", "_id": "96143"} -{"city": "TAHOE CITY", "loc": [-120.144532, 39.180618], "pop": 4944, "state": "CA", "_id": "96145"} -{"city": "TAHOE VISTA", "loc": [-120.052128, 39.24475], "pop": 717, "state": "CA", "_id": "96148"} -{"city": "SOUTH LAKE TAHOE", "loc": [-119.986469, 38.916976], "pop": 28975, "state": "CA", "_id": "96150"} -{"city": "TRUCKEE", "loc": [-120.172942, 39.338546], "pop": 9544, "state": "CA", "_id": "96161"} -{"city": "TRUCKEE", "loc": [-120.295031, 39.319321], "pop": 199, "state": "CA", "_id": "96162"} -{"city": "ARVADA", "loc": [-105.098402, 39.794533], "pop": 12065, "state": "CO", "_id": "80002"} -{"city": "ARVADA", "loc": [-105.065549, 39.828572], "pop": 32980, "state": "CO", "_id": "80003"} -{"city": "ARVADA", "loc": [-105.11771, 39.814066], "pop": 33260, "state": "CO", "_id": "80004"} -{"city": "ARVADA", "loc": [-105.109719, 39.842189], "pop": 22613, "state": "CO", "_id": "80005"} -{"city": "AURORA", "loc": [-104.864618, 39.736788], "pop": 27090, "state": "CO", "_id": "80010"} -{"city": "AURORA", "loc": [-104.815233, 39.737809], "pop": 36021, "state": "CO", "_id": "80011"} -{"city": "AURORA", "loc": [-104.837693, 39.698672], "pop": 37711, "state": "CO", "_id": "80012"} -{"city": "AURORA", "loc": [-104.784566, 39.657457], "pop": 45335, "state": "CO", "_id": "80013"} -{"city": "AURORA", "loc": [-104.834954, 39.666171], "pop": 31059, "state": "CO", "_id": "80014"} -{"city": "AURORA", "loc": [-104.787438, 39.62552], "pop": 28161, "state": "CO", "_id": "80015"} -{"city": "AURORA", "loc": [-104.741734, 39.618713], "pop": 4085, "state": "CO", "_id": "80016"} -{"city": "AURORA", "loc": [-104.788093, 39.694827], "pop": 25910, "state": "CO", "_id": "80017"} -{"city": "AURORA", "loc": [-104.707102, 39.710179], "pop": 321, "state": "CO", "_id": "80018"} -{"city": "AURORA", "loc": [-104.706906, 39.765608], "pop": 46, "state": "CO", "_id": "80019"} -{"city": "BROOMFIELD", "loc": [-105.060902, 39.924513], "pop": 31533, "state": "CO", "_id": "80020"} -{"city": "WESTMINSTER", "loc": [-105.102837, 39.875996], "pop": 20461, "state": "CO", "_id": "80021"} -{"city": "COMMERCE CITY", "loc": [-104.911349, 39.825875], "pop": 23205, "state": "CO", "_id": "80022"} -{"city": "LAFAYETTE", "loc": [-105.096346, 39.997964], "pop": 17111, "state": "CO", "_id": "80026"} -{"city": "LOUISVILLE", "loc": [-105.145557, 39.978942], "pop": 12612, "state": "CO", "_id": "80027"} -{"city": "WESTMINSTER", "loc": [-105.037086, 39.854238], "pop": 43235, "state": "CO", "_id": "80030"} -{"city": "WHEAT RIDGE", "loc": [-105.096195, 39.774036], "pop": 23040, "state": "CO", "_id": "80033"} -{"city": "AURORA", "loc": [-104.837954, 39.748014], "pop": 1715, "state": "CO", "_id": "80045"} -{"city": "AGATE", "loc": [-103.984575, 39.420256], "pop": 230, "state": "CO", "_id": "80101"} -{"city": "BENNETT", "loc": [-104.427284, 39.725398], "pop": 3885, "state": "CO", "_id": "80102"} -{"city": "BYERS", "loc": [-104.201872, 39.698454], "pop": 1448, "state": "CO", "_id": "80103"} -{"city": "CASTLE ROCK", "loc": [-104.860187, 39.39256], "pop": 11763, "state": "CO", "_id": "80104"} -{"city": "DEER TRAIL", "loc": [-104.068003, 39.593121], "pop": 634, "state": "CO", "_id": "80105"} -{"city": "ELBERT", "loc": [-104.574631, 39.096892], "pop": 1808, "state": "CO", "_id": "80106"} -{"city": "ELIZABETH", "loc": [-104.591961, 39.383618], "pop": 4973, "state": "CO", "_id": "80107"} -{"city": "CHERRY HILLS VIL", "loc": [-104.990022, 39.646027], "pop": 40226, "state": "CO", "_id": "80110"} -{"city": "CHERRY HILLS VIL", "loc": [-104.882832, 39.610327], "pop": 20230, "state": "CO", "_id": "80111"} -{"city": "ENGLEWOOD", "loc": [-104.901115, 39.58051], "pop": 20210, "state": "CO", "_id": "80112"} -{"city": "FRANKTOWN", "loc": [-104.725569, 39.372841], "pop": 3742, "state": "CO", "_id": "80116"} -{"city": "KIOWA", "loc": [-104.452263, 39.323972], "pop": 680, "state": "CO", "_id": "80117"} -{"city": "LARKSPUR", "loc": [-104.854587, 39.201079], "pop": 1424, "state": "CO", "_id": "80118"} -{"city": "LITTLETON", "loc": [-105.0044, 39.599426], "pop": 24992, "state": "CO", "_id": "80120"} -{"city": "GREENWOOD VILLAG", "loc": [-104.957285, 39.605835], "pop": 17238, "state": "CO", "_id": "80121"} -{"city": "LITTLETON", "loc": [-104.955673, 39.581418], "pop": 31135, "state": "CO", "_id": "80122"} -{"city": "BOW MAR", "loc": [-105.07766, 39.596854], "pop": 59418, "state": "CO", "_id": "80123"} -{"city": "LITTLETON", "loc": [-104.897204, 39.55061], "pop": 5393, "state": "CO", "_id": "80124"} -{"city": "LITTLETON", "loc": [-105.056098, 39.484466], "pop": 3230, "state": "CO", "_id": "80125"} -{"city": "HIGHLANDS RANCH", "loc": [-104.963751, 39.55134], "pop": 13649, "state": "CO", "_id": "80126"} -{"city": "LITTLETON", "loc": [-105.132811, 39.591968], "pop": 23204, "state": "CO", "_id": "80127"} -{"city": "MONUMENT", "loc": [-104.85416, 39.100726], "pop": 7411, "state": "CO", "_id": "80132"} -{"city": "PALMER LAKE", "loc": [-104.914795, 39.120498], "pop": 1237, "state": "CO", "_id": "80133"} -{"city": "PARKER", "loc": [-104.734904, 39.505466], "pop": 19466, "state": "CO", "_id": "80134"} -{"city": "DECKERS", "loc": [-105.008305, 39.330109], "pop": 3257, "state": "CO", "_id": "80135"} -{"city": "STRASBURG", "loc": [-104.268258, 39.781359], "pop": 1197, "state": "CO", "_id": "80136"} -{"city": "WATKINS", "loc": [-104.583391, 39.762317], "pop": 406, "state": "CO", "_id": "80137"} -{"city": "DENVER", "loc": [-104.994591, 39.749107], "pop": 2816, "state": "CO", "_id": "80202"} -{"city": "DENVER", "loc": [-104.981111, 39.731285], "pop": 15775, "state": "CO", "_id": "80203"} -{"city": "DENVER", "loc": [-105.025854, 39.734022], "pop": 27439, "state": "CO", "_id": "80204"} -{"city": "DENVER", "loc": [-104.966141, 39.758993], "pop": 24169, "state": "CO", "_id": "80205"} -{"city": "DENVER", "loc": [-104.9524, 39.733109], "pop": 19145, "state": "CO", "_id": "80206"} -{"city": "DENVER", "loc": [-104.91771, 39.758425], "pop": 20955, "state": "CO", "_id": "80207"} -{"city": "DENVER", "loc": [-104.968587, 39.707437], "pop": 19691, "state": "CO", "_id": "80209"} -{"city": "DENVER", "loc": [-104.963124, 39.679003], "pop": 30868, "state": "CO", "_id": "80210"} -{"city": "DENVER", "loc": [-105.020377, 39.766515], "pop": 34679, "state": "CO", "_id": "80211"} -{"city": "MOUNTAIN VIEW", "loc": [-105.046979, 39.772396], "pop": 17745, "state": "CO", "_id": "80212"} -{"city": "EDGEWATER", "loc": [-105.062036, 39.746931], "pop": 13154, "state": "CO", "_id": "80214"} -{"city": "LAKEWOOD", "loc": [-105.102329, 39.744033], "pop": 27556, "state": "CO", "_id": "80215"} -{"city": "DENVER", "loc": [-104.966946, 39.783469], "pop": 9113, "state": "CO", "_id": "80216"} -{"city": "DENVER", "loc": [-104.971652, 39.732747], "pop": 14916, "state": "CO", "_id": "80218"} -{"city": "DENVER", "loc": [-105.034134, 39.695624], "pop": 48234, "state": "CO", "_id": "80219"} -{"city": "DENVER", "loc": [-104.912866, 39.7312], "pop": 35520, "state": "CO", "_id": "80220"} -{"city": "FEDERAL HEIGHTS", "loc": [-105.007985, 39.840562], "pop": 54069, "state": "CO", "_id": "80221"} -{"city": "GLENDALE", "loc": [-104.927992, 39.682803], "pop": 28373, "state": "CO", "_id": "80222"} -{"city": "DENVER", "loc": [-105.002799, 39.700239], "pop": 16692, "state": "CO", "_id": "80223"} -{"city": "DENVER", "loc": [-104.910778, 39.687995], "pop": 14918, "state": "CO", "_id": "80224"} -{"city": "LAKEWOOD", "loc": [-105.066703, 39.712186], "pop": 13675, "state": "CO", "_id": "80226"} -{"city": "DENVER", "loc": [-105.085359, 39.666746], "pop": 26932, "state": "CO", "_id": "80227"} -{"city": "LAKEWOOD", "loc": [-105.143009, 39.696898], "pop": 25008, "state": "CO", "_id": "80228"} -{"city": "THORNTON", "loc": [-104.961749, 39.860998], "pop": 33512, "state": "CO", "_id": "80229"} -{"city": "DENVER", "loc": [-104.884326, 39.679324], "pop": 35985, "state": "CO", "_id": "80231"} -{"city": "LAKEWOOD", "loc": [-105.094524, 39.697282], "pop": 35087, "state": "CO", "_id": "80232"} -{"city": "NORTHGLENN", "loc": [-104.958257, 39.901222], "pop": 30749, "state": "CO", "_id": "80233"} -{"city": "NORTHGLENN", "loc": [-105.004474, 39.905479], "pop": 13350, "state": "CO", "_id": "80234"} -{"city": "DENVER", "loc": [-105.079466, 39.647175], "pop": 5783, "state": "CO", "_id": "80235"} -{"city": "DENVER", "loc": [-105.037595, 39.653535], "pop": 12979, "state": "CO", "_id": "80236"} -{"city": "DENVER", "loc": [-104.89866, 39.64314], "pop": 14211, "state": "CO", "_id": "80237"} -{"city": "DENVER", "loc": [-104.828837, 39.787757], "pop": 17622, "state": "CO", "_id": "80239"} -{"city": "NORTHGLENN", "loc": [-104.941809, 39.927792], "pop": 10108, "state": "CO", "_id": "80241"} -{"city": "DENVER", "loc": [-104.75565, 39.778264], "pop": 2740, "state": "CO", "_id": "80249"} -{"city": "BOULDER", "loc": [-105.21426, 40.049733], "pop": 18174, "state": "CO", "_id": "80301"} -{"city": "BOULDER", "loc": [-105.285131, 40.017235], "pop": 29384, "state": "CO", "_id": "80302"} -{"city": "BOULDER", "loc": [-105.239178, 39.991381], "pop": 39860, "state": "CO", "_id": "80303"} -{"city": "BOULDER", "loc": [-105.277073, 40.037482], "pop": 21550, "state": "CO", "_id": "80304"} -{"city": "GOLDEN", "loc": [-105.191528, 39.730548], "pop": 32876, "state": "CO", "_id": "80401"} -{"city": "GOLDEN", "loc": [-105.282516, 39.823219], "pop": 13159, "state": "CO", "_id": "80403"} -{"city": "BAILEY", "loc": [-105.469282, 39.448233], "pop": 4448, "state": "CO", "_id": "80421"} -{"city": "BLACK HAWK", "loc": [-105.503991, 39.801105], "pop": 551, "state": "CO", "_id": "80422"} -{"city": "BOND", "loc": [-106.676273, 39.869119], "pop": 149, "state": "CO", "_id": "80423"} -{"city": "CLARK", "loc": [-106.921482, 40.726783], "pop": 455, "state": "CO", "_id": "80428"} -{"city": "CLIMAX", "loc": [-106.258027, 39.298804], "pop": 346, "state": "CO", "_id": "80429"} -{"city": "COALMONT", "loc": [-106.532134, 40.538252], "pop": 88, "state": "CO", "_id": "80430"} -{"city": "CONIFER", "loc": [-105.316873, 39.519735], "pop": 5919, "state": "CO", "_id": "80433"} -{"city": "KEYSTONE", "loc": [-106.057345, 39.574827], "pop": 12881, "state": "CO", "_id": "80435"} -{"city": "EVERGREEN", "loc": [-105.340248, 39.637405], "pop": 19250, "state": "CO", "_id": "80439"} -{"city": "FAIRPLAY", "loc": [-105.999416, 39.225617], "pop": 1030, "state": "CO", "_id": "80440"} -{"city": "FOXTON", "loc": [-105.24815, 39.372056], "pop": 127, "state": "CO", "_id": "80441"} -{"city": "GRANBY", "loc": [-105.889916, 40.012834], "pop": 4839, "state": "CO", "_id": "80446"} -{"city": "GRAND LAKE", "loc": [-105.860488, 40.228862], "pop": 1202, "state": "CO", "_id": "80447"} -{"city": "IDAHO SPRINGS", "loc": [-105.598261, 39.740192], "pop": 5113, "state": "CO", "_id": "80452"} -{"city": "JAMESTOWN", "loc": [-105.418971, 40.10056], "pop": 654, "state": "CO", "_id": "80455"} -{"city": "JEFFERSON", "loc": [-105.78633, 39.300926], "pop": 157, "state": "CO", "_id": "80456"} -{"city": "KREMMLING", "loc": [-106.395472, 40.063224], "pop": 1580, "state": "CO", "_id": "80459"} -{"city": "LEADVILLE", "loc": [-106.301545, 39.249742], "pop": 5544, "state": "CO", "_id": "80461"} -{"city": "MC COY", "loc": [-106.730906, 39.913377], "pop": 61, "state": "CO", "_id": "80463"} -{"city": "MORRISON", "loc": [-105.174641, 39.612452], "pop": 13657, "state": "CO", "_id": "80465"} -{"city": "NEDERLAND", "loc": [-105.481265, 39.970259], "pop": 2621, "state": "CO", "_id": "80466"} -{"city": "OAK CREEK", "loc": [-106.929621, 40.256733], "pop": 1135, "state": "CO", "_id": "80467"} -{"city": "PARSHALL", "loc": [-106.225492, 40.053765], "pop": 345, "state": "CO", "_id": "80468"} -{"city": "PINE", "loc": [-105.374116, 39.46667], "pop": 2827, "state": "CO", "_id": "80470"} -{"city": "PINECLIFFE", "loc": [-105.354004, 39.942898], "pop": 225, "state": "CO", "_id": "80471"} -{"city": "ROLLINSVILLE", "loc": [-105.472596, 39.910757], "pop": 343, "state": "CO", "_id": "80474"} -{"city": "TOPONAS", "loc": [-106.908172, 40.119601], "pop": 697, "state": "CO", "_id": "80479"} -{"city": "WALDEN", "loc": [-106.276728, 40.709978], "pop": 1517, "state": "CO", "_id": "80480"} -{"city": "WARD", "loc": [-105.508023, 40.072572], "pop": 159, "state": "CO", "_id": "80481"} -{"city": "STEAMBOAT SPRING", "loc": [-106.8457, 40.474124], "pop": 9773, "state": "CO", "_id": "80487"} -{"city": "LONGMONT", "loc": [-105.10095, 40.177921], "pop": 47166, "state": "CO", "_id": "80501"} -{"city": "LONGMONT", "loc": [-105.162432, 40.15588], "pop": 16814, "state": "CO", "_id": "80503"} -{"city": "LONGMONT", "loc": [-104.950446, 40.130615], "pop": 3716, "state": "CO", "_id": "80504"} -{"city": "ALLENSPARK", "loc": [-105.520064, 40.226775], "pop": 183, "state": "CO", "_id": "80510"} -{"city": "BELLVUE", "loc": [-105.260977, 40.626528], "pop": 1982, "state": "CO", "_id": "80512"} -{"city": "BERTHOUD", "loc": [-105.105459, 40.299333], "pop": 6886, "state": "CO", "_id": "80513"} -{"city": "DACONO", "loc": [-104.929705, 40.08361], "pop": 2223, "state": "CO", "_id": "80514"} -{"city": "DRAKE", "loc": [-105.296925, 40.443257], "pop": 721, "state": "CO", "_id": "80515"} -{"city": "ERIE", "loc": [-105.068583, 40.059746], "pop": 304, "state": "CO", "_id": "80516"} -{"city": "ESTES PARK", "loc": [-105.514163, 40.365761], "pop": 6428, "state": "CO", "_id": "80517"} -{"city": "FORT COLLINS", "loc": [-105.103884, 40.581293], "pop": 30059, "state": "CO", "_id": "80521"} -{"city": "FORT COLLINS", "loc": [-105.05811, 40.59865], "pop": 21204, "state": "CO", "_id": "80524"} -{"city": "FORT COLLINS", "loc": [-105.054715, 40.538354], "pop": 31263, "state": "CO", "_id": "80525"} -{"city": "FORT COLLINS", "loc": [-105.107646, 40.547294], "pop": 29180, "state": "CO", "_id": "80526"} -{"city": "JOHNSTOWN", "loc": [-104.923558, 40.335526], "pop": 2601, "state": "CO", "_id": "80534"} -{"city": "LAPORTE", "loc": [-105.148757, 40.634683], "pop": 2300, "state": "CO", "_id": "80535"} -{"city": "VIRGINIA DALE", "loc": [-105.367745, 40.779878], "pop": 513, "state": "CO", "_id": "80536"} -{"city": "LOVELAND", "loc": [-105.09164, 40.384917], "pop": 24502, "state": "CO", "_id": "80537"} -{"city": "LOVELAND", "loc": [-105.089985, 40.426239], "pop": 26449, "state": "CO", "_id": "80538"} -{"city": "LYONS", "loc": [-105.323071, 40.235715], "pop": 3696, "state": "CO", "_id": "80540"} -{"city": "MILLIKEN", "loc": [-104.876137, 40.310656], "pop": 2632, "state": "CO", "_id": "80543"} -{"city": "RED FEATHER LAKE", "loc": [-105.624455, 40.796451], "pop": 440, "state": "CO", "_id": "80545"} -{"city": "WELLINGTON", "loc": [-105.031844, 40.725525], "pop": 3309, "state": "CO", "_id": "80549"} -{"city": "WINDSOR", "loc": [-104.899442, 40.483663], "pop": 6724, "state": "CO", "_id": "80550"} -{"city": "LOCHBUI", "loc": [-104.810278, 39.980553], "pop": 20533, "state": "CO", "_id": "80601"} -{"city": "AULT", "loc": [-104.735629, 40.593772], "pop": 2684, "state": "CO", "_id": "80610"} -{"city": "BRIGGSDALE", "loc": [-104.28707, 40.639192], "pop": 259, "state": "CO", "_id": "80611"} -{"city": "CARR", "loc": [-104.885865, 40.866551], "pop": 94, "state": "CO", "_id": "80612"} -{"city": "EATON", "loc": [-104.714559, 40.527272], "pop": 2902, "state": "CO", "_id": "80615"} -{"city": "EVANS", "loc": [-104.697095, 40.380255], "pop": 6262, "state": "CO", "_id": "80620"} -{"city": "WATTENBURG", "loc": [-104.865639, 40.078876], "pop": 12913, "state": "CO", "_id": "80621"} -{"city": "GALETON", "loc": [-104.597254, 40.515034], "pop": 360, "state": "CO", "_id": "80622"} -{"city": "GILL", "loc": [-104.499995, 40.469586], "pop": 130, "state": "CO", "_id": "80624"} -{"city": "GARDEN CITY", "loc": [-104.704756, 40.413968], "pop": 53905, "state": "CO", "_id": "80631"} -{"city": "GREELEY", "loc": [-104.754113, 40.410947], "pop": 14100, "state": "CO", "_id": "80634"} -{"city": "HENDERSON", "loc": [-104.871834, 39.898304], "pop": 1388, "state": "CO", "_id": "80640"} -{"city": "HUDSON", "loc": [-104.653208, 40.060555], "pop": 2369, "state": "CO", "_id": "80642"} -{"city": "KEENESBURG", "loc": [-104.446366, 40.095847], "pop": 1979, "state": "CO", "_id": "80643"} -{"city": "KERSEY", "loc": [-104.528768, 40.396305], "pop": 2843, "state": "CO", "_id": "80644"} -{"city": "LA SALLE", "loc": [-104.726784, 40.321138], "pop": 4455, "state": "CO", "_id": "80645"} -{"city": "NUNN", "loc": [-104.785012, 40.726483], "pop": 505, "state": "CO", "_id": "80648"} -{"city": "ORCHARD", "loc": [-104.097325, 40.363946], "pop": 277, "state": "CO", "_id": "80649"} -{"city": "PIERCE", "loc": [-104.763764, 40.635911], "pop": 1020, "state": "CO", "_id": "80650"} -{"city": "PLATTEVILLE", "loc": [-104.802776, 40.213121], "pop": 2577, "state": "CO", "_id": "80651"} -{"city": "ROGGEN", "loc": [-104.315747, 40.207445], "pop": 150, "state": "CO", "_id": "80652"} -{"city": "WELDONA", "loc": [-103.967755, 40.368093], "pop": 328, "state": "CO", "_id": "80653"} -{"city": "HOYT", "loc": [-104.052814, 40.205651], "pop": 1746, "state": "CO", "_id": "80654"} -{"city": "FORT MORGAN", "loc": [-103.803119, 40.254084], "pop": 13263, "state": "CO", "_id": "80701"} -{"city": "AKRON", "loc": [-103.225885, 40.180315], "pop": 2559, "state": "CO", "_id": "80720"} -{"city": "AMHERST", "loc": [-102.170567, 40.682386], "pop": 86, "state": "CO", "_id": "80721"} -{"city": "ATWOOD", "loc": [-103.039301, 40.517432], "pop": 140, "state": "CO", "_id": "80722"} -{"city": "BRUSH", "loc": [-103.62788, 40.260255], "pop": 5603, "state": "CO", "_id": "80723"} -{"city": "CROOK", "loc": [-102.847174, 40.874743], "pop": 638, "state": "CO", "_id": "80726"} -{"city": "ECKLEY", "loc": [-102.482776, 40.113775], "pop": 242, "state": "CO", "_id": "80727"} -{"city": "FLEMING", "loc": [-102.868821, 40.637019], "pop": 853, "state": "CO", "_id": "80728"} -{"city": "GROVER", "loc": [-104.234613, 40.871635], "pop": 492, "state": "CO", "_id": "80729"} -{"city": "HAXTUN", "loc": [-102.605175, 40.640587], "pop": 1569, "state": "CO", "_id": "80731"} -{"city": "HILLROSE", "loc": [-103.541816, 40.307186], "pop": 516, "state": "CO", "_id": "80733"} -{"city": "HOLYOKE", "loc": [-102.282545, 40.582542], "pop": 2642, "state": "CO", "_id": "80734"} -{"city": "HALE", "loc": [-102.211563, 39.733147], "pop": 393, "state": "CO", "_id": "80735"} -{"city": "ILIFF", "loc": [-103.096808, 40.769174], "pop": 778, "state": "CO", "_id": "80736"} -{"city": "JULESBURG", "loc": [-102.257501, 40.97083], "pop": 1712, "state": "CO", "_id": "80737"} -{"city": "LINDON", "loc": [-103.314471, 39.830849], "pop": 136, "state": "CO", "_id": "80740"} -{"city": "WILLARD", "loc": [-103.34471, 40.511548], "pop": 959, "state": "CO", "_id": "80741"} -{"city": "NEW RAYMER", "loc": [-103.838982, 40.685079], "pop": 310, "state": "CO", "_id": "80742"} -{"city": "OTIS", "loc": [-102.939219, 40.202989], "pop": 1102, "state": "CO", "_id": "80743"} -{"city": "OVID", "loc": [-102.387396, 40.945865], "pop": 563, "state": "CO", "_id": "80744"} -{"city": "PADRONI", "loc": [-103.358163, 40.954911], "pop": 72, "state": "CO", "_id": "80745"} -{"city": "PEETZ", "loc": [-103.116606, 40.95195], "pop": 451, "state": "CO", "_id": "80747"} -{"city": "SEDGWICK", "loc": [-102.529122, 40.910317], "pop": 415, "state": "CO", "_id": "80749"} -{"city": "SNYDER", "loc": [-103.597134, 40.330655], "pop": 134, "state": "CO", "_id": "80750"} -{"city": "STERLING", "loc": [-103.221183, 40.63062], "pop": 13524, "state": "CO", "_id": "80751"} -{"city": "STONEHAM", "loc": [-103.638687, 40.686994], "pop": 141, "state": "CO", "_id": "80754"} -{"city": "VERNON", "loc": [-102.319343, 39.933127], "pop": 318, "state": "CO", "_id": "80755"} -{"city": "LAST CHANCE", "loc": [-103.578033, 39.938105], "pop": 265, "state": "CO", "_id": "80757"} -{"city": "LAIRD", "loc": [-102.233751, 40.104917], "pop": 3396, "state": "CO", "_id": "80758"} -{"city": "YUMA", "loc": [-102.707174, 40.130063], "pop": 3811, "state": "CO", "_id": "80759"} -{"city": "ANTON", "loc": [-103.434296, 39.686527], "pop": 240, "state": "CO", "_id": "80801"} -{"city": "ARAPAHOE", "loc": [-102.19401, 38.841716], "pop": 335, "state": "CO", "_id": "80802"} -{"city": "ARRIBA", "loc": [-103.270968, 39.30253], "pop": 388, "state": "CO", "_id": "80804"} -{"city": "BETHUNE", "loc": [-102.428142, 39.344771], "pop": 543, "state": "CO", "_id": "80805"} -{"city": "BURLINGTON", "loc": [-102.258281, 39.310649], "pop": 3982, "state": "CO", "_id": "80807"} -{"city": "CALHAN", "loc": [-104.355274, 38.964773], "pop": 2955, "state": "CO", "_id": "80808"} -{"city": "NORTH POLE", "loc": [-104.993684, 38.921314], "pop": 2259, "state": "CO", "_id": "80809"} -{"city": "CHEYENNE WELLS", "loc": [-102.358173, 38.819762], "pop": 1364, "state": "CO", "_id": "80810"} -{"city": "COPE", "loc": [-102.988098, 39.746227], "pop": 626, "state": "CO", "_id": "80812"} -{"city": "DIVIDE", "loc": [-105.19937, 38.957622], "pop": 1675, "state": "CO", "_id": "80814"} -{"city": "FLAGLER", "loc": [-103.062395, 39.312654], "pop": 854, "state": "CO", "_id": "80815"} -{"city": "FLORISSANT", "loc": [-105.226106, 38.827669], "pop": 2480, "state": "CO", "_id": "80816"} -{"city": "FOUNTAIN", "loc": [-104.700469, 38.699563], "pop": 11570, "state": "CO", "_id": "80817"} -{"city": "GENOA", "loc": [-103.460689, 39.338328], "pop": 320, "state": "CO", "_id": "80818"} -{"city": "GUFFEY", "loc": [-105.57835, 38.814584], "pop": 300, "state": "CO", "_id": "80820"} -{"city": "HUGO", "loc": [-103.498971, 39.084318], "pop": 1064, "state": "CO", "_id": "80821"} -{"city": "JOES", "loc": [-102.615134, 39.672771], "pop": 315, "state": "CO", "_id": "80822"} -{"city": "KARVAL", "loc": [-103.500613, 38.71194], "pop": 339, "state": "CO", "_id": "80823"} -{"city": "KIRK", "loc": [-102.477554, 39.617072], "pop": 479, "state": "CO", "_id": "80824"} -{"city": "KIT CARSON", "loc": [-102.819842, 38.803984], "pop": 698, "state": "CO", "_id": "80825"} -{"city": "LAKE GEORGE", "loc": [-105.434654, 39.034233], "pop": 587, "state": "CO", "_id": "80827"} -{"city": "LIMON", "loc": [-103.685572, 39.27126], "pop": 2244, "state": "CO", "_id": "80828"} -{"city": "MANITOU SPRINGS", "loc": [-104.905839, 38.854994], "pop": 4989, "state": "CO", "_id": "80829"} -{"city": "MATHESON", "loc": [-103.913165, 39.132044], "pop": 439, "state": "CO", "_id": "80830"} -{"city": "PEYTON", "loc": [-104.54722, 38.954097], "pop": 3707, "state": "CO", "_id": "80831"} -{"city": "RAMAH", "loc": [-104.124733, 39.073571], "pop": 561, "state": "CO", "_id": "80832"} -{"city": "RUSH", "loc": [-104.024065, 38.764248], "pop": 408, "state": "CO", "_id": "80833"} -{"city": "SEIBERT", "loc": [-102.882184, 39.318329], "pop": 364, "state": "CO", "_id": "80834"} -{"city": "SIMLA", "loc": [-104.086236, 39.146457], "pop": 580, "state": "CO", "_id": "80835"} -{"city": "STRATTON", "loc": [-102.597928, 39.308733], "pop": 1102, "state": "CO", "_id": "80836"} -{"city": "UNITED STATES AI", "loc": [-104.860139, 38.990448], "pop": 9062, "state": "CO", "_id": "80840"} -{"city": "VONA", "loc": [-102.739287, 39.323564], "pop": 295, "state": "CO", "_id": "80861"} -{"city": "WOODLAND PARK", "loc": [-105.062292, 38.996929], "pop": 8272, "state": "CO", "_id": "80863"} -{"city": "YODER", "loc": [-104.218353, 38.775252], "pop": 511, "state": "CO", "_id": "80864"} -{"city": "COLORADO SPRINGS", "loc": [-104.814466, 38.838832], "pop": 13972, "state": "CO", "_id": "80903"} -{"city": "COLORADO SPRINGS", "loc": [-104.859513, 38.853318], "pop": 17366, "state": "CO", "_id": "80904"} -{"city": "COLORADO SPRINGS", "loc": [-104.836997, 38.837692], "pop": 3435, "state": "CO", "_id": "80905"} -{"city": "COLORADO SPRINGS", "loc": [-104.819893, 38.790164], "pop": 38856, "state": "CO", "_id": "80906"} -{"city": "COLORADO SPRINGS", "loc": [-104.817034, 38.876001], "pop": 25123, "state": "CO", "_id": "80907"} -{"city": "COLORADO SPRINGS", "loc": [-104.693331, 39.023745], "pop": 6803, "state": "CO", "_id": "80908"} -{"city": "COLORADO SPRINGS", "loc": [-104.773483, 38.852038], "pop": 34887, "state": "CO", "_id": "80909"} -{"city": "COLORADO SPRINGS", "loc": [-104.770299, 38.815164], "pop": 24867, "state": "CO", "_id": "80910"} -{"city": "COLORADO SPRINGS", "loc": [-104.722322, 38.745665], "pop": 22116, "state": "CO", "_id": "80911"} -{"city": "FORT CARSON", "loc": [-104.782218, 38.741967], "pop": 11309, "state": "CO", "_id": "80913"} -{"city": "CHEYENNE MTN AFB", "loc": [-104.719052, 38.784241], "pop": 0, "state": "CO", "_id": "80914"} -{"city": "COLORADO SPRINGS", "loc": [-104.713422, 38.855845], "pop": 18209, "state": "CO", "_id": "80915"} -{"city": "COLORADO SPRINGS", "loc": [-104.74034, 38.807619], "pop": 26402, "state": "CO", "_id": "80916"} -{"city": "COLORADO SPRINGS", "loc": [-104.739904, 38.886027], "pop": 27664, "state": "CO", "_id": "80917"} -{"city": "COLORADO SPRINGS", "loc": [-104.773444, 38.912924], "pop": 36410, "state": "CO", "_id": "80918"} -{"city": "COLORADO SPRINGS", "loc": [-104.84642, 38.926795], "pop": 17468, "state": "CO", "_id": "80919"} -{"city": "COLORADO SPRINGS", "loc": [-104.766951, 38.949732], "pop": 16907, "state": "CO", "_id": "80920"} -{"city": "COLORADO SPRINGS", "loc": [-104.814042, 39.048674], "pop": 3539, "state": "CO", "_id": "80921"} -{"city": "COLORADO SPRINGS", "loc": [-104.698161, 38.90503], "pop": 1836, "state": "CO", "_id": "80922"} -{"city": "COLORADO SPRINGS", "loc": [-104.660087, 38.731329], "pop": 2550, "state": "CO", "_id": "80925"} -{"city": "COLORADO SPRINGS", "loc": [-104.85051, 38.698073], "pop": 1040, "state": "CO", "_id": "80926"} -{"city": "COLORADO SPRINGS", "loc": [-104.457043, 38.623261], "pop": 273, "state": "CO", "_id": "80928"} -{"city": "COLORADO SPRINGS", "loc": [-104.607857, 38.796837], "pop": 197, "state": "CO", "_id": "80929"} -{"city": "COLORADO SPRINGS", "loc": [-104.526924, 38.828926], "pop": 484, "state": "CO", "_id": "80930"} -{"city": "PUEBLO", "loc": [-104.584828, 38.287876], "pop": 28837, "state": "CO", "_id": "81001"} -{"city": "PUEBLO", "loc": [-104.62337, 38.284277], "pop": 13461, "state": "CO", "_id": "81003"} -{"city": "PUEBLO", "loc": [-104.627829, 38.244063], "pop": 25748, "state": "CO", "_id": "81004"} -{"city": "PUEBLO", "loc": [-104.660031, 38.235157], "pop": 26273, "state": "CO", "_id": "81005"} -{"city": "PUEBLO", "loc": [-104.531834, 38.24465], "pop": 12277, "state": "CO", "_id": "81006"} -{"city": "PUEBLO WEST", "loc": [-104.743264, 38.319975], "pop": 4592, "state": "CO", "_id": "81007"} -{"city": "PUEBLO", "loc": [-104.628433, 38.313251], "pop": 5953, "state": "CO", "_id": "81008"} -{"city": "AGUILAR", "loc": [-104.676926, 37.393304], "pop": 928, "state": "CO", "_id": "81020"} -{"city": "ARLINGTON", "loc": [-103.369741, 38.40677], "pop": 28, "state": "CO", "_id": "81021"} -{"city": "NORTH AVONDALE", "loc": [-104.359686, 38.211603], "pop": 1483, "state": "CO", "_id": "81022"} -{"city": "BEULAH", "loc": [-104.97245, 38.083712], "pop": 867, "state": "CO", "_id": "81023"} -{"city": "BOONE", "loc": [-104.25851, 38.264614], "pop": 976, "state": "CO", "_id": "81025"} -{"city": "BRANDON", "loc": [-102.781906, 38.485133], "pop": 1094, "state": "CO", "_id": "81026"} -{"city": "BRANSON", "loc": [-103.874115, 37.051775], "pop": 140, "state": "CO", "_id": "81027"} -{"city": "BRISTOL", "loc": [-102.342642, 38.133184], "pop": 256, "state": "CO", "_id": "81028"} -{"city": "CAMPO", "loc": [-102.546423, 37.119547], "pop": 487, "state": "CO", "_id": "81029"} -{"city": "CHIVINGTON", "loc": [-102.50536, 38.444141], "pop": 123, "state": "CO", "_id": "81036"} -{"city": "FOWLER", "loc": [-104.029908, 38.123071], "pop": 1877, "state": "CO", "_id": "81039"} -{"city": "FARISITA", "loc": [-105.237397, 37.763753], "pop": 428, "state": "CO", "_id": "81040"} -{"city": "GRANADA", "loc": [-102.32712, 38.054485], "pop": 741, "state": "CO", "_id": "81041"} -{"city": "HARTMAN", "loc": [-102.186609, 38.145291], "pop": 256, "state": "CO", "_id": "81043"} -{"city": "CADDOA", "loc": [-102.933145, 38.107347], "pop": 263, "state": "CO", "_id": "81044"} -{"city": "HASWELL", "loc": [-103.150543, 38.447431], "pop": 109, "state": "CO", "_id": "81045"} -{"city": "HOLLY", "loc": [-102.141466, 38.0205], "pop": 1480, "state": "CO", "_id": "81047"} -{"city": "VILLEGREEN", "loc": [-103.358348, 37.331189], "pop": 389, "state": "CO", "_id": "81049"} -{"city": "TIMPAS", "loc": [-103.549068, 37.991552], "pop": 11742, "state": "CO", "_id": "81050"} -{"city": "LAMAR", "loc": [-102.619195, 38.084136], "pop": 9903, "state": "CO", "_id": "81052"} -{"city": "DEORA", "loc": [-103.208492, 38.065514], "pop": 4217, "state": "CO", "_id": "81054"} -{"city": "CUCHARA", "loc": [-105.012994, 37.498292], "pop": 1234, "state": "CO", "_id": "81055"} -{"city": "MC CLAVE", "loc": [-102.816934, 38.150484], "pop": 568, "state": "CO", "_id": "81057"} -{"city": "MANZANOLA", "loc": [-103.876602, 38.110861], "pop": 961, "state": "CO", "_id": "81058"} -{"city": "DELHI", "loc": [-104.13074, 37.478533], "pop": 237, "state": "CO", "_id": "81059"} -{"city": "OLNEY SPRINGS", "loc": [-103.941033, 38.201877], "pop": 706, "state": "CO", "_id": "81062"} -{"city": "ORDWAY", "loc": [-103.800277, 38.209546], "pop": 2692, "state": "CO", "_id": "81063"} -{"city": "UTLEYVILLE", "loc": [-102.893106, 37.355885], "pop": 383, "state": "CO", "_id": "81064"} -{"city": "ROCKY FORD", "loc": [-103.725143, 38.049016], "pop": 5946, "state": "CO", "_id": "81067"} -{"city": "RYE", "loc": [-104.886257, 37.937145], "pop": 2369, "state": "CO", "_id": "81069"} -{"city": "TOWNER", "loc": [-102.295367, 38.462172], "pop": 162, "state": "CO", "_id": "81071"} -{"city": "SPRINGFIELD", "loc": [-102.617322, 37.406727], "pop": 1992, "state": "CO", "_id": "81073"} -{"city": "SUGAR CITY", "loc": [-103.655557, 38.244368], "pop": 400, "state": "CO", "_id": "81076"} -{"city": "81080", "loc": [-102.108268, 38.438391], "pop": 172, "state": "CO", "_id": "81080"} -{"city": "TRINCHERA", "loc": [-104.118354, 37.075662], "pop": 21, "state": "CO", "_id": "81081"} -{"city": "JANSEN", "loc": [-104.500715, 37.175475], "pop": 10978, "state": "CO", "_id": "81082"} -{"city": "LYCAN", "loc": [-102.320128, 37.57476], "pop": 183, "state": "CO", "_id": "81084"} -{"city": "VILAS", "loc": [-102.44374, 37.373043], "pop": 145, "state": "CO", "_id": "81087"} -{"city": "FARISTA", "loc": [-104.804301, 37.638159], "pop": 4347, "state": "CO", "_id": "81089"} -{"city": "WALSH", "loc": [-102.253716, 37.352057], "pop": 1366, "state": "CO", "_id": "81090"} -{"city": "WESTON", "loc": [-104.824749, 37.170211], "pop": 1094, "state": "CO", "_id": "81091"} -{"city": "WILEY", "loc": [-102.714734, 38.158978], "pop": 711, "state": "CO", "_id": "81092"} -{"city": "ALAMOSA", "loc": [-105.878602, 37.470274], "pop": 12580, "state": "CO", "_id": "81101"} -{"city": "ANTONITO", "loc": [-106.037946, 37.085473], "pop": 2246, "state": "CO", "_id": "81120"} -{"city": "ARBOLES", "loc": [-107.390749, 37.101633], "pop": 587, "state": "CO", "_id": "81121"} -{"city": "BAYFIELD", "loc": [-107.613728, 37.260328], "pop": 3866, "state": "CO", "_id": "81122"} -{"city": "BLANCA", "loc": [-105.517784, 37.431702], "pop": 429, "state": "CO", "_id": "81123"} -{"city": "CENTER", "loc": [-106.090628, 37.734295], "pop": 4419, "state": "CO", "_id": "81125"} -{"city": "CREEDE", "loc": [-106.927679, 37.816367], "pop": 558, "state": "CO", "_id": "81130"} -{"city": "LA GARITA", "loc": [-106.350502, 37.671346], "pop": 2791, "state": "CO", "_id": "81132"} -{"city": "FORT GARLAND", "loc": [-105.404879, 37.426978], "pop": 725, "state": "CO", "_id": "81133"} -{"city": "HOOPER", "loc": [-105.871193, 37.723203], "pop": 265, "state": "CO", "_id": "81136"} -{"city": "IGNACIO", "loc": [-107.639465, 37.126412], "pop": 3494, "state": "CO", "_id": "81137"} -{"city": "LA JARA", "loc": [-106.005427, 37.290726], "pop": 2274, "state": "CO", "_id": "81140"} -{"city": "MOFFAT", "loc": [-105.841051, 38.045195], "pop": 737, "state": "CO", "_id": "81143"} -{"city": "MONTE VISTA", "loc": [-106.140833, 37.573095], "pop": 6041, "state": "CO", "_id": "81144"} -{"city": "MOSCA", "loc": [-105.806866, 37.635796], "pop": 399, "state": "CO", "_id": "81146"} -{"city": "PAGOSA SPRINGS", "loc": [-107.038497, 37.252345], "pop": 4758, "state": "CO", "_id": "81147"} -{"city": "SAGUACHE", "loc": [-106.187592, 38.09775], "pop": 895, "state": "CO", "_id": "81149"} -{"city": "SAN ACACIO", "loc": [-105.439949, 37.201347], "pop": 1146, "state": "CO", "_id": "81150"} -{"city": "SANFORD", "loc": [-105.928588, 37.208724], "pop": 3037, "state": "CO", "_id": "81151"} -{"city": "MESITA", "loc": [-105.575625, 37.05057], "pop": 252, "state": "CO", "_id": "81152"} -{"city": "SAN PABLO", "loc": [-105.346196, 37.134872], "pop": 638, "state": "CO", "_id": "81153"} -{"city": "SOUTH FORK", "loc": [-106.612451, 37.67248], "pop": 722, "state": "CO", "_id": "81154"} -{"city": "VILLA GROVE", "loc": [-106.110183, 38.2952], "pop": 53, "state": "CO", "_id": "81155"} -{"city": "SALIDA", "loc": [-105.997818, 38.525909], "pop": 7658, "state": "CO", "_id": "81201"} -{"city": "ALMONT", "loc": [-106.627099, 38.64997], "pop": 150, "state": "CO", "_id": "81210"} -{"city": "BUENA VISTA", "loc": [-106.147121, 38.838003], "pop": 5220, "state": "CO", "_id": "81211"} -{"city": "CANON CITY", "loc": [-105.217829, 38.445074], "pop": 23049, "state": "CO", "_id": "81212"} -{"city": "CIMARRON", "loc": [-107.482366, 38.387633], "pop": 84, "state": "CO", "_id": "81220"} -{"city": "CRESTED BUTTE", "loc": [-106.961899, 38.869081], "pop": 1750, "state": "CO", "_id": "81224"} -{"city": "FLORENCE", "loc": [-105.123233, 38.385016], "pop": 4461, "state": "CO", "_id": "81226"} -{"city": "GRANITE", "loc": [-106.311417, 39.095294], "pop": 79, "state": "CO", "_id": "81228"} -{"city": "GUNNISON", "loc": [-106.931013, 38.551056], "pop": 7814, "state": "CO", "_id": "81230"} -{"city": "HOWARD", "loc": [-105.747124, 38.388519], "pop": 485, "state": "CO", "_id": "81233"} -{"city": "LAKE CITY", "loc": [-107.302037, 37.986769], "pop": 467, "state": "CO", "_id": "81235"} -{"city": "NATHROP", "loc": [-106.116576, 38.710343], "pop": 440, "state": "CO", "_id": "81236"} -{"city": "PARLIN", "loc": [-106.677995, 38.508762], "pop": 67, "state": "CO", "_id": "81239"} -{"city": "PENROSE", "loc": [-105.011325, 38.433622], "pop": 3166, "state": "CO", "_id": "81240"} -{"city": "PITKIN", "loc": [-106.516774, 38.608542], "pop": 53, "state": "CO", "_id": "81241"} -{"city": "POWDERHORN", "loc": [-107.108449, 38.282165], "pop": 11, "state": "CO", "_id": "81243"} -{"city": "81250", "loc": [-105.531825, 38.395556], "pop": 467, "state": "CO", "_id": "81250"} -{"city": "TWIN LAKES", "loc": [-106.435079, 39.090231], "pop": 49, "state": "CO", "_id": "81251"} -{"city": "WESTCLIFFE", "loc": [-105.433154, 38.123023], "pop": 1569, "state": "CO", "_id": "81252"} -{"city": "WETMORE", "loc": [-105.106441, 38.189857], "pop": 357, "state": "CO", "_id": "81253"} -{"city": "DURANGO", "loc": [-107.861684, 37.287388], "pop": 23506, "state": "CO", "_id": "81301"} -{"city": "CAHONE", "loc": [-108.579442, 37.69163], "pop": 384, "state": "CO", "_id": "81320"} -{"city": "CORTEZ", "loc": [-108.583726, 37.354949], "pop": 11937, "state": "CO", "_id": "81321"} -{"city": "DOLORES", "loc": [-108.471748, 37.466571], "pop": 1770, "state": "CO", "_id": "81323"} -{"city": "DOVE CREEK", "loc": [-108.918147, 37.763199], "pop": 1120, "state": "CO", "_id": "81324"} -{"city": "EGNAR", "loc": [-108.929889, 37.934448], "pop": 119, "state": "CO", "_id": "81325"} -{"city": "HESPERUS", "loc": [-108.121917, 37.165368], "pop": 1303, "state": "CO", "_id": "81326"} -{"city": "LEWIS", "loc": [-108.61891, 37.47101], "pop": 1455, "state": "CO", "_id": "81327"} -{"city": "MANCOS", "loc": [-108.298242, 37.347133], "pop": 2141, "state": "CO", "_id": "81328"} -{"city": "PLEASANT VIEW", "loc": [-108.809487, 37.588763], "pop": 223, "state": "CO", "_id": "81331"} -{"city": "TOWAOC", "loc": [-108.719993, 37.208408], "pop": 1135, "state": "CO", "_id": "81334"} -{"city": "YELLOW JACKET", "loc": [-108.785167, 37.499526], "pop": 126, "state": "CO", "_id": "81335"} -{"city": "MONTROSE", "loc": [-107.875182, 38.46783], "pop": 17834, "state": "CO", "_id": "81401"} -{"city": "AUSTIN", "loc": [-107.97384, 38.797544], "pop": 1258, "state": "CO", "_id": "81410"} -{"city": "BEDROCK", "loc": [-108.953224, 38.384352], "pop": 191, "state": "CO", "_id": "81411"} -{"city": "CEDAREDGE", "loc": [-107.926786, 38.911878], "pop": 3254, "state": "CO", "_id": "81413"} -{"city": "CRAWFORD", "loc": [-107.614864, 38.69408], "pop": 882, "state": "CO", "_id": "81415"} -{"city": "DELTA", "loc": [-108.060421, 38.734891], "pop": 8644, "state": "CO", "_id": "81416"} -{"city": "ECKERT", "loc": [-107.962452, 38.844982], "pop": 1211, "state": "CO", "_id": "81418"} -{"city": "HOTCHKISS", "loc": [-107.747173, 38.812417], "pop": 2735, "state": "CO", "_id": "81419"} -{"city": "NATURITA", "loc": [-108.572836, 38.222559], "pop": 554, "state": "CO", "_id": "81422"} -{"city": "NORWOOD", "loc": [-108.284472, 38.110406], "pop": 1079, "state": "CO", "_id": "81423"} -{"city": "NUCLA", "loc": [-108.547644, 38.268219], "pop": 1135, "state": "CO", "_id": "81424"} -{"city": "OLATHE", "loc": [-107.992118, 38.597575], "pop": 4246, "state": "CO", "_id": "81425"} -{"city": "OPHIR", "loc": [-107.851961, 37.856197], "pop": 138, "state": "CO", "_id": "81426"} -{"city": "OURAY", "loc": [-107.67261, 38.02576], "pop": 686, "state": "CO", "_id": "81427"} -{"city": "PAONIA", "loc": [-107.598483, 38.864978], "pop": 3314, "state": "CO", "_id": "81428"} -{"city": "PLACERVILLE", "loc": [-108.024775, 38.008759], "pop": 467, "state": "CO", "_id": "81430"} -{"city": "REDVALE", "loc": [-108.389536, 38.186452], "pop": 409, "state": "CO", "_id": "81431"} -{"city": "RIDGWAY", "loc": [-107.753341, 38.138074], "pop": 1299, "state": "CO", "_id": "81432"} -{"city": "SILVERTON", "loc": [-107.666686, 37.808995], "pop": 745, "state": "CO", "_id": "81433"} -{"city": "SOMERSET", "loc": [-107.378145, 38.946801], "pop": 180, "state": "CO", "_id": "81434"} -{"city": "TELLURIDE", "loc": [-107.821371, 37.940028], "pop": 1850, "state": "CO", "_id": "81435"} -{"city": "GRAND JUNCTION", "loc": [-108.545692, 39.078326], "pop": 19665, "state": "CO", "_id": "81501"} -{"city": "GRAND JUNCTION", "loc": [-108.575609, 39.056777], "pop": 20467, "state": "CO", "_id": "81503"} -{"city": "FRUITVALE", "loc": [-108.489094, 39.083136], "pop": 16754, "state": "CO", "_id": "81504"} -{"city": "GRAND JUNCTION", "loc": [-108.596834, 39.107097], "pop": 4877, "state": "CO", "_id": "81505"} -{"city": "GRAND JUNCTION", "loc": [-108.54911, 39.103209], "pop": 7471, "state": "CO", "_id": "81506"} -{"city": "CLIFTON", "loc": [-108.449628, 39.0805], "pop": 8408, "state": "CO", "_id": "81520"} -{"city": "FRUITA", "loc": [-108.721757, 39.163656], "pop": 6230, "state": "CO", "_id": "81521"} -{"city": "GATEWAY", "loc": [-108.791344, 38.915136], "pop": 752, "state": "CO", "_id": "81522"} -{"city": "LOMA", "loc": [-108.814902, 39.227896], "pop": 1067, "state": "CO", "_id": "81524"} -{"city": "MACK", "loc": [-108.929597, 39.255367], "pop": 176, "state": "CO", "_id": "81525"} -{"city": "PALISADE", "loc": [-108.367977, 39.103178], "pop": 4366, "state": "CO", "_id": "81526"} -{"city": "WHITEWATER", "loc": [-108.399042, 38.974422], "pop": 664, "state": "CO", "_id": "81527"} -{"city": "GLENWOOD SPRINGS", "loc": [-107.325188, 39.529607], "pop": 9606, "state": "CO", "_id": "81601"} -{"city": "DINOSAUR", "loc": [-108.965184, 40.256609], "pop": 498, "state": "CO", "_id": "81610"} -{"city": "ASPEN", "loc": [-106.823593, 39.195139], "pop": 7431, "state": "CO", "_id": "81611"} -{"city": "BASALT", "loc": [-106.998752, 39.353466], "pop": 3248, "state": "CO", "_id": "81621"} -{"city": "MARBLE", "loc": [-107.171012, 39.385431], "pop": 9406, "state": "CO", "_id": "81623"} -{"city": "COLLBRAN", "loc": [-107.924945, 39.245267], "pop": 1043, "state": "CO", "_id": "81624"} -{"city": "CRAIG", "loc": [-107.561458, 40.522351], "pop": 10242, "state": "CO", "_id": "81625"} -{"city": "DE BEQUE", "loc": [-108.230405, 39.311764], "pop": 464, "state": "CO", "_id": "81630"} -{"city": "EAGLE", "loc": [-106.75884, 39.634138], "pop": 3368, "state": "CO", "_id": "81631"} -{"city": "ELK SPRINGS", "loc": [-108.419729, 40.414588], "pop": 10, "state": "CO", "_id": "81633"} -{"city": "BATTLEMENT MESA", "loc": [-108.038038, 39.440729], "pop": 2602, "state": "CO", "_id": "81635"} -{"city": "GYPSUM", "loc": [-106.967083, 39.661848], "pop": 2593, "state": "CO", "_id": "81637"} -{"city": "HAMILTON", "loc": [-107.584089, 40.32504], "pop": 191, "state": "CO", "_id": "81638"} -{"city": "HAYDEN", "loc": [-107.257055, 40.494487], "pop": 2028, "state": "CO", "_id": "81639"} -{"city": "MAYBELL", "loc": [-108.272264, 40.650649], "pop": 351, "state": "CO", "_id": "81640"} -{"city": "MEEKER", "loc": [-107.892498, 40.038726], "pop": 3094, "state": "CO", "_id": "81641"} -{"city": "MEREDITH", "loc": [-106.67823, 39.335348], "pop": 76, "state": "CO", "_id": "81642"} -{"city": "MESA", "loc": [-108.104401, 39.161161], "pop": 741, "state": "CO", "_id": "81643"} -{"city": "NEW CASTLE", "loc": [-107.542758, 39.570922], "pop": 2719, "state": "CO", "_id": "81647"} -{"city": "RANGELY", "loc": [-108.799148, 40.082844], "pop": 2740, "state": "CO", "_id": "81648"} -{"city": "RIFLE", "loc": [-107.789804, 39.549073], "pop": 7146, "state": "CO", "_id": "81650"} -{"city": "SILT", "loc": [-107.657411, 39.541464], "pop": 2430, "state": "CO", "_id": "81652"} -{"city": "SLATER", "loc": [-107.497178, 40.947985], "pop": 65, "state": "CO", "_id": "81653"} -{"city": "SNOWMASS", "loc": [-106.950839, 39.250059], "pop": 2627, "state": "CO", "_id": "81654"} -{"city": "VAIL", "loc": [-106.463454, 39.623793], "pop": 11449, "state": "CO", "_id": "81657"} -{"city": "AVON", "loc": [-72.865323, 41.790498], "pop": 13988, "state": "CT", "_id": "06001"} -{"city": "BLOOMFIELD", "loc": [-72.72493, 41.831647], "pop": 19524, "state": "CT", "_id": "06002"} -{"city": "BRISTOL", "loc": [-72.930193, 41.682293], "pop": 60670, "state": "CT", "_id": "06010"} -{"city": "BURLINGTON", "loc": [-72.944386, 41.757296], "pop": 7017, "state": "CT", "_id": "06013"} -{"city": "WINDSORVILLE", "loc": [-72.543667, 41.911405], "pop": 5067, "state": "CT", "_id": "06016"} -{"city": "CANAAN", "loc": [-73.323177, 42.024821], "pop": 2948, "state": "CT", "_id": "06018"} -{"city": "CANTON", "loc": [-72.898731, 41.838401], "pop": 4125, "state": "CT", "_id": "06019"} -{"city": "CANTON CENTER", "loc": [-72.905847, 41.871586], "pop": 192, "state": "CT", "_id": "06020"} -{"city": "COLEBROOK", "loc": [-73.104069, 42.028217], "pop": 203, "state": "CT", "_id": "06021"} -{"city": "COLLINSVILLE", "loc": [-72.92827, 41.851319], "pop": 4630, "state": "CT", "_id": "06022"} -{"city": "EAST BERLIN", "loc": [-72.719007, 41.61277], "pop": 1021, "state": "CT", "_id": "06023"} -{"city": "EAST CANAAN", "loc": [-73.278462, 42.011686], "pop": 519, "state": "CT", "_id": "06024"} -{"city": "EAST GRANBY", "loc": [-72.745889, 41.932215], "pop": 4301, "state": "CT", "_id": "06026"} -{"city": "EAST HARTLAND", "loc": [-72.924213, 42.001617], "pop": 1833, "state": "CT", "_id": "06027"} -{"city": "ELLINGTON", "loc": [-72.462599, 41.911417], "pop": 9070, "state": "CT", "_id": "06029"} -{"city": "FALLS VILLAGE", "loc": [-73.351659, 41.95784], "pop": 1192, "state": "CT", "_id": "06031"} -{"city": "FARMINGTON", "loc": [-72.841476, 41.72839], "pop": 14953, "state": "CT", "_id": "06032"} -{"city": "GLASTONBURY", "loc": [-72.572705, 41.707329], "pop": 24287, "state": "CT", "_id": "06033"} -{"city": "GRANBY", "loc": [-72.799377, 41.960227], "pop": 6939, "state": "CT", "_id": "06035"} -{"city": "BERLIN", "loc": [-72.770482, 41.620826], "pop": 15755, "state": "CT", "_id": "06037"} -{"city": "LAKEVILLE", "loc": [-73.43766, 41.951631], "pop": 2086, "state": "CT", "_id": "06039"} -{"city": "MANCHESTER", "loc": [-72.52444, 41.777732], "pop": 51618, "state": "CT", "_id": "06040"} -{"city": "BOLTON", "loc": [-72.43958, 41.768888], "pop": 4569, "state": "CT", "_id": "06043"} -{"city": "NEW BRITAIN", "loc": [-72.772208, 41.666683], "pop": 28705, "state": "CT", "_id": "06051"} -{"city": "NEW BRITAIN", "loc": [-72.798858, 41.658792], "pop": 8798, "state": "CT", "_id": "06052"} -{"city": "NEW BRITAIN", "loc": [-72.790835, 41.686667], "pop": 37995, "state": "CT", "_id": "06053"} -{"city": "NEW HARTFORD", "loc": [-73.010369, 41.846797], "pop": 4521, "state": "CT", "_id": "06057"} -{"city": "NORFOLK", "loc": [-73.199197, 41.985386], "pop": 2060, "state": "CT", "_id": "06058"} -{"city": "NORTH CANTON", "loc": [-72.90414, 41.923516], "pop": 783, "state": "CT", "_id": "06059"} -{"city": "NORTH GRANBY", "loc": [-72.840938, 42.021925], "pop": 1455, "state": "CT", "_id": "06060"} -{"city": "PLAINVILLE", "loc": [-72.864373, 41.672653], "pop": 17320, "state": "CT", "_id": "06062"} -{"city": "PLEASANT VALLEY", "loc": [-72.982675, 41.924873], "pop": 838, "state": "CT", "_id": "06063"} -{"city": "RIVERTON", "loc": [-73.025469, 41.961395], "pop": 155, "state": "CT", "_id": "06065"} -{"city": "VERNON ROCKVILLE", "loc": [-72.464855, 41.850073], "pop": 31966, "state": "CT", "_id": "06066"} -{"city": "ROCKY HILL", "loc": [-72.663197, 41.658295], "pop": 16638, "state": "CT", "_id": "06067"} -{"city": "SALISBURY", "loc": [-73.421492, 42.001452], "pop": 1686, "state": "CT", "_id": "06068"} -{"city": "SHARON", "loc": [-73.457758, 41.871446], "pop": 2794, "state": "CT", "_id": "06069"} -{"city": "SIMSBURY", "loc": [-72.821267, 41.873712], "pop": 14589, "state": "CT", "_id": "06070"} -{"city": "SOMERS", "loc": [-72.458266, 41.997813], "pop": 9685, "state": "CT", "_id": "06071"} -{"city": "SOUTH GLASTONBUR", "loc": [-72.554308, 41.660682], "pop": 3614, "state": "CT", "_id": "06073"} -{"city": "SOUTH WINDSOR", "loc": [-72.557585, 41.834081], "pop": 22090, "state": "CT", "_id": "06074"} -{"city": "STAFFORD SPRINGS", "loc": [-72.289857, 41.966127], "pop": 12392, "state": "CT", "_id": "06076"} -{"city": "SUFFIELD", "loc": [-72.641997, 41.990029], "pop": 8518, "state": "CT", "_id": "06078"} -{"city": "TARIFFVILLE", "loc": [-72.767786, 41.907715], "pop": 1477, "state": "CT", "_id": "06081"} -{"city": "ENFIELD", "loc": [-72.565218, 41.989016], "pop": 45558, "state": "CT", "_id": "06082"} -{"city": "TOLLAND", "loc": [-72.371789, 41.869647], "pop": 10973, "state": "CT", "_id": "06084"} -{"city": "UNIONVILLE", "loc": [-72.887406, 41.747711], "pop": 5604, "state": "CT", "_id": "06085"} -{"city": "EAST WINDSOR", "loc": [-72.602945, 41.9099], "pop": 4988, "state": "CT", "_id": "06088"} -{"city": "WEATOGUE", "loc": [-72.825254, 41.837152], "pop": 2308, "state": "CT", "_id": "06089"} -{"city": "WEST GRANBY", "loc": [-72.855496, 41.965509], "pop": 948, "state": "CT", "_id": "06090"} -{"city": "WEST SIMSBURY", "loc": [-72.857749, 41.871797], "pop": 3691, "state": "CT", "_id": "06092"} -{"city": "WEST SUFFIELD", "loc": [-72.736232, 42.011464], "pop": 2912, "state": "CT", "_id": "06093"} -{"city": "WINDSOR", "loc": [-72.663893, 41.856122], "pop": 27815, "state": "CT", "_id": "06095"} -{"city": "WINDSOR LOCKS", "loc": [-72.645762, 41.926078], "pop": 12358, "state": "CT", "_id": "06096"} -{"city": "WINSTED", "loc": [-73.066341, 41.925214], "pop": 14584, "state": "CT", "_id": "06098"} -{"city": "HARTFORD", "loc": [-72.675966, 41.767196], "pop": 1202, "state": "CT", "_id": "06103"} -{"city": "HARTFORD", "loc": [-72.701006, 41.769116], "pop": 20887, "state": "CT", "_id": "06105"} -{"city": "HARTFORD", "loc": [-72.694734, 41.749841], "pop": 47841, "state": "CT", "_id": "06106"} -{"city": "W HARTFORD", "loc": [-72.75322, 41.755553], "pop": 18466, "state": "CT", "_id": "06107"} -{"city": "EAST HARTFORD", "loc": [-72.618014, 41.780291], "pop": 23698, "state": "CT", "_id": "06108"} -{"city": "WETHERSFIELD", "loc": [-72.676308, 41.701332], "pop": 25583, "state": "CT", "_id": "06109"} -{"city": "W HARTFORD", "loc": [-72.733691, 41.732566], "pop": 11817, "state": "CT", "_id": "06110"} -{"city": "MAPLE HILL", "loc": [-72.729747, 41.686402], "pop": 29192, "state": "CT", "_id": "06111"} -{"city": "HARTFORD", "loc": [-72.69641, 41.79053], "pop": 29714, "state": "CT", "_id": "06112"} -{"city": "HARTFORD", "loc": [-72.680726, 41.740293], "pop": 23302, "state": "CT", "_id": "06114"} -{"city": "W HARTFORD", "loc": [-72.745689, 41.790021], "pop": 14774, "state": "CT", "_id": "06117"} -{"city": "EAST HARTFORD", "loc": [-72.610265, 41.747211], "pop": 26754, "state": "CT", "_id": "06118"} -{"city": "W HARTFORD", "loc": [-72.726799, 41.762765], "pop": 15066, "state": "CT", "_id": "06119"} -{"city": "HARTFORD", "loc": [-72.675807, 41.78596], "pop": 16739, "state": "CT", "_id": "06120"} -{"city": "WILLIMANTIC", "loc": [-72.213396, 41.714918], "pop": 16023, "state": "CT", "_id": "06226"} -{"city": "AMSTON", "loc": [-72.364601, 41.628969], "pop": 2887, "state": "CT", "_id": "06231"} -{"city": "ANDOVER", "loc": [-72.376719, 41.733215], "pop": 2546, "state": "CT", "_id": "06232"} -{"city": "BROOKLYN", "loc": [-71.954129, 41.780747], "pop": 4835, "state": "CT", "_id": "06234"} -{"city": "CHAPLIN", "loc": [-72.137197, 41.802584], "pop": 902, "state": "CT", "_id": "06235"} -{"city": "COLUMBIA", "loc": [-72.30717, 41.697274], "pop": 4510, "state": "CT", "_id": "06237"} -{"city": "COVENTRY", "loc": [-72.333249, 41.782195], "pop": 10776, "state": "CT", "_id": "06238"} -{"city": "DANIELSON", "loc": [-71.880703, 41.798246], "pop": 12754, "state": "CT", "_id": "06239"} -{"city": "DAYVILLE", "loc": [-71.868342, 41.854045], "pop": 5860, "state": "CT", "_id": "06241"} -{"city": "EASTFORD", "loc": [-72.089451, 41.877104], "pop": 76, "state": "CT", "_id": "06242"} -{"city": "EAST KILLINGLY", "loc": [-71.798534, 41.848756], "pop": 25, "state": "CT", "_id": "06243"} -{"city": "HAMPTON", "loc": [-72.067979, 41.761668], "pop": 2139, "state": "CT", "_id": "06247"} -{"city": "HEBRON", "loc": [-72.398553, 41.684161], "pop": 4192, "state": "CT", "_id": "06248"} -{"city": "LEBANON", "loc": [-72.244035, 41.632988], "pop": 6043, "state": "CT", "_id": "06249"} -{"city": "MANSFIELD CENTER", "loc": [-72.201112, 41.769814], "pop": 4306, "state": "CT", "_id": "06250"} -{"city": "NORTH FRANKLIN", "loc": [-72.142544, 41.616141], "pop": 1654, "state": "CT", "_id": "06254"} -{"city": "NORTH GROSVENORD", "loc": [-71.902734, 41.987878], "pop": 5641, "state": "CT", "_id": "06255"} -{"city": "NORTH WINDHAM", "loc": [-72.160106, 41.745144], "pop": 3825, "state": "CT", "_id": "06256"} -{"city": "POMFRET CENTER", "loc": [-71.98201, 41.869683], "pop": 3325, "state": "CT", "_id": "06259"} -{"city": "PUTNAM", "loc": [-71.896804, 41.91853], "pop": 9031, "state": "CT", "_id": "06260"} -{"city": "QUINEBAUG", "loc": [-71.939137, 42.021747], "pop": 72, "state": "CT", "_id": "06262"} -{"city": "SCOTLAND", "loc": [-72.078263, 41.689849], "pop": 102, "state": "CT", "_id": "06264"} -{"city": "SOUTH WINDHAM", "loc": [-72.168112, 41.667724], "pop": 372, "state": "CT", "_id": "06266"} -{"city": "STORRS MANSFIELD", "loc": [-72.257172, 41.805364], "pop": 16117, "state": "CT", "_id": "06268"} -{"city": "THOMPSON", "loc": [-71.837587, 41.980285], "pop": 2960, "state": "CT", "_id": "06277"} -{"city": "WARRENVILLE", "loc": [-72.15873, 41.86434], "pop": 2720, "state": "CT", "_id": "06278"} -{"city": "WEST WILLINGTON", "loc": [-72.272774, 41.873996], "pop": 5981, "state": "CT", "_id": "06279"} -{"city": "WINDHAM", "loc": [-72.15261, 41.702652], "pop": 3337, "state": "CT", "_id": "06280"} -{"city": "WOODSTOCK", "loc": [-72.004027, 41.960218], "pop": 5698, "state": "CT", "_id": "06281"} -{"city": "WOODSTOCK VALLEY", "loc": [-72.09366, 41.915296], "pop": 1099, "state": "CT", "_id": "06282"} -{"city": "NEW LONDON", "loc": [-72.106245, 41.350718], "pop": 28367, "state": "CT", "_id": "06320"} -{"city": "BALTIC", "loc": [-72.077499, 41.62629], "pop": 3188, "state": "CT", "_id": "06330"} -{"city": "CANTERBURY", "loc": [-72.000985, 41.684403], "pop": 4345, "state": "CT", "_id": "06331"} -{"city": "EAST LYME", "loc": [-72.232987, 41.366806], "pop": 6095, "state": "CT", "_id": "06333"} -{"city": "BOZRAH", "loc": [-72.171078, 41.546515], "pop": 2297, "state": "CT", "_id": "06334"} -{"city": "GALES FERRY", "loc": [-72.06719, 41.42852], "pop": 6994, "state": "CT", "_id": "06335"} -{"city": "GILMAN", "loc": [-72.126623, 41.58117], "pop": 156, "state": "CT", "_id": "06336"} -{"city": "LEDYARD", "loc": [-71.995626, 41.44014], "pop": 7602, "state": "CT", "_id": "06339"} -{"city": "GROTON", "loc": [-72.057947, 41.357171], "pop": 32435, "state": "CT", "_id": "06340"} -{"city": "GROTON", "loc": [-72.090058, 41.397648], "pop": 5398, "state": "CT", "_id": "06349"} -{"city": "JEWETT CITY", "loc": [-71.980759, 41.605182], "pop": 11674, "state": "CT", "_id": "06351"} -{"city": "MONTVILLE", "loc": [-72.126476, 41.445331], "pop": 258, "state": "CT", "_id": "06353"} -{"city": "MOOSUP", "loc": [-71.884962, 41.721031], "pop": 7048, "state": "CT", "_id": "06354"} -{"city": "MYSTIC", "loc": [-71.977364, 41.361626], "pop": 11183, "state": "CT", "_id": "06355"} -{"city": "NIANTIC", "loc": [-72.210819, 41.3253], "pop": 9245, "state": "CT", "_id": "06357"} -{"city": "NORTH STONINGTON", "loc": [-71.872701, 41.453113], "pop": 4882, "state": "CT", "_id": "06359"} -{"city": "NORWICH", "loc": [-72.08494, 41.537055], "pop": 30145, "state": "CT", "_id": "06360"} -{"city": "PRESTON", "loc": [-72.021079, 41.522061], "pop": 11191, "state": "CT", "_id": "06365"} -{"city": "OAKDALE", "loc": [-72.190358, 41.470573], "pop": 6505, "state": "CT", "_id": "06370"} -{"city": "OLD LYME", "loc": [-72.308562, 41.334745], "pop": 8484, "state": "CT", "_id": "06371"} -{"city": "PLAINFIELD", "loc": [-71.921968, 41.67753], "pop": 6533, "state": "CT", "_id": "06374"} -{"city": "QUAKER HILL", "loc": [-72.11722, 41.40324], "pop": 2886, "state": "CT", "_id": "06375"} -{"city": "STERLING", "loc": [-71.819588, 41.715629], "pop": 2324, "state": "CT", "_id": "06377"} -{"city": "STONINGTON", "loc": [-71.915544, 41.366437], "pop": 5282, "state": "CT", "_id": "06378"} -{"city": "PAWCATUCK", "loc": [-71.847761, 41.373475], "pop": 8082, "state": "CT", "_id": "06379"} -{"city": "TAFTVILLE", "loc": [-72.052877, 41.565263], "pop": 2538, "state": "CT", "_id": "06380"} -{"city": "UNCASVILLE", "loc": [-72.112556, 41.462168], "pop": 9909, "state": "CT", "_id": "06382"} -{"city": "VOLUNTOWN", "loc": [-71.855002, 41.583053], "pop": 3167, "state": "CT", "_id": "06384"} -{"city": "WATERFORD", "loc": [-72.145816, 41.346853], "pop": 15217, "state": "CT", "_id": "06385"} -{"city": "ANSONIA", "loc": [-73.074211, 41.342712], "pop": 18430, "state": "CT", "_id": "06401"} -{"city": "BEACON FALLS", "loc": [-73.059656, 41.436917], "pop": 5083, "state": "CT", "_id": "06403"} -{"city": "BRANFORD", "loc": [-72.810643, 41.279991], "pop": 27726, "state": "CT", "_id": "06405"} -{"city": "CENTERBROOK", "loc": [-72.41731, 41.34743], "pop": 483, "state": "CT", "_id": "06409"} -{"city": "CHESHIRE", "loc": [-72.908127, 41.505473], "pop": 25684, "state": "CT", "_id": "06410"} -{"city": "CHESTER", "loc": [-72.464293, 41.404903], "pop": 3417, "state": "CT", "_id": "06412"} -{"city": "CLINTON", "loc": [-72.527973, 41.29117], "pop": 12772, "state": "CT", "_id": "06413"} -{"city": "COLCHESTER", "loc": [-72.344123, 41.566198], "pop": 10978, "state": "CT", "_id": "06415"} -{"city": "CROMWELL", "loc": [-72.666317, 41.61049], "pop": 12286, "state": "CT", "_id": "06416"} -{"city": "DEEP RIVER", "loc": [-72.448568, 41.376478], "pop": 4332, "state": "CT", "_id": "06417"} -{"city": "DERBY", "loc": [-73.080035, 41.322858], "pop": 12199, "state": "CT", "_id": "06418"} -{"city": "KILLINGWORTH", "loc": [-72.571192, 41.369622], "pop": 4809, "state": "CT", "_id": "06419"} -{"city": "SALEM", "loc": [-72.272454, 41.496627], "pop": 4041, "state": "CT", "_id": "06420"} -{"city": "DURHAM", "loc": [-72.68752, 41.464951], "pop": 5737, "state": "CT", "_id": "06422"} -{"city": "EAST HADDAM", "loc": [-72.405876, 41.469575], "pop": 3451, "state": "CT", "_id": "06423"} -{"city": "EAST HAMPTON", "loc": [-72.509345, 41.576058], "pop": 10309, "state": "CT", "_id": "06424"} -{"city": "ESSEX", "loc": [-72.396504, 41.354944], "pop": 2710, "state": "CT", "_id": "06426"} -{"city": "FAIRFIELD", "loc": [-73.257109, 41.166442], "pop": 40889, "state": "CT", "_id": "06430"} -{"city": "FAIRFIELD", "loc": [-73.235408, 41.201651], "pop": 8634, "state": "CT", "_id": "06432"} -{"city": "GUILFORD", "loc": [-72.69679, 41.31537], "pop": 19717, "state": "CT", "_id": "06437"} -{"city": "HADDAM", "loc": [-72.504988, 41.462718], "pop": 2245, "state": "CT", "_id": "06438"} -{"city": "HIGGANUM", "loc": [-72.575143, 41.468246], "pop": 4248, "state": "CT", "_id": "06441"} -{"city": "IVORYTON", "loc": [-72.440387, 41.342101], "pop": 2748, "state": "CT", "_id": "06442"} -{"city": "MADISON", "loc": [-72.615254, 41.309019], "pop": 15488, "state": "CT", "_id": "06443"} -{"city": "MARLBOROUGH", "loc": [-72.460871, 41.641226], "pop": 5535, "state": "CT", "_id": "06447"} -{"city": "MERIDEN", "loc": [-72.799734, 41.533396], "pop": 59441, "state": "CT", "_id": "06450"} -{"city": "MIDDLEFIELD", "loc": [-72.718624, 41.516789], "pop": 2515, "state": "CT", "_id": "06455"} -{"city": "MIDDLETOWN", "loc": [-72.665225, 41.556895], "pop": 42846, "state": "CT", "_id": "06457"} -{"city": "MILFORD", "loc": [-73.054948, 41.217466], "pop": 49940, "state": "CT", "_id": "06460"} -{"city": "MONROE", "loc": [-73.224333, 41.331171], "pop": 16845, "state": "CT", "_id": "06468"} -{"city": "MOODUS", "loc": [-72.441879, 41.507807], "pop": 2634, "state": "CT", "_id": "06469"} -{"city": "NEWTOWN", "loc": [-73.316744, 41.393095], "pop": 12787, "state": "CT", "_id": "06470"} -{"city": "NORTH BRANFORD", "loc": [-72.776034, 41.327985], "pop": 6767, "state": "CT", "_id": "06471"} -{"city": "NORTHFORD", "loc": [-72.780909, 41.396219], "pop": 6229, "state": "CT", "_id": "06472"} -{"city": "NORTH HAVEN", "loc": [-72.85852, 41.382156], "pop": 22481, "state": "CT", "_id": "06473"} -{"city": "OLD SAYBROOK", "loc": [-72.385022, 41.291297], "pop": 9552, "state": "CT", "_id": "06475"} -{"city": "ORANGE", "loc": [-73.028725, 41.281527], "pop": 12828, "state": "CT", "_id": "06477"} -{"city": "OXFORD", "loc": [-73.12961, 41.420237], "pop": 9286, "state": "CT", "_id": "06478"} -{"city": "PLANTSVILLE", "loc": [-72.899031, 41.579747], "pop": 10024, "state": "CT", "_id": "06479"} -{"city": "PORTLAND", "loc": [-72.612797, 41.585223], "pop": 8673, "state": "CT", "_id": "06480"} -{"city": "ROCKFALL", "loc": [-72.699674, 41.534094], "pop": 1330, "state": "CT", "_id": "06481"} -{"city": "SANDY HOOK", "loc": [-73.248522, 41.408706], "pop": 8046, "state": "CT", "_id": "06482"} -{"city": "SEYMOUR", "loc": [-73.081745, 41.386209], "pop": 13660, "state": "CT", "_id": "06483"} -{"city": "SHELTON", "loc": [-73.129439, 41.304689], "pop": 35447, "state": "CT", "_id": "06484"} -{"city": "SOUTHBURY", "loc": [-73.224077, 41.476695], "pop": 15818, "state": "CT", "_id": "06488"} -{"city": "SOUTHINGTON", "loc": [-72.872681, 41.605221], "pop": 28567, "state": "CT", "_id": "06489"} -{"city": "SOUTHPORT", "loc": [-73.290205, 41.14527], "pop": 3701, "state": "CT", "_id": "06490"} -{"city": "WALLINGFORD", "loc": [-72.822179, 41.459997], "pop": 40838, "state": "CT", "_id": "06492"} -{"city": "STRATFORD", "loc": [-73.135594, 41.2044], "pop": 49495, "state": "CT", "_id": "06497"} -{"city": "WESTBROOK", "loc": [-72.456313, 41.292662], "pop": 5377, "state": "CT", "_id": "06498"} -{"city": "NEW HAVEN", "loc": [-72.92706, 41.308701], "pop": 4448, "state": "CT", "_id": "06510"} -{"city": "NEW HAVEN", "loc": [-72.931771, 41.318364], "pop": 54142, "state": "CT", "_id": "06511"} -{"city": "EAST HAVEN", "loc": [-72.874144, 41.280522], "pop": 28963, "state": "CT", "_id": "06512"} -{"city": "EAST HAVEN", "loc": [-72.882554, 41.314215], "pop": 31342, "state": "CT", "_id": "06513"} -{"city": "HAMDEN", "loc": [-72.93613, 41.361987], "pop": 23883, "state": "CT", "_id": "06514"} -{"city": "NEW HAVEN", "loc": [-72.966445, 41.329301], "pop": 18603, "state": "CT", "_id": "06515"} -{"city": "WEST HAVEN", "loc": [-72.963842, 41.270079], "pop": 53642, "state": "CT", "_id": "06516"} -{"city": "HAMDEN", "loc": [-72.911678, 41.348393], "pop": 14604, "state": "CT", "_id": "06517"} -{"city": "HAMDEN", "loc": [-72.911001, 41.409664], "pop": 13802, "state": "CT", "_id": "06518"} -{"city": "NEW HAVEN", "loc": [-72.937307, 41.296284], "pop": 19410, "state": "CT", "_id": "06519"} -{"city": "BETHANY", "loc": [-73.000704, 41.426194], "pop": 4730, "state": "CT", "_id": "06524"} -{"city": "WOODBRIDGE", "loc": [-73.013902, 41.351668], "pop": 7802, "state": "CT", "_id": "06525"} -{"city": "BRIDGEPORT", "loc": [-73.201859, 41.179574], "pop": 28219, "state": "CT", "_id": "06604"} -{"city": "BRIDGEPORT", "loc": [-73.216251, 41.166796], "pop": 23800, "state": "CT", "_id": "06605"} -{"city": "BRIDGEPORT", "loc": [-73.208619, 41.20907], "pop": 41879, "state": "CT", "_id": "06606"} -{"city": "BRIDGEPORT", "loc": [-73.165048, 41.178382], "pop": 9292, "state": "CT", "_id": "06607"} -{"city": "BRIDGEPORT", "loc": [-73.181141, 41.189466], "pop": 17721, "state": "CT", "_id": "06608"} -{"city": "BRIDGEPORT", "loc": [-73.168771, 41.200508], "pop": 20727, "state": "CT", "_id": "06610"} -{"city": "TRUMBULL", "loc": [-73.211063, 41.25641], "pop": 32213, "state": "CT", "_id": "06611"} -{"city": "EASTON", "loc": [-73.287108, 41.252328], "pop": 6213, "state": "CT", "_id": "06612"} -{"city": "WATERBURY", "loc": [-73.038545, 41.556568], "pop": 4522, "state": "CT", "_id": "06702"} -{"city": "WATERBURY", "loc": [-73.031805, 41.575435], "pop": 26018, "state": "CT", "_id": "06704"} -{"city": "WATERBURY", "loc": [-72.996268, 41.550328], "pop": 25128, "state": "CT", "_id": "06705"} -{"city": "WATERBURY", "loc": [-73.03064, 41.536261], "pop": 15431, "state": "CT", "_id": "06706"} -{"city": "WATERBURY", "loc": [-73.064495, 41.551102], "pop": 27661, "state": "CT", "_id": "06708"} -{"city": "WATERBURY", "loc": [-73.046821, 41.567503], "pop": 9921, "state": "CT", "_id": "06710"} -{"city": "PROSPECT", "loc": [-72.978803, 41.502198], "pop": 7775, "state": "CT", "_id": "06712"} -{"city": "WOLCOTT", "loc": [-72.982799, 41.596995], "pop": 13787, "state": "CT", "_id": "06716"} -{"city": "BANTAM", "loc": [-73.252028, 41.721483], "pop": 1418, "state": "CT", "_id": "06750"} -{"city": "BETHLEHEM", "loc": [-73.209098, 41.638683], "pop": 3071, "state": "CT", "_id": "06751"} -{"city": "BRIDGEWATER", "loc": [-73.360936, 41.528684], "pop": 1654, "state": "CT", "_id": "06752"} -{"city": "WARREN", "loc": [-73.367472, 41.770084], "pop": 1278, "state": "CT", "_id": "06754"} -{"city": "GAYLORDSVILLE", "loc": [-73.483524, 41.648635], "pop": 890, "state": "CT", "_id": "06755"} -{"city": "GOSHEN", "loc": [-73.242876, 41.833452], "pop": 2314, "state": "CT", "_id": "06756"} -{"city": "KENT", "loc": [-73.458345, 41.731619], "pop": 2035, "state": "CT", "_id": "06757"} -{"city": "LAKESIDE", "loc": [-73.238235, 41.69001], "pop": 997, "state": "CT", "_id": "06758"} -{"city": "LITCHFIELD", "loc": [-73.200011, 41.754069], "pop": 5573, "state": "CT", "_id": "06759"} -{"city": "MIDDLEBURY", "loc": [-73.11307, 41.534277], "pop": 6338, "state": "CT", "_id": "06762"} -{"city": "MORRIS", "loc": [-73.176509, 41.688121], "pop": 849, "state": "CT", "_id": "06763"} -{"city": "NAUGATUCK", "loc": [-73.049342, 41.492039], "pop": 30625, "state": "CT", "_id": "06770"} -{"city": "NEW MILFORD", "loc": [-73.412752, 41.581745], "pop": 22750, "state": "CT", "_id": "06776"} -{"city": "NEW PRESTON MARB", "loc": [-73.349296, 41.689373], "pop": 1697, "state": "CT", "_id": "06777"} -{"city": "NORTHFIELD", "loc": [-73.132079, 41.707382], "pop": 442, "state": "CT", "_id": "06778"} -{"city": "OAKVILLE", "loc": [-73.087311, 41.590932], "pop": 8455, "state": "CT", "_id": "06779"} -{"city": "PLYMOUTH", "loc": [-73.044887, 41.661121], "pop": 2910, "state": "CT", "_id": "06782"} -{"city": "ROXBURY", "loc": [-73.299346, 41.5509], "pop": 1825, "state": "CT", "_id": "06783"} -{"city": "SHERMAN", "loc": [-73.494694, 41.571366], "pop": 2808, "state": "CT", "_id": "06784"} -{"city": "SOUTH KENT", "loc": [-73.469023, 41.695051], "pop": 719, "state": "CT", "_id": "06785"} -{"city": "TERRYVILLE", "loc": [-73.009214, 41.676197], "pop": 8912, "state": "CT", "_id": "06786"} -{"city": "THOMASTON", "loc": [-73.088557, 41.678643], "pop": 8179, "state": "CT", "_id": "06787"} -{"city": "TORRINGTON", "loc": [-73.115579, 41.813065], "pop": 33969, "state": "CT", "_id": "06790"} -{"city": "HARWINTON", "loc": [-73.072819, 41.770145], "pop": 5228, "state": "CT", "_id": "06791"} -{"city": "WASHINGTON DEPOT", "loc": [-73.293518, 41.634595], "pop": 1381, "state": "CT", "_id": "06793"} -{"city": "WASHINGTON DEPOT", "loc": [-73.327352, 41.655924], "pop": 1323, "state": "CT", "_id": "06794"} -{"city": "WATERTOWN", "loc": [-73.122122, 41.605654], "pop": 12021, "state": "CT", "_id": "06795"} -{"city": "WEST CORNWALL", "loc": [-73.331271, 41.868888], "pop": 1042, "state": "CT", "_id": "06796"} -{"city": "WOODBURY", "loc": [-73.208264, 41.552061], "pop": 8131, "state": "CT", "_id": "06798"} -{"city": "BETHEL", "loc": [-73.400827, 41.381298], "pop": 17538, "state": "CT", "_id": "06801"} -{"city": "BROOKFIELD", "loc": [-73.397986, 41.46504], "pop": 14223, "state": "CT", "_id": "06804"} -{"city": "COS COB", "loc": [-73.593498, 41.052952], "pop": 7000, "state": "CT", "_id": "06807"} -{"city": "DANBURY", "loc": [-73.453165, 41.391663], "pop": 37764, "state": "CT", "_id": "06810"} -{"city": "DANBURY", "loc": [-73.471587, 41.423983], "pop": 27609, "state": "CT", "_id": "06811"} -{"city": "NEW FAIRFIELD", "loc": [-73.497784, 41.472999], "pop": 13013, "state": "CT", "_id": "06812"} -{"city": "DARIEN", "loc": [-73.485254, 41.076759], "pop": 18062, "state": "CT", "_id": "06820"} -{"city": "BYRAM", "loc": [-73.630047, 41.030238], "pop": 22685, "state": "CT", "_id": "06830"} -{"city": "GREENWICH", "loc": [-73.659405, 41.054885], "pop": 14545, "state": "CT", "_id": "06831"} -{"city": "NEW CANAAN", "loc": [-73.494356, 41.151024], "pop": 17937, "state": "CT", "_id": "06840"} -{"city": "NORWALK", "loc": [-73.435827, 41.12222], "pop": 17332, "state": "CT", "_id": "06850"} -{"city": "NORWALK", "loc": [-73.405802, 41.132346], "pop": 24412, "state": "CT", "_id": "06851"} -{"city": "NORWALK", "loc": [-73.439667, 41.070243], "pop": 3979, "state": "CT", "_id": "06853"} -{"city": "NORWALK", "loc": [-73.428485, 41.095722], "pop": 25358, "state": "CT", "_id": "06854"} -{"city": "NORWALK", "loc": [-73.401119, 41.101382], "pop": 7311, "state": "CT", "_id": "06855"} -{"city": "OLD GREENWICH", "loc": [-73.567253, 41.035437], "pop": 6511, "state": "CT", "_id": "06870"} -{"city": "RIDGEFIELD", "loc": [-73.497268, 41.297683], "pop": 21691, "state": "CT", "_id": "06877"} -{"city": "RIVERSIDE", "loc": [-73.581136, 41.037998], "pop": 7765, "state": "CT", "_id": "06878"} -{"city": "WESTPORT", "loc": [-73.349579, 41.143433], "pop": 24705, "state": "CT", "_id": "06880"} -{"city": "WESTON", "loc": [-73.371474, 41.219499], "pop": 8517, "state": "CT", "_id": "06883"} -{"city": "WEST REDDING", "loc": [-73.3935, 41.306915], "pop": 7183, "state": "CT", "_id": "06896"} -{"city": "WILTON", "loc": [-73.438323, 41.201761], "pop": 15795, "state": "CT", "_id": "06897"} -{"city": "STAMFORD", "loc": [-73.539039, 41.053083], "pop": 5860, "state": "CT", "_id": "06901"} -{"city": "STAMFORD", "loc": [-73.537428, 41.052552], "pop": 54605, "state": "CT", "_id": "06902"} -{"city": "STAMFORD", "loc": [-73.568356, 41.135235], "pop": 14172, "state": "CT", "_id": "06903"} -{"city": "RIDGEWAY", "loc": [-73.543757, 41.082576], "pop": 17839, "state": "CT", "_id": "06905"} -{"city": "STAMFORD", "loc": [-73.523563, 41.069218], "pop": 7019, "state": "CT", "_id": "06906"} -{"city": "STAMFORD", "loc": [-73.520297, 41.094206], "pop": 8498, "state": "CT", "_id": "06907"} -{"city": "BEAR", "loc": [-75.674729, 39.610187], "pop": 21384, "state": "DE", "_id": "19701"} -{"city": "NEWARK", "loc": [-75.699339, 39.634869], "pop": 30402, "state": "DE", "_id": "19702"} -{"city": "CLAYMONT", "loc": [-75.46494, 39.804432], "pop": 16699, "state": "DE", "_id": "19703"} -{"city": "HOCKESSIN", "loc": [-75.688873, 39.77604], "pop": 13149, "state": "DE", "_id": "19707"} -{"city": "MIDDLETOWN", "loc": [-75.683183, 39.481535], "pop": 10243, "state": "DE", "_id": "19709"} -{"city": "NEWARK", "loc": [-75.737534, 39.701129], "pop": 50573, "state": "DE", "_id": "19711"} -{"city": "NEWARK", "loc": [-75.715101, 39.669881], "pop": 30699, "state": "DE", "_id": "19713"} -{"city": "MANOR", "loc": [-75.589938, 39.67703], "pop": 46906, "state": "DE", "_id": "19720"} -{"city": "TOWNSEND", "loc": [-75.683368, 39.381882], "pop": 4512, "state": "DE", "_id": "19734"} -{"city": "WILMINGTON", "loc": [-75.549658, 39.737752], "pop": 15151, "state": "DE", "_id": "19801"} -{"city": "WILMINGTON", "loc": [-75.534041, 39.75638], "pop": 27411, "state": "DE", "_id": "19802"} -{"city": "TALLEYVILLE", "loc": [-75.531076, 39.793236], "pop": 20901, "state": "DE", "_id": "19803"} -{"city": "NEWPORT", "loc": [-75.612815, 39.720854], "pop": 18306, "state": "DE", "_id": "19804"} -{"city": "WILMINGTON", "loc": [-75.582724, 39.743375], "pop": 40087, "state": "DE", "_id": "19805"} -{"city": "WILMINGTON", "loc": [-75.563503, 39.757076], "pop": 9645, "state": "DE", "_id": "19806"} -{"city": "GREENVILLE", "loc": [-75.607205, 39.782206], "pop": 7176, "state": "DE", "_id": "19807"} -{"city": "MARSHALLTON", "loc": [-75.663891, 39.734737], "pop": 34260, "state": "DE", "_id": "19808"} -{"city": "EDGEMOOR", "loc": [-75.494592, 39.771913], "pop": 14285, "state": "DE", "_id": "19809"} -{"city": "EDGEMOOR", "loc": [-75.505999, 39.819377], "pop": 26334, "state": "DE", "_id": "19810"} -{"city": "DOVER", "loc": [-75.535983, 39.156639], "pop": 46005, "state": "DE", "_id": "19901"} -{"city": "DOVER AFB", "loc": [-75.478966, 39.120246], "pop": 5500, "state": "DE", "_id": "19902"} -{"city": "BETHANY BEACH", "loc": [-75.067396, 38.531009], "pop": 769, "state": "DE", "_id": "19930"} -{"city": "BETHEL", "loc": [-75.624298, 38.568517], "pop": 108, "state": "DE", "_id": "19931"} -{"city": "BRIDGEVILLE", "loc": [-75.608768, 38.736628], "pop": 4353, "state": "DE", "_id": "19933"} -{"city": "CAMDEN WYOMING", "loc": [-75.596559, 39.099099], "pop": 8877, "state": "DE", "_id": "19934"} -{"city": "CLAYTON", "loc": [-75.690361, 39.256395], "pop": 4906, "state": "DE", "_id": "19938"} -{"city": "DAGSBORO", "loc": [-75.21133, 38.559559], "pop": 2593, "state": "DE", "_id": "19939"} -{"city": "DELMAR", "loc": [-75.575887, 38.476955], "pop": 4257, "state": "DE", "_id": "19940"} -{"city": "ELLENDALE", "loc": [-75.405588, 38.805737], "pop": 2445, "state": "DE", "_id": "19941"} -{"city": "FELTON", "loc": [-75.582906, 39.022538], "pop": 7282, "state": "DE", "_id": "19943"} -{"city": "FRANKFORD", "loc": [-75.200633, 38.51767], "pop": 5500, "state": "DE", "_id": "19945"} -{"city": "FREDERICA", "loc": [-75.454458, 39.034188], "pop": 2725, "state": "DE", "_id": "19946"} -{"city": "GEORGETOWN", "loc": [-75.393198, 38.679006], "pop": 11371, "state": "DE", "_id": "19947"} -{"city": "GREENWOOD", "loc": [-75.593547, 38.817519], "pop": 4279, "state": "DE", "_id": "19950"} -{"city": "HARBESON", "loc": [-75.223621, 38.672294], "pop": 598, "state": "DE", "_id": "19951"} -{"city": "HARRINGTON", "loc": [-75.584268, 38.924031], "pop": 7972, "state": "DE", "_id": "19952"} -{"city": "HARTLY", "loc": [-75.693476, 39.15421], "pop": 3558, "state": "DE", "_id": "19953"} -{"city": "HOUSTON", "loc": [-75.506354, 38.91143], "pop": 1776, "state": "DE", "_id": "19954"} -{"city": "LAUREL", "loc": [-75.563052, 38.553695], "pop": 9283, "state": "DE", "_id": "19956"} -{"city": "LEWES", "loc": [-75.174702, 38.738149], "pop": 9503, "state": "DE", "_id": "19958"} -{"city": "LINCOLN", "loc": [-75.399961, 38.86128], "pop": 4435, "state": "DE", "_id": "19960"} -{"city": "MAGNOLIA", "loc": [-75.508321, 39.07353], "pop": 4567, "state": "DE", "_id": "19962"} -{"city": "MILFORD", "loc": [-75.429877, 38.921801], "pop": 12187, "state": "DE", "_id": "19963"} -{"city": "MARYDEL", "loc": [-75.728661, 39.099794], "pop": 975, "state": "DE", "_id": "19964"} -{"city": "LONG NECK", "loc": [-75.255547, 38.593436], "pop": 11962, "state": "DE", "_id": "19966"} -{"city": "MILLVILLE", "loc": [-75.107762, 38.558946], "pop": 623, "state": "DE", "_id": "19967"} -{"city": "MILTON", "loc": [-75.295298, 38.768687], "pop": 4656, "state": "DE", "_id": "19968"} -{"city": "MILLVILLE", "loc": [-75.096518, 38.555528], "pop": 1532, "state": "DE", "_id": "19970"} -{"city": "DEWEY BEACH", "loc": [-75.107346, 38.714715], "pop": 6249, "state": "DE", "_id": "19971"} -{"city": "SEAFORD", "loc": [-75.604122, 38.640358], "pop": 18404, "state": "DE", "_id": "19973"} -{"city": "SELBYVILLE", "loc": [-75.157325, 38.465357], "pop": 4478, "state": "DE", "_id": "19975"} -{"city": "SMYRNA", "loc": [-75.600832, 39.293379], "pop": 14164, "state": "DE", "_id": "19977"} -{"city": "VIOLA", "loc": [-75.572605, 39.041872], "pop": 153, "state": "DE", "_id": "19979"} -{"city": "WASHINGTON", "loc": [-77.017691, 38.912217], "pop": 34745, "state": "DC", "_id": "20001"} -{"city": "WASHINGTON", "loc": [-76.990055, 38.902365], "pop": 56756, "state": "DC", "_id": "20002"} -{"city": "WASHINGTON", "loc": [-76.989539, 38.882941], "pop": 24818, "state": "DC", "_id": "20003"} -{"city": "WASHINGTON", "loc": [-77.026303, 38.892955], "pop": 11, "state": "DC", "_id": "20004"} -{"city": "WASHINGTON", "loc": [-77.031236, 38.906731], "pop": 9862, "state": "DC", "_id": "20005"} -{"city": "WASHINGTON", "loc": [-77.044701, 38.896444], "pop": 2317, "state": "DC", "_id": "20006"} -{"city": "WASHINGTON", "loc": [-77.074042, 38.914365], "pop": 27076, "state": "DC", "_id": "20007"} -{"city": "WASHINGTON", "loc": [-77.059936, 38.936282], "pop": 26736, "state": "DC", "_id": "20008"} -{"city": "WASHINGTON", "loc": [-77.037504, 38.920202], "pop": 47086, "state": "DC", "_id": "20009"} -{"city": "WASHINGTON", "loc": [-77.032183, 38.93272], "pop": 28849, "state": "DC", "_id": "20010"} -{"city": "WASHINGTON", "loc": [-77.020251, 38.951786], "pop": 62924, "state": "DC", "_id": "20011"} -{"city": "WASHINGTON", "loc": [-77.028248, 38.975712], "pop": 15081, "state": "DC", "_id": "20012"} -{"city": "WASHINGTON", "loc": [-77.067961, 38.965768], "pop": 14569, "state": "DC", "_id": "20015"} -{"city": "WASHINGTON", "loc": [-77.086037, 38.938117], "pop": 31042, "state": "DC", "_id": "20016"} -{"city": "WASHINGTON", "loc": [-76.994038, 38.936723], "pop": 19548, "state": "DC", "_id": "20017"} -{"city": "WASHINGTON", "loc": [-76.976159, 38.927724], "pop": 18298, "state": "DC", "_id": "20018"} -{"city": "WASHINGTON", "loc": [-76.937588, 38.890237], "pop": 59492, "state": "DC", "_id": "20019"} -{"city": "WASHINGTON", "loc": [-76.974187, 38.860039], "pop": 54339, "state": "DC", "_id": "20020"} -{"city": "WASHINGTON", "loc": [-77.016028, 38.875939], "pop": 11562, "state": "DC", "_id": "20024"} -{"city": "WASHINGTON", "loc": [-76.999549, 38.833843], "pop": 40265, "state": "DC", "_id": "20032"} -{"city": "WASHINGTON", "loc": [-77.041434, 38.908704], "pop": 4146, "state": "DC", "_id": "20036"} -{"city": "WASHINGTON", "loc": [-77.050448, 38.901446], "pop": 11046, "state": "DC", "_id": "20037"} -{"city": "PENTAGON", "loc": [-77.038196, 38.891019], "pop": 21, "state": "DC", "_id": "20301"} -{"city": "WASHINGTON", "loc": [-77.014827, 38.839473], "pop": 6311, "state": "DC", "_id": "20336"} -{"city": "BRANFORD", "loc": [-82.899288, 29.939472], "pop": 2439, "state": "FL", "_id": "32008"} -{"city": "BRYCEVILLE", "loc": [-81.972397, 30.419274], "pop": 1875, "state": "FL", "_id": "32009"} -{"city": "CALLAHAN", "loc": [-81.814465, 30.551958], "pop": 9111, "state": "FL", "_id": "32011"} -{"city": "DAY", "loc": [-83.285052, 30.149666], "pop": 1567, "state": "FL", "_id": "32013"} -{"city": "ELKTON", "loc": [-81.46199, 29.788243], "pop": 1557, "state": "FL", "_id": "32033"} -{"city": "AMELIA ISLAND", "loc": [-81.468829, 30.635388], "pop": 19016, "state": "FL", "_id": "32034"} -{"city": "FORT WHITE", "loc": [-82.687938, 29.92073], "pop": 3439, "state": "FL", "_id": "32038"} -{"city": "GLEN SAINT MARY", "loc": [-82.204056, 30.286058], "pop": 6467, "state": "FL", "_id": "32040"} -{"city": "GREEN COVE SPRIN", "loc": [-81.726182, 30.00425], "pop": 16033, "state": "FL", "_id": "32043"} -{"city": "HAMPTON", "loc": [-82.148347, 29.857511], "pop": 1274, "state": "FL", "_id": "32044"} -{"city": "HILLIARD", "loc": [-81.93453, 30.688367], "pop": 6486, "state": "FL", "_id": "32046"} -{"city": "JASPER", "loc": [-82.932186, 30.502914], "pop": 6588, "state": "FL", "_id": "32052"} -{"city": "JENNINGS", "loc": [-83.134971, 30.548243], "pop": 2977, "state": "FL", "_id": "32053"} -{"city": "LAKE BUTLER", "loc": [-82.382796, 30.003485], "pop": 8658, "state": "FL", "_id": "32054"} -{"city": "LAKE CITY", "loc": [-82.659888, 30.165239], "pop": 38018, "state": "FL", "_id": "32055"} -{"city": "LAWTEY", "loc": [-82.105544, 30.047164], "pop": 4108, "state": "FL", "_id": "32058"} -{"city": "LEE", "loc": [-83.284392, 30.397863], "pop": 1260, "state": "FL", "_id": "32059"} -{"city": "BOYS RANCH", "loc": [-83.024994, 30.286622], "pop": 19075, "state": "FL", "_id": "32060"} -{"city": "LULU", "loc": [-82.538481, 30.07544], "pop": 295, "state": "FL", "_id": "32061"} -{"city": "MC ALPIN", "loc": [-82.966182, 30.150899], "pop": 2062, "state": "FL", "_id": "32062"} -{"city": "MACCLENNY", "loc": [-82.132475, 30.273671], "pop": 9749, "state": "FL", "_id": "32063"} -{"city": "ORANGE PARK", "loc": [-81.774199, 30.138233], "pop": 19248, "state": "FL", "_id": "32065"} -{"city": "MAYO", "loc": [-83.146208, 30.039979], "pop": 3475, "state": "FL", "_id": "32066"} -{"city": "MIDDLEBURG", "loc": [-81.864476, 30.083984], "pop": 23245, "state": "FL", "_id": "32068"} -{"city": "O BRIEN", "loc": [-82.93005, 30.038114], "pop": 1305, "state": "FL", "_id": "32071"} -{"city": "ORANGE PARK", "loc": [-81.72907, 30.16369], "pop": 37281, "state": "FL", "_id": "32073"} -{"city": "PONTE VEDRA BEAC", "loc": [-81.386383, 30.215326], "pop": 14727, "state": "FL", "_id": "32082"} -{"city": "RAIFORD", "loc": [-82.20012, 30.070379], "pop": 1594, "state": "FL", "_id": "32083"} -{"city": "SAINT AUGUSTINE", "loc": [-81.298367, 29.880457], "pop": 24906, "state": "FL", "_id": "32084"} -{"city": "SAINT AUGUSTINE", "loc": [-81.323734, 29.828514], "pop": 16939, "state": "FL", "_id": "32086"} -{"city": "SANDERSON", "loc": [-82.337741, 30.302536], "pop": 2270, "state": "FL", "_id": "32087"} -{"city": "STARKE", "loc": [-82.118518, 29.958299], "pop": 15058, "state": "FL", "_id": "32091"} -{"city": "SAINT AUGUSTINE", "loc": [-81.526379, 29.947511], "pop": 4702, "state": "FL", "_id": "32092"} -{"city": "WELLBORN", "loc": [-82.850532, 30.179624], "pop": 1621, "state": "FL", "_id": "32094"} -{"city": "SAINT AUGUSTINE", "loc": [-81.347626, 29.905726], "pop": 12132, "state": "FL", "_id": "32095"} -{"city": "WHITE SPRINGS", "loc": [-82.776453, 30.338749], "pop": 1671, "state": "FL", "_id": "32096"} -{"city": "YULEE", "loc": [-81.590603, 30.622225], "pop": 7453, "state": "FL", "_id": "32097"} -{"city": "ASTOR", "loc": [-81.539929, 29.165031], "pop": 2092, "state": "FL", "_id": "32102"} -{"city": "BUNNELL", "loc": [-81.324431, 29.45616], "pop": 4925, "state": "FL", "_id": "32110"} -{"city": "CRESCENT CITY", "loc": [-81.557909, 29.445438], "pop": 7481, "state": "FL", "_id": "32112"} -{"city": "CITRA", "loc": [-82.106222, 29.39182], "pop": 3340, "state": "FL", "_id": "32113"} -{"city": "DAYTONA BEACH", "loc": [-81.037071, 29.201168], "pop": 34235, "state": "FL", "_id": "32114"} -{"city": "HOLLY HILL", "loc": [-81.054698, 29.236006], "pop": 22599, "state": "FL", "_id": "32117"} -{"city": "DAYTONA BEACH", "loc": [-81.009469, 29.221874], "pop": 17009, "state": "FL", "_id": "32118"} -{"city": "DUNLAWTON", "loc": [-81.022142, 29.152526], "pop": 36500, "state": "FL", "_id": "32119"} -{"city": "PORT ORANGE", "loc": [-81.106746, 29.122456], "pop": 7360, "state": "FL", "_id": "32124"} -{"city": "PORT ORANGE", "loc": [-80.988351, 29.1124], "pop": 25925, "state": "FL", "_id": "32127"} -{"city": "DE LEON SPRINGS", "loc": [-81.348762, 29.116592], "pop": 2267, "state": "FL", "_id": "32130"} -{"city": "EAST PALATKA", "loc": [-81.587879, 29.660861], "pop": 5851, "state": "FL", "_id": "32131"} -{"city": "EDGEWATER", "loc": [-80.910344, 28.981801], "pop": 6690, "state": "FL", "_id": "32132"} -{"city": "SALT SPRINGS", "loc": [-81.887757, 29.279554], "pop": 14324, "state": "FL", "_id": "32134"} -{"city": "FLAGLER BEACH", "loc": [-81.130288, 29.474978], "pop": 4608, "state": "FL", "_id": "32136"} -{"city": "PALM COAST", "loc": [-81.21899, 29.556515], "pop": 18194, "state": "FL", "_id": "32137"} -{"city": "GEORGETOWN", "loc": [-81.629783, 29.403315], "pop": 1839, "state": "FL", "_id": "32139"} -{"city": "FLORAHOME", "loc": [-81.862224, 29.758105], "pop": 1475, "state": "FL", "_id": "32140"} -{"city": "EDGEWATER", "loc": [-80.896869, 28.945481], "pop": 11379, "state": "FL", "_id": "32141"} -{"city": "HASTINGS", "loc": [-81.490908, 29.705147], "pop": 2189, "state": "FL", "_id": "32145"} -{"city": "INTERLACHEN", "loc": [-81.889432, 29.627001], "pop": 15416, "state": "FL", "_id": "32148"} -{"city": "LADY LAKE", "loc": [-81.925598, 28.929939], "pop": 11493, "state": "FL", "_id": "32159"} -{"city": "NEW SMYRNA BEACH", "loc": [-80.958436, 29.024672], "pop": 17624, "state": "FL", "_id": "32168"} -{"city": "NEW SMYRNA BEACH", "loc": [-80.888463, 29.017196], "pop": 9169, "state": "FL", "_id": "32169"} -{"city": "ORMOND BEACH", "loc": [-81.088216, 29.283305], "pop": 34477, "state": "FL", "_id": "32174"} -{"city": "ORMOND BEACH", "loc": [-81.058432, 29.322192], "pop": 15383, "state": "FL", "_id": "32176"} -{"city": "PALATKA", "loc": [-81.659452, 29.657748], "pop": 24263, "state": "FL", "_id": "32177"} -{"city": "OCKLAWAHA", "loc": [-81.88569, 29.064308], "pop": 4385, "state": "FL", "_id": "32179"} -{"city": "PIERSON", "loc": [-81.43533, 29.222596], "pop": 7341, "state": "FL", "_id": "32180"} -{"city": "POMONA PARK", "loc": [-81.630739, 29.502106], "pop": 1006, "state": "FL", "_id": "32181"} -{"city": "SAN MATEO", "loc": [-81.5921, 29.588827], "pop": 1864, "state": "FL", "_id": "32187"} -{"city": "SATSUMA", "loc": [-81.640596, 29.559354], "pop": 3490, "state": "FL", "_id": "32189"} -{"city": "SEVILLE", "loc": [-81.527894, 29.320084], "pop": 436, "state": "FL", "_id": "32190"} -{"city": "WEIRSDALE", "loc": [-81.893168, 28.978182], "pop": 3034, "state": "FL", "_id": "32195"} -{"city": "JACKSONVILLE", "loc": [-81.651672, 30.329882], "pop": 4724, "state": "FL", "_id": "32202"} -{"city": "JACKSONVILLE", "loc": [-81.685445, 30.318899], "pop": 8839, "state": "FL", "_id": "32204"} -{"city": "JACKSONVILLE", "loc": [-81.722034, 30.317236], "pop": 46463, "state": "FL", "_id": "32205"} -{"city": "JACKSONVILLE", "loc": [-81.648769, 30.351073], "pop": 23301, "state": "FL", "_id": "32206"} -{"city": "JACKSONVILLE", "loc": [-81.63205, 30.290766], "pop": 35661, "state": "FL", "_id": "32207"} -{"city": "JACKSONVILLE", "loc": [-81.688939, 30.393664], "pop": 35615, "state": "FL", "_id": "32208"} -{"city": "JACKSONVILLE", "loc": [-81.691974, 30.35841], "pop": 42856, "state": "FL", "_id": "32209"} -{"city": "JACKSONVILLE", "loc": [-81.747312, 30.268743], "pop": 54548, "state": "FL", "_id": "32210"} -{"city": "JACKSONVILLE", "loc": [-81.588248, 30.348034], "pop": 54199, "state": "FL", "_id": "32211"} -{"city": "JACKSONVILLE N A", "loc": [-81.68848, 30.220905], "pop": 2517, "state": "FL", "_id": "32212"} -{"city": "CECIL FIELD NAS", "loc": [-81.663142, 30.23295], "pop": 0, "state": "FL", "_id": "32215"} -{"city": "JACKSONVILLE", "loc": [-81.547387, 30.293907], "pop": 58867, "state": "FL", "_id": "32216"} -{"city": "JACKSONVILLE", "loc": [-81.616956, 30.240678], "pop": 19356, "state": "FL", "_id": "32217"} -{"city": "JACKSONVILLE", "loc": [-81.662631, 30.45067], "pop": 30493, "state": "FL", "_id": "32218"} -{"city": "JACKSONVILLE", "loc": [-81.763451, 30.403365], "pop": 9570, "state": "FL", "_id": "32219"} -{"city": "JACKSONVILLE", "loc": [-81.817572, 30.329003], "pop": 9389, "state": "FL", "_id": "32220"} -{"city": "JACKSONVILLE", "loc": [-81.820231, 30.283707], "pop": 18244, "state": "FL", "_id": "32221"} -{"city": "JACKSONVILLE", "loc": [-81.813081, 30.229176], "pop": 4093, "state": "FL", "_id": "32222"} -{"city": "JACKSONVILLE", "loc": [-81.629961, 30.154817], "pop": 19120, "state": "FL", "_id": "32223"} -{"city": "JACKSONVILLE", "loc": [-81.440427, 30.303076], "pop": 2535, "state": "FL", "_id": "32224"} -{"city": "JACKSONVILLE", "loc": [-81.506092, 30.350968], "pop": 26551, "state": "FL", "_id": "32225"} -{"city": "JACKSONVILLE", "loc": [-81.544808, 30.473485], "pop": 6880, "state": "FL", "_id": "32226"} -{"city": "JACKSONVILLE BEA", "loc": [-81.405424, 30.388275], "pop": 9055, "state": "FL", "_id": "32227"} -{"city": "ATLANTIC BEACH", "loc": [-81.415866, 30.348258], "pop": 23412, "state": "FL", "_id": "32233"} -{"city": "BALDWIN", "loc": [-81.978345, 30.229562], "pop": 5830, "state": "FL", "_id": "32234"} -{"city": "JACKSONVILLE", "loc": [-81.75558, 30.223137], "pop": 37603, "state": "FL", "_id": "32244"} -{"city": "JACKSONVILLE BEA", "loc": [-81.406243, 30.28319], "pop": 22392, "state": "FL", "_id": "32250"} -{"city": "JACKSONVILLE", "loc": [-81.557139, 30.221356], "pop": 17293, "state": "FL", "_id": "32256"} -{"city": "JACKSONVILLE", "loc": [-81.605042, 30.192703], "pop": 30022, "state": "FL", "_id": "32257"} -{"city": "JACKSONVILLE", "loc": [-81.573864, 30.145944], "pop": 7261, "state": "FL", "_id": "32258"} -{"city": "JACKSONVILLE", "loc": [-81.621701, 30.095578], "pop": 6677, "state": "FL", "_id": "32259"} -{"city": "NEPTUNE BEACH", "loc": [-81.405123, 30.31548], "pop": 6816, "state": "FL", "_id": "32266"} -{"city": "TALLAHASSEE", "loc": [-84.259337, 30.428563], "pop": 21329, "state": "FL", "_id": "32301"} -{"city": "TALLAHASSEE", "loc": [-84.318946, 30.487433], "pop": 36053, "state": "FL", "_id": "32303"} -{"city": "TALLAHASSEE", "loc": [-84.321132, 30.447752], "pop": 33437, "state": "FL", "_id": "32304"} -{"city": "TALLAHASSEE", "loc": [-84.295594, 30.442152], "pop": 1690, "state": "FL", "_id": "32306"} -{"city": "TALLAHASSEE", "loc": [-84.206903, 30.507725], "pop": 34857, "state": "FL", "_id": "32308"} -{"city": "TALLAHASSEE", "loc": [-84.3298, 30.399125], "pop": 30379, "state": "FL", "_id": "32310"} -{"city": "TALLAHASSEE", "loc": [-84.186995, 30.415625], "pop": 17005, "state": "FL", "_id": "32311"} -{"city": "TALLAHASSEE", "loc": [-84.262708, 30.518474], "pop": 17743, "state": "FL", "_id": "32312"} -{"city": "APALACHICOLA", "loc": [-85.006264, 29.725465], "pop": 3859, "state": "FL", "_id": "32320"} -{"city": "BRISTOL", "loc": [-84.946558, 30.422279], "pop": 4078, "state": "FL", "_id": "32321"} -{"city": "CARRABELLE", "loc": [-84.635845, 29.869205], "pop": 2138, "state": "FL", "_id": "32322"} -{"city": "CHATTAHOOCHEE", "loc": [-84.828044, 30.683394], "pop": 6325, "state": "FL", "_id": "32324"} -{"city": "CRAWFORDVILLE", "loc": [-84.320479, 30.210831], "pop": 10004, "state": "FL", "_id": "32327"} -{"city": "SAINT GEORGE ISL", "loc": [-84.8701, 29.733906], "pop": 2541, "state": "FL", "_id": "32328"} -{"city": "GREENVILLE", "loc": [-83.647397, 30.451199], "pop": 4107, "state": "FL", "_id": "32331"} -{"city": "HAVANA", "loc": [-84.41434, 30.609242], "pop": 9767, "state": "FL", "_id": "32333"} -{"city": "HOSFORD", "loc": [-84.805433, 30.363875], "pop": 1491, "state": "FL", "_id": "32334"} -{"city": "LAMONT", "loc": [-83.900266, 30.365341], "pop": 1409, "state": "FL", "_id": "32336"} -{"city": "MADISON", "loc": [-83.406678, 30.480209], "pop": 11339, "state": "FL", "_id": "32340"} -{"city": "MONTICELLO", "loc": [-83.892454, 30.519681], "pop": 9578, "state": "FL", "_id": "32344"} -{"city": "PANACEA", "loc": [-84.391212, 30.015322], "pop": 1292, "state": "FL", "_id": "32346"} -{"city": "PERRY", "loc": [-83.585021, 30.097489], "pop": 15401, "state": "FL", "_id": "32347"} -{"city": "QUINCY", "loc": [-84.60945, 30.586675], "pop": 25013, "state": "FL", "_id": "32351"} -{"city": "SALEM", "loc": [-83.385828, 29.823815], "pop": 264, "state": "FL", "_id": "32356"} -{"city": "SOPCHOPPY", "loc": [-84.454877, 30.071353], "pop": 3335, "state": "FL", "_id": "32358"} -{"city": "STEINHATCHEE", "loc": [-83.372332, 29.673871], "pop": 1415, "state": "FL", "_id": "32359"} -{"city": "PANAMA CITY", "loc": [-85.649403, 30.160624], "pop": 24968, "state": "FL", "_id": "32401"} -{"city": "PANAMA CITY", "loc": [-85.576225, 30.058252], "pop": 5333, "state": "FL", "_id": "32403"} -{"city": "PANAMA CITY", "loc": [-85.576264, 30.165291], "pop": 30101, "state": "FL", "_id": "32404"} -{"city": "PANAMA CITY", "loc": [-85.672686, 30.194949], "pop": 25701, "state": "FL", "_id": "32405"} -{"city": "PANAMA CITY BEAC", "loc": [-85.791984, 30.194623], "pop": 3115, "state": "FL", "_id": "32407"} -{"city": "PANAMA CITY BEAC", "loc": [-85.763628, 30.160859], "pop": 9702, "state": "FL", "_id": "32408"} -{"city": "SOUTHPORT", "loc": [-85.644536, 30.310679], "pop": 5001, "state": "FL", "_id": "32409"} -{"city": "PANAMA CITY BEAC", "loc": [-85.904946, 30.245835], "pop": 5646, "state": "FL", "_id": "32413"} -{"city": "ALFORD", "loc": [-85.34838, 30.714106], "pop": 576, "state": "FL", "_id": "32420"} -{"city": "ALTHA", "loc": [-85.17043, 30.531882], "pop": 3280, "state": "FL", "_id": "32421"} -{"city": "BASCOM", "loc": [-85.09722, 30.951365], "pop": 1011, "state": "FL", "_id": "32423"} -{"city": "BLOUNTSTOWN", "loc": [-85.062022, 30.4394], "pop": 6984, "state": "FL", "_id": "32424"} -{"city": "BONIFAY", "loc": [-85.689962, 30.846369], "pop": 9342, "state": "FL", "_id": "32425"} -{"city": "CAMPBELLTON", "loc": [-85.376596, 30.95629], "pop": 741, "state": "FL", "_id": "32426"} -{"city": "CARYVILLE", "loc": [-85.799787, 30.796878], "pop": 2517, "state": "FL", "_id": "32427"} -{"city": "CHIPLEY", "loc": [-85.548646, 30.710658], "pop": 11248, "state": "FL", "_id": "32428"} -{"city": "CLARKSVILLE", "loc": [-85.189806, 30.356834], "pop": 129, "state": "FL", "_id": "32430"} -{"city": "COTTONDALE", "loc": [-85.384672, 30.800359], "pop": 3333, "state": "FL", "_id": "32431"} -{"city": "DE FUNIAK SPRING", "loc": [-86.138006, 30.751783], "pop": 15496, "state": "FL", "_id": "32433"} -{"city": "EBRO", "loc": [-85.888066, 30.435181], "pop": 361, "state": "FL", "_id": "32437"} -{"city": "FOUNTAIN", "loc": [-85.429272, 30.475327], "pop": 1869, "state": "FL", "_id": "32438"} -{"city": "FREEPORT", "loc": [-86.168441, 30.489596], "pop": 744, "state": "FL", "_id": "32439"} -{"city": "GRACEVILLE", "loc": [-85.513622, 30.942601], "pop": 5353, "state": "FL", "_id": "32440"} -{"city": "GRAND RIDGE", "loc": [-85.020954, 30.714831], "pop": 1708, "state": "FL", "_id": "32442"} -{"city": "GREENWOOD", "loc": [-85.15549, 30.852506], "pop": 4058, "state": "FL", "_id": "32443"} -{"city": "LYNN HAVEN", "loc": [-85.646658, 30.236165], "pop": 12205, "state": "FL", "_id": "32444"} -{"city": "MALONE", "loc": [-85.163874, 30.960245], "pop": 1046, "state": "FL", "_id": "32445"} -{"city": "MARIANNA", "loc": [-85.229367, 30.758587], "pop": 17908, "state": "FL", "_id": "32446"} -{"city": "KINARD", "loc": [-85.206467, 30.263241], "pop": 297, "state": "FL", "_id": "32449"} -{"city": "PONCE DE LEON", "loc": [-85.954633, 30.704146], "pop": 2200, "state": "FL", "_id": "32455"} -{"city": "PORT SAINT JOE", "loc": [-85.298787, 29.83539], "pop": 7490, "state": "FL", "_id": "32456"} -{"city": "SANTA ROSA BEACH", "loc": [-86.245809, 30.365883], "pop": 5039, "state": "FL", "_id": "32459"} -{"city": "SNEADS", "loc": [-84.933655, 30.727619], "pop": 6334, "state": "FL", "_id": "32460"} -{"city": "VERNON", "loc": [-85.755286, 30.62668], "pop": 4111, "state": "FL", "_id": "32462"} -{"city": "WESTVILLE", "loc": [-85.912973, 30.874689], "pop": 3081, "state": "FL", "_id": "32464"} -{"city": "WEWAHITCHKA", "loc": [-85.20483, 30.093255], "pop": 4014, "state": "FL", "_id": "32465"} -{"city": "YOUNGSTOWN", "loc": [-85.516881, 30.326913], "pop": 3634, "state": "FL", "_id": "32466"} -{"city": "PENSACOLA", "loc": [-87.224763, 30.422282], "pop": 16485, "state": "FL", "_id": "32501"} -{"city": "PENSACOLA", "loc": [-87.210432, 30.456406], "pop": 34491, "state": "FL", "_id": "32503"} -{"city": "PENSACOLA", "loc": [-87.187242, 30.487299], "pop": 23077, "state": "FL", "_id": "32504"} -{"city": "PENSACOLA", "loc": [-87.258937, 30.448069], "pop": 29026, "state": "FL", "_id": "32505"} -{"city": "PENSACOLA", "loc": [-87.309185, 30.412912], "pop": 29834, "state": "FL", "_id": "32506"} -{"city": "PENSACOLA", "loc": [-87.312558, 30.373707], "pop": 23525, "state": "FL", "_id": "32507"} -{"city": "PENSACOLA", "loc": [-87.274945, 30.351063], "pop": 3688, "state": "FL", "_id": "32508"} -{"city": "PENSACOLA", "loc": [-87.216723, 30.524148], "pop": 30185, "state": "FL", "_id": "32514"} -{"city": "PENSACOLA", "loc": [-87.317925, 30.475593], "pop": 28674, "state": "FL", "_id": "32526"} -{"city": "BAKER", "loc": [-86.677015, 30.831569], "pop": 3389, "state": "FL", "_id": "32531"} -{"city": "CANTONMENT", "loc": [-87.325052, 30.614253], "pop": 19829, "state": "FL", "_id": "32533"} -{"city": "PENSACOLA", "loc": [-87.279324, 30.530065], "pop": 12046, "state": "FL", "_id": "32534"} -{"city": "CENTURY", "loc": [-87.321582, 30.968742], "pop": 5422, "state": "FL", "_id": "32535"} -{"city": "CRESTVIEW", "loc": [-86.553678, 30.77061], "pop": 21799, "state": "FL", "_id": "32536"} -{"city": "SANDESTIN", "loc": [-86.484903, 30.397198], "pop": 8080, "state": "FL", "_id": "32541"} -{"city": "EGLIN A F B", "loc": [-86.615958, 30.479409], "pop": 13431, "state": "FL", "_id": "32542"} -{"city": "FORT WALTON BEAC", "loc": [-86.627487, 30.447297], "pop": 27344, "state": "FL", "_id": "32547"} -{"city": "FORT WALTON BEAC", "loc": [-86.621479, 30.415262], "pop": 21791, "state": "FL", "_id": "32548"} -{"city": "GULF BREEZE", "loc": [-87.043875, 30.3847], "pop": 27875, "state": "FL", "_id": "32561"} -{"city": "HOLT", "loc": [-86.704638, 30.72522], "pop": 1821, "state": "FL", "_id": "32564"} -{"city": "JAY", "loc": [-87.133237, 30.898488], "pop": 5952, "state": "FL", "_id": "32565"} -{"city": "NAVARRE", "loc": [-86.937102, 30.590261], "pop": 5537, "state": "FL", "_id": "32566"} -{"city": "LAUREL HILL", "loc": [-86.400323, 30.95236], "pop": 2967, "state": "FL", "_id": "32567"} -{"city": "WALNUT HILL", "loc": [-87.449628, 30.870043], "pop": 3604, "state": "FL", "_id": "32568"} -{"city": "MARY ESTHER", "loc": [-86.712719, 30.412186], "pop": 9382, "state": "FL", "_id": "32569"} -{"city": "MILTON", "loc": [-87.047278, 30.660413], "pop": 20038, "state": "FL", "_id": "32570"} -{"city": "PACE", "loc": [-87.15033, 30.616173], "pop": 15661, "state": "FL", "_id": "32571"} -{"city": "NICEVILLE", "loc": [-86.41446, 30.495771], "pop": 25146, "state": "FL", "_id": "32578"} -{"city": "SHALIMAR", "loc": [-86.571724, 30.445565], "pop": 9327, "state": "FL", "_id": "32579"} -{"city": "VALPARAISO", "loc": [-86.500914, 30.509197], "pop": 4964, "state": "FL", "_id": "32580"} -{"city": "MILTON", "loc": [-87.066273, 30.576058], "pop": 9457, "state": "FL", "_id": "32583"} -{"city": "GAINESVILLE", "loc": [-82.310046, 29.645029], "pop": 31328, "state": "FL", "_id": "32601"} -{"city": "GAINESVILLE", "loc": [-82.349286, 29.651484], "pop": 5271, "state": "FL", "_id": "32603"} -{"city": "GAINESVILLE", "loc": [-82.36794, 29.678458], "pop": 21349, "state": "FL", "_id": "32605"} -{"city": "GAINESVILLE", "loc": [-82.402324, 29.695393], "pop": 18408, "state": "FL", "_id": "32606"} -{"city": "GAINESVILLE", "loc": [-82.403252, 29.645618], "pop": 21103, "state": "FL", "_id": "32607"} -{"city": "GAINESVILLE", "loc": [-82.387282, 29.613204], "pop": 22945, "state": "FL", "_id": "32608"} -{"city": "GAINESVILLE", "loc": [-82.308032, 29.70053], "pop": 17668, "state": "FL", "_id": "32609"} -{"city": "GAINESVILLE", "loc": [-82.35092, 29.644148], "pop": 8023, "state": "FL", "_id": "32611"} -{"city": "SANTA FE", "loc": [-82.480531, 29.796996], "pop": 9414, "state": "FL", "_id": "32615"} -{"city": "ANTHONY", "loc": [-82.126157, 29.304785], "pop": 6296, "state": "FL", "_id": "32617"} -{"city": "ARCHER", "loc": [-82.51084, 29.559738], "pop": 6188, "state": "FL", "_id": "32618"} -{"city": "BELL", "loc": [-82.871106, 29.78373], "pop": 2446, "state": "FL", "_id": "32619"} -{"city": "32620", "loc": [-82.04621, 29.050444], "pop": 11567, "state": "FL", "_id": "32620"} -{"city": "BRONSON", "loc": [-82.635644, 29.460952], "pop": 2111, "state": "FL", "_id": "32621"} -{"city": "BROOKER", "loc": [-82.295634, 29.919028], "pop": 1194, "state": "FL", "_id": "32622"} -{"city": "CEDAR KEY", "loc": [-83.016793, 29.171006], "pop": 1173, "state": "FL", "_id": "32625"} -{"city": "CHIEFLAND", "loc": [-82.880896, 29.483243], "pop": 7498, "state": "FL", "_id": "32626"} -{"city": "32629", "loc": [-82.60677, 28.910801], "pop": 4922, "state": "FL", "_id": "32629"} -{"city": "32630", "loc": [-82.433118, 29.063564], "pop": 20682, "state": "FL", "_id": "32630"} -{"city": "EARLETON", "loc": [-82.113762, 29.722159], "pop": 1014, "state": "FL", "_id": "32631"} -{"city": "32636", "loc": [-82.290401, 28.742508], "pop": 7024, "state": "FL", "_id": "32636"} -{"city": "HAWTHORNE", "loc": [-82.105625, 29.573998], "pop": 4151, "state": "FL", "_id": "32640"} -{"city": "32642", "loc": [-82.392516, 28.926371], "pop": 8744, "state": "FL", "_id": "32642"} -{"city": "HIGH SPRINGS", "loc": [-82.615628, 29.841022], "pop": 7557, "state": "FL", "_id": "32643"} -{"city": "32646", "loc": [-82.593623, 28.785839], "pop": 5676, "state": "FL", "_id": "32646"} -{"city": "HORSESHOE BEACH", "loc": [-83.261587, 29.48689], "pop": 652, "state": "FL", "_id": "32648"} -{"city": "32649", "loc": [-82.661343, 29.076034], "pop": 4778, "state": "FL", "_id": "32649"} -{"city": "32650", "loc": [-82.320713, 28.851002], "pop": 15207, "state": "FL", "_id": "32650"} -{"city": "32652", "loc": [-82.343647, 28.80514], "pop": 8683, "state": "FL", "_id": "32652"} -{"city": "KEYSTONE HEIGHTS", "loc": [-81.989885, 29.797579], "pop": 8011, "state": "FL", "_id": "32656"} -{"city": "32661", "loc": [-82.528448, 28.824661], "pop": 20969, "state": "FL", "_id": "32661"} -{"city": "32665", "loc": [-82.501478, 28.911886], "pop": 11309, "state": "FL", "_id": "32665"} -{"city": "MELROSE", "loc": [-82.027863, 29.732456], "pop": 5507, "state": "FL", "_id": "32666"} -{"city": "MICANOPY", "loc": [-82.279698, 29.526029], "pop": 2409, "state": "FL", "_id": "32667"} -{"city": "MORRISTON", "loc": [-82.491668, 29.28126], "pop": 2054, "state": "FL", "_id": "32668"} -{"city": "NEWBERRY", "loc": [-82.585188, 29.660906], "pop": 5491, "state": "FL", "_id": "32669"} -{"city": "32670", "loc": [-82.111182, 29.216545], "pop": 24226, "state": "FL", "_id": "32670"} -{"city": "32671", "loc": [-82.090654, 29.166825], "pop": 30749, "state": "FL", "_id": "32671"} -{"city": "32672", "loc": [-82.012052, 29.109572], "pop": 14868, "state": "FL", "_id": "32672"} -{"city": "32673", "loc": [-82.231082, 28.991755], "pop": 4757, "state": "FL", "_id": "32673"} -{"city": "32674", "loc": [-82.172984, 29.161336], "pop": 12026, "state": "FL", "_id": "32674"} -{"city": "32675", "loc": [-82.231943, 29.209198], "pop": 21074, "state": "FL", "_id": "32675"} -{"city": "32676", "loc": [-82.18804, 29.078026], "pop": 9663, "state": "FL", "_id": "32676"} -{"city": "OLD TOWN", "loc": [-83.057393, 29.624558], "pop": 9494, "state": "FL", "_id": "32680"} -{"city": "32684", "loc": [-82.037814, 28.920137], "pop": 2574, "state": "FL", "_id": "32684"} -{"city": "REDDICK", "loc": [-82.243995, 29.375352], "pop": 10006, "state": "FL", "_id": "32686"} -{"city": "32688", "loc": [-82.043715, 29.226185], "pop": 1337, "state": "FL", "_id": "32688"} -{"city": "32691", "loc": [-82.046434, 28.999563], "pop": 10274, "state": "FL", "_id": "32691"} -{"city": "TRENTON", "loc": [-82.809345, 29.626375], "pop": 6925, "state": "FL", "_id": "32693"} -{"city": "WALDO", "loc": [-82.160791, 29.787096], "pop": 1676, "state": "FL", "_id": "32694"} -{"city": "WILLISTON", "loc": [-82.485601, 29.397737], "pop": 7664, "state": "FL", "_id": "32696"} -{"city": "32698", "loc": [-82.728163, 29.022103], "pop": 173, "state": "FL", "_id": "32698"} -{"city": "ALTAMONTE SPRING", "loc": [-81.371908, 28.662728], "pop": 21392, "state": "FL", "_id": "32701"} -{"city": "ALTOONA", "loc": [-81.632322, 29.021935], "pop": 1743, "state": "FL", "_id": "32702"} -{"city": "HUNT CLUB", "loc": [-81.485149, 28.661865], "pop": 34100, "state": "FL", "_id": "32703"} -{"city": "CASSELBERRY", "loc": [-81.312215, 28.661671], "pop": 30933, "state": "FL", "_id": "32707"} -{"city": "WINTER SPRINGS", "loc": [-81.281367, 28.683097], "pop": 27311, "state": "FL", "_id": "32708"} -{"city": "CHRISTMAS", "loc": [-81.01157, 28.546244], "pop": 2331, "state": "FL", "_id": "32709"} -{"city": "APOPKA", "loc": [-81.513615, 28.711976], "pop": 20208, "state": "FL", "_id": "32712"} -{"city": "DEBARY", "loc": [-81.306506, 28.884573], "pop": 9491, "state": "FL", "_id": "32713"} -{"city": "FOREST CITY", "loc": [-81.408533, 28.664983], "pop": 29133, "state": "FL", "_id": "32714"} -{"city": "DELAND", "loc": [-81.334853, 29.02659], "pop": 23152, "state": "FL", "_id": "32720"} -{"city": "DELAND", "loc": [-81.286341, 29.04225], "pop": 21715, "state": "FL", "_id": "32724"} -{"city": "DELTONA", "loc": [-81.247307, 28.898897], "pop": 27678, "state": "FL", "_id": "32725"} -{"city": "EUSTIS", "loc": [-81.64513, 28.857686], "pop": 19585, "state": "FL", "_id": "32726"} -{"city": "FERN PARK", "loc": [-81.341837, 28.651161], "pop": 4815, "state": "FL", "_id": "32730"} -{"city": "GENEVA", "loc": [-81.11137, 28.750299], "pop": 3827, "state": "FL", "_id": "32732"} -{"city": "GRAND ISLAND", "loc": [-81.739093, 28.886552], "pop": 1416, "state": "FL", "_id": "32735"} -{"city": "DELTONA", "loc": [-81.192171, 28.909311], "pop": 22426, "state": "FL", "_id": "32738"} -{"city": "LAKE HELEN", "loc": [-81.233367, 28.980567], "pop": 3229, "state": "FL", "_id": "32744"} -{"city": "HEATHROW", "loc": [-81.338075, 28.752352], "pop": 9959, "state": "FL", "_id": "32746"} -{"city": "LONGWOOD", "loc": [-81.355238, 28.711994], "pop": 27633, "state": "FL", "_id": "32750"} -{"city": "EATONVILLE", "loc": [-81.354598, 28.631284], "pop": 19834, "state": "FL", "_id": "32751"} -{"city": "MIMS", "loc": [-80.866278, 28.697383], "pop": 8943, "state": "FL", "_id": "32754"} -{"city": "MOUNT DORA", "loc": [-81.645593, 28.792787], "pop": 15757, "state": "FL", "_id": "32757"} -{"city": "OAK HILL", "loc": [-80.855063, 28.869985], "pop": 2261, "state": "FL", "_id": "32759"} -{"city": "ORANGE CITY", "loc": [-81.299524, 28.945291], "pop": 12946, "state": "FL", "_id": "32763"} -{"city": "OSTEEN", "loc": [-81.156224, 28.842617], "pop": 2215, "state": "FL", "_id": "32764"} -{"city": "OVIEDO", "loc": [-81.206593, 28.651256], "pop": 19519, "state": "FL", "_id": "32765"} -{"city": "CHULUOTA", "loc": [-81.118237, 28.640634], "pop": 3280, "state": "FL", "_id": "32766"} -{"city": "PAISLEY", "loc": [-81.503009, 28.999323], "pop": 1963, "state": "FL", "_id": "32767"} -{"city": "SANFORD", "loc": [-81.285044, 28.801307], "pop": 27016, "state": "FL", "_id": "32771"} -{"city": "SANFORD", "loc": [-81.282042, 28.764385], "pop": 19707, "state": "FL", "_id": "32773"} -{"city": "SORRENTO", "loc": [-81.532317, 28.803519], "pop": 5382, "state": "FL", "_id": "32776"} -{"city": "TAVARES", "loc": [-81.73405, 28.801027], "pop": 12131, "state": "FL", "_id": "32778"} -{"city": "SPRINGS PLAZA", "loc": [-81.422767, 28.703978], "pop": 27075, "state": "FL", "_id": "32779"} -{"city": "TITUSVILLE", "loc": [-80.819141, 28.569712], "pop": 28649, "state": "FL", "_id": "32780"} -{"city": "DONA VISTA", "loc": [-81.671653, 28.931443], "pop": 7866, "state": "FL", "_id": "32784"} -{"city": "WINTER PARK", "loc": [-81.353436, 28.597824], "pop": 24236, "state": "FL", "_id": "32789"} -{"city": "ALOMA", "loc": [-81.302112, 28.60779], "pop": 44973, "state": "FL", "_id": "32792"} -{"city": "TITUSVILLE", "loc": [-80.842915, 28.627078], "pop": 19916, "state": "FL", "_id": "32796"} -{"city": "ZELLWOOD", "loc": [-81.576174, 28.71944], "pop": 1930, "state": "FL", "_id": "32798"} -{"city": "ORLANDO", "loc": [-81.372668, 28.539882], "pop": 9275, "state": "FL", "_id": "32801"} -{"city": "ORLANDO", "loc": [-81.353462, 28.555897], "pop": 19992, "state": "FL", "_id": "32803"} -{"city": "FAIRVILLA", "loc": [-81.391955, 28.576547], "pop": 18087, "state": "FL", "_id": "32804"} -{"city": "ORLANDO", "loc": [-81.404516, 28.5302], "pop": 29117, "state": "FL", "_id": "32805"} -{"city": "ORLANDO", "loc": [-81.356968, 28.513958], "pop": 25996, "state": "FL", "_id": "32806"} -{"city": "AZALEA PARK", "loc": [-81.305274, 28.544924], "pop": 28087, "state": "FL", "_id": "32807"} -{"city": "PINE HILLS", "loc": [-81.44758, 28.580463], "pop": 42278, "state": "FL", "_id": "32808"} -{"city": "PINE CASTLE", "loc": [-81.381751, 28.461916], "pop": 17602, "state": "FL", "_id": "32809"} -{"city": "LOCKHART", "loc": [-81.425852, 28.622183], "pop": 23781, "state": "FL", "_id": "32810"} -{"city": "ORLO VISTA", "loc": [-81.442014, 28.516082], "pop": 21545, "state": "FL", "_id": "32811"} -{"city": "ORLANDO", "loc": [-81.328816, 28.49981], "pop": 26888, "state": "FL", "_id": "32812"} -{"city": "NAVAL TRAINING C", "loc": [-81.328966, 28.570467], "pop": 9216, "state": "FL", "_id": "32813"} -{"city": "KENNEDY SPACE CE", "loc": [-80.58248, 28.498821], "pop": 1, "state": "FL", "_id": "32815"} -{"city": "UNION PARK", "loc": [-81.253537, 28.590251], "pop": 20723, "state": "FL", "_id": "32817"} -{"city": "ORLANDO", "loc": [-81.484618, 28.580147], "pop": 26887, "state": "FL", "_id": "32818"} -{"city": "SAND LAKE", "loc": [-81.452484, 28.467258], "pop": 4434, "state": "FL", "_id": "32819"} -{"city": "UNION PARK", "loc": [-81.110628, 28.578256], "pop": 2587, "state": "FL", "_id": "32820"} -{"city": "ORLANDO", "loc": [-81.466602, 28.395724], "pop": 9982, "state": "FL", "_id": "32821"} -{"city": "VENTURA", "loc": [-81.293874, 28.504765], "pop": 33986, "state": "FL", "_id": "32822"} -{"city": "ORLANDO", "loc": [-81.362187, 28.393157], "pop": 8225, "state": "FL", "_id": "32824"} -{"city": "ORLANDO", "loc": [-81.257081, 28.546865], "pop": 26373, "state": "FL", "_id": "32825"} -{"city": "ORLANDO", "loc": [-81.190705, 28.582601], "pop": 12369, "state": "FL", "_id": "32826"} -{"city": "ORLANDO", "loc": [-81.342979, 28.43168], "pop": 3831, "state": "FL", "_id": "32827"} -{"city": "ORLANDO", "loc": [-81.179489, 28.552297], "pop": 3249, "state": "FL", "_id": "32828"} -{"city": "ORLANDO", "loc": [-81.260778, 28.484877], "pop": 3848, "state": "FL", "_id": "32829"} -{"city": "LAKE BUENA VISTA", "loc": [-81.519034, 28.369378], "pop": 6, "state": "FL", "_id": "32830"} -{"city": "ORLANDO", "loc": [-81.191768, 28.488229], "pop": 1123, "state": "FL", "_id": "32831"} -{"city": "ORLANDO", "loc": [-81.188807, 28.377428], "pop": 1863, "state": "FL", "_id": "32832"} -{"city": "UNION PARK", "loc": [-81.098129, 28.531797], "pop": 3748, "state": "FL", "_id": "32833"} -{"city": "ORLANDO", "loc": [-81.478663, 28.528885], "pop": 20343, "state": "FL", "_id": "32835"} -{"city": "ORLANDO", "loc": [-81.49564, 28.460842], "pop": 21329, "state": "FL", "_id": "32836"} -{"city": "ORLANDO", "loc": [-81.417882, 28.394861], "pop": 13075, "state": "FL", "_id": "32837"} -{"city": "ORLANDO", "loc": [-81.408162, 28.487102], "pop": 33946, "state": "FL", "_id": "32839"} -{"city": "MELBOURNE", "loc": [-80.620015, 28.069132], "pop": 21138, "state": "FL", "_id": "32901"} -{"city": "INDIALANTIC", "loc": [-80.578718, 28.109059], "pop": 11020, "state": "FL", "_id": "32903"} -{"city": "MELBOURNE VILLAG", "loc": [-80.668577, 28.073177], "pop": 15441, "state": "FL", "_id": "32904"} -{"city": "PALM BAY", "loc": [-80.599087, 28.014605], "pop": 26367, "state": "FL", "_id": "32905"} -{"city": "PALM BAY", "loc": [-80.673889, 28.016849], "pop": 25674, "state": "FL", "_id": "32907"} -{"city": "PALM BAY", "loc": [-80.689426, 27.981636], "pop": 3272, "state": "FL", "_id": "32908"} -{"city": "PALM BAY", "loc": [-80.647327, 27.96936], "pop": 12028, "state": "FL", "_id": "32909"} -{"city": "CAPE CANAVERAL", "loc": [-80.604267, 28.39034], "pop": 7655, "state": "FL", "_id": "32920"} -{"city": "COCOA", "loc": [-80.746455, 28.367183], "pop": 17316, "state": "FL", "_id": "32922"} -{"city": "PATRICK A F B", "loc": [-80.607126, 28.259896], "pop": 597, "state": "FL", "_id": "32925"} -{"city": "COCOA", "loc": [-80.786969, 28.390987], "pop": 17930, "state": "FL", "_id": "32926"} -{"city": "PORT SAINT JOHN", "loc": [-80.791114, 28.46844], "pop": 17351, "state": "FL", "_id": "32927"} -{"city": "COCOA BEACH", "loc": [-80.612066, 28.332451], "pop": 14989, "state": "FL", "_id": "32931"} -{"city": "EAU GALLIE", "loc": [-80.691683, 28.136822], "pop": 9539, "state": "FL", "_id": "32934"} -{"city": "MELBOURNE", "loc": [-80.652353, 28.138385], "pop": 34153, "state": "FL", "_id": "32935"} -{"city": "INDIAN HARBOR BE", "loc": [-80.598671, 28.178571], "pop": 28921, "state": "FL", "_id": "32937"} -{"city": "MELBOURNE", "loc": [-80.684959, 28.206136], "pop": 5360, "state": "FL", "_id": "32940"} -{"city": "FELLSMERE", "loc": [-80.601947, 27.764273], "pop": 2936, "state": "FL", "_id": "32948"} -{"city": "MELBOURNE BEACH", "loc": [-80.538936, 28.021923], "pop": 8060, "state": "FL", "_id": "32951"} -{"city": "MERRITT ISLAND", "loc": [-80.67818, 28.328607], "pop": 12919, "state": "FL", "_id": "32952"} -{"city": "MERRITT ISLAND", "loc": [-80.695865, 28.391234], "pop": 23338, "state": "FL", "_id": "32953"} -{"city": "ROCKLEDGE", "loc": [-80.73193, 28.313441], "pop": 20576, "state": "FL", "_id": "32955"} -{"city": "SEBASTIAN", "loc": [-80.478432, 27.790082], "pop": 14084, "state": "FL", "_id": "32958"} -{"city": "VERO BEACH", "loc": [-80.403075, 27.632985], "pop": 19207, "state": "FL", "_id": "32960"} -{"city": "VERO BEACH", "loc": [-80.392251, 27.588486], "pop": 17462, "state": "FL", "_id": "32962"} -{"city": "INDIAN RIVER SHO", "loc": [-80.360916, 27.653623], "pop": 10980, "state": "FL", "_id": "32963"} -{"city": "VERO BEACH", "loc": [-80.47939, 27.637214], "pop": 10687, "state": "FL", "_id": "32966"} -{"city": "VERO BEACH", "loc": [-80.441617, 27.697223], "pop": 9607, "state": "FL", "_id": "32967"} -{"city": "VERO BEACH", "loc": [-80.438223, 27.59993], "pop": 5238, "state": "FL", "_id": "32968"} -{"city": "BAREFOOT BAY", "loc": [-80.516051, 27.878146], "pop": 7870, "state": "FL", "_id": "32976"} -{"city": "DANIA", "loc": [-80.144728, 26.047557], "pop": 12552, "state": "FL", "_id": "33004"} -{"city": "HALLANDALE", "loc": [-80.140737, 25.985019], "pop": 33743, "state": "FL", "_id": "33009"} -{"city": "HIALEAH", "loc": [-80.280801, 25.832536], "pop": 40437, "state": "FL", "_id": "33010"} -{"city": "HIALEAH", "loc": [-80.3059, 25.865395], "pop": 73194, "state": "FL", "_id": "33012"} -{"city": "HIALEAH", "loc": [-80.272533, 25.859351], "pop": 30108, "state": "FL", "_id": "33013"} -{"city": "HIALEAH", "loc": [-80.306255, 25.896349], "pop": 35873, "state": "FL", "_id": "33014"} -{"city": "HIALEAH", "loc": [-80.316545, 25.938841], "pop": 31171, "state": "FL", "_id": "33015"} -{"city": "HIALEAH", "loc": [-80.33681, 25.880262], "pop": 32053, "state": "FL", "_id": "33016"} -{"city": "HOLLYWOOD", "loc": [-80.121931, 26.007011], "pop": 12115, "state": "FL", "_id": "33019"} -{"city": "HOLLYWOOD", "loc": [-80.15166, 26.016091], "pop": 35468, "state": "FL", "_id": "33020"} -{"city": "HOLLYWOOD", "loc": [-80.189085, 26.021836], "pop": 39987, "state": "FL", "_id": "33021"} -{"city": "MIRAMAR", "loc": [-80.216035, 25.987516], "pop": 54274, "state": "FL", "_id": "33023"} -{"city": "PEMBROKE PINES", "loc": [-80.240183, 26.024273], "pop": 54411, "state": "FL", "_id": "33024"} -{"city": "HOLLYWOOD", "loc": [-80.271236, 25.992061], "pop": 24778, "state": "FL", "_id": "33025"} -{"city": "HOLLYWOOD", "loc": [-80.297441, 26.022927], "pop": 21473, "state": "FL", "_id": "33026"} -{"city": "HOLLYWOOD", "loc": [-80.32484, 25.997449], "pop": 6955, "state": "FL", "_id": "33027"} -{"city": "HOLLYWOOD", "loc": [-80.330797, 26.024804], "pop": 186, "state": "FL", "_id": "33028"} -{"city": "PEMBROKE PINES", "loc": [-80.428407, 26.01375], "pop": 2882, "state": "FL", "_id": "33029"} -{"city": "HOMESTEAD", "loc": [-80.483853, 25.476639], "pop": 26721, "state": "FL", "_id": "33030"} -{"city": "HOMESTEAD", "loc": [-80.507463, 25.532314], "pop": 5880, "state": "FL", "_id": "33031"} -{"city": "PRINCETON", "loc": [-80.40918, 25.521191], "pop": 18070, "state": "FL", "_id": "33032"} -{"city": "HOMESTEAD", "loc": [-80.438014, 25.490576], "pop": 25439, "state": "FL", "_id": "33033"} -{"city": "FLORIDA CITY", "loc": [-80.548438, 25.396332], "pop": 12115, "state": "FL", "_id": "33034"} -{"city": "HOMESTEAD", "loc": [-80.457153, 25.457338], "pop": 1727, "state": "FL", "_id": "33035"} -{"city": "ISLAMORADA", "loc": [-80.629953, 24.923331], "pop": 3810, "state": "FL", "_id": "33036"} -{"city": "OCEAN REEF", "loc": [-80.406084, 25.140214], "pop": 12076, "state": "FL", "_id": "33037"} -{"city": "HOMESTEAD AIR FO", "loc": [-80.390513, 25.499088], "pop": 6538, "state": "FL", "_id": "33039"} -{"city": "NAVAL AIR STATIO", "loc": [-81.762179, 24.565313], "pop": 32986, "state": "FL", "_id": "33040"} -{"city": "SUMMERLAND KEY", "loc": [-81.493564, 24.655322], "pop": 3952, "state": "FL", "_id": "33042"} -{"city": "BIG PINE KEY", "loc": [-81.362029, 24.679996], "pop": 5956, "state": "FL", "_id": "33043"} -{"city": "MARATHON", "loc": [-81.038581, 24.727919], "pop": 12792, "state": "FL", "_id": "33050"} -{"city": "OPA LOCKA", "loc": [-80.247004, 25.909662], "pop": 30405, "state": "FL", "_id": "33054"} -{"city": "CAROL CITY", "loc": [-80.277291, 25.944076], "pop": 40586, "state": "FL", "_id": "33055"} -{"city": "CAROL CITY", "loc": [-80.248059, 25.946906], "pop": 31968, "state": "FL", "_id": "33056"} -{"city": "POMPANO BEACH", "loc": [-80.12346, 26.231529], "pop": 32292, "state": "FL", "_id": "33060"} -{"city": "POMPANO BEACH", "loc": [-80.094133, 26.234314], "pop": 20836, "state": "FL", "_id": "33062"} -{"city": "MARGATE", "loc": [-80.211483, 26.249221], "pop": 37607, "state": "FL", "_id": "33063"} -{"city": "LIGHTHOUSE POINT", "loc": [-80.112439, 26.278698], "pop": 50084, "state": "FL", "_id": "33064"} -{"city": "CORAL SPRINGS", "loc": [-80.255578, 26.271403], "pop": 43659, "state": "FL", "_id": "33065"} -{"city": "MARGATE", "loc": [-80.177878, 26.254237], "pop": 16494, "state": "FL", "_id": "33066"} -{"city": "NORTH CORAL SPRI", "loc": [-80.22188, 26.305134], "pop": 7227, "state": "FL", "_id": "33067"} -{"city": "POMPANO BEACH", "loc": [-80.22054, 26.216021], "pop": 41835, "state": "FL", "_id": "33068"} -{"city": "POMPANO BEACH", "loc": [-80.163486, 26.228817], "pop": 20158, "state": "FL", "_id": "33069"} -{"city": "TAVERNIER", "loc": [-80.521816, 25.010788], "pop": 6196, "state": "FL", "_id": "33070"} -{"city": "POMPANO BEACH", "loc": [-80.260085, 26.243515], "pop": 28251, "state": "FL", "_id": "33071"} -{"city": "POMPANO BEACH", "loc": [-80.180966, 26.299693], "pop": 7091, "state": "FL", "_id": "33073"} -{"city": "POMPANO BEACH", "loc": [-80.248086, 26.291902], "pop": 4728, "state": "FL", "_id": "33076"} -{"city": "MIAMI", "loc": [-80.320733, 25.7911], "pop": 8, "state": "FL", "_id": "33122"} -{"city": "MIAMI", "loc": [-80.234118, 25.782547], "pop": 47761, "state": "FL", "_id": "33125"} -{"city": "MIAMI", "loc": [-80.291932, 25.776255], "pop": 39861, "state": "FL", "_id": "33126"} -{"city": "MIAMI", "loc": [-80.205121, 25.814344], "pop": 29900, "state": "FL", "_id": "33127"} -{"city": "MIAMI", "loc": [-80.208858, 25.775612], "pop": 6965, "state": "FL", "_id": "33128"} -{"city": "MIAMI", "loc": [-80.201301, 25.755926], "pop": 10225, "state": "FL", "_id": "33129"} -{"city": "MIAMI", "loc": [-80.205888, 25.767197], "pop": 21777, "state": "FL", "_id": "33130"} -{"city": "MIAMI", "loc": [-80.189506, 25.762852], "pop": 2614, "state": "FL", "_id": "33131"} -{"city": "MIAMI", "loc": [-80.179996, 25.786712], "pop": 5198, "state": "FL", "_id": "33132"} -{"city": "CORAL GABLES", "loc": [-80.243639, 25.732251], "pop": 28672, "state": "FL", "_id": "33133"} -{"city": "CORAL GABLES", "loc": [-80.269576, 25.755582], "pop": 33492, "state": "FL", "_id": "33134"} -{"city": "MIAMI", "loc": [-80.231746, 25.766391], "pop": 35425, "state": "FL", "_id": "33135"} -{"city": "MIAMI", "loc": [-80.204232, 25.786385], "pop": 14040, "state": "FL", "_id": "33136"} -{"city": "MIAMI", "loc": [-80.189663, 25.815648], "pop": 19862, "state": "FL", "_id": "33137"} -{"city": "MIAMI SHORES", "loc": [-80.18526, 25.850208], "pop": 30108, "state": "FL", "_id": "33138"} -{"city": "CARL FISHER", "loc": [-80.136378, 25.785179], "pop": 48971, "state": "FL", "_id": "33139"} -{"city": "MIAMI", "loc": [-80.127921, 25.819505], "pop": 13057, "state": "FL", "_id": "33140"} -{"city": "NORTH BAY VILLAG", "loc": [-80.133578, 25.852384], "pop": 29489, "state": "FL", "_id": "33141"} -{"city": "MIAMI", "loc": [-80.232023, 25.812966], "pop": 52262, "state": "FL", "_id": "33142"} -{"city": "SOUTH MIAMI", "loc": [-80.301408, 25.700252], "pop": 28410, "state": "FL", "_id": "33143"} -{"city": "MIAMI", "loc": [-80.309631, 25.762563], "pop": 22968, "state": "FL", "_id": "33144"} -{"city": "CORAL GABLES", "loc": [-80.235134, 25.752648], "pop": 28170, "state": "FL", "_id": "33145"} -{"city": "CORAL GABLES", "loc": [-80.274649, 25.720089], "pop": 13791, "state": "FL", "_id": "33146"} -{"city": "MIAMI", "loc": [-80.236558, 25.850675], "pop": 49395, "state": "FL", "_id": "33147"} -{"city": "KEY BISCAYNE", "loc": [-80.162475, 25.692104], "pop": 8854, "state": "FL", "_id": "33149"} -{"city": "MIAMI", "loc": [-80.206968, 25.851214], "pop": 28408, "state": "FL", "_id": "33150"} -{"city": "BAL HARBOUR", "loc": [-80.127055, 25.879094], "pop": 17312, "state": "FL", "_id": "33154"} -{"city": "MIAMI", "loc": [-80.31032, 25.7392], "pop": 42864, "state": "FL", "_id": "33155"} -{"city": "KENDALL", "loc": [-80.308535, 25.66767], "pop": 27901, "state": "FL", "_id": "33156"} -{"city": "PERRINE", "loc": [-80.352473, 25.604384], "pop": 57749, "state": "FL", "_id": "33157"} -{"city": "MIAMI", "loc": [-80.318703, 25.636433], "pop": 6037, "state": "FL", "_id": "33158"} -{"city": "NORTH MIAMI BEAC", "loc": [-80.135141, 25.936086], "pop": 26987, "state": "FL", "_id": "33160"} -{"city": "NORTH MIAMI", "loc": [-80.182034, 25.893806], "pop": 44800, "state": "FL", "_id": "33161"} -{"city": "NORTH MIAMI BEAC", "loc": [-80.177238, 25.92807], "pop": 37052, "state": "FL", "_id": "33162"} -{"city": "OLYMPIA HEIGHTS", "loc": [-80.359084, 25.735353], "pop": 56064, "state": "FL", "_id": "33165"} -{"city": "MIAMI SPRINGS", "loc": [-80.29902, 25.817473], "pop": 21066, "state": "FL", "_id": "33166"} -{"city": "MIAMI", "loc": [-80.229168, 25.885605], "pop": 18840, "state": "FL", "_id": "33167"} -{"city": "MIAMI", "loc": [-80.210106, 25.890232], "pop": 21629, "state": "FL", "_id": "33168"} -{"city": "MIAMI", "loc": [-80.21436, 25.944083], "pop": 30294, "state": "FL", "_id": "33169"} -{"city": "QUAIL HEIGHTS", "loc": [-80.3981, 25.558847], "pop": 6842, "state": "FL", "_id": "33170"} -{"city": "MIAMI", "loc": [-80.357232, 25.773523], "pop": 29823, "state": "FL", "_id": "33172"} -{"city": "MIAMI", "loc": [-80.361824, 25.699242], "pop": 33787, "state": "FL", "_id": "33173"} -{"city": "MIAMI", "loc": [-80.361128, 25.762779], "pop": 27442, "state": "FL", "_id": "33174"} -{"city": "OLYMPIA HEIGHTS", "loc": [-80.408226, 25.733677], "pop": 41712, "state": "FL", "_id": "33175"} -{"city": "MIAMI", "loc": [-80.362667, 25.657449], "pop": 47435, "state": "FL", "_id": "33176"} -{"city": "QUAIL HEIGHTS", "loc": [-80.39377, 25.593255], "pop": 25043, "state": "FL", "_id": "33177"} -{"city": "MIAMI", "loc": [-80.354925, 25.814079], "pop": 3146, "state": "FL", "_id": "33178"} -{"city": "MIAMI", "loc": [-80.181382, 25.957095], "pop": 31877, "state": "FL", "_id": "33179"} -{"city": "OJUS", "loc": [-80.139447, 25.961902], "pop": 14167, "state": "FL", "_id": "33180"} -{"city": "NORTH MIAMI BEAC", "loc": [-80.160329, 25.896548], "pop": 14089, "state": "FL", "_id": "33181"} -{"city": "MIAMI", "loc": [-80.416643, 25.787678], "pop": 4983, "state": "FL", "_id": "33182"} -{"city": "MIAMI", "loc": [-80.412969, 25.699977], "pop": 32077, "state": "FL", "_id": "33183"} -{"city": "MIAMI", "loc": [-80.402997, 25.757382], "pop": 19617, "state": "FL", "_id": "33184"} -{"city": "OLYMPIA HEIGHTS", "loc": [-80.437366, 25.718082], "pop": 3606, "state": "FL", "_id": "33185"} -{"city": "MIAMI", "loc": [-80.408501, 25.669437], "pop": 43611, "state": "FL", "_id": "33186"} -{"city": "QUAIL HEIGHTS", "loc": [-80.47137, 25.597112], "pop": 6882, "state": "FL", "_id": "33187"} -{"city": "QUAIL HEIGHTS", "loc": [-80.350851, 25.57431], "pop": 15680, "state": "FL", "_id": "33189"} -{"city": "QUAIL HEIGHTS", "loc": [-80.35381, 25.560935], "pop": 2807, "state": "FL", "_id": "33190"} -{"city": "MIAMI", "loc": [-80.440087, 25.696365], "pop": 17432, "state": "FL", "_id": "33193"} -{"city": "MIAMI", "loc": [-80.441031, 25.661502], "pop": 14612, "state": "FL", "_id": "33196"} -{"city": "FORT LAUDERDALE", "loc": [-80.128778, 26.121561], "pop": 12040, "state": "FL", "_id": "33301"} -{"city": "OAKLAND PARK", "loc": [-80.125283, 26.137908], "pop": 18976, "state": "FL", "_id": "33304"} -{"city": "OAKLAND PARK", "loc": [-80.127768, 26.153115], "pop": 11018, "state": "FL", "_id": "33305"} -{"city": "OAKLAND PARK", "loc": [-80.112572, 26.165091], "pop": 3424, "state": "FL", "_id": "33306"} -{"city": "OAKLAND PARK", "loc": [-80.107674, 26.187883], "pop": 28624, "state": "FL", "_id": "33308"} -{"city": "FORT LAUDERDALE", "loc": [-80.174624, 26.181698], "pop": 28226, "state": "FL", "_id": "33309"} -{"city": "FORT LAUDERDALE", "loc": [-80.172786, 26.142104], "pop": 65378, "state": "FL", "_id": "33311"} -{"city": "FORT LAUDERDALE", "loc": [-80.181038, 26.096819], "pop": 44230, "state": "FL", "_id": "33312"} -{"city": "CITY OF SUNRISE", "loc": [-80.223142, 26.151145], "pop": 46804, "state": "FL", "_id": "33313"} -{"city": "DAVIE", "loc": [-80.225034, 26.068199], "pop": 19621, "state": "FL", "_id": "33314"} -{"city": "FORT LAUDERDALE", "loc": [-80.15408, 26.098885], "pop": 12849, "state": "FL", "_id": "33315"} -{"city": "FORT LAUDERDALE", "loc": [-80.125951, 26.104193], "pop": 11206, "state": "FL", "_id": "33316"} -{"city": "PLANTATION", "loc": [-80.224272, 26.113536], "pop": 31518, "state": "FL", "_id": "33317"} -{"city": "TAMARAC", "loc": [-80.225413, 26.181153], "pop": 36178, "state": "FL", "_id": "33319"} -{"city": "TAMARAC", "loc": [-80.264356, 26.212072], "pop": 29504, "state": "FL", "_id": "33321"} -{"city": "SUNRISE", "loc": [-80.271954, 26.151923], "pop": 37348, "state": "FL", "_id": "33322"} -{"city": "SUNRISE", "loc": [-80.307583, 26.164641], "pop": 10658, "state": "FL", "_id": "33323"} -{"city": "PLANTATION", "loc": [-80.271019, 26.113639], "pop": 29427, "state": "FL", "_id": "33324"} -{"city": "DAVIE", "loc": [-80.321952, 26.10862], "pop": 19539, "state": "FL", "_id": "33325"} -{"city": "DAVIE", "loc": [-80.369941, 26.114338], "pop": 8393, "state": "FL", "_id": "33326"} -{"city": "FORT LAUDERDALE", "loc": [-80.40645, 26.097291], "pop": 4605, "state": "FL", "_id": "33327"} -{"city": "DAVIE", "loc": [-80.272022, 26.060708], "pop": 17233, "state": "FL", "_id": "33328"} -{"city": "DAVIE", "loc": [-80.312907, 26.055479], "pop": 9371, "state": "FL", "_id": "33330"} -{"city": "DAVIE", "loc": [-80.364533, 26.044366], "pop": 6928, "state": "FL", "_id": "33331"} -{"city": "DAVIE", "loc": [-80.41299, 26.054436], "pop": 1511, "state": "FL", "_id": "33332"} -{"city": "OAKLAND PARK", "loc": [-80.135511, 26.181514], "pop": 29072, "state": "FL", "_id": "33334"} -{"city": "TAMARAC", "loc": [-80.273376, 26.177148], "pop": 26228, "state": "FL", "_id": "33351"} -{"city": "FORT LAUDERDALE", "loc": [-80.250587, 26.117586], "pop": 435, "state": "FL", "_id": "33388"} -{"city": "WEST PALM BEACH", "loc": [-80.065874, 26.713956], "pop": 19833, "state": "FL", "_id": "33401"} -{"city": "LAKE PARK", "loc": [-80.073078, 26.803187], "pop": 8743, "state": "FL", "_id": "33403"} -{"city": "RIVIERA BEACH", "loc": [-80.06852, 26.781343], "pop": 27997, "state": "FL", "_id": "33404"} -{"city": "WEST PALM BEACH", "loc": [-80.058234, 26.669968], "pop": 18164, "state": "FL", "_id": "33405"} -{"city": "GLEN RIDGE", "loc": [-80.093026, 26.655582], "pop": 23595, "state": "FL", "_id": "33406"} -{"city": "WEST PALM BEACH", "loc": [-80.072492, 26.749154], "pop": 25017, "state": "FL", "_id": "33407"} -{"city": "NORTH PALM BEACH", "loc": [-80.060334, 26.828854], "pop": 17968, "state": "FL", "_id": "33408"} -{"city": "HAVERHILL", "loc": [-80.096347, 26.713218], "pop": 16142, "state": "FL", "_id": "33409"} -{"city": "PALM BEACH GARDE", "loc": [-80.087304, 26.844373], "pop": 23249, "state": "FL", "_id": "33410"} -{"city": "ROYAL PALM BEACH", "loc": [-80.209898, 26.700539], "pop": 21027, "state": "FL", "_id": "33411"} -{"city": "WEST PALM BEACH", "loc": [-80.248203, 26.805526], "pop": 312, "state": "FL", "_id": "33412"} -{"city": "WEST PALM BEACH", "loc": [-80.140474, 26.67616], "pop": 4864, "state": "FL", "_id": "33413"} -{"city": "WEST PALM BEACH", "loc": [-80.25299, 26.662707], "pop": 22046, "state": "FL", "_id": "33414"} -{"city": "HAVERHILL", "loc": [-80.127966, 26.655722], "pop": 35663, "state": "FL", "_id": "33415"} -{"city": "HAVERHILL", "loc": [-80.124764, 26.713006], "pop": 25892, "state": "FL", "_id": "33417"} -{"city": "PALM BEACH GARDE", "loc": [-80.132533, 26.838977], "pop": 15974, "state": "FL", "_id": "33418"} -{"city": "BOYNTON BEACH", "loc": [-80.083427, 26.51747], "pop": 9390, "state": "FL", "_id": "33426"} -{"city": "BOCA RATON", "loc": [-80.210942, 26.344605], "pop": 24103, "state": "FL", "_id": "33428"} -{"city": "BELLE GLADE", "loc": [-80.672392, 26.684289], "pop": 22652, "state": "FL", "_id": "33430"} -{"city": "BOCA RATON", "loc": [-80.097488, 26.379929], "pop": 13075, "state": "FL", "_id": "33431"} -{"city": "BOCA RATON", "loc": [-80.084421, 26.34619], "pop": 17141, "state": "FL", "_id": "33432"} -{"city": "BOCA RATON", "loc": [-80.156399, 26.346409], "pop": 35495, "state": "FL", "_id": "33433"} -{"city": "BOCA RATON", "loc": [-80.174858, 26.383909], "pop": 19075, "state": "FL", "_id": "33434"} -{"city": "BRINY BREEZES", "loc": [-80.06424, 26.529161], "pop": 28536, "state": "FL", "_id": "33435"} -{"city": "VILLAGE OF GOLF", "loc": [-80.106423, 26.526862], "pop": 19263, "state": "FL", "_id": "33436"} -{"city": "BOYNTON BEACH", "loc": [-80.141812, 26.531187], "pop": 14809, "state": "FL", "_id": "33437"} -{"city": "CANAL POINT", "loc": [-80.629931, 26.859279], "pop": 1494, "state": "FL", "_id": "33438"} -{"city": "CLEWISTON", "loc": [-80.949249, 26.717171], "pop": 14427, "state": "FL", "_id": "33440"} -{"city": "DEERFIELD BEACH", "loc": [-80.099173, 26.309556], "pop": 24529, "state": "FL", "_id": "33441"} -{"city": "DEERFIELD BEACH", "loc": [-80.141242, 26.312365], "pop": 21532, "state": "FL", "_id": "33442"} -{"city": "DELRAY BEACH", "loc": [-80.079321, 26.456445], "pop": 18450, "state": "FL", "_id": "33444"} -{"city": "DELRAY BEACH", "loc": [-80.105397, 26.456359], "pop": 20740, "state": "FL", "_id": "33445"} -{"city": "DELRAY BEACH", "loc": [-80.158016, 26.451717], "pop": 13016, "state": "FL", "_id": "33446"} -{"city": "HOBE SOUND", "loc": [-80.150851, 27.081306], "pop": 15209, "state": "FL", "_id": "33455"} -{"city": "JUPITER", "loc": [-80.120091, 26.933938], "pop": 23869, "state": "FL", "_id": "33458"} -{"city": "LAKE WORTH", "loc": [-80.055996, 26.618207], "pop": 28653, "state": "FL", "_id": "33460"} -{"city": "LAKE WORTH", "loc": [-80.094573, 26.62316], "pop": 30905, "state": "FL", "_id": "33461"} -{"city": "LANTANA", "loc": [-80.077264, 26.576766], "pop": 30704, "state": "FL", "_id": "33462"} -{"city": "GREENACRES", "loc": [-80.130503, 26.609609], "pop": 28841, "state": "FL", "_id": "33463"} -{"city": "LAKE WORTH", "loc": [-80.168299, 26.610366], "pop": 21547, "state": "FL", "_id": "33467"} -{"city": "TEQUESTA", "loc": [-80.100161, 26.966066], "pop": 11781, "state": "FL", "_id": "33469"} -{"city": "LOXAHATCHEE", "loc": [-80.276007, 26.738295], "pop": 14094, "state": "FL", "_id": "33470"} -{"city": "MOORE HAVEN", "loc": [-81.218776, 26.832749], "pop": 4724, "state": "FL", "_id": "33471"} -{"city": "PAHOKEE", "loc": [-80.662897, 26.814199], "pop": 8354, "state": "FL", "_id": "33476"} -{"city": "JUPITER", "loc": [-80.077034, 26.921701], "pop": 7748, "state": "FL", "_id": "33477"} -{"city": "JUPITER", "loc": [-80.214388, 26.921242], "pop": 10534, "state": "FL", "_id": "33478"} -{"city": "PALM BEACH", "loc": [-80.038825, 26.72065], "pop": 6588, "state": "FL", "_id": "33480"} -{"city": "DELRAY BEACH", "loc": [-80.065637, 26.45457], "pop": 10326, "state": "FL", "_id": "33483"} -{"city": "DELRAY BEACH", "loc": [-80.13459, 26.454272], "pop": 19141, "state": "FL", "_id": "33484"} -{"city": "BOCA RATON", "loc": [-80.110418, 26.348099], "pop": 19601, "state": "FL", "_id": "33486"} -{"city": "HIGHLAND BEACH", "loc": [-80.089072, 26.409142], "pop": 14606, "state": "FL", "_id": "33487"} -{"city": "SOUTH BAY", "loc": [-80.731214, 26.670126], "pop": 3723, "state": "FL", "_id": "33493"} -{"city": "BOCA RATON", "loc": [-80.181287, 26.402975], "pop": 7116, "state": "FL", "_id": "33496"} -{"city": "BOCA RATON", "loc": [-80.216087, 26.390693], "pop": 5871, "state": "FL", "_id": "33498"} -{"city": "BRANDON", "loc": [-82.296554, 27.955112], "pop": 20184, "state": "FL", "_id": "33510"} -{"city": "BRANDON", "loc": [-82.288116, 27.905649], "pop": 29861, "state": "FL", "_id": "33511"} -{"city": "BUSHNELL", "loc": [-82.155297, 28.661062], "pop": 8728, "state": "FL", "_id": "33513"} -{"city": "CENTER HILL", "loc": [-81.996289, 28.663484], "pop": 1202, "state": "FL", "_id": "33514"} -{"city": "RIDGE MANOR", "loc": [-82.207936, 28.386912], "pop": 29328, "state": "FL", "_id": "33525"} -{"city": "DOVER", "loc": [-82.213845, 27.991975], "pop": 13171, "state": "FL", "_id": "33527"} -{"city": "GIBSONTON", "loc": [-82.369831, 27.841059], "pop": 7010, "state": "FL", "_id": "33534"} -{"city": "LAKE PANASOFFKEE", "loc": [-82.136279, 28.795261], "pop": 3617, "state": "FL", "_id": "33538"} -{"city": "ZEPHYRHILLS", "loc": [-82.168347, 28.24096], "pop": 15608, "state": "FL", "_id": "33540"} -{"city": "ZEPHYRHILLS", "loc": [-82.20566, 28.231063], "pop": 17575, "state": "FL", "_id": "33541"} -{"city": "WESLEY CHAPEL", "loc": [-82.288956, 28.210365], "pop": 4073, "state": "FL", "_id": "33543"} -{"city": "ZEPHYRHILLS", "loc": [-82.34933, 28.263664], "pop": 5989, "state": "FL", "_id": "33544"} -{"city": "LITHIA", "loc": [-82.135679, 27.829349], "pop": 6780, "state": "FL", "_id": "33547"} -{"city": "LUTZ", "loc": [-82.461045, 28.136688], "pop": 30905, "state": "FL", "_id": "33549"} -{"city": "ODESSA", "loc": [-82.590506, 28.142072], "pop": 7046, "state": "FL", "_id": "33556"} -{"city": "PLANT CITY", "loc": [-82.157554, 28.069855], "pop": 13299, "state": "FL", "_id": "33565"} -{"city": "PLANT CITY", "loc": [-82.113816, 28.009448], "pop": 19533, "state": "FL", "_id": "33566"} -{"city": "PLANT CITY", "loc": [-82.146268, 27.976167], "pop": 18937, "state": "FL", "_id": "33567"} -{"city": "RIVERVIEW", "loc": [-82.312473, 27.844952], "pop": 21930, "state": "FL", "_id": "33569"} -{"city": "RUSKIN", "loc": [-82.435501, 27.701501], "pop": 14654, "state": "FL", "_id": "33570"} -{"city": "APOLLO BEACH", "loc": [-82.410199, 27.771553], "pop": 6074, "state": "FL", "_id": "33572"} -{"city": "SUN CITY CENTER", "loc": [-82.353832, 27.714711], "pop": 9070, "state": "FL", "_id": "33573"} -{"city": "SAN ANTONIO", "loc": [-82.288237, 28.337139], "pop": 1396, "state": "FL", "_id": "33576"} -{"city": "SEFFNER", "loc": [-82.286296, 27.992199], "pop": 20956, "state": "FL", "_id": "33584"} -{"city": "THONOTOSASSA", "loc": [-82.308212, 28.061747], "pop": 9009, "state": "FL", "_id": "33592"} -{"city": "VALRICO", "loc": [-82.246557, 27.912435], "pop": 27594, "state": "FL", "_id": "33594"} -{"city": "RIDGE MANOR ESTA", "loc": [-82.092975, 28.577492], "pop": 5880, "state": "FL", "_id": "33597"} -{"city": "WIMAUMA", "loc": [-82.315136, 27.701497], "pop": 7538, "state": "FL", "_id": "33598"} -{"city": "TAMPA", "loc": [-82.45972, 27.961381], "pop": 8473, "state": "FL", "_id": "33602"} -{"city": "TAMPA", "loc": [-82.462997, 27.984534], "pop": 19614, "state": "FL", "_id": "33603"} -{"city": "TAMPA", "loc": [-82.457848, 28.017312], "pop": 34243, "state": "FL", "_id": "33604"} -{"city": "TAMPA", "loc": [-82.433368, 27.967078], "pop": 19813, "state": "FL", "_id": "33605"} -{"city": "TAMPA", "loc": [-82.467035, 27.933828], "pop": 14191, "state": "FL", "_id": "33606"} -{"city": "TAMPA", "loc": [-82.489535, 27.962538], "pop": 22386, "state": "FL", "_id": "33607"} -{"city": "TAMPA", "loc": [-82.507097, 27.865916], "pop": 3578, "state": "FL", "_id": "33608"} -{"city": "TAMPA", "loc": [-82.50572, 27.942456], "pop": 15797, "state": "FL", "_id": "33609"} -{"city": "TAMPA", "loc": [-82.404584, 27.995125], "pop": 34244, "state": "FL", "_id": "33610"} -{"city": "TAMPA", "loc": [-82.506714, 27.891422], "pop": 30070, "state": "FL", "_id": "33611"} -{"city": "TAMPA", "loc": [-82.450018, 28.050187], "pop": 36784, "state": "FL", "_id": "33612"} -{"city": "TAMPA", "loc": [-82.445519, 28.077184], "pop": 24849, "state": "FL", "_id": "33613"} -{"city": "TAMPA", "loc": [-82.503393, 28.00914], "pop": 39021, "state": "FL", "_id": "33614"} -{"city": "TAMPA", "loc": [-82.580495, 28.008057], "pop": 36532, "state": "FL", "_id": "33615"} -{"city": "TAMPA", "loc": [-82.52029, 27.87418], "pop": 11318, "state": "FL", "_id": "33616"} -{"city": "TAMPA", "loc": [-82.394876, 28.038358], "pop": 38114, "state": "FL", "_id": "33617"} -{"city": "CARROLLWOOD", "loc": [-82.493291, 28.075875], "pop": 20229, "state": "FL", "_id": "33618"} -{"city": "TAMPA", "loc": [-82.375558, 27.93824], "pop": 27185, "state": "FL", "_id": "33619"} -{"city": "TAMPA", "loc": [-82.409188, 28.069465], "pop": 3757, "state": "FL", "_id": "33620"} -{"city": "CARROLLWOOD", "loc": [-82.524944, 28.077194], "pop": 39616, "state": "FL", "_id": "33624"} -{"city": "TAMPA", "loc": [-82.558987, 28.072551], "pop": 14778, "state": "FL", "_id": "33625"} -{"city": "TAMPA", "loc": [-82.616378, 28.050932], "pop": 2213, "state": "FL", "_id": "33626"} -{"city": "TAMPA", "loc": [-82.507897, 27.92102], "pop": 21545, "state": "FL", "_id": "33629"} -{"city": "TAMPA", "loc": [-82.556006, 28.006783], "pop": 18712, "state": "FL", "_id": "33634"} -{"city": "TAMPA", "loc": [-82.604822, 28.03013], "pop": 6241, "state": "FL", "_id": "33635"} -{"city": "TAMPA", "loc": [-82.365876, 28.03377], "pop": 9673, "state": "FL", "_id": "33637"} -{"city": "TAMPA", "loc": [-82.367751, 28.114698], "pop": 5866, "state": "FL", "_id": "33647"} -{"city": "SAINT PETERSBURG", "loc": [-82.638609, 27.772318], "pop": 15737, "state": "FL", "_id": "33701"} -{"city": "SAINT PETERSBURG", "loc": [-82.644795, 27.842712], "pop": 28888, "state": "FL", "_id": "33702"} -{"city": "SAINT PETERSBURG", "loc": [-82.626393, 27.816957], "pop": 23348, "state": "FL", "_id": "33703"} -{"city": "SAINT PETERSBURG", "loc": [-82.637289, 27.795435], "pop": 17112, "state": "FL", "_id": "33704"} -{"city": "SAINT PETERSBURG", "loc": [-82.64349, 27.739113], "pop": 28261, "state": "FL", "_id": "33705"} -{"city": "SAINT PETERSBURG", "loc": [-82.751646, 27.745606], "pop": 18974, "state": "FL", "_id": "33706"} -{"city": "SAINT PETERSBURG", "loc": [-82.720791, 27.75487], "pop": 23630, "state": "FL", "_id": "33707"} -{"city": "MADEIRA BEACH", "loc": [-82.800779, 27.816529], "pop": 18018, "state": "FL", "_id": "33708"} -{"city": "KENNETH CITY", "loc": [-82.729845, 27.817427], "pop": 26024, "state": "FL", "_id": "33709"} -{"city": "SAINT PETERSBURG", "loc": [-82.724285, 27.789798], "pop": 32402, "state": "FL", "_id": "33710"} -{"city": "SAINT PETERSBURG", "loc": [-82.689708, 27.74649], "pop": 18084, "state": "FL", "_id": "33711"} -{"city": "SAINT PETERSBURG", "loc": [-82.666298, 27.735336], "pop": 27715, "state": "FL", "_id": "33712"} -{"city": "SAINT PETERSBURG", "loc": [-82.677939, 27.789015], "pop": 29160, "state": "FL", "_id": "33713"} -{"city": "SAINT PETERSBURG", "loc": [-82.677612, 27.817621], "pop": 18227, "state": "FL", "_id": "33714"} -{"city": "TIERRA VERDE", "loc": [-82.715646, 27.694792], "pop": 3877, "state": "FL", "_id": "33715"} -{"city": "SAINT PETERSBURG", "loc": [-82.640039, 27.873764], "pop": 9328, "state": "FL", "_id": "33716"} -{"city": "LAKELAND", "loc": [-81.939153, 28.038134], "pop": 45005, "state": "FL", "_id": "33801"} -{"city": "LAKELAND", "loc": [-81.952283, 28.014045], "pop": 23761, "state": "FL", "_id": "33803"} -{"city": "LAKELAND", "loc": [-81.96091, 28.072006], "pop": 19676, "state": "FL", "_id": "33805"} -{"city": "LAKELAND", "loc": [-81.984219, 28.123356], "pop": 39958, "state": "FL", "_id": "33809"} -{"city": "SOUTHSIDE", "loc": [-82.007236, 27.966284], "pop": 11456, "state": "FL", "_id": "33811"} -{"city": "SOUTHSIDE", "loc": [-81.933187, 27.969534], "pop": 28497, "state": "FL", "_id": "33813"} -{"city": "ARCADIA", "loc": [-81.865755, 27.18996], "pop": 23865, "state": "FL", "_id": "33821"} -{"city": "AUBURNDALE", "loc": [-81.812234, 28.072443], "pop": 24489, "state": "FL", "_id": "33823"} -{"city": "AVON PARK", "loc": [-81.501486, 27.600085], "pop": 16945, "state": "FL", "_id": "33825"} -{"city": "BABSON PARK", "loc": [-81.534221, 27.831698], "pop": 1901, "state": "FL", "_id": "33827"} -{"city": "BARTOW", "loc": [-81.812684, 27.895664], "pop": 25968, "state": "FL", "_id": "33830"} -{"city": "DUETTE", "loc": [-81.845058, 27.627738], "pop": 3700, "state": "FL", "_id": "33834"} -{"city": "DAVENPORT", "loc": [-81.607912, 28.196265], "pop": 8268, "state": "FL", "_id": "33837"} -{"city": "DUNDEE", "loc": [-81.621207, 28.019412], "pop": 2335, "state": "FL", "_id": "33838"} -{"city": "EAGLE LAKE", "loc": [-81.756357, 27.978661], "pop": 1456, "state": "FL", "_id": "33839"} -{"city": "FORT MEADE", "loc": [-81.782346, 27.746356], "pop": 8169, "state": "FL", "_id": "33841"} -{"city": "FROSTPROOF", "loc": [-81.514778, 27.721058], "pop": 8747, "state": "FL", "_id": "33843"} -{"city": "GRENELEFE", "loc": [-81.614712, 28.095073], "pop": 23835, "state": "FL", "_id": "33844"} -{"city": "KATHLEEN", "loc": [-82.043499, 28.205224], "pop": 1096, "state": "FL", "_id": "33849"} -{"city": "LAKE ALFRED", "loc": [-81.727138, 28.089483], "pop": 3916, "state": "FL", "_id": "33850"} -{"city": "LAKE PLACID", "loc": [-81.364918, 27.294474], "pop": 13767, "state": "FL", "_id": "33852"} -{"city": "LAKE WALES", "loc": [-81.548805, 27.903734], "pop": 32570, "state": "FL", "_id": "33853"} -{"city": "LORIDA", "loc": [-81.196533, 27.414952], "pop": 1186, "state": "FL", "_id": "33857"} -{"city": "MULBERRY", "loc": [-82.001489, 27.90202], "pop": 13338, "state": "FL", "_id": "33860"} -{"city": "ONA", "loc": [-81.92805, 27.412657], "pop": 885, "state": "FL", "_id": "33865"} -{"city": "POLK CITY", "loc": [-81.808282, 28.19867], "pop": 7604, "state": "FL", "_id": "33868"} -{"city": "SEBRING", "loc": [-81.435712, 27.492391], "pop": 19922, "state": "FL", "_id": "33870"} -{"city": "SEBRING", "loc": [-81.487242, 27.470289], "pop": 15390, "state": "FL", "_id": "33872"} -{"city": "WAUCHULA", "loc": [-81.807388, 27.551742], "pop": 11513, "state": "FL", "_id": "33873"} -{"city": "ELOISE", "loc": [-81.751507, 27.999296], "pop": 30803, "state": "FL", "_id": "33880"} -{"city": "WINTER HAVEN", "loc": [-81.732485, 28.045219], "pop": 25957, "state": "FL", "_id": "33881"} -{"city": "CYPRESS GARDENS", "loc": [-81.678905, 27.994901], "pop": 14771, "state": "FL", "_id": "33884"} -{"city": "ZOLFO SPRINGS", "loc": [-81.742328, 27.480042], "pop": 3515, "state": "FL", "_id": "33890"} -{"city": "FORT MYERS", "loc": [-81.8725, 26.620403], "pop": 22150, "state": "FL", "_id": "33901"} -{"city": "FORT MYERS", "loc": [-81.909632, 26.678138], "pop": 20015, "state": "FL", "_id": "33903"} -{"city": "CAPE CORAL CENTR", "loc": [-81.952243, 26.57746], "pop": 29483, "state": "FL", "_id": "33904"} -{"city": "TICE", "loc": [-81.785341, 26.676472], "pop": 25029, "state": "FL", "_id": "33905"} -{"city": "FORT MYERS", "loc": [-81.873558, 26.568057], "pop": 19015, "state": "FL", "_id": "33907"} -{"city": "FORT MYERS", "loc": [-81.927589, 26.502518], "pop": 17050, "state": "FL", "_id": "33908"} -{"city": "CAPE CORAL CENTR", "loc": [-81.958909, 26.680276], "pop": 8622, "state": "FL", "_id": "33909"} -{"city": "FORT MYERS", "loc": [-81.824554, 26.49722], "pop": 20141, "state": "FL", "_id": "33912"} -{"city": "FORT MYERS", "loc": [-81.706469, 26.522808], "pop": 473, "state": "FL", "_id": "33913"} -{"city": "CAPE CORAL CENTR", "loc": [-81.990915, 26.56971], "pop": 15782, "state": "FL", "_id": "33914"} -{"city": "FORT MYERS", "loc": [-81.842946, 26.646595], "pop": 17673, "state": "FL", "_id": "33916"} -{"city": "FORT MYERS", "loc": [-81.859447, 26.707947], "pop": 24751, "state": "FL", "_id": "33917"} -{"city": "COLLEGE PARKWAY", "loc": [-81.900587, 26.554159], "pop": 22641, "state": "FL", "_id": "33919"} -{"city": "ALVA", "loc": [-81.635055, 26.714657], "pop": 3044, "state": "FL", "_id": "33920"} -{"city": "BOKEELIA", "loc": [-82.140064, 26.662726], "pop": 2979, "state": "FL", "_id": "33922"} -{"city": "BONITA SPRINGS", "loc": [-81.789963, 26.348035], "pop": 19697, "state": "FL", "_id": "33923"} -{"city": "CAPTIVA", "loc": [-82.261017, 26.750541], "pop": 831, "state": "FL", "_id": "33924"} -{"city": "EL JOBEAN", "loc": [-82.19957, 26.97608], "pop": 91, "state": "FL", "_id": "33927"} -{"city": "ESTERO", "loc": [-81.810244, 26.435052], "pop": 1846, "state": "FL", "_id": "33928"} -{"city": "FORT MYERS BEACH", "loc": [-81.924543, 26.451952], "pop": 10612, "state": "FL", "_id": "33931"} -{"city": "IMMOKALEE", "loc": [-81.445365, 26.409794], "pop": 18066, "state": "FL", "_id": "33934"} -{"city": "LABELLE", "loc": [-81.434027, 26.732093], "pop": 11346, "state": "FL", "_id": "33935"} -{"city": "LEHIGH ACRES", "loc": [-81.61046, 26.615302], "pop": 10851, "state": "FL", "_id": "33936"} -{"city": "MARCO ISLAND", "loc": [-81.720394, 25.939568], "pop": 9495, "state": "FL", "_id": "33937"} -{"city": "NAPLES", "loc": [-81.802196, 26.171391], "pop": 20934, "state": "FL", "_id": "33940"} -{"city": "NAPLES", "loc": [-81.766125, 26.201578], "pop": 23719, "state": "FL", "_id": "33942"} -{"city": "OCHOPEE", "loc": [-81.311228, 25.87998], "pop": 1257, "state": "FL", "_id": "33943"} -{"city": "PLACIDA", "loc": [-82.261638, 26.819301], "pop": 126, "state": "FL", "_id": "33946"} -{"city": "PLACIDA", "loc": [-82.293778, 26.90039], "pop": 7811, "state": "FL", "_id": "33947"} -{"city": "PORT CHARLOTTE", "loc": [-82.141173, 26.98268], "pop": 12212, "state": "FL", "_id": "33948"} -{"city": "PUNTA GORDA", "loc": [-82.053166, 26.915163], "pop": 15495, "state": "FL", "_id": "33950"} -{"city": "PORT CHARLOTTE", "loc": [-82.096372, 26.990475], "pop": 27923, "state": "FL", "_id": "33952"} -{"city": "PORT CHARLOTTE", "loc": [-82.211743, 27.004008], "pop": 1982, "state": "FL", "_id": "33953"} -{"city": "PORT CHARLOTTE", "loc": [-82.110782, 27.022815], "pop": 3993, "state": "FL", "_id": "33954"} -{"city": "PUNTA GORDA", "loc": [-81.954712, 26.823981], "pop": 5206, "state": "FL", "_id": "33955"} -{"city": "SAINT JAMES CITY", "loc": [-82.091591, 26.529012], "pop": 3653, "state": "FL", "_id": "33956"} -{"city": "SANIBEL", "loc": [-82.086825, 26.4514], "pop": 5999, "state": "FL", "_id": "33957"} -{"city": "VENUS", "loc": [-81.359412, 27.13463], "pop": 925, "state": "FL", "_id": "33960"} -{"city": "NAPLES", "loc": [-81.658635, 26.027721], "pop": 7121, "state": "FL", "_id": "33961"} -{"city": "NAPLES", "loc": [-81.749661, 26.113096], "pop": 28714, "state": "FL", "_id": "33962"} -{"city": "NAPLES", "loc": [-81.808092, 26.263499], "pop": 14863, "state": "FL", "_id": "33963"} -{"city": "NAPLES", "loc": [-81.640436, 26.211253], "pop": 5869, "state": "FL", "_id": "33964"} -{"city": "LEHIGH ACRES", "loc": [-81.665822, 26.602252], "pop": 11401, "state": "FL", "_id": "33971"} -{"city": "PORT CHARLOTTE", "loc": [-82.058886, 26.983969], "pop": 7753, "state": "FL", "_id": "33980"} -{"city": "PORT CHARLOTTE", "loc": [-82.238774, 26.937925], "pop": 5758, "state": "FL", "_id": "33981"} -{"city": "PUNTA GORDA", "loc": [-81.954484, 26.966751], "pop": 6235, "state": "FL", "_id": "33982"} -{"city": "PUNTA GORDA", "loc": [-82.016268, 27.007398], "pop": 7319, "state": "FL", "_id": "33983"} -{"city": "CAPE CORAL CENTR", "loc": [-81.945967, 26.630893], "pop": 16975, "state": "FL", "_id": "33990"} -{"city": "CAPE CORAL CENTR", "loc": [-82.006703, 26.628881], "pop": 5352, "state": "FL", "_id": "33991"} -{"city": "NAPLES", "loc": [-81.70927, 26.191612], "pop": 21226, "state": "FL", "_id": "33999"} -{"city": "BRADEN RIVER", "loc": [-82.431487, 27.46521], "pop": 6618, "state": "FL", "_id": "34202"} -{"city": "BRADEN RIVER", "loc": [-82.5404, 27.444871], "pop": 22408, "state": "FL", "_id": "34203"} -{"city": "WESTGATE", "loc": [-82.584733, 27.480896], "pop": 31114, "state": "FL", "_id": "34205"} -{"city": "COLLEGE PLAZA", "loc": [-82.580627, 27.439663], "pop": 27775, "state": "FL", "_id": "34207"} -{"city": "BRADEN RIVER", "loc": [-82.536961, 27.485881], "pop": 20668, "state": "FL", "_id": "34208"} -{"city": "PALMA SOLA", "loc": [-82.627631, 27.487909], "pop": 30012, "state": "FL", "_id": "34209"} -{"city": "BRADENTON", "loc": [-82.635752, 27.454393], "pop": 11345, "state": "FL", "_id": "34210"} -{"city": "CORTEZ", "loc": [-82.700642, 27.472686], "pop": 1657, "state": "FL", "_id": "34215"} -{"city": "BRADENTON BEACH", "loc": [-82.721027, 27.515149], "pop": 6554, "state": "FL", "_id": "34217"} -{"city": "PARRISH", "loc": [-82.396009, 27.557162], "pop": 3811, "state": "FL", "_id": "34219"} -{"city": "PALMETTO", "loc": [-82.562957, 27.542946], "pop": 23994, "state": "FL", "_id": "34221"} -{"city": "ELLENTON", "loc": [-82.5006, 27.53818], "pop": 8252, "state": "FL", "_id": "34222"} -{"city": "ENGLEWOOD", "loc": [-82.359886, 26.966743], "pop": 15705, "state": "FL", "_id": "34223"} -{"city": "GROVE CITY", "loc": [-82.311731, 26.92504], "pop": 5110, "state": "FL", "_id": "34224"} -{"city": "WHITNEY BEACH", "loc": [-82.638403, 27.38669], "pop": 5937, "state": "FL", "_id": "34228"} -{"city": "OSPREY", "loc": [-82.485339, 27.18384], "pop": 3612, "state": "FL", "_id": "34229"} -{"city": "SOUTH TRAIL", "loc": [-82.513793, 27.26757], "pop": 32813, "state": "FL", "_id": "34231"} -{"city": "FOREST LAKES", "loc": [-82.475709, 27.320056], "pop": 29847, "state": "FL", "_id": "34232"} -{"city": "SARASOTA", "loc": [-82.47698, 27.286614], "pop": 11476, "state": "FL", "_id": "34233"} -{"city": "MEADOWS VILLAGE", "loc": [-82.535182, 27.365355], "pop": 20243, "state": "FL", "_id": "34234"} -{"city": "SARASOTA", "loc": [-82.484759, 27.367162], "pop": 11275, "state": "FL", "_id": "34235"} -{"city": "SARASOTA", "loc": [-82.548624, 27.331588], "pop": 10942, "state": "FL", "_id": "34236"} -{"city": "SARASOTA", "loc": [-82.512778, 27.336915], "pop": 15902, "state": "FL", "_id": "34237"} -{"city": "SARASOTA SQUARE", "loc": [-82.482898, 27.243834], "pop": 5493, "state": "FL", "_id": "34238"} -{"city": "SARASOTA", "loc": [-82.519545, 27.311137], "pop": 15949, "state": "FL", "_id": "34239"} -{"city": "SARASOTA", "loc": [-82.385594, 27.32765], "pop": 4943, "state": "FL", "_id": "34240"} -{"city": "SARASOTA", "loc": [-82.418112, 27.282179], "pop": 8902, "state": "FL", "_id": "34241"} -{"city": "CRESCENT BEACH", "loc": [-82.546932, 27.266025], "pop": 10594, "state": "FL", "_id": "34242"} -{"city": "SARASOTA", "loc": [-82.530299, 27.407235], "pop": 14096, "state": "FL", "_id": "34243"} -{"city": "MYAKKA CITY", "loc": [-82.184897, 27.364764], "pop": 1636, "state": "FL", "_id": "34251"} -{"city": "NOKOMIS", "loc": [-82.451779, 27.138398], "pop": 13638, "state": "FL", "_id": "34275"} -{"city": "VENICE", "loc": [-82.44983, 27.093312], "pop": 9069, "state": "FL", "_id": "34285"} -{"city": "NORTH PORT", "loc": [-82.241616, 27.047839], "pop": 16491, "state": "FL", "_id": "34287"} -{"city": "MID VENICE", "loc": [-82.415112, 27.103245], "pop": 13901, "state": "FL", "_id": "34292"} -{"city": "SOUTH VENICE", "loc": [-82.404096, 27.053022], "pop": 26720, "state": "FL", "_id": "34293"} -{"city": "BROOKSVILLE", "loc": [-82.373674, 28.565805], "pop": 20190, "state": "FL", "_id": "34601"} -{"city": "RIDGE MANOR WEST", "loc": [-82.290545, 28.511167], "pop": 4940, "state": "FL", "_id": "34602"} -{"city": "SPRING HILL", "loc": [-82.598084, 28.46551], "pop": 18190, "state": "FL", "_id": "34606"} -{"city": "SPRING HILL", "loc": [-82.626671, 28.506546], "pop": 5420, "state": "FL", "_id": "34607"} -{"city": "SPRING HILL", "loc": [-82.556206, 28.479696], "pop": 16755, "state": "FL", "_id": "34608"} -{"city": "SPRING HILL", "loc": [-82.499896, 28.477611], "pop": 19824, "state": "FL", "_id": "34609"} -{"city": "SHADY HILLS", "loc": [-82.530148, 28.405084], "pop": 9958, "state": "FL", "_id": "34610"} -{"city": "BROOKSVILLE", "loc": [-82.521286, 28.546558], "pop": 9899, "state": "FL", "_id": "34613"} -{"city": "BROOKSVILLE", "loc": [-82.523613, 28.662244], "pop": 3687, "state": "FL", "_id": "34614"} -{"city": "CLEARWATER", "loc": [-82.780807, 27.986241], "pop": 30847, "state": "FL", "_id": "34615"} -{"city": "CLEARWATER", "loc": [-82.786711, 27.945624], "pop": 28460, "state": "FL", "_id": "34616"} -{"city": "CLEARWATER", "loc": [-82.717248, 27.976503], "pop": 15886, "state": "FL", "_id": "34619"} -{"city": "CLEARWATER", "loc": [-82.715885, 27.913981], "pop": 15769, "state": "FL", "_id": "34620"} -{"city": "CLEARWATER", "loc": [-82.723718, 28.02961], "pop": 16102, "state": "FL", "_id": "34621"} -{"city": "AIRPORT", "loc": [-82.676876, 27.896713], "pop": 3190, "state": "FL", "_id": "34622"} -{"city": "CLEARWATER", "loc": [-82.747405, 28.002734], "pop": 20280, "state": "FL", "_id": "34623"} -{"city": "CLEARWATER", "loc": [-82.743485, 27.93595], "pop": 27315, "state": "FL", "_id": "34624"} -{"city": "CLEARWATER", "loc": [-82.745504, 27.973063], "pop": 10394, "state": "FL", "_id": "34625"} -{"city": "CLEARWATER", "loc": [-82.822281, 27.984526], "pop": 6231, "state": "FL", "_id": "34630"} -{"city": "BELLEAIR BEACH", "loc": [-82.840486, 27.917605], "pop": 7736, "state": "FL", "_id": "34635"} -{"city": "LAND O LAKES", "loc": [-82.45472, 28.225849], "pop": 11815, "state": "FL", "_id": "34639"} -{"city": "BELLEAIR BLUFFS", "loc": [-82.801978, 27.915835], "pop": 22793, "state": "FL", "_id": "34640"} -{"city": "LARGO", "loc": [-82.75937, 27.907547], "pop": 24087, "state": "FL", "_id": "34641"} -{"city": "SEMINOLE", "loc": [-82.796896, 27.844571], "pop": 24078, "state": "FL", "_id": "34642"} -{"city": "LARGO", "loc": [-82.762806, 27.880334], "pop": 17707, "state": "FL", "_id": "34643"} -{"city": "LARGO", "loc": [-82.826287, 27.883597], "pop": 20162, "state": "FL", "_id": "34644"} -{"city": "LARGO", "loc": [-82.826978, 27.852906], "pop": 11284, "state": "FL", "_id": "34646"} -{"city": "LARGO", "loc": [-82.758701, 27.851549], "pop": 15130, "state": "FL", "_id": "34647"} -{"city": "LARGO", "loc": [-82.795946, 27.884391], "pop": 13347, "state": "FL", "_id": "34648"} -{"city": "NEW PORT RICHEY", "loc": [-82.732721, 28.232574], "pop": 22422, "state": "FL", "_id": "34652"} -{"city": "NEW PORT RICHEY", "loc": [-82.6986, 28.244398], "pop": 26729, "state": "FL", "_id": "34653"} -{"city": "NEW PORT RICHEY", "loc": [-82.626423, 28.302201], "pop": 13750, "state": "FL", "_id": "34654"} -{"city": "NEW PORT RICHEY", "loc": [-82.680729, 28.212898], "pop": 13849, "state": "FL", "_id": "34655"} -{"city": "PINELLAS PARK", "loc": [-82.71335, 27.840313], "pop": 24459, "state": "FL", "_id": "34665"} -{"city": "PINELLAS PARK", "loc": [-82.709353, 27.860742], "pop": 19840, "state": "FL", "_id": "34666"} -{"city": "HUDSON", "loc": [-82.675669, 28.364763], "pop": 26410, "state": "FL", "_id": "34667"} -{"city": "PORT RICHEY", "loc": [-82.692714, 28.301148], "pop": 39471, "state": "FL", "_id": "34668"} -{"city": "HUDSON", "loc": [-82.628793, 28.350634], "pop": 8577, "state": "FL", "_id": "34669"} -{"city": "OLDSMAR", "loc": [-82.684778, 28.046035], "pop": 12858, "state": "FL", "_id": "34677"} -{"city": "PALM HARBOR", "loc": [-82.758488, 28.066248], "pop": 42350, "state": "FL", "_id": "34683"} -{"city": "LAKE TARPON", "loc": [-82.726573, 28.073963], "pop": 21753, "state": "FL", "_id": "34684"} -{"city": "PALM HARBOR", "loc": [-82.696357, 28.096725], "pop": 2278, "state": "FL", "_id": "34685"} -{"city": "TARPON SPRINGS", "loc": [-82.743023, 28.138465], "pop": 26381, "state": "FL", "_id": "34689"} -{"city": "HOLIDAY", "loc": [-82.727935, 28.191273], "pop": 11980, "state": "FL", "_id": "34690"} -{"city": "HOLIDAY", "loc": [-82.755965, 28.191336], "pop": 16548, "state": "FL", "_id": "34691"} -{"city": "SAFETY HARBOR", "loc": [-82.696658, 28.009608], "pop": 16853, "state": "FL", "_id": "34695"} -{"city": "DUNEDIN", "loc": [-82.779434, 28.028382], "pop": 15304, "state": "FL", "_id": "34698"} -{"city": "ASTATULA", "loc": [-81.719473, 28.708754], "pop": 1831, "state": "FL", "_id": "34705"} -{"city": "CLERMONT", "loc": [-81.757407, 28.552541], "pop": 15109, "state": "FL", "_id": "34711"} -{"city": "FRUITLAND PARK", "loc": [-81.899755, 28.863949], "pop": 8513, "state": "FL", "_id": "34731"} -{"city": "GROVELAND", "loc": [-81.874526, 28.564445], "pop": 8692, "state": "FL", "_id": "34736"} -{"city": "HOWEY IN THE HIL", "loc": [-81.789983, 28.709818], "pop": 1370, "state": "FL", "_id": "34737"} -{"city": "KENANSVILLE", "loc": [-81.050049, 27.876698], "pop": 736, "state": "FL", "_id": "34739"} -{"city": "KISSIMMEE", "loc": [-81.424208, 28.305056], "pop": 23576, "state": "FL", "_id": "34741"} -{"city": "BUENA VENTURA LA", "loc": [-81.356044, 28.329656], "pop": 14287, "state": "FL", "_id": "34743"} -{"city": "KISSIMMEE", "loc": [-81.368122, 28.307807], "pop": 21101, "state": "FL", "_id": "34744"} -{"city": "KISSIMMEE", "loc": [-81.467478, 28.26796], "pop": 12922, "state": "FL", "_id": "34746"} -{"city": "LEESBURG", "loc": [-81.885772, 28.807965], "pop": 21309, "state": "FL", "_id": "34748"} -{"city": "MONTVERDE", "loc": [-81.679368, 28.597153], "pop": 2216, "state": "FL", "_id": "34756"} -{"city": "KISSIMMEE", "loc": [-81.487014, 28.198436], "pop": 6306, "state": "FL", "_id": "34758"} -{"city": "POINCIANA", "loc": [-81.458984, 28.124786], "pop": 2430, "state": "FL", "_id": "34759"} -{"city": "OCOEE", "loc": [-81.532618, 28.583685], "pop": 14171, "state": "FL", "_id": "34761"} -{"city": "OKAHUMPKA", "loc": [-81.883949, 28.737257], "pop": 1779, "state": "FL", "_id": "34762"} -{"city": "SAINT CLOUD", "loc": [-81.287626, 28.247992], "pop": 15024, "state": "FL", "_id": "34769"} -{"city": "SAINT CLOUD", "loc": [-81.200311, 28.27301], "pop": 5870, "state": "FL", "_id": "34771"} -{"city": "SAINT CLOUD", "loc": [-81.264493, 28.190518], "pop": 6041, "state": "FL", "_id": "34772"} -{"city": "SAINT CLOUD", "loc": [-81.017552, 28.129295], "pop": 1000, "state": "FL", "_id": "34773"} -{"city": "WILDWOOD", "loc": [-82.03473, 28.845353], "pop": 10604, "state": "FL", "_id": "34785"} -{"city": "WINDERMERE", "loc": [-81.535411, 28.50061], "pop": 5725, "state": "FL", "_id": "34786"} -{"city": "WINTER GARDEN", "loc": [-81.591127, 28.542321], "pop": 18939, "state": "FL", "_id": "34787"} -{"city": "HAINES CREEK", "loc": [-81.781159, 28.85744], "pop": 12883, "state": "FL", "_id": "34788"} -{"city": "YALAHA", "loc": [-81.826324, 28.744443], "pop": 1061, "state": "FL", "_id": "34797"} -{"city": "FORT PIERCE", "loc": [-80.443963, 27.438233], "pop": 3711, "state": "FL", "_id": "34945"} -{"city": "FORT PIERCE", "loc": [-80.35996, 27.50077], "pop": 10873, "state": "FL", "_id": "34946"} -{"city": "FORT PIERCE", "loc": [-80.359185, 27.449281], "pop": 10882, "state": "FL", "_id": "34947"} -{"city": "FORT PIERCE", "loc": [-80.261468, 27.389594], "pop": 8853, "state": "FL", "_id": "34949"} -{"city": "FORT PIERCE", "loc": [-80.3385, 27.448567], "pop": 19708, "state": "FL", "_id": "34950"} -{"city": "FORT PIERCE", "loc": [-80.405195, 27.539097], "pop": 6821, "state": "FL", "_id": "34951"} -{"city": "PORT SAINT LUCIE", "loc": [-80.297971, 27.288895], "pop": 23437, "state": "FL", "_id": "34952"} -{"city": "PORT SAINT LUCIE", "loc": [-80.379323, 27.262506], "pop": 11796, "state": "FL", "_id": "34953"} -{"city": "INDIANTOWN", "loc": [-80.480277, 27.061461], "pop": 7823, "state": "FL", "_id": "34956"} -{"city": "JENSEN BEACH", "loc": [-80.227656, 27.235568], "pop": 13656, "state": "FL", "_id": "34957"} -{"city": "BASINGER", "loc": [-80.847853, 27.311532], "pop": 14955, "state": "FL", "_id": "34972"} -{"city": "OKEECHOBEE", "loc": [-80.84103, 27.200224], "pop": 18122, "state": "FL", "_id": "34974"} -{"city": "FORT PIERCE", "loc": [-80.362257, 27.404882], "pop": 3243, "state": "FL", "_id": "34981"} -{"city": "FORT PIERCE", "loc": [-80.324633, 27.390764], "pop": 20061, "state": "FL", "_id": "34982"} -{"city": "PORT SAINT LUCIE", "loc": [-80.345029, 27.309444], "pop": 22031, "state": "FL", "_id": "34983"} -{"city": "PORT SAINT LUCIE", "loc": [-80.338936, 27.265476], "pop": 7091, "state": "FL", "_id": "34984"} -{"city": "PORT SAINT LUCIE", "loc": [-80.403045, 27.32148], "pop": 610, "state": "FL", "_id": "34986"} -{"city": "PORT SAINT LUCIE", "loc": [-80.477052, 27.260595], "pop": 67, "state": "FL", "_id": "34987"} -{"city": "PORT SAINT LUCIE", "loc": [-80.51726, 27.323233], "pop": 416, "state": "FL", "_id": "34988"} -{"city": "PALM CITY", "loc": [-80.291646, 27.165646], "pop": 13225, "state": "FL", "_id": "34990"} -{"city": "STUART", "loc": [-80.253786, 27.196834], "pop": 14524, "state": "FL", "_id": "34994"} -{"city": "STUART", "loc": [-80.216378, 27.192857], "pop": 7410, "state": "FL", "_id": "34996"} -{"city": "STUART", "loc": [-80.212937, 27.139817], "pop": 25374, "state": "FL", "_id": "34997"} -{"city": "AUSTELL", "loc": [-84.605026, 33.807226], "pop": 25021, "state": "GA", "_id": "30001"} -{"city": "AVONDALE ESTATES", "loc": [-84.260691, 33.771727], "pop": 4459, "state": "GA", "_id": "30002"} -{"city": "CLARKSTON", "loc": [-84.238825, 33.810107], "pop": 17325, "state": "GA", "_id": "30021"} -{"city": "CONLEY", "loc": [-84.327571, 33.645104], "pop": 6815, "state": "GA", "_id": "30027"} -{"city": "DECATUR", "loc": [-84.295044, 33.769883], "pop": 23185, "state": "GA", "_id": "30030"} -{"city": "DECATUR", "loc": [-84.263165, 33.740825], "pop": 56056, "state": "GA", "_id": "30032"} -{"city": "DECATUR", "loc": [-84.281918, 33.812305], "pop": 22071, "state": "GA", "_id": "30033"} -{"city": "DECATUR", "loc": [-84.248939, 33.695385], "pop": 32312, "state": "GA", "_id": "30034"} -{"city": "DECATUR", "loc": [-84.2143, 33.72784], "pop": 15062, "state": "GA", "_id": "30035"} -{"city": "LITHONIA", "loc": [-84.160997, 33.682311], "pop": 15583, "state": "GA", "_id": "30038"} -{"city": "ELLENWOOD", "loc": [-84.264333, 33.635376], "pop": 16742, "state": "GA", "_id": "30049"} -{"city": "FOREST PARK", "loc": [-84.367075, 33.609737], "pop": 26825, "state": "GA", "_id": "30050"} -{"city": "LITHIA SPRINGS", "loc": [-84.66081, 33.772999], "pop": 14533, "state": "GA", "_id": "30057"} -{"city": "CENTERVILLE GWIN", "loc": [-84.099291, 33.746412], "pop": 37322, "state": "GA", "_id": "30058"} -{"city": "MABLETON", "loc": [-84.565062, 33.816572], "pop": 22519, "state": "GA", "_id": "30059"} -{"city": "MARIETTA", "loc": [-84.564881, 33.909199], "pop": 52530, "state": "GA", "_id": "30060"} -{"city": "MARIETTA", "loc": [-84.463291, 34.002521], "pop": 52642, "state": "GA", "_id": "30062"} -{"city": "MARIETTA", "loc": [-84.607584, 33.934285], "pop": 30260, "state": "GA", "_id": "30064"} -{"city": "MARIETTA", "loc": [-84.503817, 34.037807], "pop": 41948, "state": "GA", "_id": "30066"} -{"city": "MARIETTA", "loc": [-84.473251, 33.928198], "pop": 40460, "state": "GA", "_id": "30067"} -{"city": "MARIETTA", "loc": [-84.438549, 33.967861], "pop": 29488, "state": "GA", "_id": "30068"} -{"city": "NORCROSS", "loc": [-84.197158, 33.938145], "pop": 16266, "state": "GA", "_id": "30071"} -{"city": "POWDER SPRINGS", "loc": [-84.685645, 33.875377], "pop": 27767, "state": "GA", "_id": "30073"} -{"city": "ROSWELL", "loc": [-84.385901, 34.040832], "pop": 32144, "state": "GA", "_id": "30075"} -{"city": "ROSWELL", "loc": [-84.310408, 34.021324], "pop": 34027, "state": "GA", "_id": "30076"} -{"city": "SCOTTDALE", "loc": [-84.258521, 33.793396], "pop": 3288, "state": "GA", "_id": "30079"} -{"city": "SMYRNA", "loc": [-84.502284, 33.879602], "pop": 36899, "state": "GA", "_id": "30080"} -{"city": "SMYRNA", "loc": [-84.538234, 33.863051], "pop": 17917, "state": "GA", "_id": "30082"} -{"city": "STONE MOUNTAIN", "loc": [-84.201754, 33.794233], "pop": 48785, "state": "GA", "_id": "30083"} -{"city": "TUCKER", "loc": [-84.21602, 33.856986], "pop": 29307, "state": "GA", "_id": "30084"} -{"city": "STONE MOUNTAIN", "loc": [-84.128829, 33.811511], "pop": 27251, "state": "GA", "_id": "30087"} -{"city": "STONE MOUNTAIN", "loc": [-84.18022, 33.75798], "pop": 24029, "state": "GA", "_id": "30088"} -{"city": "NORCROSS", "loc": [-84.243787, 33.967688], "pop": 19901, "state": "GA", "_id": "30092"} -{"city": "NORCROSS", "loc": [-84.183953, 33.905964], "pop": 32345, "state": "GA", "_id": "30093"} -{"city": "ACWORTH", "loc": [-84.647741, 34.075627], "pop": 37976, "state": "GA", "_id": "30101"} -{"city": "ADAIRSVILLE", "loc": [-84.917634, 34.359527], "pop": 7273, "state": "GA", "_id": "30103"} -{"city": "ARAGON", "loc": [-85.069559, 34.066556], "pop": 4692, "state": "GA", "_id": "30104"} -{"city": "ARMUCHEE", "loc": [-85.184298, 34.441082], "pop": 2131, "state": "GA", "_id": "30105"} -{"city": "BALL GROUND", "loc": [-84.375756, 34.339262], "pop": 4064, "state": "GA", "_id": "30107"} -{"city": "BOWDON", "loc": [-85.25329, 33.537259], "pop": 7318, "state": "GA", "_id": "30108"} -{"city": "BREMEN", "loc": [-85.128638, 33.730872], "pop": 8495, "state": "GA", "_id": "30110"} -{"city": "BUCHANAN", "loc": [-85.150551, 33.82828], "pop": 5931, "state": "GA", "_id": "30113"} -{"city": "CANTON", "loc": [-84.457756, 34.205874], "pop": 35773, "state": "GA", "_id": "30114"} -{"city": "CARROLLTON", "loc": [-85.081211, 33.579751], "pop": 40344, "state": "GA", "_id": "30117"} -{"city": "CARTERSVILLE", "loc": [-84.820401, 34.186975], "pop": 33846, "state": "GA", "_id": "30120"} -{"city": "CAVE SPRING", "loc": [-85.337906, 34.116683], "pop": 2650, "state": "GA", "_id": "30124"} -{"city": "CEDARTOWN", "loc": [-85.2459, 34.011196], "pop": 20720, "state": "GA", "_id": "30125"} -{"city": "CUMMING", "loc": [-84.146762, 34.220197], "pop": 35496, "state": "GA", "_id": "30130"} -{"city": "DALLAS", "loc": [-84.827791, 33.916315], "pop": 28118, "state": "GA", "_id": "30132"} -{"city": "DOUGLASVILLE", "loc": [-84.747719, 33.760645], "pop": 17182, "state": "GA", "_id": "30134"} -{"city": "DOUGLASVILLE", "loc": [-84.745441, 33.698936], "pop": 32887, "state": "GA", "_id": "30135"} -{"city": "DULUTH", "loc": [-84.157936, 33.98619], "pop": 30932, "state": "GA", "_id": "30136"} -{"city": "EMERSON", "loc": [-84.757045, 34.119969], "pop": 1353, "state": "GA", "_id": "30137"} -{"city": "FAIRMOUNT", "loc": [-84.766878, 34.465241], "pop": 3405, "state": "GA", "_id": "30139"} -{"city": "FELTON", "loc": [-85.220781, 33.887066], "pop": 489, "state": "GA", "_id": "30140"} -{"city": "HIRAM", "loc": [-84.769875, 33.867286], "pop": 6613, "state": "GA", "_id": "30141"} -{"city": "JASPER", "loc": [-84.475881, 34.461965], "pop": 8256, "state": "GA", "_id": "30143"} -{"city": "KENNESAW", "loc": [-84.60466, 34.028656], "pop": 37120, "state": "GA", "_id": "30144"} -{"city": "KINGSTON", "loc": [-84.997299, 34.250053], "pop": 2534, "state": "GA", "_id": "30145"} -{"city": "LINDALE", "loc": [-85.182491, 34.170741], "pop": 3502, "state": "GA", "_id": "30147"} -{"city": "MARBLE HILL", "loc": [-84.337151, 34.415353], "pop": 98, "state": "GA", "_id": "30148"} -{"city": "ROCKMART", "loc": [-85.05937, 33.997912], "pop": 8946, "state": "GA", "_id": "30153"} -{"city": "ROME", "loc": [-85.146501, 34.250726], "pop": 33058, "state": "GA", "_id": "30161"} -{"city": "ROME", "loc": [-85.223122, 34.283679], "pop": 32739, "state": "GA", "_id": "30165"} -{"city": "ROOPVILLE", "loc": [-85.167148, 33.432226], "pop": 2952, "state": "GA", "_id": "30170"} -{"city": "PINE LOG", "loc": [-84.780315, 34.392732], "pop": 2257, "state": "GA", "_id": "30171"} -{"city": "SILVER CREEK", "loc": [-85.142933, 34.159272], "pop": 5968, "state": "GA", "_id": "30173"} -{"city": "SUWANEE", "loc": [-84.082391, 34.057245], "pop": 5965, "state": "GA", "_id": "30174"} -{"city": "TALKING ROCK", "loc": [-84.491154, 34.539356], "pop": 3037, "state": "GA", "_id": "30175"} -{"city": "TALLAPOOSA", "loc": [-85.300047, 33.760174], "pop": 6172, "state": "GA", "_id": "30176"} -{"city": "TATE", "loc": [-84.365299, 34.42387], "pop": 4286, "state": "GA", "_id": "30177"} -{"city": "TAYLORSVILLE", "loc": [-84.973859, 34.122941], "pop": 2360, "state": "GA", "_id": "30178"} -{"city": "TEMPLE", "loc": [-85.013315, 33.767687], "pop": 7111, "state": "GA", "_id": "30179"} -{"city": "VILLA RICA", "loc": [-84.929697, 33.71729], "pop": 12063, "state": "GA", "_id": "30180"} -{"city": "WACO", "loc": [-85.219674, 33.683986], "pop": 2297, "state": "GA", "_id": "30182"} -{"city": "WALESKA", "loc": [-84.561981, 34.321662], "pop": 3576, "state": "GA", "_id": "30183"} -{"city": "WHITE", "loc": [-84.738327, 34.271683], "pop": 3231, "state": "GA", "_id": "30184"} -{"city": "WHITESBURG", "loc": [-84.925426, 33.511093], "pop": 3115, "state": "GA", "_id": "30185"} -{"city": "WINSTON", "loc": [-84.863915, 33.663419], "pop": 6850, "state": "GA", "_id": "30187"} -{"city": "WOODSTOCK", "loc": [-84.511683, 34.106005], "pop": 33172, "state": "GA", "_id": "30188"} -{"city": "ALPHARETTA", "loc": [-84.295167, 34.107677], "pop": 19393, "state": "GA", "_id": "30201"} -{"city": "ALPHARETTA", "loc": [-84.238972, 34.035487], "pop": 24814, "state": "GA", "_id": "30202"} -{"city": "AUBURN", "loc": [-83.824128, 34.007758], "pop": 6457, "state": "GA", "_id": "30203"} -{"city": "BARNESVILLE", "loc": [-84.15147, 33.045713], "pop": 9595, "state": "GA", "_id": "30204"} -{"city": "BROOKS", "loc": [-84.476941, 33.298433], "pop": 1662, "state": "GA", "_id": "30205"} -{"city": "CONCORD", "loc": [-84.447025, 33.099836], "pop": 1591, "state": "GA", "_id": "30206"} -{"city": "CONYERS", "loc": [-84.019818, 33.682932], "pop": 24526, "state": "GA", "_id": "30207"} -{"city": "CONYERS", "loc": [-84.012689, 33.607711], "pop": 24236, "state": "GA", "_id": "30208"} -{"city": "STARRSVILLE", "loc": [-83.890752, 33.570328], "pop": 35231, "state": "GA", "_id": "30209"} -{"city": "DACULA", "loc": [-83.883849, 33.988234], "pop": 7320, "state": "GA", "_id": "30211"} -{"city": "FAIRBURN", "loc": [-84.580857, 33.56482], "pop": 14110, "state": "GA", "_id": "30213"} -{"city": "WOOLSEY", "loc": [-84.463586, 33.448054], "pop": 35007, "state": "GA", "_id": "30214"} -{"city": "FLOVILLA", "loc": [-83.907938, 33.250012], "pop": 2238, "state": "GA", "_id": "30216"} -{"city": "GLENN", "loc": [-85.105459, 33.291073], "pop": 7230, "state": "GA", "_id": "30217"} -{"city": "ALVATON", "loc": [-84.580576, 33.149902], "pop": 2327, "state": "GA", "_id": "30218"} -{"city": "GRANTVILLE", "loc": [-84.834977, 33.247253], "pop": 1825, "state": "GA", "_id": "30220"} -{"city": "GRAYSON", "loc": [-83.979846, 33.898344], "pop": 5528, "state": "GA", "_id": "30221"} -{"city": "STOVALL", "loc": [-84.742173, 33.037169], "pop": 4507, "state": "GA", "_id": "30222"} -{"city": "GRIFFIN", "loc": [-84.272759, 33.2549], "pop": 53247, "state": "GA", "_id": "30223"} -{"city": "HAMPTON", "loc": [-84.294744, 33.412398], "pop": 10969, "state": "GA", "_id": "30228"} -{"city": "HOGANSVILLE", "loc": [-84.930856, 33.164382], "pop": 6378, "state": "GA", "_id": "30230"} -{"city": "JACKSON", "loc": [-83.978391, 33.282006], "pop": 13206, "state": "GA", "_id": "30233"} -{"city": "JENKINSBURG", "loc": [-84.026122, 33.318619], "pop": 1242, "state": "GA", "_id": "30234"} -{"city": "JONESBORO", "loc": [-84.358968, 33.524236], "pop": 55409, "state": "GA", "_id": "30236"} -{"city": "LA GRANGE", "loc": [-85.039511, 33.034025], "pop": 43081, "state": "GA", "_id": "30240"} -{"city": "LAWRENCEVILLE", "loc": [-84.014944, 34.005049], "pop": 24119, "state": "GA", "_id": "30243"} -{"city": "LAWRENCEVILLE", "loc": [-84.081652, 33.923781], "pop": 37778, "state": "GA", "_id": "30244"} -{"city": "LAWRENCEVILLE", "loc": [-83.996413, 33.944293], "pop": 30171, "state": "GA", "_id": "30245"} -{"city": "LILBURN", "loc": [-84.119595, 33.873182], "pop": 46637, "state": "GA", "_id": "30247"} -{"city": "LOCUST GROVE", "loc": [-84.09825, 33.344936], "pop": 5812, "state": "GA", "_id": "30248"} -{"city": "LOGANVILLE", "loc": [-83.903631, 33.83366], "pop": 14843, "state": "GA", "_id": "30249"} -{"city": "LUTHERSVILLE", "loc": [-84.757707, 33.179817], "pop": 3389, "state": "GA", "_id": "30251"} -{"city": "MC DONOUGH", "loc": [-84.135751, 33.46466], "pop": 18550, "state": "GA", "_id": "30253"} -{"city": "MANSFIELD", "loc": [-83.727638, 33.540373], "pop": 1438, "state": "GA", "_id": "30255"} -{"city": "MEANSVILLE", "loc": [-84.316938, 33.013387], "pop": 1668, "state": "GA", "_id": "30256"} -{"city": "MILNER", "loc": [-84.175909, 33.141023], "pop": 3789, "state": "GA", "_id": "30257"} -{"city": "MOLENA", "loc": [-84.45578, 32.997845], "pop": 2055, "state": "GA", "_id": "30258"} -{"city": "MORELAND", "loc": [-84.75656, 33.273437], "pop": 2056, "state": "GA", "_id": "30259"} -{"city": "MORROW", "loc": [-84.324695, 33.584934], "pop": 20584, "state": "GA", "_id": "30260"} -{"city": "NEWBORN", "loc": [-83.668396, 33.494923], "pop": 1491, "state": "GA", "_id": "30262"} -{"city": "RAYMOND", "loc": [-84.816952, 33.389404], "pop": 32379, "state": "GA", "_id": "30263"} -{"city": "NEWNAN", "loc": [-84.712062, 33.39576], "pop": 5345, "state": "GA", "_id": "30265"} -{"city": "OXFORD", "loc": [-83.863517, 33.686474], "pop": 10601, "state": "GA", "_id": "30267"} -{"city": "PALMETTO", "loc": [-84.678999, 33.524195], "pop": 5838, "state": "GA", "_id": "30268"} -{"city": "PEACHTREE CITY", "loc": [-84.563478, 33.391467], "pop": 18724, "state": "GA", "_id": "30269"} -{"city": "REX", "loc": [-84.278228, 33.580805], "pop": 7218, "state": "GA", "_id": "30273"} -{"city": "RIVERDALE", "loc": [-84.400348, 33.553126], "pop": 23621, "state": "GA", "_id": "30274"} -{"city": "SENOIA", "loc": [-84.591779, 33.284459], "pop": 4653, "state": "GA", "_id": "30276"} -{"city": "SHARPSBURG", "loc": [-84.654027, 33.401287], "pop": 5956, "state": "GA", "_id": "30277"} -{"city": "SNELLVILLE", "loc": [-84.028011, 33.848607], "pop": 34314, "state": "GA", "_id": "30278"} -{"city": "SOCIAL CIRCLE", "loc": [-83.706149, 33.656386], "pop": 4598, "state": "GA", "_id": "30279"} -{"city": "STOCKBRIDGE", "loc": [-84.21649, 33.563343], "pop": 25401, "state": "GA", "_id": "30281"} -{"city": "THE ROCK", "loc": [-84.242359, 32.971734], "pop": 333, "state": "GA", "_id": "30285"} -{"city": "THOMASTON", "loc": [-84.3324, 32.901534], "pop": 22925, "state": "GA", "_id": "30286"} -{"city": "TYRONE", "loc": [-84.591357, 33.471948], "pop": 3555, "state": "GA", "_id": "30290"} -{"city": "UNION CITY", "loc": [-84.549853, 33.583155], "pop": 7411, "state": "GA", "_id": "30291"} -{"city": "WILLIAMSON", "loc": [-84.379493, 33.159838], "pop": 2940, "state": "GA", "_id": "30292"} -{"city": "WOODBURY", "loc": [-84.598632, 32.981318], "pop": 3648, "state": "GA", "_id": "30293"} -{"city": "ZEBULON", "loc": [-84.310827, 33.100235], "pop": 2470, "state": "GA", "_id": "30295"} -{"city": "RIVERDALE", "loc": [-84.43645, 33.566716], "pop": 18577, "state": "GA", "_id": "30296"} -{"city": "ATLANTA", "loc": [-84.388846, 33.752504], "pop": 1845, "state": "GA", "_id": "30303"} -{"city": "ATLANTA", "loc": [-84.385145, 33.831963], "pop": 19122, "state": "GA", "_id": "30305"} -{"city": "ATLANTA", "loc": [-84.351418, 33.786027], "pop": 20081, "state": "GA", "_id": "30306"} -{"city": "ATLANTA", "loc": [-84.335957, 33.769138], "pop": 16330, "state": "GA", "_id": "30307"} -{"city": "ATLANTA", "loc": [-84.375744, 33.771839], "pop": 8549, "state": "GA", "_id": "30308"} -{"city": "ATLANTA", "loc": [-84.388338, 33.798407], "pop": 14766, "state": "GA", "_id": "30309"} -{"city": "ATLANTA", "loc": [-84.423173, 33.727849], "pop": 34017, "state": "GA", "_id": "30310"} -{"city": "ATLANTA", "loc": [-84.470219, 33.722957], "pop": 34880, "state": "GA", "_id": "30311"} -{"city": "ATLANTA", "loc": [-84.378125, 33.746749], "pop": 17683, "state": "GA", "_id": "30312"} -{"city": "ATLANTA", "loc": [-84.39352, 33.76825], "pop": 8038, "state": "GA", "_id": "30313"} -{"city": "ATLANTA", "loc": [-84.425546, 33.756103], "pop": 26649, "state": "GA", "_id": "30314"} -{"city": "ATLANTA", "loc": [-84.380771, 33.705062], "pop": 41061, "state": "GA", "_id": "30315"} -{"city": "ATLANTA", "loc": [-84.333913, 33.721686], "pop": 34668, "state": "GA", "_id": "30316"} -{"city": "ATLANTA", "loc": [-84.31685, 33.749788], "pop": 16395, "state": "GA", "_id": "30317"} -{"city": "ATLANTA", "loc": [-84.445432, 33.786454], "pop": 53894, "state": "GA", "_id": "30318"} -{"city": "ATLANTA", "loc": [-84.335091, 33.868728], "pop": 32138, "state": "GA", "_id": "30319"} -{"city": "ATLANTA", "loc": [-84.354867, 33.820609], "pop": 15044, "state": "GA", "_id": "30324"} -{"city": "ATLANTA", "loc": [-84.358232, 33.848168], "pop": 125, "state": "GA", "_id": "30326"} -{"city": "ATLANTA", "loc": [-84.419966, 33.862723], "pop": 18467, "state": "GA", "_id": "30327"} -{"city": "SANDY SPRINGS", "loc": [-84.381143, 33.936295], "pop": 24238, "state": "GA", "_id": "30328"} -{"city": "ATLANTA", "loc": [-84.321402, 33.823555], "pop": 17013, "state": "GA", "_id": "30329"} -{"city": "ATLANTA", "loc": [-84.434735, 33.70645], "pop": 643, "state": "GA", "_id": "30330"} -{"city": "ATLANTA", "loc": [-84.520468, 33.72241], "pop": 38185, "state": "GA", "_id": "30331"} -{"city": "ATLANTA", "loc": [-84.388188, 33.74715], "pop": 0, "state": "GA", "_id": "30334"} -{"city": "ATLANTA", "loc": [-84.510028, 33.78534], "pop": 1228, "state": "GA", "_id": "30336"} -{"city": "COLLEGE PARK", "loc": [-84.460849, 33.644227], "pop": 18106, "state": "GA", "_id": "30337"} -{"city": "DUNWOODY", "loc": [-84.316529, 33.944313], "pop": 23565, "state": "GA", "_id": "30338"} -{"city": "ATLANTA", "loc": [-84.462879, 33.87125], "pop": 11158, "state": "GA", "_id": "30339"} -{"city": "DORAVILLE", "loc": [-84.248265, 33.896377], "pop": 22862, "state": "GA", "_id": "30340"} -{"city": "CHAMBLEE", "loc": [-84.286969, 33.886727], "pop": 26846, "state": "GA", "_id": "30341"} -{"city": "ATLANTA", "loc": [-84.376091, 33.884245], "pop": 19057, "state": "GA", "_id": "30342"} -{"city": "EAST POINT", "loc": [-84.457292, 33.676214], "pop": 33489, "state": "GA", "_id": "30344"} -{"city": "ATLANTA", "loc": [-84.286961, 33.851347], "pop": 19825, "state": "GA", "_id": "30345"} -{"city": "ATLANTA", "loc": [-84.333354, 33.926717], "pop": 18, "state": "GA", "_id": "30346"} -{"city": "ATLANTA", "loc": [-84.481258, 33.605331], "pop": 48116, "state": "GA", "_id": "30349"} -{"city": "ATLANTA", "loc": [-84.341146, 33.979471], "pop": 24573, "state": "GA", "_id": "30350"} -{"city": "HAPEVILLE", "loc": [-84.387025, 33.66546], "pop": 16349, "state": "GA", "_id": "30354"} -{"city": "ATLANTA", "loc": [-84.271645, 33.937772], "pop": 16023, "state": "GA", "_id": "30360"} -{"city": "OAK PARK", "loc": [-82.338668, 32.581938], "pop": 12918, "state": "GA", "_id": "30401"} -{"city": "AILEY", "loc": [-82.47619, 32.214262], "pop": 1004, "state": "GA", "_id": "30410"} -{"city": "ALAMO", "loc": [-82.794445, 32.13298], "pop": 2584, "state": "GA", "_id": "30411"} -{"city": "BARTOW", "loc": [-82.470788, 32.863186], "pop": 945, "state": "GA", "_id": "30413"} -{"city": "BROOKLET", "loc": [-81.628003, 32.294046], "pop": 4850, "state": "GA", "_id": "30415"} -{"city": "CLAXTON", "loc": [-81.908032, 32.165009], "pop": 8724, "state": "GA", "_id": "30417"} -{"city": "COBBTOWN", "loc": [-82.133274, 32.264237], "pop": 1482, "state": "GA", "_id": "30420"} -{"city": "COLLINS", "loc": [-82.10948, 32.185228], "pop": 1298, "state": "GA", "_id": "30421"} -{"city": "GARFIELD", "loc": [-82.085599, 32.600776], "pop": 593, "state": "GA", "_id": "30425"} -{"city": "GIRARD", "loc": [-81.710581, 33.043689], "pop": 1074, "state": "GA", "_id": "30426"} -{"city": "GLENNVILLE", "loc": [-81.948313, 31.946708], "pop": 7702, "state": "GA", "_id": "30427"} -{"city": "GLENWOOD", "loc": [-82.675881, 32.157243], "pop": 2319, "state": "GA", "_id": "30428"} -{"city": "LOUISVILLE", "loc": [-82.383616, 33.016342], "pop": 7089, "state": "GA", "_id": "30434"} -{"city": "LYONS", "loc": [-82.307783, 32.171148], "pop": 8675, "state": "GA", "_id": "30436"} -{"city": "MANASSAS", "loc": [-82.052925, 32.096551], "pop": 955, "state": "GA", "_id": "30438"} -{"city": "METTER", "loc": [-82.060722, 32.401034], "pop": 7744, "state": "GA", "_id": "30439"} -{"city": "MIDVILLE", "loc": [-82.204181, 32.863794], "pop": 1533, "state": "GA", "_id": "30441"} -{"city": "MILLEN", "loc": [-81.961807, 32.787739], "pop": 7700, "state": "GA", "_id": "30442"} -{"city": "MOUNT VERNON", "loc": [-82.58672, 32.18398], "pop": 3097, "state": "GA", "_id": "30445"} -{"city": "NEWINGTON", "loc": [-81.532686, 32.588385], "pop": 2296, "state": "GA", "_id": "30446"} -{"city": "PORTAL", "loc": [-81.912257, 32.55504], "pop": 3160, "state": "GA", "_id": "30450"} -{"city": "REGISTER", "loc": [-81.872828, 32.338235], "pop": 1655, "state": "GA", "_id": "30452"} -{"city": "REIDSVILLE", "loc": [-82.147809, 32.054759], "pop": 6286, "state": "GA", "_id": "30453"} -{"city": "ROCKLEDGE", "loc": [-82.747939, 32.391507], "pop": 160, "state": "GA", "_id": "30454"} -{"city": "ROCKY FORD", "loc": [-81.741749, 32.656863], "pop": 1554, "state": "GA", "_id": "30455"} -{"city": "SARDIS", "loc": [-81.773039, 32.980946], "pop": 1901, "state": "GA", "_id": "30456"} -{"city": "SOPERTON", "loc": [-82.587098, 32.386861], "pop": 5996, "state": "GA", "_id": "30457"} -{"city": "STATESBORO", "loc": [-81.773956, 32.440841], "pop": 33469, "state": "GA", "_id": "30458"} -{"city": "HILTONIA", "loc": [-81.633394, 32.77627], "pop": 9991, "state": "GA", "_id": "30467"} -{"city": "TARRYTOWN", "loc": [-82.516933, 32.290623], "pop": 1181, "state": "GA", "_id": "30470"} -{"city": "TWIN CITY", "loc": [-82.197856, 32.612926], "pop": 5531, "state": "GA", "_id": "30471"} -{"city": "UVALDA", "loc": [-82.508879, 32.048271], "pop": 1881, "state": "GA", "_id": "30473"} -{"city": "VIDALIA", "loc": [-82.406724, 32.193408], "pop": 15396, "state": "GA", "_id": "30474"} -{"city": "WADLEY", "loc": [-82.402459, 32.874742], "pop": 3376, "state": "GA", "_id": "30477"} -{"city": "GAINESVILLE", "loc": [-83.825596, 34.307269], "pop": 22334, "state": "GA", "_id": "30501"} -{"city": "GAINESVILLE", "loc": [-83.879311, 34.272281], "pop": 10061, "state": "GA", "_id": "30504"} -{"city": "GAINESVILLE", "loc": [-83.888211, 34.356227], "pop": 22232, "state": "GA", "_id": "30506"} -{"city": "GAINESVILLE", "loc": [-83.771625, 34.25915], "pop": 13327, "state": "GA", "_id": "30507"} -{"city": "ALTO", "loc": [-83.601996, 34.459079], "pop": 4701, "state": "GA", "_id": "30510"} -{"city": "BALDWIN", "loc": [-83.529781, 34.456411], "pop": 3969, "state": "GA", "_id": "30511"} -{"city": "BLAIRSVILLE", "loc": [-83.992027, 34.876253], "pop": 11360, "state": "GA", "_id": "30512"} -{"city": "BLUE RIDGE", "loc": [-84.328087, 34.855523], "pop": 4844, "state": "GA", "_id": "30513"} -{"city": "BOWERSVILLE", "loc": [-83.048364, 34.399592], "pop": 2082, "state": "GA", "_id": "30516"} -{"city": "BRASELTON", "loc": [-83.781162, 34.138941], "pop": 3142, "state": "GA", "_id": "30517"} -{"city": "BUFORD", "loc": [-83.996459, 34.112373], "pop": 24810, "state": "GA", "_id": "30518"} -{"city": "CANON", "loc": [-83.126693, 34.347766], "pop": 1393, "state": "GA", "_id": "30520"} -{"city": "CARNESVILLE", "loc": [-83.279209, 34.394723], "pop": 4135, "state": "GA", "_id": "30521"} -{"city": "CHERRYLOG", "loc": [-84.443136, 34.773711], "pop": 2150, "state": "GA", "_id": "30522"} -{"city": "CLARKESVILLE", "loc": [-83.524304, 34.644993], "pop": 8931, "state": "GA", "_id": "30523"} -{"city": "CLAYTON", "loc": [-83.40655, 34.882569], "pop": 6700, "state": "GA", "_id": "30525"} -{"city": "CLERMONT", "loc": [-83.785311, 34.476091], "pop": 2336, "state": "GA", "_id": "30527"} -{"city": "CLEVELAND", "loc": [-83.749985, 34.583949], "pop": 11036, "state": "GA", "_id": "30528"} -{"city": "COMMERCE", "loc": [-83.448011, 34.213413], "pop": 8792, "state": "GA", "_id": "30529"} -{"city": "CORNELIA", "loc": [-83.54527, 34.520882], "pop": 7597, "state": "GA", "_id": "30531"} -{"city": "DAHLONEGA", "loc": [-83.979838, 34.529949], "pop": 15003, "state": "GA", "_id": "30533"} -{"city": "JUNO", "loc": [-84.103705, 34.394331], "pop": 10196, "state": "GA", "_id": "30534"} -{"city": "DEMOREST", "loc": [-83.569625, 34.575564], "pop": 3521, "state": "GA", "_id": "30535"} -{"city": "SKY VALLEY", "loc": [-83.380113, 34.979659], "pop": 328, "state": "GA", "_id": "30537"} -{"city": "EASTANOLLEE", "loc": [-83.258685, 34.50366], "pop": 1478, "state": "GA", "_id": "30538"} -{"city": "EAST ELLIJAY", "loc": [-84.473062, 34.682636], "pop": 314, "state": "GA", "_id": "30539"} -{"city": "ELLIJAY", "loc": [-84.481226, 34.677514], "pop": 9672, "state": "GA", "_id": "30540"} -{"city": "EPWORTH", "loc": [-84.441593, 34.944249], "pop": 1523, "state": "GA", "_id": "30541"} -{"city": "FLOWERY BRANCH", "loc": [-83.902407, 34.181893], "pop": 9361, "state": "GA", "_id": "30542"} -{"city": "GILLSVILLE", "loc": [-83.675681, 34.300148], "pop": 1585, "state": "GA", "_id": "30543"} -{"city": "HELEN", "loc": [-83.739936, 34.686601], "pop": 469, "state": "GA", "_id": "30545"} -{"city": "HIAWASSEE", "loc": [-83.7478, 34.937667], "pop": 5067, "state": "GA", "_id": "30546"} -{"city": "HOMER", "loc": [-83.497432, 34.356312], "pop": 2949, "state": "GA", "_id": "30547"} -{"city": "HOSCHTON", "loc": [-83.780253, 34.086578], "pop": 3444, "state": "GA", "_id": "30548"} -{"city": "JEFFERSON", "loc": [-83.570813, 34.106197], "pop": 8795, "state": "GA", "_id": "30549"} -{"city": "LAKEMONT", "loc": [-83.403763, 34.761697], "pop": 338, "state": "GA", "_id": "30552"} -{"city": "LAVONIA", "loc": [-83.112992, 34.452758], "pop": 5797, "state": "GA", "_id": "30553"} -{"city": "LULA", "loc": [-83.694459, 34.398513], "pop": 3365, "state": "GA", "_id": "30554"} -{"city": "MC CAYSVILLE", "loc": [-84.368294, 34.952212], "pop": 4720, "state": "GA", "_id": "30555"} -{"city": "MARTIN", "loc": [-83.168553, 34.515482], "pop": 1601, "state": "GA", "_id": "30557"} -{"city": "MAYSVILLE", "loc": [-83.584019, 34.273627], "pop": 4456, "state": "GA", "_id": "30558"} -{"city": "MINERAL BLUFF", "loc": [-84.254066, 34.934112], "pop": 2180, "state": "GA", "_id": "30559"} -{"city": "MORGANTON", "loc": [-84.211479, 34.871382], "pop": 2558, "state": "GA", "_id": "30560"} -{"city": "MOUNT AIRY", "loc": [-83.471272, 34.567825], "pop": 2813, "state": "GA", "_id": "30563"} -{"city": "MURRAYVILLE", "loc": [-83.894919, 34.434167], "pop": 2126, "state": "GA", "_id": "30564"} -{"city": "NICHOLSON", "loc": [-83.421031, 34.097016], "pop": 3352, "state": "GA", "_id": "30565"} -{"city": "OAKWOOD", "loc": [-83.894013, 34.237196], "pop": 6584, "state": "GA", "_id": "30566"} -{"city": "PENDERGRASS", "loc": [-83.663356, 34.179634], "pop": 1956, "state": "GA", "_id": "30567"} -{"city": "RABUN GAP", "loc": [-83.390122, 34.957552], "pop": 1828, "state": "GA", "_id": "30568"} -{"city": "SAUTEE NACOOCHEE", "loc": [-83.693116, 34.716245], "pop": 1504, "state": "GA", "_id": "30571"} -{"city": "SUCHES", "loc": [-84.049582, 34.72529], "pop": 800, "state": "GA", "_id": "30572"} -{"city": "TALMO", "loc": [-83.71872, 34.19506], "pop": 1153, "state": "GA", "_id": "30575"} -{"city": "TIGER", "loc": [-83.433317, 34.81741], "pop": 2454, "state": "GA", "_id": "30576"} -{"city": "TOCCOA", "loc": [-83.31144, 34.566474], "pop": 20324, "state": "GA", "_id": "30577"} -{"city": "YOUNG HARRIS", "loc": [-83.86882, 34.958791], "pop": 1687, "state": "GA", "_id": "30582"} -{"city": "ATHENS", "loc": [-83.363174, 33.976097], "pop": 15661, "state": "GA", "_id": "30601"} -{"city": "ATHENS", "loc": [-83.352508, 33.932097], "pop": 33299, "state": "GA", "_id": "30605"} -{"city": "ATHENS", "loc": [-83.418019, 33.946085], "pop": 29839, "state": "GA", "_id": "30606"} -{"city": "ATHENS", "loc": [-83.427761, 34.006978], "pop": 7056, "state": "GA", "_id": "30607"} -{"city": "ARNOLDSVILLE", "loc": [-83.234137, 33.880338], "pop": 648, "state": "GA", "_id": "30619"} -{"city": "BETHLEHEM", "loc": [-83.728227, 33.926116], "pop": 2181, "state": "GA", "_id": "30620"} -{"city": "BISHOP", "loc": [-83.477655, 33.808091], "pop": 3620, "state": "GA", "_id": "30621"} -{"city": "BOGART", "loc": [-83.50546, 33.934038], "pop": 7245, "state": "GA", "_id": "30622"} -{"city": "BOWMAN", "loc": [-83.028437, 34.19193], "pop": 2315, "state": "GA", "_id": "30624"} -{"city": "BUCKHEAD", "loc": [-83.343484, 33.536842], "pop": 1222, "state": "GA", "_id": "30625"} -{"city": "CARLTON", "loc": [-83.003827, 33.985217], "pop": 2046, "state": "GA", "_id": "30627"} -{"city": "COLBERT", "loc": [-83.21906, 34.038246], "pop": 5943, "state": "GA", "_id": "30628"} -{"city": "COMER", "loc": [-83.121978, 34.088776], "pop": 3134, "state": "GA", "_id": "30629"} -{"city": "CRAWFORD", "loc": [-83.189319, 33.909533], "pop": 2883, "state": "GA", "_id": "30630"} -{"city": "CRAWFORDVILLE", "loc": [-82.887234, 33.570801], "pop": 1915, "state": "GA", "_id": "30631"} -{"city": "DANIELSVILLE", "loc": [-83.275841, 34.170799], "pop": 7119, "state": "GA", "_id": "30633"} -{"city": "DEWY ROSE", "loc": [-82.940894, 34.172052], "pop": 1138, "state": "GA", "_id": "30634"} -{"city": "ELBERTON", "loc": [-82.844765, 34.108202], "pop": 15503, "state": "GA", "_id": "30635"} -{"city": "GOOD HOPE", "loc": [-83.594952, 33.761346], "pop": 1031, "state": "GA", "_id": "30641"} -{"city": "GREENSBORO", "loc": [-83.170192, 33.563682], "pop": 6206, "state": "GA", "_id": "30642"} -{"city": "HARTWELL", "loc": [-82.929576, 34.357124], "pop": 14886, "state": "GA", "_id": "30643"} -{"city": "HULL", "loc": [-83.311029, 34.04781], "pop": 5142, "state": "GA", "_id": "30646"} -{"city": "LEXINGTON", "loc": [-83.085809, 33.879913], "pop": 1712, "state": "GA", "_id": "30648"} -{"city": "MADISON", "loc": [-83.461781, 33.594681], "pop": 8855, "state": "GA", "_id": "30650"} -{"city": "MONROE", "loc": [-83.707221, 33.808141], "pop": 18405, "state": "GA", "_id": "30655"} -{"city": "PHILOMATH", "loc": [-82.909975, 33.783593], "pop": 1137, "state": "GA", "_id": "30660"} -{"city": "ROYSTON", "loc": [-83.140614, 34.277793], "pop": 8836, "state": "GA", "_id": "30662"} -{"city": "RUTLEDGE", "loc": [-83.602302, 33.616316], "pop": 2161, "state": "GA", "_id": "30663"} -{"city": "STATHAM", "loc": [-83.5893, 33.960233], "pop": 3758, "state": "GA", "_id": "30666"} -{"city": "STEPHENS", "loc": [-83.115902, 33.771471], "pop": 1069, "state": "GA", "_id": "30667"} -{"city": "DANBURG", "loc": [-82.695558, 33.883294], "pop": 2293, "state": "GA", "_id": "30668"} -{"city": "UNION POINT", "loc": [-83.087913, 33.634544], "pop": 3561, "state": "GA", "_id": "30669"} -{"city": "WASHINGTON", "loc": [-82.742912, 33.726541], "pop": 7699, "state": "GA", "_id": "30673"} -{"city": "WATKINSVILLE", "loc": [-83.408043, 33.854247], "pop": 7723, "state": "GA", "_id": "30677"} -{"city": "WHITE PLAINS", "loc": [-83.090845, 33.484338], "pop": 1596, "state": "GA", "_id": "30678"} -{"city": "WINDER", "loc": [-83.711473, 33.998544], "pop": 16981, "state": "GA", "_id": "30680"} -{"city": "WINTERVILLE", "loc": [-83.290645, 33.954253], "pop": 3056, "state": "GA", "_id": "30683"} -{"city": "CALHOUN", "loc": [-84.934545, 34.496456], "pop": 22017, "state": "GA", "_id": "30701"} -{"city": "CHATSWORTH", "loc": [-84.794293, 34.758929], "pop": 22271, "state": "GA", "_id": "30705"} -{"city": "CHICKAMAUGA", "loc": [-85.322136, 34.857963], "pop": 13545, "state": "GA", "_id": "30707"} -{"city": "CISCO", "loc": [-84.717382, 34.847405], "pop": 978, "state": "GA", "_id": "30708"} -{"city": "COHUTTA", "loc": [-84.945801, 34.932635], "pop": 5052, "state": "GA", "_id": "30710"} -{"city": "CRANDALL", "loc": [-84.764533, 34.941225], "pop": 2901, "state": "GA", "_id": "30711"} -{"city": "DALTON", "loc": [-84.987477, 34.76352], "pop": 20680, "state": "GA", "_id": "30720"} -{"city": "DALTON", "loc": [-84.933871, 34.77922], "pop": 36051, "state": "GA", "_id": "30721"} -{"city": "FLINTSTONE", "loc": [-85.352443, 34.925162], "pop": 2775, "state": "GA", "_id": "30725"} -{"city": "LA FAYETTE", "loc": [-85.260212, 34.692008], "pop": 16287, "state": "GA", "_id": "30728"} -{"city": "LYERLY", "loc": [-85.403826, 34.402958], "pop": 2585, "state": "GA", "_id": "30730"} -{"city": "CLOUDLAND", "loc": [-85.459723, 34.570909], "pop": 3222, "state": "GA", "_id": "30731"} -{"city": "PLAINVILLE", "loc": [-85.031204, 34.41447], "pop": 1589, "state": "GA", "_id": "30733"} -{"city": "RANGER", "loc": [-84.727018, 34.540361], "pop": 2015, "state": "GA", "_id": "30734"} -{"city": "HILL CITY", "loc": [-84.916251, 34.591996], "pop": 2926, "state": "GA", "_id": "30735"} -{"city": "RINGGOLD", "loc": [-85.154908, 34.92052], "pop": 25360, "state": "GA", "_id": "30736"} -{"city": "RISING FAWN", "loc": [-85.471078, 34.853491], "pop": 4036, "state": "GA", "_id": "30738"} -{"city": "ROCK SPRING", "loc": [-85.241481, 34.806534], "pop": 4181, "state": "GA", "_id": "30739"} -{"city": "ROCKY FACE", "loc": [-85.056143, 34.774456], "pop": 6029, "state": "GA", "_id": "30740"} -{"city": "ROSSVILLE", "loc": [-85.296785, 34.953481], "pop": 18436, "state": "GA", "_id": "30741"} -{"city": "FORT OGLETHORPE", "loc": [-85.242812, 34.964082], "pop": 14807, "state": "GA", "_id": "30742"} -{"city": "SUGAR VALLEY", "loc": [-85.026048, 34.551929], "pop": 1340, "state": "GA", "_id": "30746"} -{"city": "SUMMERVILLE", "loc": [-85.336224, 34.485899], "pop": 10748, "state": "GA", "_id": "30747"} -{"city": "LOOKOUT MOUNTAIN", "loc": [-85.379384, 34.933512], "pop": 2818, "state": "GA", "_id": "30750"} -{"city": "TRENTON", "loc": [-85.517118, 34.901681], "pop": 7930, "state": "GA", "_id": "30752"} -{"city": "TRION", "loc": [-85.311156, 34.54679], "pop": 5963, "state": "GA", "_id": "30753"} -{"city": "TUNNEL HILL", "loc": [-85.046815, 34.854129], "pop": 6947, "state": "GA", "_id": "30755"} -{"city": "WILDWOOD", "loc": [-85.430456, 34.977911], "pop": 586, "state": "GA", "_id": "30757"} -{"city": "APPLING", "loc": [-82.285631, 33.627088], "pop": 2255, "state": "GA", "_id": "30802"} -{"city": "AVERA", "loc": [-82.514965, 33.140997], "pop": 836, "state": "GA", "_id": "30803"} -{"city": "BLYTHE", "loc": [-82.203044, 33.294088], "pop": 297, "state": "GA", "_id": "30805"} -{"city": "DEARING", "loc": [-82.395512, 33.40702], "pop": 4632, "state": "GA", "_id": "30808"} -{"city": "EVANS", "loc": [-82.139775, 33.541194], "pop": 12788, "state": "GA", "_id": "30809"} -{"city": "GIBSON", "loc": [-82.576591, 33.247097], "pop": 1649, "state": "GA", "_id": "30810"} -{"city": "GROVETOWN", "loc": [-82.240937, 33.476622], "pop": 13283, "state": "GA", "_id": "30813"} -{"city": "HARLEM", "loc": [-82.309659, 33.417007], "pop": 4318, "state": "GA", "_id": "30814"} -{"city": "HEPHZIBAH", "loc": [-82.088689, 33.343328], "pop": 27838, "state": "GA", "_id": "30815"} -{"city": "KEYSVILLE", "loc": [-82.183368, 33.17191], "pop": 135, "state": "GA", "_id": "30816"} -{"city": "LINCOLNTON", "loc": [-82.443481, 33.777335], "pop": 7028, "state": "GA", "_id": "30817"} -{"city": "MATTHEWS", "loc": [-82.286747, 33.217582], "pop": 607, "state": "GA", "_id": "30818"} -{"city": "MITCHELL", "loc": [-82.681654, 33.20515], "pop": 708, "state": "GA", "_id": "30820"} -{"city": "NORWOOD", "loc": [-82.735575, 33.478921], "pop": 882, "state": "GA", "_id": "30821"} -{"city": "PERKINS", "loc": [-81.869573, 32.905747], "pop": 549, "state": "GA", "_id": "30822"} -{"city": "STAPLETON", "loc": [-82.459704, 33.189423], "pop": 884, "state": "GA", "_id": "30823"} -{"city": "THOMSON", "loc": [-82.494204, 33.477363], "pop": 15487, "state": "GA", "_id": "30824"} -{"city": "WARRENTON", "loc": [-82.63568, 33.40914], "pop": 4091, "state": "GA", "_id": "30828"} -{"city": "WAYNESBORO", "loc": [-81.990797, 33.101254], "pop": 12213, "state": "GA", "_id": "30830"} -{"city": "WRENS", "loc": [-82.380962, 33.210397], "pop": 4782, "state": "GA", "_id": "30833"} -{"city": "AUGUSTA", "loc": [-81.972959, 33.460084], "pop": 24496, "state": "GA", "_id": "30901"} -{"city": "AUGUSTA", "loc": [-82.013078, 33.47374], "pop": 30408, "state": "GA", "_id": "30904"} -{"city": "FORT GORDON", "loc": [-82.139179, 33.419032], "pop": 9260, "state": "GA", "_id": "30905"} -{"city": "PEACH ORCHARD", "loc": [-82.038358, 33.402024], "pop": 58646, "state": "GA", "_id": "30906"} -{"city": "MARTINEZ", "loc": [-82.099505, 33.511692], "pop": 42573, "state": "GA", "_id": "30907"} -{"city": "FOREST HILLS", "loc": [-82.060439, 33.480932], "pop": 32202, "state": "GA", "_id": "30909"} -{"city": "ABBEVILLE", "loc": [-83.306845, 31.96484], "pop": 1991, "state": "GA", "_id": "31001"} -{"city": "ADRIAN", "loc": [-82.569508, 32.563505], "pop": 2880, "state": "GA", "_id": "31002"} -{"city": "ALLENTOWN", "loc": [-83.189796, 32.653829], "pop": 1099, "state": "GA", "_id": "31003"} -{"city": "BONAIRE", "loc": [-83.604736, 32.546037], "pop": 5095, "state": "GA", "_id": "31005"} -{"city": "BUTLER", "loc": [-84.234308, 32.572597], "pop": 4731, "state": "GA", "_id": "31006"} -{"city": "BYROMVILLE", "loc": [-83.899128, 32.185192], "pop": 1672, "state": "GA", "_id": "31007"} -{"city": "POWERSVILLE", "loc": [-83.762244, 32.647384], "pop": 8205, "state": "GA", "_id": "31008"} -{"city": "CADWELL", "loc": [-83.026767, 32.317395], "pop": 1501, "state": "GA", "_id": "31009"} -{"city": "CHAUNCEY", "loc": [-83.050804, 32.083532], "pop": 1954, "state": "GA", "_id": "31011"} -{"city": "CHESTER", "loc": [-83.174029, 32.398152], "pop": 1517, "state": "GA", "_id": "31012"} -{"city": "COCHRAN", "loc": [-83.322886, 32.398066], "pop": 11373, "state": "GA", "_id": "31014"} -{"city": "CORDELE", "loc": [-83.783507, 31.956625], "pop": 18662, "state": "GA", "_id": "31015"} -{"city": "CULLODEN", "loc": [-84.045628, 32.830078], "pop": 2501, "state": "GA", "_id": "31016"} -{"city": "DANVILLE", "loc": [-83.303298, 32.593017], "pop": 2221, "state": "GA", "_id": "31017"} -{"city": "DAVISBORO", "loc": [-82.622738, 32.944296], "pop": 2032, "state": "GA", "_id": "31018"} -{"city": "DEXTER", "loc": [-83.052771, 32.435639], "pop": 1705, "state": "GA", "_id": "31019"} -{"city": "DRY BRANCH", "loc": [-83.495105, 32.70499], "pop": 3702, "state": "GA", "_id": "31020"} -{"city": "EAST DUBLIN", "loc": [-82.90368, 32.547794], "pop": 32030, "state": "GA", "_id": "31021"} -{"city": "DUDLEY", "loc": [-83.089918, 32.525879], "pop": 966, "state": "GA", "_id": "31022"} -{"city": "EASTMAN", "loc": [-83.185959, 32.208425], "pop": 11744, "state": "GA", "_id": "31023"} -{"city": "EATONTON", "loc": [-83.36277, 33.312723], "pop": 13086, "state": "GA", "_id": "31024"} -{"city": "ELKO", "loc": [-83.730388, 32.333648], "pop": 934, "state": "GA", "_id": "31025"} -{"city": "CENTERVILLE", "loc": [-83.676773, 32.634375], "pop": 2764, "state": "GA", "_id": "31028"} -{"city": "FORSYTH", "loc": [-83.936209, 33.050764], "pop": 8959, "state": "GA", "_id": "31029"} -{"city": "FORT VALLEY", "loc": [-83.888727, 32.54956], "pop": 14027, "state": "GA", "_id": "31030"} -{"city": "STEVENS POTTERY", "loc": [-83.320416, 32.909212], "pop": 5415, "state": "GA", "_id": "31031"} -{"city": "GRAY", "loc": [-83.539951, 33.017177], "pop": 8781, "state": "GA", "_id": "31032"} -{"city": "HADDOCK", "loc": [-83.431219, 33.051888], "pop": 1193, "state": "GA", "_id": "31033"} -{"city": "HARRISON", "loc": [-82.71593, 32.842007], "pop": 1737, "state": "GA", "_id": "31035"} -{"city": "HAWKINSVILLE", "loc": [-83.494763, 32.277834], "pop": 9154, "state": "GA", "_id": "31036"} -{"city": "HELENA", "loc": [-82.91782, 32.078093], "pop": 1526, "state": "GA", "_id": "31037"} -{"city": "ROUND OAK", "loc": [-83.687092, 33.179994], "pop": 811, "state": "GA", "_id": "31038"} -{"city": "IDEAL", "loc": [-84.148149, 32.393022], "pop": 1924, "state": "GA", "_id": "31041"} -{"city": "IRWINTON", "loc": [-83.173963, 32.808849], "pop": 447, "state": "GA", "_id": "31042"} -{"city": "JEFFERSONVILLE", "loc": [-83.385896, 32.741096], "pop": 3953, "state": "GA", "_id": "31044"} -{"city": "JEWELL", "loc": [-82.732803, 33.328754], "pop": 1105, "state": "GA", "_id": "31045"} -{"city": "JULIETTE", "loc": [-83.823529, 33.119412], "pop": 687, "state": "GA", "_id": "31046"} -{"city": "KATHLEEN", "loc": [-83.61284, 32.467218], "pop": 563, "state": "GA", "_id": "31047"} -{"city": "KITE", "loc": [-82.527361, 32.707833], "pop": 1196, "state": "GA", "_id": "31049"} -{"city": "KNOXVILLE", "loc": [-83.943011, 32.64724], "pop": 2134, "state": "GA", "_id": "31050"} -{"city": "LIZELLA", "loc": [-83.825009, 32.777316], "pop": 6992, "state": "GA", "_id": "31052"} -{"city": "MC INTYRE", "loc": [-83.203953, 32.84679], "pop": 3295, "state": "GA", "_id": "31054"} -{"city": "MC RAE", "loc": [-82.887665, 32.043529], "pop": 4906, "state": "GA", "_id": "31055"} -{"city": "MARSHALLVILLE", "loc": [-83.943478, 32.452003], "pop": 2008, "state": "GA", "_id": "31057"} -{"city": "MAUK", "loc": [-84.399906, 32.509016], "pop": 319, "state": "GA", "_id": "31058"} -{"city": "MILAN", "loc": [-83.058845, 31.961797], "pop": 1627, "state": "GA", "_id": "31060"} -{"city": "MILLEDGEVILLE", "loc": [-83.237943, 33.079958], "pop": 39173, "state": "GA", "_id": "31061"} -{"city": "MONTEZUMA", "loc": [-84.004139, 32.302615], "pop": 6160, "state": "GA", "_id": "31063"} -{"city": "MONTICELLO", "loc": [-83.714049, 33.311797], "pop": 5328, "state": "GA", "_id": "31064"} -{"city": "MONTROSE", "loc": [-83.160119, 32.561867], "pop": 587, "state": "GA", "_id": "31065"} -{"city": "MUSELLA", "loc": [-84.045591, 32.820228], "pop": 121, "state": "GA", "_id": "31066"} -{"city": "OGLETHORPE", "loc": [-84.082991, 32.284186], "pop": 2951, "state": "GA", "_id": "31068"} -{"city": "PERRY", "loc": [-83.728258, 32.46051], "pop": 13945, "state": "GA", "_id": "31069"} -{"city": "PINEHURST", "loc": [-83.720853, 32.196692], "pop": 917, "state": "GA", "_id": "31070"} -{"city": "PINEVIEW", "loc": [-83.515835, 32.090728], "pop": 1154, "state": "GA", "_id": "31071"} -{"city": "PITTS", "loc": [-83.55788, 31.942533], "pop": 1278, "state": "GA", "_id": "31072"} -{"city": "RENTZ", "loc": [-82.913987, 32.363834], "pop": 3037, "state": "GA", "_id": "31075"} -{"city": "REYNOLDS", "loc": [-84.101134, 32.55411], "pop": 2455, "state": "GA", "_id": "31076"} -{"city": "RHINE", "loc": [-83.19831, 32.011303], "pop": 1449, "state": "GA", "_id": "31077"} -{"city": "ROBERTA", "loc": [-84.045114, 32.722152], "pop": 2803, "state": "GA", "_id": "31078"} -{"city": "ROCHELLE", "loc": [-83.444978, 31.949119], "pop": 2585, "state": "GA", "_id": "31079"} -{"city": "RUPERT", "loc": [-84.273753, 32.432524], "pop": 208, "state": "GA", "_id": "31081"} -{"city": "DEEPSTEP", "loc": [-82.816934, 32.986972], "pop": 10459, "state": "GA", "_id": "31082"} -{"city": "SHADY DALE", "loc": [-83.704031, 33.401353], "pop": 1606, "state": "GA", "_id": "31085"} -{"city": "DEVEREUX", "loc": [-82.985948, 33.265516], "pop": 8908, "state": "GA", "_id": "31087"} -{"city": "WARNER ROBINS", "loc": [-83.641578, 32.593365], "pop": 34526, "state": "GA", "_id": "31088"} -{"city": "TENNILLE", "loc": [-82.840024, 32.9063], "pop": 3696, "state": "GA", "_id": "31089"} -{"city": "TOOMSBORO", "loc": [-83.084427, 32.821895], "pop": 1129, "state": "GA", "_id": "31090"} -{"city": "UNADILLA", "loc": [-83.74467, 32.255766], "pop": 2623, "state": "GA", "_id": "31091"} -{"city": "VIENNA", "loc": [-83.792198, 32.091291], "pop": 4689, "state": "GA", "_id": "31092"} -{"city": "WARNER ROBINS", "loc": [-83.639466, 32.636839], "pop": 27107, "state": "GA", "_id": "31093"} -{"city": "WARTHEN", "loc": [-82.803899, 33.125455], "pop": 1188, "state": "GA", "_id": "31094"} -{"city": "WRIGHTSVILLE", "loc": [-82.726206, 32.721866], "pop": 5755, "state": "GA", "_id": "31096"} -{"city": "YATESVILLE", "loc": [-84.159253, 32.915592], "pop": 1188, "state": "GA", "_id": "31097"} -{"city": "ROBINS A F B", "loc": [-83.58775, 32.60958], "pop": 3228, "state": "GA", "_id": "31098"} -{"city": "HUBER", "loc": [-83.598686, 32.84386], "pop": 31882, "state": "GA", "_id": "31201"} -{"city": "MACON", "loc": [-83.676634, 32.842393], "pop": 38186, "state": "GA", "_id": "31204"} -{"city": "WILSON AIRPORT", "loc": [-83.682303, 32.780758], "pop": 42528, "state": "GA", "_id": "31206"} -{"city": "MACON", "loc": [-83.745537, 32.892565], "pop": 31255, "state": "GA", "_id": "31210"} -{"city": "MACON", "loc": [-83.602062, 32.886905], "pop": 16668, "state": "GA", "_id": "31211"} -{"city": "ALLENHURST", "loc": [-81.618577, 31.774059], "pop": 3075, "state": "GA", "_id": "31301"} -{"city": "BLOOMINGDALE", "loc": [-81.308465, 32.117654], "pop": 3297, "state": "GA", "_id": "31302"} -{"city": "CLYO", "loc": [-81.30857, 32.512646], "pop": 400, "state": "GA", "_id": "31303"} -{"city": "CRESCENT", "loc": [-81.389219, 31.49675], "pop": 2362, "state": "GA", "_id": "31304"} -{"city": "DARIEN", "loc": [-81.431175, 31.382574], "pop": 3067, "state": "GA", "_id": "31305"} -{"city": "ELLABELL", "loc": [-81.498302, 32.127304], "pop": 1309, "state": "GA", "_id": "31308"} -{"city": "FLEMING", "loc": [-81.423165, 31.864477], "pop": 548, "state": "GA", "_id": "31309"} -{"city": "GUYTON", "loc": [-81.389593, 32.31399], "pop": 4025, "state": "GA", "_id": "31312"} -{"city": "HINESVILLE", "loc": [-81.607214, 31.851296], "pop": 42962, "state": "GA", "_id": "31313"} -{"city": "FORT STEWART", "loc": [-81.440489, 31.979085], "pop": 0, "state": "GA", "_id": "31314"} -{"city": "LUDOWICI", "loc": [-81.745315, 31.770477], "pop": 6139, "state": "GA", "_id": "31316"} -{"city": "MERIDIAN", "loc": [-81.423915, 31.411068], "pop": 672, "state": "GA", "_id": "31319"} -{"city": "MIDWAY", "loc": [-81.390897, 31.801803], "pop": 4486, "state": "GA", "_id": "31320"} -{"city": "PEMBROKE", "loc": [-81.553341, 32.157088], "pop": 6571, "state": "GA", "_id": "31321"} -{"city": "POOLER", "loc": [-81.251958, 32.114931], "pop": 4036, "state": "GA", "_id": "31322"} -{"city": "RICEBORO", "loc": [-81.467134, 31.735696], "pop": 1737, "state": "GA", "_id": "31323"} -{"city": "RICHMOND HILL", "loc": [-81.294026, 31.896152], "pop": 7455, "state": "GA", "_id": "31324"} -{"city": "RINCON", "loc": [-81.287777, 32.235632], "pop": 14502, "state": "GA", "_id": "31326"} -{"city": "SAPELO ISLAND", "loc": [-81.267933, 31.421948], "pop": 120, "state": "GA", "_id": "31327"} -{"city": "TYBEE ISLAND", "loc": [-80.850938, 32.006797], "pop": 3106, "state": "GA", "_id": "31328"} -{"city": "STILLWELL", "loc": [-81.326435, 32.4121], "pop": 6761, "state": "GA", "_id": "31329"} -{"city": "TOWNSEND", "loc": [-81.418204, 31.567339], "pop": 2413, "state": "GA", "_id": "31331"} -{"city": "SAVANNAH", "loc": [-81.102394, 32.067631], "pop": 37544, "state": "GA", "_id": "31401"} -{"city": "STATE COLLEGE", "loc": [-81.068704, 32.044178], "pop": 33927, "state": "GA", "_id": "31404"} -{"city": "SAVANNAH", "loc": [-81.124192, 32.039119], "pop": 28739, "state": "GA", "_id": "31405"} -{"city": "SAVANNAH", "loc": [-81.097893, 31.988993], "pop": 34024, "state": "GA", "_id": "31406"} -{"city": "PORT WENTWORTH", "loc": [-81.162891, 32.148075], "pop": 2883, "state": "GA", "_id": "31407"} -{"city": "GARDEN CITY", "loc": [-81.168181, 32.109245], "pop": 12548, "state": "GA", "_id": "31408"} -{"city": "SAVANNAH", "loc": [-81.158371, 32.002104], "pop": 3509, "state": "GA", "_id": "31409"} -{"city": "SAVANNAH", "loc": [-80.983859, 32.016188], "pop": 15808, "state": "GA", "_id": "31410"} -{"city": "SAVANNAH", "loc": [-81.038074, 31.926801], "pop": 4707, "state": "GA", "_id": "31411"} -{"city": "M M", "loc": [-81.177387, 31.985149], "pop": 32901, "state": "GA", "_id": "31419"} -{"city": "OKEFENOKEE", "loc": [-82.364512, 31.219686], "pop": 33068, "state": "GA", "_id": "31501"} -{"city": "ALMA", "loc": [-82.4633, 31.546525], "pop": 9566, "state": "GA", "_id": "31510"} -{"city": "AMBROSE", "loc": [-82.997637, 31.536712], "pop": 2853, "state": "GA", "_id": "31512"} -{"city": "BAXLEY", "loc": [-82.348643, 31.783663], "pop": 14099, "state": "GA", "_id": "31513"} -{"city": "BLACKSHEAR", "loc": [-82.261708, 31.293063], "pop": 9612, "state": "GA", "_id": "31516"} -{"city": "BRISTOL", "loc": [-82.249594, 31.40278], "pop": 996, "state": "GA", "_id": "31518"} -{"city": "BROXTON", "loc": [-82.904954, 31.648426], "pop": 2915, "state": "GA", "_id": "31519"} -{"city": "GLYNCO", "loc": [-81.493045, 31.169652], "pop": 21343, "state": "GA", "_id": "31520"} -{"city": "SAINT SIMONS ISL", "loc": [-81.382421, 31.16916], "pop": 12924, "state": "GA", "_id": "31522"} -{"city": "BRUNSWICK", "loc": [-81.511365, 31.230411], "pop": 27079, "state": "GA", "_id": "31525"} -{"city": "JEKYLL ISLAND", "loc": [-81.41281, 31.074049], "pop": 1150, "state": "GA", "_id": "31527"} -{"city": "DENTON", "loc": [-82.720146, 31.745842], "pop": 1457, "state": "GA", "_id": "31532"} -{"city": "DOUGLAS", "loc": [-82.846468, 31.497287], "pop": 19607, "state": "GA", "_id": "31533"} -{"city": "FOLKSTON", "loc": [-82.011617, 30.850838], "pop": 6486, "state": "GA", "_id": "31537"} -{"city": "HAZLEHURST", "loc": [-82.590947, 31.860569], "pop": 10577, "state": "GA", "_id": "31539"} -{"city": "HOBOKEN", "loc": [-82.183847, 31.183777], "pop": 3481, "state": "GA", "_id": "31542"} -{"city": "HORTENSE", "loc": [-81.959561, 31.319949], "pop": 968, "state": "GA", "_id": "31543"} -{"city": "JACKSONVILLE", "loc": [-82.975003, 31.848079], "pop": 905, "state": "GA", "_id": "31544"} -{"city": "JESUP", "loc": [-81.88706, 31.604326], "pop": 17180, "state": "GA", "_id": "31545"} -{"city": "KINGSLAND", "loc": [-81.707483, 30.797681], "pop": 8781, "state": "GA", "_id": "31548"} -{"city": "LUMBER CITY", "loc": [-82.707312, 31.925124], "pop": 2036, "state": "GA", "_id": "31549"} -{"city": "MANOR", "loc": [-82.574173, 31.108829], "pop": 794, "state": "GA", "_id": "31550"} -{"city": "MERSHON", "loc": [-82.216995, 31.478279], "pop": 365, "state": "GA", "_id": "31551"} -{"city": "MILLWOOD", "loc": [-82.644148, 31.250566], "pop": 500, "state": "GA", "_id": "31552"} -{"city": "NAHUNTA", "loc": [-81.972212, 31.182652], "pop": 3158, "state": "GA", "_id": "31553"} -{"city": "NICHOLLS", "loc": [-82.603207, 31.449812], "pop": 3615, "state": "GA", "_id": "31554"} -{"city": "ODUM", "loc": [-81.994301, 31.699916], "pop": 2851, "state": "GA", "_id": "31555"} -{"city": "PATTERSON", "loc": [-82.127444, 31.390338], "pop": 2355, "state": "GA", "_id": "31557"} -{"city": "SAINT MARYS", "loc": [-81.565211, 30.773467], "pop": 15655, "state": "GA", "_id": "31558"} -{"city": "SCREVEN", "loc": [-82.039742, 31.516846], "pop": 2325, "state": "GA", "_id": "31560"} -{"city": "SURRENCY", "loc": [-82.198218, 31.648931], "pop": 1643, "state": "GA", "_id": "31563"} -{"city": "WAVERLY", "loc": [-81.56967, 31.042672], "pop": 331, "state": "GA", "_id": "31565"} -{"city": "WAYNESVILLE", "loc": [-81.803928, 31.244792], "pop": 3248, "state": "GA", "_id": "31566"} -{"city": "WEST GREEN", "loc": [-82.756417, 31.592271], "pop": 1940, "state": "GA", "_id": "31567"} -{"city": "WHITE OAK", "loc": [-81.808237, 31.034037], "pop": 892, "state": "GA", "_id": "31568"} -{"city": "WOODBINE", "loc": [-81.678313, 30.943692], "pop": 4508, "state": "GA", "_id": "31569"} -{"city": "CLYATTVILLE", "loc": [-83.277166, 30.810578], "pop": 32232, "state": "GA", "_id": "31601"} -{"city": "BEMISS", "loc": [-83.273299, 30.890268], "pop": 32292, "state": "GA", "_id": "31602"} -{"city": "ADEL", "loc": [-83.421346, 31.125143], "pop": 9661, "state": "GA", "_id": "31620"} -{"city": "ALAPAHA", "loc": [-83.21321, 31.394027], "pop": 1675, "state": "GA", "_id": "31622"} -{"city": "AXSON", "loc": [-82.731945, 31.303455], "pop": 912, "state": "GA", "_id": "31624"} -{"city": "BARNEY", "loc": [-83.521934, 31.007424], "pop": 895, "state": "GA", "_id": "31625"} -{"city": "BOSTON", "loc": [-83.797085, 30.785547], "pop": 2808, "state": "GA", "_id": "31626"} -{"city": "DIXIE", "loc": [-83.67918, 30.772213], "pop": 1462, "state": "GA", "_id": "31629"} -{"city": "DU PONT", "loc": [-82.855518, 30.999996], "pop": 502, "state": "GA", "_id": "31630"} -{"city": "FARGO", "loc": [-82.580056, 30.716587], "pop": 560, "state": "GA", "_id": "31631"} -{"city": "HAHIRA", "loc": [-83.357366, 30.941593], "pop": 6921, "state": "GA", "_id": "31632"} -{"city": "COGDELL", "loc": [-82.743103, 31.044991], "pop": 5098, "state": "GA", "_id": "31634"} -{"city": "LAKELAND", "loc": [-83.088859, 31.038107], "pop": 4289, "state": "GA", "_id": "31635"} -{"city": "LAKE PARK", "loc": [-83.175293, 30.690615], "pop": 4491, "state": "GA", "_id": "31636"} -{"city": "LENOX", "loc": [-83.448135, 31.266405], "pop": 1993, "state": "GA", "_id": "31637"} -{"city": "MORVEN", "loc": [-83.449796, 30.893733], "pop": 3416, "state": "GA", "_id": "31638"} -{"city": "NASHVILLE", "loc": [-83.231946, 31.207379], "pop": 8396, "state": "GA", "_id": "31639"} -{"city": "NAYLOR", "loc": [-83.122368, 30.89846], "pop": 1110, "state": "GA", "_id": "31641"} -{"city": "PEARSON", "loc": [-82.859096, 31.310583], "pop": 3590, "state": "GA", "_id": "31642"} -{"city": "QUITMAN", "loc": [-83.556748, 30.779699], "pop": 8028, "state": "GA", "_id": "31643"} -{"city": "RAY CITY", "loc": [-83.214283, 31.08247], "pop": 1859, "state": "GA", "_id": "31645"} -{"city": "SAINT GEORGE", "loc": [-82.083367, 30.559161], "pop": 2010, "state": "GA", "_id": "31646"} -{"city": "SPARKS", "loc": [-83.447586, 31.178984], "pop": 1772, "state": "GA", "_id": "31647"} -{"city": "STATENVILLE", "loc": [-82.979863, 30.725503], "pop": 1269, "state": "GA", "_id": "31648"} -{"city": "STOCKTON", "loc": [-83.013944, 31.022865], "pop": 1242, "state": "GA", "_id": "31649"} -{"city": "WILLACOOCHEE", "loc": [-83.044931, 31.345481], "pop": 1704, "state": "GA", "_id": "31650"} -{"city": "ALBANY", "loc": [-84.161923, 31.567783], "pop": 25698, "state": "GA", "_id": "31701"} -{"city": "MARINE CORPS LOG", "loc": [-84.050812, 31.550099], "pop": 1306, "state": "GA", "_id": "31704"} -{"city": "BRIDGEBORO", "loc": [-84.090089, 31.550851], "pop": 35997, "state": "GA", "_id": "31705"} -{"city": "ALBANY", "loc": [-84.211834, 31.578908], "pop": 36439, "state": "GA", "_id": "31707"} -{"city": "GEORGIA SOUTHWES", "loc": [-84.224729, 32.07077], "pop": 23590, "state": "GA", "_id": "31709"} -{"city": "ANDERSONVILLE", "loc": [-84.10101, 32.129907], "pop": 984, "state": "GA", "_id": "31711"} -{"city": "ARABI", "loc": [-83.72856, 31.862923], "pop": 1349, "state": "GA", "_id": "31712"} -{"city": "ARLINGTON", "loc": [-84.72572, 31.445597], "pop": 1402, "state": "GA", "_id": "31713"} -{"city": "ASHBURN", "loc": [-83.660775, 31.705877], "pop": 6047, "state": "GA", "_id": "31714"} -{"city": "ATTAPULGUS", "loc": [-84.486008, 30.750639], "pop": 3131, "state": "GA", "_id": "31715"} -{"city": "BACONTON", "loc": [-84.11345, 31.387804], "pop": 2566, "state": "GA", "_id": "31716"} -{"city": "BAINBRIDGE", "loc": [-84.573975, 30.897865], "pop": 17739, "state": "GA", "_id": "31717"} -{"city": "BLAKELY", "loc": [-84.935285, 31.371854], "pop": 8405, "state": "GA", "_id": "31723"} -{"city": "BLUFFTON", "loc": [-84.877175, 31.556189], "pop": 799, "state": "GA", "_id": "31724"} -{"city": "BRINSON", "loc": [-84.667112, 30.960957], "pop": 2877, "state": "GA", "_id": "31725"} -{"city": "BRONWOOD", "loc": [-84.359389, 31.821591], "pop": 1075, "state": "GA", "_id": "31726"} -{"city": "CAIRO", "loc": [-84.196246, 30.892462], "pop": 15393, "state": "GA", "_id": "31728"} -{"city": "CALVARY", "loc": [-84.32267, 30.747036], "pop": 1442, "state": "GA", "_id": "31729"} -{"city": "CAMILLA", "loc": [-84.229683, 31.219915], "pop": 9207, "state": "GA", "_id": "31730"} -{"city": "CHULA", "loc": [-83.550864, 31.543855], "pop": 2425, "state": "GA", "_id": "31733"} -{"city": "CLIMAX", "loc": [-84.443217, 30.85731], "pop": 1764, "state": "GA", "_id": "31734"} -{"city": "COBB", "loc": [-83.958087, 31.961708], "pop": 881, "state": "GA", "_id": "31735"} -{"city": "COLEMAN", "loc": [-84.874471, 31.656831], "pop": 359, "state": "GA", "_id": "31736"} -{"city": "COLQUITT", "loc": [-84.730896, 31.161871], "pop": 6277, "state": "GA", "_id": "31737"} -{"city": "COOLIDGE", "loc": [-83.875245, 30.985908], "pop": 1970, "state": "GA", "_id": "31738"} -{"city": "CUTHBERT", "loc": [-84.788874, 31.769073], "pop": 5708, "state": "GA", "_id": "31740"} -{"city": "DAMASCUS", "loc": [-84.720047, 31.341528], "pop": 1534, "state": "GA", "_id": "31741"} -{"city": "GRAVES", "loc": [-84.430782, 31.759846], "pop": 8731, "state": "GA", "_id": "31742"} -{"city": "DE SOTO", "loc": [-84.027101, 31.950186], "pop": 112, "state": "GA", "_id": "31743"} -{"city": "DOERUN", "loc": [-83.925316, 31.313647], "pop": 1738, "state": "GA", "_id": "31744"} -{"city": "DONALSONVILLE", "loc": [-84.887024, 30.981801], "pop": 7468, "state": "GA", "_id": "31745"} -{"city": "EDISON", "loc": [-84.745626, 31.564979], "pop": 1711, "state": "GA", "_id": "31746"} -{"city": "ENIGMA", "loc": [-83.355157, 31.373599], "pop": 2223, "state": "GA", "_id": "31749"} -{"city": "FITZGERALD", "loc": [-83.249534, 31.724769], "pop": 16245, "state": "GA", "_id": "31750"} -{"city": "FORT GAINES", "loc": [-85.039375, 31.646353], "pop": 2565, "state": "GA", "_id": "31751"} -{"city": "GEORGETOWN", "loc": [-85.071137, 31.884763], "pop": 1415, "state": "GA", "_id": "31754"} -{"city": "HARTSFIELD", "loc": [-83.970364, 31.217316], "pop": 285, "state": "GA", "_id": "31756"} -{"city": "IRON CITY", "loc": [-84.796632, 30.994728], "pop": 1542, "state": "GA", "_id": "31759"} -{"city": "IRWINVILLE", "loc": [-83.401708, 31.654238], "pop": 1887, "state": "GA", "_id": "31760"} -{"city": "JAKIN", "loc": [-84.994845, 31.175064], "pop": 1918, "state": "GA", "_id": "31761"} -{"city": "LEARY", "loc": [-84.509486, 31.505924], "pop": 1017, "state": "GA", "_id": "31762"} -{"city": "LEESBURG", "loc": [-84.159263, 31.681161], "pop": 14641, "state": "GA", "_id": "31763"} -{"city": "LESLIE", "loc": [-84.07834, 31.953952], "pop": 1273, "state": "GA", "_id": "31764"} -{"city": "MEIGS", "loc": [-84.082375, 31.062536], "pop": 1705, "state": "GA", "_id": "31765"} -{"city": "MORGAN", "loc": [-84.617798, 31.556557], "pop": 883, "state": "GA", "_id": "31766"} -{"city": "SPRINGVALE", "loc": [-85.057251, 31.823432], "pop": 850, "state": "GA", "_id": "31767"} -{"city": "MOULTRIE", "loc": [-83.764089, 31.179244], "pop": 34351, "state": "GA", "_id": "31768"} -{"city": "NEWTON", "loc": [-84.441107, 31.313426], "pop": 3615, "state": "GA", "_id": "31770"} -{"city": "NORMAN PARK", "loc": [-83.94044, 31.069128], "pop": 301, "state": "GA", "_id": "31771"} -{"city": "OAKFIELD", "loc": [-83.97059, 31.779768], "pop": 149, "state": "GA", "_id": "31772"} -{"city": "OCHLOCKNEE", "loc": [-84.0326, 30.959407], "pop": 2665, "state": "GA", "_id": "31773"} -{"city": "OCILLA", "loc": [-83.256542, 31.592944], "pop": 5784, "state": "GA", "_id": "31774"} -{"city": "OMEGA", "loc": [-83.593936, 31.360432], "pop": 2154, "state": "GA", "_id": "31775"} -{"city": "PARROTT", "loc": [-84.501152, 31.872016], "pop": 886, "state": "GA", "_id": "31777"} -{"city": "PAVO", "loc": [-83.740856, 30.940142], "pop": 3573, "state": "GA", "_id": "31778"} -{"city": "PELHAM", "loc": [-84.156367, 31.127233], "pop": 7123, "state": "GA", "_id": "31779"} -{"city": "PLAINS", "loc": [-84.358638, 32.033908], "pop": 3377, "state": "GA", "_id": "31780"} -{"city": "POULAN", "loc": [-83.761554, 31.467206], "pop": 3119, "state": "GA", "_id": "31781"} -{"city": "REBECCA", "loc": [-83.523478, 31.797921], "pop": 630, "state": "GA", "_id": "31783"} -{"city": "SALE CITY", "loc": [-84.042195, 31.260004], "pop": 1379, "state": "GA", "_id": "31784"} -{"city": "SHELLMAN", "loc": [-84.616616, 31.743407], "pop": 1900, "state": "GA", "_id": "31786"} -{"city": "SMITHVILLE", "loc": [-84.227066, 31.884692], "pop": 1570, "state": "GA", "_id": "31787"} -{"city": "SUMNER", "loc": [-83.724463, 31.53925], "pop": 1125, "state": "GA", "_id": "31789"} -{"city": "SYCAMORE", "loc": [-83.606363, 31.655329], "pop": 2026, "state": "GA", "_id": "31790"} -{"city": "SYLVESTER", "loc": [-83.860731, 31.539268], "pop": 9573, "state": "GA", "_id": "31791"} -{"city": "THOMASVILLE", "loc": [-83.969616, 30.838543], "pop": 28319, "state": "GA", "_id": "31792"} -{"city": "ABAC", "loc": [-83.498867, 31.451722], "pop": 27906, "state": "GA", "_id": "31794"} -{"city": "TY TY", "loc": [-83.621989, 31.45595], "pop": 2513, "state": "GA", "_id": "31795"} -{"city": "WARWICK", "loc": [-83.920092, 31.734495], "pop": 2650, "state": "GA", "_id": "31796"} -{"city": "WHIGHAM", "loc": [-84.315771, 30.90701], "pop": 2987, "state": "GA", "_id": "31797"} -{"city": "WRAY", "loc": [-83.107484, 31.595229], "pop": 978, "state": "GA", "_id": "31798"} -{"city": "JUNIPER", "loc": [-84.612951, 32.565562], "pop": 1407, "state": "GA", "_id": "31801"} -{"city": "TAZEWELL", "loc": [-84.527347, 32.354198], "pop": 5590, "state": "GA", "_id": "31803"} -{"city": "CATAULA", "loc": [-84.920691, 32.624246], "pop": 1108, "state": "GA", "_id": "31804"} -{"city": "CUSSETA", "loc": [-84.764537, 32.299026], "pop": 2290, "state": "GA", "_id": "31805"} -{"city": "ELLAVILLE", "loc": [-84.303868, 32.23901], "pop": 3599, "state": "GA", "_id": "31806"} -{"city": "ELLERSLIE", "loc": [-84.877197, 32.661718], "pop": 2153, "state": "GA", "_id": "31807"} -{"city": "FORTSON", "loc": [-85.001654, 32.628841], "pop": 1780, "state": "GA", "_id": "31808"} -{"city": "HAMILTON", "loc": [-84.884753, 32.741795], "pop": 1587, "state": "GA", "_id": "31811"} -{"city": "JUNCTION CITY", "loc": [-84.45741, 32.608046], "pop": 472, "state": "GA", "_id": "31812"} -{"city": "LUMPKIN", "loc": [-84.802227, 32.043465], "pop": 2144, "state": "GA", "_id": "31815"} -{"city": "MANCHESTER", "loc": [-84.631166, 32.8721], "pop": 7721, "state": "GA", "_id": "31816"} -{"city": "MIDLAND", "loc": [-84.855851, 32.561587], "pop": 4725, "state": "GA", "_id": "31820"} -{"city": "OMAHA", "loc": [-84.900792, 32.165234], "pop": 884, "state": "GA", "_id": "31821"} -{"city": "PINE MOUNTAIN", "loc": [-84.895953, 32.873488], "pop": 3548, "state": "GA", "_id": "31822"} -{"city": "PINE MOUNTAIN VA", "loc": [-84.823874, 32.791849], "pop": 887, "state": "GA", "_id": "31823"} -{"city": "PRESTON", "loc": [-84.548918, 32.074031], "pop": 1690, "state": "GA", "_id": "31824"} -{"city": "RICHLAND", "loc": [-84.666724, 32.084578], "pop": 2626, "state": "GA", "_id": "31825"} -{"city": "SHILOH", "loc": [-84.741171, 32.806678], "pop": 1478, "state": "GA", "_id": "31826"} -{"city": "TALBOTTON", "loc": [-84.546206, 32.679702], "pop": 2324, "state": "GA", "_id": "31827"} -{"city": "UPATOI", "loc": [-84.744819, 32.560057], "pop": 725, "state": "GA", "_id": "31829"} -{"city": "WARM SPRINGS", "loc": [-84.821728, 32.895558], "pop": 819, "state": "GA", "_id": "31830"} -{"city": "WAVERLY HALL", "loc": [-84.742463, 32.679326], "pop": 2323, "state": "GA", "_id": "31831"} -{"city": "WESTON", "loc": [-84.575579, 31.963665], "pop": 573, "state": "GA", "_id": "31832"} -{"city": "WEST POINT", "loc": [-85.119714, 32.833683], "pop": 8499, "state": "GA", "_id": "31833"} -{"city": "WOODLAND", "loc": [-84.595187, 32.806066], "pop": 2377, "state": "GA", "_id": "31836"} -{"city": "COLUMBUS", "loc": [-84.979456, 32.473035], "pop": 9694, "state": "GA", "_id": "31901"} -{"city": "COLUMBUS", "loc": [-84.948127, 32.424513], "pop": 25362, "state": "GA", "_id": "31903"} -{"city": "COLUMBUS", "loc": [-84.978475, 32.516091], "pop": 29254, "state": "GA", "_id": "31904"} -{"city": "CUSTER TERRACE", "loc": [-84.945264, 32.369728], "pop": 22869, "state": "GA", "_id": "31905"} -{"city": "COLUMBUS", "loc": [-84.948422, 32.463819], "pop": 26061, "state": "GA", "_id": "31906"} -{"city": "COLUMBUS", "loc": [-84.89799, 32.477909], "pop": 54915, "state": "GA", "_id": "31907"} -{"city": "COLUMBUS", "loc": [-84.927404, 32.536913], "pop": 20880, "state": "GA", "_id": "31909"} -{"city": "PINETTA", "loc": [-83.310491, 30.634794], "pop": 0, "state": "GA", "_id": "32350"} -{"city": "AIEA", "loc": [-157.933237, 21.390795], "pop": 43273, "state": "HI", "_id": "96701"} -{"city": "CAPTAIN COOK", "loc": [-155.887463, 19.438604], "pop": 5338, "state": "HI", "_id": "96704"} -{"city": "ELEELE", "loc": [-159.538115, 21.923017], "pop": 6466, "state": "HI", "_id": "96705"} -{"city": "EWA BEACH", "loc": [-158.010307, 21.327418], "pop": 26089, "state": "HI", "_id": "96706"} -{"city": "KAPOLEI", "loc": [-158.087007, 21.345284], "pop": 15891, "state": "HI", "_id": "96707"} -{"city": "HAIKU", "loc": [-156.299983, 20.907097], "pop": 5695, "state": "HI", "_id": "96708"} -{"city": "HAKALAU", "loc": [-155.133335, 19.888217], "pop": 198, "state": "HI", "_id": "96710"} -{"city": "HALEIWA", "loc": [-158.069315, 21.631151], "pop": 7870, "state": "HI", "_id": "96712"} -{"city": "HANA", "loc": [-156.039659, 20.761635], "pop": 1895, "state": "HI", "_id": "96713"} -{"city": "HANAPEPE", "loc": [-159.592022, 21.915644], "pop": 1523, "state": "HI", "_id": "96716"} -{"city": "HAUULA", "loc": [-157.915704, 21.61395], "pop": 3477, "state": "HI", "_id": "96717"} -{"city": "HAWAII NATIONAL", "loc": [-155.284015, 19.431103], "pop": 91, "state": "HI", "_id": "96718"} -{"city": "HAWI", "loc": [-155.838007, 20.238021], "pop": 1741, "state": "HI", "_id": "96719"} -{"city": "HILO", "loc": [-155.093921, 19.702522], "pop": 40158, "state": "HI", "_id": "96720"} -{"city": "PRINCEVILLE", "loc": [-159.462587, 22.215948], "pop": 4631, "state": "HI", "_id": "96722"} -{"city": "HOLUALOA", "loc": [-155.917639, 19.610316], "pop": 2096, "state": "HI", "_id": "96725"} -{"city": "HONAUNAU", "loc": [-155.893356, 19.44845], "pop": 1583, "state": "HI", "_id": "96726"} -{"city": "HONOKAA", "loc": [-155.488026, 20.08266], "pop": 3681, "state": "HI", "_id": "96727"} -{"city": "HONOMU", "loc": [-155.11766, 19.872767], "pop": 548, "state": "HI", "_id": "96728"} -{"city": "HOOLEHUA", "loc": [-157.079138, 21.173025], "pop": 853, "state": "HI", "_id": "96729"} -{"city": "KAAAWA", "loc": [-157.873734, 21.56737], "pop": 2305, "state": "HI", "_id": "96730"} -{"city": "KAHULUI", "loc": [-156.478327, 20.881388], "pop": 17289, "state": "HI", "_id": "96732"} -{"city": "KAILUA", "loc": [-157.744781, 21.406262], "pop": 53403, "state": "HI", "_id": "96734"} -{"city": "KAILUA KONA", "loc": [-155.979809, 19.653053], "pop": 19616, "state": "HI", "_id": "96740"} -{"city": "KALAUPAPA", "loc": [-156.983453, 21.19289], "pop": 130, "state": "HI", "_id": "96742"} -{"city": "KAMUELA", "loc": [-155.705189, 20.008128], "pop": 9140, "state": "HI", "_id": "96743"} -{"city": "KANEOHE", "loc": [-157.811543, 21.422819], "pop": 55236, "state": "HI", "_id": "96744"} -{"city": "KAPAA", "loc": [-159.344842, 22.086798], "pop": 15627, "state": "HI", "_id": "96746"} -{"city": "KAUMAKANI", "loc": [-159.62413, 21.921329], "pop": 819, "state": "HI", "_id": "96747"} -{"city": "KAUNAKAKAI", "loc": [-156.969015, 21.090504], "pop": 4419, "state": "HI", "_id": "96748"} -{"city": "KEAAU", "loc": [-154.992644, 19.589277], "pop": 4297, "state": "HI", "_id": "96749"} -{"city": "KEALAKEKUA", "loc": [-155.930025, 19.526149], "pop": 1309, "state": "HI", "_id": "96750"} -{"city": "KEKAHA", "loc": [-159.71988, 21.973509], "pop": 3785, "state": "HI", "_id": "96752"} -{"city": "KIHEI", "loc": [-156.447543, 20.744124], "pop": 14759, "state": "HI", "_id": "96753"} -{"city": "KAPAAU", "loc": [-155.798981, 20.218323], "pop": 2550, "state": "HI", "_id": "96755"} -{"city": "KOLOA", "loc": [-159.474927, 21.908293], "pop": 4906, "state": "HI", "_id": "96756"} -{"city": "KUALAPUU", "loc": [-157.027669, 21.160097], "pop": 818, "state": "HI", "_id": "96757"} -{"city": "KURTISTOWN", "loc": [-155.020659, 19.570637], "pop": 3975, "state": "HI", "_id": "96760"} -{"city": "LAHAINA", "loc": [-156.677162, 20.917432], "pop": 14508, "state": "HI", "_id": "96761"} -{"city": "LAIE", "loc": [-157.939377, 21.659513], "pop": 8481, "state": "HI", "_id": "96762"} -{"city": "LANAI CITY", "loc": [-156.921027, 20.829323], "pop": 2426, "state": "HI", "_id": "96763"} -{"city": "LAUPAHOEHOE", "loc": [-155.232263, 19.980194], "pop": 1015, "state": "HI", "_id": "96764"} -{"city": "LIHUE", "loc": [-159.368258, 21.981618], "pop": 10663, "state": "HI", "_id": "96766"} -{"city": "MAKAWAO", "loc": [-156.332735, 20.846932], "pop": 13389, "state": "HI", "_id": "96768"} -{"city": "MAKAWELI", "loc": [-159.790721, 21.927639], "pop": 797, "state": "HI", "_id": "96769"} -{"city": "MAUNALOA", "loc": [-157.219277, 21.142202], "pop": 497, "state": "HI", "_id": "96770"} -{"city": "MOUNTAIN VIEW", "loc": [-155.086436, 19.550587], "pop": 3170, "state": "HI", "_id": "96771"} -{"city": "NAALEHU", "loc": [-155.657474, 19.066844], "pop": 2729, "state": "HI", "_id": "96772"} -{"city": "NINOLE", "loc": [-155.159923, 19.904436], "pop": 0, "state": "HI", "_id": "96773"} -{"city": "OOKALA", "loc": [-155.274666, 20.011887], "pop": 315, "state": "HI", "_id": "96774"} -{"city": "PAAUHAU", "loc": [-155.449088, 20.027748], "pop": 917, "state": "HI", "_id": "96775"} -{"city": "PAAUILO", "loc": [-155.369728, 20.027119], "pop": 947, "state": "HI", "_id": "96776"} -{"city": "PAHALA", "loc": [-155.481506, 19.207898], "pop": 1616, "state": "HI", "_id": "96777"} -{"city": "PAHOA", "loc": [-154.923135, 19.508901], "pop": 6702, "state": "HI", "_id": "96778"} -{"city": "PAIA", "loc": [-156.38017, 20.91539], "pop": 2311, "state": "HI", "_id": "96779"} -{"city": "PAPAALOA", "loc": [-155.218402, 19.904835], "pop": 208, "state": "HI", "_id": "96780"} -{"city": "PAPAIKOU", "loc": [-155.098442, 19.791643], "pop": 1700, "state": "HI", "_id": "96781"} -{"city": "PEARL CITY", "loc": [-157.965164, 21.408393], "pop": 38207, "state": "HI", "_id": "96782"} -{"city": "PEPEEKEO", "loc": [-155.112994, 19.835283], "pop": 2038, "state": "HI", "_id": "96783"} -{"city": "VOLCANO", "loc": [-155.19743, 19.480066], "pop": 2639, "state": "HI", "_id": "96785"} -{"city": "WAHIAWA", "loc": [-158.043527, 21.500596], "pop": 43663, "state": "HI", "_id": "96786"} -{"city": "MILILANI", "loc": [-158.017379, 21.45311], "pop": 34734, "state": "HI", "_id": "96789"} -{"city": "KULA", "loc": [-156.326026, 20.753353], "pop": 5697, "state": "HI", "_id": "96790"} -{"city": "WAIALUA", "loc": [-158.126673, 21.576623], "pop": 7975, "state": "HI", "_id": "96791"} -{"city": "WAIANAE", "loc": [-158.178071, 21.435192], "pop": 37518, "state": "HI", "_id": "96792"} -{"city": "WAILUKU", "loc": [-156.503612, 20.896586], "pop": 15818, "state": "HI", "_id": "96793"} -{"city": "WAIMANALO", "loc": [-157.713094, 21.341786], "pop": 9055, "state": "HI", "_id": "96795"} -{"city": "WAIMEA", "loc": [-159.669429, 21.968487], "pop": 1960, "state": "HI", "_id": "96796"} -{"city": "WAIPAHU", "loc": [-158.012418, 21.398203], "pop": 52411, "state": "HI", "_id": "96797"} -{"city": "HONOLULU", "loc": [-157.852072, 21.317905], "pop": 23082, "state": "HI", "_id": "96813"} -{"city": "HONOLULU", "loc": [-157.843876, 21.299846], "pop": 14182, "state": "HI", "_id": "96814"} -{"city": "HONOLULU", "loc": [-157.826616, 21.281084], "pop": 28650, "state": "HI", "_id": "96815"} -{"city": "HONOLULU", "loc": [-157.800626, 21.288677], "pop": 49208, "state": "HI", "_id": "96816"} -{"city": "HONOLULU", "loc": [-157.861469, 21.329452], "pop": 48920, "state": "HI", "_id": "96817"} -{"city": "HONOLULU", "loc": [-157.926925, 21.353173], "pop": 62915, "state": "HI", "_id": "96818"} -{"city": "HONOLULU", "loc": [-157.875947, 21.34877], "pop": 50584, "state": "HI", "_id": "96819"} -{"city": "HONOLULU", "loc": [-157.755242, 21.292811], "pop": 18366, "state": "HI", "_id": "96821"} -{"city": "HONOLULU", "loc": [-157.829819, 21.311704], "pop": 39632, "state": "HI", "_id": "96822"} -{"city": "HONOLULU", "loc": [-157.698523, 21.298684], "pop": 27432, "state": "HI", "_id": "96825"} -{"city": "HONOLULU", "loc": [-157.828388, 21.294139], "pop": 33672, "state": "HI", "_id": "96826"} -{"city": "POCATELLO", "loc": [-112.438142, 42.887592], "pop": 33282, "state": "ID", "_id": "83201"} -{"city": "CHUBBUCK", "loc": [-112.474873, 42.926548], "pop": 11385, "state": "ID", "_id": "83202"} -{"city": "FORT HALL", "loc": [-112.459854, 42.988717], "pop": 1566, "state": "ID", "_id": "83203"} -{"city": "POCATELLO", "loc": [-112.443352, 42.846463], "pop": 15605, "state": "ID", "_id": "83204"} -{"city": "STERLING", "loc": [-112.818124, 42.976717], "pop": 2653, "state": "ID", "_id": "83210"} -{"city": "AMERICAN FALLS", "loc": [-112.870714, 42.789876], "pop": 5867, "state": "ID", "_id": "83211"} -{"city": "ARBON", "loc": [-112.558481, 42.502634], "pop": 121, "state": "ID", "_id": "83212"} -{"city": "ARCO", "loc": [-113.317559, 43.635521], "pop": 1823, "state": "ID", "_id": "83213"} -{"city": "ARIMO", "loc": [-112.174649, 42.559953], "pop": 333, "state": "ID", "_id": "83214"} -{"city": "BANCROFT", "loc": [-111.842944, 42.720463], "pop": 988, "state": "ID", "_id": "83217"} -{"city": "BERN", "loc": [-111.392595, 42.319144], "pop": 261, "state": "ID", "_id": "83220"} -{"city": "BLACKFOOT", "loc": [-112.361545, 43.194327], "pop": 18202, "state": "ID", "_id": "83221"} -{"city": "CHALLIS", "loc": [-114.19463, 44.496912], "pop": 2426, "state": "ID", "_id": "83226"} -{"city": "CLAYTON", "loc": [-114.410189, 44.273289], "pop": 41, "state": "ID", "_id": "83227"} -{"city": "CLIFTON", "loc": [-111.995737, 42.215972], "pop": 538, "state": "ID", "_id": "83228"} -{"city": "CONDA", "loc": [-111.54023, 42.717126], "pop": 21, "state": "ID", "_id": "83230"} -{"city": "DARLINGTON", "loc": [-113.380284, 43.7715], "pop": 12, "state": "ID", "_id": "83231"} -{"city": "DAYTON", "loc": [-111.985836, 42.11836], "pop": 659, "state": "ID", "_id": "83232"} -{"city": "DOWNEY", "loc": [-112.109019, 42.418127], "pop": 939, "state": "ID", "_id": "83234"} -{"city": "ELLIS", "loc": [-114.001594, 44.878829], "pop": 192, "state": "ID", "_id": "83235"} -{"city": "FIRTH", "loc": [-112.158819, 43.302066], "pop": 2878, "state": "ID", "_id": "83236"} -{"city": "FRANKLIN", "loc": [-111.822862, 42.030389], "pop": 1699, "state": "ID", "_id": "83237"} -{"city": "GENEVA", "loc": [-111.072185, 42.313585], "pop": 125, "state": "ID", "_id": "83238"} -{"city": "GRACE", "loc": [-111.739981, 42.549978], "pop": 2050, "state": "ID", "_id": "83241"} -{"city": "HOLBROOK", "loc": [-112.693404, 42.222148], "pop": 213, "state": "ID", "_id": "83243"} -{"city": "INKOM", "loc": [-112.246474, 42.796379], "pop": 823, "state": "ID", "_id": "83245"} -{"city": "LAVA HOT SPRINGS", "loc": [-112.017644, 42.618474], "pop": 512, "state": "ID", "_id": "83246"} -{"city": "MC CAMMON", "loc": [-112.175758, 42.63362], "pop": 2603, "state": "ID", "_id": "83250"} -{"city": "MACKAY", "loc": [-113.611984, 43.91106], "pop": 1207, "state": "ID", "_id": "83251"} -{"city": "MALAD CITY", "loc": [-112.262045, 42.180783], "pop": 3110, "state": "ID", "_id": "83252"} -{"city": "PATTERSON", "loc": [-113.916039, 44.701745], "pop": 210, "state": "ID", "_id": "83253"} -{"city": "MONTPELIER", "loc": [-111.31946, 42.35199], "pop": 4292, "state": "ID", "_id": "83254"} -{"city": "MOORE", "loc": [-113.260349, 43.782094], "pop": 1083, "state": "ID", "_id": "83255"} -{"city": "OVID", "loc": [-111.451109, 42.311423], "pop": 290, "state": "ID", "_id": "83260"} -{"city": "PARIS", "loc": [-111.402938, 42.207065], "pop": 852, "state": "ID", "_id": "83261"} -{"city": "PINGREE", "loc": [-112.449035, 43.195618], "pop": 7340, "state": "ID", "_id": "83262"} -{"city": "PRESTON", "loc": [-111.856516, 42.110917], "pop": 5402, "state": "ID", "_id": "83263"} -{"city": "ROCKLAND", "loc": [-112.853982, 42.555582], "pop": 478, "state": "ID", "_id": "83271"} -{"city": "SAINT CHARLES", "loc": [-111.389744, 42.112812], "pop": 199, "state": "ID", "_id": "83272"} -{"city": "SHELLEY", "loc": [-112.107549, 43.376901], "pop": 6164, "state": "ID", "_id": "83274"} -{"city": "SODA SPRINGS", "loc": [-111.569896, 42.671819], "pop": 3871, "state": "ID", "_id": "83276"} -{"city": "STANLEY", "loc": [-114.725414, 44.22908], "pop": 444, "state": "ID", "_id": "83278"} -{"city": "STONE", "loc": [-112.711473, 42.038983], "pop": 169, "state": "ID", "_id": "83280"} -{"city": "THATCHER", "loc": [-111.78899, 42.331959], "pop": 207, "state": "ID", "_id": "83283"} -{"city": "WAYAN", "loc": [-111.254056, 43.02691], "pop": 117, "state": "ID", "_id": "83285"} -{"city": "WESTON", "loc": [-111.97154, 42.044621], "pop": 727, "state": "ID", "_id": "83286"} -{"city": "FISH HAVEN", "loc": [-111.463323, 42.045926], "pop": 65, "state": "ID", "_id": "83287"} -{"city": "TWIN FALLS", "loc": [-114.469265, 42.556495], "pop": 34539, "state": "ID", "_id": "83301"} -{"city": "ROGERSON", "loc": [-114.603794, 42.219567], "pop": 92, "state": "ID", "_id": "83302"} -{"city": "BELLEVUE", "loc": [-114.249804, 43.439694], "pop": 2150, "state": "ID", "_id": "83313"} -{"city": "BLISS", "loc": [-114.910387, 42.944859], "pop": 845, "state": "ID", "_id": "83314"} -{"city": "BUHL", "loc": [-114.782545, 42.600763], "pop": 8014, "state": "ID", "_id": "83316"} -{"city": "BURLEY", "loc": [-113.793081, 42.524442], "pop": 12406, "state": "ID", "_id": "83318"} -{"city": "CAREY", "loc": [-113.892567, 43.274443], "pop": 820, "state": "ID", "_id": "83320"} -{"city": "CASTLEFORD", "loc": [-114.873433, 42.521015], "pop": 365, "state": "ID", "_id": "83321"} -{"city": "CORRAL", "loc": [-115.00871, 43.307127], "pop": 59, "state": "ID", "_id": "83322"} -{"city": "DECLO", "loc": [-113.644794, 42.524005], "pop": 2592, "state": "ID", "_id": "83323"} -{"city": "DIETRICH", "loc": [-114.266408, 42.91254], "pop": 178, "state": "ID", "_id": "83324"} -{"city": "EDEN", "loc": [-114.162762, 42.580374], "pop": 1762, "state": "ID", "_id": "83325"} -{"city": "ELBA", "loc": [-113.663559, 42.180865], "pop": 163, "state": "ID", "_id": "83326"} -{"city": "FAIRFIELD", "loc": [-114.790845, 43.367504], "pop": 668, "state": "ID", "_id": "83327"} -{"city": "FILER", "loc": [-114.614047, 42.565269], "pop": 4176, "state": "ID", "_id": "83328"} -{"city": "GOODING", "loc": [-114.711966, 42.937345], "pop": 4846, "state": "ID", "_id": "83330"} -{"city": "HAGERMAN", "loc": [-114.88697, 42.814205], "pop": 1613, "state": "ID", "_id": "83332"} -{"city": "HAILEY", "loc": [-114.306398, 43.523861], "pop": 4683, "state": "ID", "_id": "83333"} -{"city": "HANSEN", "loc": [-114.299364, 42.524895], "pop": 1525, "state": "ID", "_id": "83334"} -{"city": "HAZELTON", "loc": [-114.134984, 42.595462], "pop": 705, "state": "ID", "_id": "83335"} -{"city": "HEYBURN", "loc": [-113.770885, 42.559922], "pop": 4757, "state": "ID", "_id": "83336"} -{"city": "JEROME", "loc": [-114.501244, 42.71784], "pop": 12671, "state": "ID", "_id": "83338"} -{"city": "OBSIDIAN", "loc": [-114.373664, 43.675459], "pop": 5823, "state": "ID", "_id": "83340"} -{"city": "KIMBERLY", "loc": [-114.365725, 42.528656], "pop": 3779, "state": "ID", "_id": "83341"} -{"city": "NAF", "loc": [-113.448656, 42.364652], "pop": 2315, "state": "ID", "_id": "83342"} -{"city": "MINIDOKA", "loc": [-113.620033, 42.759784], "pop": 1379, "state": "ID", "_id": "83343"} -{"city": "MURTAUGH", "loc": [-114.160641, 42.477597], "pop": 1019, "state": "ID", "_id": "83344"} -{"city": "OAKLEY", "loc": [-113.906945, 42.347561], "pop": 2056, "state": "ID", "_id": "83346"} -{"city": "PAUL", "loc": [-113.797125, 42.623999], "pop": 3464, "state": "ID", "_id": "83347"} -{"city": "PICABO", "loc": [-114.086065, 43.310149], "pop": 76, "state": "ID", "_id": "83348"} -{"city": "RICHFIELD", "loc": [-114.15079, 43.058839], "pop": 789, "state": "ID", "_id": "83349"} -{"city": "ACEQUIA", "loc": [-113.66699, 42.621467], "pop": 9761, "state": "ID", "_id": "83350"} -{"city": "SHOSHONE", "loc": [-114.382176, 42.947353], "pop": 2341, "state": "ID", "_id": "83352"} -{"city": "WENDELL", "loc": [-114.715391, 42.757868], "pop": 4400, "state": "ID", "_id": "83355"} -{"city": "AMMON", "loc": [-111.990626, 43.517679], "pop": 27974, "state": "ID", "_id": "83401"} -{"city": "IDAHO FALLS", "loc": [-112.057762, 43.493373], "pop": 20716, "state": "ID", "_id": "83402"} -{"city": "IDAHO FALLS", "loc": [-112.012449, 43.475043], "pop": 14962, "state": "ID", "_id": "83404"} -{"city": "IDAHO FALLS", "loc": [-111.966052, 43.473233], "pop": 5935, "state": "ID", "_id": "83406"} -{"city": "ASHTON", "loc": [-111.619526, 43.988078], "pop": 8639, "state": "ID", "_id": "83420"} -{"city": "DRIGGS", "loc": [-111.119896, 43.726291], "pop": 1495, "state": "ID", "_id": "83422"} -{"city": "DUBOIS", "loc": [-112.325852, 44.185769], "pop": 650, "state": "ID", "_id": "83423"} -{"city": "FELT", "loc": [-111.189496, 43.872407], "pop": 40, "state": "ID", "_id": "83424"} -{"city": "HAMER", "loc": [-112.187189, 43.930751], "pop": 396, "state": "ID", "_id": "83425"} -{"city": "IONA", "loc": [-111.928356, 43.525946], "pop": 1491, "state": "ID", "_id": "83427"} -{"city": "ISLAND PARK", "loc": [-111.367914, 44.446606], "pop": 35, "state": "ID", "_id": "83429"} -{"city": "LEWISVILLE", "loc": [-112.018884, 43.672476], "pop": 1565, "state": "ID", "_id": "83431"} -{"city": "MENAN", "loc": [-111.983702, 43.726576], "pop": 1789, "state": "ID", "_id": "83434"} -{"city": "MONTEVIEW", "loc": [-112.578321, 43.986242], "pop": 441, "state": "ID", "_id": "83435"} -{"city": "NEWDALE", "loc": [-111.604192, 43.888078], "pop": 430, "state": "ID", "_id": "83436"} -{"city": "REXBURG", "loc": [-111.789022, 43.809968], "pop": 19157, "state": "ID", "_id": "83440"} -{"city": "RIGBY", "loc": [-111.900481, 43.671462], "pop": 8178, "state": "ID", "_id": "83442"} -{"city": "RIRIE", "loc": [-111.760692, 43.631961], "pop": 1749, "state": "ID", "_id": "83443"} -{"city": "ROBERTS", "loc": [-112.119591, 43.7116], "pop": 1436, "state": "ID", "_id": "83444"} -{"city": "SAINT ANTHONY", "loc": [-111.523156, 44.274499], "pop": 747, "state": "ID", "_id": "83445"} -{"city": "SPENCER", "loc": [-112.098821, 44.281444], "pop": 112, "state": "ID", "_id": "83446"} -{"city": "SUGAR CITY", "loc": [-111.79004, 43.866852], "pop": 4517, "state": "ID", "_id": "83448"} -{"city": "SWAN VALLEY", "loc": [-111.279358, 43.405826], "pop": 441, "state": "ID", "_id": "83449"} -{"city": "TERRETON", "loc": [-112.420041, 43.858635], "pop": 1537, "state": "ID", "_id": "83450"} -{"city": "TETON", "loc": [-111.668145, 43.898751], "pop": 1086, "state": "ID", "_id": "83451"} -{"city": "TETONIA", "loc": [-111.186997, 43.843713], "pop": 820, "state": "ID", "_id": "83452"} -{"city": "VICTOR", "loc": [-111.125934, 43.614827], "pop": 1084, "state": "ID", "_id": "83455"} -{"city": "CARMEN", "loc": [-113.857267, 45.255016], "pop": 195, "state": "ID", "_id": "83462"} -{"city": "GIBBONSVILLE", "loc": [-113.956466, 45.484608], "pop": 230, "state": "ID", "_id": "83463"} -{"city": "LEADORE", "loc": [-113.492586, 44.738909], "pop": 594, "state": "ID", "_id": "83464"} -{"city": "NORTH FORK", "loc": [-113.857287, 45.377605], "pop": 267, "state": "ID", "_id": "83466"} -{"city": "SALMON", "loc": [-113.878356, 45.157142], "pop": 5159, "state": "ID", "_id": "83467"} -{"city": "SHOUP", "loc": [-114.405987, 45.18514], "pop": 67, "state": "ID", "_id": "83469"} -{"city": "SOUTH GATE PLAZA", "loc": [-116.987714, 46.389457], "pop": 29650, "state": "ID", "_id": "83501"} -{"city": "AHSAHKA", "loc": [-116.371537, 46.510318], "pop": 335, "state": "ID", "_id": "83520"} -{"city": "COTTONWOOD", "loc": [-116.373306, 46.044789], "pop": 1791, "state": "ID", "_id": "83522"} -{"city": "CRAIGMONT", "loc": [-116.467655, 46.245292], "pop": 820, "state": "ID", "_id": "83523"} -{"city": "CULDESAC", "loc": [-116.653579, 46.378012], "pop": 1161, "state": "ID", "_id": "83524"} -{"city": "DIXIE", "loc": [-115.359158, 45.888897], "pop": 755, "state": "ID", "_id": "83525"} -{"city": "FERDINAND", "loc": [-116.39817, 46.134877], "pop": 323, "state": "ID", "_id": "83526"} -{"city": "GRANGEVILLE", "loc": [-116.107639, 45.927239], "pop": 4791, "state": "ID", "_id": "83530"} -{"city": "GREENCREEK", "loc": [-116.27239, 46.115523], "pop": 269, "state": "ID", "_id": "83533"} -{"city": "JULIAETTA", "loc": [-116.718849, 46.575376], "pop": 1014, "state": "ID", "_id": "83535"} -{"city": "KAMIAH", "loc": [-116.034742, 46.21856], "pop": 2970, "state": "ID", "_id": "83536"} -{"city": "KENDRICK", "loc": [-116.604895, 46.628628], "pop": 970, "state": "ID", "_id": "83537"} -{"city": "KEUTERVILLE", "loc": [-116.535583, 45.929443], "pop": 0, "state": "ID", "_id": "83538"} -{"city": "CLEARWATER", "loc": [-115.92396, 46.125859], "pop": 2704, "state": "ID", "_id": "83539"} -{"city": "LAPWAI", "loc": [-116.790225, 46.412403], "pop": 1784, "state": "ID", "_id": "83540"} -{"city": "LENORE", "loc": [-116.513015, 46.535408], "pop": 473, "state": "ID", "_id": "83541"} -{"city": "LUCILE", "loc": [-116.266899, 45.556963], "pop": 216, "state": "ID", "_id": "83542"} -{"city": "NEZPERCE", "loc": [-116.239281, 46.247533], "pop": 650, "state": "ID", "_id": "83543"} -{"city": "OROFINO", "loc": [-116.240417, 46.495197], "pop": 5738, "state": "ID", "_id": "83544"} -{"city": "PECK", "loc": [-116.411394, 46.480661], "pop": 295, "state": "ID", "_id": "83545"} -{"city": "PIERCE", "loc": [-115.807071, 46.492424], "pop": 900, "state": "ID", "_id": "83546"} -{"city": "POLLOCK", "loc": [-116.351742, 45.306754], "pop": 274, "state": "ID", "_id": "83547"} -{"city": "REUBENS", "loc": [-116.533334, 46.336112], "pop": 121, "state": "ID", "_id": "83548"} -{"city": "RIGGINS", "loc": [-116.300553, 45.397006], "pop": 818, "state": "ID", "_id": "83549"} -{"city": "WEIPPE", "loc": [-115.938593, 46.38069], "pop": 1193, "state": "ID", "_id": "83553"} -{"city": "WHITE BIRD", "loc": [-116.2889, 45.752096], "pop": 393, "state": "ID", "_id": "83554"} -{"city": "WINCHESTER", "loc": [-116.620382, 46.238334], "pop": 380, "state": "ID", "_id": "83555"} -{"city": "ATLANTA", "loc": [-115.357042, 43.567436], "pop": 208, "state": "ID", "_id": "83601"} -{"city": "BANKS", "loc": [-115.983737, 44.149152], "pop": 494, "state": "ID", "_id": "83602"} -{"city": "GRASMERE", "loc": [-115.677259, 42.76006], "pop": 609, "state": "ID", "_id": "83604"} -{"city": "CALDWELL", "loc": [-116.700038, 43.662719], "pop": 32407, "state": "ID", "_id": "83605"} -{"city": "CAMBRIDGE", "loc": [-116.675717, 44.59216], "pop": 962, "state": "ID", "_id": "83610"} -{"city": "WEST MOUNTAIN", "loc": [-116.027676, 44.493273], "pop": 1681, "state": "ID", "_id": "83611"} -{"city": "COUNCIL", "loc": [-116.451833, 44.762754], "pop": 1606, "state": "ID", "_id": "83612"} -{"city": "DONNELLY", "loc": [-116.08578, 44.74937], "pop": 681, "state": "ID", "_id": "83615"} -{"city": "EAGLE", "loc": [-116.361966, 43.706879], "pop": 6874, "state": "ID", "_id": "83616"} -{"city": "MONTOUR", "loc": [-116.511459, 43.879152], "pop": 11189, "state": "ID", "_id": "83617"} -{"city": "FRUITLAND", "loc": [-116.914259, 44.002658], "pop": 4611, "state": "ID", "_id": "83619"} -{"city": "GARDEN VALLEY", "loc": [-115.824311, 44.090932], "pop": 513, "state": "ID", "_id": "83622"} -{"city": "GLENNS FERRY", "loc": [-115.315973, 42.962202], "pop": 2040, "state": "ID", "_id": "83623"} -{"city": "GRAND VIEW", "loc": [-116.08187, 42.810101], "pop": 1449, "state": "ID", "_id": "83624"} -{"city": "HAMMETT", "loc": [-115.565839, 42.981755], "pop": 73, "state": "ID", "_id": "83627"} -{"city": "HOMEDALE", "loc": [-116.947228, 43.613844], "pop": 3079, "state": "ID", "_id": "83628"} -{"city": "HORSESHOE BEND", "loc": [-116.180898, 43.922882], "pop": 1111, "state": "ID", "_id": "83629"} -{"city": "IDAHO CITY", "loc": [-115.918436, 43.758601], "pop": 1324, "state": "ID", "_id": "83631"} -{"city": "INDIAN VALLEY", "loc": [-116.442969, 44.549134], "pop": 188, "state": "ID", "_id": "83632"} -{"city": "KING HILL", "loc": [-115.269098, 42.936104], "pop": 357, "state": "ID", "_id": "83633"} -{"city": "KUNA", "loc": [-116.381859, 43.487034], "pop": 8141, "state": "ID", "_id": "83634"} -{"city": "LETHA", "loc": [-116.585004, 43.840306], "pop": 29, "state": "ID", "_id": "83636"} -{"city": "LOWMAN", "loc": [-115.528488, 44.110616], "pop": 63, "state": "ID", "_id": "83637"} -{"city": "MC CALL", "loc": [-116.078873, 44.891784], "pop": 3681, "state": "ID", "_id": "83638"} -{"city": "MARSING", "loc": [-116.823968, 43.539866], "pop": 2281, "state": "ID", "_id": "83639"} -{"city": "MELBA", "loc": [-116.548875, 43.37842], "pop": 1116, "state": "ID", "_id": "83641"} -{"city": "MERIDIAN", "loc": [-116.397538, 43.614963], "pop": 19033, "state": "ID", "_id": "83642"} -{"city": "MESA", "loc": [-116.42113, 44.657747], "pop": 279, "state": "ID", "_id": "83643"} -{"city": "MIDDLETON", "loc": [-116.61122, 43.719052], "pop": 3898, "state": "ID", "_id": "83644"} -{"city": "MIDVALE", "loc": [-116.703838, 44.441979], "pop": 621, "state": "ID", "_id": "83645"} -{"city": "MOUNTAIN HOME", "loc": [-115.696334, 43.139223], "pop": 12235, "state": "ID", "_id": "83647"} -{"city": "MOUNTAIN HOME A", "loc": [-115.873609, 43.049315], "pop": 6304, "state": "ID", "_id": "83648"} -{"city": "OREANA", "loc": [-116.605379, 43.207296], "pop": 1156, "state": "ID", "_id": "83650"} -{"city": "NAMPA", "loc": [-116.584818, 43.58342], "pop": 16068, "state": "ID", "_id": "83651"} -{"city": "NEW MEADOWS", "loc": [-116.287438, 44.993969], "pop": 1179, "state": "ID", "_id": "83654"} -{"city": "NEW PLYMOUTH", "loc": [-116.804818, 43.959021], "pop": 3165, "state": "ID", "_id": "83655"} -{"city": "OLA", "loc": [-116.290915, 44.235026], "pop": 159, "state": "ID", "_id": "83657"} -{"city": "PARMA", "loc": [-116.940066, 43.789576], "pop": 4477, "state": "ID", "_id": "83660"} -{"city": "PAYETTE", "loc": [-116.920277, 44.07818], "pop": 7913, "state": "ID", "_id": "83661"} -{"city": "STAR", "loc": [-116.496735, 43.701296], "pop": 1579, "state": "ID", "_id": "83669"} -{"city": "SWEET", "loc": [-116.323215, 43.99475], "pop": 344, "state": "ID", "_id": "83670"} -{"city": "WEISER", "loc": [-116.96507, 44.25222], "pop": 6967, "state": "ID", "_id": "83672"} -{"city": "WILDER", "loc": [-116.912199, 43.657851], "pop": 3042, "state": "ID", "_id": "83676"} -{"city": "YELLOW PINE", "loc": [-115.49634, 44.969809], "pop": 68, "state": "ID", "_id": "83677"} -{"city": "NAMPA", "loc": [-116.565962, 43.544125], "pop": 17886, "state": "ID", "_id": "83686"} -{"city": "NAMPA", "loc": [-116.536024, 43.593657], "pop": 10589, "state": "ID", "_id": "83687"} -{"city": "BOISE", "loc": [-116.205192, 43.632237], "pop": 19423, "state": "ID", "_id": "83702"} -{"city": "BOISE", "loc": [-116.252396, 43.660051], "pop": 17005, "state": "ID", "_id": "83703"} -{"city": "BOISE", "loc": [-116.295099, 43.633001], "pop": 40912, "state": "ID", "_id": "83704"} -{"city": "BOISE", "loc": [-116.219104, 43.585077], "pop": 25402, "state": "ID", "_id": "83705"} -{"city": "BOISE", "loc": [-116.191006, 43.588495], "pop": 24826, "state": "ID", "_id": "83706"} -{"city": "BOISE", "loc": [-116.29407, 43.574085], "pop": 30382, "state": "ID", "_id": "83709"} -{"city": "BOISE", "loc": [-116.164924, 43.602311], "pop": 7572, "state": "ID", "_id": "83712"} -{"city": "GARDEN CITY", "loc": [-116.265751, 43.643036], "pop": 5897, "state": "ID", "_id": "83714"} -{"city": "ATHOL", "loc": [-116.731821, 47.92674], "pop": 2520, "state": "ID", "_id": "83801"} -{"city": "AVERY", "loc": [-115.866012, 47.271431], "pop": 113, "state": "ID", "_id": "83802"} -{"city": "BAYVIEW", "loc": [-116.568745, 47.96535], "pop": 722, "state": "ID", "_id": "83803"} -{"city": "BLANCHARD", "loc": [-116.990865, 48.022344], "pop": 507, "state": "ID", "_id": "83804"} -{"city": "BONNERS FERRY", "loc": [-116.332178, 48.730642], "pop": 5219, "state": "ID", "_id": "83805"} -{"city": "CALDER", "loc": [-116.222793, 47.274135], "pop": 77, "state": "ID", "_id": "83808"} -{"city": "CAREYWOOD", "loc": [-116.598761, 48.062494], "pop": 361, "state": "ID", "_id": "83809"} -{"city": "CATALDO", "loc": [-116.443138, 47.552169], "pop": 982, "state": "ID", "_id": "83810"} -{"city": "CLARK FORK", "loc": [-116.169865, 48.140457], "pop": 971, "state": "ID", "_id": "83811"} -{"city": "CLARKIA", "loc": [-116.277408, 47.044477], "pop": 85, "state": "ID", "_id": "83812"} -{"city": "COCOLALLA", "loc": [-116.657051, 48.124828], "pop": 715, "state": "ID", "_id": "83813"} -{"city": "COEUR D ALENE", "loc": [-116.784976, 47.692841], "pop": 33589, "state": "ID", "_id": "83814"} -{"city": "COOLIN", "loc": [-116.840823, 48.522754], "pop": 194, "state": "ID", "_id": "83821"} -{"city": "OLD TOWN", "loc": [-116.927382, 48.187988], "pop": 2229, "state": "ID", "_id": "83822"} -{"city": "DEARY", "loc": [-116.523782, 46.806062], "pop": 1483, "state": "ID", "_id": "83823"} -{"city": "DESMET", "loc": [-116.893746, 47.125954], "pop": 265, "state": "ID", "_id": "83824"} -{"city": "ELK RIVER", "loc": [-116.179943, 46.782972], "pop": 154, "state": "ID", "_id": "83827"} -{"city": "FERNWOOD", "loc": [-116.383126, 47.116027], "pop": 372, "state": "ID", "_id": "83830"} -{"city": "GENESEE", "loc": [-116.928991, 46.571394], "pop": 1241, "state": "ID", "_id": "83832"} -{"city": "HARRISON", "loc": [-116.744607, 47.501692], "pop": 1077, "state": "ID", "_id": "83833"} -{"city": "HARVARD", "loc": [-116.702524, 46.937647], "pop": 226, "state": "ID", "_id": "83834"} -{"city": "HAYDEN LAKE", "loc": [-116.776821, 47.773853], "pop": 9287, "state": "ID", "_id": "83835"} -{"city": "HOPE", "loc": [-116.279504, 48.244402], "pop": 687, "state": "ID", "_id": "83836"} -{"city": "KELLOGG", "loc": [-116.125281, 47.543069], "pop": 4640, "state": "ID", "_id": "83837"} -{"city": "KINGSTON", "loc": [-116.288722, 47.550881], "pop": 690, "state": "ID", "_id": "83839"} -{"city": "MEDIMONT", "loc": [-116.568291, 47.462482], "pop": 32, "state": "ID", "_id": "83842"} -{"city": "MOSCOW", "loc": [-116.989683, 46.730921], "pop": 21714, "state": "ID", "_id": "83843"} -{"city": "MOYIE SPRINGS", "loc": [-116.179603, 48.746434], "pop": 1496, "state": "ID", "_id": "83845"} -{"city": "MULLAN", "loc": [-115.792603, 47.470906], "pop": 995, "state": "ID", "_id": "83846"} -{"city": "NAPLES", "loc": [-116.319636, 48.60491], "pop": 1556, "state": "ID", "_id": "83847"} -{"city": "NORDMAN", "loc": [-116.92126, 48.566944], "pop": 446, "state": "ID", "_id": "83848"} -{"city": "PINEHURST", "loc": [-116.264679, 47.501823], "pop": 371, "state": "ID", "_id": "83850"} -{"city": "PLUMMER", "loc": [-116.866161, 47.327782], "pop": 1439, "state": "ID", "_id": "83851"} -{"city": "PORTHILL", "loc": [-116.477517, 48.992037], "pop": 61, "state": "ID", "_id": "83853"} -{"city": "POST FALLS", "loc": [-116.935349, 47.720475], "pop": 14952, "state": "ID", "_id": "83854"} -{"city": "POTLATCH", "loc": [-116.914101, 46.944833], "pop": 1836, "state": "ID", "_id": "83855"} -{"city": "PRIEST RIVER", "loc": [-116.906617, 48.16637], "pop": 4345, "state": "ID", "_id": "83856"} -{"city": "PRINCETON", "loc": [-116.828728, 46.899556], "pop": 733, "state": "ID", "_id": "83857"} -{"city": "RATHDRUM", "loc": [-116.887294, 47.824107], "pop": 4798, "state": "ID", "_id": "83858"} -{"city": "SAGLE", "loc": [-116.5455, 48.2035], "pop": 3512, "state": "ID", "_id": "83860"} -{"city": "SAINT MARIES", "loc": [-116.568107, 47.297727], "pop": 5894, "state": "ID", "_id": "83861"} -{"city": "SANDPOINT", "loc": [-116.533249, 48.311989], "pop": 12421, "state": "ID", "_id": "83864"} -{"city": "SMELTERVILLE", "loc": [-116.240113, 47.537096], "pop": 2272, "state": "ID", "_id": "83868"} -{"city": "SPIRIT LAKE", "loc": [-116.868046, 47.965652], "pop": 860, "state": "ID", "_id": "83869"} -{"city": "TENSED", "loc": [-116.902716, 47.170735], "pop": 332, "state": "ID", "_id": "83870"} -{"city": "TROY", "loc": [-116.768105, 46.742648], "pop": 1481, "state": "ID", "_id": "83871"} -{"city": "VIOLA", "loc": [-116.97319, 46.858293], "pop": 519, "state": "ID", "_id": "83872"} -{"city": "WALLACE", "loc": [-115.962001, 47.490842], "pop": 4688, "state": "ID", "_id": "83873"} -{"city": "WORLEY", "loc": [-116.905634, 47.429213], "pop": 845, "state": "ID", "_id": "83876"} -{"city": "ANTIOCH", "loc": [-88.117802, 42.464811], "pop": 18058, "state": "IL", "_id": "60002"} -{"city": "ARLINGTON HEIGHT", "loc": [-87.979099, 42.111619], "pop": 52947, "state": "IL", "_id": "60004"} -{"city": "ARLINGTON HEIGHT", "loc": [-87.985461, 42.066599], "pop": 26742, "state": "IL", "_id": "60005"} -{"city": "ELK GROVE VILLAG", "loc": [-88.012775, 42.005613], "pop": 34577, "state": "IL", "_id": "60007"} -{"city": "ROLLING MEADOWS", "loc": [-88.019075, 42.072979], "pop": 18672, "state": "IL", "_id": "60008"} -{"city": "BARRINGTON", "loc": [-88.138345, 42.161387], "pop": 37323, "state": "IL", "_id": "60010"} -{"city": "CRYSTAL LAKE", "loc": [-88.321294, 42.266198], "pop": 6855, "state": "IL", "_id": "60012"} -{"city": "CARY", "loc": [-88.242594, 42.219599], "pop": 17521, "state": "IL", "_id": "60013"} -{"city": "CRYSTAL LAKE", "loc": [-88.332364, 42.230755], "pop": 29595, "state": "IL", "_id": "60014"} -{"city": "DEERFIELD", "loc": [-87.859033, 42.170494], "pop": 22048, "state": "IL", "_id": "60015"} -{"city": "DES PLAINES", "loc": [-87.885899, 42.046734], "pop": 54734, "state": "IL", "_id": "60016"} -{"city": "ROSEMONT", "loc": [-87.897882, 42.015116], "pop": 28884, "state": "IL", "_id": "60018"} -{"city": "FOX LAKE", "loc": [-88.164752, 42.393701], "pop": 10336, "state": "IL", "_id": "60020"} -{"city": "FOX RIVER GROVE", "loc": [-88.220483, 42.193594], "pop": 4898, "state": "IL", "_id": "60021"} -{"city": "GLENCOE", "loc": [-87.761486, 42.133339], "pop": 8168, "state": "IL", "_id": "60022"} -{"city": "GLENVIEW", "loc": [-87.822299, 42.075785], "pop": 45038, "state": "IL", "_id": "60025"} -{"city": "GLENVIEW NAS", "loc": [-87.824782, 42.09134], "pop": 437, "state": "IL", "_id": "60026"} -{"city": "GAGES LAKE", "loc": [-88.037789, 42.34848], "pop": 8038, "state": "IL", "_id": "60030"} -{"city": "GURNEE", "loc": [-87.945232, 42.366906], "pop": 32114, "state": "IL", "_id": "60031"} -{"city": "HARVARD", "loc": [-88.604812, 42.422727], "pop": 10790, "state": "IL", "_id": "60033"} -{"city": "HEBRON", "loc": [-88.417583, 42.464173], "pop": 1606, "state": "IL", "_id": "60034"} -{"city": "HIGHLAND PARK", "loc": [-87.805894, 42.179446], "pop": 29346, "state": "IL", "_id": "60035"} -{"city": "FORT SHERIDAN", "loc": [-87.805572, 42.209683], "pop": 2598, "state": "IL", "_id": "60037"} -{"city": "HIGHWOOD", "loc": [-87.814068, 42.203549], "pop": 3956, "state": "IL", "_id": "60040"} -{"city": "INGLESIDE", "loc": [-88.158749, 42.363093], "pop": 4267, "state": "IL", "_id": "60041"} -{"city": "ISLAND LAKE", "loc": [-88.19263, 42.274186], "pop": 3919, "state": "IL", "_id": "60042"} -{"city": "KENILWORTH", "loc": [-87.716463, 42.088444], "pop": 2509, "state": "IL", "_id": "60043"} -{"city": "LAKE BLUFF", "loc": [-87.85595, 42.28196], "pop": 8031, "state": "IL", "_id": "60044"} -{"city": "LAKE FOREST", "loc": [-87.848154, 42.237398], "pop": 17948, "state": "IL", "_id": "60045"} -{"city": "LINDENHURST", "loc": [-88.063318, 42.414796], "pop": 20764, "state": "IL", "_id": "60046"} -{"city": "LONG GROVE", "loc": [-88.070299, 42.196721], "pop": 26893, "state": "IL", "_id": "60047"} -{"city": "LIBERTYVILLE", "loc": [-87.949955, 42.281001], "pop": 28573, "state": "IL", "_id": "60048"} -{"city": "MC HENRY", "loc": [-88.254429, 42.345527], "pop": 39545, "state": "IL", "_id": "60050"} -{"city": "MORTON GROVE", "loc": [-87.789879, 42.043133], "pop": 22502, "state": "IL", "_id": "60053"} -{"city": "MOUNT PROSPECT", "loc": [-87.937667, 42.062392], "pop": 54459, "state": "IL", "_id": "60056"} -{"city": "MUNDELEIN", "loc": [-88.004762, 42.263616], "pop": 22817, "state": "IL", "_id": "60060"} -{"city": "VERNON HILLS", "loc": [-87.971852, 42.228753], "pop": 19713, "state": "IL", "_id": "60061"} -{"city": "NORTHBROOK", "loc": [-87.846535, 42.125443], "pop": 40216, "state": "IL", "_id": "60062"} -{"city": "ABBOTT PARK", "loc": [-87.847819, 42.318901], "pop": 26542, "state": "IL", "_id": "60064"} -{"city": "PALATINE", "loc": [-88.042937, 42.113888], "pop": 57281, "state": "IL", "_id": "60067"} -{"city": "PARK RIDGE", "loc": [-87.841675, 42.012171], "pop": 37450, "state": "IL", "_id": "60068"} -{"city": "PRAIRIE VIEW", "loc": [-87.904823, 42.192872], "pop": 4047, "state": "IL", "_id": "60069"} -{"city": "PROSPECT HEIGHTS", "loc": [-87.914934, 42.103324], "pop": 14692, "state": "IL", "_id": "60070"} -{"city": "RICHMOND", "loc": [-88.290024, 42.466863], "pop": 2658, "state": "IL", "_id": "60071"} -{"city": "RINGWOOD", "loc": [-88.297073, 42.380427], "pop": 1926, "state": "IL", "_id": "60072"} -{"city": "ROUND LAKE", "loc": [-88.088819, 42.366809], "pop": 28919, "state": "IL", "_id": "60073"} -{"city": "PALATINE", "loc": [-88.022998, 42.145775], "pop": 11712, "state": "IL", "_id": "60074"} -{"city": "SKOKIE", "loc": [-87.732828, 42.036168], "pop": 31589, "state": "IL", "_id": "60076"} -{"city": "SKOKIE", "loc": [-87.754123, 42.034525], "pop": 22680, "state": "IL", "_id": "60077"} -{"city": "SPRING GROVE", "loc": [-88.223734, 42.441267], "pop": 2783, "state": "IL", "_id": "60081"} -{"city": "TECHNY", "loc": [-87.804882, 42.121425], "pop": 196, "state": "IL", "_id": "60082"} -{"city": "WADSWORTH", "loc": [-87.904048, 42.446032], "pop": 5510, "state": "IL", "_id": "60083"} -{"city": "WAUCONDA", "loc": [-88.133284, 42.263553], "pop": 11142, "state": "IL", "_id": "60084"} -{"city": "MC GAW PARK", "loc": [-87.852585, 42.361882], "pop": 55778, "state": "IL", "_id": "60085"} -{"city": "WAUKEGAN", "loc": [-87.855387, 42.398906], "pop": 17816, "state": "IL", "_id": "60087"} -{"city": "GREAT LAKES", "loc": [-87.864192, 42.303173], "pop": 8831, "state": "IL", "_id": "60088"} -{"city": "BUFFALO GROVE", "loc": [-87.964364, 42.159843], "pop": 41478, "state": "IL", "_id": "60089"} -{"city": "WHEELING", "loc": [-87.934097, 42.13404], "pop": 31261, "state": "IL", "_id": "60090"} -{"city": "WILMETTE", "loc": [-87.724572, 42.076462], "pop": 26657, "state": "IL", "_id": "60091"} -{"city": "NORTHFIELD", "loc": [-87.752256, 42.103605], "pop": 19317, "state": "IL", "_id": "60093"} -{"city": "WINTHROP HARBOR", "loc": [-87.831788, 42.479269], "pop": 7433, "state": "IL", "_id": "60096"} -{"city": "WONDER LAKE", "loc": [-88.353364, 42.384908], "pop": 8401, "state": "IL", "_id": "60097"} -{"city": "WOODSTOCK", "loc": [-88.447671, 42.319775], "pop": 21770, "state": "IL", "_id": "60098"} -{"city": "ZION", "loc": [-87.838925, 42.444249], "pop": 24944, "state": "IL", "_id": "60099"} -{"city": "ADDISON", "loc": [-88.0054, 41.933509], "pop": 35140, "state": "IL", "_id": "60101"} -{"city": "LAKE IN THE HILL", "loc": [-88.301429, 42.170923], "pop": 22082, "state": "IL", "_id": "60102"} -{"city": "HANOVER PARK", "loc": [-88.160356, 41.983559], "pop": 51877, "state": "IL", "_id": "60103"} -{"city": "BELLWOOD", "loc": [-87.878557, 41.882484], "pop": 19336, "state": "IL", "_id": "60104"} -{"city": "BENSENVILLE", "loc": [-87.944973, 41.950145], "pop": 20080, "state": "IL", "_id": "60106"} -{"city": "STREAMWOOD", "loc": [-88.168965, 42.022539], "pop": 30513, "state": "IL", "_id": "60107"} -{"city": "BLOOMINGDALE", "loc": [-88.07824, 41.94827], "pop": 16560, "state": "IL", "_id": "60108"} -{"city": "CARPENTERSVILLE", "loc": [-88.2606, 42.123004], "pop": 23550, "state": "IL", "_id": "60110"} -{"city": "CLARE", "loc": [-88.837832, 42.027045], "pop": 373, "state": "IL", "_id": "60111"} -{"city": "DE KALB", "loc": [-88.760673, 41.934245], "pop": 38360, "state": "IL", "_id": "60115"} -{"city": "DUNDEE", "loc": [-88.290202, 42.096197], "pop": 11919, "state": "IL", "_id": "60118"} -{"city": "ELBURN", "loc": [-88.461106, 41.882405], "pop": 6271, "state": "IL", "_id": "60119"} -{"city": "ELGIN", "loc": [-88.260631, 42.038356], "pop": 42848, "state": "IL", "_id": "60120"} -{"city": "ELGIN", "loc": [-88.318615, 42.037574], "pop": 43835, "state": "IL", "_id": "60123"} -{"city": "ELMHURST", "loc": [-87.941025, 41.892661], "pop": 43637, "state": "IL", "_id": "60126"} -{"city": "ESMOND", "loc": [-88.94386, 42.022458], "pop": 388, "state": "IL", "_id": "60129"} -{"city": "FOREST PARK", "loc": [-87.810624, 41.874373], "pop": 14882, "state": "IL", "_id": "60130"} -{"city": "FRANKLIN PARK", "loc": [-87.873423, 41.933878], "pop": 18572, "state": "IL", "_id": "60131"} -{"city": "GENEVA", "loc": [-88.310954, 41.886013], "pop": 13603, "state": "IL", "_id": "60134"} -{"city": "GENOA", "loc": [-88.690803, 42.09811], "pop": 4356, "state": "IL", "_id": "60135"} -{"city": "GILBERTS", "loc": [-88.369089, 42.098377], "pop": 1212, "state": "IL", "_id": "60136"} -{"city": "GLEN ELLYN", "loc": [-88.064774, 41.866112], "pop": 35643, "state": "IL", "_id": "60137"} -{"city": "GLENDALE HEIGHTS", "loc": [-88.079282, 41.920523], "pop": 27324, "state": "IL", "_id": "60139"} -{"city": "HAMPSHIRE", "loc": [-88.517033, 42.080748], "pop": 6255, "state": "IL", "_id": "60140"} -{"city": "HINES", "loc": [-87.835542, 41.862262], "pop": 200, "state": "IL", "_id": "60141"} -{"city": "HUNTLEY", "loc": [-88.426848, 42.175555], "pop": 4351, "state": "IL", "_id": "60142"} -{"city": "ITASCA", "loc": [-88.020247, 41.971967], "pop": 8650, "state": "IL", "_id": "60143"} -{"city": "KINGSTON", "loc": [-88.769496, 42.105654], "pop": 1555, "state": "IL", "_id": "60145"} -{"city": "KIRKLAND", "loc": [-88.868522, 42.101406], "pop": 1930, "state": "IL", "_id": "60146"} -{"city": "LOMBARD", "loc": [-88.015988, 41.872139], "pop": 52289, "state": "IL", "_id": "60148"} -{"city": "MALTA", "loc": [-88.868818, 41.918332], "pop": 1619, "state": "IL", "_id": "60150"} -{"city": "MAPLE PARK", "loc": [-88.59985, 41.923217], "pop": 4893, "state": "IL", "_id": "60151"} -{"city": "MARENGO", "loc": [-88.607369, 42.244189], "pop": 8536, "state": "IL", "_id": "60152"} -{"city": "BROADVIEW", "loc": [-87.847675, 41.874857], "pop": 37099, "state": "IL", "_id": "60153"} -{"city": "WESTCHESTER", "loc": [-87.884488, 41.852368], "pop": 16957, "state": "IL", "_id": "60154"} -{"city": "MEDINAH", "loc": [-88.057507, 41.970545], "pop": 3110, "state": "IL", "_id": "60157"} -{"city": "MELROSE PARK", "loc": [-87.858066, 41.900347], "pop": 21235, "state": "IL", "_id": "60160"} -{"city": "HILLSIDE", "loc": [-87.901591, 41.872452], "pop": 7757, "state": "IL", "_id": "60162"} -{"city": "HILLSIDE", "loc": [-87.910678, 41.886538], "pop": 5079, "state": "IL", "_id": "60163"} -{"city": "NORTHLAKE", "loc": [-87.89592, 41.917961], "pop": 21306, "state": "IL", "_id": "60164"} -{"city": "STONE PARK", "loc": [-87.881053, 41.903005], "pop": 4387, "state": "IL", "_id": "60165"} -{"city": "RIVER GROVE", "loc": [-87.838707, 41.927886], "pop": 9949, "state": "IL", "_id": "60171"} -{"city": "ROSELLE", "loc": [-88.085699, 41.979834], "pop": 22626, "state": "IL", "_id": "60172"} -{"city": "SCHAUMBURG", "loc": [-88.048189, 42.05807], "pop": 9314, "state": "IL", "_id": "60173"} -{"city": "SAINT CHARLES", "loc": [-88.307022, 41.919417], "pop": 27454, "state": "IL", "_id": "60174"} -{"city": "SAINT CHARLES", "loc": [-88.391799, 41.947842], "pop": 11851, "state": "IL", "_id": "60175"} -{"city": "SCHILLER PARK", "loc": [-87.869179, 41.956304], "pop": 11189, "state": "IL", "_id": "60176"} -{"city": "SOUTH ELGIN", "loc": [-88.298558, 41.996868], "pop": 9117, "state": "IL", "_id": "60177"} -{"city": "SYCAMORE", "loc": [-88.692809, 41.991117], "pop": 13512, "state": "IL", "_id": "60178"} -{"city": "UNION", "loc": [-88.528295, 42.210274], "pop": 1450, "state": "IL", "_id": "60180"} -{"city": "VILLA PARK", "loc": [-87.978246, 41.879899], "pop": 27217, "state": "IL", "_id": "60181"} -{"city": "WEST CHICAGO", "loc": [-88.202168, 41.888558], "pop": 23894, "state": "IL", "_id": "60185"} -{"city": "WHEATON", "loc": [-88.107633, 41.856592], "pop": 57758, "state": "IL", "_id": "60187"} -{"city": "CAROL STREAM", "loc": [-88.136962, 41.91784], "pop": 34902, "state": "IL", "_id": "60188"} -{"city": "WINFIELD", "loc": [-88.151621, 41.874358], "pop": 9255, "state": "IL", "_id": "60190"} -{"city": "WOOD DALE", "loc": [-87.980971, 41.960171], "pop": 13750, "state": "IL", "_id": "60191"} -{"city": "SCHAUMBURG", "loc": [-88.093481, 42.014432], "pop": 39438, "state": "IL", "_id": "60193"} -{"city": "HOFFMAN ESTATES", "loc": [-88.109442, 42.039025], "pop": 37295, "state": "IL", "_id": "60194"} -{"city": "HOFFMAN ESTATES", "loc": [-88.108709, 42.073865], "pop": 29236, "state": "IL", "_id": "60195"} -{"city": "EVANSTON", "loc": [-87.694331, 42.054551], "pop": 41692, "state": "IL", "_id": "60201"} -{"city": "EVANSTON", "loc": [-87.686544, 42.03022], "pop": 31509, "state": "IL", "_id": "60202"} -{"city": "EVANSTON", "loc": [-87.71759, 42.048487], "pop": 4764, "state": "IL", "_id": "60203"} -{"city": "OAK PARK", "loc": [-87.798598, 41.888601], "pop": 1673, "state": "IL", "_id": "60301"} -{"city": "OAK PARK", "loc": [-87.789543, 41.892471], "pop": 33298, "state": "IL", "_id": "60302"} -{"city": "OAK PARK", "loc": [-87.787712, 41.872458], "pop": 18677, "state": "IL", "_id": "60304"} -{"city": "RIVER FOREST", "loc": [-87.8159, 41.895064], "pop": 11669, "state": "IL", "_id": "60305"} -{"city": "BEECHER", "loc": [-87.611538, 41.34437], "pop": 3724, "state": "IL", "_id": "60401"} -{"city": "STICKNEY", "loc": [-87.79075, 41.841819], "pop": 51541, "state": "IL", "_id": "60402"} -{"city": "BLUE ISLAND", "loc": [-87.679465, 41.658187], "pop": 23305, "state": "IL", "_id": "60406"} -{"city": "BRACEVILLE", "loc": [-88.269042, 41.228788], "pop": 1535, "state": "IL", "_id": "60407"} -{"city": "BRAIDWOOD", "loc": [-88.223124, 41.26574], "pop": 3814, "state": "IL", "_id": "60408"} -{"city": "CALUMET CITY", "loc": [-87.548328, 41.615257], "pop": 36065, "state": "IL", "_id": "60409"} -{"city": "CHANNAHON", "loc": [-88.213786, 41.434664], "pop": 3870, "state": "IL", "_id": "60410"} -{"city": "SAUK VILLAGE", "loc": [-87.613209, 41.506202], "pop": 60738, "state": "IL", "_id": "60411"} -{"city": "CHICAGO RIDGE", "loc": [-87.777381, 41.70171], "pop": 13472, "state": "IL", "_id": "60415"} -{"city": "COAL CITY", "loc": [-88.282346, 41.290769], "pop": 6248, "state": "IL", "_id": "60416"} -{"city": "CRETE", "loc": [-87.602738, 41.438952], "pop": 14372, "state": "IL", "_id": "60417"} -{"city": "DOLTON", "loc": [-87.597952, 41.625723], "pop": 22705, "state": "IL", "_id": "60419"} -{"city": "DWIGHT", "loc": [-88.415884, 41.088701], "pop": 4956, "state": "IL", "_id": "60420"} -{"city": "ELWOOD", "loc": [-88.08642, 41.426018], "pop": 2700, "state": "IL", "_id": "60421"} -{"city": "FLOSSMOOR", "loc": [-87.683737, 41.540574], "pop": 8627, "state": "IL", "_id": "60422"} -{"city": "FRANKFORT", "loc": [-87.824774, 41.509361], "pop": 15682, "state": "IL", "_id": "60423"} -{"city": "GARDNER", "loc": [-88.296543, 41.179321], "pop": 2349, "state": "IL", "_id": "60424"} -{"city": "GLENWOOD", "loc": [-87.612584, 41.546718], "pop": 10180, "state": "IL", "_id": "60425"} -{"city": "MARKHAM", "loc": [-87.661115, 41.608536], "pop": 48332, "state": "IL", "_id": "60426"} -{"city": "HAZEL CREST", "loc": [-87.684885, 41.573803], "pop": 14987, "state": "IL", "_id": "60429"} -{"city": "HOMEWOOD", "loc": [-87.661578, 41.555579], "pop": 19469, "state": "IL", "_id": "60430"} -{"city": "JOLIET", "loc": [-88.08241, 41.527154], "pop": 512, "state": "IL", "_id": "60431"} -{"city": "JOLIET", "loc": [-88.057178, 41.537758], "pop": 20199, "state": "IL", "_id": "60432"} -{"city": "JOLIET", "loc": [-88.05687, 41.511873], "pop": 18342, "state": "IL", "_id": "60433"} -{"city": "SHOREWOOD", "loc": [-88.128107, 41.541468], "pop": 56510, "state": "IL", "_id": "60435"} -{"city": "ROCKDALE", "loc": [-88.135779, 41.508818], "pop": 23888, "state": "IL", "_id": "60436"} -{"city": "KINSMAN", "loc": [-88.480477, 41.161825], "pop": 687, "state": "IL", "_id": "60437"} -{"city": "LANSING", "loc": [-87.544634, 41.566045], "pop": 28810, "state": "IL", "_id": "60438"} -{"city": "ARGONNE", "loc": [-88.023558, 41.695076], "pop": 31018, "state": "IL", "_id": "60439"} -{"city": "BOLINGBROOK", "loc": [-88.087315, 41.697605], "pop": 23726, "state": "IL", "_id": "60440"} -{"city": "ROMEOVILLE", "loc": [-88.025581, 41.613481], "pop": 55268, "state": "IL", "_id": "60441"} -{"city": "MANHATTAN", "loc": [-87.977133, 41.428883], "pop": 3657, "state": "IL", "_id": "60442"} -{"city": "MATTESON", "loc": [-87.740648, 41.510183], "pop": 13624, "state": "IL", "_id": "60443"} -{"city": "MAZON", "loc": [-88.409561, 41.297189], "pop": 3909, "state": "IL", "_id": "60444"} -{"city": "CRESTWOOD", "loc": [-87.732418, 41.634106], "pop": 26378, "state": "IL", "_id": "60445"} -{"city": "MINOOKA", "loc": [-88.278596, 41.461516], "pop": 6005, "state": "IL", "_id": "60447"} -{"city": "MOKENA", "loc": [-87.891121, 41.53421], "pop": 12324, "state": "IL", "_id": "60448"} -{"city": "MONEE", "loc": [-87.77484, 41.419133], "pop": 3537, "state": "IL", "_id": "60449"} -{"city": "MORRIS", "loc": [-88.417769, 41.367233], "pop": 13423, "state": "IL", "_id": "60450"} -{"city": "NEW LENOX", "loc": [-87.963083, 41.506701], "pop": 17470, "state": "IL", "_id": "60451"} -{"city": "OAK FOREST", "loc": [-87.754219, 41.607684], "pop": 26772, "state": "IL", "_id": "60452"} -{"city": "OAK LAWN", "loc": [-87.751564, 41.714305], "pop": 56039, "state": "IL", "_id": "60453"} -{"city": "BRIDGEVIEW", "loc": [-87.806572, 41.743128], "pop": 14065, "state": "IL", "_id": "60455"} -{"city": "HOMETOWN", "loc": [-87.731536, 41.73113], "pop": 4769, "state": "IL", "_id": "60456"} -{"city": "HICKORY HILLS", "loc": [-87.828893, 41.726228], "pop": 12894, "state": "IL", "_id": "60457"} -{"city": "JUSTICE", "loc": [-87.834587, 41.744709], "pop": 12773, "state": "IL", "_id": "60458"} -{"city": "BURBANK", "loc": [-87.769907, 41.744704], "pop": 27870, "state": "IL", "_id": "60459"} -{"city": "ODELL", "loc": [-88.515641, 41.023773], "pop": 2795, "state": "IL", "_id": "60460"} -{"city": "OLYMPIA FIELDS", "loc": [-87.689952, 41.51564], "pop": 4253, "state": "IL", "_id": "60461"} -{"city": "ORLAND PARK", "loc": [-87.84225, 41.619378], "pop": 42564, "state": "IL", "_id": "60462"} -{"city": "PALOS HEIGHTS", "loc": [-87.792697, 41.662146], "pop": 13509, "state": "IL", "_id": "60463"} -{"city": "PALOS PARK", "loc": [-87.852146, 41.662352], "pop": 8967, "state": "IL", "_id": "60464"} -{"city": "PALOS HILLS", "loc": [-87.826276, 41.700389], "pop": 18112, "state": "IL", "_id": "60465"} -{"city": "UNIVERSITY PARK", "loc": [-87.682867, 41.474064], "pop": 31607, "state": "IL", "_id": "60466"} -{"city": "PEOTONE", "loc": [-87.78968, 41.33609], "pop": 4936, "state": "IL", "_id": "60468"} -{"city": "POSEN", "loc": [-87.687213, 41.62766], "pop": 4158, "state": "IL", "_id": "60469"} -{"city": "RANSOM", "loc": [-88.650214, 41.153048], "pop": 690, "state": "IL", "_id": "60470"} -{"city": "RICHTON PARK", "loc": [-87.723834, 41.481854], "pop": 10776, "state": "IL", "_id": "60471"} -{"city": "ROBBINS", "loc": [-87.708909, 41.642289], "pop": 7132, "state": "IL", "_id": "60472"} -{"city": "SOUTH HOLLAND", "loc": [-87.593814, 41.597916], "pop": 24457, "state": "IL", "_id": "60473"} -{"city": "STEGER", "loc": [-87.638585, 41.468608], "pop": 8531, "state": "IL", "_id": "60475"} -{"city": "THORNTON", "loc": [-87.607823, 41.572726], "pop": 2678, "state": "IL", "_id": "60476"} -{"city": "TINLEY PARK", "loc": [-87.804963, 41.582535], "pop": 45371, "state": "IL", "_id": "60477"} -{"city": "COUNTRY CLUB HIL", "loc": [-87.718452, 41.559773], "pop": 16225, "state": "IL", "_id": "60478"} -{"city": "VERONA", "loc": [-88.51701, 41.250094], "pop": 769, "state": "IL", "_id": "60479"} -{"city": "WILLOW SPRINGS", "loc": [-87.878588, 41.736416], "pop": 4469, "state": "IL", "_id": "60480"} -{"city": "CUSTER PARK", "loc": [-88.130083, 41.298063], "pop": 11034, "state": "IL", "_id": "60481"} -{"city": "WORTH", "loc": [-87.786272, 41.689498], "pop": 13081, "state": "IL", "_id": "60482"} -{"city": "ARGO", "loc": [-87.807468, 41.784245], "pop": 10525, "state": "IL", "_id": "60501"} -{"city": "AURORA", "loc": [-88.245281, 41.752269], "pop": 15334, "state": "IL", "_id": "60504"} -{"city": "AURORA", "loc": [-88.297139, 41.758209], "pop": 51422, "state": "IL", "_id": "60505"} -{"city": "AURORA", "loc": [-88.344582, 41.766414], "pop": 42636, "state": "IL", "_id": "60506"} -{"city": "BATAVIA", "loc": [-88.309756, 41.848165], "pop": 19299, "state": "IL", "_id": "60510"} -{"city": "BIG ROCK", "loc": [-88.537617, 41.759308], "pop": 1976, "state": "IL", "_id": "60511"} -{"city": "BRISTOL", "loc": [-88.401354, 41.707446], "pop": 595, "state": "IL", "_id": "60512"} -{"city": "BROOKFIELD", "loc": [-87.849246, 41.82167], "pop": 18859, "state": "IL", "_id": "60513"} -{"city": "CLARENDON HILLS", "loc": [-87.955322, 41.779729], "pop": 17321, "state": "IL", "_id": "60514"} -{"city": "DOWNERS GROVE", "loc": [-88.013753, 41.803428], "pop": 26971, "state": "IL", "_id": "60515"} -{"city": "DOWNERS GROVE", "loc": [-88.015873, 41.760157], "pop": 35756, "state": "IL", "_id": "60516"} -{"city": "WOODRIDGE", "loc": [-88.04885, 41.751755], "pop": 23761, "state": "IL", "_id": "60517"} -{"city": "EARLVILLE", "loc": [-88.910346, 41.585901], "pop": 2305, "state": "IL", "_id": "60518"} -{"city": "HINCKLEY", "loc": [-88.644831, 41.769108], "pop": 2387, "state": "IL", "_id": "60520"} -{"city": "OAK BROOK", "loc": [-87.940089, 41.7891], "pop": 44245, "state": "IL", "_id": "60521"} -{"city": "HODGKINS", "loc": [-87.875252, 41.801345], "pop": 45424, "state": "IL", "_id": "60525"} -{"city": "LEE", "loc": [-88.971386, 41.786418], "pop": 825, "state": "IL", "_id": "60530"} -{"city": "LELAND", "loc": [-88.771574, 41.606591], "pop": 1601, "state": "IL", "_id": "60531"} -{"city": "LISLE", "loc": [-88.0879, 41.786174], "pop": 24914, "state": "IL", "_id": "60532"} -{"city": "LYONS", "loc": [-87.823559, 41.813016], "pop": 9828, "state": "IL", "_id": "60534"} -{"city": "MONTGOMERY", "loc": [-88.331965, 41.717742], "pop": 14146, "state": "IL", "_id": "60538"} -{"city": "MOOSEHEART", "loc": [-88.331532, 41.824148], "pop": 371, "state": "IL", "_id": "60539"} -{"city": "NAPERVILLE", "loc": [-88.141038, 41.766198], "pop": 35414, "state": "IL", "_id": "60540"} -{"city": "NEWARK", "loc": [-88.527006, 41.526679], "pop": 3016, "state": "IL", "_id": "60541"} -{"city": "NORTH AURORA", "loc": [-88.327424, 41.808932], "pop": 6618, "state": "IL", "_id": "60542"} -{"city": "OSWEGO", "loc": [-88.345305, 41.684893], "pop": 9649, "state": "IL", "_id": "60543"} -{"city": "PLAINFIELD", "loc": [-88.199391, 41.600884], "pop": 10416, "state": "IL", "_id": "60544"} -{"city": "PLANO", "loc": [-88.53838, 41.666987], "pop": 7506, "state": "IL", "_id": "60545"} -{"city": "NORTH RIVERSIDE", "loc": [-87.821356, 41.837367], "pop": 14899, "state": "IL", "_id": "60546"} -{"city": "SANDWICH", "loc": [-88.639303, 41.635286], "pop": 10125, "state": "IL", "_id": "60548"} -{"city": "SERENA", "loc": [-88.75089, 41.499481], "pop": 830, "state": "IL", "_id": "60549"} -{"city": "SHABBONA", "loc": [-88.875249, 41.763846], "pop": 1180, "state": "IL", "_id": "60550"} -{"city": "SHERIDAN", "loc": [-88.670634, 41.516428], "pop": 3345, "state": "IL", "_id": "60551"} -{"city": "SOMONAUK", "loc": [-88.681645, 41.638289], "pop": 1475, "state": "IL", "_id": "60552"} -{"city": "STEWARD", "loc": [-89.015086, 41.847545], "pop": 661, "state": "IL", "_id": "60553"} -{"city": "SUGAR GROVE", "loc": [-88.439721, 41.774113], "pop": 4255, "state": "IL", "_id": "60554"} -{"city": "WARRENVILLE", "loc": [-88.19213, 41.828046], "pop": 12421, "state": "IL", "_id": "60555"} -{"city": "WATERMAN", "loc": [-88.775381, 41.750365], "pop": 1914, "state": "IL", "_id": "60556"} -{"city": "WESTERN SPRINGS", "loc": [-87.899485, 41.804864], "pop": 11862, "state": "IL", "_id": "60558"} -{"city": "WESTMONT", "loc": [-87.975736, 41.772848], "pop": 41903, "state": "IL", "_id": "60559"} -{"city": "YORKVILLE", "loc": [-88.443794, 41.638725], "pop": 8161, "state": "IL", "_id": "60560"} -{"city": "NAPERVILLE", "loc": [-88.16901, 41.78955], "pop": 26348, "state": "IL", "_id": "60563"} -{"city": "NAPERVILLE", "loc": [-88.195248, 41.704022], "pop": 8549, "state": "IL", "_id": "60564"} -{"city": "NAPERVILLE", "loc": [-88.128245, 41.732833], "pop": 32693, "state": "IL", "_id": "60565"} -{"city": "CHICAGO", "loc": [-87.618123, 41.885847], "pop": 4585, "state": "IL", "_id": "60601"} -{"city": "CHICAGO", "loc": [-87.632125, 41.882883], "pop": 59, "state": "IL", "_id": "60602"} -{"city": "CHICAGO", "loc": [-87.628499, 41.87985], "pop": 0, "state": "IL", "_id": "60603"} -{"city": "CHICAGO", "loc": [-87.632999, 41.87845], "pop": 3, "state": "IL", "_id": "60604"} -{"city": "CHICAGO", "loc": [-87.627715, 41.87125], "pop": 7709, "state": "IL", "_id": "60605"} -{"city": "CHICAGO", "loc": [-87.638648, 41.886822], "pop": 58, "state": "IL", "_id": "60606"} -{"city": "CHICAGO", "loc": [-87.657845, 41.872075], "pop": 13745, "state": "IL", "_id": "60607"} -{"city": "CHICAGO", "loc": [-87.669444, 41.851482], "pop": 84518, "state": "IL", "_id": "60608"} -{"city": "CHICAGO", "loc": [-87.653279, 41.809721], "pop": 89762, "state": "IL", "_id": "60609"} -{"city": "CHICAGO", "loc": [-87.633565, 41.903294], "pop": 40840, "state": "IL", "_id": "60610"} -{"city": "CHICAGO", "loc": [-87.622285, 41.897105], "pop": 22264, "state": "IL", "_id": "60611"} -{"city": "CHICAGO", "loc": [-87.687333, 41.880483], "pop": 44363, "state": "IL", "_id": "60612"} -{"city": "CHICAGO", "loc": [-87.657491, 41.954341], "pop": 48963, "state": "IL", "_id": "60613"} -{"city": "CHICAGO", "loc": [-87.648295, 41.92286], "pop": 61350, "state": "IL", "_id": "60614"} -{"city": "CHICAGO", "loc": [-87.600623, 41.802211], "pop": 44137, "state": "IL", "_id": "60615"} -{"city": "CHICAGO", "loc": [-87.630552, 41.84258], "pop": 45750, "state": "IL", "_id": "60616"} -{"city": "CHICAGO", "loc": [-87.556012, 41.725743], "pop": 98612, "state": "IL", "_id": "60617"} -{"city": "CHICAGO", "loc": [-87.704214, 41.946401], "pop": 88377, "state": "IL", "_id": "60618"} -{"city": "CHICAGO", "loc": [-87.60539, 41.745765], "pop": 74469, "state": "IL", "_id": "60619"} -{"city": "CHICAGO", "loc": [-87.654251, 41.741119], "pop": 92005, "state": "IL", "_id": "60620"} -{"city": "CHICAGO", "loc": [-87.642136, 41.774993], "pop": 56458, "state": "IL", "_id": "60621"} -{"city": "CHICAGO", "loc": [-87.67785, 41.901923], "pop": 74468, "state": "IL", "_id": "60622"} -{"city": "CHICAGO", "loc": [-87.7157, 41.849015], "pop": 112047, "state": "IL", "_id": "60623"} -{"city": "CHICAGO", "loc": [-87.722349, 41.880394], "pop": 50030, "state": "IL", "_id": "60624"} -{"city": "CHICAGO", "loc": [-87.704157, 41.970325], "pop": 83401, "state": "IL", "_id": "60625"} -{"city": "CHICAGO", "loc": [-87.668887, 42.009475], "pop": 57320, "state": "IL", "_id": "60626"} -{"city": "RIVERDALE", "loc": [-87.618213, 41.645918], "pop": 24996, "state": "IL", "_id": "60627"} -{"city": "CHICAGO", "loc": [-87.624277, 41.693443], "pop": 94317, "state": "IL", "_id": "60628"} -{"city": "CHICAGO", "loc": [-87.706936, 41.778149], "pop": 91814, "state": "IL", "_id": "60629"} -{"city": "CHICAGO", "loc": [-87.760273, 41.969862], "pop": 48371, "state": "IL", "_id": "60630"} -{"city": "CHICAGO", "loc": [-87.808215, 41.995145], "pop": 25175, "state": "IL", "_id": "60631"} -{"city": "CHICAGO", "loc": [-87.70518, 41.809274], "pop": 62368, "state": "IL", "_id": "60632"} -{"city": "BURNHAM", "loc": [-87.549489, 41.649791], "pop": 12367, "state": "IL", "_id": "60633"} -{"city": "NORRIDGE", "loc": [-87.796054, 41.945213], "pop": 69160, "state": "IL", "_id": "60634"} -{"city": "ELMWOOD PARK", "loc": [-87.808593, 41.922907], "pop": 38056, "state": "IL", "_id": "60635"} -{"city": "CHICAGO", "loc": [-87.667368, 41.775989], "pop": 58048, "state": "IL", "_id": "60636"} -{"city": "CHICAGO", "loc": [-87.605097, 41.781312], "pop": 59637, "state": "IL", "_id": "60637"} -{"city": "BEDFORD PARK", "loc": [-87.771927, 41.789703], "pop": 53145, "state": "IL", "_id": "60638"} -{"city": "CHICAGO", "loc": [-87.753502, 41.920162], "pop": 74209, "state": "IL", "_id": "60639"} -{"city": "CHICAGO", "loc": [-87.662405, 41.971928], "pop": 76829, "state": "IL", "_id": "60640"} -{"city": "CHICAGO", "loc": [-87.747376, 41.945333], "pop": 59870, "state": "IL", "_id": "60641"} -{"city": "EVERGREEN PARK", "loc": [-87.701721, 41.718765], "pop": 24016, "state": "IL", "_id": "60642"} -{"city": "CALUMET PARK", "loc": [-87.659445, 41.693243], "pop": 63953, "state": "IL", "_id": "60643"} -{"city": "CHICAGO", "loc": [-87.758163, 41.882913], "pop": 57376, "state": "IL", "_id": "60644"} -{"city": "LINCOLNWOOD", "loc": [-87.6962, 42.007718], "pop": 43829, "state": "IL", "_id": "60645"} -{"city": "LINCOLNWOOD", "loc": [-87.759172, 41.996414], "pop": 32075, "state": "IL", "_id": "60646"} -{"city": "CHICAGO", "loc": [-87.704322, 41.920903], "pop": 95971, "state": "IL", "_id": "60647"} -{"city": "CHICAGO", "loc": [-87.81636, 42.031101], "pop": 30924, "state": "IL", "_id": "60648"} -{"city": "CHICAGO", "loc": [-87.570252, 41.761968], "pop": 54795, "state": "IL", "_id": "60649"} -{"city": "CICERO", "loc": [-87.76008, 41.84776], "pop": 67670, "state": "IL", "_id": "60650"} -{"city": "CHICAGO", "loc": [-87.739307, 41.902509], "pop": 78082, "state": "IL", "_id": "60651"} -{"city": "CHICAGO", "loc": [-87.713516, 41.745393], "pop": 36337, "state": "IL", "_id": "60652"} -{"city": "CHICAGO", "loc": [-87.612605, 41.819645], "pop": 40091, "state": "IL", "_id": "60653"} -{"city": "CHICAGO", "loc": [-87.635292, 41.888533], "pop": 0, "state": "IL", "_id": "60654"} -{"city": "MERRIONETTE PARK", "loc": [-87.702188, 41.693033], "pop": 29847, "state": "IL", "_id": "60655"} -{"city": "HARWOOD HEIGHTS", "loc": [-87.819981, 41.971844], "pop": 43597, "state": "IL", "_id": "60656"} -{"city": "CHICAGO", "loc": [-87.652805, 41.93992], "pop": 65533, "state": "IL", "_id": "60657"} -{"city": "ALSIP", "loc": [-87.729967, 41.671505], "pop": 16461, "state": "IL", "_id": "60658"} -{"city": "LINCOLNWOOD", "loc": [-87.700823, 41.991687], "pop": 35461, "state": "IL", "_id": "60659"} -{"city": "CHICAGO", "loc": [-87.662856, 41.990879], "pop": 45106, "state": "IL", "_id": "60660"} -{"city": "CHICAGO", "loc": [-87.642969, 41.881351], "pop": 2031, "state": "IL", "_id": "60661"} -{"city": "AMF OHARE", "loc": [-87.906803, 41.9821], "pop": 262, "state": "IL", "_id": "60666"} -{"city": "KANKAKEE", "loc": [-87.869607, 41.116582], "pop": 35952, "state": "IL", "_id": "60901"} -{"city": "AROMA PARK", "loc": [-87.771887, 41.094653], "pop": 3151, "state": "IL", "_id": "60910"} -{"city": "ASHKUM", "loc": [-87.941148, 40.884431], "pop": 1484, "state": "IL", "_id": "60911"} -{"city": "BEAVERVILLE", "loc": [-87.621715, 40.967164], "pop": 672, "state": "IL", "_id": "60912"} -{"city": "BONFIELD", "loc": [-88.061854, 41.15731], "pop": 1189, "state": "IL", "_id": "60913"} -{"city": "BOURBONNAIS", "loc": [-87.879023, 41.166119], "pop": 18311, "state": "IL", "_id": "60914"} -{"city": "BRADLEY", "loc": [-87.860115, 41.145376], "pop": 10071, "state": "IL", "_id": "60915"} -{"city": "BUCKINGHAM", "loc": [-88.177156, 41.043316], "pop": 557, "state": "IL", "_id": "60917"} -{"city": "BUCKLEY", "loc": [-88.036092, 40.601827], "pop": 875, "state": "IL", "_id": "60918"} -{"city": "CABERY", "loc": [-88.192085, 40.981895], "pop": 510, "state": "IL", "_id": "60919"} -{"city": "CHATSWORTH", "loc": [-88.293662, 40.748441], "pop": 1703, "state": "IL", "_id": "60921"} -{"city": "CHEBANSE", "loc": [-87.895917, 41.02541], "pop": 3580, "state": "IL", "_id": "60922"} -{"city": "CISSNA PARK", "loc": [-87.87588, 40.585814], "pop": 2282, "state": "IL", "_id": "60924"} -{"city": "CLIFTON", "loc": [-87.920237, 40.939444], "pop": 2033, "state": "IL", "_id": "60927"} -{"city": "CRESCENT CITY", "loc": [-87.83703, 40.7682], "pop": 1271, "state": "IL", "_id": "60928"} -{"city": "CULLOM", "loc": [-88.276476, 40.878066], "pop": 782, "state": "IL", "_id": "60929"} -{"city": "DANFORTH", "loc": [-87.986824, 40.82443], "pop": 959, "state": "IL", "_id": "60930"} -{"city": "DONOVAN", "loc": [-87.604635, 40.889074], "pop": 613, "state": "IL", "_id": "60931"} -{"city": "EMINGTON", "loc": [-88.321135, 40.978317], "pop": 330, "state": "IL", "_id": "60934"} -{"city": "ESSEX", "loc": [-88.184528, 41.167644], "pop": 994, "state": "IL", "_id": "60935"} -{"city": "GIBSON CITY", "loc": [-88.360873, 40.465932], "pop": 4608, "state": "IL", "_id": "60936"} -{"city": "GILMAN", "loc": [-87.993336, 40.767987], "pop": 2125, "state": "IL", "_id": "60938"} -{"city": "GRANT PARK", "loc": [-87.647992, 41.247677], "pop": 3009, "state": "IL", "_id": "60940"} -{"city": "HERSCHER", "loc": [-88.085801, 41.046441], "pop": 2138, "state": "IL", "_id": "60941"} -{"city": "HOOPESTON", "loc": [-87.666229, 40.463873], "pop": 6600, "state": "IL", "_id": "60942"} -{"city": "KEMPTON", "loc": [-88.209013, 40.912604], "pop": 462, "state": "IL", "_id": "60946"} -{"city": "LODA", "loc": [-88.092675, 40.524097], "pop": 1474, "state": "IL", "_id": "60948"} -{"city": "LUDLOW", "loc": [-88.137965, 40.374736], "pop": 832, "state": "IL", "_id": "60949"} -{"city": "MANTENO", "loc": [-87.846761, 41.251439], "pop": 5673, "state": "IL", "_id": "60950"} -{"city": "MARTINTON", "loc": [-87.744257, 40.905233], "pop": 1004, "state": "IL", "_id": "60951"} -{"city": "MELVIN", "loc": [-88.255078, 40.571379], "pop": 654, "state": "IL", "_id": "60952"} -{"city": "MILFORD", "loc": [-87.685332, 40.629253], "pop": 2367, "state": "IL", "_id": "60953"} -{"city": "MOMENCE", "loc": [-87.657515, 41.159308], "pop": 6804, "state": "IL", "_id": "60954"} -{"city": "ONARGA", "loc": [-87.995841, 40.712005], "pop": 1678, "state": "IL", "_id": "60955"} -{"city": "PAXTON", "loc": [-88.098989, 40.456546], "pop": 5226, "state": "IL", "_id": "60957"} -{"city": "PIPER CITY", "loc": [-88.187347, 40.755615], "pop": 1187, "state": "IL", "_id": "60959"} -{"city": "RANKIN", "loc": [-87.888355, 40.455911], "pop": 1697, "state": "IL", "_id": "60960"} -{"city": "REDDICK", "loc": [-88.208928, 41.10053], "pop": 480, "state": "IL", "_id": "60961"} -{"city": "ROBERTS", "loc": [-88.180414, 40.619333], "pop": 617, "state": "IL", "_id": "60962"} -{"city": "ROSSVILLE", "loc": [-87.669181, 40.362548], "pop": 2082, "state": "IL", "_id": "60963"} -{"city": "SAINT ANNE", "loc": [-87.656363, 41.048725], "pop": 6081, "state": "IL", "_id": "60964"} -{"city": "SHELDON", "loc": [-87.57364, 40.780288], "pop": 1966, "state": "IL", "_id": "60966"} -{"city": "THAWVILLE", "loc": [-88.09993, 40.684011], "pop": 374, "state": "IL", "_id": "60968"} -{"city": "WATSEKA", "loc": [-87.730932, 40.773351], "pop": 7072, "state": "IL", "_id": "60970"} -{"city": "WELLINGTON", "loc": [-87.65607, 40.53394], "pop": 782, "state": "IL", "_id": "60973"} -{"city": "APPLE RIVER", "loc": [-90.120145, 42.471432], "pop": 1010, "state": "IL", "_id": "61001"} -{"city": "ASHTON", "loc": [-89.2086, 41.864327], "pop": 1911, "state": "IL", "_id": "61006"} -{"city": "BAILEYVILLE", "loc": [-89.593937, 42.190465], "pop": 430, "state": "IL", "_id": "61007"} -{"city": "BELVIDERE", "loc": [-88.850943, 42.259465], "pop": 22199, "state": "IL", "_id": "61008"} -{"city": "BYRON", "loc": [-89.265887, 42.129236], "pop": 4894, "state": "IL", "_id": "61010"} -{"city": "CALEDONIA", "loc": [-88.918456, 42.38346], "pop": 2086, "state": "IL", "_id": "61011"} -{"city": "CAPRON", "loc": [-88.746518, 42.408659], "pop": 1893, "state": "IL", "_id": "61012"} -{"city": "CHADWICK", "loc": [-89.896278, 41.996205], "pop": 1252, "state": "IL", "_id": "61014"} -{"city": "CHANA", "loc": [-89.211693, 41.993343], "pop": 1167, "state": "IL", "_id": "61015"} -{"city": "CHERRY VALLEY", "loc": [-88.961923, 42.220562], "pop": 3768, "state": "IL", "_id": "61016"} -{"city": "DAKOTA", "loc": [-89.546782, 42.403078], "pop": 1134, "state": "IL", "_id": "61018"} -{"city": "DAVIS", "loc": [-89.406721, 42.442157], "pop": 2337, "state": "IL", "_id": "61019"} -{"city": "DAVIS JUNCTION", "loc": [-89.083838, 42.09792], "pop": 1263, "state": "IL", "_id": "61020"} -{"city": "DIXON", "loc": [-89.489303, 41.847797], "pop": 22293, "state": "IL", "_id": "61021"} -{"city": "DURAND", "loc": [-89.309378, 42.433653], "pop": 2633, "state": "IL", "_id": "61024"} -{"city": "EAST DUBUQUE", "loc": [-90.604597, 42.487488], "pop": 4999, "state": "IL", "_id": "61025"} -{"city": "ELIZABETH", "loc": [-90.19862, 42.308942], "pop": 1951, "state": "IL", "_id": "61028"} -{"city": "FORRESTON", "loc": [-89.583124, 42.122924], "pop": 2261, "state": "IL", "_id": "61030"} -{"city": "FRANKLIN GROVE", "loc": [-89.317112, 41.857968], "pop": 2070, "state": "IL", "_id": "61031"} -{"city": "FREEPORT", "loc": [-89.634521, 42.299148], "pop": 33259, "state": "IL", "_id": "61032"} -{"city": "GALENA", "loc": [-90.419509, 42.418233], "pop": 5479, "state": "IL", "_id": "61036"} -{"city": "GARDEN PRAIRIE", "loc": [-88.74367, 42.250983], "pop": 1584, "state": "IL", "_id": "61038"} -{"city": "GERMAN VALLEY", "loc": [-89.471151, 42.21761], "pop": 1003, "state": "IL", "_id": "61039"} -{"city": "HANOVER", "loc": [-90.289707, 42.259405], "pop": 1559, "state": "IL", "_id": "61041"} -{"city": "HARMON", "loc": [-89.569513, 41.697296], "pop": 793, "state": "IL", "_id": "61042"} -{"city": "KENT", "loc": [-89.919489, 42.315528], "pop": 425, "state": "IL", "_id": "61044"} -{"city": "KINGS", "loc": [-89.099685, 42.013987], "pop": 377, "state": "IL", "_id": "61045"} -{"city": "LANARK", "loc": [-89.824732, 42.093534], "pop": 1843, "state": "IL", "_id": "61046"} -{"city": "EGAN", "loc": [-89.401024, 42.140975], "pop": 1804, "state": "IL", "_id": "61047"} -{"city": "LENA", "loc": [-89.825251, 42.379054], "pop": 4251, "state": "IL", "_id": "61048"} -{"city": "LINDENWOOD", "loc": [-89.033979, 42.050741], "pop": 351, "state": "IL", "_id": "61049"} -{"city": "MC CONNELL", "loc": [-89.741545, 42.439511], "pop": 357, "state": "IL", "_id": "61050"} -{"city": "MILLEDGEVILLE", "loc": [-89.780121, 41.96737], "pop": 1526, "state": "IL", "_id": "61051"} -{"city": "MONROE CENTER", "loc": [-89.016946, 42.10501], "pop": 1327, "state": "IL", "_id": "61052"} -{"city": "MOUNT CARROLL", "loc": [-89.984454, 42.105308], "pop": 3529, "state": "IL", "_id": "61053"} -{"city": "MOUNT MORRIS", "loc": [-89.434614, 42.047903], "pop": 4169, "state": "IL", "_id": "61054"} -{"city": "ORANGEVILLE", "loc": [-89.644757, 42.472779], "pop": 1318, "state": "IL", "_id": "61060"} -{"city": "OREGON", "loc": [-89.344364, 42.009512], "pop": 6482, "state": "IL", "_id": "61061"} -{"city": "PEARL CITY", "loc": [-89.839329, 42.260972], "pop": 1987, "state": "IL", "_id": "61062"} -{"city": "PECATONICA", "loc": [-89.347225, 42.305111], "pop": 3554, "state": "IL", "_id": "61063"} -{"city": "POLO", "loc": [-89.598358, 41.98895], "pop": 4524, "state": "IL", "_id": "61064"} -{"city": "POPLAR GROVE", "loc": [-88.842774, 42.359365], "pop": 2593, "state": "IL", "_id": "61065"} -{"city": "RIDOTT", "loc": [-89.462664, 42.299607], "pop": 750, "state": "IL", "_id": "61067"} -{"city": "ROCHELLE", "loc": [-89.071035, 41.928156], "pop": 12890, "state": "IL", "_id": "61068"} -{"city": "ROCK CITY", "loc": [-89.475901, 42.410345], "pop": 1420, "state": "IL", "_id": "61070"} -{"city": "ROCK FALLS", "loc": [-89.692473, 41.766525], "pop": 14548, "state": "IL", "_id": "61071"} -{"city": "ROCKTON", "loc": [-89.088744, 42.454371], "pop": 6514, "state": "IL", "_id": "61072"} -{"city": "ROSCOE", "loc": [-88.99433, 42.421659], "pop": 10391, "state": "IL", "_id": "61073"} -{"city": "SAVANNA", "loc": [-90.140061, 42.095581], "pop": 4943, "state": "IL", "_id": "61074"} -{"city": "SCALES MOUND", "loc": [-90.258033, 42.471548], "pop": 909, "state": "IL", "_id": "61075"} -{"city": "SHANNON", "loc": [-89.748075, 42.161049], "pop": 1753, "state": "IL", "_id": "61078"} -{"city": "SOUTH BELOIT", "loc": [-89.029791, 42.483672], "pop": 6833, "state": "IL", "_id": "61080"} -{"city": "STERLING", "loc": [-89.70539, 41.805511], "pop": 22261, "state": "IL", "_id": "61081"} -{"city": "STILLMAN VALLEY", "loc": [-89.189762, 42.11835], "pop": 2772, "state": "IL", "_id": "61084"} -{"city": "STOCKTON", "loc": [-90.020185, 42.349224], "pop": 3489, "state": "IL", "_id": "61085"} -{"city": "WARREN", "loc": [-89.985992, 42.489001], "pop": 1967, "state": "IL", "_id": "61087"} -{"city": "WINNEBAGO", "loc": [-89.237316, 42.272723], "pop": 4059, "state": "IL", "_id": "61088"} -{"city": "WINSLOW", "loc": [-89.806028, 42.48383], "pop": 887, "state": "IL", "_id": "61089"} -{"city": "ROCKFORD", "loc": [-89.116118, 42.292233], "pop": 23908, "state": "IL", "_id": "61101"} -{"city": "ROCKFORD", "loc": [-89.124695, 42.254669], "pop": 19427, "state": "IL", "_id": "61102"} -{"city": "ROCKFORD", "loc": [-89.083326, 42.300986], "pop": 24143, "state": "IL", "_id": "61103"} -{"city": "ROCKFORD", "loc": [-89.076779, 42.255355], "pop": 19912, "state": "IL", "_id": "61104"} -{"city": "ROCKFORD", "loc": [-89.036107, 42.278629], "pop": 28879, "state": "IL", "_id": "61107"} -{"city": "ROCKFORD", "loc": [-89.023519, 42.251406], "pop": 25501, "state": "IL", "_id": "61108"} -{"city": "ROCKFORD", "loc": [-89.05118, 42.216581], "pop": 25246, "state": "IL", "_id": "61109"} -{"city": "LOVES PARK", "loc": [-89.033521, 42.32952], "pop": 47733, "state": "IL", "_id": "61111"} -{"city": "ROCKFORD", "loc": [-88.970429, 42.245639], "pop": 15, "state": "IL", "_id": "61112"} -{"city": "ROCK ISLAND", "loc": [-90.564796, 41.491317], "pop": 37799, "state": "IL", "_id": "61201"} -{"city": "ALBANY", "loc": [-90.208051, 41.765874], "pop": 1287, "state": "IL", "_id": "61230"} -{"city": "ALEDO", "loc": [-90.741629, 41.20078], "pop": 5189, "state": "IL", "_id": "61231"} -{"city": "ANDALUSIA", "loc": [-90.728385, 41.435324], "pop": 1899, "state": "IL", "_id": "61232"} -{"city": "ANNAWAN", "loc": [-89.912949, 41.398022], "pop": 1432, "state": "IL", "_id": "61234"} -{"city": "ATKINSON", "loc": [-90.022482, 41.41619], "pop": 1619, "state": "IL", "_id": "61235"} -{"city": "CAMBRIDGE", "loc": [-90.18048, 41.311379], "pop": 3265, "state": "IL", "_id": "61238"} -{"city": "COAL VALLEY", "loc": [-90.465179, 41.435143], "pop": 5435, "state": "IL", "_id": "61240"} -{"city": "GREEN ROCK", "loc": [-90.349231, 41.475224], "pop": 7809, "state": "IL", "_id": "61241"} -{"city": "CORDOVA", "loc": [-90.307121, 41.69278], "pop": 954, "state": "IL", "_id": "61242"} -{"city": "DEER GROVE", "loc": [-89.6972, 41.631599], "pop": 393, "state": "IL", "_id": "61243"} -{"city": "EAST MOLINE", "loc": [-90.432118, 41.511804], "pop": 24023, "state": "IL", "_id": "61244"} -{"city": "ERIE", "loc": [-90.084264, 41.655958], "pop": 2428, "state": "IL", "_id": "61250"} -{"city": "FENTON", "loc": [-90.045685, 41.728495], "pop": 289, "state": "IL", "_id": "61251"} -{"city": "FULTON", "loc": [-90.150653, 41.8522], "pop": 5743, "state": "IL", "_id": "61252"} -{"city": "GENESEO", "loc": [-90.171127, 41.46881], "pop": 10023, "state": "IL", "_id": "61254"} -{"city": "HAMPTON", "loc": [-90.323037, 41.541805], "pop": 538, "state": "IL", "_id": "61256"} -{"city": "HILLSDALE", "loc": [-90.226255, 41.592896], "pop": 1807, "state": "IL", "_id": "61257"} -{"city": "ILLINOIS CITY", "loc": [-90.892507, 41.389236], "pop": 1553, "state": "IL", "_id": "61259"} -{"city": "JOY", "loc": [-90.851757, 41.226198], "pop": 1185, "state": "IL", "_id": "61260"} -{"city": "LYNDON", "loc": [-89.916865, 41.719933], "pop": 936, "state": "IL", "_id": "61261"} -{"city": "LYNN CENTER", "loc": [-90.330444, 41.288761], "pop": 1694, "state": "IL", "_id": "61262"} -{"city": "MATHERVILLE", "loc": [-90.602343, 41.269075], "pop": 1409, "state": "IL", "_id": "61263"} -{"city": "MILAN", "loc": [-90.573935, 41.426197], "pop": 14565, "state": "IL", "_id": "61264"} -{"city": "MOLINE", "loc": [-90.497968, 41.490609], "pop": 45240, "state": "IL", "_id": "61265"} -{"city": "MORRISON", "loc": [-89.968995, 41.816664], "pop": 7580, "state": "IL", "_id": "61270"} -{"city": "NEW BOSTON", "loc": [-90.98786, 41.215259], "pop": 1663, "state": "IL", "_id": "61272"} -{"city": "ORION", "loc": [-90.384929, 41.363367], "pop": 3121, "state": "IL", "_id": "61273"} -{"city": "OSCO", "loc": [-90.268093, 41.363674], "pop": 538, "state": "IL", "_id": "61274"} -{"city": "PORT BYRON", "loc": [-90.326291, 41.601346], "pop": 3441, "state": "IL", "_id": "61275"} -{"city": "PROPHETSTOWN", "loc": [-89.946665, 41.631223], "pop": 3736, "state": "IL", "_id": "61277"} -{"city": "REYNOLDS", "loc": [-90.638367, 41.327675], "pop": 944, "state": "IL", "_id": "61279"} -{"city": "SHERRARD", "loc": [-90.493863, 41.302669], "pop": 2192, "state": "IL", "_id": "61281"} -{"city": "SILVIS", "loc": [-90.412609, 41.500677], "pop": 9832, "state": "IL", "_id": "61282"} -{"city": "TAMPICO", "loc": [-89.794793, 41.652158], "pop": 1753, "state": "IL", "_id": "61283"} -{"city": "TAYLOR RIDGE", "loc": [-90.734047, 41.382755], "pop": 1090, "state": "IL", "_id": "61284"} -{"city": "THOMSON", "loc": [-90.084443, 41.981626], "pop": 1868, "state": "IL", "_id": "61285"} -{"city": "LA SALLE", "loc": [-89.095468, 41.344221], "pop": 10188, "state": "IL", "_id": "61301"} -{"city": "AMBOY", "loc": [-89.34716, 41.704181], "pop": 3994, "state": "IL", "_id": "61310"} -{"city": "ANCONA", "loc": [-88.766039, 41.055118], "pop": 38, "state": "IL", "_id": "61311"} -{"city": "ARLINGTON", "loc": [-89.221949, 41.443669], "pop": 1044, "state": "IL", "_id": "61312"} -{"city": "BLACKSTONE", "loc": [-88.649782, 41.071945], "pop": 258, "state": "IL", "_id": "61313"} -{"city": "BUDA", "loc": [-89.679476, 41.313973], "pop": 931, "state": "IL", "_id": "61314"} -{"city": "COMPTON", "loc": [-89.087708, 41.684976], "pop": 551, "state": "IL", "_id": "61318"} -{"city": "MANVILLE", "loc": [-88.742694, 40.985827], "pop": 968, "state": "IL", "_id": "61319"} -{"city": "DALZELL", "loc": [-89.203269, 41.373077], "pop": 2255, "state": "IL", "_id": "61320"} -{"city": "DANA", "loc": [-88.962793, 40.954675], "pop": 257, "state": "IL", "_id": "61321"} -{"city": "GRAND RIDGE", "loc": [-88.816836, 41.238621], "pop": 1179, "state": "IL", "_id": "61325"} -{"city": "GRANVILLE", "loc": [-89.225029, 41.264212], "pop": 2784, "state": "IL", "_id": "61326"} -{"city": "HENNEPIN", "loc": [-89.321791, 41.235154], "pop": 1111, "state": "IL", "_id": "61327"} -{"city": "LA MOILLE", "loc": [-89.297024, 41.537557], "pop": 1315, "state": "IL", "_id": "61330"} -{"city": "LEONORE", "loc": [-88.996936, 41.166368], "pop": 437, "state": "IL", "_id": "61332"} -{"city": "LONG POINT", "loc": [-88.881106, 40.989553], "pop": 541, "state": "IL", "_id": "61333"} -{"city": "LOSTANT", "loc": [-89.075031, 41.145007], "pop": 747, "state": "IL", "_id": "61334"} -{"city": "MC NABB", "loc": [-89.218664, 41.173026], "pop": 747, "state": "IL", "_id": "61335"} -{"city": "MAGNOLIA", "loc": [-89.22701, 41.116374], "pop": 519, "state": "IL", "_id": "61336"} -{"city": "MALDEN", "loc": [-89.36761, 41.437743], "pop": 1109, "state": "IL", "_id": "61337"} -{"city": "MARSEILLES", "loc": [-88.694678, 41.330201], "pop": 7360, "state": "IL", "_id": "61341"} -{"city": "MENDOTA", "loc": [-89.10828, 41.544308], "pop": 9660, "state": "IL", "_id": "61342"} -{"city": "MINERAL", "loc": [-89.820087, 41.403556], "pop": 648, "state": "IL", "_id": "61344"} -{"city": "NEPONSET", "loc": [-89.794382, 41.290457], "pop": 819, "state": "IL", "_id": "61345"} -{"city": "NEW BEDFORD", "loc": [-89.778558, 41.540883], "pop": 641, "state": "IL", "_id": "61346"} -{"city": "OGLESBY", "loc": [-89.055341, 41.292768], "pop": 5323, "state": "IL", "_id": "61348"} -{"city": "OHIO", "loc": [-89.457411, 41.537149], "pop": 1031, "state": "IL", "_id": "61349"} -{"city": "OTTAWA", "loc": [-88.841589, 41.352619], "pop": 23727, "state": "IL", "_id": "61350"} -{"city": "PAW PAW", "loc": [-88.967377, 41.685228], "pop": 1539, "state": "IL", "_id": "61353"} -{"city": "PERU", "loc": [-89.126478, 41.333021], "pop": 10050, "state": "IL", "_id": "61354"} -{"city": "PRINCETON", "loc": [-89.427017, 41.362934], "pop": 12333, "state": "IL", "_id": "61356"} -{"city": "RUTLAND", "loc": [-89.038829, 40.984407], "pop": 629, "state": "IL", "_id": "61358"} -{"city": "SENECA", "loc": [-88.610013, 41.315248], "pop": 2246, "state": "IL", "_id": "61360"} -{"city": "SHEFFIELD", "loc": [-89.711502, 41.394876], "pop": 1964, "state": "IL", "_id": "61361"} -{"city": "SPRING VALLEY", "loc": [-89.20417, 41.327923], "pop": 5541, "state": "IL", "_id": "61362"} -{"city": "STREATOR", "loc": [-88.830672, 41.12249], "pop": 22239, "state": "IL", "_id": "61364"} -{"city": "SUBLETTE", "loc": [-89.235409, 41.633144], "pop": 899, "state": "IL", "_id": "61367"} -{"city": "TISKILWA", "loc": [-89.507968, 41.289055], "pop": 1587, "state": "IL", "_id": "61368"} -{"city": "TOLUCA", "loc": [-89.13481, 41.004553], "pop": 1755, "state": "IL", "_id": "61369"} -{"city": "TONICA", "loc": [-89.088993, 41.232741], "pop": 1409, "state": "IL", "_id": "61370"} -{"city": "UTICA", "loc": [-89.000795, 41.363033], "pop": 1926, "state": "IL", "_id": "61373"} -{"city": "VARNA", "loc": [-89.248331, 41.032723], "pop": 1527, "state": "IL", "_id": "61375"} -{"city": "NORMANDY", "loc": [-89.592237, 41.553035], "pop": 2050, "state": "IL", "_id": "61376"} -{"city": "WENONA", "loc": [-89.041637, 41.054846], "pop": 1410, "state": "IL", "_id": "61377"} -{"city": "WEST BROOKLYN", "loc": [-89.190917, 41.729156], "pop": 946, "state": "IL", "_id": "61378"} -{"city": "WYANET", "loc": [-89.574423, 41.378452], "pop": 1799, "state": "IL", "_id": "61379"} -{"city": "GALESBURG", "loc": [-90.369807, 40.952138], "pop": 36161, "state": "IL", "_id": "61401"} -{"city": "ABINGDON", "loc": [-90.400898, 40.802312], "pop": 4241, "state": "IL", "_id": "61410"} -{"city": "ADAIR", "loc": [-90.503742, 40.385197], "pop": 731, "state": "IL", "_id": "61411"} -{"city": "ALEXIS", "loc": [-90.543576, 41.052146], "pop": 1866, "state": "IL", "_id": "61412"} -{"city": "ALPHA", "loc": [-90.382081, 41.193029], "pop": 1152, "state": "IL", "_id": "61413"} -{"city": "ALTONA", "loc": [-90.159826, 41.112828], "pop": 813, "state": "IL", "_id": "61414"} -{"city": "AVON", "loc": [-90.446053, 40.654947], "pop": 2125, "state": "IL", "_id": "61415"} -{"city": "BARDOLPH", "loc": [-90.502495, 40.498078], "pop": 365, "state": "IL", "_id": "61416"} -{"city": "BERWICK", "loc": [-90.505913, 40.779911], "pop": 461, "state": "IL", "_id": "61417"} -{"city": "BIGGSVILLE", "loc": [-90.856057, 40.853122], "pop": 627, "state": "IL", "_id": "61418"} -{"city": "BLANDINSVILLE", "loc": [-90.859521, 40.551585], "pop": 1272, "state": "IL", "_id": "61420"} -{"city": "BRADFORD", "loc": [-89.652077, 41.15323], "pop": 2071, "state": "IL", "_id": "61421"} -{"city": "BUSHNELL", "loc": [-90.506027, 40.553916], "pop": 3511, "state": "IL", "_id": "61422"} -{"city": "CAMERON", "loc": [-90.50013, 40.888963], "pop": 969, "state": "IL", "_id": "61423"} -{"city": "CARMAN", "loc": [-91.056396, 40.755054], "pop": 398, "state": "IL", "_id": "61425"} -{"city": "CUBA", "loc": [-90.181055, 40.4995], "pop": 2169, "state": "IL", "_id": "61427"} -{"city": "DAHINDA", "loc": [-90.139808, 40.95508], "pop": 710, "state": "IL", "_id": "61428"} -{"city": "ELLISVILLE", "loc": [-90.287469, 40.604652], "pop": 565, "state": "IL", "_id": "61431"} -{"city": "FAIRVIEW", "loc": [-90.165265, 40.64418], "pop": 702, "state": "IL", "_id": "61432"} -{"city": "FIATT", "loc": [-90.162186, 40.571779], "pop": 495, "state": "IL", "_id": "61433"} -{"city": "GALVA", "loc": [-90.048093, 41.165627], "pop": 3725, "state": "IL", "_id": "61434"} -{"city": "GERLAW", "loc": [-90.622765, 40.999519], "pop": 520, "state": "IL", "_id": "61435"} -{"city": "GILSON", "loc": [-90.174663, 40.876525], "pop": 696, "state": "IL", "_id": "61436"} -{"city": "GLADSTONE", "loc": [-90.994078, 40.837682], "pop": 1166, "state": "IL", "_id": "61437"} -{"city": "GOOD HOPE", "loc": [-90.632432, 40.574891], "pop": 499, "state": "IL", "_id": "61438"} -{"city": "INDUSTRY", "loc": [-90.610524, 40.3256], "pop": 885, "state": "IL", "_id": "61440"} -{"city": "IPAVA", "loc": [-90.296744, 40.359375], "pop": 1152, "state": "IL", "_id": "61441"} -{"city": "KEITHSBURG", "loc": [-90.926337, 41.104333], "pop": 1024, "state": "IL", "_id": "61442"} -{"city": "KEWANEE", "loc": [-89.92739, 41.241116], "pop": 14861, "state": "IL", "_id": "61443"} -{"city": "KIRKWOOD", "loc": [-90.745659, 40.863849], "pop": 1171, "state": "IL", "_id": "61447"} -{"city": "KNOXVILLE", "loc": [-90.287116, 40.910672], "pop": 5958, "state": "IL", "_id": "61448"} -{"city": "LA FAYETTE", "loc": [-89.957466, 41.109535], "pop": 454, "state": "IL", "_id": "61449"} -{"city": "LA HARPE", "loc": [-90.968746, 40.584586], "pop": 1686, "state": "IL", "_id": "61450"} -{"city": "LAURA", "loc": [-89.934908, 40.933468], "pop": 500, "state": "IL", "_id": "61451"} -{"city": "LITTLETON", "loc": [-90.619008, 40.233929], "pop": 386, "state": "IL", "_id": "61452"} -{"city": "LITTLE YORK", "loc": [-90.736434, 41.01529], "pop": 679, "state": "IL", "_id": "61453"} -{"city": "LOMAX", "loc": [-91.039096, 40.676143], "pop": 1036, "state": "IL", "_id": "61454"} -{"city": "MACOMB", "loc": [-90.678674, 40.461674], "pop": 23503, "state": "IL", "_id": "61455"} -{"city": "MAQUON", "loc": [-90.200841, 40.784863], "pop": 1226, "state": "IL", "_id": "61458"} -{"city": "MARIETTA", "loc": [-90.38846, 40.497775], "pop": 421, "state": "IL", "_id": "61459"} -{"city": "MEDIA", "loc": [-90.85696, 40.761775], "pop": 484, "state": "IL", "_id": "61460"} -{"city": "MONMOUTH", "loc": [-90.644828, 40.910702], "pop": 11245, "state": "IL", "_id": "61462"} -{"city": "NEW WINDSOR", "loc": [-90.459842, 41.198734], "pop": 1171, "state": "IL", "_id": "61465"} -{"city": "NORTH HENDERSON", "loc": [-90.473571, 41.100607], "pop": 390, "state": "IL", "_id": "61466"} -{"city": "ONEIDA", "loc": [-90.239093, 41.083236], "pop": 1122, "state": "IL", "_id": "61467"} -{"city": "OQUAWKA", "loc": [-90.930199, 40.944174], "pop": 2410, "state": "IL", "_id": "61469"} -{"city": "PRAIRIE CITY", "loc": [-90.472748, 40.617952], "pop": 651, "state": "IL", "_id": "61470"} -{"city": "RARITAN", "loc": [-90.831891, 40.687808], "pop": 345, "state": "IL", "_id": "61471"} -{"city": "RIO", "loc": [-90.389978, 41.110319], "pop": 570, "state": "IL", "_id": "61472"} -{"city": "ROSEVILLE", "loc": [-90.65145, 40.723821], "pop": 1734, "state": "IL", "_id": "61473"} -{"city": "SAINT AUGUSTINE", "loc": [-90.379781, 40.7289], "pop": 376, "state": "IL", "_id": "61474"} -{"city": "SCIOTA", "loc": [-90.707999, 40.567361], "pop": 622, "state": "IL", "_id": "61475"} -{"city": "SEATON", "loc": [-90.825654, 41.073222], "pop": 704, "state": "IL", "_id": "61476"} -{"city": "SMITHFIELD", "loc": [-90.285601, 40.48551], "pop": 647, "state": "IL", "_id": "61477"} -{"city": "SMITHSHIRE", "loc": [-90.739874, 40.740021], "pop": 568, "state": "IL", "_id": "61478"} -{"city": "SPEER", "loc": [-89.693254, 41.009682], "pop": 398, "state": "IL", "_id": "61479"} -{"city": "STRONGHURST", "loc": [-90.925702, 40.752265], "pop": 1055, "state": "IL", "_id": "61480"} -{"city": "TABLE GROVE", "loc": [-90.423901, 40.378441], "pop": 429, "state": "IL", "_id": "61482"} -{"city": "TOULON", "loc": [-89.860584, 41.100949], "pop": 2378, "state": "IL", "_id": "61483"} -{"city": "VERMONT", "loc": [-90.422028, 40.30623], "pop": 1112, "state": "IL", "_id": "61484"} -{"city": "VICTORIA", "loc": [-90.093325, 41.025635], "pop": 669, "state": "IL", "_id": "61485"} -{"city": "VIOLA", "loc": [-90.593583, 41.202422], "pop": 1651, "state": "IL", "_id": "61486"} -{"city": "WATAGA", "loc": [-90.27231, 41.022351], "pop": 1197, "state": "IL", "_id": "61488"} -{"city": "WILLIAMSFIELD", "loc": [-90.026725, 40.927724], "pop": 912, "state": "IL", "_id": "61489"} -{"city": "WOODHULL", "loc": [-90.283282, 41.184887], "pop": 1390, "state": "IL", "_id": "61490"} -{"city": "WYOMING", "loc": [-89.778238, 41.059879], "pop": 1818, "state": "IL", "_id": "61491"} -{"city": "ASTORIA", "loc": [-90.344254, 40.231144], "pop": 2093, "state": "IL", "_id": "61501"} -{"city": "BENSON", "loc": [-89.116501, 40.83058], "pop": 838, "state": "IL", "_id": "61516"} -{"city": "BRIMFIELD", "loc": [-89.897038, 40.840654], "pop": 1177, "state": "IL", "_id": "61517"} -{"city": "OAK HILL", "loc": [-89.83725, 40.786938], "pop": 1007, "state": "IL", "_id": "61518"} -{"city": "BRYANT", "loc": [-90.066134, 40.483072], "pop": 1017, "state": "IL", "_id": "61519"} -{"city": "CANTON", "loc": [-90.024151, 40.560137], "pop": 16309, "state": "IL", "_id": "61520"} -{"city": "CHILLICOTHE", "loc": [-89.506793, 40.901349], "pop": 9929, "state": "IL", "_id": "61523"} -{"city": "DUNFERMLINE", "loc": [-90.031349, 40.490342], "pop": 319, "state": "IL", "_id": "61524"} -{"city": "DUNLAP", "loc": [-89.639655, 40.844417], "pop": 4669, "state": "IL", "_id": "61525"} -{"city": "EDELSTEIN", "loc": [-89.585812, 40.945367], "pop": 1866, "state": "IL", "_id": "61526"} -{"city": "EDWARDS", "loc": [-89.705344, 40.764362], "pop": 2896, "state": "IL", "_id": "61528"} -{"city": "ELMWOOD", "loc": [-89.928882, 40.772594], "pop": 2698, "state": "IL", "_id": "61529"} -{"city": "EUREKA", "loc": [-89.270561, 40.715249], "pop": 5688, "state": "IL", "_id": "61530"} -{"city": "MIDDLEGROVE", "loc": [-90.013434, 40.690265], "pop": 3569, "state": "IL", "_id": "61531"} -{"city": "FOREST CITY", "loc": [-89.833426, 40.35942], "pop": 670, "state": "IL", "_id": "61532"} -{"city": "GLASFORD", "loc": [-89.811326, 40.575976], "pop": 2531, "state": "IL", "_id": "61533"} -{"city": "GREEN VALLEY", "loc": [-89.654925, 40.41978], "pop": 1800, "state": "IL", "_id": "61534"} -{"city": "HANNA CITY", "loc": [-89.795242, 40.679776], "pop": 3255, "state": "IL", "_id": "61536"} -{"city": "HENRY", "loc": [-89.374328, 41.111543], "pop": 3255, "state": "IL", "_id": "61537"} -{"city": "KINGSTON MINES", "loc": [-89.806791, 40.49028], "pop": 1745, "state": "IL", "_id": "61539"} -{"city": "LACON", "loc": [-89.400842, 41.021587], "pop": 2809, "state": "IL", "_id": "61540"} -{"city": "LEWISTOWN", "loc": [-90.156287, 40.383046], "pop": 3849, "state": "IL", "_id": "61542"} -{"city": "LIVERPOOL", "loc": [-90.038972, 40.411574], "pop": 730, "state": "IL", "_id": "61543"} -{"city": "LONDON MILLS", "loc": [-90.261594, 40.694954], "pop": 746, "state": "IL", "_id": "61544"} -{"city": "CAZENOVIA", "loc": [-89.370399, 40.871082], "pop": 1247, "state": "IL", "_id": "61545"} -{"city": "MANITO", "loc": [-89.789791, 40.415991], "pop": 2593, "state": "IL", "_id": "61546"} -{"city": "MAPLETON", "loc": [-89.718429, 40.611699], "pop": 2593, "state": "IL", "_id": "61547"} -{"city": "METAMORA", "loc": [-89.430876, 40.784428], "pop": 9052, "state": "IL", "_id": "61548"} -{"city": "MORTON", "loc": [-89.460445, 40.614771], "pop": 15207, "state": "IL", "_id": "61550"} -{"city": "PEKIN", "loc": [-89.624332, 40.567435], "pop": 44902, "state": "IL", "_id": "61554"} -{"city": "PRINCEVILLE", "loc": [-89.772285, 40.909277], "pop": 3210, "state": "IL", "_id": "61559"} -{"city": "PUTNAM", "loc": [-89.440901, 41.19486], "pop": 724, "state": "IL", "_id": "61560"} -{"city": "ROANOKE", "loc": [-89.209334, 40.795601], "pop": 2653, "state": "IL", "_id": "61561"} -{"city": "SAINT DAVID", "loc": [-90.043739, 40.522571], "pop": 241, "state": "IL", "_id": "61563"} -{"city": "SPARLAND", "loc": [-89.45711, 41.013366], "pop": 1190, "state": "IL", "_id": "61565"} -{"city": "TOPEKA", "loc": [-89.93263, 40.38108], "pop": 969, "state": "IL", "_id": "61567"} -{"city": "TREMONT", "loc": [-89.483316, 40.505337], "pop": 4492, "state": "IL", "_id": "61568"} -{"city": "TRIVOLI", "loc": [-89.913546, 40.679506], "pop": 1166, "state": "IL", "_id": "61569"} -{"city": "WASHBURN", "loc": [-89.283049, 40.91413], "pop": 1771, "state": "IL", "_id": "61570"} -{"city": "SUNNYLAND", "loc": [-89.447926, 40.699364], "pop": 18931, "state": "IL", "_id": "61571"} -{"city": "YATES CITY", "loc": [-90.026481, 40.787826], "pop": 1402, "state": "IL", "_id": "61572"} -{"city": "PEORIA", "loc": [-89.601178, 40.687987], "pop": 740, "state": "IL", "_id": "61602"} -{"city": "PEORIA HEIGHTS", "loc": [-89.580813, 40.713915], "pop": 20163, "state": "IL", "_id": "61603"} -{"city": "PEORIA", "loc": [-89.632377, 40.711142], "pop": 33171, "state": "IL", "_id": "61604"} -{"city": "PEORIA", "loc": [-89.626325, 40.677512], "pop": 20320, "state": "IL", "_id": "61605"} -{"city": "PEORIA", "loc": [-89.612189, 40.698926], "pop": 10299, "state": "IL", "_id": "61606"} -{"city": "BARTONVILLE", "loc": [-89.673898, 40.652434], "pop": 10389, "state": "IL", "_id": "61607"} -{"city": "EAST PEORIA", "loc": [-89.55141, 40.673121], "pop": 29630, "state": "IL", "_id": "61611"} -{"city": "PEORIA HEIGHTS", "loc": [-89.603295, 40.75481], "pop": 35177, "state": "IL", "_id": "61614"} -{"city": "PEORIA", "loc": [-89.632083, 40.770165], "pop": 15452, "state": "IL", "_id": "61615"} -{"city": "BLOOMINGTON", "loc": [-88.989318, 40.478295], "pop": 35218, "state": "IL", "_id": "61701"} -{"city": "BLOOMINGTON", "loc": [-88.962466, 40.471618], "pop": 24135, "state": "IL", "_id": "61704"} -{"city": "ANCHOR", "loc": [-88.526581, 40.544091], "pop": 393, "state": "IL", "_id": "61720"} -{"city": "ARMINGTON", "loc": [-89.321775, 40.317046], "pop": 1163, "state": "IL", "_id": "61721"} -{"city": "ARROWSMITH", "loc": [-88.629648, 40.411966], "pop": 813, "state": "IL", "_id": "61722"} -{"city": "ATLANTA", "loc": [-89.230021, 40.258624], "pop": 1978, "state": "IL", "_id": "61723"} -{"city": "BELLFLOWER", "loc": [-88.522702, 40.340091], "pop": 702, "state": "IL", "_id": "61724"} -{"city": "CARLOCK", "loc": [-89.109779, 40.602898], "pop": 1066, "state": "IL", "_id": "61725"} -{"city": "CHENOA", "loc": [-88.721853, 40.744633], "pop": 2898, "state": "IL", "_id": "61726"} -{"city": "CLINTON", "loc": [-88.96266, 40.148708], "pop": 10043, "state": "IL", "_id": "61727"} -{"city": "COLFAX", "loc": [-88.620016, 40.570377], "pop": 1391, "state": "IL", "_id": "61728"} -{"city": "CONGERVILLE", "loc": [-89.199397, 40.620762], "pop": 802, "state": "IL", "_id": "61729"} -{"city": "COOKSVILLE", "loc": [-88.735047, 40.536014], "pop": 478, "state": "IL", "_id": "61730"} -{"city": "CROPSEY", "loc": [-88.494343, 40.602983], "pop": 240, "state": "IL", "_id": "61731"} -{"city": "DANVERS", "loc": [-89.188487, 40.536353], "pop": 1825, "state": "IL", "_id": "61732"} -{"city": "DEER CREEK", "loc": [-89.3003, 40.627992], "pop": 2277, "state": "IL", "_id": "61733"} -{"city": "DELAVAN", "loc": [-89.532133, 40.369029], "pop": 2285, "state": "IL", "_id": "61734"} -{"city": "DEWITT", "loc": [-88.763672, 40.184759], "pop": 417, "state": "IL", "_id": "61735"} -{"city": "HOLDER", "loc": [-88.870814, 40.380858], "pop": 992, "state": "IL", "_id": "61736"} -{"city": "ELLSWORTH", "loc": [-88.737121, 40.443154], "pop": 660, "state": "IL", "_id": "61737"} -{"city": "EL PASO", "loc": [-89.011952, 40.738948], "pop": 3338, "state": "IL", "_id": "61738"} -{"city": "FAIRBURY", "loc": [-88.516486, 40.745033], "pop": 4706, "state": "IL", "_id": "61739"} -{"city": "FLANAGAN", "loc": [-88.86196, 40.879003], "pop": 1424, "state": "IL", "_id": "61740"} -{"city": "FORREST", "loc": [-88.411143, 40.751324], "pop": 1809, "state": "IL", "_id": "61741"} -{"city": "GRAYMONT", "loc": [-88.760299, 40.90663], "pop": 180, "state": "IL", "_id": "61743"} -{"city": "GRIDLEY", "loc": [-88.884044, 40.74387], "pop": 2142, "state": "IL", "_id": "61744"} -{"city": "HEYWORTH", "loc": [-88.977608, 40.3307], "pop": 2934, "state": "IL", "_id": "61745"} -{"city": "HOPEDALE", "loc": [-89.421398, 40.427318], "pop": 1355, "state": "IL", "_id": "61747"} -{"city": "HUDSON", "loc": [-88.975931, 40.620485], "pop": 1850, "state": "IL", "_id": "61748"} -{"city": "KENNEY", "loc": [-89.078925, 40.10247], "pop": 789, "state": "IL", "_id": "61749"} -{"city": "LE ROY", "loc": [-88.759813, 40.346781], "pop": 3379, "state": "IL", "_id": "61752"} -{"city": "LEXINGTON", "loc": [-88.806203, 40.635685], "pop": 3098, "state": "IL", "_id": "61753"} -{"city": "MC LEAN", "loc": [-89.164483, 40.328159], "pop": 1432, "state": "IL", "_id": "61754"} -{"city": "MACKINAW", "loc": [-89.345795, 40.539643], "pop": 2772, "state": "IL", "_id": "61755"} -{"city": "MAROA", "loc": [-88.957769, 40.034159], "pop": 1883, "state": "IL", "_id": "61756"} -{"city": "MINIER", "loc": [-89.316484, 40.435889], "pop": 1483, "state": "IL", "_id": "61759"} -{"city": "MINONK", "loc": [-89.034863, 40.898501], "pop": 2559, "state": "IL", "_id": "61760"} -{"city": "NORMAL", "loc": [-88.988287, 40.512446], "pop": 40851, "state": "IL", "_id": "61761"} -{"city": "PONTIAC", "loc": [-88.632775, 40.876404], "pop": 14036, "state": "IL", "_id": "61764"} -{"city": "SAUNEMIN", "loc": [-88.40936, 40.888516], "pop": 683, "state": "IL", "_id": "61769"} -{"city": "SAYBROOK", "loc": [-88.524656, 40.432005], "pop": 1051, "state": "IL", "_id": "61770"} -{"city": "SECOR", "loc": [-89.127065, 40.722402], "pop": 952, "state": "IL", "_id": "61771"} -{"city": "SHIRLEY", "loc": [-89.082181, 40.417437], "pop": 332, "state": "IL", "_id": "61772"} -{"city": "SIBLEY", "loc": [-88.38146, 40.582315], "pop": 608, "state": "IL", "_id": "61773"} -{"city": "STANFORD", "loc": [-89.216434, 40.437575], "pop": 996, "state": "IL", "_id": "61774"} -{"city": "STRAWN", "loc": [-88.404036, 40.647615], "pop": 322, "state": "IL", "_id": "61775"} -{"city": "TOWANDA", "loc": [-88.88865, 40.553326], "pop": 1191, "state": "IL", "_id": "61776"} -{"city": "WAPELLA", "loc": [-88.967264, 40.232305], "pop": 1031, "state": "IL", "_id": "61777"} -{"city": "WAYNESVILLE", "loc": [-89.114299, 40.243673], "pop": 768, "state": "IL", "_id": "61778"} -{"city": "URBANA", "loc": [-88.203631, 40.109522], "pop": 46110, "state": "IL", "_id": "61801"} -{"city": "ALLERTON", "loc": [-87.931235, 39.918818], "pop": 405, "state": "IL", "_id": "61810"} -{"city": "ALVIN", "loc": [-87.608003, 40.3007], "pop": 817, "state": "IL", "_id": "61811"} -{"city": "ARMSTRONG", "loc": [-87.894256, 40.217548], "pop": 247, "state": "IL", "_id": "61812"} -{"city": "BEMENT", "loc": [-88.56877, 39.922207], "pop": 1928, "state": "IL", "_id": "61813"} -{"city": "BISMARCK", "loc": [-87.613769, 40.255187], "pop": 1476, "state": "IL", "_id": "61814"} -{"city": "BROADLANDS", "loc": [-87.994831, 39.914247], "pop": 481, "state": "IL", "_id": "61816"} -{"city": "CATLIN", "loc": [-87.711255, 40.069922], "pop": 3359, "state": "IL", "_id": "61817"} -{"city": "CERRO GORDO", "loc": [-88.725634, 39.868062], "pop": 2060, "state": "IL", "_id": "61818"} -{"city": "CHAMPAIGN", "loc": [-88.240747, 40.111017], "pop": 33409, "state": "IL", "_id": "61820"} -{"city": "CHAMPAIGN", "loc": [-88.278847, 40.107262], "pop": 37547, "state": "IL", "_id": "61821"} -{"city": "CISCO", "loc": [-88.696153, 39.997164], "pop": 732, "state": "IL", "_id": "61830"} -{"city": "COLLISON", "loc": [-87.798709, 40.220737], "pop": 421, "state": "IL", "_id": "61831"} -{"city": "DANVILLE", "loc": [-87.621737, 40.136976], "pop": 49857, "state": "IL", "_id": "61832"} -{"city": "TILTON", "loc": [-87.644048, 40.096406], "pop": 2945, "state": "IL", "_id": "61833"} -{"city": "DE LAND", "loc": [-88.639193, 40.110656], "pop": 848, "state": "IL", "_id": "61839"} -{"city": "DEWEY", "loc": [-88.276966, 40.313055], "pop": 548, "state": "IL", "_id": "61840"} -{"city": "FAIRMOUNT", "loc": [-87.836456, 40.037329], "pop": 1317, "state": "IL", "_id": "61841"} -{"city": "FARMER CITY", "loc": [-88.663385, 40.244689], "pop": 2889, "state": "IL", "_id": "61842"} -{"city": "FISHER", "loc": [-88.355991, 40.299126], "pop": 2823, "state": "IL", "_id": "61843"} -{"city": "FITHIAN", "loc": [-87.879713, 40.11924], "pop": 673, "state": "IL", "_id": "61844"} -{"city": "FOOSLAND", "loc": [-88.420177, 40.35537], "pop": 319, "state": "IL", "_id": "61845"} -{"city": "GEORGETOWN", "loc": [-87.63648, 39.97922], "pop": 4534, "state": "IL", "_id": "61846"} -{"city": "GIFFORD", "loc": [-88.031705, 40.302755], "pop": 1459, "state": "IL", "_id": "61847"} -{"city": "HOMER", "loc": [-87.962742, 40.034619], "pop": 1624, "state": "IL", "_id": "61849"} -{"city": "INDIANOLA", "loc": [-87.73882, 39.926819], "pop": 715, "state": "IL", "_id": "61850"} -{"city": "IVESDALE", "loc": [-88.445095, 39.950233], "pop": 596, "state": "IL", "_id": "61851"} -{"city": "LONGVIEW", "loc": [-88.075282, 39.901241], "pop": 555, "state": "IL", "_id": "61852"} -{"city": "MAHOMET", "loc": [-88.39283, 40.196437], "pop": 8734, "state": "IL", "_id": "61853"} -{"city": "MANSFIELD", "loc": [-88.517895, 40.214697], "pop": 1407, "state": "IL", "_id": "61854"} -{"city": "MILMINE", "loc": [-88.648696, 39.900702], "pop": 148, "state": "IL", "_id": "61855"} -{"city": "MONTICELLO", "loc": [-88.568555, 40.02632], "pop": 5339, "state": "IL", "_id": "61856"} -{"city": "OAKWOOD", "loc": [-87.7825, 40.116656], "pop": 2852, "state": "IL", "_id": "61858"} -{"city": "OGDEN", "loc": [-87.966499, 40.140117], "pop": 1397, "state": "IL", "_id": "61859"} -{"city": "PENFIELD", "loc": [-87.95702, 40.310137], "pop": 601, "state": "IL", "_id": "61862"} -{"city": "PESOTUM", "loc": [-88.274331, 39.9151], "pop": 774, "state": "IL", "_id": "61863"} -{"city": "PHILO", "loc": [-88.159506, 40.005156], "pop": 1377, "state": "IL", "_id": "61864"} -{"city": "POTOMAC", "loc": [-87.823192, 40.309], "pop": 1544, "state": "IL", "_id": "61865"} -{"city": "RANTOUL", "loc": [-88.146179, 40.310742], "pop": 11146, "state": "IL", "_id": "61866"} -{"city": "RANTOUL", "loc": [-88.149884, 40.295886], "pop": 7185, "state": "IL", "_id": "61868"} -{"city": "RIDGE FARM", "loc": [-87.634578, 39.915455], "pop": 2094, "state": "IL", "_id": "61870"} -{"city": "SADORUS", "loc": [-88.344717, 39.961312], "pop": 1022, "state": "IL", "_id": "61872"} -{"city": "SAINT JOSEPH", "loc": [-88.047232, 40.120736], "pop": 4168, "state": "IL", "_id": "61873"} -{"city": "SAVOY", "loc": [-88.252837, 40.065373], "pop": 2972, "state": "IL", "_id": "61874"} -{"city": "SEYMOUR", "loc": [-88.394431, 40.103471], "pop": 1135, "state": "IL", "_id": "61875"} -{"city": "SIDELL", "loc": [-87.824802, 39.911018], "pop": 703, "state": "IL", "_id": "61876"} -{"city": "SIDNEY", "loc": [-88.069029, 40.023206], "pop": 1521, "state": "IL", "_id": "61877"} -{"city": "THOMASBORO", "loc": [-88.183033, 40.240206], "pop": 1638, "state": "IL", "_id": "61878"} -{"city": "TOLONO", "loc": [-88.259641, 39.985006], "pop": 2837, "state": "IL", "_id": "61880"} -{"city": "WELDON", "loc": [-88.753055, 40.117741], "pop": 579, "state": "IL", "_id": "61882"} -{"city": "WESTVILLE", "loc": [-87.635952, 40.045113], "pop": 4398, "state": "IL", "_id": "61883"} -{"city": "WHITE HEATH", "loc": [-88.519297, 40.100911], "pop": 1481, "state": "IL", "_id": "61884"} -{"city": "ARCOLA", "loc": [-88.303679, 39.687001], "pop": 3132, "state": "IL", "_id": "61910"} -{"city": "ARTHUR", "loc": [-88.455509, 39.707679], "pop": 4951, "state": "IL", "_id": "61911"} -{"city": "ASHMORE", "loc": [-88.034097, 39.525428], "pop": 1467, "state": "IL", "_id": "61912"} -{"city": "ATWOOD", "loc": [-88.449446, 39.804368], "pop": 2455, "state": "IL", "_id": "61913"} -{"city": "BETHANY", "loc": [-88.754301, 39.634754], "pop": 1983, "state": "IL", "_id": "61914"} -{"city": "BROCTON", "loc": [-87.926258, 39.692311], "pop": 748, "state": "IL", "_id": "61917"} -{"city": "CAMARGO", "loc": [-88.146815, 39.799955], "pop": 1015, "state": "IL", "_id": "61919"} -{"city": "CHARLESTON", "loc": [-88.176115, 39.486933], "pop": 22767, "state": "IL", "_id": "61920"} -{"city": "CHRISMAN", "loc": [-87.655552, 39.799572], "pop": 2269, "state": "IL", "_id": "61924"} -{"city": "DALTON CITY", "loc": [-88.797459, 39.711943], "pop": 715, "state": "IL", "_id": "61925"} -{"city": "GAYS", "loc": [-88.524153, 39.479553], "pop": 678, "state": "IL", "_id": "61928"} -{"city": "HAMMOND", "loc": [-88.579305, 39.79465], "pop": 797, "state": "IL", "_id": "61929"} -{"city": "HINDSBORO", "loc": [-88.148611, 39.701799], "pop": 811, "state": "IL", "_id": "61930"} -{"city": "HUMBOLDT", "loc": [-88.314089, 39.60118], "pop": 856, "state": "IL", "_id": "61931"} -{"city": "HUME", "loc": [-87.874659, 39.800882], "pop": 593, "state": "IL", "_id": "61932"} -{"city": "KANSAS", "loc": [-87.935238, 39.552533], "pop": 1114, "state": "IL", "_id": "61933"} -{"city": "LOVINGTON", "loc": [-88.641675, 39.719192], "pop": 1995, "state": "IL", "_id": "61937"} -{"city": "MATTOON", "loc": [-88.376152, 39.480184], "pop": 23012, "state": "IL", "_id": "61938"} -{"city": "METCALF", "loc": [-87.795524, 39.800847], "pop": 554, "state": "IL", "_id": "61940"} -{"city": "NEWMAN", "loc": [-88.000055, 39.784788], "pop": 1591, "state": "IL", "_id": "61942"} -{"city": "OAKLAND", "loc": [-88.025325, 39.651618], "pop": 1447, "state": "IL", "_id": "61943"} -{"city": "PARIS", "loc": [-87.697631, 39.613219], "pop": 12509, "state": "IL", "_id": "61944"} -{"city": "SULLIVAN", "loc": [-88.603767, 39.593431], "pop": 6946, "state": "IL", "_id": "61951"} -{"city": "TUSCOLA", "loc": [-88.281585, 39.799509], "pop": 5056, "state": "IL", "_id": "61953"} -{"city": "VILLA GROVE", "loc": [-88.161635, 39.868716], "pop": 3259, "state": "IL", "_id": "61956"} -{"city": "WINDSOR", "loc": [-88.585747, 39.430198], "pop": 2019, "state": "IL", "_id": "61957"} -{"city": "ALHAMBRA", "loc": [-89.744123, 38.882211], "pop": 1543, "state": "IL", "_id": "62001"} -{"city": "ALTON", "loc": [-90.156806, 38.908651], "pop": 37541, "state": "IL", "_id": "62002"} -{"city": "BATCHTOWN", "loc": [-90.659114, 39.072468], "pop": 579, "state": "IL", "_id": "62006"} -{"city": "BENLD", "loc": [-89.803057, 39.093903], "pop": 1604, "state": "IL", "_id": "62009"} -{"city": "BETHALTO", "loc": [-90.034447, 38.907353], "pop": 10587, "state": "IL", "_id": "62010"} -{"city": "BINGHAM", "loc": [-89.195789, 39.14667], "pop": 565, "state": "IL", "_id": "62011"} -{"city": "BRIGHTON", "loc": [-90.144312, 39.036098], "pop": 6467, "state": "IL", "_id": "62012"} -{"city": "MEPPEN", "loc": [-90.590696, 38.937115], "pop": 814, "state": "IL", "_id": "62013"} -{"city": "BUNKER HILL", "loc": [-89.962379, 39.040827], "pop": 3052, "state": "IL", "_id": "62014"} -{"city": "BUTLER", "loc": [-89.530535, 39.211419], "pop": 570, "state": "IL", "_id": "62015"} -{"city": "CARROLLTON", "loc": [-90.409211, 39.300937], "pop": 3079, "state": "IL", "_id": "62016"} -{"city": "COFFEEN", "loc": [-89.39454, 39.090757], "pop": 1207, "state": "IL", "_id": "62017"} -{"city": "COTTAGE HILLS", "loc": [-90.082632, 38.912377], "pop": 4437, "state": "IL", "_id": "62018"} -{"city": "DONNELLSON", "loc": [-89.490858, 39.034422], "pop": 1010, "state": "IL", "_id": "62019"} -{"city": "62020", "loc": [-89.865796, 39.048485], "pop": 1517, "state": "IL", "_id": "62020"} -{"city": "DORSEY", "loc": [-89.978635, 38.983237], "pop": 652, "state": "IL", "_id": "62021"} -{"city": "DOW", "loc": [-90.301059, 39.031198], "pop": 1758, "state": "IL", "_id": "62022"} -{"city": "EAST ALTON", "loc": [-90.083045, 38.88031], "pop": 13839, "state": "IL", "_id": "62024"} -{"city": "EDWARDSVILLE", "loc": [-89.963697, 38.804967], "pop": 19241, "state": "IL", "_id": "62025"} -{"city": "ELDRED", "loc": [-90.532874, 39.283592], "pop": 907, "state": "IL", "_id": "62027"} -{"city": "ELSAH", "loc": [-90.331913, 38.961297], "pop": 2553, "state": "IL", "_id": "62028"} -{"city": "FIDELITY", "loc": [-90.198803, 39.132286], "pop": 655, "state": "IL", "_id": "62030"} -{"city": "FIELDON", "loc": [-90.529742, 39.108608], "pop": 1382, "state": "IL", "_id": "62031"} -{"city": "FILLMORE", "loc": [-89.294551, 39.103872], "pop": 977, "state": "IL", "_id": "62032"} -{"city": "DORCHESTER", "loc": [-89.818577, 39.136106], "pop": 6518, "state": "IL", "_id": "62033"} -{"city": "GLEN CARBON", "loc": [-89.970583, 38.760871], "pop": 9729, "state": "IL", "_id": "62034"} -{"city": "GODFREY", "loc": [-90.206024, 38.946035], "pop": 13959, "state": "IL", "_id": "62035"} -{"city": "GOLDEN EAGLE", "loc": [-90.560199, 38.896138], "pop": 266, "state": "IL", "_id": "62036"} -{"city": "GRAFTON", "loc": [-90.432335, 39.002134], "pop": 2036, "state": "IL", "_id": "62037"} -{"city": "MITCHELL", "loc": [-90.115822, 38.721572], "pop": 48697, "state": "IL", "_id": "62040"} -{"city": "GREENFIELD", "loc": [-90.208851, 39.349058], "pop": 1718, "state": "IL", "_id": "62044"} -{"city": "HAMBURG", "loc": [-90.699546, 39.223488], "pop": 383, "state": "IL", "_id": "62045"} -{"city": "HAMEL", "loc": [-89.872678, 38.878433], "pop": 1685, "state": "IL", "_id": "62046"} -{"city": "HARDIN", "loc": [-90.624002, 39.154652], "pop": 1232, "state": "IL", "_id": "62047"} -{"city": "HARTFORD", "loc": [-90.074533, 38.829852], "pop": 3763, "state": "IL", "_id": "62048"} -{"city": "HILLSBORO", "loc": [-89.488146, 39.149412], "pop": 7916, "state": "IL", "_id": "62049"} -{"city": "HILLVIEW", "loc": [-90.512837, 39.467373], "pop": 795, "state": "IL", "_id": "62050"} -{"city": "IRVING", "loc": [-89.41043, 39.208903], "pop": 909, "state": "IL", "_id": "62051"} -{"city": "JERSEYVILLE", "loc": [-90.333757, 39.121324], "pop": 9439, "state": "IL", "_id": "62052"} -{"city": "KAMPSVILLE", "loc": [-90.626934, 39.306388], "pop": 623, "state": "IL", "_id": "62053"} -{"city": "KANE", "loc": [-90.37188, 39.203688], "pop": 1044, "state": "IL", "_id": "62054"} -{"city": "LITCHFIELD", "loc": [-89.649915, 39.179345], "pop": 9172, "state": "IL", "_id": "62056"} -{"city": "MADISON", "loc": [-90.156585, 38.68109], "pop": 7556, "state": "IL", "_id": "62060"} -{"city": "MARINE", "loc": [-89.821376, 38.785458], "pop": 2020, "state": "IL", "_id": "62061"} -{"city": "MEDORA", "loc": [-90.154225, 39.198593], "pop": 531, "state": "IL", "_id": "62063"} -{"city": "62064", "loc": [-90.624693, 39.010683], "pop": 503, "state": "IL", "_id": "62064"} -{"city": "MICHAEL", "loc": [-90.633498, 39.21821], "pop": 343, "state": "IL", "_id": "62065"} -{"city": "MORO", "loc": [-89.961771, 38.931769], "pop": 2775, "state": "IL", "_id": "62067"} -{"city": "MOUNT OLIVE", "loc": [-89.744842, 39.070504], "pop": 3443, "state": "IL", "_id": "62069"} -{"city": "MOZIER", "loc": [-90.714978, 39.287719], "pop": 241, "state": "IL", "_id": "62070"} -{"city": "NEW DOUGLAS", "loc": [-89.739235, 38.967633], "pop": 2464, "state": "IL", "_id": "62074"} -{"city": "NOKOMIS", "loc": [-89.285297, 39.303642], "pop": 4576, "state": "IL", "_id": "62075"} -{"city": "PIASA", "loc": [-90.131147, 39.151697], "pop": 863, "state": "IL", "_id": "62079"} -{"city": "RAMSEY", "loc": [-89.105147, 39.078123], "pop": 4166, "state": "IL", "_id": "62080"} -{"city": "ROCKBRIDGE", "loc": [-90.255818, 39.283146], "pop": 718, "state": "IL", "_id": "62081"} -{"city": "ROODHOUSE", "loc": [-90.349814, 39.484646], "pop": 3020, "state": "IL", "_id": "62082"} -{"city": "ROSAMOND", "loc": [-89.1846, 39.389229], "pop": 443, "state": "IL", "_id": "62083"} -{"city": "ROXANA", "loc": [-90.07978, 38.848154], "pop": 1513, "state": "IL", "_id": "62084"} -{"city": "SORENTO", "loc": [-89.565347, 38.969342], "pop": 2127, "state": "IL", "_id": "62086"} -{"city": "STAUNTON", "loc": [-89.785697, 39.01348], "pop": 5482, "state": "IL", "_id": "62088"} -{"city": "VENICE", "loc": [-90.168928, 38.67063], "pop": 1559, "state": "IL", "_id": "62090"} -{"city": "WALSHVILLE", "loc": [-89.634968, 39.047257], "pop": 359, "state": "IL", "_id": "62091"} -{"city": "WHITE HALL", "loc": [-90.401899, 39.428804], "pop": 3882, "state": "IL", "_id": "62092"} -{"city": "WITT", "loc": [-89.341426, 39.246919], "pop": 1251, "state": "IL", "_id": "62094"} -{"city": "WOOD RIVER", "loc": [-90.087507, 38.864279], "pop": 10817, "state": "IL", "_id": "62095"} -{"city": "WORDEN", "loc": [-89.853214, 38.944895], "pop": 1818, "state": "IL", "_id": "62097"} -{"city": "SAUGET", "loc": [-90.138066, 38.631538], "pop": 11213, "state": "IL", "_id": "62201"} -{"city": "EAST SAINT LOUIS", "loc": [-90.074449, 38.599191], "pop": 12435, "state": "IL", "_id": "62203"} -{"city": "WASHINGTON PARK", "loc": [-90.102008, 38.631335], "pop": 14425, "state": "IL", "_id": "62204"} -{"city": "EAST SAINT LOUIS", "loc": [-90.127502, 38.614947], "pop": 14488, "state": "IL", "_id": "62205"} -{"city": "CAHOKIA", "loc": [-90.16587, 38.561899], "pop": 20356, "state": "IL", "_id": "62206"} -{"city": "ALORTON", "loc": [-90.12829, 38.58734], "pop": 11681, "state": "IL", "_id": "62207"} -{"city": "FAIRVIEW HEIGHTS", "loc": [-90.007093, 38.596044], "pop": 10882, "state": "IL", "_id": "62208"} -{"city": "VENEDY", "loc": [-89.52273, 38.360422], "pop": 539, "state": "IL", "_id": "62214"} -{"city": "ALBERS", "loc": [-89.620151, 38.531955], "pop": 1641, "state": "IL", "_id": "62215"} -{"city": "BALDWIN", "loc": [-89.841391, 38.175351], "pop": 1093, "state": "IL", "_id": "62217"} -{"city": "BARTELSO", "loc": [-89.457841, 38.53851], "pop": 1270, "state": "IL", "_id": "62218"} -{"city": "BELLEVILLE", "loc": [-89.984693, 38.512677], "pop": 23454, "state": "IL", "_id": "62220"} -{"city": "BELLEVILLE", "loc": [-89.958302, 38.539639], "pop": 29321, "state": "IL", "_id": "62221"} -{"city": "BELLEVILLE", "loc": [-90.037775, 38.545581], "pop": 34650, "state": "IL", "_id": "62223"} -{"city": "SCOTT A F B", "loc": [-89.858775, 38.54692], "pop": 7391, "state": "IL", "_id": "62225"} -{"city": "BREESE", "loc": [-89.52838, 38.618802], "pop": 4882, "state": "IL", "_id": "62230"} -{"city": "CARLYLE", "loc": [-89.380544, 38.606609], "pop": 6529, "state": "IL", "_id": "62231"} -{"city": "CASEYVILLE", "loc": [-90.013486, 38.634458], "pop": 7601, "state": "IL", "_id": "62232"} -{"city": "CHESTER", "loc": [-89.821807, 37.918822], "pop": 9436, "state": "IL", "_id": "62233"} -{"city": "COLLINSVILLE", "loc": [-89.98529, 38.683545], "pop": 33686, "state": "IL", "_id": "62234"} -{"city": "COLUMBIA", "loc": [-90.202717, 38.432469], "pop": 8489, "state": "IL", "_id": "62236"} -{"city": "SWANWICK", "loc": [-89.582064, 38.176401], "pop": 2359, "state": "IL", "_id": "62237"} -{"city": "CUTLER", "loc": [-89.566127, 38.042663], "pop": 787, "state": "IL", "_id": "62238"} -{"city": "DUPO", "loc": [-90.194188, 38.514771], "pop": 5663, "state": "IL", "_id": "62239"} -{"city": "EAST CARONDELET", "loc": [-90.220782, 38.534912], "pop": 1579, "state": "IL", "_id": "62240"} -{"city": "ELLIS GROVE", "loc": [-89.900847, 38.005358], "pop": 999, "state": "IL", "_id": "62241"} -{"city": "EVANSVILLE", "loc": [-89.917028, 38.09258], "pop": 1540, "state": "IL", "_id": "62242"} -{"city": "FREEBURG", "loc": [-89.91806, 38.408016], "pop": 5837, "state": "IL", "_id": "62243"} -{"city": "FULTS", "loc": [-90.197353, 38.179736], "pop": 635, "state": "IL", "_id": "62244"} -{"city": "GERMANTOWN", "loc": [-89.54134, 38.548677], "pop": 1773, "state": "IL", "_id": "62245"} -{"city": "GREENVILLE", "loc": [-89.405185, 38.893338], "pop": 7623, "state": "IL", "_id": "62246"} -{"city": "HECKER", "loc": [-89.983115, 38.281039], "pop": 1454, "state": "IL", "_id": "62248"} -{"city": "HIGHLAND", "loc": [-89.678894, 38.763086], "pop": 6772, "state": "IL", "_id": "62249"} -{"city": "KEYESPORT", "loc": [-89.306323, 38.738773], "pop": 1464, "state": "IL", "_id": "62253"} -{"city": "LEBANON", "loc": [-89.799213, 38.60528], "pop": 4950, "state": "IL", "_id": "62254"} -{"city": "LENZBURG", "loc": [-89.792202, 38.295003], "pop": 1316, "state": "IL", "_id": "62255"} -{"city": "MAEYSTOWN", "loc": [-90.20178, 38.24112], "pop": 592, "state": "IL", "_id": "62256"} -{"city": "MARISSA", "loc": [-89.750119, 38.245455], "pop": 2634, "state": "IL", "_id": "62257"} -{"city": "MASCOUTAH", "loc": [-89.787745, 38.474496], "pop": 8151, "state": "IL", "_id": "62258"} -{"city": "MILLSTADT", "loc": [-90.088818, 38.444264], "pop": 4979, "state": "IL", "_id": "62260"} -{"city": "MODOC", "loc": [-90.016261, 38.050683], "pop": 381, "state": "IL", "_id": "62261"} -{"city": "MULBERRY GROVE", "loc": [-89.246297, 38.931082], "pop": 1721, "state": "IL", "_id": "62262"} -{"city": "NASHVILLE", "loc": [-89.384058, 38.335208], "pop": 4546, "state": "IL", "_id": "62263"} -{"city": "NEW ATHENS", "loc": [-89.872777, 38.315998], "pop": 2488, "state": "IL", "_id": "62264"} -{"city": "NEW BADEN", "loc": [-89.692232, 38.531485], "pop": 3455, "state": "IL", "_id": "62265"} -{"city": "OAKDALE", "loc": [-89.596046, 38.257285], "pop": 1348, "state": "IL", "_id": "62268"} -{"city": "SHILOH", "loc": [-89.9093, 38.59052], "pop": 21198, "state": "IL", "_id": "62269"} -{"city": "OKAWVILLE", "loc": [-89.523045, 38.431862], "pop": 2187, "state": "IL", "_id": "62271"} -{"city": "PERCY", "loc": [-89.616961, 38.01261], "pop": 1241, "state": "IL", "_id": "62272"} -{"city": "PINCKNEYVILLE", "loc": [-89.38578, 38.090327], "pop": 7013, "state": "IL", "_id": "62274"} -{"city": "POCAHONTAS", "loc": [-89.524684, 38.78456], "pop": 3279, "state": "IL", "_id": "62275"} -{"city": "PRAIRIE DU ROCHE", "loc": [-90.090147, 38.085468], "pop": 1017, "state": "IL", "_id": "62277"} -{"city": "RED BUD", "loc": [-89.988356, 38.190738], "pop": 4923, "state": "IL", "_id": "62278"} -{"city": "RENAULT", "loc": [-90.095043, 38.170396], "pop": 929, "state": "IL", "_id": "62279"} -{"city": "ROCKWOOD", "loc": [-89.621445, 37.832201], "pop": 770, "state": "IL", "_id": "62280"} -{"city": "SAINT JACOB", "loc": [-89.669616, 38.72448], "pop": 5477, "state": "IL", "_id": "62281"} -{"city": "SHATTUC", "loc": [-89.205436, 38.643533], "pop": 1079, "state": "IL", "_id": "62283"} -{"city": "SMITHBORO", "loc": [-89.326556, 38.873893], "pop": 771, "state": "IL", "_id": "62284"} -{"city": "SMITHTON", "loc": [-89.989604, 38.423137], "pop": 2749, "state": "IL", "_id": "62285"} -{"city": "SPARTA", "loc": [-89.703458, 38.131815], "pop": 8035, "state": "IL", "_id": "62286"} -{"city": "STEELEVILLE", "loc": [-89.666458, 38.005713], "pop": 2951, "state": "IL", "_id": "62288"} -{"city": "TRENTON", "loc": [-89.644696, 38.619102], "pop": 5574, "state": "IL", "_id": "62293"} -{"city": "TROY", "loc": [-89.870848, 38.724275], "pop": 10526, "state": "IL", "_id": "62294"} -{"city": "VALMEYER", "loc": [-90.309234, 38.295163], "pop": 1841, "state": "IL", "_id": "62295"} -{"city": "62296", "loc": [-89.644751, 38.377482], "pop": 1006, "state": "IL", "_id": "62296"} -{"city": "WALSH", "loc": [-89.829658, 38.020286], "pop": 801, "state": "IL", "_id": "62297"} -{"city": "WATERLOO", "loc": [-90.147773, 38.322273], "pop": 8482, "state": "IL", "_id": "62298"} -{"city": "QUINCY", "loc": [-91.376284, 39.930701], "pop": 52014, "state": "IL", "_id": "62301"} -{"city": "AUGUSTA", "loc": [-90.955416, 40.234109], "pop": 867, "state": "IL", "_id": "62311"} -{"city": "BARRY", "loc": [-91.026482, 39.704662], "pop": 2697, "state": "IL", "_id": "62312"} -{"city": "BASCO", "loc": [-91.196983, 40.328303], "pop": 399, "state": "IL", "_id": "62313"} -{"city": "BAYLIS", "loc": [-90.883216, 39.761201], "pop": 628, "state": "IL", "_id": "62314"} -{"city": "BOWEN", "loc": [-91.070491, 40.234076], "pop": 693, "state": "IL", "_id": "62316"} -{"city": "BURNSIDE", "loc": [-91.142392, 40.497445], "pop": 770, "state": "IL", "_id": "62318"} -{"city": "CAMDEN", "loc": [-90.754433, 40.151076], "pop": 317, "state": "IL", "_id": "62319"} -{"city": "CAMP POINT", "loc": [-91.076926, 40.029285], "pop": 2113, "state": "IL", "_id": "62320"} -{"city": "CARTHAGE", "loc": [-91.100522, 40.412937], "pop": 4171, "state": "IL", "_id": "62321"} -{"city": "CHAMBERSBURG", "loc": [-90.663017, 39.807058], "pop": 203, "state": "IL", "_id": "62323"} -{"city": "CLAYTON", "loc": [-90.955394, 40.013342], "pop": 1354, "state": "IL", "_id": "62324"} -{"city": "COATSBURG", "loc": [-91.174733, 40.055385], "pop": 446, "state": "IL", "_id": "62325"} -{"city": "COLCHESTER", "loc": [-90.78456, 40.415643], "pop": 2470, "state": "IL", "_id": "62326"} -{"city": "PONTOOSUC", "loc": [-91.163454, 40.620062], "pop": 2146, "state": "IL", "_id": "62330"} -{"city": "DETROIT", "loc": [-90.65449, 39.618307], "pop": 353, "state": "IL", "_id": "62332"} -{"city": "ELVASTON", "loc": [-91.21642, 40.40662], "pop": 449, "state": "IL", "_id": "62334"} -{"city": "FOWLER", "loc": [-91.245226, 39.992504], "pop": 508, "state": "IL", "_id": "62338"} -{"city": "GOLDEN", "loc": [-91.029588, 40.120257], "pop": 938, "state": "IL", "_id": "62339"} -{"city": "GRIGGSVILLE", "loc": [-90.724891, 39.708378], "pop": 1611, "state": "IL", "_id": "62340"} -{"city": "HAMILTON", "loc": [-91.344042, 40.439213], "pop": 5574, "state": "IL", "_id": "62341"} -{"city": "HULL", "loc": [-91.233844, 39.718562], "pop": 892, "state": "IL", "_id": "62343"} -{"city": "HUNTSVILLE", "loc": [-90.853136, 40.154396], "pop": 189, "state": "IL", "_id": "62344"} -{"city": "KINDERHOOK", "loc": [-91.163728, 39.695313], "pop": 427, "state": "IL", "_id": "62345"} -{"city": "LA PRAIRIE", "loc": [-90.984387, 40.157191], "pop": 218, "state": "IL", "_id": "62346"} -{"city": "LIBERTY", "loc": [-91.08688, 39.889176], "pop": 1331, "state": "IL", "_id": "62347"} -{"city": "LIMA", "loc": [-91.386756, 40.170098], "pop": 559, "state": "IL", "_id": "62348"} -{"city": "LORAINE", "loc": [-91.21301, 40.153197], "pop": 634, "state": "IL", "_id": "62349"} -{"city": "MENDON", "loc": [-91.289923, 40.085658], "pop": 1462, "state": "IL", "_id": "62351"} -{"city": "MILTON", "loc": [-90.644346, 39.550811], "pop": 548, "state": "IL", "_id": "62352"} -{"city": "MOUNT STERLING", "loc": [-90.741424, 39.980279], "pop": 4058, "state": "IL", "_id": "62353"} -{"city": "NEBO", "loc": [-90.7692, 39.420181], "pop": 1011, "state": "IL", "_id": "62355"} -{"city": "NEW CANTON", "loc": [-91.088556, 39.634242], "pop": 658, "state": "IL", "_id": "62356"} -{"city": "NEW SALEM", "loc": [-90.843986, 39.699573], "pop": 292, "state": "IL", "_id": "62357"} -{"city": "NIOTA", "loc": [-91.299136, 40.597753], "pop": 672, "state": "IL", "_id": "62358"} -{"city": "PALOMA", "loc": [-91.205287, 40.036616], "pop": 254, "state": "IL", "_id": "62359"} -{"city": "PAYSON", "loc": [-91.262676, 39.815253], "pop": 1939, "state": "IL", "_id": "62360"} -{"city": "PEARL", "loc": [-90.637954, 39.444072], "pop": 415, "state": "IL", "_id": "62361"} -{"city": "PERRY", "loc": [-90.746105, 39.787042], "pop": 703, "state": "IL", "_id": "62362"} -{"city": "PITTSFIELD", "loc": [-90.807269, 39.601306], "pop": 5678, "state": "IL", "_id": "62363"} -{"city": "PLAINVILLE", "loc": [-91.143598, 39.800448], "pop": 986, "state": "IL", "_id": "62365"} -{"city": "PLEASANT HILL", "loc": [-90.877011, 39.446697], "pop": 1521, "state": "IL", "_id": "62366"} -{"city": "COLMAR", "loc": [-90.873815, 40.288493], "pop": 1425, "state": "IL", "_id": "62367"} -{"city": "ROCKPORT", "loc": [-90.972178, 39.532846], "pop": 641, "state": "IL", "_id": "62370"} -{"city": "SUTTER", "loc": [-91.376094, 40.309683], "pop": 747, "state": "IL", "_id": "62373"} -{"city": "TENNESSEE", "loc": [-90.855605, 40.41399], "pop": 414, "state": "IL", "_id": "62374"} -{"city": "TIMEWELL", "loc": [-90.866067, 40.011658], "pop": 539, "state": "IL", "_id": "62375"} -{"city": "URSA", "loc": [-91.373341, 40.080918], "pop": 1074, "state": "IL", "_id": "62376"} -{"city": "VERSAILLES", "loc": [-90.674147, 39.888286], "pop": 1136, "state": "IL", "_id": "62378"} -{"city": "WARSAW", "loc": [-91.434803, 40.354449], "pop": 1882, "state": "IL", "_id": "62379"} -{"city": "WEST POINT", "loc": [-91.249568, 40.245184], "pop": 874, "state": "IL", "_id": "62380"} -{"city": "EFFINGHAM", "loc": [-88.561105, 39.121727], "pop": 15524, "state": "IL", "_id": "62401"} -{"city": "ALLENDALE", "loc": [-87.721901, 38.523236], "pop": 1051, "state": "IL", "_id": "62410"} -{"city": "ALTAMONT", "loc": [-88.748092, 39.063449], "pop": 3867, "state": "IL", "_id": "62411"} -{"city": "ANNAPOLIS", "loc": [-87.802875, 39.117857], "pop": 692, "state": "IL", "_id": "62413"} -{"city": "BEECHER CITY", "loc": [-88.80385, 39.183515], "pop": 1968, "state": "IL", "_id": "62414"} -{"city": "BIRDS", "loc": [-87.681545, 38.812059], "pop": 842, "state": "IL", "_id": "62415"} -{"city": "BRIDGEPORT", "loc": [-87.854926, 38.694506], "pop": 2076, "state": "IL", "_id": "62417"} -{"city": "BROWNSTOWN", "loc": [-88.949438, 38.989118], "pop": 2434, "state": "IL", "_id": "62418"} -{"city": "CALHOUN", "loc": [-88.003745, 38.635116], "pop": 715, "state": "IL", "_id": "62419"} -{"city": "CASEY", "loc": [-87.991306, 39.301716], "pop": 5300, "state": "IL", "_id": "62420"} -{"city": "CLAREMONT", "loc": [-87.972685, 38.742904], "pop": 1320, "state": "IL", "_id": "62421"} -{"city": "COWDEN", "loc": [-88.886799, 39.232643], "pop": 1521, "state": "IL", "_id": "62422"} -{"city": "DENNISON", "loc": [-87.58671, 39.449033], "pop": 753, "state": "IL", "_id": "62423"} -{"city": "DIETERICH", "loc": [-88.407448, 39.031875], "pop": 1639, "state": "IL", "_id": "62424"} -{"city": "DUNDAS", "loc": [-88.097309, 38.830572], "pop": 437, "state": "IL", "_id": "62425"} -{"city": "LACLEDE", "loc": [-88.663868, 38.91318], "pop": 772, "state": "IL", "_id": "62426"} -{"city": "FLAT ROCK", "loc": [-87.683747, 38.909606], "pop": 2173, "state": "IL", "_id": "62427"} -{"city": "HAZEL DELL", "loc": [-88.144502, 39.239454], "pop": 2914, "state": "IL", "_id": "62428"} -{"city": "HERRICK", "loc": [-88.981211, 39.224481], "pop": 628, "state": "IL", "_id": "62431"} -{"city": "HIDALGO", "loc": [-88.139706, 39.122808], "pop": 820, "state": "IL", "_id": "62432"} -{"city": "HUTSONVILLE", "loc": [-87.669455, 39.106357], "pop": 1039, "state": "IL", "_id": "62433"} -{"city": "INGRAHAM", "loc": [-88.320374, 38.828446], "pop": 1077, "state": "IL", "_id": "62434"} -{"city": "JEWETT", "loc": [-88.24744, 39.207596], "pop": 402, "state": "IL", "_id": "62436"} -{"city": "LAKEWOOD", "loc": [-88.871474, 39.313358], "pop": 415, "state": "IL", "_id": "62438"} -{"city": "LAWRENCEVILLE", "loc": [-87.678397, 38.730862], "pop": 7833, "state": "IL", "_id": "62439"} -{"city": "LERNA", "loc": [-88.253026, 39.39579], "pop": 1289, "state": "IL", "_id": "62440"} -{"city": "MARSHALL", "loc": [-87.692261, 39.421999], "pop": 8305, "state": "IL", "_id": "62441"} -{"city": "MARTINSVILLE", "loc": [-87.870737, 39.317443], "pop": 1881, "state": "IL", "_id": "62442"} -{"city": "MASON", "loc": [-88.623396, 38.964453], "pop": 1774, "state": "IL", "_id": "62443"} -{"city": "MONTROSE", "loc": [-88.33497, 39.157274], "pop": 1611, "state": "IL", "_id": "62445"} -{"city": "MOUNT ERIE", "loc": [-88.218521, 38.522151], "pop": 470, "state": "IL", "_id": "62446"} -{"city": "NEOGA", "loc": [-88.450288, 39.322024], "pop": 3494, "state": "IL", "_id": "62447"} -{"city": "NEWTON", "loc": [-88.170386, 38.984678], "pop": 5296, "state": "IL", "_id": "62448"} -{"city": "OBLONG", "loc": [-87.895016, 39.001043], "pop": 3508, "state": "IL", "_id": "62449"} -{"city": "OLNEY", "loc": [-88.080936, 38.733389], "pop": 11163, "state": "IL", "_id": "62450"} -{"city": "PALESTINE", "loc": [-87.615695, 39.002823], "pop": 2413, "state": "IL", "_id": "62451"} -{"city": "PARKERSBURG", "loc": [-88.064722, 38.589998], "pop": 485, "state": "IL", "_id": "62452"} -{"city": "ROBINSON", "loc": [-87.748352, 39.007034], "pop": 9243, "state": "IL", "_id": "62454"} -{"city": "SAINT ELMO", "loc": [-88.855181, 39.031544], "pop": 2091, "state": "IL", "_id": "62458"} -{"city": "SAINT FRANCISVIL", "loc": [-87.673628, 38.612494], "pop": 1806, "state": "IL", "_id": "62460"} -{"city": "SHUMWAY", "loc": [-88.641826, 39.188138], "pop": 539, "state": "IL", "_id": "62461"} -{"city": "SIGEL", "loc": [-88.480301, 39.21762], "pop": 2239, "state": "IL", "_id": "62462"} -{"city": "STEWARDSON", "loc": [-88.631853, 39.27199], "pop": 1219, "state": "IL", "_id": "62463"} -{"city": "STRASBURG", "loc": [-88.627862, 39.364111], "pop": 758, "state": "IL", "_id": "62465"} -{"city": "SUMNER", "loc": [-87.780728, 38.734077], "pop": 3517, "state": "IL", "_id": "62466"} -{"city": "TEUTOPOLIS", "loc": [-88.476741, 39.131978], "pop": 3095, "state": "IL", "_id": "62467"} -{"city": "TOLEDO", "loc": [-88.246821, 39.277223], "pop": 2046, "state": "IL", "_id": "62468"} -{"city": "TRILLA", "loc": [-88.345698, 39.385286], "pop": 499, "state": "IL", "_id": "62469"} -{"city": "VANDALIA", "loc": [-89.104116, 38.94391], "pop": 7894, "state": "IL", "_id": "62471"} -{"city": "WATSON", "loc": [-88.559876, 39.039488], "pop": 2740, "state": "IL", "_id": "62473"} -{"city": "WESTFIELD", "loc": [-88.044799, 39.442369], "pop": 1602, "state": "IL", "_id": "62474"} -{"city": "WEST LIBERTY", "loc": [-88.097929, 38.894989], "pop": 593, "state": "IL", "_id": "62475"} -{"city": "WEST SALEM", "loc": [-88.032297, 38.518267], "pop": 1994, "state": "IL", "_id": "62476"} -{"city": "WEST UNION", "loc": [-87.651215, 39.240185], "pop": 993, "state": "IL", "_id": "62477"} -{"city": "WEST YORK", "loc": [-87.713073, 39.193793], "pop": 665, "state": "IL", "_id": "62478"} -{"city": "WHEELER", "loc": [-88.317536, 39.018185], "pop": 809, "state": "IL", "_id": "62479"} -{"city": "WILLOW HILL", "loc": [-88.017156, 38.975654], "pop": 1708, "state": "IL", "_id": "62480"} -{"city": "YALE", "loc": [-88.01047, 39.129975], "pop": 410, "state": "IL", "_id": "62481"} -{"city": "NEWBURG", "loc": [-88.821371, 39.991581], "pop": 1444, "state": "IL", "_id": "62501"} -{"city": "ASSUMPTION", "loc": [-89.039807, 39.509489], "pop": 1949, "state": "IL", "_id": "62510"} -{"city": "ATWATER", "loc": [-89.763374, 39.310733], "pop": 489, "state": "IL", "_id": "62511"} -{"city": "BEASON", "loc": [-89.194801, 40.143652], "pop": 467, "state": "IL", "_id": "62512"} -{"city": "BLUE MOUND", "loc": [-89.113558, 39.697813], "pop": 1488, "state": "IL", "_id": "62513"} -{"city": "BOODY", "loc": [-89.072061, 39.762824], "pop": 541, "state": "IL", "_id": "62514"} -{"city": "BUFFALO HART", "loc": [-89.388846, 39.847201], "pop": 797, "state": "IL", "_id": "62515"} -{"city": "CHESTNUT", "loc": [-89.190014, 40.058202], "pop": 436, "state": "IL", "_id": "62518"} -{"city": "DAWSON", "loc": [-89.460329, 39.85629], "pop": 558, "state": "IL", "_id": "62520"} -{"city": "DECATUR", "loc": [-88.925984, 39.827137], "pop": 39666, "state": "IL", "_id": "62521"} -{"city": "DECATUR", "loc": [-88.986139, 39.843237], "pop": 19224, "state": "IL", "_id": "62522"} -{"city": "DECATUR", "loc": [-88.953435, 39.841694], "pop": 864, "state": "IL", "_id": "62523"} -{"city": "BEARSDALE", "loc": [-88.953515, 39.877413], "pop": 39674, "state": "IL", "_id": "62526"} -{"city": "CIMIC", "loc": [-89.654654, 39.567684], "pop": 1484, "state": "IL", "_id": "62530"} -{"city": "EDINBURG", "loc": [-89.377884, 39.661223], "pop": 1925, "state": "IL", "_id": "62531"} -{"city": "THOMASVILLE", "loc": [-89.642718, 39.453048], "pop": 1047, "state": "IL", "_id": "62533"} -{"city": "BRUNSWICK", "loc": [-88.756208, 39.503253], "pop": 1410, "state": "IL", "_id": "62534"} -{"city": "GLENARM", "loc": [-89.658144, 39.632713], "pop": 739, "state": "IL", "_id": "62536"} -{"city": "HARVEL", "loc": [-89.53804, 39.371865], "pop": 374, "state": "IL", "_id": "62538"} -{"city": "ILLIOPOLIS", "loc": [-89.251319, 39.849929], "pop": 1366, "state": "IL", "_id": "62539"} -{"city": "LATHAM", "loc": [-89.172492, 39.97111], "pop": 857, "state": "IL", "_id": "62543"} -{"city": "MACON", "loc": [-88.988026, 39.704093], "pop": 1737, "state": "IL", "_id": "62544"} -{"city": "BOLIVIA", "loc": [-89.417692, 39.77771], "pop": 2228, "state": "IL", "_id": "62545"} -{"city": "MORRISONVILLE", "loc": [-89.45407, 39.415336], "pop": 1418, "state": "IL", "_id": "62546"} -{"city": "MOUNT AUBURN", "loc": [-89.235672, 39.762845], "pop": 1011, "state": "IL", "_id": "62547"} -{"city": "MOUNT PULASKI", "loc": [-89.293482, 40.004482], "pop": 2500, "state": "IL", "_id": "62548"} -{"city": "HERVEY CITY", "loc": [-88.870207, 39.778883], "pop": 5691, "state": "IL", "_id": "62549"} -{"city": "RADFORD", "loc": [-89.012776, 39.616135], "pop": 3112, "state": "IL", "_id": "62550"} -{"city": "NIANTIC", "loc": [-89.170071, 39.858175], "pop": 850, "state": "IL", "_id": "62551"} -{"city": "CASNER", "loc": [-88.805591, 39.896897], "pop": 810, "state": "IL", "_id": "62552"} -{"city": "OCONEE", "loc": [-89.083636, 39.288408], "pop": 796, "state": "IL", "_id": "62553"} -{"city": "OREANA", "loc": [-88.854675, 39.935135], "pop": 1554, "state": "IL", "_id": "62554"} -{"city": "OWANECO", "loc": [-89.195071, 39.477508], "pop": 540, "state": "IL", "_id": "62555"} -{"city": "CLARKSDALE", "loc": [-89.425555, 39.470817], "pop": 689, "state": "IL", "_id": "62556"} -{"city": "DUNKEL", "loc": [-89.078165, 39.388124], "pop": 7081, "state": "IL", "_id": "62557"} -{"city": "SICILY", "loc": [-89.578955, 39.590966], "pop": 2822, "state": "IL", "_id": "62558"} -{"city": "RAYMOND", "loc": [-89.585135, 39.310551], "pop": 1660, "state": "IL", "_id": "62560"} -{"city": "SPAULDING", "loc": [-89.541908, 39.855239], "pop": 3921, "state": "IL", "_id": "62561"} -{"city": "BERRY", "loc": [-89.547208, 39.759047], "pop": 8726, "state": "IL", "_id": "62563"} -{"city": "CLARKSBURG", "loc": [-88.804615, 39.404873], "pop": 7481, "state": "IL", "_id": "62565"} -{"city": "STONINGTON", "loc": [-89.191328, 39.640482], "pop": 1280, "state": "IL", "_id": "62567"} -{"city": "HEWITTSVILLE", "loc": [-89.313357, 39.554516], "pop": 17182, "state": "IL", "_id": "62568"} -{"city": "DOLLVILLE", "loc": [-88.974517, 39.36576], "pop": 1505, "state": "IL", "_id": "62571"} -{"city": "WAGGONER", "loc": [-89.647429, 39.38383], "pop": 547, "state": "IL", "_id": "62572"} -{"city": "HEMAN", "loc": [-89.068316, 39.939737], "pop": 1780, "state": "IL", "_id": "62573"} -{"city": "ORLEANS", "loc": [-90.035304, 39.725935], "pop": 501, "state": "IL", "_id": "62601"} -{"city": "ARENZVILLE", "loc": [-90.363595, 39.897927], "pop": 1008, "state": "IL", "_id": "62611"} -{"city": "NEWMANSVILLE", "loc": [-90.028721, 39.89168], "pop": 1904, "state": "IL", "_id": "62612"} -{"city": "FANCY PRAIRIE", "loc": [-89.72108, 39.964813], "pop": 2696, "state": "IL", "_id": "62613"} -{"city": "AUBURN", "loc": [-89.744033, 39.591787], "pop": 3934, "state": "IL", "_id": "62615"} -{"city": "LYNCHBURG", "loc": [-90.148568, 40.161774], "pop": 1118, "state": "IL", "_id": "62617"} -{"city": "BEARDSTOWN", "loc": [-90.422851, 40.004356], "pop": 7369, "state": "IL", "_id": "62618"} -{"city": "EXETER", "loc": [-90.530527, 39.744078], "pop": 1101, "state": "IL", "_id": "62621"} -{"city": "BADER", "loc": [-90.351671, 40.155025], "pop": 766, "state": "IL", "_id": "62624"} -{"city": "CANTRALL", "loc": [-89.66426, 39.911267], "pop": 1811, "state": "IL", "_id": "62625"} -{"city": "COMER", "loc": [-89.888935, 39.279821], "pop": 7343, "state": "IL", "_id": "62626"} -{"city": "PANTHER CREEK", "loc": [-90.147657, 40.038437], "pop": 1283, "state": "IL", "_id": "62627"} -{"city": "CHAPIN", "loc": [-90.411299, 39.771581], "pop": 879, "state": "IL", "_id": "62628"} -{"city": "CHATHAM", "loc": [-89.711212, 39.673679], "pop": 4922, "state": "IL", "_id": "62629"} -{"city": "HAGAMAN", "loc": [-90.076632, 39.270756], "pop": 683, "state": "IL", "_id": "62630"} -{"city": "CONCORD", "loc": [-90.372175, 39.822958], "pop": 460, "state": "IL", "_id": "62631"} -{"city": "BIGGS", "loc": [-89.846447, 40.235482], "pop": 1056, "state": "IL", "_id": "62633"} -{"city": "BROADWELL", "loc": [-89.474938, 40.002394], "pop": 922, "state": "IL", "_id": "62634"} -{"city": "EMDEN", "loc": [-89.517441, 40.294538], "pop": 1144, "state": "IL", "_id": "62635"} -{"city": "CLEMENTS", "loc": [-90.048535, 39.608461], "pop": 1280, "state": "IL", "_id": "62638"} -{"city": "FREDERICK", "loc": [-90.504391, 40.070246], "pop": 690, "state": "IL", "_id": "62639"} -{"city": "MCVEY", "loc": [-89.780535, 39.447182], "pop": 2454, "state": "IL", "_id": "62640"} -{"city": "HUBLY", "loc": [-89.711606, 40.079859], "pop": 1499, "state": "IL", "_id": "62642"} -{"city": "HARTSBURG", "loc": [-89.451944, 40.245073], "pop": 602, "state": "IL", "_id": "62643"} -{"city": "ECKARD", "loc": [-90.04952, 40.295995], "pop": 5593, "state": "IL", "_id": "62644"} -{"city": "HETTICK", "loc": [-90.067161, 39.375217], "pop": 518, "state": "IL", "_id": "62649"} -{"city": "ARCADIA", "loc": [-90.236238, 39.729269], "pop": 28240, "state": "IL", "_id": "62650"} -{"city": "KILBOURNE", "loc": [-90.004279, 40.158696], "pop": 633, "state": "IL", "_id": "62655"} -{"city": "LINCOLN", "loc": [-89.368376, 40.14508], "pop": 20130, "state": "IL", "_id": "62656"} -{"city": "LOAMI", "loc": [-89.858823, 39.670394], "pop": 1286, "state": "IL", "_id": "62661"} -{"city": "LUTHER", "loc": [-89.699256, 40.199938], "pop": 2968, "state": "IL", "_id": "62664"} -{"city": "NAPLES", "loc": [-90.549542, 39.797631], "pop": 2215, "state": "IL", "_id": "62665"} -{"city": "MIDDLETOWN", "loc": [-89.58182, 40.096668], "pop": 586, "state": "IL", "_id": "62666"} -{"city": "MODESTO", "loc": [-89.979386, 39.475965], "pop": 564, "state": "IL", "_id": "62667"} -{"city": "NORTONVILLE", "loc": [-90.231189, 39.572952], "pop": 1387, "state": "IL", "_id": "62668"} -{"city": "BATES", "loc": [-89.905721, 39.736048], "pop": 1470, "state": "IL", "_id": "62670"} -{"city": "NEW HOLLAND", "loc": [-89.56044, 40.168226], "pop": 663, "state": "IL", "_id": "62671"} -{"city": "NILWOOD", "loc": [-89.778411, 39.387199], "pop": 633, "state": "IL", "_id": "62672"} -{"city": "OAKFORD", "loc": [-89.960135, 40.099445], "pop": 493, "state": "IL", "_id": "62673"} -{"city": "BARR", "loc": [-89.956736, 39.41654], "pop": 1490, "state": "IL", "_id": "62674"} -{"city": "ATTERBURY", "loc": [-89.847874, 40.011529], "pop": 5049, "state": "IL", "_id": "62675"} -{"city": "PLAINVIEW", "loc": [-89.975687, 39.128311], "pop": 792, "state": "IL", "_id": "62676"} -{"city": "FARMINGDALE", "loc": [-89.838361, 39.849767], "pop": 3081, "state": "IL", "_id": "62677"} -{"city": "LAYTON", "loc": [-90.560883, 40.124189], "pop": 4747, "state": "IL", "_id": "62681"} -{"city": "ALLEN", "loc": [-89.625029, 40.294466], "pop": 669, "state": "IL", "_id": "62682"} -{"city": "SCOTTVILLE", "loc": [-90.096921, 39.478167], "pop": 387, "state": "IL", "_id": "62683"} -{"city": "BARCLAY", "loc": [-89.60251, 39.889456], "pop": 2185, "state": "IL", "_id": "62684"} -{"city": "ROYAL LAKES", "loc": [-90.048737, 39.124165], "pop": 844, "state": "IL", "_id": "62685"} -{"city": "TALLULA", "loc": [-89.882274, 39.940188], "pop": 1427, "state": "IL", "_id": "62688"} -{"city": "VIRDEN", "loc": [-89.778332, 39.506447], "pop": 5939, "state": "IL", "_id": "62690"} -{"city": "LITTLE INDIAN", "loc": [-90.212681, 39.945475], "pop": 2144, "state": "IL", "_id": "62691"} -{"city": "WAVERLY", "loc": [-89.944903, 39.586983], "pop": 1968, "state": "IL", "_id": "62692"} -{"city": "WILLIAMSVILLE", "loc": [-89.534172, 39.930438], "pop": 2320, "state": "IL", "_id": "62693"} -{"city": "GLASGOW", "loc": [-90.433696, 39.614113], "pop": 3909, "state": "IL", "_id": "62694"} -{"city": "SPRINGFIELD", "loc": [-89.649531, 39.80004], "pop": 1155, "state": "IL", "_id": "62701"} -{"city": "GRANDVIEW", "loc": [-89.644147, 39.816768], "pop": 42047, "state": "IL", "_id": "62702"} -{"city": "SOUTHERN VIEW", "loc": [-89.63333, 39.772401], "pop": 32501, "state": "IL", "_id": "62703"} -{"city": "JEROME", "loc": [-89.681066, 39.780319], "pop": 41611, "state": "IL", "_id": "62704"} -{"city": "ANDREW", "loc": [-89.663991, 39.772842], "pop": 16264, "state": "IL", "_id": "62707"} -{"city": "CENTRALIA", "loc": [-89.136478, 38.524117], "pop": 23956, "state": "IL", "_id": "62801"} -{"city": "HOYLETON", "loc": [-89.306854, 38.445468], "pop": 1295, "state": "IL", "_id": "62803"} -{"city": "ALBION", "loc": [-88.063557, 38.374003], "pop": 3162, "state": "IL", "_id": "62806"} -{"city": "ALMA", "loc": [-88.915695, 38.723089], "pop": 1251, "state": "IL", "_id": "62807"} -{"city": "ASHLEY", "loc": [-89.231115, 38.306002], "pop": 1260, "state": "IL", "_id": "62808"} -{"city": "BARNHILL", "loc": [-88.350831, 38.278144], "pop": 165, "state": "IL", "_id": "62809"} -{"city": "BELLE RIVE", "loc": [-88.755801, 38.215268], "pop": 1149, "state": "IL", "_id": "62810"} -{"city": "BENTON", "loc": [-88.922659, 37.99998], "pop": 11419, "state": "IL", "_id": "62812"} -{"city": "BLUFORD", "loc": [-88.75872, 38.353138], "pop": 2622, "state": "IL", "_id": "62814"} -{"city": "BONE GAP", "loc": [-88.002491, 38.448967], "pop": 496, "state": "IL", "_id": "62815"} -{"city": "BONNIE", "loc": [-88.922884, 38.198017], "pop": 994, "state": "IL", "_id": "62816"} -{"city": "BROUGHTON", "loc": [-88.467775, 37.954554], "pop": 732, "state": "IL", "_id": "62817"} -{"city": "BROWNS", "loc": [-87.992824, 38.376433], "pop": 440, "state": "IL", "_id": "62818"} -{"city": "BUCKNER", "loc": [-88.977993, 37.966881], "pop": 278, "state": "IL", "_id": "62819"} -{"city": "BURNT PRAIRIE", "loc": [-88.214719, 38.208151], "pop": 503, "state": "IL", "_id": "62820"} -{"city": "CARMI", "loc": [-88.166987, 38.080819], "pop": 8234, "state": "IL", "_id": "62821"} -{"city": "CHRISTOPHER", "loc": [-89.057367, 37.984905], "pop": 5042, "state": "IL", "_id": "62822"} -{"city": "CISNE", "loc": [-88.404524, 38.513774], "pop": 1403, "state": "IL", "_id": "62823"} -{"city": "CLAY CITY", "loc": [-88.351641, 38.669501], "pop": 1753, "state": "IL", "_id": "62824"} -{"city": "CROSSVILLE", "loc": [-88.059474, 38.166029], "pop": 1388, "state": "IL", "_id": "62827"} -{"city": "DAHLGREN", "loc": [-88.636311, 38.197479], "pop": 1627, "state": "IL", "_id": "62828"} -{"city": "DALE", "loc": [-88.534231, 37.996585], "pop": 594, "state": "IL", "_id": "62829"} -{"city": "DIX", "loc": [-88.965697, 38.43329], "pop": 1552, "state": "IL", "_id": "62830"} -{"city": "DU BOIS", "loc": [-89.204187, 38.252638], "pop": 732, "state": "IL", "_id": "62831"} -{"city": "DU QUOIN", "loc": [-89.233268, 38.013687], "pop": 10061, "state": "IL", "_id": "62832"} -{"city": "ELLERY", "loc": [-88.133455, 38.365021], "pop": 141, "state": "IL", "_id": "62833"} -{"city": "ENFIELD", "loc": [-88.332546, 38.092746], "pop": 1145, "state": "IL", "_id": "62835"} -{"city": "EWING", "loc": [-88.850441, 38.07021], "pop": 1286, "state": "IL", "_id": "62836"} -{"city": "FAIRFIELD", "loc": [-88.359323, 38.378214], "pop": 8723, "state": "IL", "_id": "62837"} -{"city": "FARINA", "loc": [-88.761412, 38.846902], "pop": 925, "state": "IL", "_id": "62838"} -{"city": "FLORA", "loc": [-88.49186, 38.670337], "pop": 6989, "state": "IL", "_id": "62839"} -{"city": "FRANKFORT HEIGHT", "loc": [-88.845651, 37.901301], "pop": 778, "state": "IL", "_id": "62840"} -{"city": "GEFF", "loc": [-88.414428, 38.441326], "pop": 632, "state": "IL", "_id": "62842"} -{"city": "GOLDEN GATE", "loc": [-88.207466, 38.364514], "pop": 733, "state": "IL", "_id": "62843"} -{"city": "GRAYVILLE", "loc": [-88.003539, 38.262707], "pop": 2569, "state": "IL", "_id": "62844"} -{"city": "HERALD", "loc": [-88.213152, 37.967542], "pop": 641, "state": "IL", "_id": "62845"} -{"city": "INA", "loc": [-88.889362, 38.152743], "pop": 800, "state": "IL", "_id": "62846"} -{"city": "IUKA", "loc": [-88.768925, 38.613563], "pop": 965, "state": "IL", "_id": "62849"} -{"city": "JOHNSONVILLE", "loc": [-88.588701, 38.525628], "pop": 1264, "state": "IL", "_id": "62850"} -{"city": "KEENES", "loc": [-88.648058, 38.369146], "pop": 968, "state": "IL", "_id": "62851"} -{"city": "KELL", "loc": [-88.841107, 38.513256], "pop": 1261, "state": "IL", "_id": "62853"} -{"city": "KINMUNDY", "loc": [-88.812999, 38.755857], "pop": 2134, "state": "IL", "_id": "62854"} -{"city": "LANCASTER", "loc": [-87.871216, 38.537438], "pop": 567, "state": "IL", "_id": "62855"} -{"city": "BIBLE GROVE", "loc": [-88.510046, 38.811856], "pop": 3688, "state": "IL", "_id": "62858"} -{"city": "MC LEANSBORO", "loc": [-88.528556, 38.093861], "pop": 4357, "state": "IL", "_id": "62859"} -{"city": "MACEDONIA", "loc": [-88.696077, 38.013023], "pop": 1505, "state": "IL", "_id": "62860"} -{"city": "MILL SHOALS", "loc": [-88.333817, 38.244675], "pop": 412, "state": "IL", "_id": "62862"} -{"city": "MOUNT CARMEL", "loc": [-87.791107, 38.414727], "pop": 11493, "state": "IL", "_id": "62863"} -{"city": "MOUNT VERNON", "loc": [-88.910525, 38.317014], "pop": 23844, "state": "IL", "_id": "62864"} -{"city": "MULKEYTOWN", "loc": [-89.1159, 37.968712], "pop": 474, "state": "IL", "_id": "62865"} -{"city": "NASON", "loc": [-88.968839, 38.173872], "pop": 269, "state": "IL", "_id": "62866"} -{"city": "NEW HAVEN", "loc": [-88.128458, 37.899907], "pop": 558, "state": "IL", "_id": "62867"} -{"city": "NOBLE", "loc": [-88.219038, 38.711851], "pop": 2425, "state": "IL", "_id": "62868"} -{"city": "NORRIS CITY", "loc": [-88.32434, 37.9773], "pop": 2377, "state": "IL", "_id": "62869"} -{"city": "ODIN", "loc": [-89.055228, 38.608786], "pop": 1758, "state": "IL", "_id": "62870"} -{"city": "OMAHA", "loc": [-88.286484, 37.890423], "pop": 638, "state": "IL", "_id": "62871"} -{"city": "OPDYKE", "loc": [-88.774955, 38.274955], "pop": 284, "state": "IL", "_id": "62872"} -{"city": "PATOKA", "loc": [-89.094188, 38.754852], "pop": 857, "state": "IL", "_id": "62875"} -{"city": "RICHVIEW", "loc": [-89.175572, 38.408227], "pop": 1087, "state": "IL", "_id": "62877"} -{"city": "RINARD", "loc": [-88.464094, 38.580596], "pop": 377, "state": "IL", "_id": "62878"} -{"city": "SAINT PETER", "loc": [-88.855998, 38.869684], "pop": 725, "state": "IL", "_id": "62880"} -{"city": "SALEM", "loc": [-88.948077, 38.626421], "pop": 11599, "state": "IL", "_id": "62881"} -{"city": "SANDOVAL", "loc": [-89.11401, 38.613113], "pop": 3145, "state": "IL", "_id": "62882"} -{"city": "SCHELLER", "loc": [-89.092691, 38.173095], "pop": 528, "state": "IL", "_id": "62883"} -{"city": "SESSER", "loc": [-89.057404, 38.089422], "pop": 2844, "state": "IL", "_id": "62884"} -{"city": "SHOBONIER", "loc": [-89.078959, 38.844584], "pop": 219, "state": "IL", "_id": "62885"} -{"city": "SIMS", "loc": [-88.530642, 38.392318], "pop": 868, "state": "IL", "_id": "62886"} -{"city": "SPRINGERTON", "loc": [-88.372611, 38.169868], "pop": 737, "state": "IL", "_id": "62887"} -{"city": "TAMAROA", "loc": [-89.223091, 38.137969], "pop": 1788, "state": "IL", "_id": "62888"} -{"city": "TEXICO", "loc": [-88.870151, 38.425035], "pop": 1159, "state": "IL", "_id": "62889"} -{"city": "THOMPSONVILLE", "loc": [-88.768394, 37.880438], "pop": 2413, "state": "IL", "_id": "62890"} -{"city": "VERNON", "loc": [-89.083001, 38.803328], "pop": 371, "state": "IL", "_id": "62892"} -{"city": "WALNUT HILL", "loc": [-88.984514, 38.520006], "pop": 1386, "state": "IL", "_id": "62893"} -{"city": "WALTONVILLE", "loc": [-89.006702, 38.246479], "pop": 1923, "state": "IL", "_id": "62894"} -{"city": "WAYNE CITY", "loc": [-88.583317, 38.332808], "pop": 1523, "state": "IL", "_id": "62895"} -{"city": "WEST FRANKFORT", "loc": [-88.930724, 37.897914], "pop": 11800, "state": "IL", "_id": "62896"} -{"city": "WHITTINGTON", "loc": [-88.863456, 38.098937], "pop": 370, "state": "IL", "_id": "62897"} -{"city": "WOODLAWN", "loc": [-89.074457, 38.38444], "pop": 1896, "state": "IL", "_id": "62898"} -{"city": "XENIA", "loc": [-88.63789, 38.669747], "pop": 1252, "state": "IL", "_id": "62899"} -{"city": "CARBONDALE", "loc": [-89.215762, 37.719994], "pop": 31742, "state": "IL", "_id": "62901"} -{"city": "ALTO PASS", "loc": [-89.317179, 37.568142], "pop": 912, "state": "IL", "_id": "62905"} -{"city": "ANNA", "loc": [-89.220684, 37.466777], "pop": 8151, "state": "IL", "_id": "62906"} -{"city": "AVA", "loc": [-89.465366, 37.879286], "pop": 1806, "state": "IL", "_id": "62907"} -{"city": "BELKNAP", "loc": [-88.950652, 37.326584], "pop": 188, "state": "IL", "_id": "62908"} -{"city": "NEW LIBERTY", "loc": [-88.611944, 37.138731], "pop": 2565, "state": "IL", "_id": "62910"} -{"city": "BUNCOMBE", "loc": [-88.980586, 37.463661], "pop": 696, "state": "IL", "_id": "62912"} -{"city": "CACHE", "loc": [-89.298018, 37.114131], "pop": 80, "state": "IL", "_id": "62913"} -{"city": "CAIRO", "loc": [-89.181104, 37.012293], "pop": 5439, "state": "IL", "_id": "62914"} -{"city": "CAMPBELL HILL", "loc": [-89.579901, 37.922778], "pop": 976, "state": "IL", "_id": "62916"} -{"city": "CARRIER MILLS", "loc": [-88.612706, 37.678199], "pop": 3355, "state": "IL", "_id": "62917"} -{"city": "CARTERVILLE", "loc": [-89.097793, 37.774785], "pop": 10457, "state": "IL", "_id": "62918"} -{"city": "CAVE IN ROCK", "loc": [-88.221961, 37.517947], "pop": 2266, "state": "IL", "_id": "62919"} -{"city": "COBDEN", "loc": [-89.245741, 37.542358], "pop": 2348, "state": "IL", "_id": "62920"} -{"city": "CREAL SPRINGS", "loc": [-88.880697, 37.628393], "pop": 2743, "state": "IL", "_id": "62922"} -{"city": "CYPRESS", "loc": [-89.014414, 37.366223], "pop": 618, "state": "IL", "_id": "62923"} -{"city": "DE SOTO", "loc": [-89.221834, 37.81473], "pop": 2073, "state": "IL", "_id": "62924"} -{"city": "DONGOLA", "loc": [-89.134933, 37.371234], "pop": 2361, "state": "IL", "_id": "62926"} -{"city": "EDDYVILLE", "loc": [-88.594893, 37.52439], "pop": 677, "state": "IL", "_id": "62928"} -{"city": "ELDORADO", "loc": [-88.443382, 37.813885], "pop": 7036, "state": "IL", "_id": "62930"} -{"city": "ELIZABETHTOWN", "loc": [-88.286735, 37.466492], "pop": 815, "state": "IL", "_id": "62931"} -{"city": "ELKVILLE", "loc": [-89.233574, 37.915506], "pop": 2091, "state": "IL", "_id": "62932"} -{"city": "EQUALITY", "loc": [-88.3445, 37.727786], "pop": 1173, "state": "IL", "_id": "62934"} -{"city": "GALATIA", "loc": [-88.623488, 37.827444], "pop": 2802, "state": "IL", "_id": "62935"} -{"city": "BROWNFIELD", "loc": [-88.555148, 37.349984], "pop": 2997, "state": "IL", "_id": "62938"} -{"city": "GOREVILLE", "loc": [-88.965524, 37.574985], "pop": 2867, "state": "IL", "_id": "62939"} -{"city": "GORHAM", "loc": [-89.444031, 37.740611], "pop": 982, "state": "IL", "_id": "62940"} -{"city": "GRAND CHAIN", "loc": [-89.008335, 37.251043], "pop": 603, "state": "IL", "_id": "62941"} -{"city": "GRAND TOWER", "loc": [-89.49986, 37.63222], "pop": 903, "state": "IL", "_id": "62942"} -{"city": "GRANTSBURG", "loc": [-88.770489, 37.39672], "pop": 2549, "state": "IL", "_id": "62943"} -{"city": "HARRISBURG", "loc": [-88.54404, 37.725661], "pop": 12122, "state": "IL", "_id": "62946"} -{"city": "HEROD", "loc": [-88.458062, 37.469731], "pop": 699, "state": "IL", "_id": "62947"} -{"city": "HERRIN", "loc": [-89.02316, 37.801884], "pop": 13900, "state": "IL", "_id": "62948"} -{"city": "JACOB", "loc": [-89.544359, 37.743738], "pop": 257, "state": "IL", "_id": "62950"} -{"city": "JOHNSTON CITY", "loc": [-88.920858, 37.824477], "pop": 5424, "state": "IL", "_id": "62951"} -{"city": "JONESBORO", "loc": [-89.291494, 37.446082], "pop": 3383, "state": "IL", "_id": "62952"} -{"city": "JOPPA", "loc": [-88.84819, 37.247192], "pop": 1687, "state": "IL", "_id": "62953"} -{"city": "JUNCTION", "loc": [-88.249071, 37.695206], "pop": 581, "state": "IL", "_id": "62954"} -{"city": "KARBERS RIDGE", "loc": [-88.367893, 37.463071], "pop": 686, "state": "IL", "_id": "62955"} -{"city": "KARNAK", "loc": [-88.973943, 37.291066], "pop": 710, "state": "IL", "_id": "62956"} -{"city": "MC CLURE", "loc": [-89.453119, 37.301983], "pop": 1231, "state": "IL", "_id": "62957"} -{"city": "MAKANDA", "loc": [-89.214232, 37.656396], "pop": 3893, "state": "IL", "_id": "62958"} -{"city": "MARION", "loc": [-88.929447, 37.725662], "pop": 20722, "state": "IL", "_id": "62959"} -{"city": "METROPOLIS", "loc": [-88.725179, 37.175348], "pop": 10500, "state": "IL", "_id": "62960"} -{"city": "MILLCREEK", "loc": [-89.269889, 37.353127], "pop": 356, "state": "IL", "_id": "62961"} -{"city": "MILLER CITY", "loc": [-89.349443, 37.103359], "pop": 122, "state": "IL", "_id": "62962"} -{"city": "MOUND CITY", "loc": [-89.163687, 37.086474], "pop": 845, "state": "IL", "_id": "62963"} -{"city": "MOUNDS", "loc": [-89.200064, 37.11883], "pop": 2064, "state": "IL", "_id": "62964"} -{"city": "MURPHYSBORO", "loc": [-89.331749, 37.765464], "pop": 15335, "state": "IL", "_id": "62966"} -{"city": "NEW BURNSIDE", "loc": [-88.771253, 37.580608], "pop": 512, "state": "IL", "_id": "62967"} -{"city": "OLMSTED", "loc": [-89.093266, 37.193507], "pop": 692, "state": "IL", "_id": "62970"} -{"city": "OZARK", "loc": [-88.768778, 37.536704], "pop": 463, "state": "IL", "_id": "62972"} -{"city": "PITTSBURG", "loc": [-88.870393, 37.78387], "pop": 1330, "state": "IL", "_id": "62974"} -{"city": "POMONA", "loc": [-89.336308, 37.641081], "pop": 769, "state": "IL", "_id": "62975"} -{"city": "PULASKI", "loc": [-89.196801, 37.214575], "pop": 663, "state": "IL", "_id": "62976"} -{"city": "RALEIGH", "loc": [-88.532368, 37.825597], "pop": 817, "state": "IL", "_id": "62977"} -{"city": "RIDGWAY", "loc": [-88.261202, 37.804242], "pop": 1686, "state": "IL", "_id": "62979"} -{"city": "ROSICLARE", "loc": [-88.346189, 37.423987], "pop": 1422, "state": "IL", "_id": "62982"} -{"city": "ROYALTON", "loc": [-89.114149, 37.879033], "pop": 1344, "state": "IL", "_id": "62983"} -{"city": "SHAWNEETOWN", "loc": [-88.178503, 37.713188], "pop": 2400, "state": "IL", "_id": "62984"} -{"city": "ROBBS", "loc": [-88.762459, 37.463032], "pop": 507, "state": "IL", "_id": "62985"} -{"city": "STONEFORT", "loc": [-88.742807, 37.62992], "pop": 1255, "state": "IL", "_id": "62987"} -{"city": "TAMMS", "loc": [-89.276346, 37.234493], "pop": 2020, "state": "IL", "_id": "62988"} -{"city": "GALE", "loc": [-89.396896, 37.191554], "pop": 1732, "state": "IL", "_id": "62990"} -{"city": "TUNNEL HILL", "loc": [-88.883459, 37.573676], "pop": 867, "state": "IL", "_id": "62991"} -{"city": "ULLIN", "loc": [-89.191137, 37.270385], "pop": 787, "state": "IL", "_id": "62992"} -{"city": "VERGENNES", "loc": [-89.326863, 37.905128], "pop": 681, "state": "IL", "_id": "62994"} -{"city": "VIENNA", "loc": [-88.887869, 37.420541], "pop": 2767, "state": "IL", "_id": "62995"} -{"city": "VILLA RIDGE", "loc": [-89.182522, 37.157765], "pop": 736, "state": "IL", "_id": "62996"} -{"city": "WILLISVILLE", "loc": [-89.578487, 37.982142], "pop": 912, "state": "IL", "_id": "62997"} -{"city": "WOLF LAKE", "loc": [-89.440761, 37.511985], "pop": 533, "state": "IL", "_id": "62998"} -{"city": "ZEIGLER", "loc": [-89.06026, 37.906923], "pop": 2502, "state": "IL", "_id": "62999"} -{"city": "ALEXANDRIA", "loc": [-85.668148, 40.256081], "pop": 11011, "state": "IN", "_id": "46001"} -{"city": "ANDERSON", "loc": [-85.725305, 40.114577], "pop": 17280, "state": "IN", "_id": "46011"} -{"city": "ANDERSON", "loc": [-85.653591, 40.130947], "pop": 20949, "state": "IN", "_id": "46012"} -{"city": "ANDERSON", "loc": [-85.680073, 40.061865], "pop": 17037, "state": "IN", "_id": "46013"} -{"city": "ANDERSON", "loc": [-85.684566, 40.098799], "pop": 22838, "state": "IN", "_id": "46016"} -{"city": "CHESTERFIELD", "loc": [-85.601493, 40.096431], "pop": 6038, "state": "IN", "_id": "46017"} -{"city": "ARCADIA", "loc": [-86.040882, 40.17758], "pop": 4137, "state": "IN", "_id": "46030"} -{"city": "ATLANTA", "loc": [-85.933945, 40.146964], "pop": 2450, "state": "IN", "_id": "46031"} -{"city": "CARMEL", "loc": [-86.124545, 39.971232], "pop": 40090, "state": "IN", "_id": "46032"} -{"city": "CICERO", "loc": [-86.024844, 40.126781], "pop": 4309, "state": "IN", "_id": "46034"} -{"city": "COLFAX", "loc": [-86.659271, 40.195619], "pop": 1343, "state": "IN", "_id": "46035"} -{"city": "ELWOOD", "loc": [-85.839055, 40.280278], "pop": 13598, "state": "IN", "_id": "46036"} -{"city": "FISHERS", "loc": [-86.023048, 39.957486], "pop": 11918, "state": "IN", "_id": "46038"} -{"city": "FOREST", "loc": [-86.320098, 40.375728], "pop": 879, "state": "IN", "_id": "46039"} -{"city": "FORTVILLE", "loc": [-85.818554, 39.922835], "pop": 5887, "state": "IN", "_id": "46040"} -{"city": "HILLISBURG", "loc": [-86.511387, 40.288404], "pop": 20713, "state": "IN", "_id": "46041"} -{"city": "FRANKTON", "loc": [-85.77911, 40.228548], "pop": 3723, "state": "IN", "_id": "46044"} -{"city": "INGALLS", "loc": [-85.825596, 40.000237], "pop": 763, "state": "IN", "_id": "46048"} -{"city": "KEMPTON", "loc": [-86.1887, 40.275946], "pop": 1501, "state": "IN", "_id": "46049"} -{"city": "KIRKLIN", "loc": [-86.332372, 40.203066], "pop": 2038, "state": "IN", "_id": "46050"} -{"city": "LAPEL", "loc": [-85.84395, 40.085429], "pop": 3221, "state": "IN", "_id": "46051"} -{"city": "LEBANON", "loc": [-86.464074, 40.044894], "pop": 17322, "state": "IN", "_id": "46052"} -{"city": "MC CORDSVILLE", "loc": [-85.909502, 39.901823], "pop": 1450, "state": "IN", "_id": "46055"} -{"city": "MARKLEVILLE", "loc": [-85.622736, 39.994385], "pop": 2957, "state": "IN", "_id": "46056"} -{"city": "MICHIGANTOWN", "loc": [-86.375306, 40.310823], "pop": 2207, "state": "IN", "_id": "46057"} -{"city": "MULBERRY", "loc": [-86.661261, 40.343299], "pop": 1938, "state": "IN", "_id": "46058"} -{"city": "NOBLESVILLE", "loc": [-86.016294, 40.056292], "pop": 26318, "state": "IN", "_id": "46060"} -{"city": "PENDLETON", "loc": [-85.794614, 39.979237], "pop": 13824, "state": "IN", "_id": "46064"} -{"city": "ROSSVILLE", "loc": [-86.607966, 40.410928], "pop": 2217, "state": "IN", "_id": "46065"} -{"city": "SHARPSVILLE", "loc": [-86.108641, 40.373232], "pop": 3570, "state": "IN", "_id": "46068"} -{"city": "SHERIDAN", "loc": [-86.236741, 40.110407], "pop": 7155, "state": "IN", "_id": "46069"} -{"city": "SUMMITVILLE", "loc": [-85.640261, 40.339833], "pop": 1902, "state": "IN", "_id": "46070"} -{"city": "THORNTOWN", "loc": [-86.589822, 40.113335], "pop": 4159, "state": "IN", "_id": "46071"} -{"city": "TIPTON", "loc": [-86.043291, 40.281725], "pop": 8060, "state": "IN", "_id": "46072"} -{"city": "WESTFIELD", "loc": [-86.149907, 40.048868], "pop": 6841, "state": "IN", "_id": "46074"} -{"city": "WHITESTOWN", "loc": [-86.350716, 40.00002], "pop": 1378, "state": "IN", "_id": "46075"} -{"city": "WINDFALL", "loc": [-85.947624, 40.366891], "pop": 1535, "state": "IN", "_id": "46076"} -{"city": "ZIONSVILLE", "loc": [-86.276737, 39.956111], "pop": 9759, "state": "IN", "_id": "46077"} -{"city": "ARLINGTON", "loc": [-85.582775, 39.648791], "pop": 1194, "state": "IN", "_id": "46104"} -{"city": "BAINBRIDGE", "loc": [-86.771119, 39.740664], "pop": 3147, "state": "IN", "_id": "46105"} -{"city": "BARGERSVILLE", "loc": [-86.179687, 39.499989], "pop": 3310, "state": "IN", "_id": "46106"} -{"city": "BEECH GROVE", "loc": [-86.093299, 39.715434], "pop": 13051, "state": "IN", "_id": "46107"} -{"city": "BOGGSTOWN", "loc": [-85.915965, 39.568331], "pop": 958, "state": "IN", "_id": "46110"} -{"city": "BROWNSBURG", "loc": [-86.386933, 39.846605], "pop": 18768, "state": "IN", "_id": "46112"} -{"city": "CAMBY", "loc": [-86.311811, 39.640501], "pop": 2681, "state": "IN", "_id": "46113"} -{"city": "CARTHAGE", "loc": [-85.575382, 39.746627], "pop": 1910, "state": "IN", "_id": "46115"} -{"city": "CHARLOTTESVILLE", "loc": [-85.653599, 39.811981], "pop": 1762, "state": "IN", "_id": "46117"} -{"city": "CLAYTON", "loc": [-86.495921, 39.668154], "pop": 4566, "state": "IN", "_id": "46118"} -{"city": "CLOVERDALE", "loc": [-86.814021, 39.543442], "pop": 7062, "state": "IN", "_id": "46120"} -{"city": "COATESVILLE", "loc": [-86.631447, 39.693041], "pop": 1992, "state": "IN", "_id": "46121"} -{"city": "DANVILLE", "loc": [-86.534254, 39.762815], "pop": 8632, "state": "IN", "_id": "46122"} -{"city": "EDINBURGH", "loc": [-85.970712, 39.362601], "pop": 5115, "state": "IN", "_id": "46124"} -{"city": "FAIRLAND", "loc": [-85.891284, 39.629467], "pop": 6682, "state": "IN", "_id": "46126"} -{"city": "FALMOUTH", "loc": [-85.352423, 39.743617], "pop": 531, "state": "IN", "_id": "46127"} -{"city": "FILLMORE", "loc": [-86.746861, 39.647518], "pop": 1711, "state": "IN", "_id": "46128"} -{"city": "FOUNTAINTOWN", "loc": [-85.784827, 39.675131], "pop": 1555, "state": "IN", "_id": "46130"} -{"city": "FRANKLIN", "loc": [-86.060752, 39.485389], "pop": 14959, "state": "IN", "_id": "46131"} -{"city": "GLENWOOD", "loc": [-85.273532, 39.612436], "pop": 149, "state": "IN", "_id": "46133"} -{"city": "GREENCASTLE", "loc": [-86.868614, 39.649487], "pop": 13300, "state": "IN", "_id": "46135"} -{"city": "GREENFIELD", "loc": [-85.814102, 39.790204], "pop": 28919, "state": "IN", "_id": "46140"} -{"city": "GREENWOOD", "loc": [-86.148993, 39.622398], "pop": 24735, "state": "IN", "_id": "46142"} -{"city": "GREENWOOD", "loc": [-86.130919, 39.596037], "pop": 24633, "state": "IN", "_id": "46143"} -{"city": "JAMESTOWN", "loc": [-86.623561, 39.95789], "pop": 2355, "state": "IN", "_id": "46147"} -{"city": "KNIGHTSTOWN", "loc": [-85.526148, 39.806029], "pop": 3842, "state": "IN", "_id": "46148"} -{"city": "LIZTON", "loc": [-86.542857, 39.884326], "pop": 1586, "state": "IN", "_id": "46149"} -{"city": "MANILLA", "loc": [-85.635369, 39.575711], "pop": 1696, "state": "IN", "_id": "46150"} -{"city": "CENTERTON", "loc": [-86.42901, 39.447646], "pop": 31539, "state": "IN", "_id": "46151"} -{"city": "MILROY", "loc": [-85.504371, 39.495474], "pop": 2154, "state": "IN", "_id": "46156"} -{"city": "MONROVIA", "loc": [-86.48937, 39.571397], "pop": 2205, "state": "IN", "_id": "46157"} -{"city": "MOORESVILLE", "loc": [-86.364173, 39.591469], "pop": 18800, "state": "IN", "_id": "46158"} -{"city": "MORGANTOWN", "loc": [-86.280297, 39.362841], "pop": 2193, "state": "IN", "_id": "46160"} -{"city": "MORRISTOWN", "loc": [-85.693419, 39.667477], "pop": 2215, "state": "IN", "_id": "46161"} -{"city": "NEEDHAM", "loc": [-86.004094, 39.477935], "pop": 2353, "state": "IN", "_id": "46162"} -{"city": "NEW PALESTINE", "loc": [-85.90515, 39.723264], "pop": 3794, "state": "IN", "_id": "46163"} -{"city": "NINEVEH", "loc": [-86.097641, 39.365597], "pop": 3528, "state": "IN", "_id": "46164"} -{"city": "NORTH SALEM", "loc": [-86.638833, 39.867051], "pop": 1541, "state": "IN", "_id": "46165"} -{"city": "PARAGON", "loc": [-86.577867, 39.404203], "pop": 1244, "state": "IN", "_id": "46166"} -{"city": "PITTSBORO", "loc": [-86.464548, 39.861529], "pop": 3466, "state": "IN", "_id": "46167"} -{"city": "AVON", "loc": [-86.395061, 39.716036], "pop": 29182, "state": "IN", "_id": "46168"} -{"city": "REELSVILLE", "loc": [-86.94998, 39.546416], "pop": 2027, "state": "IN", "_id": "46171"} -{"city": "ROACHDALE", "loc": [-86.790225, 39.832545], "pop": 2293, "state": "IN", "_id": "46172"} -{"city": "RUSHVILLE", "loc": [-85.43212, 39.619232], "pop": 11721, "state": "IN", "_id": "46173"} -{"city": "RUSSELLVILLE", "loc": [-86.966975, 39.836598], "pop": 775, "state": "IN", "_id": "46175"} -{"city": "SHELBYVILLE", "loc": [-85.787515, 39.50434], "pop": 24691, "state": "IN", "_id": "46176"} -{"city": "STILESVILLE", "loc": [-86.61819, 39.639113], "pop": 1135, "state": "IN", "_id": "46180"} -{"city": "TRAFALGAR", "loc": [-86.183793, 39.369585], "pop": 3538, "state": "IN", "_id": "46181"} -{"city": "WALDRON", "loc": [-85.664407, 39.468849], "pop": 1878, "state": "IN", "_id": "46182"} -{"city": "NEW WHITELAND", "loc": [-86.093476, 39.555313], "pop": 7226, "state": "IN", "_id": "46184"} -{"city": "WILKINSON", "loc": [-85.61436, 39.895668], "pop": 2567, "state": "IN", "_id": "46186"} -{"city": "INDIANAPOLIS", "loc": [-86.109348, 39.775006], "pop": 42096, "state": "IN", "_id": "46201"} -{"city": "INDIANAPOLIS", "loc": [-86.159502, 39.785063], "pop": 15672, "state": "IN", "_id": "46202"} -{"city": "INDIANAPOLIS", "loc": [-86.117859, 39.743025], "pop": 42566, "state": "IN", "_id": "46203"} -{"city": "INDIANAPOLIS", "loc": [-86.153491, 39.771986], "pop": 4327, "state": "IN", "_id": "46204"} -{"city": "INDIANAPOLIS", "loc": [-86.138582, 39.826761], "pop": 35328, "state": "IN", "_id": "46205"} -{"city": "INDIANAPOLIS", "loc": [-86.179444, 39.829905], "pop": 39610, "state": "IN", "_id": "46208"} -{"city": "EAGLE CREEK", "loc": [-86.289952, 39.792678], "pop": 16644, "state": "IN", "_id": "46214"} -{"city": "FORT BENJAMIN HA", "loc": [-86.016688, 39.857731], "pop": 1566, "state": "IN", "_id": "46216"} -{"city": "SOUTHPORT", "loc": [-86.175394, 39.664141], "pop": 16644, "state": "IN", "_id": "46217"} -{"city": "INDIANAPOLIS", "loc": [-86.101425, 39.80817], "pop": 39965, "state": "IN", "_id": "46218"} -{"city": "INDIANAPOLIS", "loc": [-86.049533, 39.782092], "pop": 38198, "state": "IN", "_id": "46219"} -{"city": "INDIANAPOLIS", "loc": [-86.11815, 39.864685], "pop": 35482, "state": "IN", "_id": "46220"} -{"city": "INDIANAPOLIS", "loc": [-86.19243, 39.750885], "pop": 7920, "state": "IN", "_id": "46221"} -{"city": "INDIANAPOLIS", "loc": [-86.213574, 39.788971], "pop": 39240, "state": "IN", "_id": "46222"} -{"city": "SPEEDWAY", "loc": [-86.257308, 39.798674], "pop": 32130, "state": "IN", "_id": "46224"} -{"city": "INDIANAPOLIS", "loc": [-86.156944, 39.740599], "pop": 8464, "state": "IN", "_id": "46225"} -{"city": "LAWRENCE", "loc": [-86.048945, 39.836969], "pop": 47144, "state": "IN", "_id": "46226"} -{"city": "SOUTHPORT", "loc": [-86.129817, 39.675], "pop": 52257, "state": "IN", "_id": "46227"} -{"city": "CUMBERLAND", "loc": [-85.983826, 39.792219], "pop": 19914, "state": "IN", "_id": "46229"} -{"city": "BRIDGEPORT", "loc": [-86.318289, 39.740637], "pop": 5531, "state": "IN", "_id": "46231"} -{"city": "CLERMONT", "loc": [-86.324117, 39.788438], "pop": 13865, "state": "IN", "_id": "46234"} -{"city": "OAKLANDON", "loc": [-85.985059, 39.849588], "pop": 31475, "state": "IN", "_id": "46236"} -{"city": "SOUTHPORT", "loc": [-86.07891, 39.686777], "pop": 18919, "state": "IN", "_id": "46237"} -{"city": "WANAMAKER", "loc": [-86.008209, 39.721826], "pop": 8611, "state": "IN", "_id": "46239"} -{"city": "NORA", "loc": [-86.129548, 39.9057], "pop": 17553, "state": "IN", "_id": "46240"} -{"city": "PARK FLETCHER", "loc": [-86.250856, 39.723814], "pop": 44731, "state": "IN", "_id": "46241"} -{"city": "CASTLETON", "loc": [-86.069112, 39.9069], "pop": 17196, "state": "IN", "_id": "46250"} -{"city": "EAGLE CREEK", "loc": [-86.2638, 39.841379], "pop": 23015, "state": "IN", "_id": "46254"} -{"city": "CASTLETON", "loc": [-86.023877, 39.90114], "pop": 20589, "state": "IN", "_id": "46256"} -{"city": "ACTON", "loc": [-85.992603, 39.660901], "pop": 3642, "state": "IN", "_id": "46259"} -{"city": "NORA", "loc": [-86.184809, 39.897488], "pop": 29718, "state": "IN", "_id": "46260"} -{"city": "NEW AUGUSTA", "loc": [-86.222104, 39.900296], "pop": 14109, "state": "IN", "_id": "46268"} -{"city": "NEW AUGUSTA", "loc": [-86.291455, 39.883858], "pop": 4727, "state": "IN", "_id": "46278"} -{"city": "NORA", "loc": [-86.13894, 39.938417], "pop": 5281, "state": "IN", "_id": "46280"} -{"city": "NORA", "loc": [-86.167118, 39.93077], "pop": 75, "state": "IN", "_id": "46290"} -{"city": "EAST CEDAR LAKE", "loc": [-87.444509, 41.377338], "pop": 11557, "state": "IN", "_id": "46303"} -{"city": "PORTER", "loc": [-87.050196, 41.603949], "pop": 19024, "state": "IN", "_id": "46304"} -{"city": "CROWN POINT", "loc": [-87.355586, 41.423571], "pop": 37816, "state": "IN", "_id": "46307"} -{"city": "DEMOTTE", "loc": [-87.249129, 41.171319], "pop": 10188, "state": "IN", "_id": "46310"} -{"city": "DYER", "loc": [-87.510803, 41.491976], "pop": 13426, "state": "IN", "_id": "46311"} -{"city": "EAST CHICAGO", "loc": [-87.462734, 41.634893], "pop": 33775, "state": "IN", "_id": "46312"} -{"city": "GRIFFITH", "loc": [-87.422837, 41.53352], "pop": 19758, "state": "IN", "_id": "46319"} -{"city": "HAMMOND", "loc": [-87.507911, 41.609929], "pop": 16636, "state": "IN", "_id": "46320"} -{"city": "MUNSTER", "loc": [-87.501101, 41.554438], "pop": 19906, "state": "IN", "_id": "46321"} -{"city": "HIGHLAND", "loc": [-87.456911, 41.55005], "pop": 24029, "state": "IN", "_id": "46322"} -{"city": "HAMMOND", "loc": [-87.453196, 41.587755], "pop": 23456, "state": "IN", "_id": "46323"} -{"city": "HAMMOND", "loc": [-87.503393, 41.583954], "pop": 23585, "state": "IN", "_id": "46324"} -{"city": "HAMMOND", "loc": [-87.51135, 41.632695], "pop": 12384, "state": "IN", "_id": "46327"} -{"city": "HANNA", "loc": [-86.775922, 41.408767], "pop": 941, "state": "IN", "_id": "46340"} -{"city": "HEBRON", "loc": [-87.208791, 41.315537], "pop": 6230, "state": "IN", "_id": "46341"} -{"city": "HOBART", "loc": [-87.252499, 41.526281], "pop": 32127, "state": "IN", "_id": "46342"} -{"city": "KOUTS", "loc": [-87.024043, 41.309085], "pop": 3244, "state": "IN", "_id": "46347"} -{"city": "LA CROSSE", "loc": [-86.868216, 41.315709], "pop": 1352, "state": "IN", "_id": "46348"} -{"city": "LAKE VILLAGE", "loc": [-87.445422, 41.138741], "pop": 2208, "state": "IN", "_id": "46349"} -{"city": "LA PORTE", "loc": [-86.707654, 41.599438], "pop": 36301, "state": "IN", "_id": "46350"} -{"city": "LOWELL", "loc": [-87.419072, 41.284531], "pop": 12579, "state": "IN", "_id": "46356"} -{"city": "MICHIGAN CITY", "loc": [-86.869899, 41.698031], "pop": 55392, "state": "IN", "_id": "46360"} -{"city": "MILL CREEK", "loc": [-86.547247, 41.556102], "pop": 2091, "state": "IN", "_id": "46365"} -{"city": "NORTH JUDSON", "loc": [-86.696601, 41.224372], "pop": 8426, "state": "IN", "_id": "46366"} -{"city": "PORTAGE", "loc": [-87.175689, 41.567201], "pop": 40860, "state": "IN", "_id": "46368"} -{"city": "ROLLING PRAIRIE", "loc": [-86.584092, 41.72286], "pop": 3694, "state": "IN", "_id": "46371"} -{"city": "SAINT JOHN", "loc": [-87.476376, 41.44949], "pop": 4786, "state": "IN", "_id": "46373"} -{"city": "SAN PIERRE", "loc": [-86.872532, 41.21108], "pop": 1499, "state": "IN", "_id": "46374"} -{"city": "SCHERERVILLE", "loc": [-87.460532, 41.492233], "pop": 14152, "state": "IN", "_id": "46375"} -{"city": "UNION MILLS", "loc": [-86.835512, 41.460236], "pop": 3155, "state": "IN", "_id": "46382"} -{"city": "VALPARAISO", "loc": [-87.075866, 41.475661], "pop": 53439, "state": "IN", "_id": "46383"} -{"city": "WANATAH", "loc": [-86.876439, 41.384477], "pop": 711, "state": "IN", "_id": "46390"} -{"city": "WESTVILLE", "loc": [-86.901317, 41.536516], "pop": 6212, "state": "IN", "_id": "46391"} -{"city": "WHEATFIELD", "loc": [-87.069876, 41.177948], "pop": 5415, "state": "IN", "_id": "46392"} -{"city": "WHITING", "loc": [-87.500537, 41.678656], "pop": 13157, "state": "IN", "_id": "46394"} -{"city": "GARY", "loc": [-87.338548, 41.599711], "pop": 10873, "state": "IN", "_id": "46402"} -{"city": "GARY", "loc": [-87.258984, 41.603612], "pop": 16489, "state": "IN", "_id": "46403"} -{"city": "GARY", "loc": [-87.373153, 41.589937], "pop": 23031, "state": "IN", "_id": "46404"} -{"city": "LAKE STATION", "loc": [-87.262209, 41.568629], "pop": 12437, "state": "IN", "_id": "46405"} -{"city": "GARY", "loc": [-87.40621, 41.587806], "pop": 15132, "state": "IN", "_id": "46406"} -{"city": "GARY", "loc": [-87.334958, 41.580429], "pop": 21360, "state": "IN", "_id": "46407"} -{"city": "GARY", "loc": [-87.35883, 41.542178], "pop": 21586, "state": "IN", "_id": "46408"} -{"city": "GARY", "loc": [-87.327126, 41.541247], "pop": 14119, "state": "IN", "_id": "46409"} -{"city": "MERRILLVILLE", "loc": [-87.350932, 41.4957], "pop": 30765, "state": "IN", "_id": "46410"} -{"city": "ARGOS", "loc": [-86.250573, 41.230827], "pop": 3630, "state": "IN", "_id": "46501"} -{"city": "BOURBON", "loc": [-86.117438, 41.309785], "pop": 2976, "state": "IN", "_id": "46504"} -{"city": "BREMEN", "loc": [-86.19323, 41.446701], "pop": 13832, "state": "IN", "_id": "46506"} -{"city": "BRISTOL", "loc": [-85.826192, 41.716885], "pop": 7086, "state": "IN", "_id": "46507"} -{"city": "CLAYPOOL", "loc": [-85.868571, 41.116497], "pop": 4891, "state": "IN", "_id": "46510"} -{"city": "CULVER MILITARY", "loc": [-86.412888, 41.22307], "pop": 3289, "state": "IN", "_id": "46511"} -{"city": "ELKHART", "loc": [-85.972949, 41.710083], "pop": 33830, "state": "IN", "_id": "46514"} -{"city": "ELKHART", "loc": [-85.962137, 41.676333], "pop": 29971, "state": "IN", "_id": "46516"} -{"city": "ELKHART", "loc": [-85.972849, 41.646922], "pop": 17983, "state": "IN", "_id": "46517"} -{"city": "ETNA GREEN", "loc": [-86.034995, 41.291789], "pop": 1290, "state": "IN", "_id": "46524"} -{"city": "FORAKER", "loc": [-85.837988, 41.584484], "pop": 41317, "state": "IN", "_id": "46526"} -{"city": "GRANGER", "loc": [-86.141104, 41.742704], "pop": 17591, "state": "IN", "_id": "46530"} -{"city": "GROVERTOWN", "loc": [-86.528905, 41.321969], "pop": 1077, "state": "IN", "_id": "46531"} -{"city": "HAMLET", "loc": [-86.53213, 41.393338], "pop": 4314, "state": "IN", "_id": "46532"} -{"city": "OBER", "loc": [-86.610715, 41.29006], "pop": 7437, "state": "IN", "_id": "46534"} -{"city": "LAKEVILLE", "loc": [-86.27144, 41.525328], "pop": 3355, "state": "IN", "_id": "46536"} -{"city": "LEESBURG", "loc": [-85.816028, 41.326592], "pop": 1823, "state": "IN", "_id": "46538"} -{"city": "MENTONE", "loc": [-86.029918, 41.161494], "pop": 2163, "state": "IN", "_id": "46539"} -{"city": "MIDDLEBURY", "loc": [-85.711443, 41.675415], "pop": 7737, "state": "IN", "_id": "46540"} -{"city": "MILFORD", "loc": [-85.855438, 41.401141], "pop": 4861, "state": "IN", "_id": "46542"} -{"city": "MILLERSBURG", "loc": [-85.707249, 41.533513], "pop": 2078, "state": "IN", "_id": "46543"} -{"city": "MISHAWAKA", "loc": [-86.162301, 41.650659], "pop": 29911, "state": "IN", "_id": "46544"} -{"city": "MISHAWAKA", "loc": [-86.168232, 41.683498], "pop": 23031, "state": "IN", "_id": "46545"} -{"city": "NAPPANEE", "loc": [-85.994534, 41.449297], "pop": 10640, "state": "IN", "_id": "46550"} -{"city": "NEW CARLISLE", "loc": [-86.483827, 41.705115], "pop": 3574, "state": "IN", "_id": "46552"} -{"city": "NEW PARIS", "loc": [-85.83383, 41.491652], "pop": 2723, "state": "IN", "_id": "46553"} -{"city": "NORTH LIBERTY", "loc": [-86.413328, 41.542507], "pop": 4056, "state": "IN", "_id": "46554"} -{"city": "NORTH WEBSTER", "loc": [-85.707872, 41.308423], "pop": 6197, "state": "IN", "_id": "46555"} -{"city": "SAINT MARYS", "loc": [-86.242893, 41.70057], "pop": 6903, "state": "IN", "_id": "46556"} -{"city": "OSCEOLA", "loc": [-86.078883, 41.674074], "pop": 8029, "state": "IN", "_id": "46561"} -{"city": "PIERCETON", "loc": [-85.706119, 41.212406], "pop": 3128, "state": "IN", "_id": "46562"} -{"city": "INWOOD", "loc": [-86.324087, 41.333897], "pop": 16087, "state": "IN", "_id": "46563"} -{"city": "SHIPSHEWANA", "loc": [-85.593198, 41.663257], "pop": 3850, "state": "IN", "_id": "46565"} -{"city": "SYRACUSE", "loc": [-85.718352, 41.40649], "pop": 7695, "state": "IN", "_id": "46567"} -{"city": "TIPPECANOE", "loc": [-86.109508, 41.216609], "pop": 1188, "state": "IN", "_id": "46570"} -{"city": "TOPEKA", "loc": [-85.531668, 41.563441], "pop": 5749, "state": "IN", "_id": "46571"} -{"city": "WAKARUSA", "loc": [-86.020524, 41.540073], "pop": 2776, "state": "IN", "_id": "46573"} -{"city": "WALKERTON", "loc": [-86.451018, 41.445748], "pop": 5279, "state": "IN", "_id": "46574"} -{"city": "WARSAW", "loc": [-85.85078, 41.24377], "pop": 22851, "state": "IN", "_id": "46580"} -{"city": "WINONA LAKE", "loc": [-85.830531, 41.211007], "pop": 7559, "state": "IN", "_id": "46590"} -{"city": "SOUTH BEND", "loc": [-86.253489, 41.672699], "pop": 5479, "state": "IN", "_id": "46601"} -{"city": "SOUTH BEND", "loc": [-86.247865, 41.654636], "pop": 11827, "state": "IN", "_id": "46613"} -{"city": "SOUTH BEND", "loc": [-86.243278, 41.625461], "pop": 27521, "state": "IN", "_id": "46614"} -{"city": "SOUTH BEND", "loc": [-86.210375, 41.67413], "pop": 15580, "state": "IN", "_id": "46615"} -{"city": "SOUTH BEND", "loc": [-86.264739, 41.691894], "pop": 8132, "state": "IN", "_id": "46616"} -{"city": "SOUTH BEND", "loc": [-86.2351, 41.684966], "pop": 11057, "state": "IN", "_id": "46617"} -{"city": "SOUTH BEND", "loc": [-86.315266, 41.667397], "pop": 19880, "state": "IN", "_id": "46619"} -{"city": "SOUTH BEND", "loc": [-86.294929, 41.701525], "pop": 24914, "state": "IN", "_id": "46628"} -{"city": "SOUTH BEND", "loc": [-86.207806, 41.716768], "pop": 6989, "state": "IN", "_id": "46635"} -{"city": "SOUTH BEND", "loc": [-86.240694, 41.729936], "pop": 16351, "state": "IN", "_id": "46637"} -{"city": "ALBION", "loc": [-85.414183, 41.348217], "pop": 6907, "state": "IN", "_id": "46701"} -{"city": "ANDREWS", "loc": [-85.606726, 40.861792], "pop": 2138, "state": "IN", "_id": "46702"} -{"city": "ANGOLA", "loc": [-85.019803, 41.656321], "pop": 15134, "state": "IN", "_id": "46703"} -{"city": "ASHLEY", "loc": [-85.050385, 41.534651], "pop": 1272, "state": "IN", "_id": "46705"} -{"city": "AUBURN", "loc": [-85.046848, 41.359001], "pop": 12503, "state": "IN", "_id": "46706"} -{"city": "AVILLA", "loc": [-85.241418, 41.36894], "pop": 2154, "state": "IN", "_id": "46710"} -{"city": "LINN GROVE", "loc": [-84.934271, 40.661517], "pop": 6577, "state": "IN", "_id": "46711"} -{"city": "BLUFFTON", "loc": [-85.162199, 40.736837], "pop": 14669, "state": "IN", "_id": "46714"} -{"city": "BUTLER", "loc": [-84.878716, 41.42873], "pop": 5982, "state": "IN", "_id": "46721"} -{"city": "CHURUBUSCO", "loc": [-85.324364, 41.228988], "pop": 6796, "state": "IN", "_id": "46723"} -{"city": "COLUMBIA CITY", "loc": [-85.473736, 41.161855], "pop": 17282, "state": "IN", "_id": "46725"} -{"city": "CORUNNA", "loc": [-85.137028, 41.450377], "pop": 2373, "state": "IN", "_id": "46730"} -{"city": "CRAIGVILLE", "loc": [-85.090379, 40.793034], "pop": 323, "state": "IN", "_id": "46731"} -{"city": "CROMWELL", "loc": [-85.603133, 41.37514], "pop": 2872, "state": "IN", "_id": "46732"} -{"city": "DECATUR", "loc": [-84.931432, 40.827333], "pop": 19069, "state": "IN", "_id": "46733"} -{"city": "FREMONT", "loc": [-84.945247, 41.733125], "pop": 4248, "state": "IN", "_id": "46737"} -{"city": "GARRETT", "loc": [-85.13467, 41.348216], "pop": 6459, "state": "IN", "_id": "46738"} -{"city": "GENEVA", "loc": [-84.962074, 40.607129], "pop": 3676, "state": "IN", "_id": "46740"} -{"city": "GRABILL", "loc": [-84.940593, 41.210753], "pop": 4267, "state": "IN", "_id": "46741"} -{"city": "HAMILTON", "loc": [-84.895092, 41.5566], "pop": 2344, "state": "IN", "_id": "46742"} -{"city": "HARLAN", "loc": [-84.838635, 41.228468], "pop": 653, "state": "IN", "_id": "46743"} -{"city": "HOAGLAND", "loc": [-85.00751, 40.952375], "pop": 1483, "state": "IN", "_id": "46745"} -{"city": "HOWE", "loc": [-85.472746, 41.728594], "pop": 6040, "state": "IN", "_id": "46746"} -{"city": "HELMER", "loc": [-85.141953, 41.559887], "pop": 2012, "state": "IN", "_id": "46747"} -{"city": "HUNTERTOWN", "loc": [-85.167724, 41.239113], "pop": 2265, "state": "IN", "_id": "46748"} -{"city": "HUNTINGTON", "loc": [-85.505438, 40.881128], "pop": 23716, "state": "IN", "_id": "46750"} -{"city": "KENDALLVILLE", "loc": [-85.260874, 41.448206], "pop": 11784, "state": "IN", "_id": "46755"} -{"city": "KEYSTONE", "loc": [-85.276748, 40.589667], "pop": 518, "state": "IN", "_id": "46759"} -{"city": "KIMMELL", "loc": [-85.518734, 41.363103], "pop": 2095, "state": "IN", "_id": "46760"} -{"city": "LAGRANGE", "loc": [-85.40401, 41.652008], "pop": 8410, "state": "IN", "_id": "46761"} -{"city": "LAOTTO", "loc": [-85.190086, 41.299119], "pop": 3483, "state": "IN", "_id": "46763"} -{"city": "LARWILL", "loc": [-85.613869, 41.164623], "pop": 1496, "state": "IN", "_id": "46764"} -{"city": "LEO", "loc": [-85.030093, 41.224864], "pop": 3047, "state": "IN", "_id": "46765"} -{"city": "LIBERTY CENTER", "loc": [-85.277411, 40.700159], "pop": 1027, "state": "IN", "_id": "46766"} -{"city": "LIGONIER", "loc": [-85.59272, 41.466175], "pop": 5212, "state": "IN", "_id": "46767"} -{"city": "MARKLE", "loc": [-85.373994, 40.837972], "pop": 3084, "state": "IN", "_id": "46770"} -{"city": "MONROE", "loc": [-84.844128, 40.700523], "pop": 873, "state": "IN", "_id": "46772"} -{"city": "MONROEVILLE", "loc": [-84.89373, 40.987044], "pop": 4093, "state": "IN", "_id": "46773"} -{"city": "NEW HAVEN", "loc": [-85.01173, 41.069856], "pop": 12742, "state": "IN", "_id": "46774"} -{"city": "ORLAND", "loc": [-85.146512, 41.730884], "pop": 1326, "state": "IN", "_id": "46776"} -{"city": "OSSIAN", "loc": [-85.157041, 40.880611], "pop": 5394, "state": "IN", "_id": "46777"} -{"city": "PLEASANT LAKE", "loc": [-85.021276, 41.584255], "pop": 1625, "state": "IN", "_id": "46779"} -{"city": "PONETO", "loc": [-85.256188, 40.641871], "pop": 709, "state": "IN", "_id": "46781"} -{"city": "ROANOKE", "loc": [-85.35263, 40.960003], "pop": 4973, "state": "IN", "_id": "46783"} -{"city": "ROME CITY", "loc": [-85.374303, 41.484907], "pop": 3040, "state": "IN", "_id": "46784"} -{"city": "SAINT JOE", "loc": [-84.90425, 41.324049], "pop": 1161, "state": "IN", "_id": "46785"} -{"city": "SOUTH WHITLEY", "loc": [-85.614252, 41.072635], "pop": 3791, "state": "IN", "_id": "46787"} -{"city": "SPENCERVILLE", "loc": [-84.93975, 41.269555], "pop": 1905, "state": "IN", "_id": "46788"} -{"city": "UNIONDALE", "loc": [-85.273206, 40.871658], "pop": 2419, "state": "IN", "_id": "46791"} -{"city": "WARREN", "loc": [-85.418337, 40.688646], "pop": 2404, "state": "IN", "_id": "46792"} -{"city": "WATERLOO", "loc": [-85.022103, 41.440216], "pop": 3802, "state": "IN", "_id": "46793"} -{"city": "WAWAKA", "loc": [-85.480427, 41.483251], "pop": 1545, "state": "IN", "_id": "46794"} -{"city": "WOLCOTTVILLE", "loc": [-85.314986, 41.556972], "pop": 5921, "state": "IN", "_id": "46795"} -{"city": "WOODBURN", "loc": [-84.892871, 41.136102], "pop": 5044, "state": "IN", "_id": "46797"} -{"city": "YODER", "loc": [-85.195828, 40.937059], "pop": 691, "state": "IN", "_id": "46798"} -{"city": "FORT WAYNE", "loc": [-85.15431, 41.070717], "pop": 10837, "state": "IN", "_id": "46802"} -{"city": "FORT WAYNE", "loc": [-85.107362, 41.069452], "pop": 13295, "state": "IN", "_id": "46803"} -{"city": "FORT WAYNE", "loc": [-85.256013, 41.050843], "pop": 23713, "state": "IN", "_id": "46804"} -{"city": "FORT WAYNE", "loc": [-85.118865, 41.097663], "pop": 22657, "state": "IN", "_id": "46805"} -{"city": "FORT WAYNE", "loc": [-85.113496, 41.047988], "pop": 28184, "state": "IN", "_id": "46806"} -{"city": "FORT WAYNE", "loc": [-85.146167, 41.049054], "pop": 18346, "state": "IN", "_id": "46807"} -{"city": "FORT WAYNE", "loc": [-85.162121, 41.093877], "pop": 19401, "state": "IN", "_id": "46808"} -{"city": "FORT WAYNE", "loc": [-85.1834, 41.02543], "pop": 9804, "state": "IN", "_id": "46809"} -{"city": "FORT WAYNE", "loc": [-85.062397, 41.105318], "pop": 25377, "state": "IN", "_id": "46815"} -{"city": "FORT WAYNE", "loc": [-85.097573, 41.016519], "pop": 15507, "state": "IN", "_id": "46816"} -{"city": "FORT WAYNE", "loc": [-85.206686, 41.146847], "pop": 10155, "state": "IN", "_id": "46818"} -{"city": "FORT WAYNE", "loc": [-85.152743, 41.005167], "pop": 9139, "state": "IN", "_id": "46819"} -{"city": "FORT WAYNE", "loc": [-85.123156, 41.146482], "pop": 19522, "state": "IN", "_id": "46825"} -{"city": "FORT WAYNE", "loc": [-85.068531, 41.137051], "pop": 26758, "state": "IN", "_id": "46835"} -{"city": "FORT WAYNE", "loc": [-85.119088, 41.195783], "pop": 9168, "state": "IN", "_id": "46845"} -{"city": "KOKOMO", "loc": [-86.145273, 40.49884], "pop": 37261, "state": "IN", "_id": "46901"} -{"city": "KOKOMO", "loc": [-86.135227, 40.450856], "pop": 36889, "state": "IN", "_id": "46902"} -{"city": "AKRON", "loc": [-86.039471, 41.038921], "pop": 2617, "state": "IN", "_id": "46910"} -{"city": "AMBOY", "loc": [-85.949726, 40.610505], "pop": 1555, "state": "IN", "_id": "46911"} -{"city": "BRINGHURST", "loc": [-86.520369, 40.516294], "pop": 212, "state": "IN", "_id": "46913"} -{"city": "BUNKER HILL", "loc": [-86.09615, 40.642267], "pop": 3073, "state": "IN", "_id": "46914"} -{"city": "CAMDEN", "loc": [-86.515217, 40.599588], "pop": 1527, "state": "IN", "_id": "46917"} -{"city": "CONVERSE", "loc": [-85.876299, 40.577031], "pop": 1353, "state": "IN", "_id": "46919"} -{"city": "CUTLER", "loc": [-86.446172, 40.4785], "pop": 2495, "state": "IN", "_id": "46920"} -{"city": "DELPHI", "loc": [-86.678849, 40.57339], "pop": 7576, "state": "IN", "_id": "46923"} -{"city": "CHILI", "loc": [-86.076953, 40.869363], "pop": 5339, "state": "IN", "_id": "46926"} -{"city": "FAIRMOUNT", "loc": [-85.671327, 40.418755], "pop": 6230, "state": "IN", "_id": "46928"} -{"city": "FLORA", "loc": [-86.501572, 40.544467], "pop": 3249, "state": "IN", "_id": "46929"} -{"city": "GALVESTON", "loc": [-86.1972, 40.586249], "pop": 3059, "state": "IN", "_id": "46932"} -{"city": "GAS CITY", "loc": [-85.605533, 40.487895], "pop": 6975, "state": "IN", "_id": "46933"} -{"city": "GREENTOWN", "loc": [-85.958195, 40.479096], "pop": 5785, "state": "IN", "_id": "46936"} -{"city": "JONESBORO", "loc": [-85.636496, 40.481456], "pop": 3989, "state": "IN", "_id": "46938"} -{"city": "KEWANNA", "loc": [-86.406055, 41.008706], "pop": 1966, "state": "IN", "_id": "46939"} -{"city": "LA FONTAINE", "loc": [-85.697138, 40.690896], "pop": 2947, "state": "IN", "_id": "46940"} -{"city": "LAGRO", "loc": [-85.720447, 40.819897], "pop": 2255, "state": "IN", "_id": "46941"} -{"city": "LOGANSPORT", "loc": [-86.359888, 40.760377], "pop": 27829, "state": "IN", "_id": "46947"} -{"city": "LUCERNE", "loc": [-86.407726, 40.861434], "pop": 809, "state": "IN", "_id": "46950"} -{"city": "MACY", "loc": [-86.126428, 40.961838], "pop": 633, "state": "IN", "_id": "46951"} -{"city": "MARION", "loc": [-85.674127, 40.574333], "pop": 24986, "state": "IN", "_id": "46952"} -{"city": "MARION", "loc": [-85.661624, 40.53592], "pop": 22079, "state": "IN", "_id": "46953"} -{"city": "MONTEREY", "loc": [-86.517862, 41.138334], "pop": 997, "state": "IN", "_id": "46960"} -{"city": "NORTH MANCHESTER", "loc": [-85.784184, 40.998603], "pop": 10772, "state": "IN", "_id": "46962"} -{"city": "PERU", "loc": [-86.068044, 40.749203], "pop": 19549, "state": "IN", "_id": "46970"} -{"city": "GRISSOM AIR FORC", "loc": [-86.147292, 40.663546], "pop": 4364, "state": "IN", "_id": "46971"} -{"city": "ROANN", "loc": [-85.889885, 40.906649], "pop": 1612, "state": "IN", "_id": "46974"} -{"city": "ROCHESTER", "loc": [-86.231007, 41.065493], "pop": 14259, "state": "IN", "_id": "46975"} -{"city": "ROYAL CENTER", "loc": [-86.507751, 40.864499], "pop": 1558, "state": "IN", "_id": "46978"} -{"city": "RUSSIAVILLE", "loc": [-86.267467, 40.415086], "pop": 1724, "state": "IN", "_id": "46979"} -{"city": "SILVER LAKE", "loc": [-85.879207, 41.074318], "pop": 1566, "state": "IN", "_id": "46982"} -{"city": "STAR CITY", "loc": [-86.540405, 40.960176], "pop": 951, "state": "IN", "_id": "46985"} -{"city": "SWAYZEE", "loc": [-85.826542, 40.511199], "pop": 1891, "state": "IN", "_id": "46986"} -{"city": "TWELVE MILE", "loc": [-86.212595, 40.854661], "pop": 873, "state": "IN", "_id": "46988"} -{"city": "UPLAND", "loc": [-85.499049, 40.454841], "pop": 6027, "state": "IN", "_id": "46989"} -{"city": "URBANA", "loc": [-85.748481, 40.898655], "pop": 505, "state": "IN", "_id": "46990"} -{"city": "LANDESS", "loc": [-85.481891, 40.63199], "pop": 3670, "state": "IN", "_id": "46991"} -{"city": "WABASH", "loc": [-85.832124, 40.790947], "pop": 17371, "state": "IN", "_id": "46992"} -{"city": "WALTON", "loc": [-86.280501, 40.677225], "pop": 4935, "state": "IN", "_id": "46994"} -{"city": "WINAMAC", "loc": [-86.630697, 41.056242], "pop": 6857, "state": "IN", "_id": "46996"} -{"city": "AURORA", "loc": [-84.945188, 39.071897], "pop": 9435, "state": "IN", "_id": "47001"} -{"city": "BATESVILLE", "loc": [-85.222053, 39.300057], "pop": 6963, "state": "IN", "_id": "47006"} -{"city": "BATH", "loc": [-84.836007, 39.499237], "pop": 197, "state": "IN", "_id": "47010"} -{"city": "BENNINGTON", "loc": [-85.071897, 38.875993], "pop": 1376, "state": "IN", "_id": "47011"} -{"city": "BROOKVILLE", "loc": [-84.999423, 39.421305], "pop": 9204, "state": "IN", "_id": "47012"} -{"city": "CEDAR GROVE", "loc": [-84.892402, 39.345891], "pop": 1522, "state": "IN", "_id": "47016"} -{"city": "CROSS PLAINS", "loc": [-85.21221, 38.949046], "pop": 802, "state": "IN", "_id": "47017"} -{"city": "DILLSBORO", "loc": [-85.054994, 38.996195], "pop": 3699, "state": "IN", "_id": "47018"} -{"city": "FLORENCE", "loc": [-84.939872, 38.822436], "pop": 1019, "state": "IN", "_id": "47020"} -{"city": "FRIENDSHIP", "loc": [-85.215296, 39.114591], "pop": 218, "state": "IN", "_id": "47021"} -{"city": "GUILFORD", "loc": [-84.961586, 39.205855], "pop": 2595, "state": "IN", "_id": "47022"} -{"city": "HOLTON", "loc": [-85.373945, 39.049817], "pop": 2384, "state": "IN", "_id": "47023"} -{"city": "LAUREL", "loc": [-85.208044, 39.491573], "pop": 3041, "state": "IN", "_id": "47024"} -{"city": "LAWRENCEBURG", "loc": [-84.865819, 39.140123], "pop": 15358, "state": "IN", "_id": "47025"} -{"city": "METAMORA", "loc": [-85.15044, 39.428775], "pop": 1085, "state": "IN", "_id": "47030"} -{"city": "MILAN", "loc": [-85.13242, 39.150333], "pop": 3877, "state": "IN", "_id": "47031"} -{"city": "MOORES HILL", "loc": [-85.063806, 39.094459], "pop": 2729, "state": "IN", "_id": "47032"} -{"city": "OLDENBURG", "loc": [-85.22223, 39.359797], "pop": 1398, "state": "IN", "_id": "47036"} -{"city": "OSGOOD", "loc": [-85.293812, 39.157342], "pop": 4785, "state": "IN", "_id": "47037"} -{"city": "PATRIOT", "loc": [-84.851543, 38.853691], "pop": 1265, "state": "IN", "_id": "47038"} -{"city": "RISING SUN", "loc": [-84.880676, 38.956667], "pop": 4500, "state": "IN", "_id": "47040"} -{"city": "SUNMAN", "loc": [-85.115929, 39.262307], "pop": 4719, "state": "IN", "_id": "47041"} -{"city": "VERSAILLES", "loc": [-85.223489, 39.051074], "pop": 4753, "state": "IN", "_id": "47042"} -{"city": "VEVAY", "loc": [-85.085217, 38.772423], "pop": 3974, "state": "IN", "_id": "47043"} -{"city": "W HARRISON", "loc": [-84.878027, 39.266727], "pop": 5338, "state": "IN", "_id": "47060"} -{"city": "AUSTIN", "loc": [-85.796188, 38.747801], "pop": 6713, "state": "IN", "_id": "47102"} -{"city": "BORDEN", "loc": [-85.921473, 38.436171], "pop": 4080, "state": "IN", "_id": "47106"} -{"city": "CAMPBELLSBURG", "loc": [-86.235846, 38.669037], "pop": 2900, "state": "IN", "_id": "47108"} -{"city": "CENTRAL", "loc": [-86.197381, 38.094543], "pop": 779, "state": "IN", "_id": "47110"} -{"city": "CHARLESTOWN", "loc": [-85.660614, 38.456778], "pop": 9896, "state": "IN", "_id": "47111"} -{"city": "CORYDON", "loc": [-86.114465, 38.218865], "pop": 10928, "state": "IN", "_id": "47112"} -{"city": "CRANDALL", "loc": [-86.069804, 38.28927], "pop": 239, "state": "IN", "_id": "47114"} -{"city": "DEPAUW", "loc": [-86.210857, 38.336078], "pop": 3269, "state": "IN", "_id": "47115"} -{"city": "ECKERTY", "loc": [-86.605939, 38.318548], "pop": 971, "state": "IN", "_id": "47116"} -{"city": "ELIZABETH", "loc": [-85.958874, 38.124389], "pop": 3273, "state": "IN", "_id": "47117"} -{"city": "ENGLISH", "loc": [-86.442875, 38.325807], "pop": 3424, "state": "IN", "_id": "47118"} -{"city": "FLOYDS KNOBS", "loc": [-85.899555, 38.351006], "pop": 9954, "state": "IN", "_id": "47119"} -{"city": "FREDERICKSBURG", "loc": [-86.178255, 38.482118], "pop": 1846, "state": "IN", "_id": "47120"} -{"city": "GEORGETOWN", "loc": [-85.961704, 38.302943], "pop": 4446, "state": "IN", "_id": "47122"} -{"city": "GRANTSBURG", "loc": [-86.466043, 38.289183], "pop": 154, "state": "IN", "_id": "47123"} -{"city": "GREENVILLE", "loc": [-86.008299, 38.353533], "pop": 1162, "state": "IN", "_id": "47124"} -{"city": "HARDINSBURG", "loc": [-86.317983, 38.462599], "pop": 2496, "state": "IN", "_id": "47125"} -{"city": "HENRYVILLE", "loc": [-85.773403, 38.539829], "pop": 2648, "state": "IN", "_id": "47126"} -{"city": "CLARKSVILLE", "loc": [-85.524438, 38.537273], "pop": 379, "state": "IN", "_id": "47129"} -{"city": "JEFFERSONVILLE", "loc": [-85.735885, 38.307767], "pop": 56543, "state": "IN", "_id": "47130"} -{"city": "LACONIA", "loc": [-86.084415, 38.052703], "pop": 1058, "state": "IN", "_id": "47135"} -{"city": "LANESVILLE", "loc": [-85.959314, 38.244817], "pop": 3524, "state": "IN", "_id": "47136"} -{"city": "LEAVENWORTH", "loc": [-86.359862, 38.194244], "pop": 1152, "state": "IN", "_id": "47137"} -{"city": "LEXINGTON", "loc": [-85.658915, 38.650643], "pop": 2803, "state": "IN", "_id": "47138"} -{"city": "MARENGO", "loc": [-86.357784, 38.373603], "pop": 1539, "state": "IN", "_id": "47140"} -{"city": "MARYSVILLE", "loc": [-85.630809, 38.554488], "pop": 828, "state": "IN", "_id": "47141"} -{"city": "MAUCKPORT", "loc": [-86.184622, 38.04366], "pop": 520, "state": "IN", "_id": "47142"} -{"city": "MEMPHIS", "loc": [-85.777501, 38.464181], "pop": 2381, "state": "IN", "_id": "47143"} -{"city": "MILLTOWN", "loc": [-86.300344, 38.344453], "pop": 1724, "state": "IN", "_id": "47145"} -{"city": "NABB", "loc": [-85.521704, 38.612768], "pop": 2246, "state": "IN", "_id": "47147"} -{"city": "NEW ALBANY", "loc": [-85.822085, 38.308919], "pop": 44969, "state": "IN", "_id": "47150"} -{"city": "NEW MIDDLETOWN", "loc": [-86.055261, 38.144199], "pop": 1593, "state": "IN", "_id": "47160"} -{"city": "NEW SALISBURY", "loc": [-86.088731, 38.339885], "pop": 4814, "state": "IN", "_id": "47161"} -{"city": "NEW WASHINGTON", "loc": [-85.45987, 38.557521], "pop": 380, "state": "IN", "_id": "47162"} -{"city": "OTISCO", "loc": [-85.664702, 38.542342], "pop": 1738, "state": "IN", "_id": "47163"} -{"city": "PALMYRA", "loc": [-86.088778, 38.410484], "pop": 814, "state": "IN", "_id": "47164"} -{"city": "PEKIN", "loc": [-86.017045, 38.49308], "pop": 5351, "state": "IN", "_id": "47165"} -{"city": "RAMSEY", "loc": [-86.130768, 38.335442], "pop": 1146, "state": "IN", "_id": "47166"} -{"city": "SALEM", "loc": [-86.078742, 38.607138], "pop": 10949, "state": "IN", "_id": "47167"} -{"city": "SCOTTSBURG", "loc": [-85.798654, 38.688499], "pop": 13114, "state": "IN", "_id": "47170"} -{"city": "SPEED", "loc": [-85.763518, 38.390331], "pop": 8658, "state": "IN", "_id": "47172"} -{"city": "SULPHUR", "loc": [-86.487296, 38.227194], "pop": 303, "state": "IN", "_id": "47174"} -{"city": "TASWELL", "loc": [-86.538675, 38.346302], "pop": 950, "state": "IN", "_id": "47175"} -{"city": "UNDERWOOD", "loc": [-85.767211, 38.590251], "pop": 818, "state": "IN", "_id": "47177"} -{"city": "COLUMBUS", "loc": [-85.931745, 39.205507], "pop": 40769, "state": "IN", "_id": "47201"} -{"city": "COLUMBUS", "loc": [-85.885497, 39.230097], "pop": 15656, "state": "IN", "_id": "47203"} -{"city": "BROWNSTOWN", "loc": [-86.048619, 38.883593], "pop": 4963, "state": "IN", "_id": "47220"} -{"city": "BUTLERVILLE", "loc": [-85.494415, 39.120598], "pop": 813, "state": "IN", "_id": "47223"} -{"city": "CANAAN", "loc": [-85.268896, 38.869644], "pop": 936, "state": "IN", "_id": "47224"} -{"city": "COMMISKEY", "loc": [-85.643384, 38.852629], "pop": 896, "state": "IN", "_id": "47227"} -{"city": "CORTLAND", "loc": [-86.000918, 38.991546], "pop": 1680, "state": "IN", "_id": "47228"} -{"city": "CROTHERSVILLE", "loc": [-85.846965, 38.806672], "pop": 3865, "state": "IN", "_id": "47229"} -{"city": "DEPUTY", "loc": [-85.630407, 38.775891], "pop": 1448, "state": "IN", "_id": "47230"} -{"city": "DUPONT", "loc": [-85.509205, 38.890504], "pop": 716, "state": "IN", "_id": "47231"} -{"city": "ELIZABETHTOWN", "loc": [-85.815311, 39.124291], "pop": 1222, "state": "IN", "_id": "47232"} -{"city": "FLAT ROCK", "loc": [-85.675886, 39.407503], "pop": 1496, "state": "IN", "_id": "47234"} -{"city": "FREETOWN", "loc": [-86.124051, 38.995728], "pop": 1380, "state": "IN", "_id": "47235"} -{"city": "GRAMMER", "loc": [-85.718338, 39.159192], "pop": 412, "state": "IN", "_id": "47236"} -{"city": "ADAMS", "loc": [-85.473532, 39.331223], "pop": 19250, "state": "IN", "_id": "47240"} -{"city": "HANOVER", "loc": [-85.476316, 38.71378], "pop": 4900, "state": "IN", "_id": "47243"} -{"city": "HARTSVILLE", "loc": [-85.70046, 39.273318], "pop": 543, "state": "IN", "_id": "47244"} -{"city": "HOPE", "loc": [-85.766538, 39.25733], "pop": 5676, "state": "IN", "_id": "47246"} -{"city": "MADISON", "loc": [-85.407019, 38.764866], "pop": 20596, "state": "IN", "_id": "47250"} -{"city": "MEDORA", "loc": [-86.189733, 38.825505], "pop": 1576, "state": "IN", "_id": "47260"} -{"city": "NORMAN", "loc": [-86.207934, 38.929996], "pop": 1834, "state": "IN", "_id": "47264"} -{"city": "NORTH VERNON", "loc": [-85.627216, 39.001763], "pop": 13466, "state": "IN", "_id": "47265"} -{"city": "PARIS CROSSING", "loc": [-85.748725, 38.855843], "pop": 972, "state": "IN", "_id": "47270"} -{"city": "SAINT PAUL", "loc": [-85.599374, 39.427747], "pop": 1389, "state": "IN", "_id": "47272"} -{"city": "SCIPIO", "loc": [-85.712859, 39.066544], "pop": 5043, "state": "IN", "_id": "47273"} -{"city": "SEYMOUR", "loc": [-85.882477, 38.957133], "pop": 21094, "state": "IN", "_id": "47274"} -{"city": "VALLONIA", "loc": [-86.068997, 38.817413], "pop": 1338, "state": "IN", "_id": "47281"} -{"city": "VERNON", "loc": [-85.598377, 38.96887], "pop": 2277, "state": "IN", "_id": "47282"} -{"city": "WESTPORT", "loc": [-85.585596, 39.174856], "pop": 3065, "state": "IN", "_id": "47283"} -{"city": "MUNCIE", "loc": [-85.380689, 40.168414], "pop": 29709, "state": "IN", "_id": "47302"} -{"city": "MUNCIE", "loc": [-85.378966, 40.217992], "pop": 26033, "state": "IN", "_id": "47303"} -{"city": "MUNCIE", "loc": [-85.429115, 40.211134], "pop": 28452, "state": "IN", "_id": "47304"} -{"city": "MUNCIE", "loc": [-85.386163, 40.193299], "pop": 5251, "state": "IN", "_id": "47305"} -{"city": "BALL STATE UNIVE", "loc": [-85.410153, 40.192739], "pop": 3259, "state": "IN", "_id": "47306"} -{"city": "ALBANY", "loc": [-85.257987, 40.292049], "pop": 4625, "state": "IN", "_id": "47320"} -{"city": "BROWNSVILLE", "loc": [-84.98807, 39.684485], "pop": 850, "state": "IN", "_id": "47325"} -{"city": "BRYANT", "loc": [-84.911748, 40.54462], "pop": 1350, "state": "IN", "_id": "47326"} -{"city": "CAMBRIDGE CITY", "loc": [-85.168455, 39.818171], "pop": 4998, "state": "IN", "_id": "47327"} -{"city": "CENTERVILLE", "loc": [-85.00317, 39.808103], "pop": 5475, "state": "IN", "_id": "47330"} -{"city": "CONNERSVILLE", "loc": [-85.146431, 39.643508], "pop": 25503, "state": "IN", "_id": "47331"} -{"city": "DALEVILLE", "loc": [-85.511016, 40.125738], "pop": 5063, "state": "IN", "_id": "47334"} -{"city": "DUNKIRK", "loc": [-85.225541, 40.388291], "pop": 4413, "state": "IN", "_id": "47336"} -{"city": "EATON", "loc": [-85.354387, 40.337673], "pop": 3637, "state": "IN", "_id": "47338"} -{"city": "ECONOMY", "loc": [-85.087803, 39.971215], "pop": 677, "state": "IN", "_id": "47339"} -{"city": "FARMLAND", "loc": [-85.125381, 40.194454], "pop": 1784, "state": "IN", "_id": "47340"} -{"city": "FOUNTAIN CITY", "loc": [-84.908996, 39.963429], "pop": 2020, "state": "IN", "_id": "47341"} -{"city": "GASTON", "loc": [-85.489842, 40.29479], "pop": 4594, "state": "IN", "_id": "47342"} -{"city": "GREENS FORK", "loc": [-85.049413, 39.89163], "pop": 1054, "state": "IN", "_id": "47345"} -{"city": "HAGERSTOWN", "loc": [-85.160091, 39.92277], "pop": 3906, "state": "IN", "_id": "47346"} -{"city": "HARTFORD CITY", "loc": [-85.375771, 40.454106], "pop": 9762, "state": "IN", "_id": "47348"} -{"city": "LEWISVILLE", "loc": [-85.36211, 39.828288], "pop": 1219, "state": "IN", "_id": "47352"} -{"city": "LIBERTY", "loc": [-84.908947, 39.612645], "pop": 6126, "state": "IN", "_id": "47353"} -{"city": "LOSANTVILLE", "loc": [-85.21085, 40.047492], "pop": 1768, "state": "IN", "_id": "47354"} -{"city": "LYNN", "loc": [-84.93003, 40.051863], "pop": 3544, "state": "IN", "_id": "47355"} -{"city": "MIDDLETOWN", "loc": [-85.536778, 40.047488], "pop": 4613, "state": "IN", "_id": "47356"} -{"city": "MILTON", "loc": [-85.142267, 39.776279], "pop": 1488, "state": "IN", "_id": "47357"} -{"city": "MODOC", "loc": [-85.091904, 40.05816], "pop": 1253, "state": "IN", "_id": "47358"} -{"city": "MONTPELIER", "loc": [-85.251339, 40.557656], "pop": 3753, "state": "IN", "_id": "47359"} -{"city": "MOORELAND", "loc": [-85.258065, 39.994066], "pop": 1265, "state": "IN", "_id": "47360"} -{"city": "NEW CASTLE", "loc": [-85.366322, 39.920765], "pop": 25888, "state": "IN", "_id": "47362"} -{"city": "PARKER CITY", "loc": [-85.196265, 40.193841], "pop": 1818, "state": "IN", "_id": "47368"} -{"city": "PENNVILLE", "loc": [-85.149166, 40.508195], "pop": 1236, "state": "IN", "_id": "47369"} -{"city": "PORTLAND", "loc": [-84.992825, 40.430564], "pop": 13186, "state": "IN", "_id": "47371"} -{"city": "REDKEY", "loc": [-85.161966, 40.326491], "pop": 2971, "state": "IN", "_id": "47373"} -{"city": "RICHMOND", "loc": [-84.893606, 39.83244], "pop": 50516, "state": "IN", "_id": "47374"} -{"city": "RIDGEVILLE", "loc": [-85.037101, 40.280359], "pop": 1343, "state": "IN", "_id": "47380"} -{"city": "SALAMONIA", "loc": [-84.855098, 40.358234], "pop": 741, "state": "IN", "_id": "47381"} -{"city": "SARATOGA", "loc": [-84.950846, 40.257365], "pop": 1244, "state": "IN", "_id": "47382"} -{"city": "SELMA", "loc": [-85.273765, 40.169295], "pop": 4009, "state": "IN", "_id": "47383"} -{"city": "SHIRLEY", "loc": [-85.518151, 39.91582], "pop": 2292, "state": "IN", "_id": "47384"} -{"city": "SPICELAND", "loc": [-85.445043, 39.827078], "pop": 2270, "state": "IN", "_id": "47385"} -{"city": "SPRINGPORT", "loc": [-85.368867, 40.008848], "pop": 3393, "state": "IN", "_id": "47386"} -{"city": "STRAUGHN", "loc": [-85.272406, 39.831946], "pop": 1300, "state": "IN", "_id": "47387"} -{"city": "SULPHUR SPRINGS", "loc": [-85.440747, 40.011121], "pop": 1242, "state": "IN", "_id": "47388"} -{"city": "UNION CITY", "loc": [-84.826787, 40.202372], "pop": 5393, "state": "IN", "_id": "47390"} -{"city": "WEBSTER", "loc": [-84.942908, 39.905701], "pop": 540, "state": "IN", "_id": "47392"} -{"city": "WILLIAMSBURG", "loc": [-84.998442, 39.958005], "pop": 1272, "state": "IN", "_id": "47393"} -{"city": "WINCHESTER", "loc": [-85.004366, 40.16959], "pop": 8830, "state": "IN", "_id": "47394"} -{"city": "YORKTOWN", "loc": [-85.495995, 40.183581], "pop": 5027, "state": "IN", "_id": "47396"} -{"city": "BLOOMINGTON", "loc": [-86.508262, 39.140057], "pop": 31456, "state": "IN", "_id": "47401"} -{"city": "BLOOMINGTON", "loc": [-86.576867, 39.12632], "pop": 23435, "state": "IN", "_id": "47403"} -{"city": "BLOOMINGTON", "loc": [-86.57572, 39.195026], "pop": 15079, "state": "IN", "_id": "47404"} -{"city": "WOODBRIDGE", "loc": [-86.505836, 39.183175], "pop": 30907, "state": "IN", "_id": "47408"} -{"city": "BEDFORD", "loc": [-86.487072, 38.872881], "pop": 27071, "state": "IN", "_id": "47421"} -{"city": "BLOOMFIELD", "loc": [-86.867549, 39.029542], "pop": 9014, "state": "IN", "_id": "47424"} -{"city": "COAL CITY", "loc": [-86.988297, 39.257304], "pop": 1647, "state": "IN", "_id": "47427"} -{"city": "ELLETTSVILLE", "loc": [-86.619635, 39.254477], "pop": 4960, "state": "IN", "_id": "47429"} -{"city": "FREEDOM", "loc": [-86.850027, 39.215147], "pop": 1003, "state": "IN", "_id": "47431"} -{"city": "FRENCH LICK", "loc": [-86.619555, 38.532351], "pop": 3920, "state": "IN", "_id": "47432"} -{"city": "GOSPORT", "loc": [-86.6583, 39.344969], "pop": 2269, "state": "IN", "_id": "47433"} -{"city": "HELTONVILLE", "loc": [-86.370328, 38.948014], "pop": 1402, "state": "IN", "_id": "47436"} -{"city": "JASONVILLE", "loc": [-87.202292, 39.172333], "pop": 4340, "state": "IN", "_id": "47438"} -{"city": "LINTON", "loc": [-87.172286, 39.046139], "pop": 9233, "state": "IN", "_id": "47441"} -{"city": "LYONS", "loc": [-87.101595, 38.971731], "pop": 1706, "state": "IN", "_id": "47443"} -{"city": "MITCHELL", "loc": [-86.476096, 38.742625], "pop": 9516, "state": "IN", "_id": "47446"} -{"city": "NASHVILLE", "loc": [-86.22199, 39.236712], "pop": 11751, "state": "IN", "_id": "47448"} -{"city": "NEWBERRY", "loc": [-87.008055, 38.922905], "pop": 386, "state": "IN", "_id": "47449"} -{"city": "OOLITIC", "loc": [-86.524617, 38.89378], "pop": 1493, "state": "IN", "_id": "47451"} -{"city": "ORLEANS", "loc": [-86.453157, 38.653464], "pop": 4309, "state": "IN", "_id": "47452"} -{"city": "OWENSBURG", "loc": [-86.744122, 38.952216], "pop": 1497, "state": "IN", "_id": "47453"} -{"city": "PAOLI", "loc": [-86.44902, 38.550697], "pop": 6790, "state": "IN", "_id": "47454"} -{"city": "QUINCY", "loc": [-86.802506, 39.439521], "pop": 2959, "state": "IN", "_id": "47456"} -{"city": "SOLSBERRY", "loc": [-86.737843, 39.119047], "pop": 1832, "state": "IN", "_id": "47459"} -{"city": "SPENCER", "loc": [-86.77894, 39.289067], "pop": 10170, "state": "IN", "_id": "47460"} -{"city": "SPRINGVILLE", "loc": [-86.613879, 38.950655], "pop": 1726, "state": "IN", "_id": "47462"} -{"city": "SWITZ CITY", "loc": [-87.050238, 39.036937], "pop": 1347, "state": "IN", "_id": "47465"} -{"city": "UNIONVILLE", "loc": [-86.418947, 39.251396], "pop": 1111, "state": "IN", "_id": "47468"} -{"city": "WEST BADEN SPRIN", "loc": [-86.613826, 38.585501], "pop": 1757, "state": "IN", "_id": "47469"} -{"city": "WILLIAMS", "loc": [-86.628488, 38.773478], "pop": 1960, "state": "IN", "_id": "47470"} -{"city": "WORTHINGTON", "loc": [-86.999058, 39.12298], "pop": 2365, "state": "IN", "_id": "47471"} -{"city": "WASHINGTON", "loc": [-87.170656, 38.653568], "pop": 15495, "state": "IN", "_id": "47501"} -{"city": "BICKNELL", "loc": [-87.313718, 38.77272], "pop": 4470, "state": "IN", "_id": "47512"} -{"city": "BIRDSEYE", "loc": [-86.710193, 38.30058], "pop": 1591, "state": "IN", "_id": "47513"} -{"city": "BRANCHVILLE", "loc": [-86.585866, 38.157189], "pop": 1314, "state": "IN", "_id": "47514"} -{"city": "SIBERIA", "loc": [-86.721815, 38.163101], "pop": 912, "state": "IN", "_id": "47515"} -{"city": "BRUCEVILLE", "loc": [-87.431299, 38.756279], "pop": 1167, "state": "IN", "_id": "47516"} -{"city": "CANNELBURG", "loc": [-86.966868, 38.694696], "pop": 1265, "state": "IN", "_id": "47519"} -{"city": "MOUNT PLEASANT", "loc": [-86.732617, 37.910851], "pop": 2290, "state": "IN", "_id": "47520"} -{"city": "CELESTINE", "loc": [-86.756809, 38.387789], "pop": 858, "state": "IN", "_id": "47521"} -{"city": "CRANE NAVAL DEPO", "loc": [-86.865473, 38.849618], "pop": 371, "state": "IN", "_id": "47522"} -{"city": "DALE", "loc": [-87.006982, 38.170638], "pop": 3459, "state": "IN", "_id": "47523"} -{"city": "DECKER", "loc": [-87.553713, 38.507629], "pop": 621, "state": "IN", "_id": "47524"} -{"city": "DERBY", "loc": [-86.576951, 38.023933], "pop": 485, "state": "IN", "_id": "47525"} -{"city": "DUBOIS", "loc": [-86.78319, 38.472824], "pop": 2200, "state": "IN", "_id": "47527"} -{"city": "EDWARDSPORT", "loc": [-87.251856, 38.810584], "pop": 497, "state": "IN", "_id": "47528"} -{"city": "ELNORA", "loc": [-87.065243, 38.822284], "pop": 2373, "state": "IN", "_id": "47529"} -{"city": "EVANSTON", "loc": [-86.836422, 38.022436], "pop": 163, "state": "IN", "_id": "47531"} -{"city": "FERDINAND", "loc": [-86.860669, 38.233582], "pop": 3725, "state": "IN", "_id": "47532"} -{"city": "GENTRYVILLE", "loc": [-87.044137, 38.085458], "pop": 824, "state": "IN", "_id": "47537"} -{"city": "HOLLAND", "loc": [-87.008768, 38.23851], "pop": 2072, "state": "IN", "_id": "47541"} -{"city": "HUNTINGBURG", "loc": [-86.953299, 38.297902], "pop": 7415, "state": "IN", "_id": "47542"} -{"city": "HAYSVILLE", "loc": [-86.940787, 38.404392], "pop": 16927, "state": "IN", "_id": "47546"} -{"city": "BUFFALOVILLE", "loc": [-86.962908, 38.047904], "pop": 202, "state": "IN", "_id": "47550"} -{"city": "LEOPOLD", "loc": [-86.60443, 38.101138], "pop": 623, "state": "IN", "_id": "47551"} -{"city": "LINCOLN CITY", "loc": [-86.987204, 38.127565], "pop": 233, "state": "IN", "_id": "47552"} -{"city": "LOOGOOTEE", "loc": [-86.913658, 38.662901], "pop": 6068, "state": "IN", "_id": "47553"} -{"city": "MAGNET", "loc": [-86.48353, 38.088165], "pop": 240, "state": "IN", "_id": "47555"} -{"city": "MARIAH HILL", "loc": [-86.831593, 38.149493], "pop": 1317, "state": "IN", "_id": "47556"} -{"city": "MONROE CITY", "loc": [-87.364136, 38.60015], "pop": 1915, "state": "IN", "_id": "47557"} -{"city": "MONTGOMERY", "loc": [-87.047603, 38.652146], "pop": 2675, "state": "IN", "_id": "47558"} -{"city": "47559", "loc": [-86.519682, 38.126823], "pop": 23, "state": "IN", "_id": "47559"} -{"city": "OAKTOWN", "loc": [-87.387877, 38.857939], "pop": 2569, "state": "IN", "_id": "47561"} -{"city": "ODON", "loc": [-86.975214, 38.818666], "pop": 4198, "state": "IN", "_id": "47562"} -{"city": "OTWELL", "loc": [-87.098548, 38.466221], "pop": 912, "state": "IN", "_id": "47564"} -{"city": "PETERSBURG", "loc": [-87.288294, 38.478929], "pop": 6614, "state": "IN", "_id": "47567"} -{"city": "PLAINVILLE", "loc": [-87.157822, 38.791507], "pop": 895, "state": "IN", "_id": "47568"} -{"city": "ROME", "loc": [-86.595787, 37.937561], "pop": 297, "state": "IN", "_id": "47574"} -{"city": "KYANA", "loc": [-86.824858, 38.332222], "pop": 2012, "state": "IN", "_id": "47575"} -{"city": "SAINT CROIX", "loc": [-86.603511, 38.239773], "pop": 325, "state": "IN", "_id": "47576"} -{"city": "SAINT MEINRAD", "loc": [-86.842916, 38.181946], "pop": 652, "state": "IN", "_id": "47577"} -{"city": "SANDBORN", "loc": [-87.202532, 38.881743], "pop": 919, "state": "IN", "_id": "47578"} -{"city": "SANTA CLAUS", "loc": [-86.903528, 38.094496], "pop": 2323, "state": "IN", "_id": "47579"} -{"city": "SCHNELLVILLE", "loc": [-86.756506, 38.341436], "pop": 199, "state": "IN", "_id": "47580"} -{"city": "SHOALS", "loc": [-86.776094, 38.679103], "pop": 4562, "state": "IN", "_id": "47581"} -{"city": "STENDAL", "loc": [-87.12045, 38.283472], "pop": 596, "state": "IN", "_id": "47585"} -{"city": "TELL CITY", "loc": [-86.745704, 37.965505], "pop": 11583, "state": "IN", "_id": "47586"} -{"city": "TOBINSPORT", "loc": [-86.634128, 37.874726], "pop": 151, "state": "IN", "_id": "47587"} -{"city": "TROY", "loc": [-86.797766, 37.997994], "pop": 727, "state": "IN", "_id": "47588"} -{"city": "VELPEN", "loc": [-87.098996, 38.367981], "pop": 393, "state": "IN", "_id": "47590"} -{"city": "VINCENNES", "loc": [-87.509806, 38.67344], "pop": 26841, "state": "IN", "_id": "47591"} -{"city": "WHEATLAND", "loc": [-87.306204, 38.667055], "pop": 885, "state": "IN", "_id": "47597"} -{"city": "WINSLOW", "loc": [-87.222328, 38.363951], "pop": 3994, "state": "IN", "_id": "47598"} -{"city": "BOONVILLE", "loc": [-87.261994, 38.047435], "pop": 11964, "state": "IN", "_id": "47601"} -{"city": "CHANDLER", "loc": [-87.375552, 38.042304], "pop": 5641, "state": "IN", "_id": "47610"} -{"city": "CHRISNEY", "loc": [-87.070421, 38.005995], "pop": 1455, "state": "IN", "_id": "47611"} -{"city": "CYNTHIANA", "loc": [-87.711518, 38.174299], "pop": 1277, "state": "IN", "_id": "47612"} -{"city": "ELBERFELD", "loc": [-87.417943, 38.205267], "pop": 3361, "state": "IN", "_id": "47613"} -{"city": "GRANDVIEW", "loc": [-86.956774, 37.970297], "pop": 1990, "state": "IN", "_id": "47615"} -{"city": "GRIFFIN", "loc": [-87.916631, 38.206858], "pop": 329, "state": "IN", "_id": "47616"} -{"city": "LYNNVILLE", "loc": [-87.293486, 38.196085], "pop": 1329, "state": "IN", "_id": "47619"} -{"city": "MOUNT VERNON", "loc": [-87.856887, 37.950569], "pop": 16037, "state": "IN", "_id": "47620"} -{"city": "NEWBURGH", "loc": [-87.393798, 37.963746], "pop": 21793, "state": "IN", "_id": "47630"} -{"city": "NEW HARMONY", "loc": [-87.917186, 38.124502], "pop": 1432, "state": "IN", "_id": "47631"} -{"city": "POSEYVILLE", "loc": [-87.802747, 38.171986], "pop": 2009, "state": "IN", "_id": "47633"} -{"city": "RICHLAND", "loc": [-87.200957, 37.913599], "pop": 2862, "state": "IN", "_id": "47634"} -{"city": "ROCKPORT", "loc": [-87.07701, 37.885803], "pop": 4850, "state": "IN", "_id": "47635"} -{"city": "TENNYSON", "loc": [-87.13875, 38.123207], "pop": 1378, "state": "IN", "_id": "47637"} -{"city": "WADESVILLE", "loc": [-87.754295, 38.082791], "pop": 3614, "state": "IN", "_id": "47638"} -{"city": "HAUBSTADT", "loc": [-87.579827, 38.189536], "pop": 3568, "state": "IN", "_id": "47639"} -{"city": "HAZLETON", "loc": [-87.498348, 38.462283], "pop": 1214, "state": "IN", "_id": "47640"} -{"city": "BUCKSKIN", "loc": [-87.43077, 38.220867], "pop": 236, "state": "IN", "_id": "47647"} -{"city": "FORT BRANCH", "loc": [-87.568233, 38.247136], "pop": 4031, "state": "IN", "_id": "47648"} -{"city": "FRANCISCO", "loc": [-87.453621, 38.332953], "pop": 1503, "state": "IN", "_id": "47649"} -{"city": "OAKLAND CITY", "loc": [-87.351907, 38.336053], "pop": 4244, "state": "IN", "_id": "47660"} -{"city": "OWENSVILLE", "loc": [-87.709052, 38.274414], "pop": 3159, "state": "IN", "_id": "47665"} -{"city": "PATOKA", "loc": [-87.595789, 38.414296], "pop": 1434, "state": "IN", "_id": "47666"} -{"city": "PRINCETON", "loc": [-87.569069, 38.352502], "pop": 11579, "state": "IN", "_id": "47670"} -{"city": "EVANSVILLE", "loc": [-87.571973, 37.971818], "pop": 857, "state": "IN", "_id": "47708"} -{"city": "EVANSVILLE", "loc": [-87.574569, 38.008617], "pop": 22336, "state": "IN", "_id": "47710"} -{"city": "EVANSVILLE", "loc": [-87.535236, 38.076377], "pop": 9387, "state": "IN", "_id": "47711"} -{"city": "EVANSVILLE", "loc": [-87.634682, 37.998484], "pop": 35603, "state": "IN", "_id": "47712"} -{"city": "EVANSVILLE", "loc": [-87.55768, 37.962326], "pop": 14121, "state": "IN", "_id": "47713"} -{"city": "EVANSVILLE", "loc": [-87.529302, 37.959076], "pop": 27381, "state": "IN", "_id": "47714"} -{"city": "EVANSVILLE", "loc": [-87.485526, 37.967815], "pop": 30611, "state": "IN", "_id": "47715"} -{"city": "EVANSVILLE", "loc": [-87.538793, 37.998832], "pop": 25504, "state": "IN", "_id": "47720"} -{"city": "TERRE HAUTE", "loc": [-87.402019, 39.40697], "pop": 29656, "state": "IN", "_id": "47802"} -{"city": "TERRE HAUTE", "loc": [-87.353967, 39.465696], "pop": 17709, "state": "IN", "_id": "47803"} -{"city": "TERRE HAUTE", "loc": [-87.394494, 39.493665], "pop": 11573, "state": "IN", "_id": "47804"} -{"city": "NORTH TERRE HAUT", "loc": [-87.341109, 39.535981], "pop": 13652, "state": "IN", "_id": "47805"} -{"city": "TERRE HAUTE", "loc": [-87.400859, 39.470974], "pop": 19106, "state": "IN", "_id": "47807"} -{"city": "BLOOMINGDALE", "loc": [-87.255967, 39.834808], "pop": 770, "state": "IN", "_id": "47832"} -{"city": "BOWLING GREEN", "loc": [-87.005249, 39.381209], "pop": 734, "state": "IN", "_id": "47833"} -{"city": "BRAZIL", "loc": [-87.127767, 39.521041], "pop": 18960, "state": "IN", "_id": "47834"} -{"city": "BRIDGETON", "loc": [-87.181396, 39.648366], "pop": 818, "state": "IN", "_id": "47836"} -{"city": "CARBON", "loc": [-87.064455, 39.57463], "pop": 790, "state": "IN", "_id": "47837"} -{"city": "CARLISLE", "loc": [-87.366738, 38.976297], "pop": 2324, "state": "IN", "_id": "47838"} -{"city": "CENTERPOINT", "loc": [-87.09347, 39.390989], "pop": 955, "state": "IN", "_id": "47840"} -{"city": "CLAY CITY", "loc": [-87.108244, 39.2727], "pop": 2275, "state": "IN", "_id": "47841"} -{"city": "CLINTON", "loc": [-87.4208, 39.659142], "pop": 9250, "state": "IN", "_id": "47842"} -{"city": "CORY", "loc": [-87.195644, 39.343545], "pop": 259, "state": "IN", "_id": "47846"} -{"city": "DANA", "loc": [-87.501018, 39.752829], "pop": 1833, "state": "IN", "_id": "47847"} -{"city": "DUGGER", "loc": [-87.259867, 39.069057], "pop": 1144, "state": "IN", "_id": "47848"} -{"city": "FAIRBANKS", "loc": [-87.518483, 39.212865], "pop": 655, "state": "IN", "_id": "47849"} -{"city": "FARMERSBURG", "loc": [-87.502365, 39.268456], "pop": 335, "state": "IN", "_id": "47850"} -{"city": "HILLSDALE", "loc": [-87.404102, 39.771235], "pop": 858, "state": "IN", "_id": "47854"} -{"city": "LEWIS", "loc": [-87.263681, 39.273261], "pop": 308, "state": "IN", "_id": "47858"} -{"city": "MARSHALL", "loc": [-87.178032, 39.906216], "pop": 617, "state": "IN", "_id": "47859"} -{"city": "MEROM", "loc": [-87.539675, 39.032307], "pop": 649, "state": "IN", "_id": "47861"} -{"city": "MONTEZUMA", "loc": [-87.360679, 39.796061], "pop": 1444, "state": "IN", "_id": "47862"} -{"city": "PIMENTO", "loc": [-87.379186, 39.289792], "pop": 1123, "state": "IN", "_id": "47866"} -{"city": "POLAND", "loc": [-86.963103, 39.446195], "pop": 292, "state": "IN", "_id": "47868"} -{"city": "ROCKVILLE", "loc": [-87.197794, 39.768152], "pop": 7895, "state": "IN", "_id": "47872"} -{"city": "ROSEDALE", "loc": [-87.308592, 39.62391], "pop": 3463, "state": "IN", "_id": "47874"} -{"city": "SHELBURN", "loc": [-87.361194, 39.209924], "pop": 5300, "state": "IN", "_id": "47879"} -{"city": "SULLIVAN", "loc": [-87.410235, 39.101018], "pop": 8921, "state": "IN", "_id": "47882"} -{"city": "SANDFORD", "loc": [-87.467086, 39.49384], "pop": 11459, "state": "IN", "_id": "47885"} -{"city": "LAFAYETTE", "loc": [-86.888358, 40.417743], "pop": 2971, "state": "IN", "_id": "47901"} -{"city": "LAFAYETTE", "loc": [-86.873464, 40.427649], "pop": 15231, "state": "IN", "_id": "47904"} -{"city": "LAFAYETTE", "loc": [-86.860236, 40.400054], "pop": 53104, "state": "IN", "_id": "47905"} -{"city": "WEST LAFAYETTE", "loc": [-86.923661, 40.444025], "pop": 54702, "state": "IN", "_id": "47906"} -{"city": "AMBIA", "loc": [-87.479554, 40.479328], "pop": 794, "state": "IN", "_id": "47917"} -{"city": "ATTICA", "loc": [-87.224108, 40.281127], "pop": 5523, "state": "IN", "_id": "47918"} -{"city": "BATTLE GROUND", "loc": [-86.823784, 40.5248], "pop": 1646, "state": "IN", "_id": "47920"} -{"city": "BOSWELL", "loc": [-87.378947, 40.518021], "pop": 1118, "state": "IN", "_id": "47921"} -{"city": "BROOK", "loc": [-87.352685, 40.865534], "pop": 1341, "state": "IN", "_id": "47922"} -{"city": "BROOKSTON", "loc": [-86.875311, 40.601088], "pop": 3200, "state": "IN", "_id": "47923"} -{"city": "BURNETTSVILLE", "loc": [-86.574744, 40.707753], "pop": 2102, "state": "IN", "_id": "47926"} -{"city": "CAYUGA", "loc": [-87.459019, 39.930028], "pop": 3160, "state": "IN", "_id": "47928"} -{"city": "CHALMERS", "loc": [-86.862621, 40.669265], "pop": 809, "state": "IN", "_id": "47929"} -{"city": "CLARKS HILL", "loc": [-86.760766, 40.262805], "pop": 2119, "state": "IN", "_id": "47930"} -{"city": "COVINGTON", "loc": [-87.381876, 40.132618], "pop": 4711, "state": "IN", "_id": "47932"} -{"city": "CRAWFORDSVILLE", "loc": [-86.907424, 40.032524], "pop": 22838, "state": "IN", "_id": "47933"} -{"city": "DARLINGTON", "loc": [-86.764505, 40.111778], "pop": 1941, "state": "IN", "_id": "47940"} -{"city": "EARL PARK", "loc": [-87.423199, 40.694465], "pop": 903, "state": "IN", "_id": "47942"} -{"city": "FAIR OAKS", "loc": [-87.197031, 41.072862], "pop": 1254, "state": "IN", "_id": "47943"} -{"city": "FOWLER", "loc": [-87.309019, 40.625511], "pop": 3762, "state": "IN", "_id": "47944"} -{"city": "FRANCESVILLE", "loc": [-86.85534, 40.97094], "pop": 1808, "state": "IN", "_id": "47946"} -{"city": "GOODLAND", "loc": [-87.299858, 40.766872], "pop": 1341, "state": "IN", "_id": "47948"} -{"city": "HILLSBORO", "loc": [-87.154509, 40.083452], "pop": 2318, "state": "IN", "_id": "47949"} -{"city": "IDAVILLE", "loc": [-86.655647, 40.767372], "pop": 673, "state": "IN", "_id": "47950"} -{"city": "KENTLAND", "loc": [-87.447071, 40.787678], "pop": 2609, "state": "IN", "_id": "47951"} -{"city": "CATES", "loc": [-87.303548, 39.989383], "pop": 2183, "state": "IN", "_id": "47952"} -{"city": "LADOGA", "loc": [-86.803098, 39.91325], "pop": 2405, "state": "IN", "_id": "47954"} -{"city": "LINDEN", "loc": [-86.890549, 40.183316], "pop": 1274, "state": "IN", "_id": "47955"} -{"city": "MEDARYVILLE", "loc": [-86.880844, 41.089734], "pop": 2030, "state": "IN", "_id": "47957"} -{"city": "MONON", "loc": [-86.863537, 40.861176], "pop": 3119, "state": "IN", "_id": "47959"} -{"city": "MONTICELLO", "loc": [-86.754999, 40.762556], "pop": 13599, "state": "IN", "_id": "47960"} -{"city": "MOROCCO", "loc": [-87.41871, 40.964547], "pop": 2461, "state": "IN", "_id": "47963"} -{"city": "NEW RICHMOND", "loc": [-86.978925, 40.18118], "pop": 909, "state": "IN", "_id": "47967"} -{"city": "NEW ROSS", "loc": [-86.752768, 39.988266], "pop": 1440, "state": "IN", "_id": "47968"} -{"city": "OTTERBEIN", "loc": [-87.12249, 40.517013], "pop": 1527, "state": "IN", "_id": "47970"} -{"city": "OXFORD", "loc": [-87.252576, 40.521708], "pop": 1641, "state": "IN", "_id": "47971"} -{"city": "PERRYSVILLE", "loc": [-87.464787, 40.073697], "pop": 1672, "state": "IN", "_id": "47974"} -{"city": "PINE VILLAGE", "loc": [-87.231724, 40.432759], "pop": 1310, "state": "IN", "_id": "47975"} -{"city": "REMINGTON", "loc": [-87.15994, 40.766653], "pop": 1937, "state": "IN", "_id": "47977"} -{"city": "COLLEGEVILLE", "loc": [-87.133733, 40.947731], "pop": 9776, "state": "IN", "_id": "47978"} -{"city": "REYNOLDS", "loc": [-86.86914, 40.760608], "pop": 1148, "state": "IN", "_id": "47980"} -{"city": "ROMNEY", "loc": [-86.904412, 40.2605], "pop": 694, "state": "IN", "_id": "47981"} -{"city": "TANGIER", "loc": [-87.341551, 39.920601], "pop": 711, "state": "IN", "_id": "47985"} -{"city": "VEEDERSBURG", "loc": [-87.260167, 40.118616], "pop": 3081, "state": "IN", "_id": "47987"} -{"city": "WAVELAND", "loc": [-87.01776, 39.901996], "pop": 1548, "state": "IN", "_id": "47989"} -{"city": "WAYNETOWN", "loc": [-87.051165, 40.085788], "pop": 1529, "state": "IN", "_id": "47990"} -{"city": "WEST LEBANON", "loc": [-87.436676, 40.241491], "pop": 2957, "state": "IN", "_id": "47991"} -{"city": "WESTPOINT", "loc": [-87.057477, 40.358052], "pop": 145, "state": "IN", "_id": "47992"} -{"city": "MARSHFIELD", "loc": [-87.278689, 40.309435], "pop": 3591, "state": "IN", "_id": "47993"} -{"city": "WINGATE", "loc": [-87.066441, 40.166594], "pop": 552, "state": "IN", "_id": "47994"} -{"city": "WOLCOTT", "loc": [-87.028997, 40.751613], "pop": 1909, "state": "IN", "_id": "47995"} -{"city": "ACKWORTH", "loc": [-93.376719, 41.37372], "pop": 491, "state": "IA", "_id": "50001"} -{"city": "ADAIR", "loc": [-94.644363, 41.513687], "pop": 1748, "state": "IA", "_id": "50002"} -{"city": "ADEL", "loc": [-94.037965, 41.62214], "pop": 4884, "state": "IA", "_id": "50003"} -{"city": "ALBION", "loc": [-92.98821, 42.11427], "pop": 773, "state": "IA", "_id": "50005"} -{"city": "ALDEN", "loc": [-93.384123, 42.51789], "pop": 1374, "state": "IA", "_id": "50006"} -{"city": "ALLEMAN", "loc": [-93.606305, 41.815886], "pop": 449, "state": "IA", "_id": "50007"} -{"city": "ALLERTON", "loc": [-93.37239, 40.702301], "pop": 805, "state": "IA", "_id": "50008"} -{"city": "ALTOONA", "loc": [-93.472415, 41.647549], "pop": 7546, "state": "IA", "_id": "50009"} -{"city": "AMES", "loc": [-93.639398, 42.029859], "pop": 52105, "state": "IA", "_id": "50010"} -{"city": "ANITA", "loc": [-94.779401, 41.449973], "pop": 1529, "state": "IA", "_id": "50020"} -{"city": "ANKENY", "loc": [-93.602175, 41.72755], "pop": 20465, "state": "IA", "_id": "50021"} -{"city": "ATLANTIC", "loc": [-95.010123, 41.400279], "pop": 8468, "state": "IA", "_id": "50022"} -{"city": "AUDUBON", "loc": [-94.916412, 41.730032], "pop": 4034, "state": "IA", "_id": "50025"} -{"city": "BAGLEY", "loc": [-94.441535, 41.834053], "pop": 568, "state": "IA", "_id": "50026"} -{"city": "BARNES CITY", "loc": [-92.470338, 41.488269], "pop": 355, "state": "IA", "_id": "50027"} -{"city": "BAXTER", "loc": [-93.158332, 41.820369], "pop": 1381, "state": "IA", "_id": "50028"} -{"city": "BAYARD", "loc": [-94.591533, 41.838599], "pop": 971, "state": "IA", "_id": "50029"} -{"city": "BEACONSFIELD", "loc": [-94.075126, 40.778431], "pop": 195, "state": "IA", "_id": "50030"} -{"city": "BEAVER", "loc": [-94.103309, 42.039936], "pop": 501, "state": "IA", "_id": "50031"} -{"city": "BEVINGTON", "loc": [-93.812865, 41.371763], "pop": 273, "state": "IA", "_id": "50033"} -{"city": "BLAIRSBURG", "loc": [-93.659444, 42.493725], "pop": 458, "state": "IA", "_id": "50034"} -{"city": "BONDURANT", "loc": [-93.452689, 41.70166], "pop": 2666, "state": "IA", "_id": "50035"} -{"city": "BOONE", "loc": [-93.878083, 42.069354], "pop": 14773, "state": "IA", "_id": "50036"} -{"city": "BOONEVILLE", "loc": [-93.881357, 41.533987], "pop": 100, "state": "IA", "_id": "50038"} -{"city": "BOUTON", "loc": [-93.996286, 41.828432], "pop": 552, "state": "IA", "_id": "50039"} -{"city": "BOXHOLM", "loc": [-94.105246, 42.170567], "pop": 437, "state": "IA", "_id": "50040"} -{"city": "BRADFORD", "loc": [-93.218068, 42.610434], "pop": 406, "state": "IA", "_id": "50041"} -{"city": "BRAYTON", "loc": [-94.974017, 41.55236], "pop": 416, "state": "IA", "_id": "50042"} -{"city": "BUSSEY", "loc": [-92.896252, 41.209422], "pop": 707, "state": "IA", "_id": "50044"} -{"city": "CAMBRIDGE", "loc": [-93.530538, 41.90251], "pop": 1103, "state": "IA", "_id": "50046"} -{"city": "CARLISLE", "loc": [-93.49214, 41.481384], "pop": 5109, "state": "IA", "_id": "50047"} -{"city": "CASEY", "loc": [-94.414475, 41.496116], "pop": 1822, "state": "IA", "_id": "50048"} -{"city": "CHARITON", "loc": [-93.30419, 41.022638], "pop": 6836, "state": "IA", "_id": "50049"} -{"city": "CHURDAN", "loc": [-94.493346, 42.161402], "pop": 797, "state": "IA", "_id": "50050"} -{"city": "CLEMONS", "loc": [-93.16878, 42.090041], "pop": 557, "state": "IA", "_id": "50051"} -{"city": "CLIO", "loc": [-93.486443, 40.680261], "pop": 125, "state": "IA", "_id": "50052"} -{"city": "COLFAX", "loc": [-93.258816, 41.678785], "pop": 3475, "state": "IA", "_id": "50054"} -{"city": "COLLINS", "loc": [-93.300808, 41.903226], "pop": 764, "state": "IA", "_id": "50055"} -{"city": "COLO", "loc": [-93.307114, 42.013102], "pop": 1293, "state": "IA", "_id": "50056"} -{"city": "COLUMBIA", "loc": [-93.158217, 41.183814], "pop": 304, "state": "IA", "_id": "50057"} -{"city": "COON RAPIDS", "loc": [-94.679979, 41.893957], "pop": 1802, "state": "IA", "_id": "50058"} -{"city": "COOPER", "loc": [-94.339073, 41.903433], "pop": 170, "state": "IA", "_id": "50059"} -{"city": "SEWAL", "loc": [-93.322549, 40.76131], "pop": 2617, "state": "IA", "_id": "50060"} -{"city": "CUMMING", "loc": [-93.75386, 41.484876], "pop": 635, "state": "IA", "_id": "50061"} -{"city": "DALLAS", "loc": [-93.253525, 41.221332], "pop": 996, "state": "IA", "_id": "50062"} -{"city": "DALLAS CENTER", "loc": [-93.970657, 41.686248], "pop": 1838, "state": "IA", "_id": "50063"} -{"city": "DANA", "loc": [-94.232212, 42.10634], "pop": 131, "state": "IA", "_id": "50064"} -{"city": "PLEASANTON", "loc": [-93.807022, 40.632075], "pop": 762, "state": "IA", "_id": "50065"} -{"city": "DAWSON", "loc": [-94.217127, 41.832396], "pop": 434, "state": "IA", "_id": "50066"} -{"city": "DECATUR", "loc": [-93.832598, 40.755258], "pop": 394, "state": "IA", "_id": "50067"} -{"city": "DERBY", "loc": [-93.47959, 40.935476], "pop": 339, "state": "IA", "_id": "50068"} -{"city": "DE SOTO", "loc": [-94.027582, 41.533285], "pop": 1535, "state": "IA", "_id": "50069"} -{"city": "DEXTER", "loc": [-94.2145, 41.519806], "pop": 824, "state": "IA", "_id": "50070"} -{"city": "DOWS", "loc": [-93.505801, 42.661026], "pop": 1373, "state": "IA", "_id": "50071"} -{"city": "EARLHAM", "loc": [-94.129443, 41.470916], "pop": 2010, "state": "IA", "_id": "50072"} -{"city": "ELKHART", "loc": [-93.485458, 41.811384], "pop": 1374, "state": "IA", "_id": "50073"} -{"city": "ELLSTON", "loc": [-94.084116, 40.850485], "pop": 286, "state": "IA", "_id": "50074"} -{"city": "ELLSWORTH", "loc": [-93.548979, 42.30086], "pop": 1129, "state": "IA", "_id": "50075"} -{"city": "EXIRA", "loc": [-94.857145, 41.576771], "pop": 1590, "state": "IA", "_id": "50076"} -{"city": "GALT", "loc": [-93.603512, 42.691187], "pop": 57, "state": "IA", "_id": "50101"} -{"city": "GARDEN CITY", "loc": [-93.403068, 42.25266], "pop": 422, "state": "IA", "_id": "50102"} -{"city": "GARDEN GROVE", "loc": [-93.611669, 40.771078], "pop": 603, "state": "IA", "_id": "50103"} -{"city": "GIBSON", "loc": [-92.353121, 41.464206], "pop": 458, "state": "IA", "_id": "50104"} -{"city": "GILMAN", "loc": [-92.810067, 41.901236], "pop": 1221, "state": "IA", "_id": "50106"} -{"city": "GRAND JUNCTION", "loc": [-94.233629, 42.029578], "pop": 1055, "state": "IA", "_id": "50107"} -{"city": "GRAND RIVER", "loc": [-93.954372, 40.813704], "pop": 454, "state": "IA", "_id": "50108"} -{"city": "GRANGER", "loc": [-93.841002, 41.752355], "pop": 1324, "state": "IA", "_id": "50109"} -{"city": "GRAY", "loc": [-95.02272, 41.82663], "pop": 344, "state": "IA", "_id": "50110"} -{"city": "GRIMES", "loc": [-93.782109, 41.701838], "pop": 4205, "state": "IA", "_id": "50111"} -{"city": "GRINNELL", "loc": [-92.734432, 41.742081], "pop": 11530, "state": "IA", "_id": "50112"} -{"city": "GUTHRIE CENTER", "loc": [-94.486444, 41.683671], "pop": 3398, "state": "IA", "_id": "50115"} -{"city": "HAMILTON", "loc": [-92.924349, 41.175543], "pop": 314, "state": "IA", "_id": "50116"} -{"city": "HAMLIN", "loc": [-94.915745, 41.650571], "pop": 336, "state": "IA", "_id": "50117"} -{"city": "HARTFORD", "loc": [-93.402213, 41.459629], "pop": 1201, "state": "IA", "_id": "50118"} -{"city": "HARVEY", "loc": [-92.931843, 41.302972], "pop": 683, "state": "IA", "_id": "50119"} -{"city": "HAVERHILL", "loc": [-92.945857, 41.93196], "pop": 419, "state": "IA", "_id": "50120"} -{"city": "HUBBARD", "loc": [-93.293928, 42.302208], "pop": 1356, "state": "IA", "_id": "50122"} -{"city": "HUMESTON", "loc": [-93.510117, 40.846922], "pop": 981, "state": "IA", "_id": "50123"} -{"city": "HUXLEY", "loc": [-93.602381, 41.899434], "pop": 2417, "state": "IA", "_id": "50124"} -{"city": "SPRING HILL", "loc": [-93.571502, 41.35617], "pop": 13920, "state": "IA", "_id": "50125"} -{"city": "IOWA FALLS", "loc": [-93.270943, 42.513756], "pop": 7714, "state": "IA", "_id": "50126"} -{"city": "IRA", "loc": [-93.164436, 41.731759], "pop": 378, "state": "IA", "_id": "50127"} -{"city": "JAMAICA", "loc": [-94.320367, 41.841931], "pop": 423, "state": "IA", "_id": "50128"} -{"city": "JEFFERSON", "loc": [-94.388768, 42.009325], "pop": 5663, "state": "IA", "_id": "50129"} -{"city": "JEWELL", "loc": [-93.642821, 42.313912], "pop": 1348, "state": "IA", "_id": "50130"} -{"city": "JOHNSTON", "loc": [-93.702792, 41.673017], "pop": 4945, "state": "IA", "_id": "50131"} -{"city": "KAMRAR", "loc": [-93.727941, 42.394517], "pop": 246, "state": "IA", "_id": "50132"} -{"city": "KELLERTON", "loc": [-94.060693, 40.686127], "pop": 601, "state": "IA", "_id": "50133"} -{"city": "KELLEY", "loc": [-93.664354, 41.948773], "pop": 314, "state": "IA", "_id": "50134"} -{"city": "KELLOGG", "loc": [-92.911404, 41.718371], "pop": 913, "state": "IA", "_id": "50135"} -{"city": "KESWICK", "loc": [-92.239547, 41.461251], "pop": 497, "state": "IA", "_id": "50136"} -{"city": "KNOXVILLE", "loc": [-93.095427, 41.316446], "pop": 11205, "state": "IA", "_id": "50138"} -{"city": "LACONA", "loc": [-93.383801, 41.198832], "pop": 700, "state": "IA", "_id": "50139"} -{"city": "LAMONI", "loc": [-93.934899, 40.622852], "pop": 2638, "state": "IA", "_id": "50140"} -{"city": "LAUREL", "loc": [-92.926294, 41.882904], "pop": 457, "state": "IA", "_id": "50141"} -{"city": "LEIGHTON", "loc": [-92.808125, 41.364677], "pop": 594, "state": "IA", "_id": "50143"} -{"city": "LEON", "loc": [-93.742903, 40.737334], "pop": 2556, "state": "IA", "_id": "50144"} -{"city": "LIBERTY CENTER", "loc": [-93.497391, 41.207668], "pop": 468, "state": "IA", "_id": "50145"} -{"city": "LINDEN", "loc": [-94.24058, 41.64225], "pop": 585, "state": "IA", "_id": "50146"} -{"city": "LINEVILLE", "loc": [-93.486207, 40.600373], "pop": 586, "state": "IA", "_id": "50147"} -{"city": "LISCOMB", "loc": [-92.983279, 42.184], "pop": 512, "state": "IA", "_id": "50148"} -{"city": "LORIMOR", "loc": [-94.063885, 41.1233], "pop": 619, "state": "IA", "_id": "50149"} -{"city": "LOVILIA", "loc": [-92.929272, 41.12719], "pop": 1053, "state": "IA", "_id": "50150"} -{"city": "LUCAS", "loc": [-93.483614, 41.058451], "pop": 617, "state": "IA", "_id": "50151"} -{"city": "LUTHER", "loc": [-93.855394, 41.991934], "pop": 529, "state": "IA", "_id": "50152"} -{"city": "LYNNVILLE", "loc": [-92.787715, 41.569766], "pop": 630, "state": "IA", "_id": "50153"} -{"city": "MC CALLSBURG", "loc": [-93.397757, 42.166475], "pop": 533, "state": "IA", "_id": "50154"} -{"city": "MACKSBURG", "loc": [-94.185673, 41.202792], "pop": 310, "state": "IA", "_id": "50155"} -{"city": "MADRID", "loc": [-93.844815, 41.884083], "pop": 4367, "state": "IA", "_id": "50156"} -{"city": "MALCOM", "loc": [-92.535426, 41.769502], "pop": 1595, "state": "IA", "_id": "50157"} -{"city": "MARSHALLTOWN", "loc": [-92.91269, 42.040482], "pop": 30857, "state": "IA", "_id": "50158"} -{"city": "MAXWELL", "loc": [-93.400125, 41.89673], "pop": 1268, "state": "IA", "_id": "50161"} -{"city": "MELBOURNE", "loc": [-93.085627, 41.932348], "pop": 970, "state": "IA", "_id": "50162"} -{"city": "MELCHER-DALLAS", "loc": [-93.219404, 41.222912], "pop": 924, "state": "IA", "_id": "50163"} -{"city": "MENLO", "loc": [-94.409002, 41.534797], "pop": 650, "state": "IA", "_id": "50164"} -{"city": "MILLERTON", "loc": [-93.279586, 40.854889], "pop": 258, "state": "IA", "_id": "50165"} -{"city": "MILO", "loc": [-93.445356, 41.29042], "pop": 1705, "state": "IA", "_id": "50166"} -{"city": "MINBURN", "loc": [-94.01501, 41.756206], "pop": 517, "state": "IA", "_id": "50167"} -{"city": "MINGO", "loc": [-93.288015, 41.789843], "pop": 725, "state": "IA", "_id": "50168"} -{"city": "MITCHELLVILLE", "loc": [-93.36995, 41.66089], "pop": 2363, "state": "IA", "_id": "50169"} -{"city": "MONROE", "loc": [-93.072551, 41.535619], "pop": 2868, "state": "IA", "_id": "50170"} -{"city": "MONTEZUMA", "loc": [-92.527582, 41.592845], "pop": 3570, "state": "IA", "_id": "50171"} -{"city": "GUERNSEY", "loc": [-92.333549, 41.662496], "pop": 128, "state": "IA", "_id": "50172"} -{"city": "MONTOUR", "loc": [-92.700575, 41.970876], "pop": 1137, "state": "IA", "_id": "50173"} -{"city": "MURRAY", "loc": [-93.952975, 41.037644], "pop": 1357, "state": "IA", "_id": "50174"} -{"city": "NEVADA", "loc": [-93.446219, 42.018983], "pop": 7381, "state": "IA", "_id": "50201"} -{"city": "NEW PROVIDENCE", "loc": [-93.175262, 42.269261], "pop": 445, "state": "IA", "_id": "50206"} -{"city": "NEW SHARON", "loc": [-92.684815, 41.463976], "pop": 2426, "state": "IA", "_id": "50207"} -{"city": "NEWTON", "loc": [-93.045456, 41.699173], "pop": 19721, "state": "IA", "_id": "50208"} -{"city": "NEW VIRGINIA", "loc": [-93.711312, 41.160063], "pop": 1593, "state": "IA", "_id": "50210"} -{"city": "NORWALK", "loc": [-93.657332, 41.486055], "pop": 9512, "state": "IA", "_id": "50211"} -{"city": "OGDEN", "loc": [-94.006333, 42.035106], "pop": 3115, "state": "IA", "_id": "50212"} -{"city": "OSCEOLA", "loc": [-93.771236, 41.029503], "pop": 5528, "state": "IA", "_id": "50213"} -{"city": "OTLEY", "loc": [-93.034778, 41.451806], "pop": 676, "state": "IA", "_id": "50214"} -{"city": "PANORA", "loc": [-94.360613, 41.696684], "pop": 1847, "state": "IA", "_id": "50216"} -{"city": "PATON", "loc": [-94.273038, 42.165841], "pop": 621, "state": "IA", "_id": "50217"} -{"city": "PATTERSON", "loc": [-93.878174, 41.353912], "pop": 258, "state": "IA", "_id": "50218"} -{"city": "PELLA", "loc": [-92.917207, 41.408175], "pop": 10771, "state": "IA", "_id": "50219"} -{"city": "PERRY", "loc": [-94.102167, 41.839695], "pop": 7918, "state": "IA", "_id": "50220"} -{"city": "PERU", "loc": [-93.943729, 41.21454], "pop": 442, "state": "IA", "_id": "50222"} -{"city": "PILOT MOUND", "loc": [-94.010184, 42.160395], "pop": 380, "state": "IA", "_id": "50223"} -{"city": "PLEASANTVILLE", "loc": [-93.271206, 41.373836], "pop": 2474, "state": "IA", "_id": "50225"} -{"city": "POLK CITY", "loc": [-93.689346, 41.754905], "pop": 4203, "state": "IA", "_id": "50226"} -{"city": "POPEJOY", "loc": [-93.430867, 42.595628], "pop": 298, "state": "IA", "_id": "50227"} -{"city": "PRAIRIE CITY", "loc": [-93.240986, 41.585425], "pop": 1941, "state": "IA", "_id": "50228"} -{"city": "PROLE", "loc": [-93.734357, 41.382973], "pop": 1488, "state": "IA", "_id": "50229"} -{"city": "RADCLIFFE", "loc": [-93.423364, 42.324064], "pop": 804, "state": "IA", "_id": "50230"} -{"city": "RANDALL", "loc": [-93.62109, 42.247463], "pop": 508, "state": "IA", "_id": "50231"} -{"city": "REASNOR", "loc": [-93.019827, 41.579802], "pop": 245, "state": "IA", "_id": "50232"} -{"city": "REDFIELD", "loc": [-94.189733, 41.587349], "pop": 1236, "state": "IA", "_id": "50233"} -{"city": "RHODES", "loc": [-93.179586, 41.917717], "pop": 564, "state": "IA", "_id": "50234"} -{"city": "RIPPEY", "loc": [-94.213202, 41.92574], "pop": 563, "state": "IA", "_id": "50235"} -{"city": "ROLAND", "loc": [-93.506313, 42.164585], "pop": 1452, "state": "IA", "_id": "50236"} -{"city": "RUNNELLS", "loc": [-93.42025, 41.551549], "pop": 2309, "state": "IA", "_id": "50237"} -{"city": "RUSSELL", "loc": [-93.178371, 40.945557], "pop": 1018, "state": "IA", "_id": "50238"} -{"city": "SAINT ANTHONY", "loc": [-93.182431, 42.151974], "pop": 328, "state": "IA", "_id": "50239"} -{"city": "SAINT CHARLES", "loc": [-93.824195, 41.292443], "pop": 1169, "state": "IA", "_id": "50240"} -{"city": "SAINT MARYS", "loc": [-93.736107, 41.297826], "pop": 632, "state": "IA", "_id": "50241"} -{"city": "SEARSBORO", "loc": [-92.698737, 41.56561], "pop": 464, "state": "IA", "_id": "50242"} -{"city": "SLATER", "loc": [-93.681155, 41.876511], "pop": 2083, "state": "IA", "_id": "50244"} -{"city": "STANHOPE", "loc": [-93.775081, 42.290407], "pop": 1001, "state": "IA", "_id": "50246"} -{"city": "STATE CENTER", "loc": [-93.167283, 42.010911], "pop": 1618, "state": "IA", "_id": "50247"} -{"city": "STORY CITY", "loc": [-93.598811, 42.183487], "pop": 3501, "state": "IA", "_id": "50248"} -{"city": "STRATFORD", "loc": [-93.90294, 42.284673], "pop": 1215, "state": "IA", "_id": "50249"} -{"city": "STUART", "loc": [-94.304563, 41.518643], "pop": 1463, "state": "IA", "_id": "50250"} -{"city": "SULLY", "loc": [-92.845085, 41.573969], "pop": 1096, "state": "IA", "_id": "50251"} -{"city": "SWAN", "loc": [-93.234165, 41.4717], "pop": 691, "state": "IA", "_id": "50252"} -{"city": "THAYER", "loc": [-94.068639, 41.00293], "pop": 453, "state": "IA", "_id": "50254"} -{"city": "TRACY", "loc": [-92.875337, 41.275564], "pop": 256, "state": "IA", "_id": "50256"} -{"city": "TRURO", "loc": [-93.840978, 41.207622], "pop": 767, "state": "IA", "_id": "50257"} -{"city": "UNION", "loc": [-93.056192, 42.252074], "pop": 937, "state": "IA", "_id": "50258"} -{"city": "VAN METER", "loc": [-93.95303, 41.539415], "pop": 1780, "state": "IA", "_id": "50261"} -{"city": "VAN WERT", "loc": [-93.80733, 40.864451], "pop": 431, "state": "IA", "_id": "50262"} -{"city": "WAUKEE", "loc": [-93.85916, 41.592959], "pop": 4933, "state": "IA", "_id": "50263"} -{"city": "WELDON", "loc": [-93.738743, 40.873906], "pop": 388, "state": "IA", "_id": "50264"} -{"city": "WEST DES MOINES", "loc": [-93.744711, 41.580512], "pop": 31896, "state": "IA", "_id": "50265"} -{"city": "WHAT CHEER", "loc": [-92.354789, 41.395276], "pop": 1108, "state": "IA", "_id": "50268"} -{"city": "WILLIAMS", "loc": [-93.56774, 42.461376], "pop": 1036, "state": "IA", "_id": "50271"} -{"city": "WILLIAMSON", "loc": [-93.26593, 41.102026], "pop": 505, "state": "IA", "_id": "50272"} -{"city": "WINTERSET", "loc": [-94.008763, 41.339077], "pop": 7254, "state": "IA", "_id": "50273"} -{"city": "WIOTA", "loc": [-94.840515, 41.384552], "pop": 596, "state": "IA", "_id": "50274"} -{"city": "WOODBURN", "loc": [-93.608282, 41.00001], "pop": 677, "state": "IA", "_id": "50275"} -{"city": "WOODWARD", "loc": [-93.905617, 41.845219], "pop": 1749, "state": "IA", "_id": "50276"} -{"city": "YALE", "loc": [-94.35026, 41.775309], "pop": 414, "state": "IA", "_id": "50277"} -{"city": "ZEARING", "loc": [-93.292766, 42.15349], "pop": 1032, "state": "IA", "_id": "50278"} -{"city": "DES MOINES", "loc": [-93.621175, 41.588743], "pop": 4879, "state": "IA", "_id": "50309"} -{"city": "DES MOINES", "loc": [-93.673611, 41.625475], "pop": 30498, "state": "IA", "_id": "50310"} -{"city": "WINDSOR HEIGHTS", "loc": [-93.674371, 41.601562], "pop": 19816, "state": "IA", "_id": "50311"} -{"city": "DES MOINES", "loc": [-93.671908, 41.585453], "pop": 16424, "state": "IA", "_id": "50312"} -{"city": "DES MOINES", "loc": [-93.620305, 41.638085], "pop": 16175, "state": "IA", "_id": "50313"} -{"city": "DES MOINES", "loc": [-93.632993, 41.603003], "pop": 11709, "state": "IA", "_id": "50314"} -{"city": "DES MOINES", "loc": [-93.619226, 41.544394], "pop": 36434, "state": "IA", "_id": "50315"} -{"city": "DES MOINES", "loc": [-93.599966, 41.609228], "pop": 16359, "state": "IA", "_id": "50316"} -{"city": "PLEASANT HILL", "loc": [-93.549446, 41.612499], "pop": 39883, "state": "IA", "_id": "50317"} -{"city": "DES MOINES", "loc": [-93.582674, 41.548693], "pop": 8444, "state": "IA", "_id": "50320"} -{"city": "DES MOINES", "loc": [-93.661846, 41.547628], "pop": 7233, "state": "IA", "_id": "50321"} -{"city": "URBANDALE", "loc": [-93.723042, 41.629479], "pop": 28512, "state": "IA", "_id": "50322"} -{"city": "CLIVE", "loc": [-93.745749, 41.606729], "pop": 7293, "state": "IA", "_id": "50325"} -{"city": "MASON CITY", "loc": [-93.195379, 43.149869], "pop": 31168, "state": "IA", "_id": "50401"} -{"city": "ALEXANDER", "loc": [-93.445, 42.811395], "pop": 664, "state": "IA", "_id": "50420"} -{"city": "BELMOND", "loc": [-93.620012, 42.851137], "pop": 3306, "state": "IA", "_id": "50421"} -{"city": "BRITT", "loc": [-93.775175, 43.100582], "pop": 3988, "state": "IA", "_id": "50423"} -{"city": "BUFFALO CENTER", "loc": [-93.937557, 43.373204], "pop": 1566, "state": "IA", "_id": "50424"} -{"city": "CLEAR LAKE", "loc": [-93.384978, 43.140607], "pop": 10056, "state": "IA", "_id": "50428"} -{"city": "CORWITH", "loc": [-93.932303, 42.992736], "pop": 737, "state": "IA", "_id": "50430"} -{"city": "CRYSTAL LAKE", "loc": [-93.794166, 43.218692], "pop": 543, "state": "IA", "_id": "50432"} -{"city": "DOUGHERTY", "loc": [-93.068586, 42.941633], "pop": 308, "state": "IA", "_id": "50433"} -{"city": "FERTILE", "loc": [-93.434485, 43.279514], "pop": 766, "state": "IA", "_id": "50434"} -{"city": "FLOYD", "loc": [-92.723988, 43.143049], "pop": 1273, "state": "IA", "_id": "50435"} -{"city": "FOREST CITY", "loc": [-93.635595, 43.269184], "pop": 6010, "state": "IA", "_id": "50436"} -{"city": "GARNER", "loc": [-93.594394, 43.114395], "pop": 3756, "state": "IA", "_id": "50438"} -{"city": "GOODELL", "loc": [-93.582426, 42.938298], "pop": 452, "state": "IA", "_id": "50439"} -{"city": "GRAFTON", "loc": [-93.079718, 43.311016], "pop": 630, "state": "IA", "_id": "50440"} -{"city": "HAMPTON", "loc": [-93.210954, 42.740531], "pop": 5313, "state": "IA", "_id": "50441"} -{"city": "HANLONTOWN", "loc": [-93.335065, 43.307234], "pop": 605, "state": "IA", "_id": "50444"} -{"city": "JOICE", "loc": [-93.445354, 43.376105], "pop": 522, "state": "IA", "_id": "50446"} -{"city": "KANAWHA", "loc": [-93.772801, 42.94338], "pop": 1181, "state": "IA", "_id": "50447"} -{"city": "KENSETT", "loc": [-93.172805, 43.37391], "pop": 729, "state": "IA", "_id": "50448"} -{"city": "KLEMME", "loc": [-93.587944, 43.013643], "pop": 849, "state": "IA", "_id": "50449"} -{"city": "LAKE MILLS", "loc": [-93.533628, 43.416696], "pop": 2801, "state": "IA", "_id": "50450"} -{"city": "LAKOTA", "loc": [-94.07055, 43.395283], "pop": 789, "state": "IA", "_id": "50451"} -{"city": "LATIMER", "loc": [-93.351798, 42.759783], "pop": 966, "state": "IA", "_id": "50452"} -{"city": "LELAND", "loc": [-93.751496, 43.307043], "pop": 333, "state": "IA", "_id": "50453"} -{"city": "LITTLE CEDAR", "loc": [-92.734376, 43.391861], "pop": 248, "state": "IA", "_id": "50454"} -{"city": "MC INTIRE", "loc": [-92.60485, 43.44999], "pop": 382, "state": "IA", "_id": "50455"} -{"city": "MANLY", "loc": [-93.201802, 43.289116], "pop": 1757, "state": "IA", "_id": "50456"} -{"city": "MESERVEY", "loc": [-93.477298, 42.913696], "pop": 298, "state": "IA", "_id": "50457"} -{"city": "NORA SPRINGS", "loc": [-93.000221, 43.147858], "pop": 1849, "state": "IA", "_id": "50458"} -{"city": "NORTHWOOD", "loc": [-93.241943, 43.450604], "pop": 2982, "state": "IA", "_id": "50459"} -{"city": "ORCHARD", "loc": [-92.719979, 43.235459], "pop": 442, "state": "IA", "_id": "50460"} -{"city": "OSAGE", "loc": [-92.814356, 43.28722], "pop": 5790, "state": "IA", "_id": "50461"} -{"city": "PLYMOUTH", "loc": [-93.122628, 43.246046], "pop": 453, "state": "IA", "_id": "50464"} -{"city": "RAKE", "loc": [-93.917128, 43.476655], "pop": 435, "state": "IA", "_id": "50465"} -{"city": "RICEVILLE", "loc": [-92.555372, 43.371899], "pop": 1546, "state": "IA", "_id": "50466"} -{"city": "ROCK FALLS", "loc": [-93.076402, 43.198014], "pop": 306, "state": "IA", "_id": "50467"} -{"city": "ROCKFORD", "loc": [-92.952034, 43.057331], "pop": 1258, "state": "IA", "_id": "50468"} -{"city": "ROCKWELL", "loc": [-93.216737, 43.003167], "pop": 2009, "state": "IA", "_id": "50469"} -{"city": "ROWAN", "loc": [-93.556966, 42.759644], "pop": 405, "state": "IA", "_id": "50470"} -{"city": "RUDD", "loc": [-92.888546, 43.141958], "pop": 688, "state": "IA", "_id": "50471"} -{"city": "SAINT ANSGAR", "loc": [-92.923523, 43.406107], "pop": 2401, "state": "IA", "_id": "50472"} -{"city": "SCARVILLE", "loc": [-93.64233, 43.467286], "pop": 420, "state": "IA", "_id": "50473"} -{"city": "SHEFFIELD", "loc": [-93.215583, 42.877881], "pop": 1988, "state": "IA", "_id": "50475"} -{"city": "STACYVILLE", "loc": [-92.761031, 43.445687], "pop": 798, "state": "IA", "_id": "50476"} -{"city": "SWALEDALE", "loc": [-93.311529, 42.962167], "pop": 414, "state": "IA", "_id": "50477"} -{"city": "THOMPSON", "loc": [-93.751671, 43.387497], "pop": 1178, "state": "IA", "_id": "50478"} -{"city": "THORNTON", "loc": [-93.408787, 42.968405], "pop": 905, "state": "IA", "_id": "50479"} -{"city": "TITONKA", "loc": [-94.036656, 43.245606], "pop": 1159, "state": "IA", "_id": "50480"} -{"city": "VENTURA", "loc": [-93.4706, 43.126178], "pop": 816, "state": "IA", "_id": "50482"} -{"city": "WESLEY", "loc": [-94.003776, 43.09765], "pop": 647, "state": "IA", "_id": "50483"} -{"city": "WODEN", "loc": [-93.910925, 43.222564], "pop": 511, "state": "IA", "_id": "50484"} -{"city": "FORT DODGE", "loc": [-94.180737, 42.508817], "pop": 27632, "state": "IA", "_id": "50501"} -{"city": "ALBERT CITY", "loc": [-94.98238, 42.778403], "pop": 1293, "state": "IA", "_id": "50510"} -{"city": "ALGONA", "loc": [-94.230638, 43.065976], "pop": 7829, "state": "IA", "_id": "50511"} -{"city": "ARMSTRONG", "loc": [-94.485305, 43.402423], "pop": 1464, "state": "IA", "_id": "50514"} -{"city": "AYRSHIRE", "loc": [-94.847804, 43.037001], "pop": 413, "state": "IA", "_id": "50515"} -{"city": "BADGER", "loc": [-94.162988, 42.599595], "pop": 1270, "state": "IA", "_id": "50516"} -{"city": "BANCROFT", "loc": [-94.210778, 43.293485], "pop": 1346, "state": "IA", "_id": "50517"} -{"city": "BARNUM", "loc": [-94.370327, 42.515867], "pop": 481, "state": "IA", "_id": "50518"} -{"city": "BODE", "loc": [-94.278055, 42.866078], "pop": 555, "state": "IA", "_id": "50519"} -{"city": "BRADGATE", "loc": [-94.400239, 42.779425], "pop": 359, "state": "IA", "_id": "50520"} -{"city": "BURNSIDE", "loc": [-94.114253, 42.341318], "pop": 355, "state": "IA", "_id": "50521"} -{"city": "BURT", "loc": [-94.21223, 43.206167], "pop": 1069, "state": "IA", "_id": "50522"} -{"city": "CALLENDER", "loc": [-94.281963, 42.349372], "pop": 1009, "state": "IA", "_id": "50523"} -{"city": "CLARE", "loc": [-94.308028, 42.595397], "pop": 821, "state": "IA", "_id": "50524"} -{"city": "CLARION", "loc": [-93.727715, 42.727149], "pop": 3849, "state": "IA", "_id": "50525"} -{"city": "CURLEW", "loc": [-94.797528, 42.963321], "pop": 278, "state": "IA", "_id": "50527"} -{"city": "CYLINDER", "loc": [-94.510672, 43.150716], "pop": 536, "state": "IA", "_id": "50528"} -{"city": "DAYTON", "loc": [-94.06105, 42.259438], "pop": 1366, "state": "IA", "_id": "50530"} -{"city": "DOLLIVER", "loc": [-94.624236, 43.466382], "pop": 260, "state": "IA", "_id": "50531"} -{"city": "DUNCOMBE", "loc": [-94.059212, 42.449303], "pop": 1561, "state": "IA", "_id": "50532"} -{"city": "EAGLE GROVE", "loc": [-93.904872, 42.660867], "pop": 4109, "state": "IA", "_id": "50533"} -{"city": "EARLY", "loc": [-95.172569, 42.448258], "pop": 1195, "state": "IA", "_id": "50535"} -{"city": "EMMETSBURG", "loc": [-94.682545, 43.108249], "pop": 4794, "state": "IA", "_id": "50536"} -{"city": "FARNHAMVILLE", "loc": [-94.422623, 42.269234], "pop": 631, "state": "IA", "_id": "50538"} -{"city": "FENTON", "loc": [-94.404366, 43.242047], "pop": 779, "state": "IA", "_id": "50539"} -{"city": "FONDA", "loc": [-94.82974, 42.605148], "pop": 1438, "state": "IA", "_id": "50540"} -{"city": "GILMORE CITY", "loc": [-94.410824, 42.706691], "pop": 604, "state": "IA", "_id": "50541"} -{"city": "GOLDFIELD", "loc": [-93.919435, 42.758055], "pop": 1148, "state": "IA", "_id": "50542"} -{"city": "GOWRIE", "loc": [-94.298669, 42.276712], "pop": 1225, "state": "IA", "_id": "50543"} -{"city": "HARCOURT", "loc": [-94.196095, 42.253052], "pop": 631, "state": "IA", "_id": "50544"} -{"city": "HARDY", "loc": [-94.030852, 42.782247], "pop": 262, "state": "IA", "_id": "50545"} -{"city": "HAVELOCK", "loc": [-94.725233, 42.84109], "pop": 586, "state": "IA", "_id": "50546"} -{"city": "HUMBOLDT", "loc": [-94.213184, 42.719574], "pop": 6493, "state": "IA", "_id": "50548"} -{"city": "JOLLEY", "loc": [-94.742338, 42.507785], "pop": 296, "state": "IA", "_id": "50551"} -{"city": "KNIERIM", "loc": [-94.453153, 42.431944], "pop": 295, "state": "IA", "_id": "50552"} -{"city": "KNOKE", "loc": [-94.85437, 42.520515], "pop": 192, "state": "IA", "_id": "50553"} -{"city": "LAURENS", "loc": [-94.850816, 42.840938], "pop": 2055, "state": "IA", "_id": "50554"} -{"city": "LEDYARD", "loc": [-94.150281, 43.434569], "pop": 384, "state": "IA", "_id": "50556"} -{"city": "LEHIGH", "loc": [-94.034158, 42.352645], "pop": 821, "state": "IA", "_id": "50557"} -{"city": "LIVERMORE", "loc": [-94.174716, 42.866601], "pop": 645, "state": "IA", "_id": "50558"} -{"city": "LONE ROCK", "loc": [-94.358578, 43.166645], "pop": 560, "state": "IA", "_id": "50559"} -{"city": "LU VERNE", "loc": [-94.095912, 42.98684], "pop": 1337, "state": "IA", "_id": "50560"} -{"city": "LYTTON", "loc": [-94.813991, 42.430375], "pop": 235, "state": "IA", "_id": "50561"} -{"city": "MALLARD", "loc": [-94.674608, 42.94167], "pop": 721, "state": "IA", "_id": "50562"} -{"city": "MANSON", "loc": [-94.530386, 42.528503], "pop": 2131, "state": "IA", "_id": "50563"} -{"city": "MARATHON", "loc": [-94.983551, 42.861233], "pop": 564, "state": "IA", "_id": "50565"} -{"city": "MOORLAND", "loc": [-94.319886, 42.434005], "pop": 541, "state": "IA", "_id": "50566"} -{"city": "NEMAHA", "loc": [-95.093914, 42.518027], "pop": 356, "state": "IA", "_id": "50567"} -{"city": "NEWELL", "loc": [-94.994591, 42.615699], "pop": 1668, "state": "IA", "_id": "50568"} -{"city": "OTHO", "loc": [-94.170776, 42.432825], "pop": 1910, "state": "IA", "_id": "50569"} -{"city": "OTTOSEN", "loc": [-94.378648, 42.876443], "pop": 301, "state": "IA", "_id": "50570"} -{"city": "PALMER", "loc": [-94.543155, 42.641871], "pop": 1119, "state": "IA", "_id": "50571"} -{"city": "PLOVER", "loc": [-94.622507, 42.869107], "pop": 275, "state": "IA", "_id": "50573"} -{"city": "POCAHONTAS", "loc": [-94.673675, 42.729602], "pop": 2802, "state": "IA", "_id": "50574"} -{"city": "POMEROY", "loc": [-94.663758, 42.532565], "pop": 1197, "state": "IA", "_id": "50575"} -{"city": "REMBRANDT", "loc": [-95.165178, 42.826034], "pop": 236, "state": "IA", "_id": "50576"} -{"city": "RENWICK", "loc": [-94.007437, 42.849132], "pop": 545, "state": "IA", "_id": "50577"} -{"city": "RINGSTED", "loc": [-94.529137, 43.297654], "pop": 860, "state": "IA", "_id": "50578"} -{"city": "ROCKWELL CITY", "loc": [-94.629219, 42.396126], "pop": 2952, "state": "IA", "_id": "50579"} -{"city": "RODMAN", "loc": [-94.499243, 43.035233], "pop": 286, "state": "IA", "_id": "50580"} -{"city": "ROLFE", "loc": [-94.516593, 42.808233], "pop": 1250, "state": "IA", "_id": "50581"} -{"city": "RUTLAND", "loc": [-94.271882, 42.763486], "pop": 529, "state": "IA", "_id": "50582"} -{"city": "SAC CITY", "loc": [-94.979552, 42.426212], "pop": 3531, "state": "IA", "_id": "50583"} -{"city": "SIOUX RAPIDS", "loc": [-95.138836, 42.906472], "pop": 1542, "state": "IA", "_id": "50585"} -{"city": "SOMERS", "loc": [-94.44639, 42.356065], "pop": 425, "state": "IA", "_id": "50586"} -{"city": "STORM LAKE", "loc": [-95.196079, 42.64738], "pop": 11663, "state": "IA", "_id": "50588"} -{"city": "SWEA CITY", "loc": [-94.319322, 43.402225], "pop": 1440, "state": "IA", "_id": "50590"} -{"city": "THOR", "loc": [-94.038958, 42.688923], "pop": 463, "state": "IA", "_id": "50591"} -{"city": "VINCENT", "loc": [-94.033534, 42.562292], "pop": 719, "state": "IA", "_id": "50594"} -{"city": "WEBSTER CITY", "loc": [-93.826216, 42.465682], "pop": 9130, "state": "IA", "_id": "50595"} -{"city": "WEST BEND", "loc": [-94.456402, 42.957415], "pop": 1099, "state": "IA", "_id": "50597"} -{"city": "WHITTEMORE", "loc": [-94.406395, 43.02608], "pop": 1252, "state": "IA", "_id": "50598"} -{"city": "WOOLSTOCK", "loc": [-93.82161, 42.584439], "pop": 394, "state": "IA", "_id": "50599"} -{"city": "ACKLEY", "loc": [-93.060835, 42.552722], "pop": 2316, "state": "IA", "_id": "50601"} -{"city": "ALLISON", "loc": [-92.782952, 42.761465], "pop": 1458, "state": "IA", "_id": "50602"} -{"city": "ALTA VISTA", "loc": [-92.423682, 43.148159], "pop": 1356, "state": "IA", "_id": "50603"} -{"city": "APLINGTON", "loc": [-92.875141, 42.587039], "pop": 1436, "state": "IA", "_id": "50604"} -{"city": "AREDALE", "loc": [-92.97688, 42.854852], "pop": 317, "state": "IA", "_id": "50605"} -{"city": "ARLINGTON", "loc": [-91.666814, 42.733827], "pop": 1124, "state": "IA", "_id": "50606"} -{"city": "AURORA", "loc": [-91.733469, 42.620317], "pop": 344, "state": "IA", "_id": "50607"} -{"city": "AUSTINVILLE", "loc": [-92.959422, 42.596245], "pop": 363, "state": "IA", "_id": "50608"} -{"city": "BEAMAN", "loc": [-92.816215, 42.236707], "pop": 395, "state": "IA", "_id": "50609"} -{"city": "BRISTOW", "loc": [-92.886424, 42.774106], "pop": 465, "state": "IA", "_id": "50611"} -{"city": "BUCKINGHAM", "loc": [-92.409788, 42.255211], "pop": 694, "state": "IA", "_id": "50612"} -{"city": "CEDAR FALLS", "loc": [-92.449725, 42.524071], "pop": 36084, "state": "IA", "_id": "50613"} -{"city": "CHARLES CITY", "loc": [-92.676062, 43.068327], "pop": 10110, "state": "IA", "_id": "50616"} -{"city": "CLARKSVILLE", "loc": [-92.658833, 42.804079], "pop": 2757, "state": "IA", "_id": "50619"} -{"city": "CONRAD", "loc": [-92.886525, 42.234188], "pop": 1346, "state": "IA", "_id": "50621"} -{"city": "DENVER", "loc": [-92.341701, 42.673137], "pop": 2543, "state": "IA", "_id": "50622"} -{"city": "DIKE", "loc": [-92.612154, 42.47301], "pop": 1860, "state": "IA", "_id": "50624"} -{"city": "DUMONT", "loc": [-92.967347, 42.736773], "pop": 1302, "state": "IA", "_id": "50625"} -{"city": "DUNKERTON", "loc": [-92.158826, 42.578028], "pop": 1676, "state": "IA", "_id": "50626"} -{"city": "ELDORA", "loc": [-93.103699, 42.357158], "pop": 3689, "state": "IA", "_id": "50627"} -{"city": "ELMA", "loc": [-92.398066, 43.27855], "pop": 2102, "state": "IA", "_id": "50628"} -{"city": "FAIRBANK", "loc": [-92.032589, 42.616235], "pop": 1770, "state": "IA", "_id": "50629"} -{"city": "FREDERICKSBURG", "loc": [-92.198334, 42.959299], "pop": 1830, "state": "IA", "_id": "50630"} -{"city": "GARWIN", "loc": [-92.68935, 42.087228], "pop": 869, "state": "IA", "_id": "50632"} -{"city": "GENEVA", "loc": [-93.103793, 42.68044], "pop": 382, "state": "IA", "_id": "50633"} -{"city": "GLADBROOK", "loc": [-92.714112, 42.177432], "pop": 1268, "state": "IA", "_id": "50635"} -{"city": "GREENE", "loc": [-92.758324, 42.91604], "pop": 2480, "state": "IA", "_id": "50636"} -{"city": "GRUNDY CENTER", "loc": [-92.777344, 42.3672], "pop": 4130, "state": "IA", "_id": "50638"} -{"city": "HANSELL", "loc": [-93.090036, 42.771195], "pop": 362, "state": "IA", "_id": "50640"} -{"city": "HAZLETON", "loc": [-91.910462, 42.607561], "pop": 1480, "state": "IA", "_id": "50641"} -{"city": "HOLLAND", "loc": [-92.799884, 42.40114], "pop": 222, "state": "IA", "_id": "50642"} -{"city": "HUDSON", "loc": [-92.454872, 42.390497], "pop": 2641, "state": "IA", "_id": "50643"} -{"city": "INDEPENDENCE", "loc": [-91.880343, 42.46744], "pop": 8466, "state": "IA", "_id": "50644"} -{"city": "IONIA", "loc": [-92.486521, 43.040253], "pop": 869, "state": "IA", "_id": "50645"} -{"city": "JANESVILLE", "loc": [-92.479096, 42.646515], "pop": 2008, "state": "IA", "_id": "50647"} -{"city": "JESUP", "loc": [-92.045625, 42.482329], "pop": 3505, "state": "IA", "_id": "50648"} -{"city": "KESLEY", "loc": [-92.853455, 42.685717], "pop": 301, "state": "IA", "_id": "50649"} -{"city": "LAMONT", "loc": [-91.670078, 42.594552], "pop": 830, "state": "IA", "_id": "50650"} -{"city": "LA PORTE CITY", "loc": [-92.186155, 42.341618], "pop": 3990, "state": "IA", "_id": "50651"} -{"city": "LINCOLN", "loc": [-92.704693, 42.256056], "pop": 433, "state": "IA", "_id": "50652"} -{"city": "MARBLE ROCK", "loc": [-92.891681, 42.964547], "pop": 940, "state": "IA", "_id": "50653"} -{"city": "MASONVILLE", "loc": [-91.550576, 42.50461], "pop": 557, "state": "IA", "_id": "50654"} -{"city": "MAYNARD", "loc": [-91.890773, 42.775156], "pop": 899, "state": "IA", "_id": "50655"} -{"city": "NASHUA", "loc": [-92.529839, 42.952848], "pop": 2007, "state": "IA", "_id": "50658"} -{"city": "NEW HAMPTON", "loc": [-92.313136, 43.056015], "pop": 5783, "state": "IA", "_id": "50659"} -{"city": "NEW HARTFORD", "loc": [-92.616359, 42.583128], "pop": 1362, "state": "IA", "_id": "50660"} -{"city": "OELWEIN", "loc": [-91.913084, 42.681095], "pop": 8426, "state": "IA", "_id": "50662"} -{"city": "PARKERSBURG", "loc": [-92.768763, 42.571377], "pop": 3544, "state": "IA", "_id": "50665"} -{"city": "PLAINFIELD", "loc": [-92.511558, 42.850267], "pop": 1030, "state": "IA", "_id": "50666"} -{"city": "RAYMOND", "loc": [-92.216247, 42.467169], "pop": 367, "state": "IA", "_id": "50667"} -{"city": "READLYN", "loc": [-92.215432, 42.692869], "pop": 1858, "state": "IA", "_id": "50668"} -{"city": "REINBECK", "loc": [-92.594839, 42.313078], "pop": 2099, "state": "IA", "_id": "50669"} -{"city": "SHELL ROCK", "loc": [-92.58878, 42.70611], "pop": 1834, "state": "IA", "_id": "50670"} -{"city": "STANLEY", "loc": [-91.812673, 42.635623], "pop": 175, "state": "IA", "_id": "50671"} -{"city": "STEAMBOAT ROCK", "loc": [-93.06291, 42.417841], "pop": 650, "state": "IA", "_id": "50672"} -{"city": "SUMNER", "loc": [-92.118562, 42.841935], "pop": 3187, "state": "IA", "_id": "50674"} -{"city": "TRAER", "loc": [-92.482681, 42.184685], "pop": 2064, "state": "IA", "_id": "50675"} -{"city": "TRIPOLI", "loc": [-92.265934, 42.810538], "pop": 1993, "state": "IA", "_id": "50676"} -{"city": "BREMER", "loc": [-92.466199, 42.737307], "pop": 10879, "state": "IA", "_id": "50677"} -{"city": "WELLSBURG", "loc": [-92.939763, 42.449418], "pop": 1353, "state": "IA", "_id": "50680"} -{"city": "WESTGATE", "loc": [-92.018299, 42.805453], "pop": 1044, "state": "IA", "_id": "50681"} -{"city": "WINTHROP", "loc": [-91.731467, 42.491163], "pop": 1330, "state": "IA", "_id": "50682"} -{"city": "WATERLOO", "loc": [-92.366099, 42.477784], "pop": 29194, "state": "IA", "_id": "50701"} -{"city": "WATERLOO", "loc": [-92.336486, 42.473112], "pop": 18939, "state": "IA", "_id": "50702"} -{"city": "WATERLOO", "loc": [-92.326919, 42.514875], "pop": 21256, "state": "IA", "_id": "50703"} -{"city": "WASHBURN", "loc": [-92.2676, 42.407605], "pop": 1010, "state": "IA", "_id": "50706"} -{"city": "EVANSDALE", "loc": [-92.281158, 42.475453], "pop": 7954, "state": "IA", "_id": "50707"} -{"city": "NEVINVILLE", "loc": [-94.369151, 41.062384], "pop": 9548, "state": "IA", "_id": "50801"} -{"city": "AFTON", "loc": [-94.194044, 41.040146], "pop": 1450, "state": "IA", "_id": "50830"} -{"city": "BEDFORD", "loc": [-94.732448, 40.66829], "pop": 2608, "state": "IA", "_id": "50833"} -{"city": "BENTON", "loc": [-94.393104, 40.725836], "pop": 203, "state": "IA", "_id": "50835"} -{"city": "BLOCKTON", "loc": [-94.504901, 40.624242], "pop": 476, "state": "IA", "_id": "50836"} -{"city": "BRIDGEWATER", "loc": [-94.641011, 41.157084], "pop": 1383, "state": "IA", "_id": "50837"} -{"city": "CLEARFIELD", "loc": [-94.494439, 40.793534], "pop": 593, "state": "IA", "_id": "50840"} -{"city": "CORNING", "loc": [-94.735789, 40.987121], "pop": 2660, "state": "IA", "_id": "50841"} -{"city": "CUMBERLAND", "loc": [-94.871006, 41.263165], "pop": 695, "state": "IA", "_id": "50843"} -{"city": "DELPHOS", "loc": [-94.316188, 40.661959], "pop": 100, "state": "IA", "_id": "50844"} -{"city": "DIAGONAL", "loc": [-94.355156, 40.822644], "pop": 793, "state": "IA", "_id": "50845"} -{"city": "FONTANELLE", "loc": [-94.541904, 41.289568], "pop": 1534, "state": "IA", "_id": "50846"} -{"city": "GRANT", "loc": [-94.984554, 41.125799], "pop": 315, "state": "IA", "_id": "50847"} -{"city": "GRAVITY", "loc": [-94.750595, 40.790914], "pop": 617, "state": "IA", "_id": "50848"} -{"city": "GREENFIELD", "loc": [-94.440757, 41.313311], "pop": 2772, "state": "IA", "_id": "50849"} -{"city": "KENT", "loc": [-94.421718, 40.944856], "pop": 309, "state": "IA", "_id": "50850"} -{"city": "LENOX", "loc": [-94.56117, 40.884096], "pop": 1973, "state": "IA", "_id": "50851"} -{"city": "MALOY", "loc": [-94.40764, 40.673648], "pop": 81, "state": "IA", "_id": "50852"} -{"city": "MASSENA", "loc": [-94.764872, 41.249867], "pop": 794, "state": "IA", "_id": "50853"} -{"city": "MOUNT AYR", "loc": [-94.232146, 40.7142], "pop": 2487, "state": "IA", "_id": "50854"} -{"city": "50855", "loc": [-94.796602, 41.111656], "pop": 335, "state": "IA", "_id": "50855"} -{"city": "NODAWAY", "loc": [-94.87309, 40.954034], "pop": 706, "state": "IA", "_id": "50857"} -{"city": "ORIENT", "loc": [-94.370496, 41.219191], "pop": 1042, "state": "IA", "_id": "50858"} -{"city": "PRESCOTT", "loc": [-94.524901, 41.024848], "pop": 205, "state": "IA", "_id": "50859"} -{"city": "REDDING", "loc": [-94.359451, 40.605312], "pop": 327, "state": "IA", "_id": "50860"} -{"city": "SHANNON CITY", "loc": [-94.250106, 40.936636], "pop": 586, "state": "IA", "_id": "50861"} -{"city": "SHARPSBURG", "loc": [-94.641633, 40.795042], "pop": 199, "state": "IA", "_id": "50862"} -{"city": "TINGLEY", "loc": [-94.190823, 40.851597], "pop": 347, "state": "IA", "_id": "50863"} -{"city": "VILLISCA", "loc": [-94.979588, 40.943718], "pop": 1794, "state": "IA", "_id": "50864"} -{"city": "AKRON", "loc": [-96.52245, 42.835392], "pop": 2068, "state": "IA", "_id": "51001"} -{"city": "ALTA", "loc": [-95.3129, 42.677118], "pop": 2757, "state": "IA", "_id": "51002"} -{"city": "ALTON", "loc": [-96.017268, 42.978175], "pop": 1371, "state": "IA", "_id": "51003"} -{"city": "ANTHON", "loc": [-95.894765, 42.386967], "pop": 1550, "state": "IA", "_id": "51004"} -{"city": "AURELIA", "loc": [-95.438086, 42.69122], "pop": 1543, "state": "IA", "_id": "51005"} -{"city": "BATTLE CREEK", "loc": [-95.604517, 42.332662], "pop": 1552, "state": "IA", "_id": "51006"} -{"city": "BRONSON", "loc": [-96.198591, 42.429181], "pop": 796, "state": "IA", "_id": "51007"} -{"city": "CALUMET", "loc": [-95.554871, 42.953272], "pop": 431, "state": "IA", "_id": "51009"} -{"city": "CASTANA", "loc": [-95.943121, 42.105404], "pop": 533, "state": "IA", "_id": "51010"} -{"city": "CHEROKEE", "loc": [-95.556842, 42.748187], "pop": 8339, "state": "IA", "_id": "51012"} -{"city": "CLEGHORN", "loc": [-95.712436, 42.808593], "pop": 541, "state": "IA", "_id": "51014"} -{"city": "CORRECTIONVILLE", "loc": [-95.780382, 42.474322], "pop": 1432, "state": "IA", "_id": "51016"} -{"city": "CUSHING", "loc": [-95.67748, 42.464182], "pop": 261, "state": "IA", "_id": "51018"} -{"city": "DANBURY", "loc": [-95.726259, 42.269337], "pop": 906, "state": "IA", "_id": "51019"} -{"city": "GALVA", "loc": [-95.429838, 42.510508], "pop": 626, "state": "IA", "_id": "51020"} -{"city": "GRANVILLE", "loc": [-95.897167, 42.969252], "pop": 592, "state": "IA", "_id": "51022"} -{"city": "HAWARDEN", "loc": [-96.472992, 43.001333], "pop": 3097, "state": "IA", "_id": "51023"} -{"city": "HINTON", "loc": [-96.305033, 42.604046], "pop": 2449, "state": "IA", "_id": "51024"} -{"city": "HOLSTEIN", "loc": [-95.564971, 42.494779], "pop": 1977, "state": "IA", "_id": "51025"} -{"city": "HORNICK", "loc": [-96.079553, 42.289741], "pop": 894, "state": "IA", "_id": "51026"} -{"city": "IRETON", "loc": [-96.323226, 42.966566], "pop": 1154, "state": "IA", "_id": "51027"} -{"city": "KINGSLEY", "loc": [-95.976151, 42.619057], "pop": 2138, "state": "IA", "_id": "51028"} -{"city": "LARRABEE", "loc": [-95.581228, 42.862758], "pop": 607, "state": "IA", "_id": "51029"} -{"city": "LAWTON", "loc": [-96.21893, 42.504748], "pop": 1481, "state": "IA", "_id": "51030"} -{"city": "LE MARS", "loc": [-96.170386, 42.79623], "pop": 11576, "state": "IA", "_id": "51031"} -{"city": "LINN GROVE", "loc": [-95.251249, 42.874229], "pop": 722, "state": "IA", "_id": "51033"} -{"city": "MAPLETON", "loc": [-95.790934, 42.158524], "pop": 2038, "state": "IA", "_id": "51034"} -{"city": "MARCUS", "loc": [-95.803431, 42.819959], "pop": 1654, "state": "IA", "_id": "51035"} -{"city": "MAURICE", "loc": [-96.165531, 42.962647], "pop": 625, "state": "IA", "_id": "51036"} -{"city": "MERIDEN", "loc": [-95.640797, 42.787416], "pop": 333, "state": "IA", "_id": "51037"} -{"city": "MERRILL", "loc": [-96.228324, 42.704737], "pop": 1468, "state": "IA", "_id": "51038"} -{"city": "MOVILLE", "loc": [-96.065583, 42.481924], "pop": 2064, "state": "IA", "_id": "51039"} -{"city": "ONAWA", "loc": [-96.107213, 42.036967], "pop": 3867, "state": "IA", "_id": "51040"} -{"city": "ORANGE CITY", "loc": [-96.056464, 43.01849], "pop": 6126, "state": "IA", "_id": "51041"} -{"city": "OTO", "loc": [-95.861575, 42.264592], "pop": 340, "state": "IA", "_id": "51044"} -{"city": "PAULLINA", "loc": [-95.708322, 42.970277], "pop": 1760, "state": "IA", "_id": "51046"} -{"city": "PETERSON", "loc": [-95.337658, 42.932794], "pop": 640, "state": "IA", "_id": "51047"} -{"city": "PIERSON", "loc": [-95.892193, 42.533493], "pop": 642, "state": "IA", "_id": "51048"} -{"city": "QUIMBY", "loc": [-95.681175, 42.628711], "pop": 562, "state": "IA", "_id": "51049"} -{"city": "REMSEN", "loc": [-95.954378, 42.8149], "pop": 2254, "state": "IA", "_id": "51050"} -{"city": "RODNEY", "loc": [-95.953456, 42.201541], "pop": 120, "state": "IA", "_id": "51051"} -{"city": "SALIX", "loc": [-96.287912, 42.330554], "pop": 1210, "state": "IA", "_id": "51052"} -{"city": "SCHALLER", "loc": [-95.284587, 42.505501], "pop": 1311, "state": "IA", "_id": "51053"} -{"city": "SERGEANT BLUFF", "loc": [-96.353392, 42.401895], "pop": 2853, "state": "IA", "_id": "51054"} -{"city": "SLOAN", "loc": [-96.230634, 42.242726], "pop": 1303, "state": "IA", "_id": "51055"} -{"city": "SMITHLAND", "loc": [-95.948647, 42.24079], "pop": 495, "state": "IA", "_id": "51056"} -{"city": "SUTHERLAND", "loc": [-95.480665, 42.982623], "pop": 1096, "state": "IA", "_id": "51058"} -{"city": "TURIN", "loc": [-95.951828, 41.973189], "pop": 387, "state": "IA", "_id": "51059"} -{"city": "UTE", "loc": [-95.712542, 42.058085], "pop": 567, "state": "IA", "_id": "51060"} -{"city": "WASHTA", "loc": [-95.729537, 42.578108], "pop": 519, "state": "IA", "_id": "51061"} -{"city": "WESTFIELD", "loc": [-96.491987, 42.695329], "pop": 1435, "state": "IA", "_id": "51062"} -{"city": "WHITING", "loc": [-96.182247, 42.15216], "pop": 895, "state": "IA", "_id": "51063"} -{"city": "SIOUX CITY", "loc": [-96.40292, 42.497223], "pop": 765, "state": "IA", "_id": "51101"} -{"city": "SIOUX CITY", "loc": [-96.42951, 42.506793], "pop": 16831, "state": "IA", "_id": "51103"} -{"city": "SIOUX CITY", "loc": [-96.400453, 42.52536], "pop": 20441, "state": "IA", "_id": "51104"} -{"city": "SIOUX CITY", "loc": [-96.382855, 42.503224], "pop": 10372, "state": "IA", "_id": "51105"} -{"city": "SIOUX CITY", "loc": [-96.352755, 42.467057], "pop": 25974, "state": "IA", "_id": "51106"} -{"city": "SIOUX CITY", "loc": [-96.376064, 42.490266], "pop": 258, "state": "IA", "_id": "51107"} -{"city": "SIOUX CITY", "loc": [-96.361695, 42.546891], "pop": 4615, "state": "IA", "_id": "51108"} -{"city": "SIOUX CITY", "loc": [-96.480304, 42.517287], "pop": 2592, "state": "IA", "_id": "51109"} -{"city": "SIOUX CITY", "loc": [-96.372559, 42.400921], "pop": 189, "state": "IA", "_id": "51110"} -{"city": "SIOUX CITY", "loc": [-96.371294, 42.408912], "pop": 12, "state": "IA", "_id": "51111"} -{"city": "SHELDON", "loc": [-95.840141, 43.180797], "pop": 5585, "state": "IA", "_id": "51201"} -{"city": "ALVORD", "loc": [-96.290994, 43.320109], "pop": 494, "state": "IA", "_id": "51230"} -{"city": "ARCHER", "loc": [-95.741656, 43.117917], "pop": 194, "state": "IA", "_id": "51231"} -{"city": "ASHTON", "loc": [-95.765509, 43.305259], "pop": 1090, "state": "IA", "_id": "51232"} -{"city": "BOYDEN", "loc": [-95.982309, 43.20343], "pop": 1652, "state": "IA", "_id": "51234"} -{"city": "DOON", "loc": [-96.201987, 43.287183], "pop": 957, "state": "IA", "_id": "51235"} -{"city": "GEORGE", "loc": [-95.989383, 43.343903], "pop": 2326, "state": "IA", "_id": "51237"} -{"city": "HOSPERS", "loc": [-95.914172, 43.079337], "pop": 1491, "state": "IA", "_id": "51238"} -{"city": "HULL", "loc": [-96.139248, 43.195201], "pop": 2283, "state": "IA", "_id": "51239"} -{"city": "INWOOD", "loc": [-96.436293, 43.304747], "pop": 1434, "state": "IA", "_id": "51240"} -{"city": "LARCHWOOD", "loc": [-96.42522, 43.438673], "pop": 2097, "state": "IA", "_id": "51241"} -{"city": "LITTLE ROCK", "loc": [-95.892622, 43.449571], "pop": 688, "state": "IA", "_id": "51243"} -{"city": "PRIMGHAR", "loc": [-95.653304, 43.075892], "pop": 2167, "state": "IA", "_id": "51245"} -{"city": "ROCK RAPIDS", "loc": [-96.178055, 43.427192], "pop": 3956, "state": "IA", "_id": "51246"} -{"city": "ROCK VALLEY", "loc": [-96.310342, 43.201356], "pop": 4100, "state": "IA", "_id": "51247"} -{"city": "SANBORN", "loc": [-95.662227, 43.189002], "pop": 1632, "state": "IA", "_id": "51248"} -{"city": "SIBLEY", "loc": [-95.7424, 43.406929], "pop": 3853, "state": "IA", "_id": "51249"} -{"city": "SIOUX CENTER", "loc": [-96.179952, 43.081416], "pop": 7417, "state": "IA", "_id": "51250"} -{"city": "SPENCER", "loc": [-95.14569, 43.145107], "pop": 12101, "state": "IA", "_id": "51301"} -{"city": "ARNOLDS PARK", "loc": [-95.131261, 43.36358], "pop": 1109, "state": "IA", "_id": "51331"} -{"city": "DICKENS", "loc": [-94.985383, 43.154836], "pop": 732, "state": "IA", "_id": "51333"} -{"city": "ESTHERVILLE", "loc": [-94.818001, 43.401709], "pop": 8614, "state": "IA", "_id": "51334"} -{"city": "EVERLY", "loc": [-95.322777, 43.160917], "pop": 1153, "state": "IA", "_id": "51338"} -{"city": "FOSTORIA", "loc": [-95.185042, 43.225857], "pop": 471, "state": "IA", "_id": "51340"} -{"city": "GRAETTINGER", "loc": [-94.733401, 43.227731], "pop": 1259, "state": "IA", "_id": "51342"} -{"city": "GREENVILLE", "loc": [-95.058184, 43.033348], "pop": 630, "state": "IA", "_id": "51343"} -{"city": "HARRIS", "loc": [-95.442603, 43.456793], "pop": 333, "state": "IA", "_id": "51345"} -{"city": "HARTLEY", "loc": [-95.481696, 43.179984], "pop": 2574, "state": "IA", "_id": "51346"} -{"city": "LAKE PARK", "loc": [-95.321601, 43.445239], "pop": 1320, "state": "IA", "_id": "51347"} -{"city": "MELVIN", "loc": [-95.592951, 43.284496], "pop": 395, "state": "IA", "_id": "51350"} -{"city": "MILFORD", "loc": [-95.161534, 43.31964], "pop": 2964, "state": "IA", "_id": "51351"} -{"city": "OCHEYEDAN", "loc": [-95.520366, 43.39105], "pop": 1596, "state": "IA", "_id": "51354"} -{"city": "OKOBOJI", "loc": [-95.153026, 43.37938], "pop": 1637, "state": "IA", "_id": "51355"} -{"city": "ROYAL", "loc": [-95.267856, 43.048751], "pop": 1035, "state": "IA", "_id": "51357"} -{"city": "RUTHVEN", "loc": [-94.884214, 43.144845], "pop": 1283, "state": "IA", "_id": "51358"} -{"city": "SPIRIT LAKE", "loc": [-95.112336, 43.426211], "pop": 6683, "state": "IA", "_id": "51360"} -{"city": "SUPERIOR", "loc": [-94.964935, 43.450317], "pop": 333, "state": "IA", "_id": "51363"} -{"city": "TERRIL", "loc": [-94.973725, 43.324073], "pop": 863, "state": "IA", "_id": "51364"} -{"city": "WALLINGFORD", "loc": [-94.82057, 43.312158], "pop": 371, "state": "IA", "_id": "51365"} -{"city": "WEBB", "loc": [-94.995124, 42.950189], "pop": 343, "state": "IA", "_id": "51366"} -{"city": "CARROLL", "loc": [-94.866664, 42.071972], "pop": 11456, "state": "IA", "_id": "51401"} -{"city": "ARCADIA", "loc": [-95.03714, 42.057279], "pop": 1183, "state": "IA", "_id": "51430"} -{"city": "ARTHUR", "loc": [-95.367519, 42.336283], "pop": 577, "state": "IA", "_id": "51431"} -{"city": "ASPINWALL", "loc": [-95.149936, 41.908252], "pop": 367, "state": "IA", "_id": "51432"} -{"city": "YETTER", "loc": [-94.903447, 42.280895], "pop": 882, "state": "IA", "_id": "51433"} -{"city": "BREDA", "loc": [-95.000679, 42.175835], "pop": 773, "state": "IA", "_id": "51436"} -{"city": "CHARTER OAK", "loc": [-95.599241, 42.070735], "pop": 797, "state": "IA", "_id": "51439"} -{"city": "DEDHAM", "loc": [-94.814896, 41.905539], "pop": 562, "state": "IA", "_id": "51440"} -{"city": "DELOIT", "loc": [-95.286338, 42.085899], "pop": 618, "state": "IA", "_id": "51441"} -{"city": "DENISON", "loc": [-95.363572, 42.019626], "pop": 8507, "state": "IA", "_id": "51442"} -{"city": "GLIDDEN", "loc": [-94.714777, 42.061816], "pop": 1518, "state": "IA", "_id": "51443"} -{"city": "HALBUR", "loc": [-94.93586, 41.997357], "pop": 720, "state": "IA", "_id": "51444"} -{"city": "IDA GROVE", "loc": [-95.464488, 42.340026], "pop": 3633, "state": "IA", "_id": "51445"} -{"city": "IRWIN", "loc": [-95.195842, 41.794094], "pop": 1120, "state": "IA", "_id": "51446"} -{"city": "KIRKMAN", "loc": [-95.267083, 41.73076], "pop": 395, "state": "IA", "_id": "51447"} -{"city": "KIRON", "loc": [-95.31146, 42.179359], "pop": 753, "state": "IA", "_id": "51448"} -{"city": "LAKE CITY", "loc": [-94.745787, 42.270494], "pop": 2539, "state": "IA", "_id": "51449"} -{"city": "LAKE VIEW", "loc": [-95.048562, 42.315367], "pop": 1792, "state": "IA", "_id": "51450"} -{"city": "LANESBORO", "loc": [-94.690274, 42.174687], "pop": 396, "state": "IA", "_id": "51451"} -{"city": "LIDDERDALE", "loc": [-94.795353, 42.152815], "pop": 525, "state": "IA", "_id": "51452"} -{"city": "LOHRVILLE", "loc": [-94.556605, 42.261923], "pop": 615, "state": "IA", "_id": "51453"} -{"city": "MANILLA", "loc": [-95.239126, 41.892233], "pop": 1185, "state": "IA", "_id": "51454"} -{"city": "MANNING", "loc": [-95.055622, 41.907248], "pop": 1807, "state": "IA", "_id": "51455"} -{"city": "ODEBOLT", "loc": [-95.25005, 42.309514], "pop": 1776, "state": "IA", "_id": "51458"} -{"city": "RALSTON", "loc": [-94.569779, 42.075655], "pop": 231, "state": "IA", "_id": "51459"} -{"city": "RICKETTS", "loc": [-95.599547, 42.15406], "pop": 346, "state": "IA", "_id": "51460"} -{"city": "SCHLESWIG", "loc": [-95.444309, 42.164586], "pop": 1191, "state": "IA", "_id": "51461"} -{"city": "SCRANTON", "loc": [-94.551764, 42.012381], "pop": 814, "state": "IA", "_id": "51462"} -{"city": "TEMPLETON", "loc": [-94.932009, 41.912077], "pop": 681, "state": "IA", "_id": "51463"} -{"city": "VAIL", "loc": [-95.177766, 42.033772], "pop": 790, "state": "IA", "_id": "51465"} -{"city": "WALL LAKE", "loc": [-95.085156, 42.265252], "pop": 1481, "state": "IA", "_id": "51466"} -{"city": "WESTSIDE", "loc": [-95.1273, 42.10844], "pop": 714, "state": "IA", "_id": "51467"} -{"city": "MANAWA", "loc": [-95.880992, 41.252954], "pop": 31614, "state": "IA", "_id": "51501"} -{"city": "COUNCIL BLUFFS", "loc": [-95.825083, 41.261584], "pop": 30513, "state": "IA", "_id": "51503"} -{"city": "CARTER LAKE", "loc": [-95.91406, 41.292524], "pop": 3200, "state": "IA", "_id": "51510"} -{"city": "ARION", "loc": [-95.46233, 41.950406], "pop": 194, "state": "IA", "_id": "51520"} -{"city": "AVOCA", "loc": [-95.350872, 41.476834], "pop": 2032, "state": "IA", "_id": "51521"} -{"city": "BLENCOE", "loc": [-96.082663, 41.923662], "pop": 373, "state": "IA", "_id": "51523"} -{"city": "CARSON", "loc": [-95.368086, 41.225276], "pop": 1398, "state": "IA", "_id": "51525"} -{"city": "CRESCENT", "loc": [-95.827648, 41.364378], "pop": 1967, "state": "IA", "_id": "51526"} -{"city": "EARLING", "loc": [-95.340293, 41.827063], "pop": 375, "state": "IA", "_id": "51527"} -{"city": "DOW CITY", "loc": [-95.4862, 41.913429], "pop": 1313, "state": "IA", "_id": "51528"} -{"city": "EARLING", "loc": [-95.615709, 41.842301], "pop": 1836, "state": "IA", "_id": "51529"} -{"city": "EARLING", "loc": [-95.421045, 41.792483], "pop": 1068, "state": "IA", "_id": "51530"} -{"city": "ELK HORN", "loc": [-95.070015, 41.582871], "pop": 990, "state": "IA", "_id": "51531"} -{"city": "ELLIOTT", "loc": [-95.160237, 41.130617], "pop": 879, "state": "IA", "_id": "51532"} -{"city": "EMERSON", "loc": [-95.410624, 41.028439], "pop": 1529, "state": "IA", "_id": "51533"} -{"city": "GLENWOOD", "loc": [-95.73892, 41.047009], "pop": 6392, "state": "IA", "_id": "51534"} -{"city": "GRISWOLD", "loc": [-95.098989, 41.223842], "pop": 1678, "state": "IA", "_id": "51535"} -{"city": "HANCOCK", "loc": [-95.344528, 41.383238], "pop": 427, "state": "IA", "_id": "51536"} -{"city": "HARLAN", "loc": [-95.311101, 41.64375], "pop": 6934, "state": "IA", "_id": "51537"} -{"city": "HASTINGS", "loc": [-95.553097, 40.960514], "pop": 267, "state": "IA", "_id": "51540"} -{"city": "HENDERSON", "loc": [-95.458629, 41.125426], "pop": 564, "state": "IA", "_id": "51541"} -{"city": "HONEY CREEK", "loc": [-95.8802, 41.463045], "pop": 656, "state": "IA", "_id": "51542"} -{"city": "KIMBALLTON", "loc": [-95.055324, 41.635214], "pop": 614, "state": "IA", "_id": "51543"} -{"city": "LEWIS", "loc": [-95.062024, 41.296289], "pop": 980, "state": "IA", "_id": "51544"} -{"city": "LITTLE SIOUX", "loc": [-96.031357, 41.808267], "pop": 402, "state": "IA", "_id": "51545"} -{"city": "LOGAN", "loc": [-95.752478, 41.624345], "pop": 2764, "state": "IA", "_id": "51546"} -{"city": "MC CLELLAND", "loc": [-95.681278, 41.293168], "pop": 714, "state": "IA", "_id": "51548"} -{"city": "MACEDONIA", "loc": [-95.431386, 41.191627], "pop": 475, "state": "IA", "_id": "51549"} -{"city": "MAGNOLIA", "loc": [-95.854955, 41.708177], "pop": 631, "state": "IA", "_id": "51550"} -{"city": "MALVERN", "loc": [-95.583988, 41.007378], "pop": 1409, "state": "IA", "_id": "51551"} -{"city": "MARNE", "loc": [-95.097308, 41.454681], "pop": 388, "state": "IA", "_id": "51552"} -{"city": "MINDEN", "loc": [-95.551887, 41.465355], "pop": 917, "state": "IA", "_id": "51553"} -{"city": "MINEOLA", "loc": [-95.755699, 41.110171], "pop": 1276, "state": "IA", "_id": "51554"} -{"city": "MISSOURI VALLEY", "loc": [-95.891346, 41.564399], "pop": 4330, "state": "IA", "_id": "51555"} -{"city": "MODALE", "loc": [-95.985225, 41.637143], "pop": 505, "state": "IA", "_id": "51556"} -{"city": "MONDAMIN", "loc": [-96.007204, 41.708876], "pop": 793, "state": "IA", "_id": "51557"} -{"city": "MOORHEAD", "loc": [-95.829031, 41.918601], "pop": 589, "state": "IA", "_id": "51558"} -{"city": "NEOLA", "loc": [-95.679358, 41.456383], "pop": 1766, "state": "IA", "_id": "51559"} -{"city": "OAKLAND", "loc": [-95.379034, 41.311964], "pop": 2385, "state": "IA", "_id": "51560"} -{"city": "PACIFIC JUNCTION", "loc": [-95.804645, 41.027123], "pop": 1228, "state": "IA", "_id": "51561"} -{"city": "PANAMA", "loc": [-95.487046, 41.728813], "pop": 511, "state": "IA", "_id": "51562"} -{"city": "PERSIA", "loc": [-95.56577, 41.56473], "pop": 691, "state": "IA", "_id": "51563"} -{"city": "PISGAH", "loc": [-95.919172, 41.821376], "pop": 673, "state": "IA", "_id": "51564"} -{"city": "PORTSMOUTH", "loc": [-95.510751, 41.648385], "pop": 476, "state": "IA", "_id": "51565"} -{"city": "RED OAK", "loc": [-95.226924, 41.007949], "pop": 7395, "state": "IA", "_id": "51566"} -{"city": "SHELBY", "loc": [-95.446888, 41.533898], "pop": 941, "state": "IA", "_id": "51570"} -{"city": "SILVER CITY", "loc": [-95.626912, 41.113024], "pop": 483, "state": "IA", "_id": "51571"} -{"city": "SOLDIER", "loc": [-95.782531, 41.994188], "pop": 665, "state": "IA", "_id": "51572"} -{"city": "STANTON", "loc": [-95.09876, 40.985924], "pop": 1315, "state": "IA", "_id": "51573"} -{"city": "TREYNOR", "loc": [-95.617041, 41.220679], "pop": 1619, "state": "IA", "_id": "51575"} -{"city": "UNDERWOOD", "loc": [-95.626997, 41.360294], "pop": 1652, "state": "IA", "_id": "51576"} -{"city": "WALNUT", "loc": [-95.217973, 41.462329], "pop": 1293, "state": "IA", "_id": "51577"} -{"city": "WESTPHALIA", "loc": [-95.386978, 41.718014], "pop": 420, "state": "IA", "_id": "51578"} -{"city": "WOODBINE", "loc": [-95.700785, 41.737267], "pop": 2105, "state": "IA", "_id": "51579"} -{"city": "SHENANDOAH", "loc": [-95.364798, 40.758087], "pop": 6226, "state": "IA", "_id": "51601"} -{"city": "BLANCHARD", "loc": [-95.205377, 40.612156], "pop": 284, "state": "IA", "_id": "51630"} -{"city": "BRADDYVILLE", "loc": [-94.995578, 40.599081], "pop": 351, "state": "IA", "_id": "51631"} -{"city": "CLARINDA", "loc": [-95.037976, 40.749084], "pop": 6652, "state": "IA", "_id": "51632"} -{"city": "COIN", "loc": [-95.221653, 40.668591], "pop": 387, "state": "IA", "_id": "51636"} -{"city": "COLLEGE SPRINGS", "loc": [-95.099717, 40.617109], "pop": 537, "state": "IA", "_id": "51637"} -{"city": "ESSEX", "loc": [-95.288777, 40.840359], "pop": 1433, "state": "IA", "_id": "51638"} -{"city": "FARRAGUT", "loc": [-95.455498, 40.729971], "pop": 1055, "state": "IA", "_id": "51639"} -{"city": "HAMBURG", "loc": [-95.625789, 40.614902], "pop": 1997, "state": "IA", "_id": "51640"} -{"city": "IMOGENE", "loc": [-95.435804, 40.863144], "pop": 318, "state": "IA", "_id": "51645"} -{"city": "NEW MARKET", "loc": [-94.891662, 40.742174], "pop": 702, "state": "IA", "_id": "51646"} -{"city": "NORTHBORO", "loc": [-95.309592, 40.612737], "pop": 287, "state": "IA", "_id": "51647"} -{"city": "PERCIVAL", "loc": [-95.809307, 40.740148], "pop": 327, "state": "IA", "_id": "51648"} -{"city": "RANDOLPH", "loc": [-95.562386, 40.870353], "pop": 419, "state": "IA", "_id": "51649"} -{"city": "RIVERTON", "loc": [-95.557803, 40.686968], "pop": 447, "state": "IA", "_id": "51650"} -{"city": "SHAMBAUGH", "loc": [-95.063591, 40.675735], "pop": 456, "state": "IA", "_id": "51651"} -{"city": "SIDNEY", "loc": [-95.64027, 40.750671], "pop": 1948, "state": "IA", "_id": "51652"} -{"city": "TABOR", "loc": [-95.672892, 40.900448], "pop": 1626, "state": "IA", "_id": "51653"} -{"city": "THURMAN", "loc": [-95.760535, 40.84241], "pop": 521, "state": "IA", "_id": "51654"} -{"city": "YORKTOWN", "loc": [-95.216335, 40.769244], "pop": 257, "state": "IA", "_id": "51656"} -{"city": "DUBUQUE", "loc": [-90.681914, 42.514977], "pop": 41934, "state": "IA", "_id": "52001"} -{"city": "DUBUQUE", "loc": [-90.738372, 42.512191], "pop": 12734, "state": "IA", "_id": "52002"} -{"city": "DUBUQUE", "loc": [-90.682884, 42.464916], "pop": 13104, "state": "IA", "_id": "52003"} -{"city": "ANDREW", "loc": [-90.60035, 42.158496], "pop": 741, "state": "IA", "_id": "52030"} -{"city": "BELLEVUE", "loc": [-90.435603, 42.25825], "pop": 2884, "state": "IA", "_id": "52031"} -{"city": "BERNARD", "loc": [-90.863032, 42.33207], "pop": 861, "state": "IA", "_id": "52032"} -{"city": "CASCADE", "loc": [-91.014395, 42.286959], "pop": 3155, "state": "IA", "_id": "52033"} -{"city": "COLESBURG", "loc": [-91.19628, 42.638346], "pop": 1401, "state": "IA", "_id": "52035"} -{"city": "DELAWARE", "loc": [-91.347176, 42.477713], "pop": 262, "state": "IA", "_id": "52036"} -{"city": "DELMAR", "loc": [-90.608712, 41.996065], "pop": 927, "state": "IA", "_id": "52037"} -{"city": "DUNDEE", "loc": [-91.547697, 42.593979], "pop": 618, "state": "IA", "_id": "52038"} -{"city": "DURANGO", "loc": [-90.860794, 42.552564], "pop": 874, "state": "IA", "_id": "52039"} -{"city": "DYERSVILLE", "loc": [-91.118265, 42.483332], "pop": 4709, "state": "IA", "_id": "52040"} -{"city": "EARLVILLE", "loc": [-91.259658, 42.500101], "pop": 1566, "state": "IA", "_id": "52041"} -{"city": "EDGEWOOD", "loc": [-91.394951, 42.654098], "pop": 2095, "state": "IA", "_id": "52042"} -{"city": "ELKADER", "loc": [-91.414308, 42.849627], "pop": 2887, "state": "IA", "_id": "52043"} -{"city": "ELKPORT", "loc": [-91.283934, 42.739892], "pop": 129, "state": "IA", "_id": "52044"} -{"city": "EPWORTH", "loc": [-90.931331, 42.443928], "pop": 1931, "state": "IA", "_id": "52045"} -{"city": "FARLEY", "loc": [-91.008266, 42.445059], "pop": 2120, "state": "IA", "_id": "52046"} -{"city": "FARMERSBURG", "loc": [-91.338984, 42.952205], "pop": 638, "state": "IA", "_id": "52047"} -{"city": "GARBER", "loc": [-91.259275, 42.74528], "pop": 191, "state": "IA", "_id": "52048"} -{"city": "GARNAVILLO", "loc": [-91.215609, 42.876188], "pop": 1408, "state": "IA", "_id": "52049"} -{"city": "GREELEY", "loc": [-91.323316, 42.59389], "pop": 679, "state": "IA", "_id": "52050"} -{"city": "GUTTENBERG", "loc": [-91.119015, 42.759443], "pop": 4113, "state": "IA", "_id": "52052"} -{"city": "HOLY CROSS", "loc": [-90.972986, 42.580778], "pop": 1139, "state": "IA", "_id": "52053"} -{"city": "LA MOTTE", "loc": [-90.617673, 42.304448], "pop": 1132, "state": "IA", "_id": "52054"} -{"city": "MANCHESTER", "loc": [-91.449001, 42.483418], "pop": 7823, "state": "IA", "_id": "52057"} -{"city": "MAQUOKETA", "loc": [-90.677075, 42.07782], "pop": 8374, "state": "IA", "_id": "52060"} -{"city": "MILES", "loc": [-90.336716, 42.100587], "pop": 1083, "state": "IA", "_id": "52064"} -{"city": "NEW VIENNA", "loc": [-91.097637, 42.567829], "pop": 835, "state": "IA", "_id": "52065"} -{"city": "NORTH BUENA VIST", "loc": [-90.958772, 42.671103], "pop": 404, "state": "IA", "_id": "52066"} -{"city": "PEOSTA", "loc": [-90.809372, 42.443535], "pop": 1565, "state": "IA", "_id": "52068"} -{"city": "PRESTON", "loc": [-90.395382, 42.053854], "pop": 1376, "state": "IA", "_id": "52069"} -{"city": "SABULA", "loc": [-90.19553, 42.073668], "pop": 1174, "state": "IA", "_id": "52070"} -{"city": "SAINT DONATUS", "loc": [-90.484514, 42.33119], "pop": 757, "state": "IA", "_id": "52071"} -{"city": "SAINT OLAF", "loc": [-91.417549, 42.948179], "pop": 466, "state": "IA", "_id": "52072"} -{"city": "SHERRILL", "loc": [-90.811486, 42.617078], "pop": 1381, "state": "IA", "_id": "52073"} -{"city": "SPRAGUEVILLE", "loc": [-90.473044, 42.072742], "pop": 492, "state": "IA", "_id": "52074"} -{"city": "SPRINGBROOK", "loc": [-90.484637, 42.167401], "pop": 497, "state": "IA", "_id": "52075"} -{"city": "STRAWBERRY POINT", "loc": [-91.540926, 42.683225], "pop": 1825, "state": "IA", "_id": "52076"} -{"city": "VOLGA", "loc": [-91.542891, 42.790389], "pop": 562, "state": "IA", "_id": "52077"} -{"city": "WORTHINGTON", "loc": [-91.106919, 42.393001], "pop": 797, "state": "IA", "_id": "52078"} -{"city": "ZWINGLE", "loc": [-90.750666, 42.277486], "pop": 1320, "state": "IA", "_id": "52079"} -{"city": "DECORAH", "loc": [-91.793947, 43.322711], "pop": 13593, "state": "IA", "_id": "52101"} -{"city": "BURR OAK", "loc": [-91.892074, 43.463912], "pop": 484, "state": "IA", "_id": "52131"} -{"city": "CALMAR", "loc": [-91.912579, 43.197402], "pop": 2218, "state": "IA", "_id": "52132"} -{"city": "CASTALIA", "loc": [-91.662904, 43.161179], "pop": 1103, "state": "IA", "_id": "52133"} -{"city": "CHESTER", "loc": [-92.415528, 43.473021], "pop": 571, "state": "IA", "_id": "52134"} -{"city": "CLERMONT", "loc": [-91.655184, 43.01298], "pop": 817, "state": "IA", "_id": "52135"} -{"city": "CRESCO", "loc": [-92.125954, 43.363007], "pop": 5785, "state": "IA", "_id": "52136"} -{"city": "DORCHESTER", "loc": [-91.50765, 43.442136], "pop": 757, "state": "IA", "_id": "52140"} -{"city": "ELGIN", "loc": [-91.645375, 42.954845], "pop": 963, "state": "IA", "_id": "52141"} -{"city": "FAYETTE", "loc": [-91.813887, 42.84941], "pop": 1939, "state": "IA", "_id": "52142"} -{"city": "FORT ATKINSON", "loc": [-91.91336, 43.133932], "pop": 1095, "state": "IA", "_id": "52144"} -{"city": "HARPERS FERRY", "loc": [-91.218822, 43.16977], "pop": 915, "state": "IA", "_id": "52146"} -{"city": "HAWKEYE", "loc": [-91.957764, 42.948702], "pop": 1205, "state": "IA", "_id": "52147"} -{"city": "JACKSON JUNCTION", "loc": [-92.018447, 43.121369], "pop": 369, "state": "IA", "_id": "52150"} -{"city": "LANSING", "loc": [-91.265979, 43.366126], "pop": 1717, "state": "IA", "_id": "52151"} -{"city": "LAWLER", "loc": [-92.143974, 43.093247], "pop": 1450, "state": "IA", "_id": "52154"} -{"city": "LIME SPRINGS", "loc": [-92.273324, 43.45563], "pop": 672, "state": "IA", "_id": "52155"} -{"city": "LUANA", "loc": [-91.458275, 43.049281], "pop": 388, "state": "IA", "_id": "52156"} -{"city": "MC GREGOR", "loc": [-91.188724, 43.023719], "pop": 1344, "state": "IA", "_id": "52157"} -{"city": "MARQUETTE", "loc": [-91.192141, 43.050457], "pop": 276, "state": "IA", "_id": "52158"} -{"city": "MONONA", "loc": [-91.458677, 43.073485], "pop": 4670, "state": "IA", "_id": "52159"} -{"city": "NEW ALBIN", "loc": [-91.294166, 43.489877], "pop": 749, "state": "IA", "_id": "52160"} -{"city": "OSSIAN", "loc": [-91.773431, 43.137858], "pop": 1349, "state": "IA", "_id": "52161"} -{"city": "POSTVILLE", "loc": [-91.547809, 43.003619], "pop": 885, "state": "IA", "_id": "52162"} -{"city": "RANDALIA", "loc": [-91.884628, 42.862551], "pop": 159, "state": "IA", "_id": "52164"} -{"city": "RIDGEWAY", "loc": [-92.003413, 43.297853], "pop": 636, "state": "IA", "_id": "52165"} -{"city": "SAINT LUCAS", "loc": [-91.911561, 43.047703], "pop": 638, "state": "IA", "_id": "52166"} -{"city": "WADENA", "loc": [-91.663865, 42.854956], "pop": 551, "state": "IA", "_id": "52169"} -{"city": "WATERVILLE", "loc": [-91.263045, 43.263608], "pop": 1204, "state": "IA", "_id": "52170"} -{"city": "WAUCOMA", "loc": [-92.03022, 43.043329], "pop": 727, "state": "IA", "_id": "52171"} -{"city": "WAUKON", "loc": [-91.479994, 43.263951], "pop": 6134, "state": "IA", "_id": "52172"} -{"city": "ELDORADO", "loc": [-91.803602, 42.973232], "pop": 3353, "state": "IA", "_id": "52175"} -{"city": "AINSWORTH", "loc": [-91.547173, 41.320226], "pop": 1265, "state": "IA", "_id": "52201"} -{"city": "ALBURNETT", "loc": [-91.639245, 42.158322], "pop": 1038, "state": "IA", "_id": "52202"} -{"city": "AMANA", "loc": [-91.88545, 41.831284], "pop": 450, "state": "IA", "_id": "52203"} -{"city": "ANAMOSA", "loc": [-91.285115, 42.107763], "pop": 7264, "state": "IA", "_id": "52205"} -{"city": "ATKINS", "loc": [-91.875966, 41.988103], "pop": 1347, "state": "IA", "_id": "52206"} -{"city": "BALDWIN", "loc": [-90.819968, 42.073188], "pop": 362, "state": "IA", "_id": "52207"} -{"city": "BELLE PLAINE", "loc": [-92.257381, 41.899159], "pop": 3562, "state": "IA", "_id": "52208"} -{"city": "BLAIRSTOWN", "loc": [-92.082861, 41.906338], "pop": 672, "state": "IA", "_id": "52209"} -{"city": "BRANDON", "loc": [-92.005892, 42.330912], "pop": 717, "state": "IA", "_id": "52210"} -{"city": "BROOKLYN", "loc": [-92.428462, 41.732147], "pop": 2196, "state": "IA", "_id": "52211"} -{"city": "CENTER JUNCTION", "loc": [-91.067013, 42.092585], "pop": 647, "state": "IA", "_id": "52212"} -{"city": "CENTER POINT", "loc": [-91.775764, 42.189844], "pop": 2462, "state": "IA", "_id": "52213"} -{"city": "CENTRAL CITY", "loc": [-91.491399, 42.198234], "pop": 3478, "state": "IA", "_id": "52214"} -{"city": "CHELSEA", "loc": [-92.407815, 41.913132], "pop": 906, "state": "IA", "_id": "52215"} -{"city": "CLARENCE", "loc": [-91.060457, 41.88958], "pop": 1219, "state": "IA", "_id": "52216"} -{"city": "CLUTIER", "loc": [-92.376347, 42.078892], "pop": 497, "state": "IA", "_id": "52217"} -{"city": "COGGON", "loc": [-91.541285, 42.279158], "pop": 1053, "state": "IA", "_id": "52218"} -{"city": "CONROY", "loc": [-91.969479, 41.74255], "pop": 21, "state": "IA", "_id": "52220"} -{"city": "DEEP RIVER", "loc": [-92.364513, 41.571713], "pop": 596, "state": "IA", "_id": "52222"} -{"city": "DELHI", "loc": [-91.270226, 42.427082], "pop": 1530, "state": "IA", "_id": "52223"} -{"city": "DYSART", "loc": [-92.318731, 42.169017], "pop": 1513, "state": "IA", "_id": "52224"} -{"city": "ELBERON", "loc": [-92.331447, 42.008769], "pop": 337, "state": "IA", "_id": "52225"} -{"city": "ELWOOD", "loc": [-90.724609, 41.99083], "pop": 413, "state": "IA", "_id": "52226"} -{"city": "ELY", "loc": [-91.573767, 41.894275], "pop": 1091, "state": "IA", "_id": "52227"} -{"city": "FAIRFAX", "loc": [-91.780102, 41.915304], "pop": 1392, "state": "IA", "_id": "52228"} -{"city": "GARRISON", "loc": [-92.164409, 42.158827], "pop": 872, "state": "IA", "_id": "52229"} -{"city": "HARPER", "loc": [-92.038758, 41.37283], "pop": 324, "state": "IA", "_id": "52231"} -{"city": "HARTWICK", "loc": [-92.353845, 41.808019], "pop": 376, "state": "IA", "_id": "52232"} -{"city": "HIAWATHA", "loc": [-91.67696, 42.042455], "pop": 4966, "state": "IA", "_id": "52233"} -{"city": "HOMESTEAD", "loc": [-91.879471, 41.733858], "pop": 557, "state": "IA", "_id": "52236"} -{"city": "HOPKINTON", "loc": [-91.246051, 42.342164], "pop": 1547, "state": "IA", "_id": "52237"} -{"city": "IOWA CITY", "loc": [-91.511192, 41.654899], "pop": 25049, "state": "IA", "_id": "52240"} -{"city": "CORALVILLE", "loc": [-91.590608, 41.693666], "pop": 12646, "state": "IA", "_id": "52241"} -{"city": "IOWA CITY", "loc": [-91.51507, 41.664916], "pop": 21140, "state": "IA", "_id": "52245"} -{"city": "IOWA CITY", "loc": [-91.566882, 41.643813], "pop": 22869, "state": "IA", "_id": "52246"} -{"city": "KALONA", "loc": [-91.705678, 41.482299], "pop": 3058, "state": "IA", "_id": "52247"} -{"city": "KEOTA", "loc": [-91.964064, 41.350429], "pop": 1439, "state": "IA", "_id": "52248"} -{"city": "KEYSTONE", "loc": [-92.217944, 42.015636], "pop": 1110, "state": "IA", "_id": "52249"} -{"city": "KINROSS", "loc": [-92.001007, 41.460998], "pop": 374, "state": "IA", "_id": "52250"} -{"city": "LADORA", "loc": [-92.187281, 41.756353], "pop": 304, "state": "IA", "_id": "52251"} -{"city": "LISBON", "loc": [-91.386137, 41.921169], "pop": 1581, "state": "IA", "_id": "52253"} -{"city": "LOST NATION", "loc": [-90.82654, 41.974503], "pop": 768, "state": "IA", "_id": "52254"} -{"city": "LOWDEN", "loc": [-90.93822, 41.859393], "pop": 1488, "state": "IA", "_id": "52255"} -{"city": "LUZERNE", "loc": [-92.179334, 41.90614], "pop": 110, "state": "IA", "_id": "52257"} -{"city": "MARENGO", "loc": [-92.072049, 41.786286], "pop": 4856, "state": "IA", "_id": "52301"} -{"city": "MARION", "loc": [-91.594135, 42.041095], "pop": 23227, "state": "IA", "_id": "52302"} -{"city": "MARTELLE", "loc": [-91.321703, 42.003512], "pop": 716, "state": "IA", "_id": "52305"} -{"city": "MECHANICSVILLE", "loc": [-91.27637, 41.903906], "pop": 1516, "state": "IA", "_id": "52306"} -{"city": "MIDDLE AMANA", "loc": [-91.882696, 41.799737], "pop": 902, "state": "IA", "_id": "52307"} -{"city": "MILLERSBURG", "loc": [-92.160843, 41.575099], "pop": 223, "state": "IA", "_id": "52308"} -{"city": "MONMOUTH", "loc": [-90.878551, 42.07716], "pop": 304, "state": "IA", "_id": "52309"} -{"city": "MONTICELLO", "loc": [-91.198901, 42.232591], "pop": 5549, "state": "IA", "_id": "52310"} -{"city": "MOUNT AUBURN", "loc": [-92.159485, 42.255395], "pop": 784, "state": "IA", "_id": "52313"} -{"city": "MOUNT VERNON", "loc": [-91.427371, 41.928741], "pop": 4874, "state": "IA", "_id": "52314"} -{"city": "NEWHALL", "loc": [-91.977888, 41.992685], "pop": 1216, "state": "IA", "_id": "52315"} -{"city": "NORTH ENGLISH", "loc": [-92.089291, 41.524128], "pop": 1287, "state": "IA", "_id": "52316"} -{"city": "NORTH LIBERTY", "loc": [-91.60612, 41.744318], "pop": 3241, "state": "IA", "_id": "52317"} -{"city": "NORWAY", "loc": [-91.89197, 41.899176], "pop": 1317, "state": "IA", "_id": "52318"} -{"city": "OLIN", "loc": [-91.140926, 41.995423], "pop": 1427, "state": "IA", "_id": "52320"} -{"city": "ONSLOW", "loc": [-90.97305, 42.123509], "pop": 322, "state": "IA", "_id": "52321"} -{"city": "OXFORD", "loc": [-91.772418, 41.650687], "pop": 3141, "state": "IA", "_id": "52322"} -{"city": "OXFORD JUNCTION", "loc": [-90.954341, 41.985382], "pop": 909, "state": "IA", "_id": "52323"} -{"city": "PALO", "loc": [-91.787703, 42.036496], "pop": 1528, "state": "IA", "_id": "52324"} -{"city": "PARNELL", "loc": [-92.005159, 41.570161], "pop": 499, "state": "IA", "_id": "52325"} -{"city": "QUASQUETON", "loc": [-91.76795, 42.395551], "pop": 834, "state": "IA", "_id": "52326"} -{"city": "RIVERSIDE", "loc": [-91.57414, 41.475792], "pop": 1514, "state": "IA", "_id": "52327"} -{"city": "ROBINS", "loc": [-91.664841, 42.073092], "pop": 877, "state": "IA", "_id": "52328"} -{"city": "ROWLEY", "loc": [-91.787543, 42.343449], "pop": 1393, "state": "IA", "_id": "52329"} -{"city": "RYAN", "loc": [-91.484838, 42.343643], "pop": 1201, "state": "IA", "_id": "52330"} -{"city": "SCOTCH GROVE", "loc": [-91.083682, 42.164441], "pop": 447, "state": "IA", "_id": "52331"} -{"city": "SHELLSBURG", "loc": [-91.874568, 42.084829], "pop": 1463, "state": "IA", "_id": "52332"} -{"city": "SOLON", "loc": [-91.508609, 41.809913], "pop": 2894, "state": "IA", "_id": "52333"} -{"city": "SOUTH AMANA", "loc": [-91.92626, 41.757609], "pop": 45, "state": "IA", "_id": "52334"} -{"city": "SOUTH ENGLISH", "loc": [-92.102557, 41.46214], "pop": 502, "state": "IA", "_id": "52335"} -{"city": "SPRINGVILLE", "loc": [-91.43935, 42.060741], "pop": 2011, "state": "IA", "_id": "52336"} -{"city": "STANWOOD", "loc": [-91.166202, 41.896318], "pop": 970, "state": "IA", "_id": "52337"} -{"city": "SWISHER", "loc": [-91.673916, 41.826843], "pop": 3101, "state": "IA", "_id": "52338"} -{"city": "TAMA", "loc": [-92.580049, 41.961642], "pop": 3242, "state": "IA", "_id": "52339"} -{"city": "TODDVILLE", "loc": [-91.728981, 42.103932], "pop": 1337, "state": "IA", "_id": "52341"} -{"city": "TOLEDO", "loc": [-92.566206, 42.007696], "pop": 3891, "state": "IA", "_id": "52342"} -{"city": "TORONTO", "loc": [-90.793889, 41.89709], "pop": 660, "state": "IA", "_id": "52343"} -{"city": "VAN HORNE", "loc": [-92.104617, 42.019394], "pop": 1215, "state": "IA", "_id": "52346"} -{"city": "VICTOR", "loc": [-92.284172, 41.729004], "pop": 1092, "state": "IA", "_id": "52347"} -{"city": "VINING", "loc": [-92.369687, 41.979881], "pop": 244, "state": "IA", "_id": "52348"} -{"city": "VINTON", "loc": [-91.989154, 42.174284], "pop": 8276, "state": "IA", "_id": "52349"} -{"city": "WALKER", "loc": [-91.730179, 42.272024], "pop": 1706, "state": "IA", "_id": "52352"} -{"city": "WASHINGTON", "loc": [-91.698671, 41.301453], "pop": 9381, "state": "IA", "_id": "52353"} -{"city": "WATKINS", "loc": [-91.996468, 41.903993], "pop": 485, "state": "IA", "_id": "52354"} -{"city": "WEBSTER", "loc": [-92.176525, 41.436408], "pop": 154, "state": "IA", "_id": "52355"} -{"city": "WELLMAN", "loc": [-91.839953, 41.470032], "pop": 1904, "state": "IA", "_id": "52356"} -{"city": "WEST AMANA", "loc": [-91.933566, 41.802996], "pop": 90, "state": "IA", "_id": "52357"} -{"city": "WEST BRANCH", "loc": [-91.3141, 41.672622], "pop": 3533, "state": "IA", "_id": "52358"} -{"city": "WEST CHESTER", "loc": [-91.861858, 41.361019], "pop": 500, "state": "IA", "_id": "52359"} -{"city": "WILLIAMSBURG", "loc": [-92.024011, 41.639308], "pop": 4304, "state": "IA", "_id": "52361"} -{"city": "WYOMING", "loc": [-90.994024, 42.060352], "pop": 884, "state": "IA", "_id": "52362"} -{"city": "CEDAR RAPIDS", "loc": [-91.655382, 41.9743], "pop": 1924, "state": "IA", "_id": "52401"} -{"city": "CEDAR RAPIDS", "loc": [-91.661222, 42.018778], "pop": 37211, "state": "IA", "_id": "52402"} -{"city": "CEDAR RAPIDS", "loc": [-91.625919, 41.984312], "pop": 25064, "state": "IA", "_id": "52403"} -{"city": "CEDAR RAPIDS", "loc": [-91.685286, 41.952108], "pop": 28262, "state": "IA", "_id": "52404"} -{"city": "CEDAR RAPIDS", "loc": [-91.709816, 41.980422], "pop": 23685, "state": "IA", "_id": "52405"} -{"city": "HIGHLAND CENTER", "loc": [-92.413428, 41.015099], "pop": 29462, "state": "IA", "_id": "52501"} -{"city": "AGENCY", "loc": [-92.316231, 40.995693], "pop": 1233, "state": "IA", "_id": "52530"} -{"city": "ALBIA", "loc": [-92.794619, 41.028718], "pop": 6397, "state": "IA", "_id": "52531"} -{"city": "BATAVIA", "loc": [-92.143059, 40.99048], "pop": 1164, "state": "IA", "_id": "52533"} -{"city": "BEACON", "loc": [-92.72843, 41.287133], "pop": 1559, "state": "IA", "_id": "52534"} -{"city": "BIRMINGHAM", "loc": [-91.953431, 40.862605], "pop": 1023, "state": "IA", "_id": "52535"} -{"city": "BLAKESBURG", "loc": [-92.594037, 40.984763], "pop": 1337, "state": "IA", "_id": "52536"} -{"city": "BLOOMFIELD", "loc": [-92.398712, 40.732368], "pop": 4788, "state": "IA", "_id": "52537"} -{"city": "WEST GROVE", "loc": [-92.549849, 40.719646], "pop": 419, "state": "IA", "_id": "52538"} -{"city": "BRIGHTON", "loc": [-91.828351, 41.154331], "pop": 1686, "state": "IA", "_id": "52540"} -{"city": "CANTRIL", "loc": [-92.046521, 40.654598], "pop": 490, "state": "IA", "_id": "52542"} -{"city": "CEDAR", "loc": [-92.509021, 41.212054], "pop": 242, "state": "IA", "_id": "52543"} -{"city": "CENTERVILLE", "loc": [-92.872826, 40.73259], "pop": 7725, "state": "IA", "_id": "52544"} -{"city": "CHILLICOTHE", "loc": [-92.532762, 41.077041], "pop": 249, "state": "IA", "_id": "52548"} -{"city": "CINCINNATI", "loc": [-92.921898, 40.634384], "pop": 793, "state": "IA", "_id": "52549"} -{"city": "DELTA", "loc": [-92.335912, 41.316682], "pop": 614, "state": "IA", "_id": "52550"} -{"city": "DOUDS", "loc": [-92.045222, 40.803931], "pop": 15, "state": "IA", "_id": "52551"} -{"city": "DRAKESVILLE", "loc": [-92.505109, 40.825332], "pop": 1576, "state": "IA", "_id": "52552"} -{"city": "EDDYVILLE", "loc": [-92.622052, 41.153266], "pop": 1474, "state": "IA", "_id": "52553"} -{"city": "ELDON", "loc": [-92.226754, 40.92613], "pop": 1603, "state": "IA", "_id": "52554"} -{"city": "EXLINE", "loc": [-92.826999, 40.641399], "pop": 432, "state": "IA", "_id": "52555"} -{"city": "FAIRFIELD", "loc": [-91.957611, 41.003943], "pop": 12147, "state": "IA", "_id": "52556"} -{"city": "FLORIS", "loc": [-92.324715, 40.859071], "pop": 1087, "state": "IA", "_id": "52560"} -{"city": "FREMONT", "loc": [-92.436186, 41.211739], "pop": 835, "state": "IA", "_id": "52561"} -{"city": "HEDRICK", "loc": [-92.309725, 41.182578], "pop": 1304, "state": "IA", "_id": "52563"} -{"city": "KEOSAUQUA", "loc": [-91.970434, 40.74235], "pop": 2022, "state": "IA", "_id": "52565"} -{"city": "KIRKVILLE", "loc": [-92.469895, 41.117927], "pop": 670, "state": "IA", "_id": "52566"} -{"city": "LIBERTYVILLE", "loc": [-92.024951, 40.949863], "pop": 658, "state": "IA", "_id": "52567"} -{"city": "MELROSE", "loc": [-93.014514, 40.966466], "pop": 664, "state": "IA", "_id": "52569"} -{"city": "MILTON", "loc": [-92.143793, 40.672143], "pop": 851, "state": "IA", "_id": "52570"} -{"city": "MORAVIA", "loc": [-92.853358, 40.882522], "pop": 1143, "state": "IA", "_id": "52571"} -{"city": "MOULTON", "loc": [-92.683252, 40.686617], "pop": 1073, "state": "IA", "_id": "52572"} -{"city": "MOUNT STERLING", "loc": [-91.902663, 40.645765], "pop": 249, "state": "IA", "_id": "52573"} -{"city": "MYSTIC", "loc": [-92.928437, 40.788268], "pop": 1002, "state": "IA", "_id": "52574"} -{"city": "NUMA", "loc": [-93.013233, 40.674147], "pop": 563, "state": "IA", "_id": "52575"} -{"city": "OLLIE", "loc": [-92.135366, 41.200134], "pop": 631, "state": "IA", "_id": "52576"} -{"city": "OSKALOOSA", "loc": [-92.64393, 41.294205], "pop": 14376, "state": "IA", "_id": "52577"} -{"city": "PACKWOOD", "loc": [-92.066389, 41.122781], "pop": 862, "state": "IA", "_id": "52580"} -{"city": "PLANO", "loc": [-93.038554, 40.775109], "pop": 414, "state": "IA", "_id": "52581"} -{"city": "PROMISE CITY", "loc": [-93.152654, 40.758477], "pop": 370, "state": "IA", "_id": "52583"} -{"city": "PULASKI", "loc": [-92.257973, 40.694081], "pop": 442, "state": "IA", "_id": "52584"} -{"city": "RICHLAND", "loc": [-91.973921, 41.193905], "pop": 1082, "state": "IA", "_id": "52585"} -{"city": "ROSE HILL", "loc": [-92.471958, 41.330918], "pop": 795, "state": "IA", "_id": "52586"} -{"city": "SELMA", "loc": [-92.110672, 40.851936], "pop": 659, "state": "IA", "_id": "52588"} -{"city": "SEYMOUR", "loc": [-93.136826, 40.672416], "pop": 1192, "state": "IA", "_id": "52590"} -{"city": "SIGOURNEY", "loc": [-92.201888, 41.330071], "pop": 3402, "state": "IA", "_id": "52591"} -{"city": "UDELL", "loc": [-92.718512, 40.783573], "pop": 195, "state": "IA", "_id": "52593"} -{"city": "UNIONVILLE", "loc": [-92.693452, 40.841637], "pop": 403, "state": "IA", "_id": "52594"} -{"city": "BURLINGTON", "loc": [-91.116972, 40.808665], "pop": 30564, "state": "IA", "_id": "52601"} -{"city": "ARGYLE", "loc": [-91.563896, 40.565658], "pop": 1899, "state": "IA", "_id": "52619"} -{"city": "BONAPARTE", "loc": [-91.789734, 40.714825], "pop": 903, "state": "IA", "_id": "52620"} -{"city": "CRAWFORDSVILLE", "loc": [-91.541444, 41.209257], "pop": 603, "state": "IA", "_id": "52621"} -{"city": "DANVILLE", "loc": [-91.314027, 40.854046], "pop": 1694, "state": "IA", "_id": "52623"} -{"city": "DENMARK", "loc": [-91.326593, 40.748693], "pop": 869, "state": "IA", "_id": "52624"} -{"city": "DONNELLSON", "loc": [-91.574547, 40.661171], "pop": 1823, "state": "IA", "_id": "52625"} -{"city": "FARMINGTON", "loc": [-91.744719, 40.639715], "pop": 961, "state": "IA", "_id": "52626"} -{"city": "FORT MADISON", "loc": [-91.339829, 40.633008], "pop": 14472, "state": "IA", "_id": "52627"} -{"city": "HILLSBORO", "loc": [-91.711938, 40.837211], "pop": 213, "state": "IA", "_id": "52630"} -{"city": "HOUGHTON", "loc": [-91.6393, 40.775001], "pop": 448, "state": "IA", "_id": "52631"} -{"city": "KEOKUK", "loc": [-91.398234, 40.409434], "pop": 13995, "state": "IA", "_id": "52632"} -{"city": "LOCKRIDGE", "loc": [-91.76454, 41.011867], "pop": 693, "state": "IA", "_id": "52635"} -{"city": "MEDIAPOLIS", "loc": [-91.132142, 41.005932], "pop": 3044, "state": "IA", "_id": "52637"} -{"city": "MIDDLETOWN", "loc": [-91.263106, 40.82963], "pop": 440, "state": "IA", "_id": "52638"} -{"city": "MONTROSE", "loc": [-91.423985, 40.513915], "pop": 1857, "state": "IA", "_id": "52639"} -{"city": "MORNING SUN", "loc": [-91.258146, 41.098424], "pop": 1165, "state": "IA", "_id": "52640"} -{"city": "MOUNT PLEASANT", "loc": [-91.561427, 40.964573], "pop": 11113, "state": "IA", "_id": "52641"} -{"city": "MOUNT UNION", "loc": [-91.413831, 41.03735], "pop": 446, "state": "IA", "_id": "52644"} -{"city": "NEW LONDON", "loc": [-91.398587, 40.916055], "pop": 3627, "state": "IA", "_id": "52645"} -{"city": "OAKVILLE", "loc": [-91.043922, 41.100326], "pop": 709, "state": "IA", "_id": "52646"} -{"city": "OLDS", "loc": [-91.548413, 41.12065], "pop": 711, "state": "IA", "_id": "52647"} -{"city": "SALEM", "loc": [-91.633621, 40.856804], "pop": 786, "state": "IA", "_id": "52649"} -{"city": "SPERRY", "loc": [-91.185047, 40.941939], "pop": 700, "state": "IA", "_id": "52650"} -{"city": "STOCKPORT", "loc": [-91.803497, 40.858933], "pop": 503, "state": "IA", "_id": "52651"} -{"city": "WAPELLO", "loc": [-91.171921, 41.206982], "pop": 4379, "state": "IA", "_id": "52653"} -{"city": "WAYLAND", "loc": [-91.658946, 41.144919], "pop": 1626, "state": "IA", "_id": "52654"} -{"city": "WEST BURLINGTON", "loc": [-91.179894, 40.832081], "pop": 5420, "state": "IA", "_id": "52655"} -{"city": "WEST POINT", "loc": [-91.439733, 40.714986], "pop": 2144, "state": "IA", "_id": "52656"} -{"city": "SAINT PAUL", "loc": [-91.53754, 40.769306], "pop": 623, "state": "IA", "_id": "52657"} -{"city": "WEVER", "loc": [-91.226767, 40.706652], "pop": 546, "state": "IA", "_id": "52658"} -{"city": "WINFIELD", "loc": [-91.437892, 41.125693], "pop": 1349, "state": "IA", "_id": "52659"} -{"city": "YARMOUTH", "loc": [-91.317548, 41.029687], "pop": 339, "state": "IA", "_id": "52660"} -{"city": "ANDOVER", "loc": [-90.238584, 41.992552], "pop": 856, "state": "IA", "_id": "52701"} -{"city": "ATALISSA", "loc": [-91.174764, 41.561443], "pop": 721, "state": "IA", "_id": "52720"} -{"city": "BENNETT", "loc": [-90.965613, 41.735267], "pop": 824, "state": "IA", "_id": "52721"} -{"city": "BETTENDORF", "loc": [-90.494201, 41.550865], "pop": 29785, "state": "IA", "_id": "52722"} -{"city": "BLUE GRASS", "loc": [-90.738015, 41.511168], "pop": 7536, "state": "IA", "_id": "52726"} -{"city": "BRYANT", "loc": [-90.338767, 41.962932], "pop": 252, "state": "IA", "_id": "52727"} -{"city": "CALAMUS", "loc": [-90.741553, 41.811122], "pop": 790, "state": "IA", "_id": "52729"} -{"city": "CAMANCHE", "loc": [-90.270855, 41.788636], "pop": 5013, "state": "IA", "_id": "52730"} -{"city": "CHARLOTTE", "loc": [-90.478226, 41.977859], "pop": 756, "state": "IA", "_id": "52731"} -{"city": "CLINTON", "loc": [-90.207784, 41.851684], "pop": 30723, "state": "IA", "_id": "52732"} -{"city": "COLUMBUS JUNCTIO", "loc": [-91.374198, 41.279911], "pop": 3945, "state": "IA", "_id": "52738"} -{"city": "CONESVILLE", "loc": [-91.346498, 41.380454], "pop": 497, "state": "IA", "_id": "52739"} -{"city": "DE WITT", "loc": [-90.523735, 41.826726], "pop": 6560, "state": "IA", "_id": "52742"} -{"city": "BIG ROCK", "loc": [-90.778332, 41.735013], "pop": 596, "state": "IA", "_id": "52745"} -{"city": "DONAHUE", "loc": [-90.682908, 41.709316], "pop": 685, "state": "IA", "_id": "52746"} -{"city": "DURANT", "loc": [-90.909988, 41.614513], "pop": 2416, "state": "IA", "_id": "52747"} -{"city": "ELDRIDGE", "loc": [-90.563379, 41.663532], "pop": 6947, "state": "IA", "_id": "52748"} -{"city": "GOOSE LAKE", "loc": [-90.381855, 41.979898], "pop": 542, "state": "IA", "_id": "52750"} -{"city": "GRAND MOUND", "loc": [-90.632878, 41.823559], "pop": 1149, "state": "IA", "_id": "52751"} -{"city": "LE CLAIRE", "loc": [-90.362788, 41.608234], "pop": 4176, "state": "IA", "_id": "52753"} -{"city": "LETTS", "loc": [-91.20399, 41.293027], "pop": 1394, "state": "IA", "_id": "52754"} -{"city": "LONE TREE", "loc": [-91.436262, 41.498804], "pop": 2038, "state": "IA", "_id": "52755"} -{"city": "LONG GROVE", "loc": [-90.55344, 41.721327], "pop": 2326, "state": "IA", "_id": "52756"} -{"city": "MOSCOW", "loc": [-91.085888, 41.564558], "pop": 752, "state": "IA", "_id": "52760"} -{"city": "MUSCATINE", "loc": [-91.050928, 41.430378], "pop": 29779, "state": "IA", "_id": "52761"} -{"city": "NEW LIBERTY", "loc": [-90.859922, 41.713229], "pop": 615, "state": "IA", "_id": "52765"} -{"city": "NICHOLS", "loc": [-91.293116, 41.476634], "pop": 797, "state": "IA", "_id": "52766"} -{"city": "PRINCETON", "loc": [-90.370574, 41.685753], "pop": 1529, "state": "IA", "_id": "52768"} -{"city": "STOCKTON", "loc": [-90.847332, 41.558327], "pop": 777, "state": "IA", "_id": "52769"} -{"city": "TIPTON", "loc": [-91.136163, 41.756276], "pop": 5751, "state": "IA", "_id": "52772"} -{"city": "WALCOTT", "loc": [-90.725603, 41.645309], "pop": 705, "state": "IA", "_id": "52773"} -{"city": "WELTON", "loc": [-90.606785, 41.907472], "pop": 539, "state": "IA", "_id": "52774"} -{"city": "WEST LIBERTY", "loc": [-91.266847, 41.570055], "pop": 3552, "state": "IA", "_id": "52776"} -{"city": "WHEATLAND", "loc": [-90.840151, 41.82585], "pop": 1092, "state": "IA", "_id": "52777"} -{"city": "WILTON", "loc": [-91.009958, 41.585801], "pop": 3032, "state": "IA", "_id": "52778"} -{"city": "DAVENPORT", "loc": [-90.61409, 41.516358], "pop": 12547, "state": "IA", "_id": "52802"} -{"city": "DAVENPORT", "loc": [-90.561348, 41.538509], "pop": 25514, "state": "IA", "_id": "52803"} -{"city": "DAVENPORT", "loc": [-90.61147, 41.538603], "pop": 23671, "state": "IA", "_id": "52804"} -{"city": "DAVENPORT", "loc": [-90.603845, 41.573271], "pop": 25480, "state": "IA", "_id": "52806"} -{"city": "DAVENPORT", "loc": [-90.540262, 41.561822], "pop": 8531, "state": "IA", "_id": "52807"} -{"city": "ATCHISON", "loc": [-95.130408, 39.559411], "pop": 13463, "state": "KS", "_id": "66002"} -{"city": "BALDWIN CITY", "loc": [-95.227621, 38.795308], "pop": 6990, "state": "KS", "_id": "66006"} -{"city": "BASEHOR", "loc": [-94.957034, 39.128137], "pop": 5182, "state": "KS", "_id": "66007"} -{"city": "BENDENA", "loc": [-95.176458, 39.717379], "pop": 334, "state": "KS", "_id": "66008"} -{"city": "BLUE MOUND", "loc": [-95.005269, 38.090772], "pop": 484, "state": "KS", "_id": "66010"} -{"city": "LAKE OF THE FORE", "loc": [-94.886098, 39.068455], "pop": 6315, "state": "KS", "_id": "66012"} -{"city": "BUCYRUS", "loc": [-94.742947, 38.687017], "pop": 989, "state": "KS", "_id": "66013"} -{"city": "CENTERVILLE", "loc": [-94.993431, 38.213032], "pop": 381, "state": "KS", "_id": "66014"} -{"city": "COLONY", "loc": [-95.328705, 38.076147], "pop": 806, "state": "KS", "_id": "66015"} -{"city": "CUMMINGS", "loc": [-95.286281, 39.482976], "pop": 647, "state": "KS", "_id": "66016"} -{"city": "DENTON", "loc": [-95.274377, 39.715358], "pop": 378, "state": "KS", "_id": "66017"} -{"city": "DE SOTO", "loc": [-94.964713, 38.956563], "pop": 4167, "state": "KS", "_id": "66018"} -{"city": "EASTON", "loc": [-95.054352, 39.362054], "pop": 2801, "state": "KS", "_id": "66020"} -{"city": "EDGERTON", "loc": [-95.009377, 38.781084], "pop": 2101, "state": "KS", "_id": "66021"} -{"city": "EFFINGHAM", "loc": [-95.41982, 39.502259], "pop": 1303, "state": "KS", "_id": "66023"} -{"city": "EUDORA", "loc": [-95.102206, 38.933043], "pop": 4011, "state": "KS", "_id": "66025"} -{"city": "FONTANA", "loc": [-94.871788, 38.410153], "pop": 774, "state": "KS", "_id": "66026"} -{"city": "FORT LEAVENWORTH", "loc": [-94.926547, 39.348508], "pop": 12630, "state": "KS", "_id": "66027"} -{"city": "GARDNER", "loc": [-94.915675, 38.80754], "pop": 5356, "state": "KS", "_id": "66030"} -{"city": "INDUSTRIAL AIRPO", "loc": [-94.932263, 38.850264], "pop": 723, "state": "KS", "_id": "66031"} -{"city": "GARNETT", "loc": [-95.259368, 38.285907], "pop": 5081, "state": "KS", "_id": "66032"} -{"city": "GREELEY", "loc": [-95.11876, 38.352428], "pop": 671, "state": "KS", "_id": "66033"} -{"city": "HIGHLAND", "loc": [-95.258335, 39.860752], "pop": 1369, "state": "KS", "_id": "66035"} -{"city": "HILLSDALE", "loc": [-94.85837, 38.684463], "pop": 1969, "state": "KS", "_id": "66036"} -{"city": "MILDRED", "loc": [-95.145167, 38.091497], "pop": 441, "state": "KS", "_id": "66039"} -{"city": "LA CYGNE", "loc": [-94.759264, 38.343402], "pop": 1958, "state": "KS", "_id": "66040"} -{"city": "HURON", "loc": [-95.308902, 39.591202], "pop": 890, "state": "KS", "_id": "66041"} -{"city": "LANE", "loc": [-95.097886, 38.437499], "pop": 582, "state": "KS", "_id": "66042"} -{"city": "LANSING", "loc": [-94.899379, 39.250191], "pop": 8145, "state": "KS", "_id": "66043"} -{"city": "LAWRENCE", "loc": [-95.241789, 38.964402], "pop": 29968, "state": "KS", "_id": "66044"} -{"city": "LAWRENCE", "loc": [-95.24203, 38.936925], "pop": 16411, "state": "KS", "_id": "66046"} -{"city": "LAWRENCE", "loc": [-95.277865, 38.940714], "pop": 9234, "state": "KS", "_id": "66047"} -{"city": "LEAVENWORTH", "loc": [-94.933865, 39.301546], "pop": 28028, "state": "KS", "_id": "66048"} -{"city": "LAWRENCE", "loc": [-95.276862, 38.970433], "pop": 12600, "state": "KS", "_id": "66049"} -{"city": "LECOMPTON", "loc": [-95.402519, 39.000125], "pop": 2701, "state": "KS", "_id": "66050"} -{"city": "LINWOOD", "loc": [-94.991388, 39.020627], "pop": 2036, "state": "KS", "_id": "66052"} -{"city": "LOUISBURG", "loc": [-94.682905, 38.607341], "pop": 4720, "state": "KS", "_id": "66053"} -{"city": "MC LOUTH", "loc": [-95.218295, 39.166763], "pop": 2139, "state": "KS", "_id": "66054"} -{"city": "MOUND CITY", "loc": [-94.818632, 38.155954], "pop": 1644, "state": "KS", "_id": "66056"} -{"city": "MUSCOTAH", "loc": [-95.49845, 39.588046], "pop": 631, "state": "KS", "_id": "66058"} -{"city": "NORTONVILLE", "loc": [-95.32357, 39.409532], "pop": 932, "state": "KS", "_id": "66060"} -{"city": "OLATHE", "loc": [-94.820359, 38.886548], "pop": 31694, "state": "KS", "_id": "66061"} -{"city": "OLATHE", "loc": [-94.775168, 38.873287], "pop": 32845, "state": "KS", "_id": "66062"} -{"city": "OSAWATOMIE", "loc": [-94.96199, 38.488803], "pop": 5887, "state": "KS", "_id": "66064"} -{"city": "OSKALOOSA", "loc": [-95.313546, 39.215183], "pop": 1832, "state": "KS", "_id": "66066"} -{"city": "OTTAWA", "loc": [-95.274519, 38.614247], "pop": 13830, "state": "KS", "_id": "66067"} -{"city": "OZAWKIE", "loc": [-95.44039, 39.213648], "pop": 2123, "state": "KS", "_id": "66070"} -{"city": "PAOLA", "loc": [-94.893694, 38.571999], "pop": 9325, "state": "KS", "_id": "66071"} -{"city": "PARKER", "loc": [-94.987074, 38.330311], "pop": 830, "state": "KS", "_id": "66072"} -{"city": "PERRY", "loc": [-95.373122, 39.087524], "pop": 2031, "state": "KS", "_id": "66073"} -{"city": "PLEASANTON", "loc": [-94.70567, 38.182285], "pop": 2050, "state": "KS", "_id": "66075"} -{"city": "POMONA", "loc": [-95.448915, 38.615255], "pop": 1680, "state": "KS", "_id": "66076"} -{"city": "PRINCETON", "loc": [-95.276444, 38.484684], "pop": 650, "state": "KS", "_id": "66078"} -{"city": "RANTOUL", "loc": [-95.123421, 38.566594], "pop": 1195, "state": "KS", "_id": "66079"} -{"city": "RICHMOND", "loc": [-95.248871, 38.407434], "pop": 833, "state": "KS", "_id": "66080"} -{"city": "66081", "loc": [-95.225453, 39.772103], "pop": 288, "state": "KS", "_id": "66081"} -{"city": "SPRING HILL", "loc": [-94.824593, 38.763088], "pop": 4024, "state": "KS", "_id": "66083"} -{"city": "STILWELL", "loc": [-94.664282, 38.790157], "pop": 4836, "state": "KS", "_id": "66085"} -{"city": "TONGANOXIE", "loc": [-95.105055, 39.102614], "pop": 5556, "state": "KS", "_id": "66086"} -{"city": "SEVERANCE", "loc": [-95.066345, 39.778746], "pop": 2422, "state": "KS", "_id": "66087"} -{"city": "VALLEY FALLS", "loc": [-95.466996, 39.34843], "pop": 2000, "state": "KS", "_id": "66088"} -{"city": "WATHENA", "loc": [-94.92546, 39.762505], "pop": 2825, "state": "KS", "_id": "66090"} -{"city": "WELDA", "loc": [-95.309606, 38.168615], "pop": 286, "state": "KS", "_id": "66091"} -{"city": "WELLSVILLE", "loc": [-95.091573, 38.713676], "pop": 2178, "state": "KS", "_id": "66092"} -{"city": "WESTPHALIA", "loc": [-95.466512, 38.171792], "pop": 518, "state": "KS", "_id": "66093"} -{"city": "WHITE CLOUD", "loc": [-95.298203, 39.962717], "pop": 379, "state": "KS", "_id": "66094"} -{"city": "WILLIAMSBURG", "loc": [-95.422615, 38.490166], "pop": 929, "state": "KS", "_id": "66095"} -{"city": "WINCHESTER", "loc": [-95.269584, 39.324531], "pop": 1191, "state": "KS", "_id": "66097"} -{"city": "KANSAS CITY", "loc": [-94.627139, 39.115733], "pop": 16147, "state": "KS", "_id": "66101"} -{"city": "KANSAS CITY", "loc": [-94.669337, 39.113247], "pop": 27909, "state": "KS", "_id": "66102"} -{"city": "ROSEDALE", "loc": [-94.625105, 39.056193], "pop": 14477, "state": "KS", "_id": "66103"} -{"city": "KANSAS CITY", "loc": [-94.679158, 39.137512], "pop": 32071, "state": "KS", "_id": "66104"} -{"city": "KANSAS CITY", "loc": [-94.635646, 39.085025], "pop": 3478, "state": "KS", "_id": "66105"} -{"city": "LAKE QUIVIRA", "loc": [-94.687396, 39.061187], "pop": 25298, "state": "KS", "_id": "66106"} -{"city": "KANSAS CITY", "loc": [-94.785598, 39.143376], "pop": 14656, "state": "KS", "_id": "66109"} -{"city": "KANSAS CITY", "loc": [-94.780593, 39.080332], "pop": 11189, "state": "KS", "_id": "66111"} -{"city": "KANSAS CITY", "loc": [-94.764024, 39.115999], "pop": 11461, "state": "KS", "_id": "66112"} -{"city": "KANSAS CITY", "loc": [-94.614647, 39.114534], "pop": 0, "state": "KS", "_id": "66115"} -{"city": "KANSAS CITY", "loc": [-94.608361, 39.096867], "pop": 0, "state": "KS", "_id": "66118"} -{"city": "COUNTRYSIDE", "loc": [-94.666998, 39.025376], "pop": 17916, "state": "KS", "_id": "66202"} -{"city": "SHAWNEE", "loc": [-94.708303, 39.019802], "pop": 20922, "state": "KS", "_id": "66203"} -{"city": "OVERLAND PARK", "loc": [-94.674769, 38.992488], "pop": 18226, "state": "KS", "_id": "66204"} -{"city": "MISSION", "loc": [-94.631806, 39.031944], "pop": 14871, "state": "KS", "_id": "66205"} -{"city": "LEAWOOD", "loc": [-94.619644, 38.960567], "pop": 9797, "state": "KS", "_id": "66206"} -{"city": "SHAWNEE MISSION", "loc": [-94.645193, 38.957472], "pop": 13863, "state": "KS", "_id": "66207"} -{"city": "PRAIRIE VILLAGE", "loc": [-94.631513, 38.996812], "pop": 22047, "state": "KS", "_id": "66208"} -{"city": "LEAWOOD", "loc": [-94.634047, 38.901798], "pop": 12176, "state": "KS", "_id": "66209"} -{"city": "LENEXA", "loc": [-94.704788, 38.922007], "pop": 17576, "state": "KS", "_id": "66210"} -{"city": "LEAWOOD", "loc": [-94.637991, 38.925956], "pop": 2155, "state": "KS", "_id": "66211"} -{"city": "OVERLAND PARK", "loc": [-94.68414, 38.958954], "pop": 36187, "state": "KS", "_id": "66212"} -{"city": "OVERLAND PARK", "loc": [-94.700344, 38.904899], "pop": 7368, "state": "KS", "_id": "66213"} -{"city": "LENEXA", "loc": [-94.713265, 38.959929], "pop": 12365, "state": "KS", "_id": "66214"} -{"city": "LENEXA", "loc": [-94.739947, 38.963028], "pop": 24051, "state": "KS", "_id": "66215"} -{"city": "SHAWNEE", "loc": [-94.738234, 39.009289], "pop": 19644, "state": "KS", "_id": "66216"} -{"city": "SHAWNEE", "loc": [-94.779663, 39.004835], "pop": 2261, "state": "KS", "_id": "66217"} -{"city": "SHAWNEE", "loc": [-94.823913, 39.017431], "pop": 1828, "state": "KS", "_id": "66218"} -{"city": "LENEXA", "loc": [-94.773145, 38.961896], "pop": 6090, "state": "KS", "_id": "66219"} -{"city": "LENEXA", "loc": [-94.829997, 38.96884], "pop": 626, "state": "KS", "_id": "66220"} -{"city": "STANLEY", "loc": [-94.706745, 38.85272], "pop": 1186, "state": "KS", "_id": "66221"} -{"city": "STANLEY", "loc": [-94.664467, 38.848477], "pop": 3539, "state": "KS", "_id": "66223"} -{"city": "STANLEY", "loc": [-94.628903, 38.867526], "pop": 713, "state": "KS", "_id": "66224"} -{"city": "SHAWNEE", "loc": [-94.873017, 38.997764], "pop": 2613, "state": "KS", "_id": "66226"} -{"city": "LENEXA", "loc": [-94.840082, 38.99416], "pop": 273, "state": "KS", "_id": "66227"} -{"city": "ALMA", "loc": [-96.292323, 39.009206], "pop": 2186, "state": "KS", "_id": "66401"} -{"city": "AUBURN", "loc": [-95.819938, 38.916673], "pop": 2157, "state": "KS", "_id": "66402"} -{"city": "AXTELL", "loc": [-96.267559, 39.870676], "pop": 669, "state": "KS", "_id": "66403"} -{"city": "BAILEYVILLE", "loc": [-96.180136, 39.881621], "pop": 613, "state": "KS", "_id": "66404"} -{"city": "BEATTIE", "loc": [-96.428618, 39.89884], "pop": 739, "state": "KS", "_id": "66406"} -{"city": "BELVUE", "loc": [-96.186635, 39.2273], "pop": 321, "state": "KS", "_id": "66407"} -{"city": "BERN", "loc": [-95.961028, 39.957051], "pop": 457, "state": "KS", "_id": "66408"} -{"city": "BERRYTON", "loc": [-95.582487, 38.944182], "pop": 1939, "state": "KS", "_id": "66409"} -{"city": "BLUE RAPIDS", "loc": [-96.63595, 39.674974], "pop": 1552, "state": "KS", "_id": "66411"} -{"city": "BREMEN", "loc": [-96.745846, 39.877473], "pop": 347, "state": "KS", "_id": "66412"} -{"city": "BURLINGAME", "loc": [-95.840017, 38.763395], "pop": 1994, "state": "KS", "_id": "66413"} -{"city": "CARBONDALE", "loc": [-95.687083, 38.820622], "pop": 2502, "state": "KS", "_id": "66414"} -{"city": "CENTRALIA", "loc": [-96.148604, 39.738051], "pop": 782, "state": "KS", "_id": "66415"} -{"city": "CIRCLEVILLE", "loc": [-95.851708, 39.515472], "pop": 466, "state": "KS", "_id": "66416"} -{"city": "CORNING", "loc": [-96.077589, 39.657015], "pop": 656, "state": "KS", "_id": "66417"} -{"city": "DELIA", "loc": [-95.960805, 39.265453], "pop": 557, "state": "KS", "_id": "66418"} -{"city": "DENISON", "loc": [-95.632851, 39.415307], "pop": 604, "state": "KS", "_id": "66419"} -{"city": "DOVER", "loc": [-95.898547, 39.019223], "pop": 1220, "state": "KS", "_id": "66420"} -{"city": "EMMETT", "loc": [-96.059325, 39.304819], "pop": 342, "state": "KS", "_id": "66422"} -{"city": "ESKRIDGE", "loc": [-96.101583, 38.851361], "pop": 771, "state": "KS", "_id": "66423"} -{"city": "EVEREST", "loc": [-95.413802, 39.687972], "pop": 525, "state": "KS", "_id": "66424"} -{"city": "FAIRVIEW", "loc": [-95.711012, 39.844504], "pop": 712, "state": "KS", "_id": "66425"} -{"city": "WINIFRED", "loc": [-96.428323, 39.718996], "pop": 1418, "state": "KS", "_id": "66427"} -{"city": "GOFF", "loc": [-95.957394, 39.665388], "pop": 537, "state": "KS", "_id": "66428"} -{"city": "GRANTVILLE", "loc": [-95.539733, 39.097208], "pop": 753, "state": "KS", "_id": "66429"} -{"city": "HARVEYVILLE", "loc": [-95.996239, 38.857413], "pop": 1065, "state": "KS", "_id": "66431"} -{"city": "HAVENSVILLE", "loc": [-96.076884, 39.494197], "pop": 417, "state": "KS", "_id": "66432"} -{"city": "HERKIMER", "loc": [-96.746861, 39.956337], "pop": 270, "state": "KS", "_id": "66433"} -{"city": "RESERVE", "loc": [-95.528905, 39.871563], "pop": 5297, "state": "KS", "_id": "66434"} -{"city": "HOLTON", "loc": [-95.752498, 39.443624], "pop": 5625, "state": "KS", "_id": "66436"} -{"city": "HOME", "loc": [-96.529201, 39.854542], "pop": 362, "state": "KS", "_id": "66438"} -{"city": "HORTON", "loc": [-95.529858, 39.678883], "pop": 2556, "state": "KS", "_id": "66439"} -{"city": "HOYT", "loc": [-95.686742, 39.255249], "pop": 1812, "state": "KS", "_id": "66440"} -{"city": "JUNCTION CITY", "loc": [-96.839553, 39.029913], "pop": 24386, "state": "KS", "_id": "66441"} -{"city": "FORT RILEY", "loc": [-96.787338, 39.061947], "pop": 5363, "state": "KS", "_id": "66442"} -{"city": "LEONARDVILLE", "loc": [-96.831223, 39.365151], "pop": 1274, "state": "KS", "_id": "66449"} -{"city": "LOUISVILLE", "loc": [-96.316954, 39.267048], "pop": 318, "state": "KS", "_id": "66450"} -{"city": "LYNDON", "loc": [-95.680226, 38.635649], "pop": 1879, "state": "KS", "_id": "66451"} -{"city": "MANHATTAN", "loc": [-96.585776, 39.193757], "pop": 50178, "state": "KS", "_id": "66502"} -{"city": "MAPLE HILL", "loc": [-96.018727, 39.069456], "pop": 796, "state": "KS", "_id": "66507"} -{"city": "MARYSVILLE", "loc": [-96.642183, 39.842827], "pop": 4104, "state": "KS", "_id": "66508"} -{"city": "MAYETTA", "loc": [-95.692753, 39.348915], "pop": 1132, "state": "KS", "_id": "66509"} -{"city": "MELVERN", "loc": [-95.628986, 38.50266], "pop": 856, "state": "KS", "_id": "66510"} -{"city": "MERIDEN", "loc": [-95.547637, 39.203832], "pop": 2299, "state": "KS", "_id": "66512"} -{"city": "MILFORD", "loc": [-96.91005, 39.169173], "pop": 704, "state": "KS", "_id": "66514"} -{"city": "MORRILL", "loc": [-95.712048, 39.935592], "pop": 591, "state": "KS", "_id": "66515"} -{"city": "NETAWAKA", "loc": [-95.727004, 39.606298], "pop": 371, "state": "KS", "_id": "66516"} -{"city": "OGDEN", "loc": [-96.700077, 39.119219], "pop": 2049, "state": "KS", "_id": "66517"} -{"city": "OKETO", "loc": [-96.623525, 39.95984], "pop": 318, "state": "KS", "_id": "66518"} -{"city": "OLSBURG", "loc": [-96.600249, 39.412506], "pop": 581, "state": "KS", "_id": "66520"} -{"city": "DULUTH", "loc": [-96.176105, 39.472974], "pop": 1358, "state": "KS", "_id": "66521"} -{"city": "ONEIDA", "loc": [-95.958097, 39.866087], "pop": 247, "state": "KS", "_id": "66522"} -{"city": "OSAGE CITY", "loc": [-95.830303, 38.626896], "pop": 3627, "state": "KS", "_id": "66523"} -{"city": "OVERBROOK", "loc": [-95.561632, 38.792173], "pop": 1630, "state": "KS", "_id": "66524"} -{"city": "PAXICO", "loc": [-96.181777, 39.080285], "pop": 1153, "state": "KS", "_id": "66526"} -{"city": "POWHATTAN", "loc": [-95.675111, 39.71769], "pop": 913, "state": "KS", "_id": "66527"} -{"city": "QUENEMO", "loc": [-95.53619, 38.578504], "pop": 500, "state": "KS", "_id": "66528"} -{"city": "RILEY", "loc": [-96.811824, 39.122421], "pop": 14793, "state": "KS", "_id": "66531"} -{"city": "LEONA", "loc": [-95.386236, 39.812721], "pop": 671, "state": "KS", "_id": "66532"} -{"city": "ROSSVILLE", "loc": [-95.9553, 39.145114], "pop": 1583, "state": "KS", "_id": "66533"} -{"city": "SABETHA", "loc": [-95.811271, 39.89929], "pop": 3242, "state": "KS", "_id": "66534"} -{"city": "SAINT GEORGE", "loc": [-96.434488, 39.210806], "pop": 1904, "state": "KS", "_id": "66535"} -{"city": "SAINT MARYS", "loc": [-96.068277, 39.198674], "pop": 2316, "state": "KS", "_id": "66536"} -{"city": "SCRANTON", "loc": [-95.74799, 38.787998], "pop": 1158, "state": "KS", "_id": "66537"} -{"city": "KELLY", "loc": [-96.059489, 39.839804], "pop": 3336, "state": "KS", "_id": "66538"} -{"city": "SILVER LAKE", "loc": [-95.855182, 39.1102], "pop": 2152, "state": "KS", "_id": "66539"} -{"city": "SOLDIER", "loc": [-95.965178, 39.501254], "pop": 578, "state": "KS", "_id": "66540"} -{"city": "SUMMERFIELD", "loc": [-96.327909, 39.979847], "pop": 271, "state": "KS", "_id": "66541"} -{"city": "TECUMSEH", "loc": [-95.537926, 39.021664], "pop": 1059, "state": "KS", "_id": "66542"} -{"city": "VASSAR", "loc": [-95.581278, 38.669127], "pop": 849, "state": "KS", "_id": "66543"} -{"city": "VLIETS", "loc": [-96.281642, 39.705044], "pop": 469, "state": "KS", "_id": "66544"} -{"city": "66545", "loc": [-96.339281, 39.705219], "pop": 102, "state": "KS", "_id": "66545"} -{"city": "WAKARUSA", "loc": [-95.701464, 38.904543], "pop": 855, "state": "KS", "_id": "66546"} -{"city": "WAMEGO", "loc": [-96.315344, 39.210001], "pop": 5200, "state": "KS", "_id": "66547"} -{"city": "WATERVILLE", "loc": [-96.749781, 39.697088], "pop": 1116, "state": "KS", "_id": "66548"} -{"city": "BLAINE", "loc": [-96.411402, 39.416545], "pop": 1373, "state": "KS", "_id": "66549"} -{"city": "WETMORE", "loc": [-95.823118, 39.642761], "pop": 546, "state": "KS", "_id": "66550"} -{"city": "ONAGA", "loc": [-96.314753, 39.510969], "pop": 238, "state": "KS", "_id": "66551"} -{"city": "WHITING", "loc": [-95.61584, 39.597396], "pop": 380, "state": "KS", "_id": "66552"} -{"city": "RANDOLPH", "loc": [-96.782842, 39.487939], "pop": 608, "state": "KS", "_id": "66554"} -{"city": "TOPEKA", "loc": [-95.680212, 39.055344], "pop": 2250, "state": "KS", "_id": "66603"} -{"city": "TOPEKA", "loc": [-95.717831, 39.040549], "pop": 23913, "state": "KS", "_id": "66604"} -{"city": "TOPEKA", "loc": [-95.643894, 39.015076], "pop": 20138, "state": "KS", "_id": "66605"} -{"city": "TOPEKA", "loc": [-95.709458, 39.058345], "pop": 13053, "state": "KS", "_id": "66606"} -{"city": "TOPEKA", "loc": [-95.644858, 39.042111], "pop": 9850, "state": "KS", "_id": "66607"} -{"city": "TOPEKA", "loc": [-95.686651, 39.085812], "pop": 7739, "state": "KS", "_id": "66608"} -{"city": "TOPEKA", "loc": [-95.668069, 38.991899], "pop": 6252, "state": "KS", "_id": "66609"} -{"city": "TOPEKA", "loc": [-95.746061, 38.982213], "pop": 4656, "state": "KS", "_id": "66610"} -{"city": "TOPEKA", "loc": [-95.69815, 39.014152], "pop": 10194, "state": "KS", "_id": "66611"} -{"city": "TOPEKA", "loc": [-95.681806, 39.042714], "pop": 2949, "state": "KS", "_id": "66612"} -{"city": "TOPEKA", "loc": [-95.746883, 39.015403], "pop": 26238, "state": "KS", "_id": "66614"} -{"city": "TOPEKA", "loc": [-95.790561, 39.04458], "pop": 677, "state": "KS", "_id": "66615"} -{"city": "TOPEKA", "loc": [-95.641302, 39.064479], "pop": 6538, "state": "KS", "_id": "66616"} -{"city": "TOPEKA", "loc": [-95.638388, 39.127098], "pop": 7779, "state": "KS", "_id": "66617"} -{"city": "TOPEKA", "loc": [-95.70231, 39.132853], "pop": 5436, "state": "KS", "_id": "66618"} -{"city": "PAULINE", "loc": [-95.700728, 38.942859], "pop": 2952, "state": "KS", "_id": "66619"} -{"city": "HIATTVILLE", "loc": [-94.704221, 37.834159], "pop": 11192, "state": "KS", "_id": "66701"} -{"city": "ALTOONA", "loc": [-95.64833, 37.519669], "pop": 905, "state": "KS", "_id": "66710"} -{"city": "ARCADIA", "loc": [-94.654782, 37.634113], "pop": 664, "state": "KS", "_id": "66711"} -{"city": "BAXTER SPRINGS", "loc": [-94.739276, 37.028057], "pop": 5434, "state": "KS", "_id": "66713"} -{"city": "BENEDICT", "loc": [-95.624213, 37.651401], "pop": 815, "state": "KS", "_id": "66714"} -{"city": "BRONSON", "loc": [-95.030345, 37.921356], "pop": 702, "state": "KS", "_id": "66716"} -{"city": "BUFFALO", "loc": [-95.701417, 37.701057], "pop": 455, "state": "KS", "_id": "66717"} -{"city": "CHANUTE", "loc": [-95.456962, 37.674928], "pop": 10852, "state": "KS", "_id": "66720"} -{"city": "CHEROKEE", "loc": [-94.842412, 37.369413], "pop": 1397, "state": "KS", "_id": "66724"} -{"city": "HALLOWELL", "loc": [-94.850227, 37.168992], "pop": 4750, "state": "KS", "_id": "66725"} -{"city": "COYVILLE", "loc": [-95.917164, 37.659167], "pop": 331, "state": "KS", "_id": "66727"} -{"city": "CRESTLINE", "loc": [-94.678275, 37.161226], "pop": 478, "state": "KS", "_id": "66728"} -{"city": "ELSMORE", "loc": [-95.154645, 37.80353], "pop": 293, "state": "KS", "_id": "66732"} -{"city": "ERIE", "loc": [-95.25138, 37.604395], "pop": 2572, "state": "KS", "_id": "66733"} -{"city": "FARLINGTON", "loc": [-94.847928, 37.6163], "pop": 519, "state": "KS", "_id": "66734"} -{"city": "LAFONTAINE", "loc": [-95.822898, 37.525622], "pop": 3747, "state": "KS", "_id": "66736"} -{"city": "FULTON", "loc": [-94.739591, 37.988279], "pop": 404, "state": "KS", "_id": "66738"} -{"city": "GALENA", "loc": [-94.655743, 37.063237], "pop": 5961, "state": "KS", "_id": "66739"} -{"city": "GALESBURG", "loc": [-95.310772, 37.469346], "pop": 932, "state": "KS", "_id": "66740"} -{"city": "GARLAND", "loc": [-94.672055, 37.717363], "pop": 377, "state": "KS", "_id": "66741"} -{"city": "GIRARD", "loc": [-94.856913, 37.509129], "pop": 3707, "state": "KS", "_id": "66743"} -{"city": "HEPLER", "loc": [-94.989225, 37.657558], "pop": 229, "state": "KS", "_id": "66746"} -{"city": "HUMBOLDT", "loc": [-95.422006, 37.80446], "pop": 3247, "state": "KS", "_id": "66748"} -{"city": "CARLYLE", "loc": [-95.402146, 37.928244], "pop": 7721, "state": "KS", "_id": "66749"} -{"city": "LA HARPE", "loc": [-95.323192, 37.92239], "pop": 1966, "state": "KS", "_id": "66751"} -{"city": "MC CUNE", "loc": [-95.037591, 37.346754], "pop": 1252, "state": "KS", "_id": "66753"} -{"city": "MAPLETON", "loc": [-94.873519, 38.022821], "pop": 351, "state": "KS", "_id": "66754"} -{"city": "MORAN", "loc": [-95.164733, 37.934159], "pop": 1214, "state": "KS", "_id": "66755"} -{"city": "MULBERRY", "loc": [-94.683902, 37.544197], "pop": 3096, "state": "KS", "_id": "66756"} -{"city": "NEODESHA", "loc": [-95.676478, 37.425709], "pop": 3664, "state": "KS", "_id": "66757"} -{"city": "NEOSHO FALLS", "loc": [-95.549732, 37.966883], "pop": 392, "state": "KS", "_id": "66758"} -{"city": "NEW ALBANY", "loc": [-95.912594, 37.544436], "pop": 372, "state": "KS", "_id": "66759"} -{"city": "PIQUA", "loc": [-95.590026, 37.872847], "pop": 206, "state": "KS", "_id": "66761"} -{"city": "RADLEY", "loc": [-94.70225, 37.413156], "pop": 24845, "state": "KS", "_id": "66762"} -{"city": "PRESCOTT", "loc": [-94.700841, 38.071802], "pop": 600, "state": "KS", "_id": "66767"} -{"city": "REDFIELD", "loc": [-94.828988, 37.856046], "pop": 1224, "state": "KS", "_id": "66769"} -{"city": "RIVERTON", "loc": [-94.678801, 37.099262], "pop": 564, "state": "KS", "_id": "66770"} -{"city": "SAINT PAUL", "loc": [-95.168784, 37.518046], "pop": 936, "state": "KS", "_id": "66771"} -{"city": "SAVONBURG", "loc": [-95.154532, 37.751725], "pop": 197, "state": "KS", "_id": "66772"} -{"city": "CARONA", "loc": [-94.851961, 37.271102], "pop": 1552, "state": "KS", "_id": "66773"} -{"city": "STARK", "loc": [-95.138794, 37.681111], "pop": 246, "state": "KS", "_id": "66775"} -{"city": "THAYER", "loc": [-95.467083, 37.452657], "pop": 1349, "state": "KS", "_id": "66776"} -{"city": "TORONTO", "loc": [-95.936818, 37.795343], "pop": 659, "state": "KS", "_id": "66777"} -{"city": "TREECE", "loc": [-94.869047, 37.039883], "pop": 528, "state": "KS", "_id": "66778"} -{"city": "UNIONTOWN", "loc": [-94.981366, 37.811681], "pop": 825, "state": "KS", "_id": "66779"} -{"city": "WALNUT", "loc": [-95.045531, 37.59674], "pop": 383, "state": "KS", "_id": "66780"} -{"city": "LAWTON", "loc": [-94.743909, 37.298173], "pop": 1515, "state": "KS", "_id": "66781"} -{"city": "YATES CENTER", "loc": [-95.72894, 37.880125], "pop": 2859, "state": "KS", "_id": "66783"} -{"city": "EMPORIA", "loc": [-96.187145, 38.418405], "pop": 29401, "state": "KS", "_id": "66801"} -{"city": "ADMIRE", "loc": [-96.101657, 38.639297], "pop": 172, "state": "KS", "_id": "66830"} -{"city": "BUSHONG", "loc": [-96.249968, 38.637522], "pop": 88, "state": "KS", "_id": "66833"} -{"city": "ALTA VISTA", "loc": [-96.479983, 38.86357], "pop": 629, "state": "KS", "_id": "66834"} -{"city": "AMERICUS", "loc": [-96.260753, 38.509803], "pop": 1491, "state": "KS", "_id": "66835"} -{"city": "BURDICK", "loc": [-96.83963, 38.567369], "pop": 230, "state": "KS", "_id": "66838"} -{"city": "STRAWN", "loc": [-95.741162, 38.201596], "pop": 4404, "state": "KS", "_id": "66839"} -{"city": "BURNS", "loc": [-96.863435, 38.12216], "pop": 593, "state": "KS", "_id": "66840"} -{"city": "CASSODAY", "loc": [-96.674565, 38.029801], "pop": 351, "state": "KS", "_id": "66842"} -{"city": "CLEMENTS", "loc": [-96.775444, 38.279757], "pop": 200, "state": "KS", "_id": "66843"} -{"city": "COTTONWOOD FALLS", "loc": [-96.541849, 38.356538], "pop": 1183, "state": "KS", "_id": "66845"} -{"city": "DUNLAP", "loc": [-96.497294, 38.667657], "pop": 3831, "state": "KS", "_id": "66846"} -{"city": "66847", "loc": [-96.836125, 38.650627], "pop": 260, "state": "KS", "_id": "66847"} -{"city": "DWIGHT", "loc": [-96.580215, 38.838902], "pop": 569, "state": "KS", "_id": "66849"} -{"city": "ELMDALE", "loc": [-96.667464, 38.377924], "pop": 168, "state": "KS", "_id": "66850"} -{"city": "FLORENCE", "loc": [-96.934561, 38.241536], "pop": 828, "state": "KS", "_id": "66851"} -{"city": "GRIDLEY", "loc": [-95.887351, 38.101234], "pop": 638, "state": "KS", "_id": "66852"} -{"city": "HAMILTON", "loc": [-96.169133, 37.979122], "pop": 600, "state": "KS", "_id": "66853"} -{"city": "HARTFORD", "loc": [-95.999718, 38.28326], "pop": 1006, "state": "KS", "_id": "66854"} -{"city": "LEBO", "loc": [-95.822174, 38.416085], "pop": 1708, "state": "KS", "_id": "66856"} -{"city": "LE ROY", "loc": [-95.622655, 38.087429], "pop": 795, "state": "KS", "_id": "66857"} -{"city": "ANTELOPE", "loc": [-96.96123, 38.481037], "pop": 562, "state": "KS", "_id": "66858"} -{"city": "LOST SPRINGS", "loc": [-96.979872, 38.565723], "pop": 241, "state": "KS", "_id": "66859"} -{"city": "MADISON", "loc": [-96.121283, 38.127755], "pop": 1430, "state": "KS", "_id": "66860"} -{"city": "MARION", "loc": [-97.02045, 38.355444], "pop": 3196, "state": "KS", "_id": "66861"} -{"city": "MATFIELD GREEN", "loc": [-96.554105, 38.144768], "pop": 143, "state": "KS", "_id": "66862"} -{"city": "NEOSHO RAPIDS", "loc": [-96.016824, 38.394762], "pop": 865, "state": "KS", "_id": "66864"} -{"city": "OLPE", "loc": [-96.189056, 38.257774], "pop": 1156, "state": "KS", "_id": "66865"} -{"city": "PEABODY", "loc": [-97.118386, 38.173746], "pop": 1927, "state": "KS", "_id": "66866"} -{"city": "READING", "loc": [-95.98949, 38.529012], "pop": 553, "state": "KS", "_id": "66868"} -{"city": "STRONG CITY", "loc": [-96.517192, 38.41292], "pop": 1149, "state": "KS", "_id": "66869"} -{"city": "VIRGIL", "loc": [-96.032935, 37.897594], "pop": 381, "state": "KS", "_id": "66870"} -{"city": "WAVERLY", "loc": [-95.598201, 38.378214], "pop": 1112, "state": "KS", "_id": "66871"} -{"city": "WHITE CITY", "loc": [-96.763677, 38.78902], "pop": 983, "state": "KS", "_id": "66872"} -{"city": "WILSEY", "loc": [-96.670407, 38.633449], "pop": 325, "state": "KS", "_id": "66873"} -{"city": "RICE", "loc": [-97.662735, 39.559427], "pop": 7733, "state": "KS", "_id": "66901"} -{"city": "AGENDA", "loc": [-97.426983, 39.702868], "pop": 198, "state": "KS", "_id": "66930"} -{"city": "AMES", "loc": [-97.538245, 39.533923], "pop": 161, "state": "KS", "_id": "66931"} -{"city": "ATHOL", "loc": [-98.907733, 39.771872], "pop": 164, "state": "KS", "_id": "66932"} -{"city": "BARNES", "loc": [-96.867574, 39.684114], "pop": 374, "state": "KS", "_id": "66933"} -{"city": "BELLEVILLE", "loc": [-97.629016, 39.824054], "pop": 3863, "state": "KS", "_id": "66935"} -{"city": "BURR OAK", "loc": [-98.349911, 39.893177], "pop": 583, "state": "KS", "_id": "66936"} -{"city": "CLIFTON", "loc": [-97.261104, 39.620121], "pop": 853, "state": "KS", "_id": "66937"} -{"city": "CLYDE", "loc": [-97.407999, 39.575842], "pop": 1164, "state": "KS", "_id": "66938"} -{"city": "COURTLAND", "loc": [-97.889956, 39.78509], "pop": 487, "state": "KS", "_id": "66939"} -{"city": "CUBA", "loc": [-97.449623, 39.797482], "pop": 342, "state": "KS", "_id": "66940"} -{"city": "ESBON", "loc": [-98.446222, 39.756186], "pop": 342, "state": "KS", "_id": "66941"} -{"city": "FORMOSO", "loc": [-97.988909, 39.779467], "pop": 223, "state": "KS", "_id": "66942"} -{"city": "GREENLEAF", "loc": [-96.977465, 39.706145], "pop": 564, "state": "KS", "_id": "66943"} -{"city": "HADDAM", "loc": [-97.308142, 39.851969], "pop": 333, "state": "KS", "_id": "66944"} -{"city": "HANOVER", "loc": [-96.868947, 39.89266], "pop": 1278, "state": "KS", "_id": "66945"} -{"city": "HOLLENBERG", "loc": [-96.973509, 39.959987], "pop": 146, "state": "KS", "_id": "66946"} -{"city": "JAMESTOWN", "loc": [-97.863082, 39.602144], "pop": 430, "state": "KS", "_id": "66948"} -{"city": "IONIA", "loc": [-98.125603, 39.669686], "pop": 752, "state": "KS", "_id": "66949"} -{"city": "KENSINGTON", "loc": [-99.030848, 39.769179], "pop": 665, "state": "KS", "_id": "66951"} -{"city": "BELLAIRE", "loc": [-98.558148, 39.812839], "pop": 526, "state": "KS", "_id": "66952"} -{"city": "LINN", "loc": [-97.085412, 39.684687], "pop": 666, "state": "KS", "_id": "66953"} -{"city": "MAHASKA", "loc": [-97.345272, 39.984502], "pop": 142, "state": "KS", "_id": "66955"} -{"city": "MANKATO", "loc": [-98.215227, 39.783259], "pop": 1929, "state": "KS", "_id": "66956"} -{"city": "MORROWVILLE", "loc": [-97.182521, 39.861551], "pop": 462, "state": "KS", "_id": "66958"} -{"city": "MUNDEN", "loc": [-97.540302, 39.927219], "pop": 266, "state": "KS", "_id": "66959"} -{"city": "NARKA", "loc": [-97.424332, 39.958162], "pop": 205, "state": "KS", "_id": "66960"} -{"city": "NORWAY", "loc": [-97.811926, 39.695305], "pop": 287, "state": "KS", "_id": "66961"} -{"city": "PALMER", "loc": [-97.112214, 39.619165], "pop": 276, "state": "KS", "_id": "66962"} -{"city": "RANDALL", "loc": [-98.066054, 39.62859], "pop": 190, "state": "KS", "_id": "66963"} -{"city": "REPUBLIC", "loc": [-97.843456, 39.937572], "pop": 297, "state": "KS", "_id": "66964"} -{"city": "SCANDIA", "loc": [-97.778645, 39.793861], "pop": 537, "state": "KS", "_id": "66966"} -{"city": "SMITH CENTER", "loc": [-98.784243, 39.804217], "pop": 2848, "state": "KS", "_id": "66967"} -{"city": "WASHINGTON", "loc": [-97.048368, 39.82234], "pop": 1979, "state": "KS", "_id": "66968"} -{"city": "WEBBER", "loc": [-97.997793, 39.924496], "pop": 232, "state": "KS", "_id": "66970"} -{"city": "ANDALE", "loc": [-97.640712, 37.782686], "pop": 1251, "state": "KS", "_id": "67001"} -{"city": "ANDOVER", "loc": [-97.117901, 37.698531], "pop": 5384, "state": "KS", "_id": "67002"} -{"city": "ANTHONY", "loc": [-98.028499, 37.151206], "pop": 2930, "state": "KS", "_id": "67003"} -{"city": "ARGONIA", "loc": [-97.755678, 37.283966], "pop": 949, "state": "KS", "_id": "67004"} -{"city": "ARKANSAS CITY", "loc": [-97.035658, 37.067593], "pop": 17740, "state": "KS", "_id": "67005"} -{"city": "ATLANTA", "loc": [-96.766135, 37.434309], "pop": 320, "state": "KS", "_id": "67008"} -{"city": "ATTICA", "loc": [-98.226425, 37.25277], "pop": 1125, "state": "KS", "_id": "67009"} -{"city": "AUGUSTA", "loc": [-96.964772, 37.683606], "pop": 11306, "state": "KS", "_id": "67010"} -{"city": "BEAUMONT", "loc": [-96.63864, 37.607871], "pop": 85, "state": "KS", "_id": "67012"} -{"city": "BELLE PLAINE", "loc": [-97.285221, 37.405155], "pop": 2784, "state": "KS", "_id": "67013"} -{"city": "67014", "loc": [-98.196403, 37.512029], "pop": 105, "state": "KS", "_id": "67014"} -{"city": "BENTLEY", "loc": [-97.516381, 37.879582], "pop": 895, "state": "KS", "_id": "67016"} -{"city": "BENTON", "loc": [-97.097117, 37.794615], "pop": 2122, "state": "KS", "_id": "67017"} -{"city": "BLUFF CITY", "loc": [-97.875412, 37.083846], "pop": 216, "state": "KS", "_id": "67018"} -{"city": "BURDEN", "loc": [-96.757044, 37.320856], "pop": 698, "state": "KS", "_id": "67019"} -{"city": "BURRTON", "loc": [-97.666649, 38.026128], "pop": 1149, "state": "KS", "_id": "67020"} -{"city": "BYERS", "loc": [-98.901662, 37.784693], "pop": 208, "state": "KS", "_id": "67021"} -{"city": "CALDWELL", "loc": [-97.624659, 37.045241], "pop": 1777, "state": "KS", "_id": "67022"} -{"city": "CAMBRIDGE", "loc": [-96.66388, 37.358348], "pop": 316, "state": "KS", "_id": "67023"} -{"city": "CEDAR VALE", "loc": [-96.470133, 37.126487], "pop": 1267, "state": "KS", "_id": "67024"} -{"city": "CHENEY", "loc": [-97.768638, 37.635299], "pop": 2497, "state": "KS", "_id": "67025"} -{"city": "CLEARWATER", "loc": [-97.508179, 37.507592], "pop": 2453, "state": "KS", "_id": "67026"} -{"city": "COATS", "loc": [-98.850398, 37.512502], "pop": 190, "state": "KS", "_id": "67028"} -{"city": "COLDWATER", "loc": [-99.311484, 37.247915], "pop": 1317, "state": "KS", "_id": "67029"} -{"city": "COLWICH", "loc": [-97.540518, 37.77824], "pop": 1745, "state": "KS", "_id": "67030"} -{"city": "CONWAY SPRINGS", "loc": [-97.628358, 37.390272], "pop": 2241, "state": "KS", "_id": "67031"} -{"city": "CORBIN", "loc": [-97.526326, 37.073142], "pop": 193, "state": "KS", "_id": "67032"} -{"city": "PENALOSA", "loc": [-98.39297, 37.666149], "pop": 497, "state": "KS", "_id": "67035"} -{"city": "DANVILLE", "loc": [-97.868933, 37.267424], "pop": 168, "state": "KS", "_id": "67036"} -{"city": "DERBY", "loc": [-97.254888, 37.552976], "pop": 17109, "state": "KS", "_id": "67037"} -{"city": "DEXTER", "loc": [-96.691702, 37.164087], "pop": 698, "state": "KS", "_id": "67038"} -{"city": "DOUGLASS", "loc": [-96.994848, 37.51951], "pop": 2388, "state": "KS", "_id": "67039"} -{"city": "ELBING", "loc": [-97.112766, 38.046208], "pop": 422, "state": "KS", "_id": "67041"} -{"city": "EL DORADO", "loc": [-96.854297, 37.822649], "pop": 14387, "state": "KS", "_id": "67042"} -{"city": "EUREKA", "loc": [-96.295877, 37.826471], "pop": 3986, "state": "KS", "_id": "67045"} -{"city": "FALL RIVER", "loc": [-96.043456, 37.621049], "pop": 527, "state": "KS", "_id": "67047"} -{"city": "FREEPORT", "loc": [-97.86342, 37.190257], "pop": 52, "state": "KS", "_id": "67049"} -{"city": "GARDEN PLAIN", "loc": [-97.66002, 37.676712], "pop": 1406, "state": "KS", "_id": "67050"} -{"city": "GEUDA SPRINGS", "loc": [-97.179504, 37.08093], "pop": 503, "state": "KS", "_id": "67051"} -{"city": "GODDARD", "loc": [-97.536408, 37.657483], "pop": 6586, "state": "KS", "_id": "67052"} -{"city": "GOESSEL", "loc": [-97.33628, 38.234259], "pop": 973, "state": "KS", "_id": "67053"} -{"city": "GREENSBURG", "loc": [-99.301057, 37.608367], "pop": 2073, "state": "KS", "_id": "67054"} -{"city": "HALSTEAD", "loc": [-97.511792, 38.006391], "pop": 2405, "state": "KS", "_id": "67056"} -{"city": "HARDTNER", "loc": [-98.654732, 37.030009], "pop": 284, "state": "KS", "_id": "67057"} -{"city": "HARPER", "loc": [-98.017978, 37.290943], "pop": 2444, "state": "KS", "_id": "67058"} -{"city": "HAVILAND", "loc": [-99.133964, 37.609615], "pop": 1277, "state": "KS", "_id": "67059"} -{"city": "HAYSVILLE", "loc": [-97.355323, 37.56467], "pop": 7666, "state": "KS", "_id": "67060"} -{"city": "HAZELTON", "loc": [-98.400319, 37.098421], "pop": 201, "state": "KS", "_id": "67061"} -{"city": "HESSTON", "loc": [-97.449458, 38.135951], "pop": 4156, "state": "KS", "_id": "67062"} -{"city": "HILLSBORO", "loc": [-97.212154, 38.344907], "pop": 3543, "state": "KS", "_id": "67063"} -{"city": "ISABEL", "loc": [-98.53514, 37.448544], "pop": 198, "state": "KS", "_id": "67065"} -{"city": "IUKA", "loc": [-98.736103, 37.739725], "pop": 387, "state": "KS", "_id": "67066"} -{"city": "BELMONT", "loc": [-98.109295, 37.636246], "pop": 6072, "state": "KS", "_id": "67068"} -{"city": "KIOWA", "loc": [-98.485908, 37.017166], "pop": 1255, "state": "KS", "_id": "67070"} -{"city": "LAKE CITY", "loc": [-98.809833, 37.356885], "pop": 97, "state": "KS", "_id": "67071"} -{"city": "LATHAM", "loc": [-96.679148, 37.530983], "pop": 293, "state": "KS", "_id": "67072"} -{"city": "LEHIGH", "loc": [-97.304315, 38.377106], "pop": 311, "state": "KS", "_id": "67073"} -{"city": "LEON", "loc": [-96.752634, 37.681268], "pop": 1342, "state": "KS", "_id": "67074"} -{"city": "MAIZE", "loc": [-97.468874, 37.774727], "pop": 1808, "state": "KS", "_id": "67101"} -{"city": "MAPLE CITY", "loc": [-96.781387, 37.071295], "pop": 78, "state": "KS", "_id": "67102"} -{"city": "MAYFIELD", "loc": [-97.541603, 37.251806], "pop": 353, "state": "KS", "_id": "67103"} -{"city": "MEDICINE LODGE", "loc": [-98.584785, 37.284452], "pop": 3208, "state": "KS", "_id": "67104"} -{"city": "MILAN", "loc": [-97.652065, 37.257764], "pop": 238, "state": "KS", "_id": "67105"} -{"city": "MILTON", "loc": [-97.759156, 37.440064], "pop": 353, "state": "KS", "_id": "67106"} -{"city": "MOUNDRIDGE", "loc": [-97.508767, 38.206029], "pop": 2307, "state": "KS", "_id": "67107"} -{"city": "MOUNT HOPE", "loc": [-97.659092, 37.868412], "pop": 1060, "state": "KS", "_id": "67108"} -{"city": "MULLINVILLE", "loc": [-99.46388, 37.571608], "pop": 427, "state": "KS", "_id": "67109"} -{"city": "MULVANE", "loc": [-97.231954, 37.476433], "pop": 6238, "state": "KS", "_id": "67110"} -{"city": "MURDOCK", "loc": [-97.95027, 37.609945], "pop": 197, "state": "KS", "_id": "67111"} -{"city": "NASHVILLE", "loc": [-98.417052, 37.434419], "pop": 220, "state": "KS", "_id": "67112"} -{"city": "NEWTON", "loc": [-97.343457, 38.045067], "pop": 20273, "state": "KS", "_id": "67114"} -{"city": "NORTH NEWTON", "loc": [-97.328796, 38.128246], "pop": 408, "state": "KS", "_id": "67117"} -{"city": "NORWICH", "loc": [-97.866247, 37.4501], "pop": 705, "state": "KS", "_id": "67118"} -{"city": "OXFORD", "loc": [-97.176131, 37.265303], "pop": 1515, "state": "KS", "_id": "67119"} -{"city": "PECK", "loc": [-97.311565, 37.472445], "pop": 1023, "state": "KS", "_id": "67120"} -{"city": "PIEDMONT", "loc": [-96.369503, 37.637169], "pop": 249, "state": "KS", "_id": "67122"} -{"city": "POTWIN", "loc": [-97.000608, 37.971872], "pop": 900, "state": "KS", "_id": "67123"} -{"city": "PRATT", "loc": [-98.73003, 37.650219], "pop": 8348, "state": "KS", "_id": "67124"} -{"city": "PROTECTION", "loc": [-99.481628, 37.196782], "pop": 864, "state": "KS", "_id": "67127"} -{"city": "RAGO", "loc": [-98.077208, 37.439824], "pop": 127, "state": "KS", "_id": "67128"} -{"city": "ROCK", "loc": [-96.936468, 37.429435], "pop": 393, "state": "KS", "_id": "67131"} -{"city": "ROSALIA", "loc": [-96.648161, 37.796041], "pop": 545, "state": "KS", "_id": "67132"} -{"city": "ROSE HILL", "loc": [-97.117346, 37.578371], "pop": 5598, "state": "KS", "_id": "67133"} -{"city": "SAWYER", "loc": [-98.66422, 37.51007], "pop": 569, "state": "KS", "_id": "67134"} -{"city": "SEDGWICK", "loc": [-97.402412, 37.869328], "pop": 7706, "state": "KS", "_id": "67135"} -{"city": "CLIMAX", "loc": [-96.229777, 37.64985], "pop": 871, "state": "KS", "_id": "67137"} -{"city": "SHARON", "loc": [-98.414168, 37.249239], "pop": 440, "state": "KS", "_id": "67138"} -{"city": "SOUTH HAVEN", "loc": [-97.404228, 37.049997], "pop": 710, "state": "KS", "_id": "67140"} -{"city": "SPIVEY", "loc": [-98.175122, 37.440986], "pop": 169, "state": "KS", "_id": "67142"} -{"city": "SUN CITY", "loc": [-98.90386, 37.394647], "pop": 191, "state": "KS", "_id": "67143"} -{"city": "TOWANDA", "loc": [-96.991769, 37.800506], "pop": 2598, "state": "KS", "_id": "67144"} -{"city": "UDALL", "loc": [-97.110784, 37.393861], "pop": 1714, "state": "KS", "_id": "67146"} -{"city": "VALLEY CENTER", "loc": [-97.262146, 37.861643], "pop": 1146, "state": "KS", "_id": "67147"} -{"city": "VIOLA", "loc": [-97.630636, 37.569624], "pop": 1330, "state": "KS", "_id": "67149"} -{"city": "WALDRON", "loc": [-98.228877, 37.072994], "pop": 189, "state": "KS", "_id": "67150"} -{"city": "WALTON", "loc": [-97.236077, 38.123909], "pop": 418, "state": "KS", "_id": "67151"} -{"city": "WELLINGTON", "loc": [-97.390976, 37.27783], "pop": 11149, "state": "KS", "_id": "67152"} -{"city": "WHITEWATER", "loc": [-97.130816, 37.961231], "pop": 1100, "state": "KS", "_id": "67154"} -{"city": "WILMORE", "loc": [-99.184577, 37.331794], "pop": 132, "state": "KS", "_id": "67155"} -{"city": "WINFIELD", "loc": [-96.979987, 37.241621], "pop": 14958, "state": "KS", "_id": "67156"} -{"city": "ZENDA", "loc": [-98.28736, 37.437253], "pop": 200, "state": "KS", "_id": "67159"} -{"city": "WICHITA", "loc": [-97.33551, 37.689945], "pop": 1250, "state": "KS", "_id": "67202"} -{"city": "WICHITA", "loc": [-97.363766, 37.704798], "pop": 30712, "state": "KS", "_id": "67203"} -{"city": "WICHITA", "loc": [-97.356563, 37.748838], "pop": 20298, "state": "KS", "_id": "67204"} -{"city": "WICHITA", "loc": [-97.426924, 37.763929], "pop": 1807, "state": "KS", "_id": "67205"} -{"city": "EASTBOROUGH", "loc": [-97.239253, 37.699622], "pop": 13008, "state": "KS", "_id": "67206"} -{"city": "EASTBOROUGH", "loc": [-97.238962, 37.667152], "pop": 20116, "state": "KS", "_id": "67207"} -{"city": "WICHITA", "loc": [-97.281062, 37.702428], "pop": 18195, "state": "KS", "_id": "67208"} -{"city": "WICHITA", "loc": [-97.42354, 37.677855], "pop": 4135, "state": "KS", "_id": "67209"} -{"city": "WICHITA", "loc": [-97.261254, 37.637915], "pop": 11414, "state": "KS", "_id": "67210"} -{"city": "WICHITA", "loc": [-97.316451, 37.666181], "pop": 20182, "state": "KS", "_id": "67211"} -{"city": "WICHITA", "loc": [-97.438344, 37.700683], "pop": 41349, "state": "KS", "_id": "67212"} -{"city": "WICHITA", "loc": [-97.359074, 37.667959], "pop": 23607, "state": "KS", "_id": "67213"} -{"city": "WICHITA", "loc": [-97.313284, 37.705051], "pop": 18651, "state": "KS", "_id": "67214"} -{"city": "WICHITA", "loc": [-97.424985, 37.633333], "pop": 2930, "state": "KS", "_id": "67215"} -{"city": "WICHITA", "loc": [-97.313625, 37.622332], "pop": 23031, "state": "KS", "_id": "67216"} -{"city": "WICHITA", "loc": [-97.358139, 37.626574], "pop": 30680, "state": "KS", "_id": "67217"} -{"city": "WICHITA", "loc": [-97.280219, 37.669007], "pop": 23491, "state": "KS", "_id": "67218"} -{"city": "PARK CITY", "loc": [-97.313517, 37.76482], "pop": 10619, "state": "KS", "_id": "67219"} -{"city": "BEL AIRE", "loc": [-97.275915, 37.74548], "pop": 9802, "state": "KS", "_id": "67220"} -{"city": "MC CONNELL A F B", "loc": [-97.254021, 37.623687], "pop": 137, "state": "KS", "_id": "67221"} -{"city": "WICHITA", "loc": [-97.467421, 37.748434], "pop": 318, "state": "KS", "_id": "67223"} -{"city": "WICHITA", "loc": [-97.247853, 37.737891], "pop": 8884, "state": "KS", "_id": "67226"} -{"city": "WICHITA", "loc": [-97.460561, 37.588466], "pop": 107, "state": "KS", "_id": "67227"} -{"city": "WICHITA", "loc": [-97.201404, 37.776061], "pop": 768, "state": "KS", "_id": "67228"} -{"city": "WICHITA", "loc": [-97.155764, 37.680814], "pop": 4957, "state": "KS", "_id": "67230"} -{"city": "WICHITA", "loc": [-97.423384, 37.526866], "pop": 855, "state": "KS", "_id": "67231"} -{"city": "WICHITA", "loc": [-97.164278, 37.642797], "pop": 128, "state": "KS", "_id": "67232"} -{"city": "WICHITA", "loc": [-97.32237, 37.54434], "pop": 3861, "state": "KS", "_id": "67233"} -{"city": "WICHITA", "loc": [-97.461145, 37.668631], "pop": 3243, "state": "KS", "_id": "67235"} -{"city": "WICHITA", "loc": [-97.26821, 37.497943], "pop": 262, "state": "KS", "_id": "67236"} -{"city": "INDEPENDENCE", "loc": [-95.716528, 37.229247], "pop": 13892, "state": "KS", "_id": "67301"} -{"city": "ALTAMONT", "loc": [-95.298302, 37.187298], "pop": 1336, "state": "KS", "_id": "67330"} -{"city": "BARTLETT", "loc": [-95.211512, 37.060096], "pop": 385, "state": "KS", "_id": "67332"} -{"city": "CANEY", "loc": [-95.909128, 37.022412], "pop": 3302, "state": "KS", "_id": "67333"} -{"city": "CHERRYVALE", "loc": [-95.559019, 37.266828], "pop": 3490, "state": "KS", "_id": "67335"} -{"city": "CHETOPA", "loc": [-95.079572, 37.041817], "pop": 2031, "state": "KS", "_id": "67336"} -{"city": "COFFEYVILLE", "loc": [-95.632756, 37.044056], "pop": 17417, "state": "KS", "_id": "67337"} -{"city": "DENNIS", "loc": [-95.411602, 37.324324], "pop": 430, "state": "KS", "_id": "67341"} -{"city": "EDNA", "loc": [-95.344159, 37.056146], "pop": 817, "state": "KS", "_id": "67342"} -{"city": "ELK CITY", "loc": [-95.913517, 37.314571], "pop": 801, "state": "KS", "_id": "67344"} -{"city": "ELK FALLS", "loc": [-96.195902, 37.374514], "pop": 206, "state": "KS", "_id": "67345"} -{"city": "GRENOLA", "loc": [-96.439564, 37.366824], "pop": 389, "state": "KS", "_id": "67346"} -{"city": "HAVANA", "loc": [-95.941372, 37.091655], "pop": 149, "state": "KS", "_id": "67347"} -{"city": "HOWARD", "loc": [-96.256298, 37.48089], "pop": 1161, "state": "KS", "_id": "67349"} -{"city": "LIBERTY", "loc": [-95.601698, 37.157629], "pop": 504, "state": "KS", "_id": "67351"} -{"city": "LONGTON", "loc": [-96.081379, 37.390455], "pop": 561, "state": "KS", "_id": "67352"} -{"city": "MOLINE", "loc": [-96.306875, 37.364868], "pop": 681, "state": "KS", "_id": "67353"} -{"city": "MOUND VALLEY", "loc": [-95.424712, 37.209078], "pop": 807, "state": "KS", "_id": "67354"} -{"city": "NIOTAZE", "loc": [-96.01922, 37.05133], "pop": 265, "state": "KS", "_id": "67355"} -{"city": "OSWEGO", "loc": [-95.133458, 37.182254], "pop": 3121, "state": "KS", "_id": "67356"} -{"city": "PARSONS", "loc": [-95.269288, 37.338888], "pop": 14375, "state": "KS", "_id": "67357"} -{"city": "PERU", "loc": [-96.140376, 37.056823], "pop": 689, "state": "KS", "_id": "67360"} -{"city": "SEDAN", "loc": [-96.173248, 37.12968], "pop": 2186, "state": "KS", "_id": "67361"} -{"city": "BAVARIA", "loc": [-97.608787, 38.823802], "pop": 45208, "state": "KS", "_id": "67401"} -{"city": "ABILENE", "loc": [-97.20632, 38.937144], "pop": 8249, "state": "KS", "_id": "67410"} -{"city": "ADA", "loc": [-97.887387, 39.157777], "pop": 179, "state": "KS", "_id": "67414"} -{"city": "ASSARIA", "loc": [-97.62032, 38.667596], "pop": 761, "state": "KS", "_id": "67416"} -{"city": "AURORA", "loc": [-97.530411, 39.447188], "pop": 195, "state": "KS", "_id": "67417"} -{"city": "BARNARD", "loc": [-98.071144, 39.181572], "pop": 302, "state": "KS", "_id": "67418"} -{"city": "SCOTTSVILLE", "loc": [-98.111714, 39.45446], "pop": 5221, "state": "KS", "_id": "67420"} -{"city": "BENNINGTON", "loc": [-97.603121, 39.02239], "pop": 844, "state": "KS", "_id": "67422"} -{"city": "BEVERLY", "loc": [-97.981785, 38.984416], "pop": 389, "state": "KS", "_id": "67423"} -{"city": "BROOKVILLE", "loc": [-97.863002, 38.785771], "pop": 323, "state": "KS", "_id": "67425"} -{"city": "BUSHTON", "loc": [-98.401629, 38.501836], "pop": 480, "state": "KS", "_id": "67427"} -{"city": "CANTON", "loc": [-97.429072, 38.385838], "pop": 1050, "state": "KS", "_id": "67428"} -{"city": "CARLTON", "loc": [-97.307506, 38.6729], "pop": 121, "state": "KS", "_id": "67429"} -{"city": "CAWKER CITY", "loc": [-98.433458, 39.511529], "pop": 637, "state": "KS", "_id": "67430"} -{"city": "CHAPMAN", "loc": [-97.016961, 38.972225], "pop": 1968, "state": "KS", "_id": "67431"} -{"city": "CLAY CENTER", "loc": [-97.127793, 39.384354], "pop": 6038, "state": "KS", "_id": "67432"} -{"city": "DELPHOS", "loc": [-97.771692, 39.273122], "pop": 639, "state": "KS", "_id": "67436"} -{"city": "DOWNS", "loc": [-98.544117, 39.502753], "pop": 1285, "state": "KS", "_id": "67437"} -{"city": "DURHAM", "loc": [-97.255532, 38.503836], "pop": 444, "state": "KS", "_id": "67438"} -{"city": "ELLSWORTH", "loc": [-98.205678, 38.731219], "pop": 3728, "state": "KS", "_id": "67439"} -{"city": "ENTERPRISE", "loc": [-97.112222, 38.906321], "pop": 1286, "state": "KS", "_id": "67441"} -{"city": "FALUN", "loc": [-97.755411, 38.664794], "pop": 226, "state": "KS", "_id": "67442"} -{"city": "GALVA", "loc": [-97.53592, 38.382377], "pop": 1151, "state": "KS", "_id": "67443"} -{"city": "GENESEO", "loc": [-98.185818, 38.503655], "pop": 569, "state": "KS", "_id": "67444"} -{"city": "GLASCO", "loc": [-97.841829, 39.362092], "pop": 663, "state": "KS", "_id": "67445"} -{"city": "GLEN ELDER", "loc": [-98.315508, 39.495775], "pop": 632, "state": "KS", "_id": "67446"} -{"city": "GREEN", "loc": [-96.999424, 39.420056], "pop": 340, "state": "KS", "_id": "67447"} -{"city": "GYPSUM", "loc": [-97.43384, 38.704744], "pop": 742, "state": "KS", "_id": "67448"} -{"city": "DELAVAN", "loc": [-97.015491, 38.702306], "pop": 4145, "state": "KS", "_id": "67449"} -{"city": "HOLYROOD", "loc": [-98.415916, 38.589955], "pop": 678, "state": "KS", "_id": "67450"} -{"city": "HOPE", "loc": [-97.106126, 38.6776], "pop": 725, "state": "KS", "_id": "67451"} -{"city": "HUNTER", "loc": [-98.401745, 39.242964], "pop": 181, "state": "KS", "_id": "67452"} -{"city": "KANOPOLIS", "loc": [-98.157468, 38.709111], "pop": 641, "state": "KS", "_id": "67454"} -{"city": "WESTFALL", "loc": [-98.140426, 39.028644], "pop": 1986, "state": "KS", "_id": "67455"} -{"city": "LINDSBORG", "loc": [-97.67389, 38.576058], "pop": 3320, "state": "KS", "_id": "67456"} -{"city": "LITTLE RIVER", "loc": [-98.011317, 38.407868], "pop": 841, "state": "KS", "_id": "67457"} -{"city": "LONGFORD", "loc": [-97.208662, 39.174324], "pop": 507, "state": "KS", "_id": "67458"} -{"city": "LORRAINE", "loc": [-98.289954, 38.565175], "pop": 302, "state": "KS", "_id": "67459"} -{"city": "CONWAY", "loc": [-97.654989, 38.373732], "pop": 16480, "state": "KS", "_id": "67460"} -{"city": "MANCHESTER", "loc": [-97.309547, 39.087051], "pop": 203, "state": "KS", "_id": "67463"} -{"city": "MARQUETTE", "loc": [-97.838079, 38.556028], "pop": 760, "state": "KS", "_id": "67464"} -{"city": "MENTOR", "loc": [-97.566034, 38.732103], "pop": 791, "state": "KS", "_id": "67465"} -{"city": "MILTONVALE", "loc": [-97.457911, 39.348078], "pop": 677, "state": "KS", "_id": "67466"} -{"city": "MINNEAPOLIS", "loc": [-97.668778, 39.129487], "pop": 2985, "state": "KS", "_id": "67467"} -{"city": "MORGANVILLE", "loc": [-97.249219, 39.509219], "pop": 847, "state": "KS", "_id": "67468"} -{"city": "NAVARRE", "loc": [-97.105554, 38.81772], "pop": 205, "state": "KS", "_id": "67469"} -{"city": "NEW CAMBRIA", "loc": [-97.523012, 38.896008], "pop": 390, "state": "KS", "_id": "67470"} -{"city": "OAKHILL", "loc": [-97.330501, 39.247668], "pop": 96, "state": "KS", "_id": "67472"} -{"city": "OSBORNE", "loc": [-98.696081, 39.419402], "pop": 2340, "state": "KS", "_id": "67473"} -{"city": "PORTIS", "loc": [-98.690446, 39.545545], "pop": 255, "state": "KS", "_id": "67474"} -{"city": "RAMONA", "loc": [-97.075874, 38.577726], "pop": 237, "state": "KS", "_id": "67475"} -{"city": "ROXBURY", "loc": [-97.42719, 38.474325], "pop": 106, "state": "KS", "_id": "67476"} -{"city": "SIMPSON", "loc": [-97.948887, 39.373074], "pop": 159, "state": "KS", "_id": "67478"} -{"city": "SMOLAN", "loc": [-97.713277, 38.76442], "pop": 706, "state": "KS", "_id": "67479"} -{"city": "SOLOMON", "loc": [-97.351803, 38.919359], "pop": 1626, "state": "KS", "_id": "67480"} -{"city": "SYLVAN GROVE", "loc": [-98.373031, 39.032443], "pop": 976, "state": "KS", "_id": "67481"} -{"city": "TALMAGE", "loc": [-97.297367, 39.008594], "pop": 200, "state": "KS", "_id": "67482"} -{"city": "TAMPA", "loc": [-97.177388, 38.553437], "pop": 211, "state": "KS", "_id": "67483"} -{"city": "CULVER", "loc": [-97.828375, 38.998318], "pop": 745, "state": "KS", "_id": "67484"} -{"city": "TIPTON", "loc": [-98.464391, 39.343639], "pop": 373, "state": "KS", "_id": "67485"} -{"city": "WAKEFIELD", "loc": [-97.022851, 39.224916], "pop": 1330, "state": "KS", "_id": "67487"} -{"city": "WELLS", "loc": [-97.554636, 39.059224], "pop": 242, "state": "KS", "_id": "67488"} -{"city": "WILSON", "loc": [-98.443216, 38.813373], "pop": 1237, "state": "KS", "_id": "67490"} -{"city": "WINDOM", "loc": [-97.896476, 38.384518], "pop": 231, "state": "KS", "_id": "67491"} -{"city": "WOODBINE", "loc": [-96.961935, 38.813064], "pop": 384, "state": "KS", "_id": "67492"} -{"city": "HUTCHINSON", "loc": [-97.931052, 38.054995], "pop": 27097, "state": "KS", "_id": "67501"} -{"city": "MEDORA", "loc": [-97.920517, 38.091483], "pop": 21197, "state": "KS", "_id": "67502"} -{"city": "SOUTH HUTCHINSON", "loc": [-97.90814, 38.007306], "pop": 4243, "state": "KS", "_id": "67505"} -{"city": "ABBYVILLE", "loc": [-98.207103, 37.962597], "pop": 267, "state": "KS", "_id": "67510"} -{"city": "ALBERT", "loc": [-98.980714, 38.475758], "pop": 544, "state": "KS", "_id": "67511"} -{"city": "ALDEN", "loc": [-98.311202, 38.234303], "pop": 278, "state": "KS", "_id": "67512"} -{"city": "ALEXANDER", "loc": [-99.537828, 38.456996], "pop": 150, "state": "KS", "_id": "67513"} -{"city": "ARLINGTON", "loc": [-98.159045, 37.859795], "pop": 930, "state": "KS", "_id": "67514"} -{"city": "ARNOLD", "loc": [-100.012003, 38.609122], "pop": 0, "state": "KS", "_id": "67515"} -{"city": "BAZINE", "loc": [-99.701607, 38.456457], "pop": 546, "state": "KS", "_id": "67516"} -{"city": "BEAVER", "loc": [-98.648327, 38.650055], "pop": 122, "state": "KS", "_id": "67517"} -{"city": "BEELER", "loc": [-100.170957, 38.48113], "pop": 91, "state": "KS", "_id": "67518"} -{"city": "BELPRE", "loc": [-99.09357, 37.934655], "pop": 268, "state": "KS", "_id": "67519"} -{"city": "BISON", "loc": [-99.198634, 38.519332], "pop": 347, "state": "KS", "_id": "67520"} -{"city": "BROWNELL", "loc": [-99.732806, 38.623854], "pop": 156, "state": "KS", "_id": "67521"} -{"city": "BUHLER", "loc": [-97.769093, 38.130903], "pop": 1854, "state": "KS", "_id": "67522"} -{"city": "BURDETT", "loc": [-99.527515, 38.210384], "pop": 340, "state": "KS", "_id": "67523"} -{"city": "CHASE", "loc": [-98.355634, 38.363515], "pop": 772, "state": "KS", "_id": "67524"} -{"city": "CLAFLIN", "loc": [-98.53722, 38.540885], "pop": 969, "state": "KS", "_id": "67525"} -{"city": "ELLINWOOD", "loc": [-98.584872, 38.356498], "pop": 3297, "state": "KS", "_id": "67526"} -{"city": "GARFIELD", "loc": [-99.237433, 38.064929], "pop": 368, "state": "KS", "_id": "67529"} -{"city": "HEIZER", "loc": [-98.783103, 38.370937], "pop": 19861, "state": "KS", "_id": "67530"} -{"city": "HAVEN", "loc": [-97.777244, 37.868593], "pop": 2181, "state": "KS", "_id": "67543"} -{"city": "SUSANK", "loc": [-98.791041, 38.529246], "pop": 3642, "state": "KS", "_id": "67544"} -{"city": "HUDSON", "loc": [-98.640815, 38.148493], "pop": 381, "state": "KS", "_id": "67545"} -{"city": "INMAN", "loc": [-97.795107, 38.223212], "pop": 1863, "state": "KS", "_id": "67546"} -{"city": "KINSLEY", "loc": [-99.411556, 37.924715], "pop": 2321, "state": "KS", "_id": "67547"} -{"city": "LA CROSSE", "loc": [-99.309733, 38.531144], "pop": 1545, "state": "KS", "_id": "67548"} -{"city": "67549", "loc": [-98.307511, 37.833447], "pop": 260, "state": "KS", "_id": "67549"} -{"city": "RADIUM", "loc": [-99.112382, 38.179643], "pop": 6572, "state": "KS", "_id": "67550"} -{"city": "LEWIS", "loc": [-99.247977, 37.906223], "pop": 758, "state": "KS", "_id": "67552"} -{"city": "LIEBENTHAL", "loc": [-99.282051, 38.646585], "pop": 231, "state": "KS", "_id": "67553"} -{"city": "LYONS", "loc": [-98.183094, 38.334881], "pop": 4931, "state": "KS", "_id": "67554"} -{"city": "MC CRACKEN", "loc": [-99.553982, 38.595734], "pop": 317, "state": "KS", "_id": "67556"} -{"city": "MACKSVILLE", "loc": [-98.948052, 37.943276], "pop": 754, "state": "KS", "_id": "67557"} -{"city": "NEKOMA", "loc": [-99.42338, 38.437226], "pop": 81, "state": "KS", "_id": "67559"} -{"city": "NESS CITY", "loc": [-99.90286, 38.438754], "pop": 2256, "state": "KS", "_id": "67560"} -{"city": "NICKERSON", "loc": [-98.067408, 38.141171], "pop": 1596, "state": "KS", "_id": "67561"} -{"city": "ODIN", "loc": [-98.626762, 38.55628], "pop": 281, "state": "KS", "_id": "67562"} -{"city": "OFFERLE", "loc": [-99.549757, 37.882805], "pop": 323, "state": "KS", "_id": "67563"} -{"city": "GALATIA", "loc": [-98.92899, 38.567045], "pop": 14, "state": "KS", "_id": "67564"} -{"city": "GALATIA", "loc": [-99.065635, 38.509239], "pop": 711, "state": "KS", "_id": "67565"} -{"city": "PARTRIDGE", "loc": [-98.079811, 37.967129], "pop": 581, "state": "KS", "_id": "67566"} -{"city": "PAWNEE ROCK", "loc": [-98.981436, 38.278193], "pop": 515, "state": "KS", "_id": "67567"} -{"city": "PLEVNA", "loc": [-98.298807, 37.965762], "pop": 270, "state": "KS", "_id": "67568"} -{"city": "PRETTY PRAIRIE", "loc": [-97.988603, 37.77828], "pop": 959, "state": "KS", "_id": "67570"} -{"city": "RANSOM", "loc": [-99.926651, 38.640003], "pop": 581, "state": "KS", "_id": "67572"} -{"city": "RAYMOND", "loc": [-98.411867, 38.287677], "pop": 254, "state": "KS", "_id": "67573"} -{"city": "ROZEL", "loc": [-99.404846, 38.214823], "pop": 275, "state": "KS", "_id": "67574"} -{"city": "RUSH CENTER", "loc": [-99.307881, 38.453341], "pop": 275, "state": "KS", "_id": "67575"} -{"city": "SAINT JOHN", "loc": [-98.764653, 38.030939], "pop": 2292, "state": "KS", "_id": "67576"} -{"city": "SEWARD", "loc": [-98.76391, 38.206558], "pop": 249, "state": "KS", "_id": "67577"} -{"city": "STAFFORD", "loc": [-98.592854, 37.955372], "pop": 1689, "state": "KS", "_id": "67578"} -{"city": "STERLING", "loc": [-98.205474, 38.212621], "pop": 2485, "state": "KS", "_id": "67579"} -{"city": "67580", "loc": [-98.761573, 38.647124], "pop": 137, "state": "KS", "_id": "67580"} -{"city": "SYLVIA", "loc": [-98.406765, 37.955687], "pop": 396, "state": "KS", "_id": "67581"} -{"city": "TIMKEN", "loc": [-99.190154, 38.445489], "pop": 185, "state": "KS", "_id": "67582"} -{"city": "LANGDON", "loc": [-98.421798, 37.809593], "pop": 558, "state": "KS", "_id": "67583"} -{"city": "UTICA", "loc": [-100.137976, 38.641057], "pop": 403, "state": "KS", "_id": "67584"} -{"city": "ANTONINO", "loc": [-99.320134, 38.878046], "pop": 20703, "state": "KS", "_id": "67601"} -{"city": "AGRA", "loc": [-99.125543, 39.803701], "pop": 604, "state": "KS", "_id": "67621"} -{"city": "ALMENA", "loc": [-99.704177, 39.889071], "pop": 574, "state": "KS", "_id": "67622"} -{"city": "ALTON", "loc": [-98.953875, 39.451422], "pop": 364, "state": "KS", "_id": "67623"} -{"city": "BOGUE", "loc": [-99.67882, 39.378221], "pop": 416, "state": "KS", "_id": "67625"} -{"city": "BUNKER HILL", "loc": [-98.678285, 38.835704], "pop": 359, "state": "KS", "_id": "67626"} -{"city": "CATHARINE", "loc": [-99.215992, 38.96034], "pop": 289, "state": "KS", "_id": "67627"} -{"city": "CEDAR", "loc": [-98.936923, 39.660242], "pop": 62, "state": "KS", "_id": "67628"} -{"city": "CLAYTON", "loc": [-100.059623, 39.69985], "pop": 485, "state": "KS", "_id": "67629"} -{"city": "CODELL", "loc": [-99.159186, 39.206652], "pop": 160, "state": "KS", "_id": "67630"} -{"city": "COLLYER", "loc": [-100.06921, 38.965231], "pop": 523, "state": "KS", "_id": "67631"} -{"city": "DAMAR", "loc": [-99.581056, 39.324181], "pop": 214, "state": "KS", "_id": "67632"} -{"city": "67633", "loc": [-99.674433, 39.624374], "pop": 2, "state": "KS", "_id": "67633"} -{"city": "DORRANCE", "loc": [-98.569471, 38.834784], "pop": 316, "state": "KS", "_id": "67634"} -{"city": "DRESDEN", "loc": [-100.411219, 39.609357], "pop": 158, "state": "KS", "_id": "67635"} -{"city": "EDMOND", "loc": [-99.784185, 39.661348], "pop": 180, "state": "KS", "_id": "67636"} -{"city": "ELLIS", "loc": [-99.52851, 38.947128], "pop": 2478, "state": "KS", "_id": "67637"} -{"city": "GAYLORD", "loc": [-98.801751, 39.659923], "pop": 722, "state": "KS", "_id": "67638"} -{"city": "GLADE", "loc": [-99.29963, 39.670373], "pop": 320, "state": "KS", "_id": "67639"} -{"city": "GORHAM", "loc": [-99.01116, 38.872226], "pop": 528, "state": "KS", "_id": "67640"} -{"city": "HARLAN", "loc": [-98.789796, 39.611345], "pop": 91, "state": "KS", "_id": "67641"} -{"city": "HILL CITY", "loc": [-99.842921, 39.35657], "pop": 2501, "state": "KS", "_id": "67642"} -{"city": "JENNINGS", "loc": [-100.283441, 39.676242], "pop": 347, "state": "KS", "_id": "67643"} -{"city": "KIRWIN", "loc": [-99.120378, 39.671716], "pop": 388, "state": "KS", "_id": "67644"} -{"city": "DENSMORE", "loc": [-100.001566, 39.609364], "pop": 344, "state": "KS", "_id": "67645"} -{"city": "LOGAN", "loc": [-99.568694, 39.661096], "pop": 751, "state": "KS", "_id": "67646"} -{"city": "LONG ISLAND", "loc": [-99.539125, 39.951683], "pop": 260, "state": "KS", "_id": "67647"} -{"city": "LUCAS", "loc": [-98.535186, 39.058126], "pop": 576, "state": "KS", "_id": "67648"} -{"city": "LURAY", "loc": [-98.685084, 39.103856], "pop": 339, "state": "KS", "_id": "67649"} -{"city": "MORLAND", "loc": [-100.07328, 39.322905], "pop": 604, "state": "KS", "_id": "67650"} -{"city": "NATOMA", "loc": [-98.982863, 39.201341], "pop": 623, "state": "KS", "_id": "67651"} -{"city": "NEW ALMELO", "loc": [-100.139829, 39.624755], "pop": 2, "state": "KS", "_id": "67652"} -{"city": "NORCATUR", "loc": [-100.200714, 39.844661], "pop": 304, "state": "KS", "_id": "67653"} -{"city": "NORTON", "loc": [-99.887832, 39.840706], "pop": 4360, "state": "KS", "_id": "67654"} -{"city": "OGALLAH", "loc": [-99.681117, 38.932307], "pop": 460, "state": "KS", "_id": "67656"} -{"city": "PALCO", "loc": [-99.559265, 39.253107], "pop": 384, "state": "KS", "_id": "67657"} -{"city": "PARADISE", "loc": [-98.920723, 39.075617], "pop": 219, "state": "KS", "_id": "67658"} -{"city": "PENOKEE", "loc": [-99.919603, 39.391667], "pop": 22, "state": "KS", "_id": "67659"} -{"city": "PFEIFER", "loc": [-99.141162, 38.729915], "pop": 123, "state": "KS", "_id": "67660"} -{"city": "PHILLIPSBURG", "loc": [-99.33284, 39.762333], "pop": 3942, "state": "KS", "_id": "67661"} -{"city": "PLAINVILLE", "loc": [-99.300827, 39.230823], "pop": 2499, "state": "KS", "_id": "67663"} -{"city": "PRAIRIE VIEW", "loc": [-99.568314, 39.836967], "pop": 325, "state": "KS", "_id": "67664"} -{"city": "RUSSELL", "loc": [-98.859451, 38.880563], "pop": 5370, "state": "KS", "_id": "67665"} -{"city": "SCHOENCHEN", "loc": [-99.386872, 38.748087], "pop": 534, "state": "KS", "_id": "67667"} -{"city": "STOCKTON", "loc": [-99.287106, 39.43761], "pop": 2147, "state": "KS", "_id": "67669"} -{"city": "VICTORIA", "loc": [-99.139057, 38.858932], "pop": 1877, "state": "KS", "_id": "67671"} -{"city": "WA KEENEY", "loc": [-99.881824, 39.011415], "pop": 2711, "state": "KS", "_id": "67672"} -{"city": "WALDO", "loc": [-98.778516, 39.087715], "pop": 128, "state": "KS", "_id": "67673"} -{"city": "WOODSTON", "loc": [-99.103745, 39.443118], "pop": 317, "state": "KS", "_id": "67675"} -{"city": "ZURICH", "loc": [-99.44908, 39.218155], "pop": 318, "state": "KS", "_id": "67676"} -{"city": "COLBY", "loc": [-101.044169, 39.383038], "pop": 7225, "state": "KS", "_id": "67701"} -{"city": "ATWOOD", "loc": [-101.031766, 39.792607], "pop": 2174, "state": "KS", "_id": "67730"} -{"city": "BIRD CITY", "loc": [-101.531875, 39.757941], "pop": 794, "state": "KS", "_id": "67731"} -{"city": "BREWSTER", "loc": [-101.372973, 39.36552], "pop": 374, "state": "KS", "_id": "67732"} -{"city": "EDSON", "loc": [-101.520999, 39.357873], "pop": 294, "state": "KS", "_id": "67733"} -{"city": "GEM", "loc": [-100.894781, 39.429587], "pop": 157, "state": "KS", "_id": "67734"} -{"city": "GOODLAND", "loc": [-101.716396, 39.349099], "pop": 6039, "state": "KS", "_id": "67735"} -{"city": "GOVE", "loc": [-100.486707, 38.886969], "pop": 379, "state": "KS", "_id": "67736"} -{"city": "GRAINFIELD", "loc": [-100.468038, 39.103016], "pop": 467, "state": "KS", "_id": "67737"} -{"city": "GRINNELL", "loc": [-100.662052, 39.085107], "pop": 632, "state": "KS", "_id": "67738"} -{"city": "HERNDON", "loc": [-100.813935, 39.90356], "pop": 477, "state": "KS", "_id": "67739"} -{"city": "HOXIE", "loc": [-100.47583, 39.332189], "pop": 2023, "state": "KS", "_id": "67740"} -{"city": "KANORADO", "loc": [-102.001491, 39.343753], "pop": 593, "state": "KS", "_id": "67741"} -{"city": "LEVANT", "loc": [-101.209587, 39.384083], "pop": 143, "state": "KS", "_id": "67743"} -{"city": "LUDELL", "loc": [-100.960352, 39.863037], "pop": 155, "state": "KS", "_id": "67744"} -{"city": "MC DONALD", "loc": [-101.322697, 39.792304], "pop": 598, "state": "KS", "_id": "67745"} -{"city": "67746", "loc": [-100.752304, 39.340679], "pop": 113, "state": "KS", "_id": "67746"} -{"city": "MONUMENT", "loc": [-101.034952, 39.07896], "pop": 164, "state": "KS", "_id": "67747"} -{"city": "OAKLEY", "loc": [-100.858048, 39.112102], "pop": 2346, "state": "KS", "_id": "67748"} -{"city": "OBERLIN", "loc": [-100.531389, 39.827507], "pop": 3046, "state": "KS", "_id": "67749"} -{"city": "PARK", "loc": [-100.34694, 39.078832], "pop": 316, "state": "KS", "_id": "67751"} -{"city": "QUINTER", "loc": [-100.233739, 39.036252], "pop": 1437, "state": "KS", "_id": "67752"} -{"city": "MENLO", "loc": [-100.748982, 39.462185], "pop": 246, "state": "KS", "_id": "67753"} -{"city": "RUSSELL SPRINGS", "loc": [-101.2759, 38.868374], "pop": 243, "state": "KS", "_id": "67755"} -{"city": "WHEELER", "loc": [-101.81226, 39.773634], "pop": 2449, "state": "KS", "_id": "67756"} -{"city": "SELDEN", "loc": [-100.525695, 39.521636], "pop": 872, "state": "KS", "_id": "67757"} -{"city": "SHARON SPRINGS", "loc": [-101.743118, 38.885661], "pop": 1148, "state": "KS", "_id": "67758"} -{"city": "STUDLEY", "loc": [-100.281436, 39.261618], "pop": 314, "state": "KS", "_id": "67759"} -{"city": "WALLACE", "loc": [-101.573474, 38.874675], "pop": 293, "state": "KS", "_id": "67761"} -{"city": "WESKAN", "loc": [-101.951162, 38.864867], "pop": 380, "state": "KS", "_id": "67762"} -{"city": "WINONA", "loc": [-101.221569, 39.061038], "pop": 328, "state": "KS", "_id": "67764"} -{"city": "DODGE CITY", "loc": [-100.024074, 37.756882], "pop": 23098, "state": "KS", "_id": "67801"} -{"city": "67830", "loc": [-100.314071, 38.488191], "pop": 75, "state": "KS", "_id": "67830"} -{"city": "ASHLAND", "loc": [-99.75904, 37.182345], "pop": 1271, "state": "KS", "_id": "67831"} -{"city": "67833", "loc": [-99.915745, 37.50452], "pop": 134, "state": "KS", "_id": "67833"} -{"city": "BUCKLIN", "loc": [-99.632543, 37.552974], "pop": 848, "state": "KS", "_id": "67834"} -{"city": "CIMARRON", "loc": [-100.343825, 37.812727], "pop": 2249, "state": "KS", "_id": "67835"} -{"city": "COPELAND", "loc": [-100.614815, 37.567994], "pop": 536, "state": "KS", "_id": "67837"} -{"city": "DEERFIELD", "loc": [-101.143141, 38.00677], "pop": 1077, "state": "KS", "_id": "67838"} -{"city": "ALAMOTA", "loc": [-100.458023, 38.474588], "pop": 1666, "state": "KS", "_id": "67839"} -{"city": "ENGLEWOOD", "loc": [-99.987793, 37.065751], "pop": 148, "state": "KS", "_id": "67840"} -{"city": "ENSIGN", "loc": [-100.249555, 37.641381], "pop": 373, "state": "KS", "_id": "67841"} -{"city": "FORD", "loc": [-99.76422, 37.632312], "pop": 403, "state": "KS", "_id": "67842"} -{"city": "FORT DODGE", "loc": [-99.945631, 37.70652], "pop": 736, "state": "KS", "_id": "67843"} -{"city": "FOWLER", "loc": [-100.19815, 37.354201], "pop": 881, "state": "KS", "_id": "67844"} -{"city": "GARDEN CITY", "loc": [-100.862088, 37.97693], "pop": 30189, "state": "KS", "_id": "67846"} -{"city": "HANSTON", "loc": [-99.69278, 38.108964], "pop": 695, "state": "KS", "_id": "67849"} -{"city": "HEALY", "loc": [-100.615941, 38.566355], "pop": 476, "state": "KS", "_id": "67850"} -{"city": "HOLCOMB", "loc": [-100.989323, 37.993105], "pop": 2060, "state": "KS", "_id": "67851"} -{"city": "INGALLS", "loc": [-100.514253, 37.829339], "pop": 807, "state": "KS", "_id": "67853"} -{"city": "JETMORE", "loc": [-99.932668, 38.073829], "pop": 1482, "state": "KS", "_id": "67854"} -{"city": "JOHNSON", "loc": [-101.719354, 37.569424], "pop": 1979, "state": "KS", "_id": "67855"} -{"city": "KALVESTA", "loc": [-100.443095, 38.102796], "pop": 326, "state": "KS", "_id": "67856"} -{"city": "KENDALL", "loc": [-101.596883, 37.963657], "pop": 86, "state": "KS", "_id": "67857"} -{"city": "KINGSDOWN", "loc": [-99.751553, 37.52138], "pop": 124, "state": "KS", "_id": "67858"} -{"city": "KISMET", "loc": [-100.750645, 37.133601], "pop": 1195, "state": "KS", "_id": "67859"} -{"city": "LAKIN", "loc": [-101.271297, 37.938197], "pop": 2950, "state": "KS", "_id": "67860"} -{"city": "LEOTI", "loc": [-101.358851, 38.498726], "pop": 2604, "state": "KS", "_id": "67861"} -{"city": "MANTER", "loc": [-101.910869, 37.545094], "pop": 354, "state": "KS", "_id": "67862"} -{"city": "MODOC", "loc": [-101.199231, 38.482523], "pop": 154, "state": "KS", "_id": "67863"} -{"city": "MEADE", "loc": [-100.336378, 37.282137], "pop": 1949, "state": "KS", "_id": "67864"} -{"city": "BLOOM", "loc": [-99.960358, 37.42454], "pop": 999, "state": "KS", "_id": "67865"} -{"city": "67866", "loc": [-100.996709, 38.486906], "pop": 813, "state": "KS", "_id": "67866"} -{"city": "MONTEZUMA", "loc": [-100.446127, 37.601638], "pop": 1431, "state": "KS", "_id": "67867"} -{"city": "PIERCEVILLE", "loc": [-100.71177, 37.907739], "pop": 495, "state": "KS", "_id": "67868"} -{"city": "PLAINS", "loc": [-100.573174, 37.270106], "pop": 1417, "state": "KS", "_id": "67869"} -{"city": "SATANTA", "loc": [-100.96908, 37.440916], "pop": 1881, "state": "KS", "_id": "67870"} -{"city": "FRIEND", "loc": [-100.899331, 38.47994], "pop": 4476, "state": "KS", "_id": "67871"} -{"city": "SHIELDS", "loc": [-100.417699, 38.620794], "pop": 158, "state": "KS", "_id": "67874"} -{"city": "SPEARVILLE", "loc": [-99.737041, 37.823473], "pop": 1361, "state": "KS", "_id": "67876"} -{"city": "SUBLETTE", "loc": [-100.820775, 37.522148], "pop": 2364, "state": "KS", "_id": "67877"} -{"city": "SYRACUSE", "loc": [-101.768654, 37.982581], "pop": 2302, "state": "KS", "_id": "67878"} -{"city": "TRIBUNE", "loc": [-101.765584, 38.496219], "pop": 1774, "state": "KS", "_id": "67879"} -{"city": "ULYSSES", "loc": [-101.348846, 37.579174], "pop": 7159, "state": "KS", "_id": "67880"} -{"city": "WRIGHT", "loc": [-99.923766, 37.767638], "pop": 759, "state": "KS", "_id": "67882"} -{"city": "LIBERAL", "loc": [-100.92857, 37.043789], "pop": 17189, "state": "KS", "_id": "67901"} -{"city": "ELKHART", "loc": [-101.901244, 37.015448], "pop": 2562, "state": "KS", "_id": "67950"} -{"city": "HUGOTON", "loc": [-101.334614, 37.168226], "pop": 4244, "state": "KS", "_id": "67951"} -{"city": "MOSCOW", "loc": [-101.242699, 37.317175], "pop": 804, "state": "KS", "_id": "67952"} -{"city": "RICHFIELD", "loc": [-101.700382, 37.283364], "pop": 269, "state": "KS", "_id": "67953"} -{"city": "ROLLA", "loc": [-101.644683, 37.108881], "pop": 649, "state": "KS", "_id": "67954"} -{"city": "BAGDAD", "loc": [-85.065142, 38.260811], "pop": 1272, "state": "KY", "_id": "40003"} -{"city": "BARDSTOWN", "loc": [-85.461343, 37.80835], "pop": 16176, "state": "KY", "_id": "40004"} -{"city": "BEDFORD", "loc": [-85.313331, 38.586363], "pop": 3359, "state": "KY", "_id": "40006"} -{"city": "BETHLEHEM", "loc": [-85.064874, 38.402433], "pop": 353, "state": "KY", "_id": "40007"} -{"city": "BLOOMFIELD", "loc": [-85.286209, 37.907979], "pop": 2461, "state": "KY", "_id": "40008"} -{"city": "BRADFORDSVILLE", "loc": [-85.143599, 37.46622], "pop": 1136, "state": "KY", "_id": "40009"} -{"city": "BUCKNER", "loc": [-85.47406, 38.363008], "pop": 34, "state": "KY", "_id": "40010"} -{"city": "CAMPBELLSBURG", "loc": [-85.161094, 38.523083], "pop": 1960, "state": "KY", "_id": "40011"} -{"city": "CHAPLIN", "loc": [-85.201472, 37.902541], "pop": 443, "state": "KY", "_id": "40012"} -{"city": "DEATSVILLE", "loc": [-85.484159, 37.896386], "pop": 4763, "state": "KY", "_id": "40013"} -{"city": "CRESTWOOD", "loc": [-85.461038, 38.332637], "pop": 10779, "state": "KY", "_id": "40014"} -{"city": "EMINENCE", "loc": [-85.17816, 38.369606], "pop": 2493, "state": "KY", "_id": "40019"} -{"city": "FINCHVILLE", "loc": [-85.347573, 38.156064], "pop": 1040, "state": "KY", "_id": "40022"} -{"city": "FISHERVILLE", "loc": [-85.428222, 38.165154], "pop": 937, "state": "KY", "_id": "40023"} -{"city": "GOSHEN", "loc": [-85.570816, 38.411294], "pop": 3626, "state": "KY", "_id": "40026"} -{"city": "HOWARDSTOWN", "loc": [-85.554769, 37.566393], "pop": 218, "state": "KY", "_id": "40028"} -{"city": "LA GRANGE", "loc": [-85.392819, 38.402897], "pop": 13280, "state": "KY", "_id": "40031"} -{"city": "LEBANON", "loc": [-85.266811, 37.565834], "pop": 10672, "state": "KY", "_id": "40033"} -{"city": "LOCKPORT", "loc": [-84.958635, 38.421874], "pop": 742, "state": "KY", "_id": "40036"} -{"city": "LORETTO", "loc": [-85.411297, 37.642112], "pop": 2586, "state": "KY", "_id": "40037"} -{"city": "MACKVILLE", "loc": [-85.085201, 37.724703], "pop": 1753, "state": "KY", "_id": "40040"} -{"city": "MILTON", "loc": [-85.36593, 38.692489], "pop": 2535, "state": "KY", "_id": "40045"} -{"city": "MOUNT EDEN", "loc": [-85.164114, 38.035238], "pop": 597, "state": "KY", "_id": "40046"} -{"city": "MOUNT WASHINGTON", "loc": [-85.558631, 38.045206], "pop": 8785, "state": "KY", "_id": "40047"} -{"city": "NEW CASTLE", "loc": [-85.175567, 38.437368], "pop": 2017, "state": "KY", "_id": "40050"} -{"city": "TRAPPIST", "loc": [-85.557694, 37.666327], "pop": 3698, "state": "KY", "_id": "40051"} -{"city": "PENDLETON", "loc": [-85.317019, 38.480918], "pop": 975, "state": "KY", "_id": "40055"} -{"city": "PEWEE VALLEY", "loc": [-85.483377, 38.303945], "pop": 2622, "state": "KY", "_id": "40056"} -{"city": "CROPPER", "loc": [-85.087198, 38.358718], "pop": 2669, "state": "KY", "_id": "40057"} -{"city": "PROSPECT", "loc": [-85.608287, 38.355987], "pop": 7454, "state": "KY", "_id": "40059"} -{"city": "RAYWICK", "loc": [-85.43003, 37.537959], "pop": 981, "state": "KY", "_id": "40060"} -{"city": "SAINT CATHARINE", "loc": [-85.27106, 37.699126], "pop": 39, "state": "KY", "_id": "40061"} -{"city": "SAINT FRANCIS", "loc": [-85.464208, 37.628644], "pop": 39, "state": "KY", "_id": "40062"} -{"city": "SHELBYVILLE", "loc": [-85.224271, 38.216222], "pop": 14843, "state": "KY", "_id": "40065"} -{"city": "SIMPSONVILLE", "loc": [-85.354797, 38.231186], "pop": 2443, "state": "KY", "_id": "40067"} -{"city": "SMITHFIELD", "loc": [-85.265579, 38.393308], "pop": 1250, "state": "KY", "_id": "40068"} -{"city": "MAUD", "loc": [-85.240473, 37.701352], "pop": 6314, "state": "KY", "_id": "40069"} -{"city": "SULPHUR", "loc": [-85.252021, 38.492062], "pop": 466, "state": "KY", "_id": "40070"} -{"city": "TAYLORSVILLE", "loc": [-85.382861, 38.047054], "pop": 7048, "state": "KY", "_id": "40071"} -{"city": "TURNERS STATION", "loc": [-85.132512, 38.550361], "pop": 611, "state": "KY", "_id": "40075"} -{"city": "WADDY", "loc": [-85.12883, 38.105445], "pop": 1510, "state": "KY", "_id": "40076"} -{"city": "WESTPORT", "loc": [-85.45242, 38.492068], "pop": 500, "state": "KY", "_id": "40077"} -{"city": "WILLISBURG", "loc": [-85.136187, 37.837333], "pop": 2245, "state": "KY", "_id": "40078"} -{"city": "40103", "loc": [-86.444084, 37.684826], "pop": 5, "state": "KY", "_id": "40103"} -{"city": "BATTLETOWN", "loc": [-86.299104, 38.039295], "pop": 2532, "state": "KY", "_id": "40104"} -{"city": "BIG SPRING", "loc": [-86.191791, 37.798795], "pop": 315, "state": "KY", "_id": "40106"} -{"city": "BOSTON", "loc": [-85.619927, 37.763991], "pop": 2589, "state": "KY", "_id": "40107"} -{"city": "BRANDENBURG", "loc": [-86.108359, 37.96624], "pop": 6480, "state": "KY", "_id": "40108"} -{"city": "BROOKS", "loc": [-85.771344, 38.054576], "pop": 4447, "state": "KY", "_id": "40109"} -{"city": "CLOVERPORT", "loc": [-86.595401, 37.822125], "pop": 2852, "state": "KY", "_id": "40111"} -{"city": "CONSTANTINE", "loc": [-86.24009, 37.679412], "pop": 67, "state": "KY", "_id": "40114"} -{"city": "CUSTER", "loc": [-86.271006, 37.742593], "pop": 47, "state": "KY", "_id": "40115"} -{"city": "EKRON", "loc": [-86.154212, 37.91129], "pop": 2565, "state": "KY", "_id": "40117"} -{"city": "FAIRDALE", "loc": [-85.754924, 38.108668], "pop": 8297, "state": "KY", "_id": "40118"} -{"city": "GLEN DEAN", "loc": [-86.510747, 37.569353], "pop": 770, "state": "KY", "_id": "40119"} -{"city": "FORT KNOX", "loc": [-85.948909, 37.892798], "pop": 15407, "state": "KY", "_id": "40121"} -{"city": "GARFIELD", "loc": [-86.316868, 37.814508], "pop": 613, "state": "KY", "_id": "40140"} -{"city": "40141", "loc": [-86.518324, 37.650765], "pop": 1294, "state": "KY", "_id": "40141"} -{"city": "GUSTON", "loc": [-86.215515, 37.89511], "pop": 1525, "state": "KY", "_id": "40142"} -{"city": "MOOLEYVILLE", "loc": [-86.416856, 37.772548], "pop": 3995, "state": "KY", "_id": "40143"} -{"city": "LOCUST HILL", "loc": [-86.37779, 37.742925], "pop": 1648, "state": "KY", "_id": "40144"} -{"city": "HUDSON", "loc": [-86.301238, 37.650613], "pop": 55, "state": "KY", "_id": "40145"} -{"city": "IRVINGTON", "loc": [-86.296534, 37.876239], "pop": 2335, "state": "KY", "_id": "40146"} -{"city": "LEBANON JUNCTION", "loc": [-85.724641, 37.851054], "pop": 5051, "state": "KY", "_id": "40150"} -{"city": "MC DANIELS", "loc": [-86.397278, 37.595251], "pop": 44, "state": "KY", "_id": "40152"} -{"city": "PAYNEVILLE", "loc": [-86.408164, 38.030128], "pop": 504, "state": "KY", "_id": "40157"} -{"city": "RADCLIFF", "loc": [-85.940408, 37.826652], "pop": 21490, "state": "KY", "_id": "40160"} -{"city": "RHODELIA", "loc": [-86.304079, 37.968538], "pop": 1322, "state": "KY", "_id": "40161"} -{"city": "RINEYVILLE", "loc": [-85.995395, 37.75249], "pop": 4542, "state": "KY", "_id": "40162"} -{"city": "40163", "loc": [-86.497033, 37.911025], "pop": 46, "state": "KY", "_id": "40163"} -{"city": "SE REE", "loc": [-86.399176, 37.676018], "pop": 299, "state": "KY", "_id": "40164"} -{"city": "SHEPHERDSVILLE", "loc": [-85.688767, 38.004515], "pop": 18649, "state": "KY", "_id": "40165"} -{"city": "STEPHENSPORT", "loc": [-86.523961, 37.904781], "pop": 134, "state": "KY", "_id": "40170"} -{"city": "UNION STAR", "loc": [-86.461519, 37.961589], "pop": 606, "state": "KY", "_id": "40171"} -{"city": "VINE GROVE", "loc": [-86.006888, 37.858937], "pop": 13294, "state": "KY", "_id": "40175"} -{"city": "WEBSTER", "loc": [-86.403204, 37.923936], "pop": 258, "state": "KY", "_id": "40176"} -{"city": "WEST POINT", "loc": [-85.954502, 37.995408], "pop": 1235, "state": "KY", "_id": "40177"} -{"city": "WESTVIEW", "loc": [-86.427286, 37.679196], "pop": 223, "state": "KY", "_id": "40178"} -{"city": "LOUISVILLE", "loc": [-85.747646, 38.250734], "pop": 5221, "state": "KY", "_id": "40202"} -{"city": "LOUISVILLE", "loc": [-85.762595, 38.245332], "pop": 19432, "state": "KY", "_id": "40203"} -{"city": "LOUISVILLE", "loc": [-85.724938, 38.236936], "pop": 16467, "state": "KY", "_id": "40204"} -{"city": "LOUISVILLE", "loc": [-85.688542, 38.22217], "pop": 24979, "state": "KY", "_id": "40205"} -{"city": "SAINT MATTHEWS", "loc": [-85.697581, 38.256495], "pop": 20604, "state": "KY", "_id": "40206"} -{"city": "SAINT MATTHEWS", "loc": [-85.649689, 38.257908], "pop": 30193, "state": "KY", "_id": "40207"} -{"city": "LOUISVILLE", "loc": [-85.764823, 38.219988], "pop": 13523, "state": "KY", "_id": "40208"} -{"city": "LOUISVILLE", "loc": [-85.751904, 38.190125], "pop": 2752, "state": "KY", "_id": "40209"} -{"city": "LOUISVILLE", "loc": [-85.790548, 38.230585], "pop": 18623, "state": "KY", "_id": "40210"} -{"city": "LOUISVILLE", "loc": [-85.81265, 38.241958], "pop": 26422, "state": "KY", "_id": "40211"} -{"city": "LOUISVILLE", "loc": [-85.804479, 38.265116], "pop": 21605, "state": "KY", "_id": "40212"} -{"city": "LOUISVILLE", "loc": [-85.710642, 38.183929], "pop": 21288, "state": "KY", "_id": "40213"} -{"city": "LOUISVILLE", "loc": [-85.778027, 38.159318], "pop": 42198, "state": "KY", "_id": "40214"} -{"city": "LOUISVILLE", "loc": [-85.784707, 38.191319], "pop": 24661, "state": "KY", "_id": "40215"} -{"city": "SHIVELY", "loc": [-85.831771, 38.186138], "pop": 41719, "state": "KY", "_id": "40216"} -{"city": "LOUISVILLE", "loc": [-85.740371, 38.21736], "pop": 13213, "state": "KY", "_id": "40217"} -{"city": "BUECHEL", "loc": [-85.654834, 38.191084], "pop": 31434, "state": "KY", "_id": "40218"} -{"city": "OKOLONA", "loc": [-85.680548, 38.141291], "pop": 38009, "state": "KY", "_id": "40219"} -{"city": "LOUISVILLE", "loc": [-85.624489, 38.21494], "pop": 29012, "state": "KY", "_id": "40220"} -{"city": "LYNDON", "loc": [-85.611183, 38.263825], "pop": 19924, "state": "KY", "_id": "40222"} -{"city": "ANCHORAGE", "loc": [-85.561151, 38.253688], "pop": 19766, "state": "KY", "_id": "40223"} -{"city": "BUECHEL", "loc": [-85.630967, 38.1392], "pop": 9192, "state": "KY", "_id": "40228"} -{"city": "OKOLONA", "loc": [-85.671889, 38.090655], "pop": 26878, "state": "KY", "_id": "40229"} -{"city": "LYNDON", "loc": [-85.582421, 38.301509], "pop": 14137, "state": "KY", "_id": "40241"} -{"city": "LYNDON", "loc": [-85.590224, 38.276858], "pop": 9742, "state": "KY", "_id": "40242"} -{"city": "MIDDLETOWN", "loc": [-85.537381, 38.240115], "pop": 9317, "state": "KY", "_id": "40243"} -{"city": "LOUISVILLE", "loc": [-85.484461, 38.268273], "pop": 8662, "state": "KY", "_id": "40245"} -{"city": "PLEASURE RIDGE P", "loc": [-85.862505, 38.142369], "pop": 24515, "state": "KY", "_id": "40258"} -{"city": "VALLEY STATION", "loc": [-85.858701, 38.097063], "pop": 32843, "state": "KY", "_id": "40272"} -{"city": "FERN CREEK", "loc": [-85.594513, 38.15205], "pop": 22429, "state": "KY", "_id": "40291"} -{"city": "JEFFERSONTOWN", "loc": [-85.568947, 38.188491], "pop": 23882, "state": "KY", "_id": "40299"} -{"city": "CARLISLE", "loc": [-84.027902, 38.321233], "pop": 6825, "state": "KY", "_id": "40311"} -{"city": "WESTBEND", "loc": [-83.934693, 37.860571], "pop": 5408, "state": "KY", "_id": "40312"} -{"city": "CLEARFIELD", "loc": [-83.327289, 38.143992], "pop": 1942, "state": "KY", "_id": "40313"} -{"city": "DENNISTON", "loc": [-83.513136, 37.919068], "pop": 238, "state": "KY", "_id": "40316"} -{"city": "SCRANTON", "loc": [-83.642405, 37.946031], "pop": 3175, "state": "KY", "_id": "40322"} -{"city": "GEORGETOWN", "loc": [-84.556179, 38.211712], "pop": 18666, "state": "KY", "_id": "40324"} -{"city": "GRATZ", "loc": [-84.942882, 38.478104], "pop": 216, "state": "KY", "_id": "40327"} -{"city": "GRAVEL SWITCH", "loc": [-85.082403, 37.575802], "pop": 962, "state": "KY", "_id": "40328"} -{"city": "CORNISHVILLE", "loc": [-84.84675, 37.769989], "pop": 17727, "state": "KY", "_id": "40330"} -{"city": "JINKS", "loc": [-83.996172, 37.705051], "pop": 13349, "state": "KY", "_id": "40336"} -{"city": "JEFFERSONVILLE", "loc": [-83.855778, 37.964004], "pop": 4303, "state": "KY", "_id": "40337"} -{"city": "KEENE", "loc": [-84.651924, 37.941057], "pop": 253, "state": "KY", "_id": "40339"} -{"city": "LAMERO", "loc": [-84.162353, 37.319166], "pop": 25, "state": "KY", "_id": "40341"} -{"city": "LAWRENCEBURG", "loc": [-84.929938, 38.018853], "pop": 13485, "state": "KY", "_id": "40342"} -{"city": "MARIBA", "loc": [-83.553692, 37.913527], "pop": 374, "state": "KY", "_id": "40345"} -{"city": "MEANS", "loc": [-83.725343, 37.928382], "pop": 324, "state": "KY", "_id": "40346"} -{"city": "MIDWAY", "loc": [-84.692845, 38.148741], "pop": 2079, "state": "KY", "_id": "40347"} -{"city": "MOOREFIELD", "loc": [-83.892889, 38.292627], "pop": 383, "state": "KY", "_id": "40350"} -{"city": "MOREHEAD", "loc": [-83.443645, 38.199047], "pop": 18413, "state": "KY", "_id": "40351"} -{"city": "MOUNT STERLING", "loc": [-83.938774, 38.054822], "pop": 15242, "state": "KY", "_id": "40353"} -{"city": "NEW LIBERTY", "loc": [-84.901064, 38.624556], "pop": 2262, "state": "KY", "_id": "40355"} -{"city": "NICHOLASVILLE", "loc": [-84.56459, 37.880794], "pop": 22920, "state": "KY", "_id": "40356"} -{"city": "OLYMPIA", "loc": [-83.758456, 38.061089], "pop": 640, "state": "KY", "_id": "40358"} -{"city": "OWENTON", "loc": [-84.808632, 38.4986], "pop": 6326, "state": "KY", "_id": "40359"} -{"city": "OWINGSVILLE", "loc": [-83.756439, 38.153196], "pop": 4583, "state": "KY", "_id": "40360"} -{"city": "PARIS", "loc": [-84.244987, 38.208293], "pop": 18722, "state": "KY", "_id": "40361"} -{"city": "POMEROYTON", "loc": [-83.526315, 37.874352], "pop": 480, "state": "KY", "_id": "40365"} -{"city": "SADIEVILLE", "loc": [-84.538424, 38.390816], "pop": 2694, "state": "KY", "_id": "40370"} -{"city": "SALT LICK", "loc": [-83.631619, 38.104031], "pop": 2687, "state": "KY", "_id": "40371"} -{"city": "BONDVILLE", "loc": [-84.884546, 37.915078], "pop": 1684, "state": "KY", "_id": "40372"} -{"city": "SHARPSBURG", "loc": [-83.893199, 38.214742], "pop": 1685, "state": "KY", "_id": "40374"} -{"city": "SLADE", "loc": [-83.684852, 37.788958], "pop": 318, "state": "KY", "_id": "40376"} -{"city": "STAMPING GROUND", "loc": [-84.681824, 38.288802], "pop": 3757, "state": "KY", "_id": "40379"} -{"city": "PATSEY", "loc": [-83.829947, 37.842703], "pop": 5966, "state": "KY", "_id": "40380"} -{"city": "VERSAILLES", "loc": [-84.728683, 38.041301], "pop": 18204, "state": "KY", "_id": "40383"} -{"city": "BYBEE", "loc": [-84.117061, 37.766833], "pop": 1433, "state": "KY", "_id": "40385"} -{"city": "KOREA", "loc": [-83.47305, 37.965353], "pop": 501, "state": "KY", "_id": "40387"} -{"city": "HIGH BRIDGE", "loc": [-84.662079, 37.862141], "pop": 5600, "state": "KY", "_id": "40390"} -{"city": "WINCHESTER", "loc": [-84.178894, 37.987161], "pop": 29149, "state": "KY", "_id": "40391"} -{"city": "MOORES CREEK", "loc": [-83.959737, 37.306965], "pop": 1941, "state": "KY", "_id": "40402"} -{"city": "BEREA", "loc": [-84.274919, 37.579942], "pop": 16561, "state": "KY", "_id": "40403"} -{"city": "BRODHEAD", "loc": [-84.43358, 37.381524], "pop": 2596, "state": "KY", "_id": "40409"} -{"city": "COBHILL", "loc": [-83.840053, 37.743428], "pop": 284, "state": "KY", "_id": "40415"} -{"city": "CONWAY", "loc": [-84.308072, 37.479933], "pop": 2806, "state": "KY", "_id": "40417"} -{"city": "CRAB ORCHARD", "loc": [-84.493912, 37.446523], "pop": 5197, "state": "KY", "_id": "40419"} -{"city": "DANVILLE", "loc": [-84.774696, 37.646524], "pop": 19483, "state": "KY", "_id": "40422"} -{"city": "DREYFUS", "loc": [-84.154495, 37.650696], "pop": 562, "state": "KY", "_id": "40426"} -{"city": "HUSTONVILLE", "loc": [-84.852775, 37.459503], "pop": 2965, "state": "KY", "_id": "40437"} -{"city": "JUNCTION CITY", "loc": [-84.802782, 37.582157], "pop": 3582, "state": "KY", "_id": "40440"} -{"city": "KINGS MOUNTAIN", "loc": [-84.714764, 37.381795], "pop": 2385, "state": "KY", "_id": "40442"} -{"city": "LANCASTER", "loc": [-84.596912, 37.658391], "pop": 8664, "state": "KY", "_id": "40444"} -{"city": "LIVINGSTON", "loc": [-84.231776, 37.307391], "pop": 1320, "state": "KY", "_id": "40445"} -{"city": "CLOVER BOTTOM", "loc": [-84.00896, 37.431678], "pop": 8071, "state": "KY", "_id": "40447"} -{"city": "CLIMAX", "loc": [-84.354882, 37.336609], "pop": 5636, "state": "KY", "_id": "40456"} -{"city": "ORLANDO", "loc": [-84.252801, 37.373405], "pop": 1429, "state": "KY", "_id": "40460"} -{"city": "PAINT LICK", "loc": [-84.426869, 37.609231], "pop": 3361, "state": "KY", "_id": "40461"} -{"city": "PARKSVILLE", "loc": [-84.928132, 37.577768], "pop": 893, "state": "KY", "_id": "40464"} -{"city": "PERRYVILLE", "loc": [-84.966508, 37.637494], "pop": 2019, "state": "KY", "_id": "40468"} -{"city": "PRYSE", "loc": [-83.842586, 37.684068], "pop": 250, "state": "KY", "_id": "40471"} -{"city": "RAVENNA", "loc": [-83.938657, 37.686711], "pop": 798, "state": "KY", "_id": "40472"} -{"city": "RICHMOND", "loc": [-84.295526, 37.754605], "pop": 37946, "state": "KY", "_id": "40475"} -{"city": "SANDGAP", "loc": [-84.056676, 37.487097], "pop": 837, "state": "KY", "_id": "40481"} -{"city": "STANFORD", "loc": [-84.691177, 37.524529], "pop": 9072, "state": "KY", "_id": "40484"} -{"city": "ELIAS", "loc": [-83.870674, 37.343023], "pop": 837, "state": "KY", "_id": "40486"} -{"city": "WAYNESBURG", "loc": [-84.665486, 37.349932], "pop": 4024, "state": "KY", "_id": "40489"} -{"city": "LEXINGTON", "loc": [-84.485423, 38.017394], "pop": 30886, "state": "KY", "_id": "40502"} -{"city": "LEXINGTON", "loc": [-84.52821, 38.001002], "pop": 33537, "state": "KY", "_id": "40503"} -{"city": "LEXINGTON", "loc": [-84.543325, 38.040628], "pop": 22397, "state": "KY", "_id": "40504"} -{"city": "LEXINGTON", "loc": [-84.458338, 38.061201], "pop": 28344, "state": "KY", "_id": "40505"} -{"city": "LEXINGTON", "loc": [-84.495289, 38.046385], "pop": 2291, "state": "KY", "_id": "40507"} -{"city": "LEXINGTON", "loc": [-84.496435, 38.04754], "pop": 25161, "state": "KY", "_id": "40508"} -{"city": "LEXINGTON", "loc": [-84.427419, 38.010166], "pop": 9029, "state": "KY", "_id": "40509"} -{"city": "LEXINGTON", "loc": [-84.591046, 38.070211], "pop": 1467, "state": "KY", "_id": "40510"} -{"city": "LEXINGTON", "loc": [-84.500671, 38.093233], "pop": 16309, "state": "KY", "_id": "40511"} -{"city": "LEXINGTON", "loc": [-84.581522, 38.01388], "pop": 3383, "state": "KY", "_id": "40513"} -{"city": "LEXINGTON", "loc": [-84.576667, 37.983291], "pop": 6252, "state": "KY", "_id": "40514"} -{"city": "LEXINGTON", "loc": [-84.470751, 37.965102], "pop": 20125, "state": "KY", "_id": "40515"} -{"city": "LEXINGTON", "loc": [-84.354802, 38.054355], "pop": 1294, "state": "KY", "_id": "40516"} -{"city": "LEXINGTON", "loc": [-84.481588, 37.984864], "pop": 26787, "state": "KY", "_id": "40517"} -{"city": "HATTON", "loc": [-84.88061, 38.192831], "pop": 46563, "state": "KY", "_id": "40601"} -{"city": "CORBIN", "loc": [-84.10208, 36.934429], "pop": 17476, "state": "KY", "_id": "40701"} -{"city": "BUSH", "loc": [-83.976117, 37.070874], "pop": 5759, "state": "KY", "_id": "40724"} -{"city": "SYMBOL", "loc": [-84.145676, 37.190779], "pop": 8589, "state": "KY", "_id": "40729"} -{"city": "GRAY", "loc": [-83.982799, 36.946743], "pop": 2418, "state": "KY", "_id": "40734"} -{"city": "KEAVY", "loc": [-84.143606, 37.015604], "pop": 1323, "state": "KY", "_id": "40737"} -{"city": "LILY", "loc": [-84.08071, 37.002295], "pop": 7471, "state": "KY", "_id": "40740"} -{"city": "SASSER", "loc": [-84.097498, 37.101605], "pop": 20329, "state": "KY", "_id": "40741"} -{"city": "NEVISDALE", "loc": [-84.111113, 36.663711], "pop": 157, "state": "KY", "_id": "40754"} -{"city": "ROCKHOLDS", "loc": [-84.104048, 36.824216], "pop": 1433, "state": "KY", "_id": "40759"} -{"city": "SILER", "loc": [-83.964192, 36.720352], "pop": 151, "state": "KY", "_id": "40763"} -{"city": "PLEASANT VIEW", "loc": [-84.143592, 36.735535], "pop": 20235, "state": "KY", "_id": "40769"} -{"city": "WOODBINE", "loc": [-84.082743, 36.918793], "pop": 10, "state": "KY", "_id": "40771"} -{"city": "AGES BROOKSIDE", "loc": [-83.235294, 36.840734], "pop": 164, "state": "KY", "_id": "40801"} -{"city": "BAXTER", "loc": [-83.314082, 36.874913], "pop": 2151, "state": "KY", "_id": "40806"} -{"city": "BENHAM", "loc": [-82.909399, 36.957377], "pop": 98, "state": "KY", "_id": "40807"} -{"city": "BIG LAUREL", "loc": [-83.156468, 37.002795], "pop": 0, "state": "KY", "_id": "40808"} -{"city": "LEWIS CREEK", "loc": [-83.353901, 36.912348], "pop": 567, "state": "KY", "_id": "40810"} -{"city": "CALVIN", "loc": [-83.737144, 36.707818], "pop": 2047, "state": "KY", "_id": "40813"} -{"city": "CRUMMIES", "loc": [-83.265719, 36.74619], "pop": 154, "state": "KY", "_id": "40815"} -{"city": "40817", "loc": [-83.387894, 36.847053], "pop": 592, "state": "KY", "_id": "40817"} -{"city": "COALGOOD", "loc": [-83.238782, 36.793394], "pop": 1218, "state": "KY", "_id": "40818"} -{"city": "COLDIRON", "loc": [-83.409591, 36.877718], "pop": 392, "state": "KY", "_id": "40819"} -{"city": "CRANKS", "loc": [-83.203948, 36.760228], "pop": 529, "state": "KY", "_id": "40820"} -{"city": "CUMBERLAND", "loc": [-82.977052, 36.971085], "pop": 6601, "state": "KY", "_id": "40823"} -{"city": "DAYHOIT", "loc": [-83.394329, 36.864603], "pop": 44, "state": "KY", "_id": "40824"} -{"city": "DIZNEY", "loc": [-83.117659, 36.817697], "pop": 42, "state": "KY", "_id": "40825"} -{"city": "EOLIA", "loc": [-82.77062, 37.061767], "pop": 676, "state": "KY", "_id": "40826"} -{"city": "LOUELLEN", "loc": [-83.181648, 36.855294], "pop": 4578, "state": "KY", "_id": "40828"} -{"city": "GRAYS KNOB", "loc": [-83.323998, 36.811105], "pop": 3057, "state": "KY", "_id": "40829"} -{"city": "GULSTON", "loc": [-83.326227, 36.765622], "pop": 456, "state": "KY", "_id": "40830"} -{"city": "CHEVROLET", "loc": [-83.319573, 36.84999], "pop": 6636, "state": "KY", "_id": "40831"} -{"city": "HOLMES MILL", "loc": [-82.994304, 36.875652], "pop": 624, "state": "KY", "_id": "40843"} -{"city": "HULEN", "loc": [-83.555132, 36.771449], "pop": 2719, "state": "KY", "_id": "40845"} -{"city": "KEITH", "loc": [-83.361367, 36.871241], "pop": 185, "state": "KY", "_id": "40846"} -{"city": "KENVIR", "loc": [-83.16055, 36.777596], "pop": 346, "state": "KY", "_id": "40847"} -{"city": "LEJUNIOR", "loc": [-83.190486, 36.921199], "pop": 1536, "state": "KY", "_id": "40849"} -{"city": "LYNCH", "loc": [-82.915629, 36.997856], "pop": 125, "state": "KY", "_id": "40855"} -{"city": "MOZELLE", "loc": [-83.413499, 37.003666], "pop": 2762, "state": "KY", "_id": "40858"} -{"city": "OVEN FORK", "loc": [-82.818484, 37.053929], "pop": 356, "state": "KY", "_id": "40861"} -{"city": "PARTRIDGE", "loc": [-82.877168, 37.012932], "pop": 861, "state": "KY", "_id": "40862"} -{"city": "PATHFORK", "loc": [-83.462925, 36.752071], "pop": 545, "state": "KY", "_id": "40863"} -{"city": "PUTNEY", "loc": [-83.260793, 36.925907], "pop": 348, "state": "KY", "_id": "40865"} -{"city": "SMITH", "loc": [-83.320906, 36.71869], "pop": 312, "state": "KY", "_id": "40867"} -{"city": "STINNETT", "loc": [-83.390742, 37.08835], "pop": 324, "state": "KY", "_id": "40868"} -{"city": "TOTZ", "loc": [-83.201891, 36.970968], "pop": 301, "state": "KY", "_id": "40870"} -{"city": "WALLINS CREEK", "loc": [-83.418968, 36.816532], "pop": 3386, "state": "KY", "_id": "40873"} -{"city": "ARJAY", "loc": [-83.641537, 36.828342], "pop": 1676, "state": "KY", "_id": "40902"} -{"city": "ARTEMUS", "loc": [-83.832886, 36.838837], "pop": 520, "state": "KY", "_id": "40903"} -{"city": "40905", "loc": [-83.919839, 36.929529], "pop": 531, "state": "KY", "_id": "40905"} -{"city": "BAILEY SWITCH", "loc": [-83.899316, 36.864807], "pop": 8506, "state": "KY", "_id": "40906"} -{"city": "BEVERLY", "loc": [-83.556031, 36.944179], "pop": 63, "state": "KY", "_id": "40913"} -{"city": "BIG CREEK", "loc": [-83.633095, 37.14168], "pop": 3877, "state": "KY", "_id": "40914"} -{"city": "BIMBLE", "loc": [-83.828179, 36.886751], "pop": 836, "state": "KY", "_id": "40915"} -{"city": "BRYANTS STORE", "loc": [-83.930729, 36.783087], "pop": 907, "state": "KY", "_id": "40921"} -{"city": "CANNON", "loc": [-83.842498, 36.94888], "pop": 2396, "state": "KY", "_id": "40923"} -{"city": "CLOSPLINT", "loc": [-83.099766, 36.89227], "pop": 1472, "state": "KY", "_id": "40927"} -{"city": "DEWITT", "loc": [-83.73156, 36.8738], "pop": 379, "state": "KY", "_id": "40930"} -{"city": "SALT GUM", "loc": [-83.771397, 36.834531], "pop": 1706, "state": "KY", "_id": "40935"} -{"city": "FONDE", "loc": [-83.895022, 36.64455], "pop": 1326, "state": "KY", "_id": "40940"} -{"city": "GIRDLER", "loc": [-83.85388, 36.969096], "pop": 229, "state": "KY", "_id": "40943"} -{"city": "GREEN ROAD", "loc": [-83.895768, 36.998937], "pop": 360, "state": "KY", "_id": "40946"} -{"city": "HEIDRICK", "loc": [-83.866509, 36.892696], "pop": 1362, "state": "KY", "_id": "40949"} -{"city": "HINKLE", "loc": [-83.780441, 36.875323], "pop": 269, "state": "KY", "_id": "40953"} -{"city": "KETTLE ISLAND", "loc": [-83.589183, 36.809344], "pop": 710, "state": "KY", "_id": "40958"} -{"city": "BRIGHT SHADE", "loc": [-83.768291, 37.142321], "pop": 13727, "state": "KY", "_id": "40962"} -{"city": "MARY ALICE", "loc": [-83.424408, 36.706797], "pop": 115, "state": "KY", "_id": "40964"} -{"city": "MIDDLESBORO", "loc": [-83.723079, 36.617188], "pop": 14900, "state": "KY", "_id": "40965"} -{"city": "MILLS", "loc": [-83.605305, 36.940659], "pop": 471, "state": "KY", "_id": "40970"} -{"city": "ONEIDA", "loc": [-83.655467, 37.2677], "pop": 2371, "state": "KY", "_id": "40972"} -{"city": "CALLAWAY", "loc": [-83.698502, 36.751302], "pop": 7658, "state": "KY", "_id": "40977"} -{"city": "ROARK", "loc": [-83.506201, 37.024432], "pop": 463, "state": "KY", "_id": "40979"} -{"city": "40980", "loc": [-83.69705, 36.964535], "pop": 448, "state": "KY", "_id": "40980"} -{"city": "SCALF", "loc": [-83.726554, 36.91748], "pop": 600, "state": "KY", "_id": "40982"} -{"city": "SEXTONS CREEK", "loc": [-83.798378, 37.27334], "pop": 1776, "state": "KY", "_id": "40983"} -{"city": "STONEY FORK", "loc": [-83.538504, 36.889184], "pop": 387, "state": "KY", "_id": "40988"} -{"city": "TROSPER", "loc": [-83.831442, 36.782564], "pop": 1245, "state": "KY", "_id": "40995"} -{"city": "WALKER", "loc": [-83.6616, 36.891138], "pop": 120, "state": "KY", "_id": "40997"} -{"city": "WOOLLUM", "loc": [-83.847202, 37.009657], "pop": 219, "state": "KY", "_id": "40999"} -{"city": "ALEXANDRIA", "loc": [-84.394329, 38.940605], "pop": 11858, "state": "KY", "_id": "41001"} -{"city": "AUGUSTA", "loc": [-83.995352, 38.762991], "pop": 1902, "state": "KY", "_id": "41002"} -{"city": "BERRY", "loc": [-84.361118, 38.515974], "pop": 2083, "state": "KY", "_id": "41003"} -{"city": "BROOKSVILLE", "loc": [-84.078649, 38.664387], "pop": 4309, "state": "KY", "_id": "41004"} -{"city": "RABBIT HASH", "loc": [-84.74142, 39.032411], "pop": 8322, "state": "KY", "_id": "41005"} -{"city": "BUTLER", "loc": [-84.34458, 38.801328], "pop": 4743, "state": "KY", "_id": "41006"} -{"city": "CALIFORNIA", "loc": [-84.309794, 38.900614], "pop": 3427, "state": "KY", "_id": "41007"} -{"city": "CARROLLTON", "loc": [-85.173036, 38.669565], "pop": 7072, "state": "KY", "_id": "41008"} -{"city": "CORINTH", "loc": [-84.584596, 38.53021], "pop": 1701, "state": "KY", "_id": "41010"} -{"city": "COVINGTON", "loc": [-84.52121, 39.070839], "pop": 29098, "state": "KY", "_id": "41011"} -{"city": "ROUSE", "loc": [-84.505061, 39.066593], "pop": 8607, "state": "KY", "_id": "41014"} -{"city": "LATONIA", "loc": [-84.498858, 39.021686], "pop": 19857, "state": "KY", "_id": "41015"} -{"city": "LUDLOW", "loc": [-84.548304, 39.088946], "pop": 7210, "state": "KY", "_id": "41016"} -{"city": "DIXIE", "loc": [-84.569611, 39.032378], "pop": 34285, "state": "KY", "_id": "41017"} -{"city": "ERLANGER", "loc": [-84.597745, 39.008238], "pop": 23909, "state": "KY", "_id": "41018"} -{"city": "CRITTENDEN", "loc": [-84.598202, 38.774107], "pop": 2630, "state": "KY", "_id": "41030"} -{"city": "CYNTHIANA", "loc": [-84.294921, 38.396403], "pop": 13375, "state": "KY", "_id": "41031"} -{"city": "DEMOSSVILLE", "loc": [-84.470802, 38.75165], "pop": 1292, "state": "KY", "_id": "41033"} -{"city": "DOVER", "loc": [-83.871793, 38.691016], "pop": 1991, "state": "KY", "_id": "41034"} -{"city": "DRY RIDGE", "loc": [-84.623736, 38.704854], "pop": 6092, "state": "KY", "_id": "41035"} -{"city": "EWING", "loc": [-83.873317, 38.415651], "pop": 2396, "state": "KY", "_id": "41039"} -{"city": "FALMOUTH", "loc": [-84.345124, 38.664304], "pop": 6779, "state": "KY", "_id": "41040"} -{"city": "FLEMINGSBURG", "loc": [-83.707968, 38.427983], "pop": 6549, "state": "KY", "_id": "41041"} -{"city": "FLORENCE", "loc": [-84.641961, 38.994065], "pop": 34010, "state": "KY", "_id": "41042"} -{"city": "FOSTER", "loc": [-84.156593, 38.75064], "pop": 1479, "state": "KY", "_id": "41043"} -{"city": "GERMANTOWN", "loc": [-83.960616, 38.707534], "pop": 88, "state": "KY", "_id": "41044"} -{"city": "GHENT", "loc": [-85.055662, 38.718823], "pop": 980, "state": "KY", "_id": "41045"} -{"city": "GLENCOE", "loc": [-84.811552, 38.722946], "pop": 589, "state": "KY", "_id": "41046"} -{"city": "HEBRON", "loc": [-84.700745, 39.07553], "pop": 3497, "state": "KY", "_id": "41048"} -{"city": "HILLSBORO", "loc": [-83.66967, 38.29294], "pop": 1981, "state": "KY", "_id": "41049"} -{"city": "INDEPENDENCE", "loc": [-84.547912, 38.935407], "pop": 12778, "state": "KY", "_id": "41051"} -{"city": "JONESVILLE", "loc": [-84.76367, 38.648251], "pop": 295, "state": "KY", "_id": "41052"} -{"city": "MAYS LICK", "loc": [-83.874874, 38.52692], "pop": 1786, "state": "KY", "_id": "41055"} -{"city": "LIMESTONE SQ", "loc": [-83.758858, 38.619716], "pop": 12889, "state": "KY", "_id": "41056"} -{"city": "MELBOURNE", "loc": [-84.353751, 39.006699], "pop": 2688, "state": "KY", "_id": "41059"} -{"city": "MORNING VIEW", "loc": [-84.506918, 38.83942], "pop": 3938, "state": "KY", "_id": "41063"} -{"city": "MOUNT OLIVET", "loc": [-84.047977, 38.521751], "pop": 2124, "state": "KY", "_id": "41064"} -{"city": "SOUTHGATE", "loc": [-84.486511, 39.082847], "pop": 22918, "state": "KY", "_id": "41071"} -{"city": "BELLEVUE", "loc": [-84.478746, 39.102431], "pop": 6997, "state": "KY", "_id": "41073"} -{"city": "DAYTON", "loc": [-84.471155, 39.111428], "pop": 6576, "state": "KY", "_id": "41074"} -{"city": "FORT THOMAS", "loc": [-84.45234, 39.078634], "pop": 16165, "state": "KY", "_id": "41075"} -{"city": "NEWPORT", "loc": [-84.440792, 39.026184], "pop": 11237, "state": "KY", "_id": "41076"} -{"city": "PETERSBURG", "loc": [-84.837104, 39.041561], "pop": 1219, "state": "KY", "_id": "41080"} -{"city": "SANDERS", "loc": [-84.973151, 38.661004], "pop": 516, "state": "KY", "_id": "41083"} -{"city": "SILVER GROVE", "loc": [-84.390849, 39.034262], "pop": 1210, "state": "KY", "_id": "41085"} -{"city": "SPARTA", "loc": [-84.881334, 38.725582], "pop": 1186, "state": "KY", "_id": "41086"} -{"city": "UNION", "loc": [-84.727359, 38.943511], "pop": 4468, "state": "KY", "_id": "41091"} -{"city": "VERONA", "loc": [-84.690722, 38.839012], "pop": 2169, "state": "KY", "_id": "41092"} -{"city": "WALLINGFORD", "loc": [-83.562532, 38.337449], "pop": 1366, "state": "KY", "_id": "41093"} -{"city": "WALTON", "loc": [-84.63277, 38.887491], "pop": 6253, "state": "KY", "_id": "41094"} -{"city": "WARSAW", "loc": [-84.849584, 38.780704], "pop": 3618, "state": "KY", "_id": "41095"} -{"city": "WILLIAMSTOWN", "loc": [-84.57441, 38.629191], "pop": 5019, "state": "KY", "_id": "41097"} -{"city": "WORTHVILLE", "loc": [-85.066321, 38.617948], "pop": 724, "state": "KY", "_id": "41098"} -{"city": "WESTWOOD", "loc": [-82.702709, 38.443038], "pop": 17464, "state": "KY", "_id": "41101"} -{"city": "ASHLAND", "loc": [-82.644721, 38.461255], "pop": 24865, "state": "KY", "_id": "41102"} -{"city": "ARGILLITE", "loc": [-82.809442, 38.432228], "pop": 467, "state": "KY", "_id": "41121"} -{"city": "BLAINE", "loc": [-82.851347, 38.026974], "pop": 982, "state": "KY", "_id": "41124"} -{"city": "CAMP DIX", "loc": [-83.287793, 38.508044], "pop": 269, "state": "KY", "_id": "41127"} -{"city": "CATLETTSBURG", "loc": [-82.632065, 38.379946], "pop": 8851, "state": "KY", "_id": "41129"} -{"city": "DENTON", "loc": [-82.826924, 38.295212], "pop": 452, "state": "KY", "_id": "41132"} -{"city": "HEAD OF GRASSY", "loc": [-83.261786, 38.367225], "pop": 435, "state": "KY", "_id": "41135"} -{"city": "FIREBRICK", "loc": [-83.043203, 38.687785], "pop": 279, "state": "KY", "_id": "41137"} -{"city": "FLATWOODS", "loc": [-82.72122, 38.518776], "pop": 8397, "state": "KY", "_id": "41139"} -{"city": "GARRISON", "loc": [-83.200036, 38.586915], "pop": 3066, "state": "KY", "_id": "41141"} -{"city": "FULTZ", "loc": [-82.965087, 38.328037], "pop": 11771, "state": "KY", "_id": "41143"} -{"city": "LYNN", "loc": [-82.868396, 38.547969], "pop": 11666, "state": "KY", "_id": "41144"} -{"city": "HITCHINS", "loc": [-82.920268, 38.254585], "pop": 3187, "state": "KY", "_id": "41146"} -{"city": "ISONVILLE", "loc": [-83.050607, 38.046229], "pop": 890, "state": "KY", "_id": "41149"} -{"city": "MARTHA", "loc": [-82.95576, 38.015228], "pop": 706, "state": "KY", "_id": "41159"} -{"city": "OLDTOWN", "loc": [-82.95695, 38.441153], "pop": 690, "state": "KY", "_id": "41163"} -{"city": "LAWTON", "loc": [-83.168985, 38.336275], "pop": 5763, "state": "KY", "_id": "41164"} -{"city": "QUINCY", "loc": [-83.105647, 38.627059], "pop": 512, "state": "KY", "_id": "41166"} -{"city": "RUSH", "loc": [-82.747602, 38.30893], "pop": 1502, "state": "KY", "_id": "41168"} -{"city": "RACELAND", "loc": [-82.715632, 38.522575], "pop": 6469, "state": "KY", "_id": "41169"} -{"city": "SAINT PAUL", "loc": [-83.067931, 38.665445], "pop": 222, "state": "KY", "_id": "41170"} -{"city": "BURKE", "loc": [-83.12725, 38.129368], "pop": 5319, "state": "KY", "_id": "41171"} -{"city": "SOUTH PORTSMOUTH", "loc": [-83.016218, 38.708706], "pop": 813, "state": "KY", "_id": "41174"} -{"city": "MALONETON", "loc": [-82.946373, 38.709269], "pop": 4923, "state": "KY", "_id": "41175"} -{"city": "STEPHENS", "loc": [-82.983689, 38.114625], "pop": 261, "state": "KY", "_id": "41177"} -{"city": "41178", "loc": [-83.271776, 38.267723], "pop": 3150, "state": "KY", "_id": "41178"} -{"city": "TRINITY", "loc": [-83.355448, 38.551511], "pop": 5141, "state": "KY", "_id": "41179"} -{"city": "WEBBVILLE", "loc": [-82.789743, 38.163377], "pop": 1654, "state": "KY", "_id": "41180"} -{"city": "WORTHINGTON", "loc": [-82.739553, 38.551102], "pop": 1829, "state": "KY", "_id": "41183"} -{"city": "TOLLESBORO", "loc": [-83.560458, 38.572332], "pop": 3061, "state": "KY", "_id": "41189"} -{"city": "ADAMS", "loc": [-82.702437, 37.991375], "pop": 2540, "state": "KY", "_id": "41201"} -{"city": "BOONS CAMP", "loc": [-82.67043, 37.821609], "pop": 260, "state": "KY", "_id": "41204"} -{"city": "DAVELLA", "loc": [-82.586729, 37.790019], "pop": 766, "state": "KY", "_id": "41214"} -{"city": "DENVER", "loc": [-82.863048, 37.796582], "pop": 573, "state": "KY", "_id": "41215"} -{"city": "EAST POINT", "loc": [-82.828801, 37.708578], "pop": 1014, "state": "KY", "_id": "41216"} -{"city": "ELNA", "loc": [-82.882825, 37.938594], "pop": 766, "state": "KY", "_id": "41219"} -{"city": "FUGET", "loc": [-82.911422, 37.902164], "pop": 285, "state": "KY", "_id": "41220"} -{"city": "HAGERHILL", "loc": [-82.81491, 37.803511], "pop": 739, "state": "KY", "_id": "41222"} -{"city": "JOB", "loc": [-82.520421, 37.867149], "pop": 4231, "state": "KY", "_id": "41224"} -{"city": "41225", "loc": [-82.534771, 37.948703], "pop": 292, "state": "KY", "_id": "41225"} -{"city": "KEATON", "loc": [-82.956105, 37.968049], "pop": 354, "state": "KY", "_id": "41226"} -{"city": "LEANDER", "loc": [-82.831045, 37.761904], "pop": 1037, "state": "KY", "_id": "41228"} -{"city": "CLIFFORD", "loc": [-82.617598, 38.094332], "pop": 8123, "state": "KY", "_id": "41230"} -{"city": "LOVELY", "loc": [-82.363794, 37.792109], "pop": 958, "state": "KY", "_id": "41231"} -{"city": "MEALLY", "loc": [-82.744177, 37.796498], "pop": 925, "state": "KY", "_id": "41234"} -{"city": "OFFUTT", "loc": [-82.708126, 37.852766], "pop": 462, "state": "KY", "_id": "41237"} -{"city": "MANILA", "loc": [-82.928079, 37.826409], "pop": 1430, "state": "KY", "_id": "41238"} -{"city": "NIPPA", "loc": [-82.805162, 37.819293], "pop": 4095, "state": "KY", "_id": "41240"} -{"city": "LAURA", "loc": [-82.448754, 37.763752], "pop": 1700, "state": "KY", "_id": "41250"} -{"city": "RIVER", "loc": [-82.670944, 37.875063], "pop": 169, "state": "KY", "_id": "41254"} -{"city": "SITKA", "loc": [-82.844554, 37.892187], "pop": 598, "state": "KY", "_id": "41255"} -{"city": "BARNETTS CREEK", "loc": [-82.85743, 37.838463], "pop": 1267, "state": "KY", "_id": "41256"} -{"city": "STAMBAUGH", "loc": [-82.76779, 37.84112], "pop": 165, "state": "KY", "_id": "41257"} -{"city": "RICEVILLE", "loc": [-82.911269, 37.743408], "pop": 816, "state": "KY", "_id": "41258"} -{"city": "THELMA", "loc": [-82.775532, 37.798745], "pop": 3956, "state": "KY", "_id": "41260"} -{"city": "DAVISPORT", "loc": [-82.599269, 37.879292], "pop": 1923, "state": "KY", "_id": "41262"} -{"city": "TUTOR KEY", "loc": [-82.735106, 37.860527], "pop": 33, "state": "KY", "_id": "41263"} -{"city": "VAN LEAR", "loc": [-82.703236, 37.692916], "pop": 3387, "state": "KY", "_id": "41265"} -{"city": "FUGET", "loc": [-82.911224, 37.902927], "pop": 764, "state": "KY", "_id": "41266"} -{"city": "HODE", "loc": [-82.432192, 37.858088], "pop": 2656, "state": "KY", "_id": "41267"} -{"city": "WHITEHOUSE", "loc": [-82.775328, 37.895347], "pop": 2105, "state": "KY", "_id": "41269"} -{"city": "WILLIAMSPORT", "loc": [-82.710455, 37.81416], "pop": 295, "state": "KY", "_id": "41271"} -{"city": "WITTENSVILLE", "loc": [-82.809987, 37.870297], "pop": 689, "state": "KY", "_id": "41274"} -{"city": "FLAT", "loc": [-83.516395, 37.750243], "pop": 5038, "state": "KY", "_id": "41301"} -{"city": "ALTRO", "loc": [-83.395749, 37.356346], "pop": 436, "state": "KY", "_id": "41306"} -{"city": "VADA", "loc": [-83.702337, 37.581891], "pop": 6877, "state": "KY", "_id": "41311"} -{"city": "MORRIS FORK", "loc": [-83.660624, 37.478717], "pop": 2675, "state": "KY", "_id": "41314"} -{"city": "BURKHART", "loc": [-83.273396, 37.687451], "pop": 233, "state": "KY", "_id": "41315"} -{"city": "41316", "loc": [-83.460672, 37.465492], "pop": 1093, "state": "KY", "_id": "41316"} -{"city": "CLAYHOLE", "loc": [-83.281254, 37.466076], "pop": 2294, "state": "KY", "_id": "41317"} -{"city": "DECOY", "loc": [-83.028956, 37.49046], "pop": 184, "state": "KY", "_id": "41321"} -{"city": "GILLMORE", "loc": [-83.348026, 37.697155], "pop": 44, "state": "KY", "_id": "41327"} -{"city": "GREEN HALL", "loc": [-83.780693, 37.368938], "pop": 185, "state": "KY", "_id": "41328"} -{"city": "HADDIX", "loc": [-83.376176, 37.492249], "pop": 513, "state": "KY", "_id": "41331"} -{"city": "GRASSY CREEK", "loc": [-83.398918, 37.775989], "pop": 978, "state": "KY", "_id": "41332"} -{"city": "ISLAND CITY", "loc": [-83.717991, 37.389926], "pop": 526, "state": "KY", "_id": "41338"} -{"city": "CANOE", "loc": [-83.396904, 37.557378], "pop": 6384, "state": "KY", "_id": "41339"} -{"city": "LAMBRIC", "loc": [-83.100888, 37.579455], "pop": 14, "state": "KY", "_id": "41340"} -{"city": "LEE CITY", "loc": [-83.325474, 37.731163], "pop": 233, "state": "KY", "_id": "41342"} -{"city": "LEECO", "loc": [-83.692806, 37.698933], "pop": 66, "state": "KY", "_id": "41343"} -{"city": "LITTLE", "loc": [-83.382373, 37.432158], "pop": 247, "state": "KY", "_id": "41346"} -{"city": "HARDSHELL", "loc": [-83.328889, 37.484106], "pop": 375, "state": "KY", "_id": "41348"} -{"city": "MISTLETOE", "loc": [-83.588078, 37.331428], "pop": 72, "state": "KY", "_id": "41351"} -{"city": "NOCTOR", "loc": [-83.270831, 37.569915], "pop": 771, "state": "KY", "_id": "41357"} -{"city": "OLD LANDING", "loc": [-83.805784, 37.639678], "pop": 253, "state": "KY", "_id": "41358"} -{"city": "41359", "loc": [-83.741021, 37.490374], "pop": 320, "state": "KY", "_id": "41359"} -{"city": "PINE RIDGE", "loc": [-83.632059, 37.774872], "pop": 334, "state": "KY", "_id": "41360"} -{"city": "QUICKSAND", "loc": [-83.365098, 37.532947], "pop": 727, "state": "KY", "_id": "41363"} -{"city": "RICETOWN", "loc": [-83.615062, 37.409305], "pop": 672, "state": "KY", "_id": "41364"} -{"city": "ROGERS", "loc": [-83.65667, 37.739538], "pop": 288, "state": "KY", "_id": "41365"} -{"city": "ROUSSEAU", "loc": [-83.21472, 37.63491], "pop": 104, "state": "KY", "_id": "41366"} -{"city": "ROWDY", "loc": [-83.20403, 37.413063], "pop": 245, "state": "KY", "_id": "41367"} -{"city": "SALDEE", "loc": [-83.373268, 37.455799], "pop": 118, "state": "KY", "_id": "41369"} -{"city": "41370", "loc": [-83.502919, 37.388184], "pop": 493, "state": "KY", "_id": "41370"} -{"city": "TALBERT", "loc": [-83.448185, 37.399505], "pop": 352, "state": "KY", "_id": "41377"} -{"city": "VANCLEVE", "loc": [-83.380553, 37.64768], "pop": 1335, "state": "KY", "_id": "41385"} -{"city": "VINCENT", "loc": [-83.784628, 37.443733], "pop": 586, "state": "KY", "_id": "41386"} -{"city": "WHICK", "loc": [-83.374707, 37.41022], "pop": 235, "state": "KY", "_id": "41390"} -{"city": "41393", "loc": [-83.346466, 37.381593], "pop": 215, "state": "KY", "_id": "41393"} -{"city": "ZACHARIAH", "loc": [-83.677244, 37.674917], "pop": 36, "state": "KY", "_id": "41396"} -{"city": "ZOE", "loc": [-83.668778, 37.666371], "pop": 190, "state": "KY", "_id": "41397"} -{"city": "41401", "loc": [-83.187829, 37.77131], "pop": 338, "state": "KY", "_id": "41401"} -{"city": "CANEY", "loc": [-83.291662, 37.809847], "pop": 1754, "state": "KY", "_id": "41407"} -{"city": "CARVER", "loc": [-83.069009, 37.650274], "pop": 368, "state": "KY", "_id": "41409"} -{"city": "COTTLE", "loc": [-83.220078, 37.892975], "pop": 73, "state": "KY", "_id": "41412"} -{"city": "EDNA", "loc": [-83.179973, 37.799813], "pop": 312, "state": "KY", "_id": "41419"} -{"city": "ELKFORK", "loc": [-83.077112, 37.940358], "pop": 2081, "state": "KY", "_id": "41421"} -{"city": "ELSIE", "loc": [-83.142321, 37.768875], "pop": 84, "state": "KY", "_id": "41422"} -{"city": "EZEL", "loc": [-83.388835, 37.903985], "pop": 1488, "state": "KY", "_id": "41425"} -{"city": "41429", "loc": [-83.119869, 37.654647], "pop": 342, "state": "KY", "_id": "41429"} -{"city": "41431", "loc": [-83.128914, 37.716626], "pop": 170, "state": "KY", "_id": "41431"} -{"city": "HENDRICKS", "loc": [-83.098108, 37.70505], "pop": 377, "state": "KY", "_id": "41441"} -{"city": "LENOX", "loc": [-83.181593, 37.967224], "pop": 550, "state": "KY", "_id": "41447"} -{"city": "ROYALTON", "loc": [-82.986669, 37.652116], "pop": 1550, "state": "KY", "_id": "41464"} -{"city": "BETHANNA", "loc": [-83.055443, 37.763499], "pop": 8442, "state": "KY", "_id": "41465"} -{"city": "SEITZ", "loc": [-83.174941, 37.668402], "pop": 395, "state": "KY", "_id": "41466"} -{"city": "BLAIRS MILL", "loc": [-83.279926, 37.949746], "pop": 4700, "state": "KY", "_id": "41472"} -{"city": "WHITE OAK", "loc": [-83.204869, 37.849769], "pop": 361, "state": "KY", "_id": "41474"} -{"city": "BROAD BOTTOM", "loc": [-82.520175, 37.506833], "pop": 17362, "state": "KY", "_id": "41501"} -{"city": "SOUTH WILLIAMSON", "loc": [-82.288608, 37.666991], "pop": 899, "state": "KY", "_id": "41503"} -{"city": "ASHCAMP", "loc": [-82.461269, 37.258909], "pop": 2274, "state": "KY", "_id": "41512"} -{"city": "BELCHER", "loc": [-82.396536, 37.3137], "pop": 111, "state": "KY", "_id": "41513"} -{"city": "BELFRY", "loc": [-82.257301, 37.64011], "pop": 1744, "state": "KY", "_id": "41514"} -{"city": "CANADA", "loc": [-82.35331, 37.637816], "pop": 817, "state": "KY", "_id": "41519"} -{"city": "SENTERVILLE", "loc": [-82.353337, 37.302649], "pop": 1988, "state": "KY", "_id": "41522"} -{"city": "BIGGS", "loc": [-82.257466, 37.361002], "pop": 724, "state": "KY", "_id": "41524"} -{"city": "FOREST HILLS", "loc": [-82.299145, 37.643238], "pop": 363, "state": "KY", "_id": "41527"} -{"city": "FREEBURN", "loc": [-82.143378, 37.551188], "pop": 1749, "state": "KY", "_id": "41528"} -{"city": "AFLEX", "loc": [-82.307961, 37.689943], "pop": 802, "state": "KY", "_id": "41529"} -{"city": "HARDY", "loc": [-82.255236, 37.602652], "pop": 1693, "state": "KY", "_id": "41531"} -{"city": "HUDDY", "loc": [-82.25004, 37.566819], "pop": 513, "state": "KY", "_id": "41535"} -{"city": "JAMBOREE", "loc": [-82.089129, 37.485976], "pop": 848, "state": "KY", "_id": "41536"} -{"city": "PAYNE GAP", "loc": [-82.630045, 37.186328], "pop": 3615, "state": "KY", "_id": "41537"} -{"city": "KIMPER", "loc": [-82.318041, 37.469588], "pop": 2317, "state": "KY", "_id": "41539"} -{"city": "LICK CREEK", "loc": [-82.325456, 37.347969], "pop": 221, "state": "KY", "_id": "41540"} -{"city": "MC ANDREWS", "loc": [-82.2558, 37.544986], "pop": 830, "state": "KY", "_id": "41543"} -{"city": "MC CARR", "loc": [-82.167365, 37.596944], "pop": 1054, "state": "KY", "_id": "41544"} -{"city": "MC COMBS", "loc": [-82.548228, 37.593432], "pop": 173, "state": "KY", "_id": "41545"} -{"city": "MC VEIGH", "loc": [-82.2228, 37.534249], "pop": 603, "state": "KY", "_id": "41546"} -{"city": "MOUTHCARD", "loc": [-82.305424, 37.333908], "pop": 156, "state": "KY", "_id": "41548"} -{"city": "PAW PAW", "loc": [-82.134942, 37.435562], "pop": 228, "state": "KY", "_id": "41551"} -{"city": "PHELPS", "loc": [-82.158378, 37.498736], "pop": 2947, "state": "KY", "_id": "41553"} -{"city": "PHYLLIS", "loc": [-82.266786, 37.422152], "pop": 891, "state": "KY", "_id": "41554"} -{"city": "PINSONFORK", "loc": [-82.195366, 37.565137], "pop": 568, "state": "KY", "_id": "41555"} -{"city": "FISHTRAP", "loc": [-82.426256, 37.524072], "pop": 3807, "state": "KY", "_id": "41557"} -{"city": "REGINA", "loc": [-82.373697, 37.359686], "pop": 1559, "state": "KY", "_id": "41559"} -{"city": "ROBINSON CREEK", "loc": [-82.567679, 37.334708], "pop": 6715, "state": "KY", "_id": "41560"} -{"city": "SHELBIANA", "loc": [-82.455321, 37.375478], "pop": 9187, "state": "KY", "_id": "41562"} -{"city": "SHELBY GAP", "loc": [-82.575149, 37.232743], "pop": 952, "state": "KY", "_id": "41563"} -{"city": "SIDNEY", "loc": [-82.338666, 37.580225], "pop": 1727, "state": "KY", "_id": "41564"} -{"city": "SPEIGHT", "loc": [-82.716502, 37.262473], "pop": 17, "state": "KY", "_id": "41565"} -{"city": "STEELE", "loc": [-82.207079, 37.40292], "pop": 1321, "state": "KY", "_id": "41566"} -{"city": "STONE", "loc": [-82.288987, 37.560639], "pop": 1239, "state": "KY", "_id": "41567"} -{"city": "ARGO", "loc": [-82.073522, 37.530336], "pop": 471, "state": "KY", "_id": "41568"} -{"city": "TURKEY CREEK", "loc": [-82.320225, 37.659347], "pop": 264, "state": "KY", "_id": "41570"} -{"city": "VARNEY", "loc": [-82.350976, 37.698541], "pop": 827, "state": "KY", "_id": "41571"} -{"city": "ETTY", "loc": [-82.649445, 37.313669], "pop": 2622, "state": "KY", "_id": "41572"} -{"city": "ALLEN", "loc": [-82.636272, 37.566182], "pop": 1768, "state": "KY", "_id": "41601"} -{"city": "AUXIER", "loc": [-82.767736, 37.721847], "pop": 2461, "state": "KY", "_id": "41602"} -{"city": "BANNER", "loc": [-82.697369, 37.577051], "pop": 1043, "state": "KY", "_id": "41603"} -{"city": "LIGON", "loc": [-82.680295, 37.372179], "pop": 956, "state": "KY", "_id": "41604"} -{"city": "BETSY LAYNE", "loc": [-82.700249, 37.531593], "pop": 353, "state": "KY", "_id": "41605"} -{"city": "BEVINSVILLE", "loc": [-82.712468, 37.338488], "pop": 3200, "state": "KY", "_id": "41606"} -{"city": "BLUE RIVER", "loc": [-82.827361, 37.648097], "pop": 247, "state": "KY", "_id": "41607"} -{"city": "CRAYNOR", "loc": [-82.69176, 37.408673], "pop": 260, "state": "KY", "_id": "41614"} -{"city": "DANA", "loc": [-82.670501, 37.546653], "pop": 227, "state": "KY", "_id": "41615"} -{"city": "DAVID", "loc": [-82.892748, 37.604387], "pop": 60, "state": "KY", "_id": "41616"} -{"city": "EASTERN", "loc": [-82.826496, 37.532386], "pop": 509, "state": "KY", "_id": "41622"} -{"city": "ENDICOTT", "loc": [-82.627608, 37.681936], "pop": 42, "state": "KY", "_id": "41626"} -{"city": "ESTILL", "loc": [-82.819629, 37.46201], "pop": 263, "state": "KY", "_id": "41627"} -{"city": "GALVESTON", "loc": [-82.661868, 37.478249], "pop": 1030, "state": "KY", "_id": "41629"} -{"city": "GARRETT", "loc": [-82.846192, 37.486126], "pop": 1408, "state": "KY", "_id": "41630"} -{"city": "GRETHEL", "loc": [-82.739575, 37.459599], "pop": 2126, "state": "KY", "_id": "41631"} -{"city": "WALDO", "loc": [-82.93574, 37.546661], "pop": 699, "state": "KY", "_id": "41632"} -{"city": "HALO", "loc": [-82.730606, 37.307629], "pop": 119, "state": "KY", "_id": "41633"} -{"city": "HAROLD", "loc": [-82.621595, 37.504184], "pop": 1286, "state": "KY", "_id": "41635"} -{"city": "BUCKINGHAM", "loc": [-82.73245, 37.393499], "pop": 879, "state": "KY", "_id": "41636"} -{"city": "PYRMID", "loc": [-82.864919, 37.513484], "pop": 568, "state": "KY", "_id": "41637"} -{"city": "HONAKER", "loc": [-82.667534, 37.514977], "pop": 772, "state": "KY", "_id": "41639"} -{"city": "ELMROCK", "loc": [-82.836764, 37.506204], "pop": 115, "state": "KY", "_id": "41640"} -{"city": "IVEL", "loc": [-82.64268, 37.555443], "pop": 1038, "state": "KY", "_id": "41642"} -{"city": "LACKEY", "loc": [-82.798012, 37.464283], "pop": 591, "state": "KY", "_id": "41643"} -{"city": "LANGLEY", "loc": [-82.797648, 37.538079], "pop": 648, "state": "KY", "_id": "41645"} -{"city": "EAST MC DOWELL", "loc": [-82.745018, 37.425232], "pop": 376, "state": "KY", "_id": "41647"} -{"city": "41648", "loc": [-82.798667, 37.566181], "pop": 344, "state": "KY", "_id": "41648"} -{"city": "HITE", "loc": [-82.747852, 37.563267], "pop": 4649, "state": "KY", "_id": "41649"} -{"city": "EMMA", "loc": [-82.788135, 37.649462], "pop": 9573, "state": "KY", "_id": "41653"} -{"city": "PRINTER", "loc": [-82.706027, 37.505485], "pop": 243, "state": "KY", "_id": "41655"} -{"city": "STANVILLE", "loc": [-82.669174, 37.578386], "pop": 309, "state": "KY", "_id": "41659"} -{"city": "TEABERRY", "loc": [-82.640456, 37.421955], "pop": 2248, "state": "KY", "_id": "41660"} -{"city": "WAYLAND", "loc": [-82.801037, 37.436709], "pop": 928, "state": "KY", "_id": "41666"} -{"city": "DARFORK", "loc": [-83.199845, 37.252641], "pop": 11851, "state": "KY", "_id": "41701"} -{"city": "ARY", "loc": [-83.139253, 37.354364], "pop": 331, "state": "KY", "_id": "41712"} -{"city": "BEAR BRANCH", "loc": [-83.494449, 37.210894], "pop": 400, "state": "KY", "_id": "41714"} -{"city": "BLUE DIAMOND", "loc": [-83.228579, 37.310812], "pop": 861, "state": "KY", "_id": "41719"} -{"city": "BUCKHORN", "loc": [-83.493648, 37.301036], "pop": 640, "state": "KY", "_id": "41721"} -{"city": "TRIBBEY", "loc": [-83.158487, 37.328174], "pop": 2312, "state": "KY", "_id": "41722"} -{"city": "BUSY", "loc": [-83.300705, 37.26316], "pop": 967, "state": "KY", "_id": "41723"} -{"city": "CARRIE", "loc": [-83.041549, 37.297995], "pop": 457, "state": "KY", "_id": "41725"} -{"city": "CHAVIES", "loc": [-83.331466, 37.355545], "pop": 564, "state": "KY", "_id": "41727"} -{"city": "CINDA", "loc": [-83.302886, 37.09667], "pop": 584, "state": "KY", "_id": "41728"} -{"city": "COMBS", "loc": [-83.202294, 37.291321], "pop": 1001, "state": "KY", "_id": "41729"} -{"city": "CONFLUENCE", "loc": [-83.371102, 37.271904], "pop": 184, "state": "KY", "_id": "41730"} -{"city": "ULVAH", "loc": [-83.035127, 37.120385], "pop": 1289, "state": "KY", "_id": "41731"} -{"city": "CUTSHIN", "loc": [-83.247304, 37.108109], "pop": 1591, "state": "KY", "_id": "41732"} -{"city": "DAISY", "loc": [-83.095095, 37.115936], "pop": 77, "state": "KY", "_id": "41733"} -{"city": "DELPHIA", "loc": [-83.095574, 37.023317], "pop": 349, "state": "KY", "_id": "41735"} -{"city": "DICE", "loc": [-83.205335, 37.382539], "pop": 1251, "state": "KY", "_id": "41736"} -{"city": "BEARVILLE", "loc": [-83.068449, 37.322096], "pop": 1961, "state": "KY", "_id": "41740"} -{"city": "FISTY", "loc": [-83.108251, 37.356348], "pop": 172, "state": "KY", "_id": "41743"} -{"city": "GAYS CREEK", "loc": [-83.455607, 37.350421], "pop": 369, "state": "KY", "_id": "41745"} -{"city": "HAPPY", "loc": [-83.081408, 37.195047], "pop": 784, "state": "KY", "_id": "41746"} -{"city": "DRYHILL", "loc": [-83.415668, 37.154604], "pop": 5507, "state": "KY", "_id": "41749"} -{"city": "NAPFOR", "loc": [-83.305913, 37.318325], "pop": 2357, "state": "KY", "_id": "41754"} -{"city": "LEATHERWOOD", "loc": [-83.151254, 37.040503], "pop": 241, "state": "KY", "_id": "41756"} -{"city": "ANCO", "loc": [-83.032125, 37.233926], "pop": 1442, "state": "KY", "_id": "41759"} -{"city": "SCUDDY", "loc": [-83.093242, 37.212182], "pop": 296, "state": "KY", "_id": "41760"} -{"city": "SLEMP", "loc": [-83.11694, 37.08037], "pop": 1012, "state": "KY", "_id": "41763"} -{"city": "SMILAX", "loc": [-83.284533, 37.166314], "pop": 634, "state": "KY", "_id": "41764"} -{"city": "TALCUM", "loc": [-83.047864, 37.383621], "pop": 375, "state": "KY", "_id": "41765"} -{"city": "VEST", "loc": [-83.06265, 37.408732], "pop": 312, "state": "KY", "_id": "41772"} -{"city": "VICCO", "loc": [-83.099792, 37.189826], "pop": 3072, "state": "KY", "_id": "41773"} -{"city": "FARLER", "loc": [-83.158299, 37.150655], "pop": 1364, "state": "KY", "_id": "41774"} -{"city": "WENDOVER", "loc": [-83.350185, 37.129115], "pop": 314, "state": "KY", "_id": "41775"} -{"city": "FREW", "loc": [-83.292655, 37.192435], "pop": 474, "state": "KY", "_id": "41776"} -{"city": "BIG ROCK", "loc": [-83.227006, 37.043019], "pop": 405, "state": "KY", "_id": "41777"} -{"city": "AMBURGEY", "loc": [-83.000483, 37.26716], "pop": 329, "state": "KY", "_id": "41801"} -{"city": "CARCASSONNE", "loc": [-82.940189, 37.134266], "pop": 552, "state": "KY", "_id": "41804"} -{"city": "BRINKLEY", "loc": [-82.913976, 37.262613], "pop": 1204, "state": "KY", "_id": "41805"} -{"city": "CROWN", "loc": [-82.847442, 37.159125], "pop": 295, "state": "KY", "_id": "41811"} -{"city": "DEANE", "loc": [-82.799722, 37.23332], "pop": 1400, "state": "KY", "_id": "41812"} -{"city": "ERMINE", "loc": [-82.822118, 37.174612], "pop": 3231, "state": "KY", "_id": "41815"} -{"city": "LARKSLANE", "loc": [-82.893847, 37.377663], "pop": 1287, "state": "KY", "_id": "41817"} -{"city": "GILLY", "loc": [-83.069317, 36.990109], "pop": 555, "state": "KY", "_id": "41819"} -{"city": "SKYLINE", "loc": [-83.020354, 37.069879], "pop": 426, "state": "KY", "_id": "41821"} -{"city": "HINDMAN", "loc": [-82.952653, 37.327607], "pop": 2965, "state": "KY", "_id": "41822"} -{"city": "HOLLYBUSH", "loc": [-82.831509, 37.337608], "pop": 380, "state": "KY", "_id": "41823"} -{"city": "ISOM", "loc": [-82.913348, 37.196027], "pop": 714, "state": "KY", "_id": "41824"} -{"city": "JACKHORN", "loc": [-82.691856, 37.171685], "pop": 1380, "state": "KY", "_id": "41825"} -{"city": "JEREMIAH", "loc": [-82.926836, 37.165127], "pop": 623, "state": "KY", "_id": "41826"} -{"city": "PUNCHEON", "loc": [-82.784725, 37.270944], "pop": 293, "state": "KY", "_id": "41828"} -{"city": "KONA", "loc": [-82.752469, 37.166814], "pop": 607, "state": "KY", "_id": "41829"} -{"city": "SOFT SHELL", "loc": [-82.956428, 37.383737], "pop": 765, "state": "KY", "_id": "41831"} -{"city": "LETCHER", "loc": [-82.980908, 37.155], "pop": 571, "state": "KY", "_id": "41832"} -{"city": "LINEFORK", "loc": [-82.919765, 37.051399], "pop": 860, "state": "KY", "_id": "41833"} -{"city": "LITTCARR", "loc": [-82.965098, 37.271457], "pop": 468, "state": "KY", "_id": "41834"} -{"city": "MALLIE", "loc": [-82.871364, 37.334623], "pop": 792, "state": "KY", "_id": "41836"} -{"city": "MILLSTONE", "loc": [-82.7533, 37.151615], "pop": 247, "state": "KY", "_id": "41838"} -{"city": "MOUSIE", "loc": [-82.901768, 37.433125], "pop": 795, "state": "KY", "_id": "41839"} -{"city": "FLEMING NEON", "loc": [-82.697555, 37.212056], "pop": 2563, "state": "KY", "_id": "41840"} -{"city": "OMAHA", "loc": [-82.830417, 37.311528], "pop": 336, "state": "KY", "_id": "41843"} -{"city": "RAVEN", "loc": [-82.829164, 37.363252], "pop": 197, "state": "KY", "_id": "41844"} -{"city": "PREMIUM", "loc": [-82.958115, 37.068587], "pop": 311, "state": "KY", "_id": "41845"} -{"city": "REDFOX", "loc": [-82.871865, 37.25311], "pop": 974, "state": "KY", "_id": "41847"} -{"city": "ROXANA", "loc": [-83.034488, 37.090502], "pop": 117, "state": "KY", "_id": "41848"} -{"city": "SECO", "loc": [-82.735832, 37.176523], "pop": 791, "state": "KY", "_id": "41849"} -{"city": "THORNTON", "loc": [-82.7382, 37.129622], "pop": 635, "state": "KY", "_id": "41855"} -{"city": "DAY RURAL", "loc": [-82.838768, 37.115463], "pop": 4678, "state": "KY", "_id": "41858"} -{"city": "DEMA", "loc": [-82.852313, 37.421491], "pop": 468, "state": "KY", "_id": "41859"} -{"city": "RAVEN", "loc": [-82.826064, 37.404328], "pop": 128, "state": "KY", "_id": "41861"} -{"city": "DRY CREEK", "loc": [-82.781036, 37.346979], "pop": 1605, "state": "KY", "_id": "41862"} -{"city": "PADUCAH", "loc": [-88.663204, 37.063377], "pop": 27002, "state": "KY", "_id": "42001"} -{"city": "PADUCAH", "loc": [-88.593388, 37.036833], "pop": 29932, "state": "KY", "_id": "42003"} -{"city": "ALMO", "loc": [-88.308666, 36.712805], "pop": 1509, "state": "KY", "_id": "42020"} -{"city": "ARLINGTON", "loc": [-88.943835, 36.800117], "pop": 1780, "state": "KY", "_id": "42021"} -{"city": "BARDWELL", "loc": [-89.020855, 36.863387], "pop": 1957, "state": "KY", "_id": "42023"} -{"city": "BARLOW", "loc": [-89.040815, 37.049279], "pop": 1335, "state": "KY", "_id": "42024"} -{"city": "BENTON", "loc": [-88.354773, 36.880649], "pop": 15886, "state": "KY", "_id": "42025"} -{"city": "BOAZ", "loc": [-88.622288, 36.929974], "pop": 1609, "state": "KY", "_id": "42027"} -{"city": "BURNA", "loc": [-88.394185, 37.231514], "pop": 453, "state": "KY", "_id": "42028"} -{"city": "CALVERT CITY", "loc": [-88.381115, 37.014741], "pop": 4479, "state": "KY", "_id": "42029"} -{"city": "CLINTON", "loc": [-88.967574, 36.667531], "pop": 4143, "state": "KY", "_id": "42031"} -{"city": "COLUMBUS", "loc": [-89.098288, 36.755505], "pop": 454, "state": "KY", "_id": "42032"} -{"city": "CUNNINGHAM", "loc": [-88.872815, 36.896256], "pop": 1501, "state": "KY", "_id": "42035"} -{"city": "DEXTER", "loc": [-88.180695, 36.70313], "pop": 1800, "state": "KY", "_id": "42036"} -{"city": "EDDYVILLE", "loc": [-88.04939, 37.066385], "pop": 4402, "state": "KY", "_id": "42038"} -{"city": "FANCY FARM", "loc": [-88.791785, 36.77674], "pop": 1423, "state": "KY", "_id": "42039"} -{"city": "FARMINGTON", "loc": [-88.547832, 36.686903], "pop": 2821, "state": "KY", "_id": "42040"} -{"city": "CRUTCHFIELD", "loc": [-88.889224, 36.528678], "pop": 5955, "state": "KY", "_id": "42041"} -{"city": "GILBERTSVILLE", "loc": [-88.274856, 36.962465], "pop": 3285, "state": "KY", "_id": "42044"} -{"city": "IUKA", "loc": [-88.261382, 37.05781], "pop": 2210, "state": "KY", "_id": "42045"} -{"city": "HAMPTON", "loc": [-88.364897, 37.334764], "pop": 922, "state": "KY", "_id": "42047"} -{"city": "HARDIN", "loc": [-88.262153, 36.776233], "pop": 3246, "state": "KY", "_id": "42048"} -{"city": "HAZEL", "loc": [-88.331862, 36.542215], "pop": 3235, "state": "KY", "_id": "42049"} -{"city": "HICKMAN", "loc": [-89.194667, 36.559269], "pop": 3676, "state": "KY", "_id": "42050"} -{"city": "HICKORY", "loc": [-88.678782, 36.847783], "pop": 3140, "state": "KY", "_id": "42051"} -{"city": "KEVIL", "loc": [-88.876366, 37.087231], "pop": 5019, "state": "KY", "_id": "42053"} -{"city": "KIRKSEY", "loc": [-88.423815, 36.673092], "pop": 3190, "state": "KY", "_id": "42054"} -{"city": "KUTTAWA", "loc": [-88.149813, 37.061871], "pop": 2222, "state": "KY", "_id": "42055"} -{"city": "LA CENTER", "loc": [-88.972954, 37.083019], "pop": 1563, "state": "KY", "_id": "42056"} -{"city": "LEDBETTER", "loc": [-88.486503, 37.049167], "pop": 2278, "state": "KY", "_id": "42058"} -{"city": "MARION", "loc": [-88.100471, 37.325426], "pop": 8733, "state": "KY", "_id": "42064"} -{"city": "MAYFIELD", "loc": [-88.650637, 36.732686], "pop": 16321, "state": "KY", "_id": "42066"} -{"city": "MELBER", "loc": [-88.752037, 36.919658], "pop": 1187, "state": "KY", "_id": "42069"} -{"city": "MURRAY", "loc": [-88.303249, 36.609915], "pop": 20388, "state": "KY", "_id": "42071"} -{"city": "NEW CONCORD", "loc": [-88.09548, 36.550003], "pop": 922, "state": "KY", "_id": "42076"} -{"city": "SALEM", "loc": [-88.271139, 37.255346], "pop": 1801, "state": "KY", "_id": "42078"} -{"city": "SEDALIA", "loc": [-88.594813, 36.588208], "pop": 1983, "state": "KY", "_id": "42079"} -{"city": "CARRSVILLE", "loc": [-88.383593, 37.12269], "pop": 1200, "state": "KY", "_id": "42081"} -{"city": "SYMSONIA", "loc": [-88.528576, 36.871544], "pop": 2725, "state": "KY", "_id": "42082"} -{"city": "TILINE", "loc": [-88.254679, 37.162773], "pop": 198, "state": "KY", "_id": "42083"} -{"city": "WATER VALLEY", "loc": [-88.808371, 36.569358], "pop": 409, "state": "KY", "_id": "42085"} -{"city": "WEST PADUCAH", "loc": [-88.761075, 37.092239], "pop": 2223, "state": "KY", "_id": "42086"} -{"city": "WICKLIFFE", "loc": [-89.017693, 36.967969], "pop": 2605, "state": "KY", "_id": "42087"} -{"city": "WINGO", "loc": [-88.739449, 36.625282], "pop": 2643, "state": "KY", "_id": "42088"} -{"city": "PLUM SPRINGS", "loc": [-86.455891, 37.007874], "pop": 40671, "state": "KY", "_id": "42101"} -{"city": "BOWLING GREEN", "loc": [-86.393321, 36.96629], "pop": 11391, "state": "KY", "_id": "42103"} -{"city": "BOWLING GREEN", "loc": [-86.448077, 36.937537], "pop": 14948, "state": "KY", "_id": "42104"} -{"city": "ADOLPHUS", "loc": [-86.263596, 36.677486], "pop": 1976, "state": "KY", "_id": "42120"} -{"city": "ALVATON", "loc": [-86.363213, 36.86296], "pop": 2934, "state": "KY", "_id": "42122"} -{"city": "AUSTIN", "loc": [-85.98503, 36.812376], "pop": 425, "state": "KY", "_id": "42123"} -{"city": "BEAUMONT", "loc": [-85.648829, 36.88758], "pop": 206, "state": "KY", "_id": "42124"} -{"city": "CAVE CITY", "loc": [-85.944283, 37.11696], "pop": 6283, "state": "KY", "_id": "42127"} -{"city": "SUBTLE", "loc": [-85.592741, 36.985197], "pop": 4020, "state": "KY", "_id": "42129"} -{"city": "EIGHTY EIGHT", "loc": [-85.828076, 36.939238], "pop": 2716, "state": "KY", "_id": "42130"} -{"city": "ETOILE", "loc": [-85.917279, 36.813428], "pop": 138, "state": "KY", "_id": "42131"} -{"city": "FOUNTAIN RUN", "loc": [-85.952023, 36.72002], "pop": 1568, "state": "KY", "_id": "42133"} -{"city": "FRANKLIN", "loc": [-86.570043, 36.725353], "pop": 14684, "state": "KY", "_id": "42134"} -{"city": "GAMALIEL", "loc": [-85.813353, 36.654037], "pop": 1377, "state": "KY", "_id": "42140"} -{"city": "GLASGOW", "loc": [-85.922053, 36.988189], "pop": 21079, "state": "KY", "_id": "42141"} -{"city": "HESTAND", "loc": [-85.569799, 36.653473], "pop": 386, "state": "KY", "_id": "42151"} -{"city": "HOLLAND", "loc": [-86.049756, 36.667316], "pop": 330, "state": "KY", "_id": "42153"} -{"city": "KNOB LICK", "loc": [-85.713698, 36.996922], "pop": 656, "state": "KY", "_id": "42154"} -{"city": "LAMB", "loc": [-85.885413, 36.799768], "pop": 189, "state": "KY", "_id": "42155"} -{"city": "LUCAS", "loc": [-86.035678, 36.837594], "pop": 415, "state": "KY", "_id": "42156"} -{"city": "MOUNT HERMAN", "loc": [-85.819162, 36.808982], "pop": 816, "state": "KY", "_id": "42157"} -{"city": "OAKLAND", "loc": [-86.285934, 37.007595], "pop": 2879, "state": "KY", "_id": "42159"} -{"city": "PARK CITY", "loc": [-86.077619, 37.057926], "pop": 1351, "state": "KY", "_id": "42160"} -{"city": "ROCKY HILL", "loc": [-86.11009, 37.067481], "pop": 139, "state": "KY", "_id": "42163"} -{"city": "SCOTTSVILLE", "loc": [-86.192863, 36.761437], "pop": 11611, "state": "KY", "_id": "42164"} -{"city": "SUMMER SHADE", "loc": [-85.708322, 36.888345], "pop": 2281, "state": "KY", "_id": "42166"} -{"city": "T VILLE", "loc": [-85.696842, 36.713085], "pop": 8016, "state": "KY", "_id": "42167"} -{"city": "WILLOW SHADE", "loc": [-85.62203, 36.858183], "pop": 126, "state": "KY", "_id": "42169"} -{"city": "WOODBURN", "loc": [-86.562291, 36.855688], "pop": 1573, "state": "KY", "_id": "42170"} -{"city": "SMITHS GROVE", "loc": [-86.193757, 37.058104], "pop": 4650, "state": "KY", "_id": "42171"} -{"city": "ADAIRVILLE", "loc": [-86.858523, 36.691385], "pop": 2481, "state": "KY", "_id": "42202"} -{"city": "ALLENSVILLE", "loc": [-87.024442, 36.694744], "pop": 842, "state": "KY", "_id": "42204"} -{"city": "AUBURN", "loc": [-86.719813, 36.881755], "pop": 4192, "state": "KY", "_id": "42206"} -{"city": "BEE SPRING", "loc": [-86.279377, 37.29754], "pop": 1480, "state": "KY", "_id": "42207"} -{"city": "REEDYVILLE", "loc": [-86.274003, 37.18817], "pop": 2296, "state": "KY", "_id": "42210"} -{"city": "GOLDEN POND", "loc": [-87.841243, 36.846421], "pop": 9683, "state": "KY", "_id": "42211"} -{"city": "CENTER", "loc": [-85.670455, 37.100897], "pop": 2051, "state": "KY", "_id": "42214"} -{"city": "CERULEAN", "loc": [-87.664848, 36.949619], "pop": 1654, "state": "KY", "_id": "42215"} -{"city": "CROFTON", "loc": [-87.489072, 37.034387], "pop": 3531, "state": "KY", "_id": "42217"} -{"city": "ELKTON", "loc": [-87.167833, 36.909403], "pop": 7207, "state": "KY", "_id": "42220"} -{"city": "GRACEY", "loc": [-87.6545, 36.856393], "pop": 92, "state": "KY", "_id": "42232"} -{"city": "TINY TOWN", "loc": [-87.170931, 36.664268], "pop": 2307, "state": "KY", "_id": "42234"} -{"city": "HERNDON", "loc": [-87.608215, 36.708469], "pop": 810, "state": "KY", "_id": "42236"} -{"city": "HOPKINSVILLE", "loc": [-87.485148, 36.862053], "pop": 39331, "state": "KY", "_id": "42240"} -{"city": "HUFF", "loc": [-86.382112, 37.234241], "pop": 92, "state": "KY", "_id": "42250"} -{"city": "JETSON", "loc": [-86.520872, 37.240367], "pop": 1375, "state": "KY", "_id": "42252"} -{"city": "LA FAYETTE", "loc": [-87.65634, 36.658165], "pop": 123, "state": "KY", "_id": "42254"} -{"city": "LEWISBURG", "loc": [-86.988748, 37.003747], "pop": 3241, "state": "KY", "_id": "42256"} -{"city": "LINDSEYVILLE", "loc": [-86.296384, 37.22813], "pop": 601, "state": "KY", "_id": "42257"} -{"city": "MAMMOTH CAVE NAT", "loc": [-86.178112, 37.274385], "pop": 1354, "state": "KY", "_id": "42259"} -{"city": "LOGANSPORT", "loc": [-86.703726, 37.19102], "pop": 5488, "state": "KY", "_id": "42261"} -{"city": "OAK GROVE", "loc": [-87.425514, 36.665225], "pop": 3556, "state": "KY", "_id": "42262"} -{"city": "OLMSTEAD", "loc": [-86.981846, 36.78463], "pop": 1529, "state": "KY", "_id": "42265"} -{"city": "PEMBROKE", "loc": [-87.331892, 36.798221], "pop": 1221, "state": "KY", "_id": "42266"} -{"city": "QUALITY", "loc": [-86.869053, 37.035402], "pop": 1436, "state": "KY", "_id": "42268"} -{"city": "ROCHESTER", "loc": [-86.85921, 37.204778], "pop": 669, "state": "KY", "_id": "42273"} -{"city": "BROWNING", "loc": [-86.585389, 36.939859], "pop": 1592, "state": "KY", "_id": "42274"} -{"city": "ROUNDHILL", "loc": [-86.406974, 37.25601], "pop": 405, "state": "KY", "_id": "42275"} -{"city": "DAYSVILLE", "loc": [-86.888708, 36.853074], "pop": 12585, "state": "KY", "_id": "42276"} -{"city": "SHARON GROVE", "loc": [-87.10029, 36.927754], "pop": 651, "state": "KY", "_id": "42280"} -{"city": "SUNFISH", "loc": [-86.390826, 37.3022], "pop": 483, "state": "KY", "_id": "42284"} -{"city": "KYROCK", "loc": [-86.29569, 37.252343], "pop": 416, "state": "KY", "_id": "42285"} -{"city": "TRENTON", "loc": [-87.261098, 36.731384], "pop": 1209, "state": "KY", "_id": "42286"} -{"city": "WELCHS CREEK", "loc": [-86.637139, 37.30856], "pop": 2972, "state": "KY", "_id": "42287"} -{"city": "OWENSBORO", "loc": [-87.155394, 37.751255], "pop": 40043, "state": "KY", "_id": "42301"} -{"city": "OWENSBORO", "loc": [-87.080252, 37.755884], "pop": 32552, "state": "KY", "_id": "42303"} -{"city": "BEAVER DAM", "loc": [-86.872932, 37.386958], "pop": 6508, "state": "KY", "_id": "42320"} -{"city": "BEECH CREEK", "loc": [-87.123854, 37.183704], "pop": 303, "state": "KY", "_id": "42321"} -{"city": "BEECHMONT", "loc": [-87.03934, 37.177552], "pop": 2431, "state": "KY", "_id": "42323"} -{"city": "BELTON", "loc": [-86.977369, 37.151056], "pop": 1137, "state": "KY", "_id": "42324"} -{"city": "BREMEN", "loc": [-87.232989, 37.342955], "pop": 3631, "state": "KY", "_id": "42325"} -{"city": "BROWDER", "loc": [-86.978197, 37.259333], "pop": 0, "state": "KY", "_id": "42326"} -{"city": "CALHOUN", "loc": [-87.277307, 37.574997], "pop": 3734, "state": "KY", "_id": "42327"} -{"city": "CENTERTOWN", "loc": [-87.00902, 37.407918], "pop": 1434, "state": "KY", "_id": "42328"} -{"city": "CENTRAL CITY", "loc": [-87.120236, 37.300699], "pop": 8187, "state": "KY", "_id": "42330"} -{"city": "CROMWELL", "loc": [-86.769964, 37.341765], "pop": 356, "state": "KY", "_id": "42333"} -{"city": "DRAKESBORO", "loc": [-87.047966, 37.213588], "pop": 1552, "state": "KY", "_id": "42337"} -{"city": "DUNDEE", "loc": [-86.760446, 37.551052], "pop": 255, "state": "KY", "_id": "42338"} -{"city": "DUNMOR", "loc": [-87.00789, 37.083967], "pop": 534, "state": "KY", "_id": "42339"} -{"city": "42340", "loc": [-86.965861, 37.351295], "pop": 139, "state": "KY", "_id": "42340"} -{"city": "FORDSVILLE", "loc": [-86.726251, 37.629738], "pop": 2108, "state": "KY", "_id": "42343"} -{"city": "GRAHAM", "loc": [-87.257849, 37.225836], "pop": 2837, "state": "KY", "_id": "42344"} -{"city": "GREENVILLE", "loc": [-87.180613, 37.207642], "pop": 8616, "state": "KY", "_id": "42345"} -{"city": "HARTFORD", "loc": [-86.917982, 37.478533], "pop": 4628, "state": "KY", "_id": "42347"} -{"city": "HAWESVILLE", "loc": [-86.763804, 37.850318], "pop": 4194, "state": "KY", "_id": "42348"} -{"city": "HORSE BRANCH", "loc": [-86.698734, 37.423417], "pop": 1880, "state": "KY", "_id": "42349"} -{"city": "ISLAND", "loc": [-87.16921, 37.447109], "pop": 1453, "state": "KY", "_id": "42350"} -{"city": "LEWISPORT", "loc": [-86.895712, 37.909016], "pop": 4102, "state": "KY", "_id": "42351"} -{"city": "LIVERMORE", "loc": [-87.123861, 37.504453], "pop": 2336, "state": "KY", "_id": "42352"} -{"city": "MACEO", "loc": [-86.999915, 37.843601], "pop": 2223, "state": "KY", "_id": "42355"} -{"city": "NARROWS", "loc": [-86.68672, 37.569072], "pop": 124, "state": "KY", "_id": "42358"} -{"city": "OLATON", "loc": [-86.728223, 37.498666], "pop": 1838, "state": "KY", "_id": "42361"} -{"city": "PENROD", "loc": [-86.998181, 37.116725], "pop": 354, "state": "KY", "_id": "42365"} -{"city": "PHILPOT", "loc": [-86.937172, 37.718317], "pop": 6890, "state": "KY", "_id": "42366"} -{"city": "REYNOLDS STATION", "loc": [-86.77942, 37.696533], "pop": 1389, "state": "KY", "_id": "42368"} -{"city": "ROCKPORT", "loc": [-86.974624, 37.326871], "pop": 647, "state": "KY", "_id": "42369"} -{"city": "RUMSEY", "loc": [-87.280644, 37.50762], "pop": 836, "state": "KY", "_id": "42371"} -{"city": "SACRAMENTO", "loc": [-87.273584, 37.417687], "pop": 1265, "state": "KY", "_id": "42372"} -{"city": "UTICA", "loc": [-87.059082, 37.620559], "pop": 4800, "state": "KY", "_id": "42376"} -{"city": "WHITESVILLE", "loc": [-86.869912, 37.683402], "pop": 640, "state": "KY", "_id": "42378"} -{"city": "CLAY", "loc": [-87.836793, 37.475599], "pop": 2708, "state": "KY", "_id": "42404"} -{"city": "CORYDON", "loc": [-87.700033, 37.744284], "pop": 3688, "state": "KY", "_id": "42406"} -{"city": "DAWSON SPRINGS", "loc": [-87.68205, 37.196386], "pop": 6728, "state": "KY", "_id": "42408"} -{"city": "DIXON", "loc": [-87.701904, 37.510587], "pop": 1093, "state": "KY", "_id": "42409"} -{"city": "EARLINGTON", "loc": [-87.522679, 37.267899], "pop": 2445, "state": "KY", "_id": "42410"} -{"city": "FREDONIA", "loc": [-88.011229, 37.212956], "pop": 1557, "state": "KY", "_id": "42411"} -{"city": "HANSON", "loc": [-87.475135, 37.438246], "pop": 2329, "state": "KY", "_id": "42413"} -{"city": "HENDERSON", "loc": [-87.563228, 37.827393], "pop": 34941, "state": "KY", "_id": "42420"} -{"city": "MADISONVILLE", "loc": [-87.495326, 37.325551], "pop": 26866, "state": "KY", "_id": "42431"} -{"city": "MANITOU", "loc": [-87.56118, 37.407972], "pop": 1425, "state": "KY", "_id": "42436"} -{"city": "HENSHAW", "loc": [-87.87686, 37.663935], "pop": 10346, "state": "KY", "_id": "42437"} -{"city": "NEBO", "loc": [-87.686521, 37.368255], "pop": 1639, "state": "KY", "_id": "42441"} -{"city": "NORTONVILLE", "loc": [-87.460505, 37.183367], "pop": 2811, "state": "KY", "_id": "42442"} -{"city": "PRINCETON", "loc": [-87.863226, 37.115097], "pop": 11736, "state": "KY", "_id": "42445"} -{"city": "PROVIDENCE", "loc": [-87.750512, 37.404958], "pop": 4778, "state": "KY", "_id": "42450"} -{"city": "REED", "loc": [-87.370382, 37.858764], "pop": 1072, "state": "KY", "_id": "42451"} -{"city": "ROBARDS", "loc": [-87.526596, 37.675836], "pop": 1416, "state": "KY", "_id": "42452"} -{"city": "SAINT CHARLES", "loc": [-87.55386, 37.176635], "pop": 848, "state": "KY", "_id": "42453"} -{"city": "SEBREE", "loc": [-87.525489, 37.588921], "pop": 2911, "state": "KY", "_id": "42455"} -{"city": "SLAUGHTERS", "loc": [-87.505344, 37.5054], "pop": 914, "state": "KY", "_id": "42456"} -{"city": "SPOTTSVILLE", "loc": [-87.424682, 37.839948], "pop": 1534, "state": "KY", "_id": "42458"} -{"city": "STURGIS", "loc": [-87.996457, 37.548669], "pop": 5483, "state": "KY", "_id": "42459"} -{"city": "UNIONTOWN", "loc": [-87.926335, 37.767741], "pop": 1555, "state": "KY", "_id": "42461"} -{"city": "WAVERLY", "loc": [-87.806688, 37.742985], "pop": 1521, "state": "KY", "_id": "42462"} -{"city": "WHITE PLAINS", "loc": [-87.364415, 37.178765], "pop": 1877, "state": "KY", "_id": "42464"} -{"city": "ALCALDE", "loc": [-84.604362, 37.073853], "pop": 28546, "state": "KY", "_id": "42501"} -{"city": "BETHELRIDGE", "loc": [-84.778971, 37.22459], "pop": 550, "state": "KY", "_id": "42516"} -{"city": "BRONSTON", "loc": [-84.631413, 36.952488], "pop": 2417, "state": "KY", "_id": "42518"} -{"city": "SLOANS VALLEY", "loc": [-84.565237, 36.96145], "pop": 2843, "state": "KY", "_id": "42519"} -{"city": "DUNNVILLE", "loc": [-84.929507, 37.176894], "pop": 2308, "state": "KY", "_id": "42528"} -{"city": "JABEZ", "loc": [-84.863157, 37.066884], "pop": 364, "state": "KY", "_id": "42532"} -{"city": "LIBERTY", "loc": [-84.971864, 37.314553], "pop": 7831, "state": "KY", "_id": "42539"} -{"city": "MIDDLEBURG", "loc": [-84.832103, 37.359082], "pop": 697, "state": "KY", "_id": "42541"} -{"city": "POINTER", "loc": [-84.766245, 37.064216], "pop": 3861, "state": "KY", "_id": "42544"} -{"city": "SCIENCE HILL", "loc": [-84.64865, 37.168333], "pop": 2899, "state": "KY", "_id": "42553"} -{"city": "SHOPVILLE", "loc": [-84.44594, 37.174243], "pop": 3258, "state": "KY", "_id": "42554"} -{"city": "SLOANS VALLEY", "loc": [-84.376201, 36.968617], "pop": 0, "state": "KY", "_id": "42555"} -{"city": "TATEVILLE", "loc": [-84.438364, 36.973892], "pop": 0, "state": "KY", "_id": "42558"} -{"city": "YOSEMITE", "loc": [-84.812412, 37.307355], "pop": 380, "state": "KY", "_id": "42566"} -{"city": "PULASKI", "loc": [-84.605593, 37.247038], "pop": 5867, "state": "KY", "_id": "42567"} -{"city": "AARON", "loc": [-85.199114, 36.812827], "pop": 270, "state": "KY", "_id": "42601"} -{"city": "ALBANY", "loc": [-85.140677, 36.68569], "pop": 6419, "state": "KY", "_id": "42602"} -{"city": "ALPHA", "loc": [-85.092405, 36.779735], "pop": 2163, "state": "KY", "_id": "42603"} -{"city": "COOPERSVILLE", "loc": [-84.718717, 36.722168], "pop": 350, "state": "KY", "_id": "42611"} -{"city": "DELTA", "loc": [-84.684024, 36.801658], "pop": 639, "state": "KY", "_id": "42613"} -{"city": "JAMESTOWN", "loc": [-85.096844, 36.967955], "pop": 5140, "state": "KY", "_id": "42629"} -{"city": "PUEBLO", "loc": [-84.839251, 36.842723], "pop": 13523, "state": "KY", "_id": "42633"} -{"city": "PARKERS LAKE", "loc": [-84.443642, 36.834689], "pop": 1952, "state": "KY", "_id": "42634"} -{"city": "HOLLYHILL", "loc": [-84.412745, 36.663677], "pop": 4865, "state": "KY", "_id": "42635"} -{"city": "REVELO", "loc": [-84.472189, 36.673363], "pop": 221, "state": "KY", "_id": "42638"} -{"city": "ROCKYBRANCH", "loc": [-84.810179, 36.668512], "pop": 79, "state": "KY", "_id": "42640"} -{"city": "WEBBS CROSS ROAD", "loc": [-85.042873, 37.056683], "pop": 9569, "state": "KY", "_id": "42642"} -{"city": "SAWYER", "loc": [-84.361745, 36.925337], "pop": 288, "state": "KY", "_id": "42643"} -{"city": "STEARNS", "loc": [-84.516484, 36.708184], "pop": 4201, "state": "KY", "_id": "42647"} -{"city": "STRUNK", "loc": [-84.430801, 36.619068], "pop": 481, "state": "KY", "_id": "42649"} -{"city": "WIBORG", "loc": [-84.468364, 36.738597], "pop": 3595, "state": "KY", "_id": "42653"} -{"city": "WINDY", "loc": [-84.961122, 36.778411], "pop": 2481, "state": "KY", "_id": "42655"} -{"city": "E TOWN", "loc": [-85.858977, 37.706973], "pop": 31300, "state": "KY", "_id": "42701"} -{"city": "BAKERTON", "loc": [-85.331723, 36.871151], "pop": 222, "state": "KY", "_id": "42711"} -{"city": "BIG CLIFTY", "loc": [-86.139498, 37.527801], "pop": 1459, "state": "KY", "_id": "42712"} -{"city": "BONNIEVILLE", "loc": [-85.895996, 37.374141], "pop": 1627, "state": "KY", "_id": "42713"} -{"city": "BOW", "loc": [-85.301935, 36.708119], "pop": 697, "state": "KY", "_id": "42714"} -{"city": "BREEDING", "loc": [-85.407822, 36.981043], "pop": 692, "state": "KY", "_id": "42715"} -{"city": "BUFFALO", "loc": [-85.643411, 37.47864], "pop": 1614, "state": "KY", "_id": "42716"} -{"city": "BURKESVILLE", "loc": [-85.397028, 36.806791], "pop": 4891, "state": "KY", "_id": "42717"} -{"city": "CAMPBELLSVILLE", "loc": [-85.350755, 37.346618], "pop": 19358, "state": "KY", "_id": "42718"} -{"city": "CANEYVILLE", "loc": [-86.470207, 37.422168], "pop": 3148, "state": "KY", "_id": "42721"} -{"city": "CANMER", "loc": [-85.720292, 37.269562], "pop": 465, "state": "KY", "_id": "42722"} -{"city": "CASEY CREEK", "loc": [-85.207648, 37.273479], "pop": 1102, "state": "KY", "_id": "42723"} -{"city": "STEPHENSBURG", "loc": [-86.006046, 37.660339], "pop": 3153, "state": "KY", "_id": "42724"} -{"city": "WAX", "loc": [-86.160219, 37.427896], "pop": 3001, "state": "KY", "_id": "42726"} -{"city": "MONTPELIER", "loc": [-85.269265, 37.116408], "pop": 10012, "state": "KY", "_id": "42728"} -{"city": "CUB RUN", "loc": [-86.08132, 37.314855], "pop": 609, "state": "KY", "_id": "42729"} -{"city": "CUNDIFF", "loc": [-85.274434, 36.957125], "pop": 429, "state": "KY", "_id": "42730"} -{"city": "DUBRE", "loc": [-85.568281, 36.891998], "pop": 210, "state": "KY", "_id": "42731"} -{"city": "E VIEW", "loc": [-86.119153, 37.61478], "pop": 2728, "state": "KY", "_id": "42732"} -{"city": "ELK HORN", "loc": [-85.191764, 37.339287], "pop": 637, "state": "KY", "_id": "42733"} -{"city": "FAIRPLAY", "loc": [-85.326214, 37.035626], "pop": 557, "state": "KY", "_id": "42735"} -{"city": "FINLEY", "loc": [-85.352449, 37.454549], "pop": 1153, "state": "KY", "_id": "42736"} -{"city": "GLENDALE", "loc": [-85.892101, 37.603398], "pop": 991, "state": "KY", "_id": "42740"} -{"city": "GLENS FORK", "loc": [-85.240117, 37.018195], "pop": 788, "state": "KY", "_id": "42741"} -{"city": "GRADYVILLE", "loc": [-85.42753, 37.054438], "pop": 775, "state": "KY", "_id": "42742"} -{"city": "GREENSBURG", "loc": [-85.523639, 37.243009], "pop": 8471, "state": "KY", "_id": "42743"} -{"city": "HARDYVILLE", "loc": [-85.754174, 37.224905], "pop": 1234, "state": "KY", "_id": "42746"} -{"city": "HODGENVILLE", "loc": [-85.723206, 37.574566], "pop": 6666, "state": "KY", "_id": "42748"} -{"city": "HORSE CAVE", "loc": [-85.878549, 37.184939], "pop": 5282, "state": "KY", "_id": "42749"} -{"city": "KETTLE", "loc": [-85.40917, 36.694573], "pop": 991, "state": "KY", "_id": "42752"} -{"city": "KNIFLEY", "loc": [-85.112844, 37.231886], "pop": 648, "state": "KY", "_id": "42753"} -{"city": "SADLER", "loc": [-86.30371, 37.493098], "pop": 13023, "state": "KY", "_id": "42754"} -{"city": "MAGNOLIA", "loc": [-85.730823, 37.416464], "pop": 2960, "state": "KY", "_id": "42757"} -{"city": "MILLTOWN", "loc": [-85.444181, 37.103415], "pop": 625, "state": "KY", "_id": "42761"} -{"city": "MILLWOOD", "loc": [-86.379821, 37.460879], "pop": 561, "state": "KY", "_id": "42762"} -{"city": "MOUNT SHERMAN", "loc": [-85.631381, 37.453408], "pop": 589, "state": "KY", "_id": "42764"} -{"city": "MUNFORDVILLE", "loc": [-85.920141, 37.289812], "pop": 4085, "state": "KY", "_id": "42765"} -{"city": "PEYTONSBURG", "loc": [-85.371663, 36.646299], "pop": 272, "state": "KY", "_id": "42768"} -{"city": "SONORA", "loc": [-85.922969, 37.52207], "pop": 2396, "state": "KY", "_id": "42776"} -{"city": "SUMMERSVILLE", "loc": [-85.619376, 37.34186], "pop": 778, "state": "KY", "_id": "42782"} -{"city": "UPTON", "loc": [-85.908619, 37.456802], "pop": 2272, "state": "KY", "_id": "42784"} -{"city": "WHITE MILLS", "loc": [-86.039519, 37.543765], "pop": 540, "state": "KY", "_id": "42788"} -{"city": "METAIRIE", "loc": [-90.169513, 29.987138], "pop": 39554, "state": "LA", "_id": "70001"} -{"city": "METAIRIE", "loc": [-90.16303, 30.009843], "pop": 19511, "state": "LA", "_id": "70002"} -{"city": "METAIRIE", "loc": [-90.21457, 29.99746], "pop": 46193, "state": "LA", "_id": "70003"} -{"city": "METAIRIE", "loc": [-90.13314, 30.000476], "pop": 26512, "state": "LA", "_id": "70005"} -{"city": "METAIRIE", "loc": [-90.191483, 30.012885], "pop": 16919, "state": "LA", "_id": "70006"} -{"city": "DES ALLEMANDS", "loc": [-90.447049, 29.821993], "pop": 3322, "state": "LA", "_id": "70030"} -{"city": "AMA", "loc": [-90.292509, 29.943494], "pop": 1300, "state": "LA", "_id": "70031"} -{"city": "ARABI", "loc": [-89.996497, 29.961154], "pop": 8954, "state": "LA", "_id": "70032"} -{"city": "BARATARIA", "loc": [-90.126232, 29.717859], "pop": 334, "state": "LA", "_id": "70036"} -{"city": "BELLE CHASSE", "loc": [-90.004177, 29.834514], "pop": 9920, "state": "LA", "_id": "70037"} -{"city": "BOUTTE", "loc": [-90.393396, 29.897319], "pop": 2432, "state": "LA", "_id": "70039"} -{"city": "BRAITHWAITE", "loc": [-89.885347, 29.673563], "pop": 2303, "state": "LA", "_id": "70040"} -{"city": "BURAS", "loc": [-89.47568, 29.341056], "pop": 6496, "state": "LA", "_id": "70041"} -{"city": "CHALMETTE", "loc": [-89.961137, 29.946611], "pop": 31850, "state": "LA", "_id": "70043"} -{"city": "NEW SARPY", "loc": [-90.373982, 29.96579], "pop": 10472, "state": "LA", "_id": "70047"} -{"city": "EDGARD", "loc": [-90.581678, 30.031863], "pop": 3702, "state": "LA", "_id": "70049"} -{"city": "GARYVILLE", "loc": [-90.620113, 30.05352], "pop": 3191, "state": "LA", "_id": "70051"} -{"city": "GRAMERCY", "loc": [-90.690182, 30.052711], "pop": 2765, "state": "LA", "_id": "70052"} -{"city": "GRETNA", "loc": [-90.053115, 29.910806], "pop": 16979, "state": "LA", "_id": "70053"} -{"city": "TERRYTOWN", "loc": [-90.029123, 29.892652], "pop": 37901, "state": "LA", "_id": "70056"} -{"city": "HAHNVILLE", "loc": [-90.488581, 30.000094], "pop": 745, "state": "LA", "_id": "70057"} -{"city": "HARVEY", "loc": [-90.067259, 29.872535], "pop": 36824, "state": "LA", "_id": "70058"} -{"city": "KENNER", "loc": [-90.247901, 29.991203], "pop": 20016, "state": "LA", "_id": "70062"} -{"city": "KENNER", "loc": [-90.252175, 30.025164], "pop": 54023, "state": "LA", "_id": "70065"} -{"city": "LAFITTE", "loc": [-90.056633, 29.562194], "pop": 0, "state": "LA", "_id": "70067"} -{"city": "LA PLACE", "loc": [-90.489544, 30.077718], "pop": 26023, "state": "LA", "_id": "70068"} -{"city": "LULING", "loc": [-90.369261, 29.925116], "pop": 11956, "state": "LA", "_id": "70070"} -{"city": "LUTCHER", "loc": [-90.70084, 30.044679], "pop": 3993, "state": "LA", "_id": "70071"} -{"city": "MARRERO", "loc": [-90.110462, 29.859756], "pop": 58905, "state": "LA", "_id": "70072"} -{"city": "MERAUX", "loc": [-89.921433, 29.933494], "pop": 7196, "state": "LA", "_id": "70075"} -{"city": "NORCO", "loc": [-90.419796, 30.005706], "pop": 4931, "state": "LA", "_id": "70079"} -{"city": "PARADIS", "loc": [-90.435246, 29.877033], "pop": 939, "state": "LA", "_id": "70080"} -{"city": "PORT SULPHUR", "loc": [-89.68475, 29.470011], "pop": 6398, "state": "LA", "_id": "70083"} -{"city": "RESERVE", "loc": [-90.551773, 30.060255], "pop": 7080, "state": "LA", "_id": "70084"} -{"city": "SAINT BERNARD", "loc": [-89.836414, 29.861093], "pop": 8082, "state": "LA", "_id": "70085"} -{"city": "SAINT JAMES", "loc": [-90.860549, 30.027598], "pop": 2515, "state": "LA", "_id": "70086"} -{"city": "SAINT ROSE", "loc": [-90.312432, 29.958074], "pop": 6340, "state": "LA", "_id": "70087"} -{"city": "VACHERIE", "loc": [-90.709699, 29.969372], "pop": 6652, "state": "LA", "_id": "70090"} -{"city": "VENICE", "loc": [-89.347776, 29.261812], "pop": 458, "state": "LA", "_id": "70091"} -{"city": "VIOLET", "loc": [-89.895992, 29.904347], "pop": 10549, "state": "LA", "_id": "70092"} -{"city": "BRIDGE CITY", "loc": [-90.181305, 29.914386], "pop": 35200, "state": "LA", "_id": "70094"} -{"city": "NEW ORLEANS", "loc": [-90.075301, 29.960484], "pop": 6047, "state": "LA", "_id": "70112"} -{"city": "NEW ORLEANS", "loc": [-90.084777, 29.940511], "pop": 12177, "state": "LA", "_id": "70113"} -{"city": "NEW ORLEANS", "loc": [-90.033126, 29.937934], "pop": 29767, "state": "LA", "_id": "70114"} -{"city": "NEW ORLEANS", "loc": [-90.1005, 29.928863], "pop": 45070, "state": "LA", "_id": "70115"} -{"city": "NEW ORLEANS", "loc": [-90.064614, 29.968608], "pop": 16592, "state": "LA", "_id": "70116"} -{"city": "NEW ORLEANS", "loc": [-90.03124, 29.970298], "pop": 56494, "state": "LA", "_id": "70117"} -{"city": "NEW ORLEANS", "loc": [-90.123598, 29.950352], "pop": 40049, "state": "LA", "_id": "70118"} -{"city": "NEW ORLEANS", "loc": [-90.085156, 29.974552], "pop": 47894, "state": "LA", "_id": "70119"} -{"city": "JEFFERSON", "loc": [-90.160953, 29.963071], "pop": 12924, "state": "LA", "_id": "70121"} -{"city": "NEW ORLEANS", "loc": [-90.064409, 30.005637], "pop": 47077, "state": "LA", "_id": "70122"} -{"city": "HARAHAN", "loc": [-90.210748, 29.953473], "pop": 25057, "state": "LA", "_id": "70123"} -{"city": "NEW ORLEANS", "loc": [-90.109384, 30.007081], "pop": 22851, "state": "LA", "_id": "70124"} -{"city": "NEW ORLEANS", "loc": [-90.102785, 29.951225], "pop": 22734, "state": "LA", "_id": "70125"} -{"city": "NEW ORLEANS", "loc": [-90.018913, 30.015341], "pop": 45119, "state": "LA", "_id": "70126"} -{"city": "NEW ORLEANS", "loc": [-89.980688, 30.033811], "pop": 29643, "state": "LA", "_id": "70127"} -{"city": "NEW ORLEANS", "loc": [-89.956421, 30.052691], "pop": 18844, "state": "LA", "_id": "70128"} -{"city": "NEW ORLEANS", "loc": [-89.906206, 30.047984], "pop": 14064, "state": "LA", "_id": "70129"} -{"city": "NEW ORLEANS", "loc": [-90.073949, 29.932438], "pop": 15576, "state": "LA", "_id": "70130"} -{"city": "NEW ORLEANS", "loc": [-89.996033, 29.916811], "pop": 26939, "state": "LA", "_id": "70131"} -{"city": "THIBODAUX", "loc": [-90.809605, 29.799213], "pop": 37831, "state": "LA", "_id": "70301"} -{"city": "PIERRE PART", "loc": [-91.200167, 29.95501], "pop": 5207, "state": "LA", "_id": "70339"} -{"city": "AMELIA", "loc": [-91.111105, 29.66046], "pop": 656, "state": "LA", "_id": "70340"} -{"city": "BELLE ROSE", "loc": [-91.044351, 30.025955], "pop": 4842, "state": "LA", "_id": "70341"} -{"city": "BERWICK", "loc": [-91.205662, 29.698786], "pop": 17631, "state": "LA", "_id": "70342"} -{"city": "BOURG", "loc": [-90.60866, 29.548489], "pop": 5310, "state": "LA", "_id": "70343"} -{"city": "CHAUVIN", "loc": [-90.597952, 29.463401], "pop": 6430, "state": "LA", "_id": "70344"} -{"city": "CUT OFF", "loc": [-90.339298, 29.523188], "pop": 13268, "state": "LA", "_id": "70345"} -{"city": "DONALDSONVILLE", "loc": [-90.997025, 30.101799], "pop": 11265, "state": "LA", "_id": "70346"} -{"city": "DULAC", "loc": [-90.655655, 29.359814], "pop": 1683, "state": "LA", "_id": "70353"} -{"city": "GALLIANO", "loc": [-90.298054, 29.431125], "pop": 3669, "state": "LA", "_id": "70354"} -{"city": "GHEENS", "loc": [-90.484855, 29.707591], "pop": 452, "state": "LA", "_id": "70355"} -{"city": "GIBSON", "loc": [-90.977637, 29.662522], "pop": 1642, "state": "LA", "_id": "70356"} -{"city": "GOLDEN MEADOW", "loc": [-90.263932, 29.382054], "pop": 4075, "state": "LA", "_id": "70357"} -{"city": "GRAND ISLE", "loc": [-90.008801, 29.22973], "pop": 1455, "state": "LA", "_id": "70358"} -{"city": "GRAY", "loc": [-90.78071, 29.69034], "pop": 4755, "state": "LA", "_id": "70359"} -{"city": "HOUMA", "loc": [-90.754808, 29.59433], "pop": 18335, "state": "LA", "_id": "70360"} -{"city": "HOUMA", "loc": [-90.691668, 29.560137], "pop": 23618, "state": "LA", "_id": "70363"} -{"city": "HOUMA", "loc": [-90.726801, 29.629887], "pop": 25330, "state": "LA", "_id": "70364"} -{"city": "LABADIEVILLE", "loc": [-90.961319, 29.834458], "pop": 2856, "state": "LA", "_id": "70372"} -{"city": "LOCKPORT", "loc": [-90.490614, 29.603493], "pop": 10863, "state": "LA", "_id": "70374"} -{"city": "MATHEWS", "loc": [-90.494914, 29.681982], "pop": 479, "state": "LA", "_id": "70375"} -{"city": "MONTEGUT", "loc": [-90.543952, 29.474366], "pop": 3892, "state": "LA", "_id": "70377"} -{"city": "MORGAN CITY", "loc": [-91.116497, 29.723937], "pop": 5341, "state": "LA", "_id": "70380"} -{"city": "NAPOLEONVILLE", "loc": [-91.026608, 29.92884], "pop": 7710, "state": "LA", "_id": "70390"} -{"city": "PATTERSON", "loc": [-91.281231, 29.69671], "pop": 13463, "state": "LA", "_id": "70392"} -{"city": "RACELAND", "loc": [-90.599908, 29.717835], "pop": 12920, "state": "LA", "_id": "70394"} -{"city": "SCHRIEVER", "loc": [-90.851341, 29.712276], "pop": 4774, "state": "LA", "_id": "70395"} -{"city": "THERIOT", "loc": [-90.765146, 29.451587], "pop": 3982, "state": "LA", "_id": "70397"} -{"city": "HAMMOND", "loc": [-90.487856, 30.51908], "pop": 16101, "state": "LA", "_id": "70401"} -{"city": "HAMMOND", "loc": [-90.46972, 30.491054], "pop": 15299, "state": "LA", "_id": "70403"} -{"city": "ABITA SPRINGS", "loc": [-90.004072, 30.483696], "pop": 2659, "state": "LA", "_id": "70420"} -{"city": "AMITE", "loc": [-90.570493, 30.718208], "pop": 12006, "state": "LA", "_id": "70422"} -{"city": "ANGIE", "loc": [-89.856714, 30.922406], "pop": 6303, "state": "LA", "_id": "70426"} -{"city": "BOGALUSA", "loc": [-89.865329, 30.773303], "pop": 18938, "state": "LA", "_id": "70427"} -{"city": "BUSH", "loc": [-89.955664, 30.613393], "pop": 3906, "state": "LA", "_id": "70431"} -{"city": "COVINGTON", "loc": [-90.095933, 30.487606], "pop": 26117, "state": "LA", "_id": "70433"} -{"city": "FLUKER", "loc": [-90.52067, 30.812776], "pop": 186, "state": "LA", "_id": "70436"} -{"city": "FOLSOM", "loc": [-90.187927, 30.61447], "pop": 4832, "state": "LA", "_id": "70437"} -{"city": "FRANKLINTON", "loc": [-90.115479, 30.857735], "pop": 16352, "state": "LA", "_id": "70438"} -{"city": "GREENSBURG", "loc": [-90.725561, 30.864693], "pop": 3833, "state": "LA", "_id": "70441"} -{"city": "INDEPENDENCE", "loc": [-90.527685, 30.635148], "pop": 4330, "state": "LA", "_id": "70443"} -{"city": "KENTWOOD", "loc": [-90.472829, 30.889215], "pop": 11244, "state": "LA", "_id": "70444"} -{"city": "LACOMBE", "loc": [-89.929744, 30.322027], "pop": 7956, "state": "LA", "_id": "70445"} -{"city": "LORANGER", "loc": [-90.356723, 30.588407], "pop": 8701, "state": "LA", "_id": "70446"} -{"city": "MADISONVILLE", "loc": [-90.177282, 30.428743], "pop": 3110, "state": "LA", "_id": "70447"} -{"city": "MANDEVILLE", "loc": [-90.076846, 30.386096], "pop": 22492, "state": "LA", "_id": "70448"} -{"city": "MAUREPAS", "loc": [-90.704255, 30.271587], "pop": 2489, "state": "LA", "_id": "70449"} -{"city": "MOUNT HERMON", "loc": [-90.276886, 30.953619], "pop": 1577, "state": "LA", "_id": "70450"} -{"city": "PEARL RIVER", "loc": [-89.77315, 30.394448], "pop": 9228, "state": "LA", "_id": "70452"} -{"city": "PINE GROVE", "loc": [-90.767235, 30.703202], "pop": 444, "state": "LA", "_id": "70453"} -{"city": "PONCHATOULA", "loc": [-90.442247, 30.440644], "pop": 15713, "state": "LA", "_id": "70454"} -{"city": "ROBERT", "loc": [-90.335171, 30.506327], "pop": 564, "state": "LA", "_id": "70455"} -{"city": "ROSELAND", "loc": [-90.524277, 30.771711], "pop": 2062, "state": "LA", "_id": "70456"} -{"city": "SLIDELL", "loc": [-89.771192, 30.278411], "pop": 28918, "state": "LA", "_id": "70458"} -{"city": "SLIDELL", "loc": [-89.812895, 30.291611], "pop": 18020, "state": "LA", "_id": "70460"} -{"city": "SLIDELL", "loc": [-89.729027, 30.272615], "pop": 17270, "state": "LA", "_id": "70461"} -{"city": "SPRINGFIELD", "loc": [-90.577479, 30.415738], "pop": 5597, "state": "LA", "_id": "70462"} -{"city": "TICKFAW", "loc": [-90.481997, 30.566849], "pop": 5565, "state": "LA", "_id": "70466"} -{"city": "VARNADO", "loc": [-89.7606, 30.982085], "pop": 15, "state": "LA", "_id": "70467"} -{"city": "LAFAYETTE", "loc": [-92.008261, 30.236141], "pop": 31717, "state": "LA", "_id": "70501"} -{"city": "LAFAYETTE", "loc": [-92.049745, 30.184256], "pop": 25109, "state": "LA", "_id": "70503"} -{"city": "LAFAYETTE", "loc": [-92.065623, 30.207707], "pop": 33970, "state": "LA", "_id": "70506"} -{"city": "LAFAYETTE", "loc": [-92.015962, 30.281313], "pop": 12074, "state": "LA", "_id": "70507"} -{"city": "LAFAYETTE", "loc": [-92.023579, 30.158222], "pop": 20568, "state": "LA", "_id": "70508"} -{"city": "FORKED ISLAND", "loc": [-92.142655, 29.958828], "pop": 21018, "state": "LA", "_id": "70510"} -{"city": "ARNAUDVILLE", "loc": [-91.926285, 30.398054], "pop": 5676, "state": "LA", "_id": "70512"} -{"city": "BALDWIN", "loc": [-91.545783, 29.848784], "pop": 4601, "state": "LA", "_id": "70514"} -{"city": "BASILE", "loc": [-92.57357, 30.497787], "pop": 2733, "state": "LA", "_id": "70515"} -{"city": "BRANCH", "loc": [-92.334618, 30.328684], "pop": 2695, "state": "LA", "_id": "70516"} -{"city": "HENDERSON", "loc": [-91.905899, 30.274955], "pop": 9596, "state": "LA", "_id": "70517"} -{"city": "BROUSSARD", "loc": [-91.950171, 30.12189], "pop": 8159, "state": "LA", "_id": "70518"} -{"city": "CARENCRO", "loc": [-92.042265, 30.324433], "pop": 12256, "state": "LA", "_id": "70520"} -{"city": "CHURCH POINT", "loc": [-92.223954, 30.401287], "pop": 11108, "state": "LA", "_id": "70525"} -{"city": "CROWLEY", "loc": [-92.377709, 30.214753], "pop": 17975, "state": "LA", "_id": "70526"} -{"city": "DELCAMBRE", "loc": [-91.988938, 29.947414], "pop": 2769, "state": "LA", "_id": "70528"} -{"city": "DUSON", "loc": [-92.152455, 30.191216], "pop": 7160, "state": "LA", "_id": "70529"} -{"city": "EGAN", "loc": [-92.500226, 30.250966], "pop": 359, "state": "LA", "_id": "70531"} -{"city": "ELTON", "loc": [-92.699614, 30.471496], "pop": 1973, "state": "LA", "_id": "70532"} -{"city": "ERATH", "loc": [-92.034266, 29.952237], "pop": 7280, "state": "LA", "_id": "70533"} -{"city": "EUNICE", "loc": [-92.398474, 30.51158], "pop": 21409, "state": "LA", "_id": "70535"} -{"city": "EVANGELINE", "loc": [-92.553244, 30.26829], "pop": 475, "state": "LA", "_id": "70537"} -{"city": "FRANKLIN", "loc": [-91.502643, 29.785656], "pop": 15334, "state": "LA", "_id": "70538"} -{"city": "GUEYDAN", "loc": [-92.533779, 30.025541], "pop": 3839, "state": "LA", "_id": "70542"} -{"city": "IOTA", "loc": [-92.532201, 30.300081], "pop": 5338, "state": "LA", "_id": "70543"} -{"city": "JEANERETTE", "loc": [-91.654397, 29.90324], "pop": 14068, "state": "LA", "_id": "70544"} -{"city": "JENNINGS", "loc": [-92.657405, 30.22011], "pop": 11966, "state": "LA", "_id": "70546"} -{"city": "KAPLAN", "loc": [-92.302463, 29.977096], "pop": 9442, "state": "LA", "_id": "70548"} -{"city": "LAKE ARTHUR", "loc": [-92.682526, 30.09097], "pop": 5011, "state": "LA", "_id": "70549"} -{"city": "LOREAUVILLE", "loc": [-91.659571, 30.068276], "pop": 109, "state": "LA", "_id": "70552"} -{"city": "MAMOU", "loc": [-92.419646, 30.649648], "pop": 6639, "state": "LA", "_id": "70554"} -{"city": "MAURICE", "loc": [-92.107035, 30.07215], "pop": 3628, "state": "LA", "_id": "70555"} -{"city": "MIDLAND", "loc": [-92.463153, 30.143437], "pop": 4566, "state": "LA", "_id": "70559"} -{"city": "NEW IBERIA", "loc": [-91.819959, 30.001027], "pop": 56105, "state": "LA", "_id": "70560"} -{"city": "OPELOUSAS", "loc": [-92.089668, 30.51442], "pop": 46673, "state": "LA", "_id": "70570"} -{"city": "PORT BARRE", "loc": [-91.928576, 30.547788], "pop": 4940, "state": "LA", "_id": "70577"} -{"city": "RAYNE", "loc": [-92.248592, 30.204508], "pop": 15652, "state": "LA", "_id": "70578"} -{"city": "ROANOKE", "loc": [-92.686973, 30.318167], "pop": 2766, "state": "LA", "_id": "70581"} -{"city": "SAINT MARTINVILL", "loc": [-91.825961, 30.208282], "pop": 29590, "state": "LA", "_id": "70582"} -{"city": "SCOTT", "loc": [-92.098079, 30.250401], "pop": 9093, "state": "LA", "_id": "70583"} -{"city": "CANKTON", "loc": [-92.075681, 30.393741], "pop": 634, "state": "LA", "_id": "70584"} -{"city": "VILLE PLATTE", "loc": [-92.27371, 30.692376], "pop": 14291, "state": "LA", "_id": "70586"} -{"city": "WASHINGTON", "loc": [-92.039888, 30.709881], "pop": 3771, "state": "LA", "_id": "70589"} -{"city": "WELSH", "loc": [-92.818972, 30.236259], "pop": 5587, "state": "LA", "_id": "70591"} -{"city": "YOUNGSVILLE", "loc": [-92.009629, 30.097498], "pop": 6671, "state": "LA", "_id": "70592"} -{"city": "LAKE CHARLES", "loc": [-93.187966, 30.228453], "pop": 49710, "state": "LA", "_id": "70601"} -{"city": "LAKE CHARLES", "loc": [-93.221798, 30.169349], "pop": 42627, "state": "LA", "_id": "70605"} -{"city": "LAKE CHARLES", "loc": [-93.211082, 30.322031], "pop": 12470, "state": "LA", "_id": "70611"} -{"city": "BELL CITY", "loc": [-92.94407, 30.114454], "pop": 1365, "state": "LA", "_id": "70630"} -{"city": "CAMERON", "loc": [-93.277662, 29.86492], "pop": 5677, "state": "LA", "_id": "70631"} -{"city": "CREOLE", "loc": [-93.034874, 29.797813], "pop": 1023, "state": "LA", "_id": "70632"} -{"city": "DEQUINCY", "loc": [-93.415053, 30.421113], "pop": 8271, "state": "LA", "_id": "70633"} -{"city": "DERIDDER", "loc": [-93.268461, 30.828738], "pop": 19304, "state": "LA", "_id": "70634"} -{"city": "DRY CREEK", "loc": [-92.988849, 30.735356], "pop": 1597, "state": "LA", "_id": "70637"} -{"city": "EVANS", "loc": [-93.422171, 31.008817], "pop": 2757, "state": "LA", "_id": "70639"} -{"city": "GRAND CHENIER", "loc": [-92.897997, 29.787535], "pop": 696, "state": "LA", "_id": "70643"} -{"city": "HACKBERRY", "loc": [-93.374973, 29.982187], "pop": 1668, "state": "LA", "_id": "70645"} -{"city": "IOWA", "loc": [-93.02586, 30.221937], "pop": 4915, "state": "LA", "_id": "70647"} -{"city": "KINDER", "loc": [-92.869332, 30.460653], "pop": 6853, "state": "LA", "_id": "70648"} -{"city": "LACASSINE", "loc": [-92.829262, 30.145587], "pop": 460, "state": "LA", "_id": "70650"} -{"city": "LONGVILLE", "loc": [-93.254806, 30.579992], "pop": 1324, "state": "LA", "_id": "70652"} -{"city": "FIELDS", "loc": [-93.530734, 30.770145], "pop": 3273, "state": "LA", "_id": "70653"} -{"city": "MITTIE", "loc": [-92.932056, 30.678812], "pop": 477, "state": "LA", "_id": "70654"} -{"city": "OBERLIN", "loc": [-92.752672, 30.6162], "pop": 3121, "state": "LA", "_id": "70655"} -{"city": "PITKIN", "loc": [-92.954762, 30.932988], "pop": 3603, "state": "LA", "_id": "70656"} -{"city": "RAGLEY", "loc": [-93.233652, 30.470262], "pop": 2530, "state": "LA", "_id": "70657"} -{"city": "REEVES", "loc": [-93.036847, 30.496734], "pop": 1718, "state": "LA", "_id": "70658"} -{"city": "SINGER", "loc": [-93.464986, 30.532906], "pop": 2374, "state": "LA", "_id": "70660"} -{"city": "STARKS", "loc": [-93.661485, 30.308477], "pop": 2457, "state": "LA", "_id": "70661"} -{"city": "SUGARTOWN", "loc": [-93.017017, 30.827653], "pop": 319, "state": "LA", "_id": "70662"} -{"city": "SULPHUR", "loc": [-93.363911, 30.219001], "pop": 29774, "state": "LA", "_id": "70663"} -{"city": "VINTON", "loc": [-93.572808, 30.201523], "pop": 6584, "state": "LA", "_id": "70668"} -{"city": "WESTLAKE", "loc": [-93.268837, 30.261274], "pop": 11106, "state": "LA", "_id": "70669"} -{"city": "ADDIS", "loc": [-91.261348, 30.355699], "pop": 2354, "state": "LA", "_id": "70710"} -{"city": "ALBANY", "loc": [-90.596415, 30.514872], "pop": 4533, "state": "LA", "_id": "70711"} -{"city": "ANGOLA", "loc": [-91.597948, 30.96562], "pop": 5382, "state": "LA", "_id": "70712"} -{"city": "BAKER", "loc": [-91.142893, 30.581395], "pop": 19623, "state": "LA", "_id": "70714"} -{"city": "BATCHELOR", "loc": [-91.668675, 30.802631], "pop": 1864, "state": "LA", "_id": "70715"} -{"city": "BLANKS", "loc": [-91.615845, 30.579813], "pop": 1657, "state": "LA", "_id": "70717"} -{"city": "BRUSLY", "loc": [-91.252649, 30.387692], "pop": 3429, "state": "LA", "_id": "70719"} -{"city": "BUECHE", "loc": [-91.338303, 30.574894], "pop": 504, "state": "LA", "_id": "70720"} -{"city": "POINT CLAIR", "loc": [-91.102484, 30.220759], "pop": 1104, "state": "LA", "_id": "70721"} -{"city": "CLINTON", "loc": [-90.933141, 30.824867], "pop": 3028, "state": "LA", "_id": "70722"} -{"city": "CONVENT", "loc": [-90.864988, 30.055251], "pop": 2052, "state": "LA", "_id": "70723"} -{"city": "DARROW", "loc": [-90.965102, 30.12999], "pop": 1090, "state": "LA", "_id": "70725"} -{"city": "PORT VINCENT", "loc": [-90.932588, 30.484623], "pop": 34574, "state": "LA", "_id": "70726"} -{"city": "ERWINVILLE", "loc": [-91.399444, 30.551252], "pop": 342, "state": "LA", "_id": "70729"} -{"city": "ETHEL", "loc": [-91.109975, 30.813124], "pop": 3979, "state": "LA", "_id": "70730"} -{"city": "FORDOCHE", "loc": [-91.697258, 30.550835], "pop": 0, "state": "LA", "_id": "70732"} -{"city": "FRENCH SETTLEMEN", "loc": [-90.773225, 30.336394], "pop": 3132, "state": "LA", "_id": "70733"} -{"city": "GEISMAR", "loc": [-90.975824, 30.236265], "pop": 3622, "state": "LA", "_id": "70734"} -{"city": "GLYNN", "loc": [-91.342311, 30.637617], "pop": 557, "state": "LA", "_id": "70736"} -{"city": "GONZALES", "loc": [-90.918012, 30.247306], "pop": 21482, "state": "LA", "_id": "70737"} -{"city": "GREENWELL SPRING", "loc": [-91.007484, 30.52114], "pop": 8000, "state": "LA", "_id": "70739"} -{"city": "GROSSE TETE", "loc": [-91.43829, 30.387935], "pop": 956, "state": "LA", "_id": "70740"} -{"city": "HOLDEN", "loc": [-90.665176, 30.555646], "pop": 2424, "state": "LA", "_id": "70744"} -{"city": "THE BLUFFS", "loc": [-91.234537, 30.827034], "pop": 7635, "state": "LA", "_id": "70748"} -{"city": "JARREAU", "loc": [-91.433205, 30.632585], "pop": 1365, "state": "LA", "_id": "70749"} -{"city": "KROTZ SPRINGS", "loc": [-91.756329, 30.537928], "pop": 935, "state": "LA", "_id": "70750"} -{"city": "LAKELAND", "loc": [-91.421677, 30.579941], "pop": 839, "state": "LA", "_id": "70752"} -{"city": "LETTSWORTH", "loc": [-91.740252, 30.932394], "pop": 1589, "state": "LA", "_id": "70753"} -{"city": "LIVINGSTON", "loc": [-90.767339, 30.474107], "pop": 4653, "state": "LA", "_id": "70754"} -{"city": "LIVONIA", "loc": [-91.533219, 30.552353], "pop": 2711, "state": "LA", "_id": "70755"} -{"city": "LOTTIE", "loc": [-91.71053, 30.503947], "pop": 9, "state": "LA", "_id": "70756"} -{"city": "RAMAH", "loc": [-91.516816, 30.482292], "pop": 2070, "state": "LA", "_id": "70757"} -{"city": "MORGANZA", "loc": [-91.595935, 30.724491], "pop": 1082, "state": "LA", "_id": "70759"} -{"city": "NEW ROADS", "loc": [-91.442124, 30.701356], "pop": 7642, "state": "LA", "_id": "70760"} -{"city": "NORWOOD", "loc": [-91.062871, 30.951772], "pop": 1833, "state": "LA", "_id": "70761"} -{"city": "OSCAR", "loc": [-91.48458, 30.598759], "pop": 744, "state": "LA", "_id": "70762"} -{"city": "PAULINA", "loc": [-90.737439, 30.035159], "pop": 2639, "state": "LA", "_id": "70763"} -{"city": "PLAQUEMINE", "loc": [-91.252361, 30.268414], "pop": 16692, "state": "LA", "_id": "70764"} -{"city": "PORT ALLEN", "loc": [-91.254088, 30.471983], "pop": 12837, "state": "LA", "_id": "70767"} -{"city": "GALVEZ", "loc": [-90.929904, 30.315588], "pop": 13275, "state": "LA", "_id": "70769"} -{"city": "PRIDE", "loc": [-90.99429, 30.613321], "pop": 5260, "state": "LA", "_id": "70770"} -{"city": "ROSEDALE", "loc": [-91.45616, 30.440776], "pop": 807, "state": "LA", "_id": "70772"} -{"city": "ROUGON", "loc": [-91.381316, 30.603452], "pop": 542, "state": "LA", "_id": "70773"} -{"city": "SAINT AMANT", "loc": [-90.843527, 30.23849], "pop": 6177, "state": "LA", "_id": "70774"} -{"city": "BAINS", "loc": [-91.392266, 30.858658], "pop": 5634, "state": "LA", "_id": "70775"} -{"city": "IBERVILLE", "loc": [-91.09162, 30.279873], "pop": 3225, "state": "LA", "_id": "70776"} -{"city": "SLAUGHTER", "loc": [-91.052251, 30.799139], "pop": 4558, "state": "LA", "_id": "70777"} -{"city": "SORRENTO", "loc": [-90.86314, 30.185388], "pop": 1303, "state": "LA", "_id": "70778"} -{"city": "SUNSHINE", "loc": [-91.179922, 30.29824], "pop": 395, "state": "LA", "_id": "70780"} -{"city": "TORBERT", "loc": [-91.421616, 30.538575], "pop": 27, "state": "LA", "_id": "70781"} -{"city": "VENTRESS", "loc": [-91.403257, 30.681404], "pop": 1926, "state": "LA", "_id": "70783"} -{"city": "WALKER", "loc": [-90.855708, 30.524748], "pop": 12659, "state": "LA", "_id": "70785"} -{"city": "WHITE CASTLE", "loc": [-91.177343, 30.15447], "pop": 5739, "state": "LA", "_id": "70788"} -{"city": "WILSON", "loc": [-91.065511, 30.947325], "pop": 77, "state": "LA", "_id": "70789"} -{"city": "ZACHARY", "loc": [-91.135841, 30.656129], "pop": 18647, "state": "LA", "_id": "70791"} -{"city": "UNCLE SAM", "loc": [-90.771879, 30.021679], "pop": 263, "state": "LA", "_id": "70792"} -{"city": "BATON ROUGE", "loc": [-91.186954, 30.450731], "pop": 62, "state": "LA", "_id": "70801"} -{"city": "BATON ROUGE", "loc": [-91.169037, 30.444236], "pop": 35116, "state": "LA", "_id": "70802"} -{"city": "BATON ROUGE", "loc": [-91.148095, 30.48604], "pop": 30584, "state": "LA", "_id": "70805"} -{"city": "BATON ROUGE", "loc": [-91.130046, 30.448486], "pop": 25893, "state": "LA", "_id": "70806"} -{"city": "SCOTLANDVILLE", "loc": [-91.178615, 30.533199], "pop": 23234, "state": "LA", "_id": "70807"} -{"city": "BATON ROUGE", "loc": [-91.146765, 30.406596], "pop": 31189, "state": "LA", "_id": "70808"} -{"city": "BATON ROUGE", "loc": [-91.084213, 30.408891], "pop": 15623, "state": "LA", "_id": "70809"} -{"city": "BATON ROUGE", "loc": [-91.091898, 30.363309], "pop": 22331, "state": "LA", "_id": "70810"} -{"city": "GREENWOOD", "loc": [-91.126539, 30.53046], "pop": 13653, "state": "LA", "_id": "70811"} -{"city": "BATON ROUGE", "loc": [-91.118111, 30.505159], "pop": 11842, "state": "LA", "_id": "70812"} -{"city": "BATON ROUGE", "loc": [-91.068936, 30.484808], "pop": 13227, "state": "LA", "_id": "70814"} -{"city": "BATON ROUGE", "loc": [-91.059558, 30.455809], "pop": 27565, "state": "LA", "_id": "70815"} -{"city": "BATON ROUGE", "loc": [-91.035645, 30.427289], "pop": 32885, "state": "LA", "_id": "70816"} -{"city": "BATON ROUGE", "loc": [-91.00213, 30.390404], "pop": 20916, "state": "LA", "_id": "70817"} -{"city": "BATON ROUGE", "loc": [-91.049964, 30.540832], "pop": 8368, "state": "LA", "_id": "70818"} -{"city": "BATON ROUGE", "loc": [-91.01565, 30.46679], "pop": 5377, "state": "LA", "_id": "70819"} -{"city": "BATON ROUGE", "loc": [-91.167064, 30.379523], "pop": 10710, "state": "LA", "_id": "70820"} -{"city": "ARCADIA", "loc": [-92.924529, 32.555643], "pop": 4367, "state": "LA", "_id": "71001"} -{"city": "ATHENS", "loc": [-93.023875, 32.645073], "pop": 1336, "state": "LA", "_id": "71003"} -{"city": "BELCHER", "loc": [-93.850799, 32.754393], "pop": 849, "state": "LA", "_id": "71004"} -{"city": "BENTON", "loc": [-93.69095, 32.697617], "pop": 7234, "state": "LA", "_id": "71006"} -{"city": "BETHANY", "loc": [-94.003394, 32.366179], "pop": 404, "state": "LA", "_id": "71007"} -{"city": "BIENVILLE", "loc": [-92.908402, 32.252323], "pop": 445, "state": "LA", "_id": "71008"} -{"city": "CASTOR", "loc": [-93.093576, 32.245181], "pop": 2784, "state": "LA", "_id": "71016"} -{"city": "COTTON VALLEY", "loc": [-93.425885, 32.819011], "pop": 2061, "state": "LA", "_id": "71018"} -{"city": "HANNA", "loc": [-93.315644, 32.050099], "pop": 9339, "state": "LA", "_id": "71019"} -{"city": "DOYLINE", "loc": [-93.399585, 32.490023], "pop": 3065, "state": "LA", "_id": "71023"} -{"city": "DUBBERLY", "loc": [-93.21419, 32.519164], "pop": 1117, "state": "LA", "_id": "71024"} -{"city": "FRIERSON", "loc": [-93.691488, 32.244968], "pop": 1600, "state": "LA", "_id": "71027"} -{"city": "GIBSLAND", "loc": [-93.070558, 32.529874], "pop": 2219, "state": "LA", "_id": "71028"} -{"city": "GILLIAM", "loc": [-93.829268, 32.825055], "pop": 367, "state": "LA", "_id": "71029"} -{"city": "GLOSTER", "loc": [-93.829309, 32.191705], "pop": 1242, "state": "LA", "_id": "71030"} -{"city": "GOLDONNA", "loc": [-92.961056, 31.999988], "pop": 1378, "state": "LA", "_id": "71031"} -{"city": "GRAND CANE", "loc": [-93.794064, 32.105], "pop": 1048, "state": "LA", "_id": "71032"} -{"city": "GREENWOOD", "loc": [-93.969252, 32.424025], "pop": 3140, "state": "LA", "_id": "71033"} -{"city": "HALL SUMMIT", "loc": [-93.30475, 32.175249], "pop": 227, "state": "LA", "_id": "71034"} -{"city": "HAUGHTON", "loc": [-93.565742, 32.550732], "pop": 13876, "state": "LA", "_id": "71037"} -{"city": "HAYNESVILLE", "loc": [-93.069137, 32.927807], "pop": 7888, "state": "LA", "_id": "71038"} -{"city": "HEFLIN", "loc": [-93.285192, 32.447008], "pop": 1415, "state": "LA", "_id": "71039"} -{"city": "HOMER", "loc": [-93.028834, 32.774883], "pop": 6963, "state": "LA", "_id": "71040"} -{"city": "HOSSTON", "loc": [-93.883425, 32.896653], "pop": 766, "state": "LA", "_id": "71043"} -{"city": "IDA", "loc": [-93.902186, 32.993393], "pop": 742, "state": "LA", "_id": "71044"} -{"city": "JAMESTOWN", "loc": [-93.184758, 32.36127], "pop": 380, "state": "LA", "_id": "71045"} -{"city": "KEATCHIE", "loc": [-93.951046, 32.162173], "pop": 1068, "state": "LA", "_id": "71046"} -{"city": "KEITHVILLE", "loc": [-93.888138, 32.316059], "pop": 8291, "state": "LA", "_id": "71047"} -{"city": "LISBON", "loc": [-92.88781, 32.845196], "pop": 427, "state": "LA", "_id": "71048"} -{"city": "LOGANSPORT", "loc": [-93.962733, 31.994327], "pop": 4130, "state": "LA", "_id": "71049"} -{"city": "ELM GROVE", "loc": [-93.502615, 32.388628], "pop": 2216, "state": "LA", "_id": "71051"} -{"city": "MANSFIELD", "loc": [-93.698045, 32.023863], "pop": 12317, "state": "LA", "_id": "71052"} -{"city": "MINDEN", "loc": [-93.288587, 32.632281], "pop": 21954, "state": "LA", "_id": "71055"} -{"city": "MIRA", "loc": [-93.918797, 32.922491], "pop": 207, "state": "LA", "_id": "71059"} -{"city": "MOORINGSPORT", "loc": [-93.973018, 32.66258], "pop": 2838, "state": "LA", "_id": "71060"} -{"city": "OIL CITY", "loc": [-93.983844, 32.745107], "pop": 1874, "state": "LA", "_id": "71061"} -{"city": "PELICAN", "loc": [-93.563361, 31.896563], "pop": 998, "state": "LA", "_id": "71063"} -{"city": "PLAIN DEALING", "loc": [-93.690534, 32.907419], "pop": 4904, "state": "LA", "_id": "71064"} -{"city": "PLEASANT HILL", "loc": [-93.513594, 31.808577], "pop": 1338, "state": "LA", "_id": "71065"} -{"city": "PRINCETON", "loc": [-93.522577, 32.579089], "pop": 1914, "state": "LA", "_id": "71067"} -{"city": "RINGGOLD", "loc": [-93.298241, 32.326302], "pop": 4442, "state": "LA", "_id": "71068"} -{"city": "RODESSA", "loc": [-93.988474, 32.970079], "pop": 1014, "state": "LA", "_id": "71069"} -{"city": "CHESTNUT", "loc": [-92.948606, 32.156604], "pop": 1207, "state": "LA", "_id": "71070"} -{"city": "SAREPTA", "loc": [-93.440407, 32.943361], "pop": 4570, "state": "LA", "_id": "71071"} -{"city": "SHONGALOO", "loc": [-93.296264, 32.971289], "pop": 752, "state": "LA", "_id": "71072"} -{"city": "SIBLEY", "loc": [-93.300902, 32.509539], "pop": 1363, "state": "LA", "_id": "71073"} -{"city": "SPRINGHILL", "loc": [-93.459563, 33.00054], "pop": 6271, "state": "LA", "_id": "71075"} -{"city": "STONEWALL", "loc": [-93.800277, 32.284758], "pop": 3009, "state": "LA", "_id": "71078"} -{"city": "SUMMERFIELD", "loc": [-92.821516, 32.923802], "pop": 25, "state": "LA", "_id": "71079"} -{"city": "TREES", "loc": [-93.987312, 32.866844], "pop": 5880, "state": "LA", "_id": "71082"} -{"city": "SHREVEPORT", "loc": [-93.748696, 32.503743], "pop": 11355, "state": "LA", "_id": "71101"} -{"city": "SHREVEPORT", "loc": [-93.772701, 32.494459], "pop": 12908, "state": "LA", "_id": "71103"} -{"city": "SHREVEPORT", "loc": [-93.734862, 32.482978], "pop": 14181, "state": "LA", "_id": "71104"} -{"city": "SHREVEPORT", "loc": [-93.714341, 32.458882], "pop": 19053, "state": "LA", "_id": "71105"} -{"city": "FORBING", "loc": [-93.747922, 32.426251], "pop": 32844, "state": "LA", "_id": "71106"} -{"city": "DIXIE", "loc": [-93.828781, 32.564652], "pop": 29013, "state": "LA", "_id": "71107"} -{"city": "SHREVEPORT", "loc": [-93.781378, 32.448596], "pop": 19800, "state": "LA", "_id": "71108"} -{"city": "SHREVEPORT", "loc": [-93.801297, 32.473994], "pop": 27393, "state": "LA", "_id": "71109"} -{"city": "BARKSDALE A F B", "loc": [-93.638172, 32.514313], "pop": 3518, "state": "LA", "_id": "71110"} -{"city": "BOSSIER CITY", "loc": [-93.703826, 32.544924], "pop": 26472, "state": "LA", "_id": "71111"} -{"city": "BOSSIER CITY", "loc": [-93.676723, 32.486025], "pop": 25299, "state": "LA", "_id": "71112"} -{"city": "CASPIANA", "loc": [-93.697402, 32.410156], "pop": 8897, "state": "LA", "_id": "71115"} -{"city": "SHREVEPORT", "loc": [-93.802543, 32.397664], "pop": 23539, "state": "LA", "_id": "71118"} -{"city": "SHREVEPORT", "loc": [-93.87261, 32.477121], "pop": 10578, "state": "LA", "_id": "71119"} -{"city": "SHREVEPORT", "loc": [-93.874192, 32.41412], "pop": 12661, "state": "LA", "_id": "71129"} -{"city": "MONROE", "loc": [-92.106104, 32.528551], "pop": 22419, "state": "LA", "_id": "71201"} -{"city": "RICHWOOD", "loc": [-92.090231, 32.463327], "pop": 32038, "state": "LA", "_id": "71202"} -{"city": "MONROE", "loc": [-92.042241, 32.553038], "pop": 35643, "state": "LA", "_id": "71203"} -{"city": "BASKIN", "loc": [-91.713154, 32.289728], "pop": 1518, "state": "LA", "_id": "71219"} -{"city": "BASTROP", "loc": [-91.90776, 32.789382], "pop": 26388, "state": "LA", "_id": "71220"} -{"city": "BERNICE", "loc": [-92.626269, 32.821024], "pop": 3510, "state": "LA", "_id": "71222"} -{"city": "BONITA", "loc": [-91.682158, 32.912263], "pop": 963, "state": "LA", "_id": "71223"} -{"city": "CALHOUN", "loc": [-92.329929, 32.524791], "pop": 4082, "state": "LA", "_id": "71225"} -{"city": "CHATHAM", "loc": [-92.437433, 32.292246], "pop": 1254, "state": "LA", "_id": "71226"} -{"city": "CHOUDRANT", "loc": [-92.522419, 32.555627], "pop": 5436, "state": "LA", "_id": "71227"} -{"city": "COLLINSTON", "loc": [-91.863419, 32.697143], "pop": 1128, "state": "LA", "_id": "71229"} -{"city": "WARDEN", "loc": [-91.512487, 32.450433], "pop": 6657, "state": "LA", "_id": "71232"} -{"city": "DOWNSVILLE", "loc": [-92.374471, 32.652508], "pop": 4019, "state": "LA", "_id": "71234"} -{"city": "DUBACH", "loc": [-92.678543, 32.694893], "pop": 2838, "state": "LA", "_id": "71235"} -{"city": "EPPS", "loc": [-91.49135, 32.616099], "pop": 1586, "state": "LA", "_id": "71237"} -{"city": "EROS", "loc": [-92.34795, 32.398822], "pop": 2296, "state": "LA", "_id": "71238"} -{"city": "EXTENSION", "loc": [-91.801091, 31.947065], "pop": 263, "state": "LA", "_id": "71239"} -{"city": "FARMERVILLE", "loc": [-92.317955, 32.753378], "pop": 7172, "state": "LA", "_id": "71241"} -{"city": "FORT NECESSITY", "loc": [-91.825694, 32.043412], "pop": 74, "state": "LA", "_id": "71243"} -{"city": "GRAMBLING", "loc": [-92.715785, 32.524398], "pop": 5740, "state": "LA", "_id": "71245"} -{"city": "JONES", "loc": [-91.596509, 32.966286], "pop": 386, "state": "LA", "_id": "71250"} -{"city": "JONESBORO", "loc": [-92.694425, 32.248292], "pop": 11078, "state": "LA", "_id": "71251"} -{"city": "LAKE PROVIDENCE", "loc": [-91.19057, 32.807067], "pop": 7774, "state": "LA", "_id": "71254"} -{"city": "LILLIE", "loc": [-92.685769, 32.952931], "pop": 439, "state": "LA", "_id": "71256"} -{"city": "MANGHAM", "loc": [-91.797607, 32.333114], "pop": 3493, "state": "LA", "_id": "71259"} -{"city": "LINVILLE", "loc": [-92.25706, 32.888155], "pop": 3761, "state": "LA", "_id": "71260"} -{"city": "MER ROUGE", "loc": [-91.771643, 32.77176], "pop": 2177, "state": "LA", "_id": "71261"} -{"city": "TERRY", "loc": [-91.412905, 32.872258], "pop": 9056, "state": "LA", "_id": "71263"} -{"city": "OAK RIDGE", "loc": [-91.761785, 32.624317], "pop": 913, "state": "LA", "_id": "71264"} -{"city": "PIONEER", "loc": [-91.464822, 32.715436], "pop": 1451, "state": "LA", "_id": "71266"} -{"city": "QUITMAN", "loc": [-92.708532, 32.356423], "pop": 2603, "state": "LA", "_id": "71268"} -{"city": "ALTO", "loc": [-91.7643, 32.468938], "pop": 11441, "state": "LA", "_id": "71269"} -{"city": "RUSTON", "loc": [-92.643927, 32.530823], "pop": 26114, "state": "LA", "_id": "71270"} -{"city": "SIMSBORO", "loc": [-92.799484, 32.538388], "pop": 2476, "state": "LA", "_id": "71275"} -{"city": "SONDHEIMER", "loc": [-91.248197, 32.577216], "pop": 887, "state": "LA", "_id": "71276"} -{"city": "SPEARSVILLE", "loc": [-92.58698, 32.955022], "pop": 2830, "state": "LA", "_id": "71277"} -{"city": "SPENCER", "loc": [-92.12134, 32.593268], "pop": 14, "state": "LA", "_id": "71280"} -{"city": "MOUND", "loc": [-91.190066, 32.402127], "pop": 12354, "state": "LA", "_id": "71282"} -{"city": "TRANSYLVANIA", "loc": [-91.228813, 32.670529], "pop": 1067, "state": "LA", "_id": "71286"} -{"city": "WEST MONROE", "loc": [-92.175971, 32.531726], "pop": 27809, "state": "LA", "_id": "71291"} -{"city": "WEST MONROE", "loc": [-92.185445, 32.456599], "pop": 17851, "state": "LA", "_id": "71292"} -{"city": "WINNSBORO", "loc": [-91.710845, 32.159229], "pop": 14751, "state": "LA", "_id": "71295"} -{"city": "ALEXANDRIA", "loc": [-92.463349, 31.288519], "pop": 25040, "state": "LA", "_id": "71301"} -{"city": "ALEXANDRIA", "loc": [-92.424169, 31.268272], "pop": 16918, "state": "LA", "_id": "71302"} -{"city": "ALEXANDRIA", "loc": [-92.508892, 31.304838], "pop": 21759, "state": "LA", "_id": "71303"} -{"city": "ACME", "loc": [-91.821563, 31.301618], "pop": 173, "state": "LA", "_id": "71316"} -{"city": "BIG BEND", "loc": [-91.845065, 31.079347], "pop": 786, "state": "LA", "_id": "71318"} -{"city": "EOLA", "loc": [-92.182582, 30.949284], "pop": 6271, "state": "LA", "_id": "71322"} -{"city": "CENTER POINT", "loc": [-92.187868, 31.263068], "pop": 1720, "state": "LA", "_id": "71323"} -{"city": "CHENEYVILLE", "loc": [-92.295144, 31.020097], "pop": 1545, "state": "LA", "_id": "71325"} -{"city": "CLAYTON", "loc": [-91.542547, 31.78857], "pop": 1070, "state": "LA", "_id": "71326"} -{"city": "COTTONPORT", "loc": [-92.058124, 30.986168], "pop": 3190, "state": "LA", "_id": "71327"} -{"city": "BUCKEYE", "loc": [-92.202287, 31.354506], "pop": 6080, "state": "LA", "_id": "71328"} -{"city": "VICK", "loc": [-92.194182, 31.189613], "pop": 447, "state": "LA", "_id": "71331"} -{"city": "GOUDEAU", "loc": [-92.090039, 30.951089], "pop": 919, "state": "LA", "_id": "71333"} -{"city": "FROGMORE", "loc": [-91.571958, 31.647944], "pop": 9280, "state": "LA", "_id": "71334"} -{"city": "GILBERT", "loc": [-91.592026, 32.034943], "pop": 793, "state": "LA", "_id": "71336"} -{"city": "HAMBURG", "loc": [-91.916177, 31.073269], "pop": 481, "state": "LA", "_id": "71339"} -{"city": "HARRISONBURG", "loc": [-91.883971, 31.766926], "pop": 2348, "state": "LA", "_id": "71340"} -{"city": "HESSMER", "loc": [-92.139933, 31.0534], "pop": 3057, "state": "LA", "_id": "71341"} -{"city": "JENA", "loc": [-92.113677, 31.674817], "pop": 5796, "state": "LA", "_id": "71342"} -{"city": "LARTO", "loc": [-91.845812, 31.636486], "pop": 5507, "state": "LA", "_id": "71343"} -{"city": "71344", "loc": [-91.862893, 31.433458], "pop": 989, "state": "LA", "_id": "71344"} -{"city": "LECOMPTE", "loc": [-92.389031, 31.106032], "pop": 3056, "state": "LA", "_id": "71346"} -{"city": "MANSURA", "loc": [-92.054333, 31.061466], "pop": 3239, "state": "LA", "_id": "71350"} -{"city": "MARKSVILLE", "loc": [-92.083145, 31.139614], "pop": 11217, "state": "LA", "_id": "71351"} -{"city": "MELVILLE", "loc": [-91.75649, 30.662643], "pop": 2713, "state": "LA", "_id": "71353"} -{"city": "MONTEREY", "loc": [-91.734455, 31.440287], "pop": 2074, "state": "LA", "_id": "71354"} -{"city": "MOREAUVILLE", "loc": [-91.981814, 31.036766], "pop": 1914, "state": "LA", "_id": "71355"} -{"city": "LE MOYEN", "loc": [-92.040781, 30.824887], "pop": 404, "state": "LA", "_id": "71356"} -{"city": "NEWELLTON", "loc": [-91.257771, 32.065621], "pop": 3235, "state": "LA", "_id": "71357"} -{"city": "PALMETTO", "loc": [-91.911384, 30.706543], "pop": 621, "state": "LA", "_id": "71358"} -{"city": "KOLIN", "loc": [-92.399276, 31.34991], "pop": 37069, "state": "LA", "_id": "71360"} -{"city": "PLAUCHEVILLE", "loc": [-91.984673, 30.936484], "pop": 2517, "state": "LA", "_id": "71362"} -{"city": "SAINT JOSEPH", "loc": [-91.278432, 31.924805], "pop": 2073, "state": "LA", "_id": "71366"} -{"city": "SAINT LANDRY", "loc": [-92.393846, 30.83812], "pop": 5679, "state": "LA", "_id": "71367"} -{"city": "SICILY ISLAND", "loc": [-91.680711, 31.850734], "pop": 1568, "state": "LA", "_id": "71368"} -{"city": "SIMMESPORT", "loc": [-91.825868, 30.977119], "pop": 3137, "state": "LA", "_id": "71369"} -{"city": "TROUT", "loc": [-92.19931, 31.653142], "pop": 3624, "state": "LA", "_id": "71371"} -{"city": "71372", "loc": [-92.04555, 31.231159], "pop": 372, "state": "LA", "_id": "71372"} -{"city": "VIDALIA", "loc": [-91.469471, 31.578222], "pop": 9257, "state": "LA", "_id": "71373"} -{"city": "WATERPROOF", "loc": [-91.387154, 31.807613], "pop": 1245, "state": "LA", "_id": "71375"} -{"city": "WISNER", "loc": [-91.676798, 31.991252], "pop": 4116, "state": "LA", "_id": "71378"} -{"city": "AIMWELL", "loc": [-91.992491, 31.761898], "pop": 51, "state": "LA", "_id": "71401"} -{"city": "ANACOCO", "loc": [-93.358949, 31.221762], "pop": 4978, "state": "LA", "_id": "71403"} -{"city": "ATLANTA", "loc": [-92.764125, 31.873583], "pop": 2466, "state": "LA", "_id": "71404"} -{"city": "BELMONT", "loc": [-93.495863, 31.709956], "pop": 616, "state": "LA", "_id": "71406"} -{"city": "BENTLEY", "loc": [-92.49273, 31.466035], "pop": 2805, "state": "LA", "_id": "71407"} -{"city": "BOYCE", "loc": [-92.686681, 31.321601], "pop": 4672, "state": "LA", "_id": "71409"} -{"city": "CAMPTI", "loc": [-93.093572, 31.895851], "pop": 3990, "state": "LA", "_id": "71411"} -{"city": "CHOPIN", "loc": [-92.949717, 31.541916], "pop": 434, "state": "LA", "_id": "71412"} -{"city": "DERRY", "loc": [-92.857326, 31.535918], "pop": 802, "state": "LA", "_id": "71416"} -{"city": "COLFAX", "loc": [-92.656758, 31.507948], "pop": 5520, "state": "LA", "_id": "71417"} -{"city": "HEBERT", "loc": [-92.103653, 32.114794], "pop": 4785, "state": "LA", "_id": "71418"} -{"city": "MITCHELL", "loc": [-93.71569, 31.785153], "pop": 1592, "state": "LA", "_id": "71419"} -{"city": "71421", "loc": [-92.980409, 31.612239], "pop": 96, "state": "LA", "_id": "71421"} -{"city": "DODSON", "loc": [-92.678292, 32.070131], "pop": 2198, "state": "LA", "_id": "71422"} -{"city": "DRY PRONG", "loc": [-92.566479, 31.597921], "pop": 1993, "state": "LA", "_id": "71423"} -{"city": "ELMER", "loc": [-92.717062, 31.146476], "pop": 1364, "state": "LA", "_id": "71424"} -{"city": "ENTERPRISE", "loc": [-91.875149, 31.906412], "pop": 124, "state": "LA", "_id": "71425"} -{"city": "FISHER", "loc": [-93.460197, 31.493655], "pop": 370, "state": "LA", "_id": "71426"} -{"city": "FLATWOODS", "loc": [-92.881246, 31.384882], "pop": 312, "state": "LA", "_id": "71427"} -{"city": "FLORIEN", "loc": [-93.517916, 31.427455], "pop": 6006, "state": "LA", "_id": "71429"} -{"city": "FOREST HILL", "loc": [-92.510798, 31.024304], "pop": 1804, "state": "LA", "_id": "71430"} -{"city": "GEORGETOWN", "loc": [-92.395182, 31.745006], "pop": 1016, "state": "LA", "_id": "71432"} -{"city": "CALCASIEU", "loc": [-92.645758, 30.978384], "pop": 4204, "state": "LA", "_id": "71433"} -{"city": "GRAYSON", "loc": [-91.974615, 32.012108], "pop": 324, "state": "LA", "_id": "71435"} -{"city": "71436", "loc": [-91.992208, 32.206305], "pop": 654, "state": "LA", "_id": "71436"} -{"city": "LEANDER", "loc": [-92.77488, 31.082535], "pop": 854, "state": "LA", "_id": "71438"} -{"city": "HORNBECK", "loc": [-93.368306, 31.322639], "pop": 1352, "state": "LA", "_id": "71439"} -{"city": "KELLY", "loc": [-92.126072, 32.028619], "pop": 4042, "state": "LA", "_id": "71441"} -{"city": "LACAMP", "loc": [-92.896183, 31.160702], "pop": 276, "state": "LA", "_id": "71444"} -{"city": "71445", "loc": [-92.84924, 31.12665], "pop": 267, "state": "LA", "_id": "71445"} -{"city": "HICKS", "loc": [-93.223957, 31.144292], "pop": 21956, "state": "LA", "_id": "71446"} -{"city": "CHOPIN", "loc": [-92.77185, 31.418481], "pop": 757, "state": "LA", "_id": "71447"} -{"city": "MANY", "loc": [-93.464113, 31.58508], "pop": 6560, "state": "LA", "_id": "71449"} -{"city": "MARTHAVILLE", "loc": [-93.395428, 31.772583], "pop": 932, "state": "LA", "_id": "71450"} -{"city": "MELDER", "loc": [-92.662186, 31.11932], "pop": 377, "state": "LA", "_id": "71451"} -{"city": "MONTGOMERY", "loc": [-92.841178, 31.667388], "pop": 2263, "state": "LA", "_id": "71454"} -{"city": "CLIFTON", "loc": [-92.888915, 31.333927], "pop": 443, "state": "LA", "_id": "71455"} -{"city": "NATCHEZ", "loc": [-93.024117, 31.661502], "pop": 2070, "state": "LA", "_id": "71456"} -{"city": "NATCHITOCHES", "loc": [-93.091572, 31.761688], "pop": 23878, "state": "LA", "_id": "71457"} -{"city": "FORT POLK", "loc": [-93.22213, 31.032068], "pop": 27181, "state": "LA", "_id": "71459"} -{"city": "NEWLLANO", "loc": [-93.287899, 31.069396], "pop": 250, "state": "LA", "_id": "71461"} -{"city": "NOBLE", "loc": [-93.716679, 31.693849], "pop": 841, "state": "LA", "_id": "71462"} -{"city": "OAKDALE", "loc": [-92.663964, 30.817173], "pop": 9577, "state": "LA", "_id": "71463"} -{"city": "OLLA", "loc": [-92.22139, 31.87339], "pop": 3448, "state": "LA", "_id": "71465"} -{"city": "OTIS", "loc": [-92.744478, 31.226442], "pop": 231, "state": "LA", "_id": "71466"} -{"city": "POLLOCK", "loc": [-92.400536, 31.499966], "pop": 4328, "state": "LA", "_id": "71467"} -{"city": "PROVENCAL", "loc": [-93.140089, 31.580117], "pop": 2700, "state": "LA", "_id": "71468"} -{"city": "ROBELINE", "loc": [-93.320972, 31.771453], "pop": 939, "state": "LA", "_id": "71469"} -{"city": "SIEPER", "loc": [-92.76981, 31.197034], "pop": 474, "state": "LA", "_id": "71472"} -{"city": "SIKES", "loc": [-92.442894, 32.068616], "pop": 579, "state": "LA", "_id": "71473"} -{"city": "TIOGA", "loc": [-92.447116, 31.415732], "pop": 2158, "state": "LA", "_id": "71477"} -{"city": "TULLOS", "loc": [-92.301254, 31.853088], "pop": 1280, "state": "LA", "_id": "71479"} -{"city": "WINNFIELD", "loc": [-92.636646, 31.921389], "pop": 9295, "state": "LA", "_id": "71483"} -{"city": "WOODWORTH", "loc": [-92.498996, 31.132588], "pop": 1932, "state": "LA", "_id": "71485"} -{"city": "ZWOLLE", "loc": [-93.663569, 31.61379], "pop": 5325, "state": "LA", "_id": "71486"} -{"city": "BERWICK", "loc": [-70.855038, 43.28992], "pop": 5942, "state": "ME", "_id": "03901"} -{"city": "CAPE NEDDICK", "loc": [-70.639685, 43.213318], "pop": 953, "state": "ME", "_id": "03902"} -{"city": "ELIOT", "loc": [-70.782216, 43.130943], "pop": 6506, "state": "ME", "_id": "03903"} -{"city": "KITTERY", "loc": [-70.742876, 43.092128], "pop": 3537, "state": "ME", "_id": "03904"} -{"city": "KITTERY POINT", "loc": [-70.712108, 43.097571], "pop": 4589, "state": "ME", "_id": "03905"} -{"city": "NORTH BERWICK", "loc": [-70.721173, 43.325401], "pop": 6465, "state": "ME", "_id": "03906"} -{"city": "OGUNQUIT", "loc": [-70.597176, 43.228457], "pop": 852, "state": "ME", "_id": "03907"} -{"city": "SOUTH BERWICK", "loc": [-70.785949, 43.229201], "pop": 5982, "state": "ME", "_id": "03908"} -{"city": "YORK", "loc": [-70.657826, 43.154447], "pop": 8477, "state": "ME", "_id": "03909"} -{"city": "ACTON", "loc": [-70.930687, 43.549405], "pop": 767, "state": "ME", "_id": "04001"} -{"city": "ALFRED", "loc": [-70.696087, 43.487503], "pop": 4730, "state": "ME", "_id": "04002"} -{"city": "BAILEY ISLAND", "loc": [-69.995175, 43.734147], "pop": 464, "state": "ME", "_id": "04003"} -{"city": "ARUNDEL", "loc": [-70.471928, 43.483647], "pop": 23646, "state": "ME", "_id": "04005"} -{"city": "BIDDEFORD POOL", "loc": [-70.352024, 43.442722], "pop": 269, "state": "ME", "_id": "04006"} -{"city": "BOWDOINHAM", "loc": [-69.918849, 44.036806], "pop": 4328, "state": "ME", "_id": "04008"} -{"city": "BRIDGTON", "loc": [-70.724081, 44.052049], "pop": 3980, "state": "ME", "_id": "04009"} -{"city": "BROWNFIELD", "loc": [-70.903225, 43.971316], "pop": 1148, "state": "ME", "_id": "04010"} -{"city": "BIRCH ISLAND", "loc": [-69.955469, 43.897591], "pop": 22557, "state": "ME", "_id": "04011"} -{"city": "BUSTINS ISLAND", "loc": [-70.042247, 43.79602], "pop": 0, "state": "ME", "_id": "04013"} -{"city": "CASCO", "loc": [-70.526013, 43.959623], "pop": 3010, "state": "ME", "_id": "04015"} -{"city": "CENTER LOVELL", "loc": [-70.883618, 44.168139], "pop": 148, "state": "ME", "_id": "04016"} -{"city": "CHEBEAGUE ISLAND", "loc": [-70.116878, 43.735363], "pop": 337, "state": "ME", "_id": "04017"} -{"city": "CLIFF ISLAND", "loc": [-70.107097, 43.695547], "pop": 87, "state": "ME", "_id": "04019"} -{"city": "CORNISH", "loc": [-70.778433, 43.779612], "pop": 1736, "state": "ME", "_id": "04020"} -{"city": "CUMBERLAND CENTE", "loc": [-70.2484, 43.809818], "pop": 8544, "state": "ME", "_id": "04021"} -{"city": "DENMARK", "loc": [-70.792409, 43.975499], "pop": 679, "state": "ME", "_id": "04022"} -{"city": "EAST BALDWIN", "loc": [-70.692159, 43.864604], "pop": 976, "state": "ME", "_id": "04024"} -{"city": "WEST LEBANON", "loc": [-70.910986, 43.410304], "pop": 5224, "state": "ME", "_id": "04027"} -{"city": "NORTH SEBAGO", "loc": [-70.633865, 43.882247], "pop": 597, "state": "ME", "_id": "04029"} -{"city": "EAST WATERBORO", "loc": [-70.690626, 43.599537], "pop": 1153, "state": "ME", "_id": "04030"} -{"city": "FREEPORT", "loc": [-70.113854, 43.851093], "pop": 8081, "state": "ME", "_id": "04032"} -{"city": "FRYEBURG", "loc": [-70.966841, 44.031278], "pop": 2551, "state": "ME", "_id": "04037"} -{"city": "GORHAM", "loc": [-70.467968, 43.684329], "pop": 13642, "state": "ME", "_id": "04038"} -{"city": "GRAY", "loc": [-70.342939, 43.894202], "pop": 5383, "state": "ME", "_id": "04039"} -{"city": "HARRISON", "loc": [-70.653855, 44.107099], "pop": 2274, "state": "ME", "_id": "04040"} -{"city": "HIRAM", "loc": [-70.853076, 43.862217], "pop": 1905, "state": "ME", "_id": "04041"} -{"city": "HOLLIS CENTER", "loc": [-70.605074, 43.594578], "pop": 2243, "state": "ME", "_id": "04042"} -{"city": "KENNEBUNK", "loc": [-70.547802, 43.388055], "pop": 7826, "state": "ME", "_id": "04043"} -{"city": "KENNEBUNKPORT", "loc": [-70.472867, 43.392288], "pop": 5220, "state": "ME", "_id": "04046"} -{"city": "KEZAR FALLS", "loc": [-70.892717, 43.785361], "pop": 852, "state": "ME", "_id": "04047"} -{"city": "LIMERICK", "loc": [-70.786556, 43.696265], "pop": 2982, "state": "ME", "_id": "04048"} -{"city": "LIMINGTON", "loc": [-70.675178, 43.726031], "pop": 776, "state": "ME", "_id": "04049"} -{"city": "LONG ISLAND", "loc": [-70.15509, 43.692014], "pop": 201, "state": "ME", "_id": "04050"} -{"city": "LOVELL", "loc": [-70.929951, 44.161404], "pop": 763, "state": "ME", "_id": "04051"} -{"city": "MEREPOINT", "loc": [-70.00347, 43.843496], "pop": 269, "state": "ME", "_id": "04053"} -{"city": "NAPLES", "loc": [-70.598754, 43.968121], "pop": 2868, "state": "ME", "_id": "04055"} -{"city": "NORTH FRYEBURG", "loc": [-70.981286, 44.132936], "pop": 185, "state": "ME", "_id": "04058"} -{"city": "NORTH SHAPLEIGH", "loc": [-70.874392, 43.583458], "pop": 302, "state": "ME", "_id": "04060"} -{"city": "NORTH WATERBORO", "loc": [-70.729799, 43.639976], "pop": 1516, "state": "ME", "_id": "04061"} -{"city": "WINDHAM", "loc": [-70.414281, 43.795771], "pop": 13482, "state": "ME", "_id": "04062"} -{"city": "OLD ORCHARD BEAC", "loc": [-70.392053, 43.517449], "pop": 8451, "state": "ME", "_id": "04064"} -{"city": "ORRS ISLAND", "loc": [-69.966793, 43.77267], "pop": 861, "state": "ME", "_id": "04066"} -{"city": "PORTER", "loc": [-70.924266, 43.826216], "pop": 985, "state": "ME", "_id": "04068"} -{"city": "POWNAL", "loc": [-70.195497, 43.890042], "pop": 1690, "state": "ME", "_id": "04069"} -{"city": "RAYMOND", "loc": [-70.449834, 43.921917], "pop": 3516, "state": "ME", "_id": "04071"} -{"city": "SACO", "loc": [-70.454632, 43.520946], "pop": 16192, "state": "ME", "_id": "04072"} -{"city": "SANFORD", "loc": [-70.758479, 43.428541], "pop": 15723, "state": "ME", "_id": "04073"} -{"city": "SCARBOROUGH", "loc": [-70.345668, 43.583476], "pop": 12550, "state": "ME", "_id": "04074"} -{"city": "SEBAGO LAKE", "loc": [-70.573454, 43.758355], "pop": 4141, "state": "ME", "_id": "04075"} -{"city": "SHAPLEIGH", "loc": [-70.828619, 43.567353], "pop": 314, "state": "ME", "_id": "04076"} -{"city": "SOUTH CASCO", "loc": [-70.512938, 43.876736], "pop": 250, "state": "ME", "_id": "04077"} -{"city": "SOUTH HARPSWELL", "loc": [-69.993709, 43.781932], "pop": 1767, "state": "ME", "_id": "04079"} -{"city": "SOUTH WATERFORD", "loc": [-70.792061, 44.151256], "pop": 439, "state": "ME", "_id": "04081"} -{"city": "SPRINGVALE", "loc": [-70.806445, 43.471499], "pop": 5472, "state": "ME", "_id": "04083"} -{"city": "STANDISH", "loc": [-70.480657, 43.814211], "pop": 1592, "state": "ME", "_id": "04084"} -{"city": "STEEP FALLS", "loc": [-70.645627, 43.757137], "pop": 2060, "state": "ME", "_id": "04085"} -{"city": "PEJEPSCOT", "loc": [-69.964479, 43.941286], "pop": 8535, "state": "ME", "_id": "04086"} -{"city": "WATERBORO", "loc": [-70.743115, 43.566097], "pop": 1797, "state": "ME", "_id": "04087"} -{"city": "WELLS", "loc": [-70.596883, 43.314352], "pop": 5590, "state": "ME", "_id": "04090"} -{"city": "WEST BALDWIN", "loc": [-70.749015, 43.829873], "pop": 770, "state": "ME", "_id": "04091"} -{"city": "WESTBROOK", "loc": [-70.358033, 43.684268], "pop": 16121, "state": "ME", "_id": "04092"} -{"city": "WEST BUXTON", "loc": [-70.601546, 43.661586], "pop": 4910, "state": "ME", "_id": "04093"} -{"city": "MAPLEWOOD", "loc": [-70.913476, 43.643417], "pop": 1126, "state": "ME", "_id": "04095"} -{"city": "YARMOUTH", "loc": [-70.174958, 43.800933], "pop": 3068, "state": "ME", "_id": "04096"} -{"city": "PORTLAND", "loc": [-70.258864, 43.660564], "pop": 17147, "state": "ME", "_id": "04101"} -{"city": "PORTLAND", "loc": [-70.28981, 43.660168], "pop": 17660, "state": "ME", "_id": "04102"} -{"city": "PORTLAND", "loc": [-70.2876, 43.687568], "pop": 28461, "state": "ME", "_id": "04103"} -{"city": "FALMOUTH", "loc": [-70.26253, 43.734038], "pop": 7609, "state": "ME", "_id": "04105"} -{"city": "SOUTH PORTLAND", "loc": [-70.270878, 43.631847], "pop": 23131, "state": "ME", "_id": "04106"} -{"city": "CAPE ELIZABETH", "loc": [-70.230099, 43.601735], "pop": 8854, "state": "ME", "_id": "04107"} -{"city": "PEAKS ISLAND", "loc": [-70.194017, 43.658921], "pop": 775, "state": "ME", "_id": "04108"} -{"city": "CUSHING ISLAND", "loc": [-70.202201, 43.674971], "pop": 28, "state": "ME", "_id": "04109"} -{"city": "CUMBERLAND FORES", "loc": [-70.188333, 43.774485], "pop": 2879, "state": "ME", "_id": "04110"} -{"city": "AUBURN", "loc": [-70.238978, 44.094785], "pop": 24160, "state": "ME", "_id": "04210"} -{"city": "ANDOVER", "loc": [-70.79666, 44.663703], "pop": 878, "state": "ME", "_id": "04216"} -{"city": "BETHEL", "loc": [-70.803685, 44.416176], "pop": 2775, "state": "ME", "_id": "04217"} -{"city": "BRYANT POND", "loc": [-70.643468, 44.395714], "pop": 1563, "state": "ME", "_id": "04219"} -{"city": "BUCKFIELD", "loc": [-70.368293, 44.287676], "pop": 1551, "state": "ME", "_id": "04220"} -{"city": "CANTON", "loc": [-70.321719, 44.418894], "pop": 1673, "state": "ME", "_id": "04221"} -{"city": "DANVILLE", "loc": [-70.272055, 44.036528], "pop": 1086, "state": "ME", "_id": "04223"} -{"city": "DIXFIELD", "loc": [-70.424099, 44.554799], "pop": 3032, "state": "ME", "_id": "04224"} -{"city": "DRYDEN", "loc": [-70.223962, 44.600218], "pop": 4520, "state": "ME", "_id": "04225"} -{"city": "EAST ANDOVER", "loc": [-70.729555, 44.603011], "pop": 154, "state": "ME", "_id": "04226"} -{"city": "EAST LIVERMORE", "loc": [-70.130334, 44.399402], "pop": 560, "state": "ME", "_id": "04228"} -{"city": "EAST STONEHAM", "loc": [-70.852936, 44.238201], "pop": 429, "state": "ME", "_id": "04231"} -{"city": "FRYE", "loc": [-70.565319, 44.599482], "pop": 28, "state": "ME", "_id": "04235"} -{"city": "GREENE", "loc": [-70.145532, 44.189059], "pop": 3661, "state": "ME", "_id": "04236"} -{"city": "HANOVER", "loc": [-70.716735, 44.495875], "pop": 272, "state": "ME", "_id": "04237"} -{"city": "HEBRON", "loc": [-70.375369, 44.202136], "pop": 826, "state": "ME", "_id": "04238"} -{"city": "JAY", "loc": [-70.209883, 44.515994], "pop": 4631, "state": "ME", "_id": "04239"} -{"city": "LEWISTON", "loc": [-70.191619, 44.098538], "pop": 40173, "state": "ME", "_id": "04240"} -{"city": "LISBON", "loc": [-70.113933, 44.025511], "pop": 3633, "state": "ME", "_id": "04250"} -{"city": "LISBON FALLS", "loc": [-70.073375, 43.997759], "pop": 8095, "state": "ME", "_id": "04252"} -{"city": "LIVERMORE FALLS", "loc": [-70.193614, 44.445756], "pop": 4845, "state": "ME", "_id": "04254"} -{"city": "MECHANIC FALLS", "loc": [-70.368206, 44.099957], "pop": 6247, "state": "ME", "_id": "04256"} -{"city": "MEXICO", "loc": [-70.535797, 44.562776], "pop": 3316, "state": "ME", "_id": "04257"} -{"city": "MONMOUTH", "loc": [-70.02627, 44.22083], "pop": 1838, "state": "ME", "_id": "04259"} -{"city": "NEW GLOUCESTER", "loc": [-70.297381, 43.960835], "pop": 3916, "state": "ME", "_id": "04260"} -{"city": "NEWRY", "loc": [-70.792611, 44.407937], "pop": 300, "state": "ME", "_id": "04261"} -{"city": "LEEDS", "loc": [-70.125277, 44.28343], "pop": 1669, "state": "ME", "_id": "04263"} -{"city": "NORTH MONMOUTH", "loc": [-70.036686, 44.275328], "pop": 616, "state": "ME", "_id": "04265"} -{"city": "NORTH TURNER", "loc": [-70.256147, 44.335031], "pop": 953, "state": "ME", "_id": "04266"} -{"city": "NORTH WATERFORD", "loc": [-70.717393, 44.206511], "pop": 1168, "state": "ME", "_id": "04267"} -{"city": "NORWAY", "loc": [-70.560135, 44.212654], "pop": 5258, "state": "ME", "_id": "04268"} -{"city": "OXFORD", "loc": [-70.509799, 44.11183], "pop": 2822, "state": "ME", "_id": "04270"} -{"city": "POLAND", "loc": [-70.411819, 44.059187], "pop": 179, "state": "ME", "_id": "04273"} -{"city": "POLAND SPRING", "loc": [-70.379664, 44.021162], "pop": 1375, "state": "ME", "_id": "04274"} -{"city": "ROXBURY", "loc": [-70.609188, 44.65657], "pop": 548, "state": "ME", "_id": "04275"} -{"city": "RUMFORD", "loc": [-70.564475, 44.543446], "pop": 7035, "state": "ME", "_id": "04276"} -{"city": "RUMFORD CENTER", "loc": [-70.700058, 44.592334], "pop": 92, "state": "ME", "_id": "04278"} -{"city": "RUMFORD POINT", "loc": [-70.700276, 44.557104], "pop": 36, "state": "ME", "_id": "04279"} -{"city": "SABATTUS", "loc": [-70.074792, 44.113269], "pop": 4809, "state": "ME", "_id": "04280"} -{"city": "SOUTH PARIS", "loc": [-70.501179, 44.216674], "pop": 6054, "state": "ME", "_id": "04281"} -{"city": "TURNER", "loc": [-70.249444, 44.255669], "pop": 3365, "state": "ME", "_id": "04282"} -{"city": "WAYNE", "loc": [-70.0712, 44.349283], "pop": 546, "state": "ME", "_id": "04284"} -{"city": "WELD", "loc": [-70.4249, 44.701624], "pop": 430, "state": "ME", "_id": "04285"} -{"city": "WEST PARIS", "loc": [-70.573167, 44.32527], "pop": 2149, "state": "ME", "_id": "04289"} -{"city": "PERU", "loc": [-70.443459, 44.494408], "pop": 1541, "state": "ME", "_id": "04290"} -{"city": "WEST POLAND", "loc": [-70.450166, 44.047167], "pop": 733, "state": "ME", "_id": "04291"} -{"city": "WEST SUMNER", "loc": [-70.432314, 44.372804], "pop": 761, "state": "ME", "_id": "04292"} -{"city": "WILTON", "loc": [-70.296064, 44.59107], "pop": 227, "state": "ME", "_id": "04294"} -{"city": "AUGUSTA", "loc": [-69.766548, 44.323228], "pop": 25195, "state": "ME", "_id": "04330"} -{"city": "COOPERS MILLS", "loc": [-69.507672, 44.258823], "pop": 1082, "state": "ME", "_id": "04341"} -{"city": "DRESDEN", "loc": [-69.745726, 44.078507], "pop": 1336, "state": "ME", "_id": "04342"} -{"city": "FARMINGDALE", "loc": [-69.791313, 44.25233], "pop": 2917, "state": "ME", "_id": "04344"} -{"city": "GARDINER", "loc": [-69.785774, 44.207029], "pop": 8387, "state": "ME", "_id": "04345"} -{"city": "RANDOLPH", "loc": [-69.774918, 44.228704], "pop": 6619, "state": "ME", "_id": "04346"} -{"city": "HALLOWELL", "loc": [-69.805738, 44.286414], "pop": 2613, "state": "ME", "_id": "04347"} -{"city": "JEFFERSON", "loc": [-69.483895, 44.189419], "pop": 1488, "state": "ME", "_id": "04348"} -{"city": "KENTS HILL", "loc": [-70.074822, 44.438259], "pop": 755, "state": "ME", "_id": "04349"} -{"city": "LITCHFIELD", "loc": [-69.958071, 44.163437], "pop": 2354, "state": "ME", "_id": "04350"} -{"city": "MANCHESTER", "loc": [-69.884657, 44.308375], "pop": 603, "state": "ME", "_id": "04351"} -{"city": "MOUNT VERNON", "loc": [-69.990336, 44.499342], "pop": 1396, "state": "ME", "_id": "04352"} -{"city": "NORTH WHITEFIELD", "loc": [-69.602164, 44.217844], "pop": 1931, "state": "ME", "_id": "04353"} -{"city": "PALERMO", "loc": [-69.43337, 44.384282], "pop": 752, "state": "ME", "_id": "04354"} -{"city": "READFIELD", "loc": [-69.950634, 44.403221], "pop": 1725, "state": "ME", "_id": "04355"} -{"city": "RICHMOND", "loc": [-69.821077, 44.104213], "pop": 3072, "state": "ME", "_id": "04357"} -{"city": "SOUTH CHINA", "loc": [-69.58036, 44.395334], "pop": 182, "state": "ME", "_id": "04358"} -{"city": "WEEKS MILLS", "loc": [-69.541738, 44.407543], "pop": 2942, "state": "ME", "_id": "04361"} -{"city": "WINDSOR", "loc": [-69.580587, 44.300878], "pop": 1618, "state": "ME", "_id": "04363"} -{"city": "WINTHROP", "loc": [-69.973128, 44.314031], "pop": 7929, "state": "ME", "_id": "04364"} -{"city": "BANGOR", "loc": [-68.791839, 44.824199], "pop": 40434, "state": "ME", "_id": "04401"} -{"city": "ABBOT VILLAGE", "loc": [-69.52514, 45.279838], "pop": 1193, "state": "ME", "_id": "04406"} -{"city": "AURORA", "loc": [-68.295929, 44.886113], "pop": 141, "state": "ME", "_id": "04408"} -{"city": "BRADFORD", "loc": [-68.923491, 45.08552], "pop": 1103, "state": "ME", "_id": "04410"} -{"city": "BRADLEY", "loc": [-68.626328, 44.901454], "pop": 1136, "state": "ME", "_id": "04411"} -{"city": "BREWER", "loc": [-68.753896, 44.787433], "pop": 9021, "state": "ME", "_id": "04412"} -{"city": "BROOKTON", "loc": [-67.707828, 45.5686], "pop": 236, "state": "ME", "_id": "04413"} -{"city": "BROWNVILLE", "loc": [-69.042331, 45.341229], "pop": 1426, "state": "ME", "_id": "04414"} -{"city": "BUCKSPORT", "loc": [-68.776823, 44.601546], "pop": 5340, "state": "ME", "_id": "04416"} -{"city": "BURLINGTON", "loc": [-68.442701, 45.218438], "pop": 643, "state": "ME", "_id": "04417"} -{"city": "CARDVILLE", "loc": [-68.603277, 45.077599], "pop": 1309, "state": "ME", "_id": "04418"} -{"city": "CARMEL", "loc": [-68.99415, 44.805315], "pop": 3328, "state": "ME", "_id": "04419"} -{"city": "CHARLESTON", "loc": [-69.086856, 45.067017], "pop": 1819, "state": "ME", "_id": "04422"} -{"city": "COSTIGAN", "loc": [-68.612969, 44.975336], "pop": 1895, "state": "ME", "_id": "04423"} -{"city": "DANFORTH", "loc": [-67.868778, 45.668654], "pop": 965, "state": "ME", "_id": "04424"} -{"city": "DOVER FOXCROFT", "loc": [-69.204472, 45.18774], "pop": 5924, "state": "ME", "_id": "04426"} -{"city": "EAST CORINTH", "loc": [-69.008532, 44.983655], "pop": 2177, "state": "ME", "_id": "04427"} -{"city": "EAST EDDINGTON", "loc": [-68.61883, 44.820642], "pop": 2263, "state": "ME", "_id": "04428"} -{"city": "EAST HOLDEN", "loc": [-68.648307, 44.742209], "pop": 4472, "state": "ME", "_id": "04429"} -{"city": "EAST MILLINOCKET", "loc": [-68.572822, 45.629967], "pop": 2198, "state": "ME", "_id": "04430"} -{"city": "EAST ORLAND", "loc": [-68.70174, 44.57249], "pop": 1281, "state": "ME", "_id": "04431"} -{"city": "ENFIELD", "loc": [-68.605802, 45.266477], "pop": 1483, "state": "ME", "_id": "04433"} -{"city": "ETNA", "loc": [-69.132225, 44.793232], "pop": 966, "state": "ME", "_id": "04434"} -{"city": "EXETER", "loc": [-69.107934, 44.967927], "pop": 561, "state": "ME", "_id": "04435"} -{"city": "FRANKFORT", "loc": [-68.933981, 44.59794], "pop": 1141, "state": "ME", "_id": "04438"} -{"city": "GREENVILLE", "loc": [-69.584376, 45.471566], "pop": 2054, "state": "ME", "_id": "04441"} -{"city": "GREENVILLE JUNCT", "loc": [-69.637526, 45.488394], "pop": 99, "state": "ME", "_id": "04442"} -{"city": "GUILFORD", "loc": [-69.397491, 45.173455], "pop": 2833, "state": "ME", "_id": "04443"} -{"city": "HAMPDEN", "loc": [-68.87305, 44.741073], "pop": 6756, "state": "ME", "_id": "04444"} -{"city": "HAYNESVILLE", "loc": [-67.98858, 45.837991], "pop": 244, "state": "ME", "_id": "04446"} -{"city": "SEBOEIS", "loc": [-68.669252, 45.247813], "pop": 1628, "state": "ME", "_id": "04448"} -{"city": "HUDSON", "loc": [-68.887831, 44.991415], "pop": 1048, "state": "ME", "_id": "04449"} -{"city": "KENDUSKEAG", "loc": [-68.934179, 44.918251], "pop": 1234, "state": "ME", "_id": "04450"} -{"city": "KINGMAN", "loc": [-68.23662, 45.598433], "pop": 378, "state": "ME", "_id": "04451"} -{"city": "LAGRANGE", "loc": [-68.83448, 45.178918], "pop": 707, "state": "ME", "_id": "04453"} -{"city": "LEE", "loc": [-68.290885, 45.363504], "pop": 832, "state": "ME", "_id": "04455"} -{"city": "LEVANT", "loc": [-68.98367, 44.884279], "pop": 1627, "state": "ME", "_id": "04456"} -{"city": "LINCOLN", "loc": [-68.507693, 45.350832], "pop": 4715, "state": "ME", "_id": "04457"} -{"city": "LINCOLN CENTER", "loc": [-68.457065, 45.431731], "pop": 1556, "state": "ME", "_id": "04458"} -{"city": "MATTAWAMKEAG", "loc": [-68.35195, 45.526387], "pop": 841, "state": "ME", "_id": "04459"} -{"city": "MEDWAY", "loc": [-68.522709, 45.60704], "pop": 1986, "state": "ME", "_id": "04460"} -{"city": "MILFORD", "loc": [-68.629597, 44.939263], "pop": 1732, "state": "ME", "_id": "04461"} -{"city": "MILLINOCKET", "loc": [-68.710117, 45.659563], "pop": 7265, "state": "ME", "_id": "04462"} -{"city": "DERBY", "loc": [-68.977098, 45.250697], "pop": 3091, "state": "ME", "_id": "04463"} -{"city": "MONSON", "loc": [-69.487986, 45.298088], "pop": 263, "state": "ME", "_id": "04464"} -{"city": "04465", "loc": [-67.844536, 45.951196], "pop": 421, "state": "ME", "_id": "04465"} -{"city": "OLD TOWN", "loc": [-68.67496, 44.943044], "pop": 9290, "state": "ME", "_id": "04468"} -{"city": "NORTH AMITY", "loc": [-67.837741, 45.805639], "pop": 138, "state": "ME", "_id": "04471"} -{"city": "ORLAND", "loc": [-68.731312, 44.545818], "pop": 524, "state": "ME", "_id": "04472"} -{"city": "ORONO", "loc": [-68.675466, 44.892472], "pop": 10484, "state": "ME", "_id": "04473"} -{"city": "ORRINGTON", "loc": [-68.787597, 44.726347], "pop": 3309, "state": "ME", "_id": "04474"} -{"city": "PASSADUMKEAG", "loc": [-68.604328, 45.183687], "pop": 428, "state": "ME", "_id": "04475"} -{"city": "PENOBSCOT", "loc": [-68.757409, 44.434371], "pop": 2290, "state": "ME", "_id": "04476"} -{"city": "ROCKWOOD", "loc": [-69.822442, 45.659329], "pop": 367, "state": "ME", "_id": "04478"} -{"city": "SANGERVILLE", "loc": [-69.321772, 45.140571], "pop": 984, "state": "ME", "_id": "04479"} -{"city": "SPRINGFIELD", "loc": [-68.110756, 45.42638], "pop": 985, "state": "ME", "_id": "04487"} -{"city": "STETSON", "loc": [-69.106877, 44.884337], "pop": 738, "state": "ME", "_id": "04488"} -{"city": "TOPSFIELD", "loc": [-67.747253, 45.430403], "pop": 274, "state": "ME", "_id": "04490"} -{"city": "VANCEBORO", "loc": [-67.463419, 45.558761], "pop": 217, "state": "ME", "_id": "04491"} -{"city": "WAITE", "loc": [-67.642812, 45.357741], "pop": 233, "state": "ME", "_id": "04492"} -{"city": "WINN", "loc": [-68.357465, 45.456786], "pop": 479, "state": "ME", "_id": "04495"} -{"city": "WINTERPORT", "loc": [-68.886174, 44.655247], "pop": 3175, "state": "ME", "_id": "04496"} -{"city": "WYTOPITLOCK", "loc": [-68.105541, 45.664476], "pop": 384, "state": "ME", "_id": "04497"} -{"city": "BATH", "loc": [-69.826565, 43.906155], "pop": 12628, "state": "ME", "_id": "04530"} -{"city": "BOOTHBAY", "loc": [-69.627322, 43.894497], "pop": 1701, "state": "ME", "_id": "04537"} -{"city": "CAPITOL ISLAND", "loc": [-69.61844, 43.854559], "pop": 2800, "state": "ME", "_id": "04538"} -{"city": "BRISTOL", "loc": [-69.495367, 43.951864], "pop": 1220, "state": "ME", "_id": "04539"} -{"city": "CHAMBERLAIN", "loc": [-69.498618, 43.884154], "pop": 482, "state": "ME", "_id": "04541"} -{"city": "DAMARISCOTTA", "loc": [-69.504237, 44.029313], "pop": 1217, "state": "ME", "_id": "04543"} -{"city": "EAST BOOTHBAY", "loc": [-69.593903, 43.826241], "pop": 156, "state": "ME", "_id": "04544"} -{"city": "FRIENDSHIP", "loc": [-69.291604, 44.006741], "pop": 2085, "state": "ME", "_id": "04547"} -{"city": "MAC MAHAN", "loc": [-69.747424, 43.819649], "pop": 936, "state": "ME", "_id": "04548"} -{"city": "MEDOMAK", "loc": [-69.430664, 44.006292], "pop": 540, "state": "ME", "_id": "04551"} -{"city": "NEWCASTLE", "loc": [-69.533113, 44.049866], "pop": 1551, "state": "ME", "_id": "04553"} -{"city": "NEW HARBOR", "loc": [-69.507934, 43.860541], "pop": 362, "state": "ME", "_id": "04554"} -{"city": "NOBLEBORO", "loc": [-69.482786, 44.094301], "pop": 1455, "state": "ME", "_id": "04555"} -{"city": "EDGECOMB", "loc": [-69.619742, 43.979179], "pop": 1238, "state": "ME", "_id": "04556"} -{"city": "PEMAQUID", "loc": [-69.528919, 43.892389], "pop": 103, "state": "ME", "_id": "04558"} -{"city": "PHIPPSBURG", "loc": [-69.814982, 43.768816], "pop": 426, "state": "ME", "_id": "04562"} -{"city": "CUSHING", "loc": [-69.272061, 43.986741], "pop": 12, "state": "ME", "_id": "04563"} -{"city": "ROUND POND", "loc": [-69.46617, 43.924983], "pop": 159, "state": "ME", "_id": "04564"} -{"city": "SEBASCO ESTATES", "loc": [-69.857557, 43.769342], "pop": 479, "state": "ME", "_id": "04565"} -{"city": "SMALL POINT", "loc": [-69.841163, 43.731724], "pop": 66, "state": "ME", "_id": "04567"} -{"city": "SOUTH BRISTOL", "loc": [-69.561367, 43.867714], "pop": 476, "state": "ME", "_id": "04568"} -{"city": "SQUIRREL ISLAND", "loc": [-69.630974, 43.809031], "pop": 3, "state": "ME", "_id": "04570"} -{"city": "TREVETT", "loc": [-69.674601, 43.893508], "pop": 338, "state": "ME", "_id": "04571"} -{"city": "WALDOBORO", "loc": [-69.374537, 44.104601], "pop": 4702, "state": "ME", "_id": "04572"} -{"city": "WALPOLE", "loc": [-69.55165, 43.946235], "pop": 349, "state": "ME", "_id": "04573"} -{"city": "WASHINGTON", "loc": [-69.384237, 44.269281], "pop": 1261, "state": "ME", "_id": "04574"} -{"city": "WEST SOUTHPORT", "loc": [-69.667343, 43.823775], "pop": 642, "state": "ME", "_id": "04576"} -{"city": "WISCASSET", "loc": [-69.682576, 44.007444], "pop": 5928, "state": "ME", "_id": "04578"} -{"city": "WOOLWICH", "loc": [-69.789098, 43.950317], "pop": 1781, "state": "ME", "_id": "04579"} -{"city": "ELLSWORTH", "loc": [-68.412093, 44.554824], "pop": 10671, "state": "ME", "_id": "04605"} -{"city": "ADDISON", "loc": [-67.714573, 44.583038], "pop": 1114, "state": "ME", "_id": "04606"} -{"city": "GOULDSBORO", "loc": [-68.089857, 44.473054], "pop": 1064, "state": "ME", "_id": "04607"} -{"city": "BAR HARBOR", "loc": [-68.244791, 44.373799], "pop": 5513, "state": "ME", "_id": "04609"} -{"city": "BEALS", "loc": [-67.605554, 44.512967], "pop": 667, "state": "ME", "_id": "04611"} -{"city": "BERNARD", "loc": [-68.35803, 44.241543], "pop": 186, "state": "ME", "_id": "04612"} -{"city": "BIRCH HARBOR", "loc": [-68.031686, 44.384195], "pop": 229, "state": "ME", "_id": "04613"} -{"city": "BLUE HILL", "loc": [-68.588513, 44.434265], "pop": 1417, "state": "ME", "_id": "04614"} -{"city": "BLUE HILL FALLS", "loc": [-68.590282, 44.390244], "pop": 375, "state": "ME", "_id": "04615"} -{"city": "BROOKLIN", "loc": [-68.556518, 44.254134], "pop": 260, "state": "ME", "_id": "04616"} -{"city": "BROOKSVILLE", "loc": [-68.731279, 44.375635], "pop": 594, "state": "ME", "_id": "04617"} -{"city": "BUCKS HARBOR", "loc": [-67.396382, 44.624762], "pop": 402, "state": "ME", "_id": "04618"} -{"city": "CALAIS", "loc": [-67.26408, 45.171478], "pop": 3963, "state": "ME", "_id": "04619"} -{"city": "CHERRYFIELD", "loc": [-67.943617, 44.622791], "pop": 1341, "state": "ME", "_id": "04622"} -{"city": "COLUMBIA FALLS", "loc": [-67.75341, 44.66988], "pop": 960, "state": "ME", "_id": "04623"} -{"city": "COREA", "loc": [-67.985032, 44.405281], "pop": 507, "state": "ME", "_id": "04624"} -{"city": "CUTLER", "loc": [-67.249869, 44.67531], "pop": 779, "state": "ME", "_id": "04626"} -{"city": "DEER ISLE", "loc": [-68.644839, 44.233951], "pop": 949, "state": "ME", "_id": "04627"} -{"city": "DENNYSVILLE", "loc": [-67.224431, 44.896105], "pop": 684, "state": "ME", "_id": "04628"} -{"city": "EAST MACHIAS", "loc": [-67.382066, 44.742362], "pop": 1574, "state": "ME", "_id": "04630"} -{"city": "EASTPORT", "loc": [-67.00739, 44.919966], "pop": 2514, "state": "ME", "_id": "04631"} -{"city": "FRANKLIN", "loc": [-68.241653, 44.608734], "pop": 1433, "state": "ME", "_id": "04634"} -{"city": "HANCOCK", "loc": [-68.2402, 44.50459], "pop": 769, "state": "ME", "_id": "04640"} -{"city": "HARBORSIDE", "loc": [-68.800901, 44.338584], "pop": 122, "state": "ME", "_id": "04642"} -{"city": "HARRINGTON", "loc": [-67.814722, 44.612218], "pop": 986, "state": "ME", "_id": "04643"} -{"city": "ISLE AU HAUT", "loc": [-68.620598, 44.056057], "pop": 46, "state": "ME", "_id": "04645"} -{"city": "JONESBORO", "loc": [-67.577692, 44.658153], "pop": 585, "state": "ME", "_id": "04648"} -{"city": "JONESPORT", "loc": [-67.604403, 44.550936], "pop": 1520, "state": "ME", "_id": "04649"} -{"city": "LITTLE DEER ISLE", "loc": [-68.706663, 44.285213], "pop": 235, "state": "ME", "_id": "04650"} -{"city": "LUBEC", "loc": [-67.046016, 44.834772], "pop": 2349, "state": "ME", "_id": "04652"} -{"city": "BASS HARBOR", "loc": [-68.380387, 44.1993], "pop": 827, "state": "ME", "_id": "04653"} -{"city": "MACHIAS", "loc": [-67.481996, 44.72154], "pop": 3766, "state": "ME", "_id": "04654"} -{"city": "MACHIASPORT", "loc": [-67.407277, 44.682039], "pop": 602, "state": "ME", "_id": "04655"} -{"city": "MANSET", "loc": [-68.314408, 44.262573], "pop": 445, "state": "ME", "_id": "04656"} -{"city": "MEDDYBEMPS", "loc": [-67.382852, 45.019306], "pop": 242, "state": "ME", "_id": "04657"} -{"city": "MILBRIDGE", "loc": [-67.884433, 44.536601], "pop": 1587, "state": "ME", "_id": "04658"} -{"city": "MOUNT DESERT", "loc": [-68.352253, 44.311547], "pop": 2184, "state": "ME", "_id": "04660"} -{"city": "NORTH BROOKLIN", "loc": [-68.577326, 44.308469], "pop": 674, "state": "ME", "_id": "04661"} -{"city": "PEMBROKE", "loc": [-67.200204, 44.965396], "pop": 1151, "state": "ME", "_id": "04666"} -{"city": "PERRY", "loc": [-67.092882, 44.988824], "pop": 781, "state": "ME", "_id": "04667"} -{"city": "PRINCETON", "loc": [-67.600826, 45.213091], "pop": 1918, "state": "ME", "_id": "04668"} -{"city": "PROSPECT HARBOR", "loc": [-68.027943, 44.419489], "pop": 385, "state": "ME", "_id": "04669"} -{"city": "ROBBINSTON", "loc": [-67.143301, 45.067007], "pop": 495, "state": "ME", "_id": "04671"} -{"city": "SARGENTVILLE", "loc": [-68.705221, 44.3345], "pop": 43, "state": "ME", "_id": "04673"} -{"city": "SEDGWICK", "loc": [-68.637659, 44.33552], "pop": 908, "state": "ME", "_id": "04676"} -{"city": "SORRENTO", "loc": [-68.178665, 44.490696], "pop": 295, "state": "ME", "_id": "04677"} -{"city": "SOUTH GOULDSBORO", "loc": [-68.030406, 44.47163], "pop": 58, "state": "ME", "_id": "04678"} -{"city": "SOUTHWEST HARBOR", "loc": [-68.326471, 44.2823], "pop": 927, "state": "ME", "_id": "04679"} -{"city": "STEUBEN", "loc": [-67.950319, 44.497082], "pop": 773, "state": "ME", "_id": "04680"} -{"city": "STONINGTON", "loc": [-68.674617, 44.175197], "pop": 1746, "state": "ME", "_id": "04681"} -{"city": "SUNSET", "loc": [-68.707002, 44.217404], "pop": 149, "state": "ME", "_id": "04683"} -{"city": "SURRY", "loc": [-68.506272, 44.488272], "pop": 1004, "state": "ME", "_id": "04684"} -{"city": "04689", "loc": [-68.195943, 44.538181], "pop": 859, "state": "ME", "_id": "04689"} -{"city": "WEST TREMONT", "loc": [-68.380365, 44.252778], "pop": 119, "state": "ME", "_id": "04690"} -{"city": "WINTER HARBOR", "loc": [-68.084316, 44.390038], "pop": 1157, "state": "ME", "_id": "04693"} -{"city": "WOODLAND", "loc": [-67.417519, 45.133234], "pop": 2909, "state": "ME", "_id": "04694"} -{"city": "HOULTON", "loc": [-67.863014, 46.11885], "pop": 10382, "state": "ME", "_id": "04730"} -{"city": "ASHLAND", "loc": [-68.387608, 46.618438], "pop": 2022, "state": "ME", "_id": "04732"} -{"city": "BENEDICTA", "loc": [-68.408855, 45.812484], "pop": 192, "state": "ME", "_id": "04733"} -{"city": "BRIDGEWATER", "loc": [-67.841469, 46.422154], "pop": 652, "state": "ME", "_id": "04735"} -{"city": "CARIBOU", "loc": [-68.020352, 46.870569], "pop": 10928, "state": "ME", "_id": "04736"} -{"city": "CLAYTON LAKE", "loc": [-69.626445, 46.629805], "pop": 54, "state": "ME", "_id": "04737"} -{"city": "EASTON", "loc": [-67.901777, 46.635697], "pop": 1056, "state": "ME", "_id": "04740"} -{"city": "FORT FAIRFIELD", "loc": [-67.840204, 46.762272], "pop": 4474, "state": "ME", "_id": "04742"} -{"city": "FORT KENT", "loc": [-68.590855, 47.196857], "pop": 6076, "state": "ME", "_id": "04743"} -{"city": "GRAND ISLE", "loc": [-68.154238, 47.304428], "pop": 318, "state": "ME", "_id": "04746"} -{"city": "ISLAND FALLS", "loc": [-68.266722, 46.016866], "pop": 1444, "state": "ME", "_id": "04747"} -{"city": "LILLE", "loc": [-68.110393, 47.263623], "pop": 253, "state": "ME", "_id": "04749"} -{"city": "LIMESTONE", "loc": [-67.845086, 46.924786], "pop": 2693, "state": "ME", "_id": "04750"} -{"city": "LORING AFB", "loc": [-67.89858, 46.9416], "pop": 7844, "state": "ME", "_id": "04751"} -{"city": "MADAWASKA", "loc": [-68.33281, 47.329447], "pop": 5860, "state": "ME", "_id": "04756"} -{"city": "MAPLETON", "loc": [-68.153584, 46.674634], "pop": 1982, "state": "ME", "_id": "04757"} -{"city": "MARS HILL", "loc": [-67.862973, 46.522268], "pop": 2010, "state": "ME", "_id": "04758"} -{"city": "MONTICELLO", "loc": [-67.841439, 46.300658], "pop": 1174, "state": "ME", "_id": "04760"} -{"city": "NEW SWEDEN", "loc": [-68.11545, 46.955916], "pop": 935, "state": "ME", "_id": "04762"} -{"city": "OAKFIELD", "loc": [-68.129805, 46.108753], "pop": 1113, "state": "ME", "_id": "04763"} -{"city": "OXBOW", "loc": [-68.521792, 46.401955], "pop": 76, "state": "ME", "_id": "04764"} -{"city": "PATTEN", "loc": [-68.464669, 46.01316], "pop": 1595, "state": "ME", "_id": "04765"} -{"city": "PORTAGE", "loc": [-68.487675, 46.775303], "pop": 445, "state": "ME", "_id": "04768"} -{"city": "PRESQUE ISLE", "loc": [-68.01179, 46.684151], "pop": 11058, "state": "ME", "_id": "04769"} -{"city": "SAINT AGATHA", "loc": [-68.323237, 47.238655], "pop": 919, "state": "ME", "_id": "04772"} -{"city": "SAINT DAVID", "loc": [-68.231368, 47.334328], "pop": 285, "state": "ME", "_id": "04773"} -{"city": "SAINT FRANCIS", "loc": [-68.950309, 47.14071], "pop": 1048, "state": "ME", "_id": "04774"} -{"city": "SHERMAN MILLS", "loc": [-68.369587, 45.877727], "pop": 1139, "state": "ME", "_id": "04776"} -{"city": "SHERMAN STATION", "loc": [-68.461844, 45.885892], "pop": 527, "state": "ME", "_id": "04777"} -{"city": "SINCLAIR", "loc": [-68.267922, 47.174384], "pop": 216, "state": "ME", "_id": "04779"} -{"city": "SMYRNA MILLS", "loc": [-68.206363, 46.146412], "pop": 453, "state": "ME", "_id": "04780"} -{"city": "SOLDIER POND", "loc": [-68.598081, 47.151529], "pop": 484, "state": "ME", "_id": "04781"} -{"city": "STOCKHOLM", "loc": [-68.208614, 47.064299], "pop": 665, "state": "ME", "_id": "04783"} -{"city": "VAN BUREN", "loc": [-67.94589, 47.158857], "pop": 3391, "state": "ME", "_id": "04785"} -{"city": "WASHBURN", "loc": [-68.133836, 46.788271], "pop": 2783, "state": "ME", "_id": "04786"} -{"city": "WESTFIELD", "loc": [-67.897365, 46.508687], "pop": 854, "state": "ME", "_id": "04787"} -{"city": "ROCKLAND", "loc": [-69.113938, 44.112326], "pop": 8975, "state": "ME", "_id": "04841"} -{"city": "CAMDEN", "loc": [-69.076682, 44.213718], "pop": 5060, "state": "ME", "_id": "04843"} -{"city": "HOPE", "loc": [-69.146743, 44.258902], "pop": 143, "state": "ME", "_id": "04847"} -{"city": "ISLESBORO", "loc": [-68.907346, 44.30823], "pop": 580, "state": "ME", "_id": "04848"} -{"city": "LINCOLNVILLE", "loc": [-69.082446, 44.304825], "pop": 2458, "state": "ME", "_id": "04849"} -{"city": "MONHEGAN", "loc": [-69.316429, 43.764222], "pop": 88, "state": "ME", "_id": "04852"} -{"city": "NORTH HAVEN", "loc": [-68.866744, 44.143598], "pop": 333, "state": "ME", "_id": "04853"} -{"city": "OWLS HEAD", "loc": [-69.089416, 44.073186], "pop": 1624, "state": "ME", "_id": "04854"} -{"city": "ROCKPORT", "loc": [-69.090114, 44.188787], "pop": 1421, "state": "ME", "_id": "04856"} -{"city": "SAINT GEORGE", "loc": [-69.201984, 43.998687], "pop": 225, "state": "ME", "_id": "04857"} -{"city": "SOUTH THOMASTON", "loc": [-69.135937, 44.037785], "pop": 1172, "state": "ME", "_id": "04858"} -{"city": "SPRUCE HEAD", "loc": [-69.17071, 44.010405], "pop": 719, "state": "ME", "_id": "04859"} -{"city": "TENANTS HARBOR", "loc": [-69.231501, 43.955473], "pop": 1322, "state": "ME", "_id": "04860"} -{"city": "THOMASTON", "loc": [-69.188803, 44.084568], "pop": 3560, "state": "ME", "_id": "04861"} -{"city": "UNION", "loc": [-69.252435, 44.242404], "pop": 3853, "state": "ME", "_id": "04862"} -{"city": "VINALHAVEN", "loc": [-68.836816, 44.03968], "pop": 1139, "state": "ME", "_id": "04863"} -{"city": "WARREN", "loc": [-69.247894, 44.127066], "pop": 2816, "state": "ME", "_id": "04864"} -{"city": "WEST ROCKPORT", "loc": [-69.151049, 44.181966], "pop": 555, "state": "ME", "_id": "04865"} -{"city": "WINSLOW", "loc": [-69.635917, 44.547967], "pop": 24915, "state": "ME", "_id": "04901"} -{"city": "ALBION", "loc": [-69.468286, 44.535527], "pop": 2616, "state": "ME", "_id": "04910"} -{"city": "ANSON", "loc": [-69.930856, 44.783107], "pop": 1698, "state": "ME", "_id": "04911"} -{"city": "ATHENS", "loc": [-69.669482, 44.938844], "pop": 897, "state": "ME", "_id": "04912"} -{"city": "BELFAST", "loc": [-69.014753, 44.435436], "pop": 9637, "state": "ME", "_id": "04915"} -{"city": "BELGRADE", "loc": [-69.860636, 44.46875], "pop": 1513, "state": "ME", "_id": "04917"} -{"city": "BELGRADE LAKES", "loc": [-69.863681, 44.511103], "pop": 316, "state": "ME", "_id": "04918"} -{"city": "BINGHAM", "loc": [-69.885721, 45.068241], "pop": 1712, "state": "ME", "_id": "04920"} -{"city": "BROOKS", "loc": [-69.140393, 44.567807], "pop": 1846, "state": "ME", "_id": "04921"} -{"city": "BURNHAM", "loc": [-69.380049, 44.684766], "pop": 961, "state": "ME", "_id": "04922"} -{"city": "CAMBRIDGE", "loc": [-69.44193, 45.051281], "pop": 863, "state": "ME", "_id": "04923"} -{"city": "CANAAN", "loc": [-69.549777, 44.744976], "pop": 1083, "state": "ME", "_id": "04924"} -{"city": "CARATUNK", "loc": [-69.938617, 45.240999], "pop": 124, "state": "ME", "_id": "04925"} -{"city": "CLINTON", "loc": [-69.528405, 44.643995], "pop": 4598, "state": "ME", "_id": "04927"} -{"city": "CORINNA", "loc": [-69.232341, 44.926045], "pop": 2363, "state": "ME", "_id": "04928"} -{"city": "DETROIT", "loc": [-69.316081, 44.777526], "pop": 751, "state": "ME", "_id": "04929"} -{"city": "DEXTER", "loc": [-69.279654, 45.020347], "pop": 5875, "state": "ME", "_id": "04930"} -{"city": "DIXMONT", "loc": [-69.102532, 44.699078], "pop": 1660, "state": "ME", "_id": "04932"} -{"city": "EUSTIS", "loc": [-70.457067, 45.162559], "pop": 539, "state": "ME", "_id": "04936"} -{"city": "BENTON STATION", "loc": [-69.63321, 44.609639], "pop": 6718, "state": "ME", "_id": "04937"} -{"city": "FARMINGTON", "loc": [-70.132878, 44.665339], "pop": 9133, "state": "ME", "_id": "04938"} -{"city": "FREEDOM", "loc": [-69.319045, 44.463311], "pop": 1775, "state": "ME", "_id": "04941"} -{"city": "WELLINGTON", "loc": [-69.56871, 45.003926], "pop": 1217, "state": "ME", "_id": "04942"} -{"city": "HARTLAND", "loc": [-69.471754, 44.878155], "pop": 1821, "state": "ME", "_id": "04943"} -{"city": "JACKMAN", "loc": [-70.249227, 45.635081], "pop": 1207, "state": "ME", "_id": "04945"} -{"city": "KINGFIELD", "loc": [-70.183177, 44.985403], "pop": 1501, "state": "ME", "_id": "04947"} -{"city": "LIBERTY", "loc": [-69.33058, 44.374085], "pop": 790, "state": "ME", "_id": "04949"} -{"city": "MADISON", "loc": [-69.8449, 44.809623], "pop": 4725, "state": "ME", "_id": "04950"} -{"city": "MONROE", "loc": [-69.03169, 44.592674], "pop": 616, "state": "ME", "_id": "04951"} -{"city": "MORRILL", "loc": [-69.147105, 44.410693], "pop": 1433, "state": "ME", "_id": "04952"} -{"city": "NEWPORT", "loc": [-69.267498, 44.839264], "pop": 3272, "state": "ME", "_id": "04953"} -{"city": "NEW PORTLAND", "loc": [-70.092857, 44.885571], "pop": 312, "state": "ME", "_id": "04954"} -{"city": "NEW SHARON", "loc": [-70.013927, 44.645803], "pop": 1175, "state": "ME", "_id": "04955"} -{"city": "NEW VINEYARD", "loc": [-70.121953, 44.796658], "pop": 661, "state": "ME", "_id": "04956"} -{"city": "NORRIDGEWOCK", "loc": [-69.830583, 44.68994], "pop": 4520, "state": "ME", "_id": "04957"} -{"city": "NORTH ANSON", "loc": [-69.911946, 44.879662], "pop": 1967, "state": "ME", "_id": "04958"} -{"city": "NORTH NEW PORTLA", "loc": [-70.043268, 44.95524], "pop": 537, "state": "ME", "_id": "04961"} -{"city": "NORTH VASSALBORO", "loc": [-69.57808, 44.477748], "pop": 2457, "state": "ME", "_id": "04962"} -{"city": "OAKLAND", "loc": [-69.740082, 44.51729], "pop": 8695, "state": "ME", "_id": "04963"} -{"city": "PALMYRA", "loc": [-69.381084, 44.857578], "pop": 1093, "state": "ME", "_id": "04965"} -{"city": "PHILLIPS", "loc": [-70.360079, 44.837486], "pop": 1930, "state": "ME", "_id": "04966"} -{"city": "PITTSFIELD", "loc": [-69.40223, 44.787109], "pop": 4719, "state": "ME", "_id": "04967"} -{"city": "PLYMOUTH", "loc": [-69.226562, 44.769938], "pop": 974, "state": "ME", "_id": "04969"} -{"city": "RANGELEY", "loc": [-70.665844, 44.96306], "pop": 1391, "state": "ME", "_id": "04970"} -{"city": "SAINT ALBANS", "loc": [-69.399179, 44.929264], "pop": 1575, "state": "ME", "_id": "04971"} -{"city": "SEARSMONT", "loc": [-69.219579, 44.377035], "pop": 422, "state": "ME", "_id": "04973"} -{"city": "SEARSPORT", "loc": [-68.931125, 44.487672], "pop": 1927, "state": "ME", "_id": "04974"} -{"city": "SKOWHEGAN", "loc": [-69.697571, 44.777212], "pop": 10166, "state": "ME", "_id": "04976"} -{"city": "SMITHFIELD", "loc": [-69.807496, 44.630107], "pop": 715, "state": "ME", "_id": "04978"} -{"city": "SOLON", "loc": [-69.833018, 44.967605], "pop": 1236, "state": "ME", "_id": "04979"} -{"city": "STOCKTON SPRINGS", "loc": [-68.855974, 44.514056], "pop": 1925, "state": "ME", "_id": "04981"} -{"city": "STRATTON", "loc": [-70.431928, 45.123197], "pop": 262, "state": "ME", "_id": "04982"} -{"city": "STRONG", "loc": [-70.222054, 44.82236], "pop": 1631, "state": "ME", "_id": "04983"} -{"city": "TEMPLE", "loc": [-70.242635, 44.695417], "pop": 560, "state": "ME", "_id": "04984"} -{"city": "WEST FORKS", "loc": [-69.984057, 45.383905], "pop": 86, "state": "ME", "_id": "04985"} -{"city": "THORNDIKE", "loc": [-69.248658, 44.574406], "pop": 962, "state": "ME", "_id": "04986"} -{"city": "TROY", "loc": [-69.25488, 44.675657], "pop": 802, "state": "ME", "_id": "04987"} -{"city": "UNITY", "loc": [-69.332778, 44.600676], "pop": 1817, "state": "ME", "_id": "04988"} -{"city": "VASSALBORO", "loc": [-69.651904, 44.440455], "pop": 1929, "state": "ME", "_id": "04989"} -{"city": "ANDREWS AFB", "loc": [-76.886695, 38.800324], "pop": 10228, "state": "MD", "_id": "20331"} -{"city": "WALDORF", "loc": [-76.877787, 38.637065], "pop": 18548, "state": "MD", "_id": "20601"} -{"city": "SAINT CHARLES", "loc": [-76.903414, 38.601545], "pop": 20663, "state": "MD", "_id": "20602"} -{"city": "SAINT CHARLES", "loc": [-76.961154, 38.624929], "pop": 11184, "state": "MD", "_id": "20603"} -{"city": "ABELL", "loc": [-76.744104, 38.249554], "pop": 601, "state": "MD", "_id": "20606"} -{"city": "ACCOKEEK", "loc": [-77.016217, 38.671992], "pop": 4043, "state": "MD", "_id": "20607"} -{"city": "AQUASCO", "loc": [-76.714947, 38.582496], "pop": 939, "state": "MD", "_id": "20608"} -{"city": "AVENUE", "loc": [-76.746553, 38.282624], "pop": 699, "state": "MD", "_id": "20609"} -{"city": "BEL ALTON", "loc": [-76.978873, 38.473141], "pop": 582, "state": "MD", "_id": "20611"} -{"city": "BRANDYWINE", "loc": [-76.832033, 38.692203], "pop": 7627, "state": "MD", "_id": "20613"} -{"city": "BROOMES ISLAND", "loc": [-76.547763, 38.417963], "pop": 404, "state": "MD", "_id": "20615"} -{"city": "BRYANS ROAD", "loc": [-77.076589, 38.641468], "pop": 3792, "state": "MD", "_id": "20616"} -{"city": "BRYANTOWN", "loc": [-76.846539, 38.542638], "pop": 722, "state": "MD", "_id": "20617"} -{"city": "BUSHWOOD", "loc": [-76.792863, 38.284439], "pop": 934, "state": "MD", "_id": "20618"} -{"city": "CALIFORNIA", "loc": [-76.531228, 38.300648], "pop": 5485, "state": "MD", "_id": "20619"} -{"city": "CALLAWAY", "loc": [-76.520963, 38.227521], "pop": 1394, "state": "MD", "_id": "20620"} -{"city": "MADDOX", "loc": [-76.782343, 38.333197], "pop": 1240, "state": "MD", "_id": "20621"} -{"city": "CHARLOTTE HALL", "loc": [-76.803792, 38.474977], "pop": 3454, "state": "MD", "_id": "20622"} -{"city": "CHELTENHAM", "loc": [-76.836852, 38.753094], "pop": 712, "state": "MD", "_id": "20623"} -{"city": "CLEMENTS", "loc": [-76.726419, 38.340716], "pop": 303, "state": "MD", "_id": "20624"} -{"city": "COLTONS POINT", "loc": [-76.764649, 38.236963], "pop": 694, "state": "MD", "_id": "20626"} -{"city": "DAMERON", "loc": [-76.357474, 38.153271], "pop": 330, "state": "MD", "_id": "20628"} -{"city": "DRAYDEN", "loc": [-76.473095, 38.171875], "pop": 413, "state": "MD", "_id": "20630"} -{"city": "FAULKNER", "loc": [-76.972937, 38.438221], "pop": 459, "state": "MD", "_id": "20632"} -{"city": "GREAT MILLS", "loc": [-76.495365, 38.267431], "pop": 4203, "state": "MD", "_id": "20634"} -{"city": "HOLLYWOOD", "loc": [-76.562644, 38.352356], "pop": 6785, "state": "MD", "_id": "20636"} -{"city": "HUGHESVILLE", "loc": [-76.781677, 38.520712], "pop": 4078, "state": "MD", "_id": "20637"} -{"city": "HUNTINGTOWN", "loc": [-76.600268, 38.609453], "pop": 8290, "state": "MD", "_id": "20639"} -{"city": "PISGAH", "loc": [-77.148357, 38.588855], "pop": 8227, "state": "MD", "_id": "20640"} -{"city": "ISSUE", "loc": [-76.867527, 38.304327], "pop": 87, "state": "MD", "_id": "20645"} -{"city": "LA PLATA", "loc": [-76.986498, 38.525673], "pop": 16900, "state": "MD", "_id": "20646"} -{"city": "LEONARDTOWN", "loc": [-76.638037, 38.277425], "pop": 8922, "state": "MD", "_id": "20650"} -{"city": "LEXINGTON PARK", "loc": [-76.45293, 38.249452], "pop": 17082, "state": "MD", "_id": "20653"} -{"city": "LOVEVILLE", "loc": [-76.67708, 38.347438], "pop": 248, "state": "MD", "_id": "20656"} -{"city": "LUSBY", "loc": [-76.43464, 38.366128], "pop": 8990, "state": "MD", "_id": "20657"} -{"city": "RISON", "loc": [-77.156329, 38.557335], "pop": 1826, "state": "MD", "_id": "20658"} -{"city": "MECHANICSVILLE", "loc": [-76.725399, 38.429319], "pop": 17326, "state": "MD", "_id": "20659"} -{"city": "NANJEMOY", "loc": [-77.198296, 38.446161], "pop": 2793, "state": "MD", "_id": "20662"} -{"city": "NEWBURG", "loc": [-76.917499, 38.329782], "pop": 3612, "state": "MD", "_id": "20664"} -{"city": "PARK HALL", "loc": [-76.441522, 38.224364], "pop": 224, "state": "MD", "_id": "20667"} -{"city": "PATUXENT RIVER", "loc": [-76.438061, 38.279089], "pop": 3015, "state": "MD", "_id": "20670"} -{"city": "PINEY POINT", "loc": [-76.504716, 38.139667], "pop": 1114, "state": "MD", "_id": "20674"} -{"city": "POMFRET", "loc": [-77.009314, 38.585541], "pop": 1410, "state": "MD", "_id": "20675"} -{"city": "PORT REPUBLIC", "loc": [-76.534865, 38.495237], "pop": 2647, "state": "MD", "_id": "20676"} -{"city": "PORT TOBACCO", "loc": [-77.041856, 38.499353], "pop": 1149, "state": "MD", "_id": "20677"} -{"city": "PRINCE FREDERICK", "loc": [-76.595495, 38.533629], "pop": 6802, "state": "MD", "_id": "20678"} -{"city": "RIDGE", "loc": [-76.371071, 38.116883], "pop": 1245, "state": "MD", "_id": "20680"} -{"city": "SAINT INIGOES", "loc": [-76.408328, 38.14406], "pop": 583, "state": "MD", "_id": "20684"} -{"city": "SAINT LEONARD", "loc": [-76.511017, 38.45013], "pop": 3895, "state": "MD", "_id": "20685"} -{"city": "SCOTLAND", "loc": [-76.347684, 38.082772], "pop": 356, "state": "MD", "_id": "20687"} -{"city": "SOLOMONS", "loc": [-76.458886, 38.334067], "pop": 661, "state": "MD", "_id": "20688"} -{"city": "SUNDERLAND", "loc": [-76.576697, 38.648968], "pop": 1664, "state": "MD", "_id": "20689"} -{"city": "TALL TIMBERS", "loc": [-76.539897, 38.165318], "pop": 255, "state": "MD", "_id": "20690"} -{"city": "VALLEY LEE", "loc": [-76.508689, 38.189937], "pop": 667, "state": "MD", "_id": "20692"} -{"city": "WELCOME", "loc": [-77.095006, 38.467206], "pop": 334, "state": "MD", "_id": "20693"} -{"city": "WHITE PLAINS", "loc": [-76.980233, 38.594848], "pop": 3721, "state": "MD", "_id": "20695"} -{"city": "ANNAPOLIS JUNCTI", "loc": [-76.798068, 39.130983], "pop": 32, "state": "MD", "_id": "20701"} -{"city": "BELTSVILLE", "loc": [-76.924168, 39.045524], "pop": 18146, "state": "MD", "_id": "20705"} -{"city": "LANHAM", "loc": [-76.8551, 38.967537], "pop": 33471, "state": "MD", "_id": "20706"} -{"city": "LAUREL", "loc": [-76.872043, 39.107687], "pop": 31065, "state": "MD", "_id": "20707"} -{"city": "MONTPELIER", "loc": [-76.847725, 39.068376], "pop": 23523, "state": "MD", "_id": "20708"} -{"city": "BLADENSBURG", "loc": [-76.920327, 38.945215], "pop": 7716, "state": "MD", "_id": "20710"} -{"city": "LOTHIAN", "loc": [-76.66281, 38.802933], "pop": 5081, "state": "MD", "_id": "20711"} -{"city": "MOUNT RAINIER", "loc": [-76.965152, 38.943072], "pop": 8422, "state": "MD", "_id": "20712"} -{"city": "NORTH BEACH", "loc": [-76.536701, 38.711884], "pop": 2292, "state": "MD", "_id": "20714"} -{"city": "BOWIE", "loc": [-76.743497, 38.979696], "pop": 25296, "state": "MD", "_id": "20715"} -{"city": "MITCHELLVILLE", "loc": [-76.731979, 38.927482], "pop": 13580, "state": "MD", "_id": "20716"} -{"city": "BOWIE", "loc": [-76.789526, 38.973733], "pop": 8239, "state": "MD", "_id": "20720"} -{"city": "MITCHELLVILLE", "loc": [-76.80527, 38.919588], "pop": 10551, "state": "MD", "_id": "20721"} -{"city": "BRENTWOOD", "loc": [-76.953062, 38.940701], "pop": 5769, "state": "MD", "_id": "20722"} -{"city": "LAUREL", "loc": [-76.84345, 39.120806], "pop": 10457, "state": "MD", "_id": "20723"} -{"city": "LAUREL", "loc": [-76.815485, 39.095801], "pop": 7656, "state": "MD", "_id": "20724"} -{"city": "CHESAPEAKE BEACH", "loc": [-76.537629, 38.669798], "pop": 4856, "state": "MD", "_id": "20732"} -{"city": "CHURCHTON", "loc": [-76.524773, 38.801791], "pop": 2539, "state": "MD", "_id": "20733"} -{"city": "CLINTON", "loc": [-76.902577, 38.754892], "pop": 26417, "state": "MD", "_id": "20735"} -{"city": "OWINGS", "loc": [-76.606093, 38.695507], "pop": 6876, "state": "MD", "_id": "20736"} -{"city": "RIVERDALE", "loc": [-76.914658, 38.960067], "pop": 16328, "state": "MD", "_id": "20737"} -{"city": "COLLEGE PARK", "loc": [-76.929891, 38.996303], "pop": 29336, "state": "MD", "_id": "20740"} -{"city": "CAPITAL HEIGHTS", "loc": [-76.906665, 38.88385], "pop": 40459, "state": "MD", "_id": "20743"} -{"city": "FORT WASHINGTON", "loc": [-76.983531, 38.758656], "pop": 44735, "state": "MD", "_id": "20744"} -{"city": "OXON HILL", "loc": [-76.989776, 38.810764], "pop": 29487, "state": "MD", "_id": "20745"} -{"city": "SUITLAND", "loc": [-76.922156, 38.842453], "pop": 30601, "state": "MD", "_id": "20746"} -{"city": "DISTRICT HEIGHTS", "loc": [-76.889132, 38.853887], "pop": 35872, "state": "MD", "_id": "20747"} -{"city": "TEMPLE HILLS", "loc": [-76.947786, 38.822159], "pop": 40471, "state": "MD", "_id": "20748"} -{"city": "DEALE", "loc": [-76.551498, 38.782909], "pop": 1909, "state": "MD", "_id": "20751"} -{"city": "DUNKIRK", "loc": [-76.642658, 38.740763], "pop": 5919, "state": "MD", "_id": "20754"} -{"city": "FORT GEORGE G ME", "loc": [-76.745013, 39.112923], "pop": 11110, "state": "MD", "_id": "20755"} -{"city": "FRIENDSHIP", "loc": [-76.600093, 38.739077], "pop": 853, "state": "MD", "_id": "20758"} -{"city": "FULTON", "loc": [-76.92999, 39.150179], "pop": 1640, "state": "MD", "_id": "20759"} -{"city": "SAVAGE", "loc": [-76.821818, 39.137962], "pop": 2227, "state": "MD", "_id": "20763"} -{"city": "SHADY SIDE", "loc": [-76.510884, 38.836794], "pop": 2957, "state": "MD", "_id": "20764"} -{"city": "GLENN DALE", "loc": [-76.805336, 38.976632], "pop": 3242, "state": "MD", "_id": "20769"} -{"city": "GREENBELT", "loc": [-76.883964, 38.999559], "pop": 21125, "state": "MD", "_id": "20770"} -{"city": "UPPER MARLBORO", "loc": [-76.798028, 38.837717], "pop": 49684, "state": "MD", "_id": "20772"} -{"city": "HARWOOD", "loc": [-76.614458, 38.858152], "pop": 3765, "state": "MD", "_id": "20776"} -{"city": "HIGHLAND", "loc": [-76.968643, 39.184252], "pop": 2596, "state": "MD", "_id": "20777"} -{"city": "WEST RIVER", "loc": [-76.539113, 38.825194], "pop": 1026, "state": "MD", "_id": "20778"} -{"city": "TRACYS LANDING", "loc": [-76.57524, 38.767101], "pop": 413, "state": "MD", "_id": "20779"} -{"city": "HYATTSVILLE", "loc": [-76.934652, 38.95063], "pop": 11716, "state": "MD", "_id": "20781"} -{"city": "WEST HYATTSVILLE", "loc": [-76.966632, 38.963575], "pop": 28026, "state": "MD", "_id": "20782"} -{"city": "ADELPHI", "loc": [-76.97472, 38.993751], "pop": 40007, "state": "MD", "_id": "20783"} -{"city": "LANDOVER HILLS", "loc": [-76.888829, 38.951541], "pop": 27339, "state": "MD", "_id": "20784"} -{"city": "LANDOVER", "loc": [-76.882243, 38.91992], "pop": 38732, "state": "MD", "_id": "20785"} -{"city": "JESSUP", "loc": [-76.792239, 39.148399], "pop": 11789, "state": "MD", "_id": "20794"} -{"city": "GLEN ECHO", "loc": [-77.143457, 38.969333], "pop": 234, "state": "MD", "_id": "20812"} -{"city": "BETHESDA", "loc": [-77.102165, 39.000343], "pop": 21704, "state": "MD", "_id": "20814"} -{"city": "CHEVY CHASE", "loc": [-77.081984, 38.977955], "pop": 25264, "state": "MD", "_id": "20815"} -{"city": "BETHESDA", "loc": [-77.11528, 38.958485], "pop": 14700, "state": "MD", "_id": "20816"} -{"city": "WEST BETHESDA", "loc": [-77.137239, 38.999659], "pop": 33484, "state": "MD", "_id": "20817"} -{"city": "CABIN JOHN", "loc": [-77.15911, 38.974302], "pop": 1120, "state": "MD", "_id": "20818"} -{"city": "OLNEY", "loc": [-77.074949, 39.152591], "pop": 20376, "state": "MD", "_id": "20832"} -{"city": "BROOKEVILLE", "loc": [-77.06026, 39.187082], "pop": 3612, "state": "MD", "_id": "20833"} -{"city": "POOLESVILLE", "loc": [-77.406717, 39.138597], "pop": 4692, "state": "MD", "_id": "20837"} -{"city": "BARNESVILLE", "loc": [-77.376426, 39.223321], "pop": 130, "state": "MD", "_id": "20838"} -{"city": "BEALLSVILLE", "loc": [-77.414403, 39.167095], "pop": 72, "state": "MD", "_id": "20839"} -{"city": "BOYDS", "loc": [-77.316731, 39.210042], "pop": 2402, "state": "MD", "_id": "20841"} -{"city": "DICKERSON", "loc": [-77.419853, 39.212615], "pop": 1726, "state": "MD", "_id": "20842"} -{"city": "ROCKVILLE", "loc": [-77.167973, 39.087037], "pop": 24215, "state": "MD", "_id": "20850"} -{"city": "ROCKVILLE", "loc": [-77.123449, 39.076265], "pop": 11494, "state": "MD", "_id": "20851"} -{"city": "ROCKVILLE", "loc": [-77.120416, 39.049628], "pop": 32913, "state": "MD", "_id": "20852"} -{"city": "ROCKVILLE", "loc": [-77.095037, 39.088738], "pop": 27339, "state": "MD", "_id": "20853"} -{"city": "POTOMAC", "loc": [-77.192151, 39.03877], "pop": 44211, "state": "MD", "_id": "20854"} -{"city": "DERWOOD", "loc": [-77.147707, 39.134539], "pop": 17235, "state": "MD", "_id": "20855"} -{"city": "SANDY SPRING", "loc": [-77.029071, 39.150319], "pop": 1890, "state": "MD", "_id": "20860"} -{"city": "ASHTON", "loc": [-76.99242, 39.151027], "pop": 1318, "state": "MD", "_id": "20861"} -{"city": "BRINKLOW", "loc": [-77.016338, 39.183777], "pop": 286, "state": "MD", "_id": "20862"} -{"city": "BURTONSVILLE", "loc": [-76.933851, 39.092151], "pop": 14497, "state": "MD", "_id": "20866"} -{"city": "SPENCERVILLE", "loc": [-76.959802, 39.121343], "pop": 278, "state": "MD", "_id": "20868"} -{"city": "CLARKSBURG", "loc": [-77.273579, 39.263719], "pop": 2674, "state": "MD", "_id": "20871"} -{"city": "DAMASCUS", "loc": [-77.213088, 39.27606], "pop": 10446, "state": "MD", "_id": "20872"} -{"city": "DARNESTOWN", "loc": [-77.269935, 39.17039], "pop": 33549, "state": "MD", "_id": "20874"} -{"city": "GERMANTOWN", "loc": [-77.235829, 39.188001], "pop": 11734, "state": "MD", "_id": "20876"} -{"city": "GAITHERSBURG", "loc": [-77.188993, 39.14187], "pop": 25136, "state": "MD", "_id": "20877"} -{"city": "DARNESTOWN", "loc": [-77.236434, 39.115534], "pop": 45367, "state": "MD", "_id": "20878"} -{"city": "LAYTONSVILLE", "loc": [-77.194599, 39.172597], "pop": 46879, "state": "MD", "_id": "20879"} -{"city": "LAYTONSVILLE", "loc": [-77.174718, 39.238345], "pop": 9795, "state": "MD", "_id": "20882"} -{"city": "KENSINGTON", "loc": [-77.079281, 39.029803], "pop": 21848, "state": "MD", "_id": "20895"} -{"city": "SILVER SPRING", "loc": [-77.007613, 39.019106], "pop": 33858, "state": "MD", "_id": "20901"} -{"city": "WHEATON", "loc": [-77.046348, 39.04158], "pop": 40299, "state": "MD", "_id": "20902"} -{"city": "SILVER SPRING", "loc": [-76.984648, 39.009513], "pop": 16724, "state": "MD", "_id": "20903"} -{"city": "COLESVILLE", "loc": [-76.976399, 39.06524], "pop": 39991, "state": "MD", "_id": "20904"} -{"city": "COLESVILLE", "loc": [-76.989928, 39.102438], "pop": 16800, "state": "MD", "_id": "20905"} -{"city": "ASPEN HILL", "loc": [-77.063233, 39.081041], "pop": 52694, "state": "MD", "_id": "20906"} -{"city": "SILVER SPRING", "loc": [-77.033776, 38.998198], "pop": 31954, "state": "MD", "_id": "20910"} -{"city": "TAKOMA PARK", "loc": [-77.000715, 38.983214], "pop": 24356, "state": "MD", "_id": "20912"} -{"city": "ABERDEEN", "loc": [-76.18054, 39.510886], "pop": 19229, "state": "MD", "_id": "21001"} -{"city": "ABERDEEN PROVING", "loc": [-76.130295, 39.472434], "pop": 5294, "state": "MD", "_id": "21005"} -{"city": "ABINGDON", "loc": [-76.299726, 39.47444], "pop": 12664, "state": "MD", "_id": "21009"} -{"city": "GUNPOWDER", "loc": [-76.274261, 39.398249], "pop": 1082, "state": "MD", "_id": "21010"} -{"city": "ARNOLD", "loc": [-76.49406, 39.047587], "pop": 19404, "state": "MD", "_id": "21012"} -{"city": "BALDWIN", "loc": [-76.492711, 39.519395], "pop": 4945, "state": "MD", "_id": "21013"} -{"city": "BEL AIR", "loc": [-76.356431, 39.539367], "pop": 18477, "state": "MD", "_id": "21014"} -{"city": "BEL AIR", "loc": [-76.315329, 39.530252], "pop": 23254, "state": "MD", "_id": "21015"} -{"city": "BELCAMP", "loc": [-76.242011, 39.475626], "pop": 2957, "state": "MD", "_id": "21017"} -{"city": "BRADSHAW", "loc": [-76.3894, 39.428113], "pop": 699, "state": "MD", "_id": "21021"} -{"city": "CHURCHVILLE", "loc": [-76.248962, 39.564845], "pop": 3038, "state": "MD", "_id": "21028"} -{"city": "CLARKSVILLE", "loc": [-76.942271, 39.194362], "pop": 3147, "state": "MD", "_id": "21029"} -{"city": "COCKEYSVILLE HUN", "loc": [-76.628655, 39.473554], "pop": 20331, "state": "MD", "_id": "21030"} -{"city": "COCKEYSVILLE HUN", "loc": [-76.659844, 39.502623], "pop": 159, "state": "MD", "_id": "21031"} -{"city": "CROWNSVILLE", "loc": [-76.593477, 39.048889], "pop": 7269, "state": "MD", "_id": "21032"} -{"city": "DARLINGTON", "loc": [-76.227795, 39.654032], "pop": 3298, "state": "MD", "_id": "21034"} -{"city": "DAVIDSONVILLE", "loc": [-76.637542, 38.937364], "pop": 6070, "state": "MD", "_id": "21035"} -{"city": "DAYTON", "loc": [-76.996755, 39.233882], "pop": 1707, "state": "MD", "_id": "21036"} -{"city": "EDGEWATER BEACH", "loc": [-76.540374, 38.922511], "pop": 14576, "state": "MD", "_id": "21037"} -{"city": "EDGEWOOD", "loc": [-76.30555, 39.427725], "pop": 22058, "state": "MD", "_id": "21040"} -{"city": "ELLICOTT CITY", "loc": [-76.861362, 39.272632], "pop": 29589, "state": "MD", "_id": "21042"} -{"city": "DANIELS", "loc": [-76.803929, 39.258216], "pop": 19217, "state": "MD", "_id": "21043"} -{"city": "COLUMBIA", "loc": [-76.878807, 39.214103], "pop": 32695, "state": "MD", "_id": "21044"} -{"city": "COLUMBIA", "loc": [-76.83223, 39.205052], "pop": 32658, "state": "MD", "_id": "21045"} -{"city": "COLUMBIA", "loc": [-76.853796, 39.170236], "pop": 13591, "state": "MD", "_id": "21046"} -{"city": "FALLSTON", "loc": [-76.43277, 39.527048], "pop": 10230, "state": "MD", "_id": "21047"} -{"city": "PATAPSCO", "loc": [-76.909862, 39.508645], "pop": 7680, "state": "MD", "_id": "21048"} -{"city": "FOREST HILL", "loc": [-76.400767, 39.57549], "pop": 9029, "state": "MD", "_id": "21050"} -{"city": "FORK", "loc": [-76.450361, 39.472937], "pop": 136, "state": "MD", "_id": "21051"} -{"city": "FREELAND", "loc": [-76.722349, 39.693957], "pop": 2482, "state": "MD", "_id": "21053"} -{"city": "GAMBRILLS", "loc": [-76.681877, 39.04069], "pop": 7441, "state": "MD", "_id": "21054"} -{"city": "GIBSON ISLAND", "loc": [-76.432418, 39.07511], "pop": 335, "state": "MD", "_id": "21056"} -{"city": "GLEN ARM", "loc": [-76.515331, 39.457484], "pop": 3679, "state": "MD", "_id": "21057"} -{"city": "GLEN BURNIE", "loc": [-76.618862, 39.158968], "pop": 75692, "state": "MD", "_id": "21061"} -{"city": "GLYNDON", "loc": [-76.787568, 39.492782], "pop": 1761, "state": "MD", "_id": "21071"} -{"city": "GREENMOUNT", "loc": [-76.849397, 39.61352], "pop": 10443, "state": "MD", "_id": "21074"} -{"city": "HANOVER", "loc": [-76.721535, 39.155069], "pop": 6265, "state": "MD", "_id": "21076"} -{"city": "HAVRE DE GRACE", "loc": [-76.117144, 39.552312], "pop": 13536, "state": "MD", "_id": "21078"} -{"city": "HYDES", "loc": [-76.469538, 39.474049], "pop": 883, "state": "MD", "_id": "21082"} -{"city": "JARRETTSVILLE", "loc": [-76.468427, 39.616241], "pop": 6667, "state": "MD", "_id": "21084"} -{"city": "JOPPA", "loc": [-76.354102, 39.424208], "pop": 15006, "state": "MD", "_id": "21085"} -{"city": "KINGSVILLE", "loc": [-76.41473, 39.455775], "pop": 4283, "state": "MD", "_id": "21087"} -{"city": "LINEBORO", "loc": [-76.839476, 39.718498], "pop": 94, "state": "MD", "_id": "21088"} -{"city": "LINTHICUM HEIGHT", "loc": [-76.657477, 39.208331], "pop": 9777, "state": "MD", "_id": "21090"} -{"city": "LUTHERVILLE", "loc": [-76.633791, 39.438636], "pop": 31560, "state": "MD", "_id": "21093"} -{"city": "MANCHESTER", "loc": [-76.894073, 39.674746], "pop": 5476, "state": "MD", "_id": "21102"} -{"city": "MARRIOTTSVILLE", "loc": [-76.913241, 39.334154], "pop": 1482, "state": "MD", "_id": "21104"} -{"city": "MILLERS", "loc": [-76.823884, 39.683224], "pop": 3218, "state": "MD", "_id": "21107"} -{"city": "MILLERSVILLE", "loc": [-76.61902, 39.104102], "pop": 16436, "state": "MD", "_id": "21108"} -{"city": "HEREFORD", "loc": [-76.592957, 39.576611], "pop": 4615, "state": "MD", "_id": "21111"} -{"city": "ODENTON", "loc": [-76.699583, 39.076197], "pop": 8201, "state": "MD", "_id": "21113"} -{"city": "CROFTON", "loc": [-76.680166, 39.011163], "pop": 15590, "state": "MD", "_id": "21114"} -{"city": "OWINGS MILLS", "loc": [-76.776934, 39.42688], "pop": 24913, "state": "MD", "_id": "21117"} -{"city": "BENTLEY SPRINGS", "loc": [-76.677591, 39.649937], "pop": 5320, "state": "MD", "_id": "21120"} -{"city": "RIVIERA BEACH", "loc": [-76.516248, 39.129284], "pop": 51386, "state": "MD", "_id": "21122"} -{"city": "PERRY HALL", "loc": [-76.450987, 39.401013], "pop": 6075, "state": "MD", "_id": "21128"} -{"city": "JACKSONVILLE", "loc": [-76.564623, 39.506978], "pop": 6703, "state": "MD", "_id": "21131"} -{"city": "PYLESVILLE", "loc": [-76.411256, 39.695905], "pop": 2782, "state": "MD", "_id": "21132"} -{"city": "RANDALLSTOWN", "loc": [-76.800245, 39.374571], "pop": 23994, "state": "MD", "_id": "21133"} -{"city": "REISTERSTOWN", "loc": [-76.813452, 39.45996], "pop": 24812, "state": "MD", "_id": "21136"} -{"city": "RIVA", "loc": [-76.585437, 38.950391], "pop": 3165, "state": "MD", "_id": "21140"} -{"city": "SEVERN", "loc": [-76.697953, 39.127543], "pop": 25272, "state": "MD", "_id": "21144"} -{"city": "SEVERNA PARK", "loc": [-76.557746, 39.081078], "pop": 23392, "state": "MD", "_id": "21146"} -{"city": "GLENCOE", "loc": [-76.669109, 39.543086], "pop": 4450, "state": "MD", "_id": "21152"} -{"city": "ROCKS", "loc": [-76.330174, 39.649928], "pop": 5018, "state": "MD", "_id": "21154"} -{"city": "FOWBELSBURG", "loc": [-76.81623, 39.56394], "pop": 2487, "state": "MD", "_id": "21155"} -{"city": "UPPER FALLS", "loc": [-76.400604, 39.43967], "pop": 464, "state": "MD", "_id": "21156"} -{"city": "CARROLLTON", "loc": [-76.99404, 39.555654], "pop": 37148, "state": "MD", "_id": "21157"} -{"city": "UNIONTOWN", "loc": [-77.036946, 39.655464], "pop": 8491, "state": "MD", "_id": "21158"} -{"city": "WHITEFORD", "loc": [-76.316015, 39.707709], "pop": 1766, "state": "MD", "_id": "21160"} -{"city": "WHITE HALL", "loc": [-76.566639, 39.66176], "pop": 4970, "state": "MD", "_id": "21161"} -{"city": "WHITE MARSH", "loc": [-76.413189, 39.392315], "pop": 2521, "state": "MD", "_id": "21162"} -{"city": "GRANITE", "loc": [-76.85757, 39.339151], "pop": 2115, "state": "MD", "_id": "21163"} -{"city": "BALTIMORE", "loc": [-76.625203, 39.29463], "pop": 16256, "state": "MD", "_id": "21201"} -{"city": "BALTIMORE", "loc": [-76.607499, 39.299844], "pop": 28656, "state": "MD", "_id": "21202"} -{"city": "EUDOWOOD", "loc": [-76.603224, 39.402517], "pop": 37257, "state": "MD", "_id": "21204"} -{"city": "BALTIMORE", "loc": [-76.579915, 39.300871], "pop": 23763, "state": "MD", "_id": "21205"} -{"city": "BALTIMORE", "loc": [-76.541135, 39.336494], "pop": 52114, "state": "MD", "_id": "21206"} -{"city": "GWYNN OAK", "loc": [-76.734064, 39.329628], "pop": 76002, "state": "MD", "_id": "21207"} -{"city": "PIKESVILLE", "loc": [-76.729013, 39.376359], "pop": 27723, "state": "MD", "_id": "21208"} -{"city": "BALTIMORE", "loc": [-76.674431, 39.371622], "pop": 20673, "state": "MD", "_id": "21209"} -{"city": "BALTIMORE", "loc": [-76.632099, 39.350727], "pop": 12581, "state": "MD", "_id": "21210"} -{"city": "BALTIMORE", "loc": [-76.633625, 39.331642], "pop": 19797, "state": "MD", "_id": "21211"} -{"city": "BALTIMORE", "loc": [-76.609989, 39.362571], "pop": 35680, "state": "MD", "_id": "21212"} -{"city": "BALTIMORE", "loc": [-76.581012, 39.312667], "pop": 47171, "state": "MD", "_id": "21213"} -{"city": "BALTIMORE", "loc": [-76.564375, 39.35206], "pop": 20949, "state": "MD", "_id": "21214"} -{"city": "BALTIMORE", "loc": [-76.679397, 39.344572], "pop": 74402, "state": "MD", "_id": "21215"} -{"city": "BALTIMORE", "loc": [-76.669891, 39.309349], "pop": 42501, "state": "MD", "_id": "21216"} -{"city": "BALTIMORE", "loc": [-76.639267, 39.306416], "pop": 52502, "state": "MD", "_id": "21217"} -{"city": "BALTIMORE", "loc": [-76.6048, 39.3265], "pop": 56677, "state": "MD", "_id": "21218"} -{"city": "DUNDALK SPARROWS", "loc": [-76.446937, 39.229068], "pop": 9467, "state": "MD", "_id": "21219"} -{"city": "MIDDLE RIVER", "loc": [-76.415255, 39.340098], "pop": 37336, "state": "MD", "_id": "21220"} -{"city": "ESSEX", "loc": [-76.453259, 39.308553], "pop": 44117, "state": "MD", "_id": "21221"} -{"city": "DUNDALK SPARROWS", "loc": [-76.502484, 39.26486], "pop": 58181, "state": "MD", "_id": "21222"} -{"city": "BALTIMORE", "loc": [-76.647586, 39.287], "pop": 39003, "state": "MD", "_id": "21223"} -{"city": "BALTIMORE", "loc": [-76.556831, 39.287558], "pop": 53629, "state": "MD", "_id": "21224"} -{"city": "BROOKLYN CURTIS", "loc": [-76.615026, 39.233343], "pop": 35032, "state": "MD", "_id": "21225"} -{"city": "BROOKLYN CURTIS", "loc": [-76.569147, 39.209315], "pop": 5478, "state": "MD", "_id": "21226"} -{"city": "HALETHORPE", "loc": [-76.696872, 39.230893], "pop": 45430, "state": "MD", "_id": "21227"} -{"city": "CATONSVILLE", "loc": [-76.740124, 39.27825], "pop": 43630, "state": "MD", "_id": "21228"} -{"city": "BALTIMORE", "loc": [-76.689885, 39.285645], "pop": 53962, "state": "MD", "_id": "21229"} -{"city": "BALTIMORE", "loc": [-76.626193, 39.269943], "pop": 34901, "state": "MD", "_id": "21230"} -{"city": "BALTIMORE", "loc": [-76.589956, 39.289193], "pop": 15928, "state": "MD", "_id": "21231"} -{"city": "PARKVILLE", "loc": [-76.541767, 39.387581], "pop": 66477, "state": "MD", "_id": "21234"} -{"city": "NOTTINGHAM", "loc": [-76.487106, 39.391412], "pop": 33099, "state": "MD", "_id": "21236"} -{"city": "ROSEDALE", "loc": [-76.501387, 39.336054], "pop": 24835, "state": "MD", "_id": "21237"} -{"city": "BALTIMORE", "loc": [-76.589082, 39.360977], "pop": 31935, "state": "MD", "_id": "21239"} -{"city": "BALTIMORE", "loc": [-76.648287, 39.17185], "pop": 1, "state": "MD", "_id": "21240"} -{"city": "CAPE SAINT CLAIR", "loc": [-76.503139, 38.999645], "pop": 44757, "state": "MD", "_id": "21401"} -{"city": "NAVAL ACADEMY", "loc": [-76.48079, 38.982436], "pop": 5780, "state": "MD", "_id": "21402"} -{"city": "ANNAPOLIS", "loc": [-76.49103, 38.952394], "pop": 26587, "state": "MD", "_id": "21403"} -{"city": "CRESAPTOWN", "loc": [-78.777442, 39.644796], "pop": 44371, "state": "MD", "_id": "21502"} -{"city": "ACCIDENT", "loc": [-79.308467, 39.635504], "pop": 1968, "state": "MD", "_id": "21520"} -{"city": "BARTON", "loc": [-79.02813, 39.533135], "pop": 1305, "state": "MD", "_id": "21521"} -{"city": "BITTINGER", "loc": [-79.214247, 39.597786], "pop": 479, "state": "MD", "_id": "21522"} -{"city": "BLOOMINGTON", "loc": [-79.113707, 39.487764], "pop": 898, "state": "MD", "_id": "21523"} -{"city": "FLINTSTONE", "loc": [-78.573949, 39.699288], "pop": 1102, "state": "MD", "_id": "21530"} -{"city": "FRIENDSVILLE", "loc": [-79.421912, 39.666502], "pop": 2021, "state": "MD", "_id": "21531"} -{"city": "FROSTBURG", "loc": [-78.930559, 39.649359], "pop": 15702, "state": "MD", "_id": "21532"} -{"city": "JENNINGS", "loc": [-79.151665, 39.684436], "pop": 3138, "state": "MD", "_id": "21536"} -{"city": "SHALLMAR", "loc": [-79.202528, 39.396275], "pop": 759, "state": "MD", "_id": "21538"} -{"city": "LONACONING", "loc": [-78.991477, 39.575703], "pop": 3494, "state": "MD", "_id": "21539"} -{"city": "LUKE", "loc": [-79.059365, 39.477392], "pop": 184, "state": "MD", "_id": "21540"} -{"city": "SANG RUN", "loc": [-79.364134, 39.559709], "pop": 1673, "state": "MD", "_id": "21541"} -{"city": "MOUNT SAVAGE", "loc": [-78.873892, 39.699128], "pop": 2067, "state": "MD", "_id": "21545"} -{"city": "DEER PARK", "loc": [-79.38966, 39.399993], "pop": 13531, "state": "MD", "_id": "21550"} -{"city": "OLDTOWN", "loc": [-78.604373, 39.584598], "pop": 2079, "state": "MD", "_id": "21555"} -{"city": "RAWLINGS", "loc": [-78.906231, 39.521363], "pop": 1919, "state": "MD", "_id": "21557"} -{"city": "SWANTON", "loc": [-79.240165, 39.476361], "pop": 1719, "state": "MD", "_id": "21561"} -{"city": "MCCOOLE", "loc": [-79.030726, 39.482625], "pop": 3976, "state": "MD", "_id": "21562"} -{"city": "EASTON", "loc": [-76.075775, 38.77682], "pop": 16439, "state": "MD", "_id": "21601"} -{"city": "BARCLAY", "loc": [-75.860079, 39.129871], "pop": 592, "state": "MD", "_id": "21607"} -{"city": "BETTERTON", "loc": [-76.063938, 39.365509], "pop": 485, "state": "MD", "_id": "21610"} -{"city": "BOZMAN", "loc": [-76.27641, 38.751493], "pop": 847, "state": "MD", "_id": "21612"} -{"city": "CAMBRIDGE", "loc": [-76.087358, 38.564282], "pop": 16908, "state": "MD", "_id": "21613"} -{"city": "CENTREVILLE", "loc": [-76.044975, 39.056423], "pop": 5834, "state": "MD", "_id": "21617"} -{"city": "CHESTER", "loc": [-76.284183, 38.958296], "pop": 4199, "state": "MD", "_id": "21619"} -{"city": "CHESTERTOWN", "loc": [-76.080214, 39.212517], "pop": 10618, "state": "MD", "_id": "21620"} -{"city": "CHURCH CREEK", "loc": [-76.169569, 38.427815], "pop": 593, "state": "MD", "_id": "21622"} -{"city": "CHURCH HILL", "loc": [-75.988028, 39.145958], "pop": 813, "state": "MD", "_id": "21623"} -{"city": "CORDOVA", "loc": [-76.002879, 38.870426], "pop": 2346, "state": "MD", "_id": "21625"} -{"city": "CRAPO", "loc": [-76.114207, 38.329541], "pop": 173, "state": "MD", "_id": "21626"} -{"city": "CRUMPTON", "loc": [-75.919522, 39.233019], "pop": 689, "state": "MD", "_id": "21628"} -{"city": "DENTON", "loc": [-75.836071, 38.877928], "pop": 7126, "state": "MD", "_id": "21629"} -{"city": "EAST NEW MARKET", "loc": [-75.956766, 38.592095], "pop": 2156, "state": "MD", "_id": "21631"} -{"city": "FEDERALSBURG", "loc": [-75.775376, 38.714681], "pop": 5535, "state": "MD", "_id": "21632"} -{"city": "FISHING CREEK", "loc": [-76.216783, 38.316292], "pop": 580, "state": "MD", "_id": "21634"} -{"city": "GALENA", "loc": [-75.871673, 39.337417], "pop": 1087, "state": "MD", "_id": "21635"} -{"city": "GOLDSBORO", "loc": [-75.792604, 39.022957], "pop": 1179, "state": "MD", "_id": "21636"} -{"city": "GOLTS", "loc": [-75.805521, 39.355598], "pop": 533, "state": "MD", "_id": "21637"} -{"city": "GRASONVILLE", "loc": [-76.199701, 38.945602], "pop": 3225, "state": "MD", "_id": "21638"} -{"city": "GREENSBORO", "loc": [-75.805911, 38.961632], "pop": 3516, "state": "MD", "_id": "21639"} -{"city": "HENDERSON", "loc": [-75.794787, 39.067197], "pop": 1391, "state": "MD", "_id": "21640"} -{"city": "WILLIAMSBURG", "loc": [-75.866254, 38.642826], "pop": 4625, "state": "MD", "_id": "21643"} -{"city": "INGLESIDE", "loc": [-75.891748, 39.124654], "pop": 159, "state": "MD", "_id": "21644"} -{"city": "KENNEDYVILLE", "loc": [-75.981797, 39.297835], "pop": 1807, "state": "MD", "_id": "21645"} -{"city": "MCDANIEL", "loc": [-76.280593, 38.819213], "pop": 610, "state": "MD", "_id": "21647"} -{"city": "MARYDEL", "loc": [-75.762247, 39.108169], "pop": 1195, "state": "MD", "_id": "21649"} -{"city": "MASSEY", "loc": [-75.821483, 39.312569], "pop": 112, "state": "MD", "_id": "21650"} -{"city": "MILLINGTON", "loc": [-75.849966, 39.257989], "pop": 2003, "state": "MD", "_id": "21651"} -{"city": "OXFORD", "loc": [-76.153783, 38.686358], "pop": 1350, "state": "MD", "_id": "21654"} -{"city": "PRESTON", "loc": [-75.916284, 38.746456], "pop": 4425, "state": "MD", "_id": "21655"} -{"city": "QUEEN ANNE", "loc": [-75.977655, 38.945583], "pop": 1092, "state": "MD", "_id": "21657"} -{"city": "QUEENSTOWN", "loc": [-76.152646, 38.956235], "pop": 3330, "state": "MD", "_id": "21658"} -{"city": "RHODESDALE", "loc": [-75.774945, 38.602985], "pop": 2276, "state": "MD", "_id": "21659"} -{"city": "RIDGELY", "loc": [-75.884825, 38.956787], "pop": 2754, "state": "MD", "_id": "21660"} -{"city": "ROCK HALL", "loc": [-76.230467, 39.134371], "pop": 2770, "state": "MD", "_id": "21661"} -{"city": "ROYAL OAK", "loc": [-76.187788, 38.72883], "pop": 889, "state": "MD", "_id": "21662"} -{"city": "SAINT MICHAELS", "loc": [-76.221547, 38.782965], "pop": 3247, "state": "MD", "_id": "21663"} -{"city": "SHERWOOD", "loc": [-76.327822, 38.73741], "pop": 262, "state": "MD", "_id": "21665"} -{"city": "STEVENSVILLE", "loc": [-76.337087, 38.939399], "pop": 8630, "state": "MD", "_id": "21666"} -{"city": "STILL POND", "loc": [-76.052156, 39.327624], "pop": 289, "state": "MD", "_id": "21667"} -{"city": "SUDLERSVILLE", "loc": [-75.849968, 39.182339], "pop": 2014, "state": "MD", "_id": "21668"} -{"city": "TAYLORS ISLAND", "loc": [-76.296406, 38.463095], "pop": 246, "state": "MD", "_id": "21669"} -{"city": "TILGHMAN", "loc": [-76.337653, 38.706309], "pop": 745, "state": "MD", "_id": "21671"} -{"city": "TODDVILLE", "loc": [-76.059634, 38.272592], "pop": 361, "state": "MD", "_id": "21672"} -{"city": "TRAPPE", "loc": [-76.05067, 38.66466], "pop": 2958, "state": "MD", "_id": "21673"} -{"city": "WINGATE", "loc": [-76.086303, 38.289898], "pop": 160, "state": "MD", "_id": "21675"} -{"city": "WITTMAN", "loc": [-76.29931, 38.789187], "pop": 338, "state": "MD", "_id": "21676"} -{"city": "WOOLFORD", "loc": [-76.204057, 38.5066], "pop": 459, "state": "MD", "_id": "21677"} -{"city": "WORTON", "loc": [-76.10078, 39.296346], "pop": 1680, "state": "MD", "_id": "21678"} -{"city": "WYE MILLS", "loc": [-76.081366, 38.928114], "pop": 270, "state": "MD", "_id": "21679"} -{"city": "LEWISTOWN", "loc": [-77.400875, 39.408235], "pop": 35492, "state": "MD", "_id": "21701"} -{"city": "FORT DETRICK", "loc": [-77.447369, 39.436532], "pop": 33704, "state": "MD", "_id": "21702"} -{"city": "DOUBS", "loc": [-77.447693, 39.306452], "pop": 2085, "state": "MD", "_id": "21710"} -{"city": "BIG POOL", "loc": [-78.010449, 39.645685], "pop": 1083, "state": "MD", "_id": "21711"} -{"city": "FAHRNEY KEEDY ME", "loc": [-77.683522, 39.532244], "pop": 10501, "state": "MD", "_id": "21713"} -{"city": "BRUNSWICK", "loc": [-77.623002, 39.316356], "pop": 5160, "state": "MD", "_id": "21716"} -{"city": "BURKITTSVILLE", "loc": [-77.626508, 39.397066], "pop": 285, "state": "MD", "_id": "21718"} -{"city": "FORT RITCHIE", "loc": [-77.495609, 39.707341], "pop": 2672, "state": "MD", "_id": "21719"} -{"city": "BIG SPRING", "loc": [-77.912592, 39.661693], "pop": 3661, "state": "MD", "_id": "21722"} -{"city": "COOKSVILLE", "loc": [-77.005076, 39.321109], "pop": 353, "state": "MD", "_id": "21723"} -{"city": "DETOUR", "loc": [-77.248272, 39.614661], "pop": 970, "state": "MD", "_id": "21725"} -{"city": "EMMITSBURG", "loc": [-77.335658, 39.694044], "pop": 5095, "state": "MD", "_id": "21727"} -{"city": "FAIR PLAY", "loc": [-77.761468, 39.542279], "pop": 153, "state": "MD", "_id": "21733"} -{"city": "GLENELG", "loc": [-77.005703, 39.262064], "pop": 681, "state": "MD", "_id": "21737"} -{"city": "GLENWOOD", "loc": [-77.014821, 39.279519], "pop": 1632, "state": "MD", "_id": "21738"} -{"city": "HAGERSTOWN", "loc": [-77.737215, 39.632022], "pop": 47679, "state": "MD", "_id": "21740"} -{"city": "HAGERSTOWN", "loc": [-77.692102, 39.657291], "pop": 28398, "state": "MD", "_id": "21742"} -{"city": "HANCOCK", "loc": [-78.176246, 39.699083], "pop": 4275, "state": "MD", "_id": "21750"} -{"city": "IJAMSVILLE", "loc": [-77.296363, 39.326737], "pop": 4078, "state": "MD", "_id": "21754"} -{"city": "JEFFERSON", "loc": [-77.544061, 39.365291], "pop": 4132, "state": "MD", "_id": "21755"} -{"city": "KEEDYSVILLE", "loc": [-77.694427, 39.456323], "pop": 2546, "state": "MD", "_id": "21756"} -{"city": "KEYMAR", "loc": [-77.281661, 39.565619], "pop": 1691, "state": "MD", "_id": "21757"} -{"city": "KNOXVILLE", "loc": [-77.651286, 39.347891], "pop": 3534, "state": "MD", "_id": "21758"} -{"city": "LINWOOD", "loc": [-77.118443, 39.577654], "pop": 591, "state": "MD", "_id": "21764"} -{"city": "LITTLE ORLEANS", "loc": [-78.378139, 39.687593], "pop": 699, "state": "MD", "_id": "21766"} -{"city": "MAUGANSVILLE", "loc": [-77.749915, 39.6996], "pop": 598, "state": "MD", "_id": "21767"} -{"city": "MIDDLETOWN", "loc": [-77.550241, 39.441586], "pop": 8732, "state": "MD", "_id": "21769"} -{"city": "MONROVIA", "loc": [-77.249442, 39.351248], "pop": 5033, "state": "MD", "_id": "21770"} -{"city": "MOUNT AIRY", "loc": [-77.172347, 39.388131], "pop": 21912, "state": "MD", "_id": "21771"} -{"city": "MYERSVILLE", "loc": [-77.551322, 39.528194], "pop": 3363, "state": "MD", "_id": "21773"} -{"city": "NEW WINDSOR", "loc": [-77.103397, 39.516233], "pop": 4576, "state": "MD", "_id": "21776"} -{"city": "POINT OF ROCKS", "loc": [-77.532762, 39.279089], "pop": 812, "state": "MD", "_id": "21777"} -{"city": "ROCKY RIDGE", "loc": [-77.329635, 39.605696], "pop": 988, "state": "MD", "_id": "21778"} -{"city": "ROHRERSVILLE", "loc": [-77.657977, 39.443052], "pop": 200, "state": "MD", "_id": "21779"} -{"city": "SABILLASVILLE", "loc": [-77.469323, 39.682772], "pop": 1605, "state": "MD", "_id": "21780"} -{"city": "SHARPSBURG", "loc": [-77.751072, 39.442394], "pop": 3624, "state": "MD", "_id": "21782"} -{"city": "SMITHSBURG", "loc": [-77.570563, 39.647036], "pop": 6857, "state": "MD", "_id": "21783"} -{"city": "CARROLLTOWNE", "loc": [-76.961439, 39.39887], "pop": 28493, "state": "MD", "_id": "21784"} -{"city": "TANEYTOWN", "loc": [-77.169058, 39.665798], "pop": 7612, "state": "MD", "_id": "21787"} -{"city": "GRACEHAM", "loc": [-77.405114, 39.606576], "pop": 8929, "state": "MD", "_id": "21788"} -{"city": "TUSCARORA", "loc": [-77.51011, 39.266689], "pop": 134, "state": "MD", "_id": "21790"} -{"city": "UNIONVILLE", "loc": [-77.194346, 39.541484], "pop": 3942, "state": "MD", "_id": "21791"} -{"city": "WALKERSVILLE", "loc": [-77.34839, 39.47875], "pop": 8957, "state": "MD", "_id": "21793"} -{"city": "WEST FRIENDSHIP", "loc": [-76.966031, 39.293447], "pop": 937, "state": "MD", "_id": "21794"} -{"city": "WILLIAMSPORT", "loc": [-77.808705, 39.593023], "pop": 8739, "state": "MD", "_id": "21795"} -{"city": "WOODBINE", "loc": [-77.064698, 39.346438], "pop": 6224, "state": "MD", "_id": "21797"} -{"city": "WOODSBORO", "loc": [-77.297187, 39.531121], "pop": 1771, "state": "MD", "_id": "21798"} -{"city": "SALISBURY", "loc": [-75.592238, 38.362996], "pop": 52905, "state": "MD", "_id": "21801"} -{"city": "BERLIN", "loc": [-75.186557, 38.347514], "pop": 11613, "state": "MD", "_id": "21811"} -{"city": "BISHOPVILLE", "loc": [-75.185544, 38.429609], "pop": 1902, "state": "MD", "_id": "21813"} -{"city": "BIVALVE", "loc": [-75.89141, 38.295337], "pop": 307, "state": "MD", "_id": "21814"} -{"city": "CHANCE", "loc": [-75.939192, 38.178504], "pop": 415, "state": "MD", "_id": "21816"} -{"city": "CRISFIELD", "loc": [-75.842882, 37.984496], "pop": 5342, "state": "MD", "_id": "21817"} -{"city": "DAMES QUARTER", "loc": [-75.900082, 38.190568], "pop": 192, "state": "MD", "_id": "21820"} -{"city": "DEAL ISLAND", "loc": [-75.949559, 38.153326], "pop": 259, "state": "MD", "_id": "21821"} -{"city": "EDEN", "loc": [-75.648167, 38.276155], "pop": 1042, "state": "MD", "_id": "21822"} -{"city": "EWELL", "loc": [-76.03506, 37.99379], "pop": 252, "state": "MD", "_id": "21824"} -{"city": "FRUITLAND", "loc": [-75.622782, 38.322523], "pop": 3771, "state": "MD", "_id": "21826"} -{"city": "GIRDLETREE", "loc": [-75.390232, 38.095815], "pop": 495, "state": "MD", "_id": "21829"} -{"city": "HEBRON", "loc": [-75.696267, 38.402597], "pop": 2415, "state": "MD", "_id": "21830"} -{"city": "LINKWOOD", "loc": [-75.963015, 38.540317], "pop": 576, "state": "MD", "_id": "21835"} -{"city": "MARDELA SPRINGS", "loc": [-75.741442, 38.486386], "pop": 3039, "state": "MD", "_id": "21837"} -{"city": "MARION STATION", "loc": [-75.757903, 38.026637], "pop": 2323, "state": "MD", "_id": "21838"} -{"city": "NANTICOKE", "loc": [-75.902127, 38.267211], "pop": 358, "state": "MD", "_id": "21840"} -{"city": "NEWARK", "loc": [-75.289316, 38.248875], "pop": 765, "state": "MD", "_id": "21841"} -{"city": "OCEAN CITY", "loc": [-75.081011, 38.379248], "pop": 6914, "state": "MD", "_id": "21842"} -{"city": "PARSONSBURG", "loc": [-75.473658, 38.391392], "pop": 2407, "state": "MD", "_id": "21849"} -{"city": "PITTSVILLE", "loc": [-75.407589, 38.375465], "pop": 1997, "state": "MD", "_id": "21850"} -{"city": "POCOMOKE CITY", "loc": [-75.555038, 38.071369], "pop": 7527, "state": "MD", "_id": "21851"} -{"city": "PRINCESS ANNE", "loc": [-75.707237, 38.191929], "pop": 10343, "state": "MD", "_id": "21853"} -{"city": "QUANTICO", "loc": [-75.785102, 38.333888], "pop": 1029, "state": "MD", "_id": "21856"} -{"city": "21858", "loc": [-76.039838, 37.977403], "pop": 89, "state": "MD", "_id": "21858"} -{"city": "SNOW HILL", "loc": [-75.404987, 38.186794], "pop": 5195, "state": "MD", "_id": "21863"} -{"city": "STOCKTON", "loc": [-75.410762, 38.045155], "pop": 691, "state": "MD", "_id": "21864"} -{"city": "TYASKIN", "loc": [-75.869032, 38.30706], "pop": 504, "state": "MD", "_id": "21865"} -{"city": "TYLERTON", "loc": [-76.023036, 37.967436], "pop": 112, "state": "MD", "_id": "21866"} -{"city": "VIENNA", "loc": [-75.872926, 38.477376], "pop": 1119, "state": "MD", "_id": "21869"} -{"city": "WENONA", "loc": [-75.944021, 38.133222], "pop": 358, "state": "MD", "_id": "21870"} -{"city": "WESTOVER", "loc": [-75.740563, 38.10099], "pop": 1881, "state": "MD", "_id": "21871"} -{"city": "WHALEYSVILLE", "loc": [-75.288868, 38.404169], "pop": 758, "state": "MD", "_id": "21872"} -{"city": "WILLARDS", "loc": [-75.355161, 38.393853], "pop": 1895, "state": "MD", "_id": "21874"} -{"city": "DELMAR", "loc": [-75.55828, 38.44451], "pop": 3712, "state": "MD", "_id": "21875"} -{"city": "NORTH EAST", "loc": [-75.953805, 39.604535], "pop": 10560, "state": "MD", "_id": "21901"} -{"city": "PERRYVILLE", "loc": [-76.059227, 39.564894], "pop": 4678, "state": "MD", "_id": "21903"} -{"city": "BAINBRIDGE", "loc": [-76.083706, 39.622264], "pop": 5507, "state": "MD", "_id": "21904"} -{"city": "RISING SUN", "loc": [-76.049164, 39.688176], "pop": 7798, "state": "MD", "_id": "21911"} -{"city": "WARWICK", "loc": [-75.799592, 39.428261], "pop": 745, "state": "MD", "_id": "21912"} -{"city": "CECILTON", "loc": [-75.865395, 39.40151], "pop": 611, "state": "MD", "_id": "21913"} -{"city": "CHARLESTOWN", "loc": [-75.979537, 39.57287], "pop": 638, "state": "MD", "_id": "21914"} -{"city": "CHESAPEAKE CITY", "loc": [-75.840581, 39.513308], "pop": 2823, "state": "MD", "_id": "21915"} -{"city": "COLORA", "loc": [-76.093364, 39.669515], "pop": 1868, "state": "MD", "_id": "21917"} -{"city": "CONOWINGO", "loc": [-76.157198, 39.67775], "pop": 3025, "state": "MD", "_id": "21918"} -{"city": "EARLEVILLE", "loc": [-75.94031, 39.427105], "pop": 2196, "state": "MD", "_id": "21919"} -{"city": "ELKTON", "loc": [-75.84584, 39.626434], "pop": 30901, "state": "MD", "_id": "21921"} -{"city": "AGAWAM", "loc": [-72.622739, 42.070206], "pop": 15338, "state": "MA", "_id": "01001"} -{"city": "CUSHMAN", "loc": [-72.51565, 42.377017], "pop": 36963, "state": "MA", "_id": "01002"} -{"city": "BARRE", "loc": [-72.108354, 42.409698], "pop": 4546, "state": "MA", "_id": "01005"} -{"city": "BELCHERTOWN", "loc": [-72.410953, 42.275103], "pop": 10579, "state": "MA", "_id": "01007"} -{"city": "BLANDFORD", "loc": [-72.936114, 42.182949], "pop": 1240, "state": "MA", "_id": "01008"} -{"city": "BRIMFIELD", "loc": [-72.188455, 42.116543], "pop": 3706, "state": "MA", "_id": "01010"} -{"city": "CHESTER", "loc": [-72.988761, 42.279421], "pop": 1688, "state": "MA", "_id": "01011"} -{"city": "CHESTERFIELD", "loc": [-72.833309, 42.38167], "pop": 177, "state": "MA", "_id": "01012"} -{"city": "CHICOPEE", "loc": [-72.607962, 42.162046], "pop": 23396, "state": "MA", "_id": "01013"} -{"city": "CHICOPEE", "loc": [-72.576142, 42.176443], "pop": 31495, "state": "MA", "_id": "01020"} -{"city": "WESTOVER AFB", "loc": [-72.558657, 42.196672], "pop": 1764, "state": "MA", "_id": "01022"} -{"city": "CUMMINGTON", "loc": [-72.905767, 42.435296], "pop": 1484, "state": "MA", "_id": "01026"} -{"city": "MOUNT TOM", "loc": [-72.679921, 42.264319], "pop": 16864, "state": "MA", "_id": "01027"} -{"city": "EAST LONGMEADOW", "loc": [-72.505565, 42.067203], "pop": 13367, "state": "MA", "_id": "01028"} -{"city": "FEEDING HILLS", "loc": [-72.675077, 42.07182], "pop": 11985, "state": "MA", "_id": "01030"} -{"city": "GILBERTVILLE", "loc": [-72.198585, 42.332194], "pop": 2385, "state": "MA", "_id": "01031"} -{"city": "GOSHEN", "loc": [-72.844092, 42.466234], "pop": 122, "state": "MA", "_id": "01032"} -{"city": "GRANBY", "loc": [-72.520001, 42.255704], "pop": 5526, "state": "MA", "_id": "01033"} -{"city": "TOLLAND", "loc": [-72.908793, 42.070234], "pop": 1652, "state": "MA", "_id": "01034"} -{"city": "HADLEY", "loc": [-72.571499, 42.36062], "pop": 4231, "state": "MA", "_id": "01035"} -{"city": "HAMPDEN", "loc": [-72.431823, 42.064756], "pop": 4709, "state": "MA", "_id": "01036"} -{"city": "HATFIELD", "loc": [-72.616735, 42.38439], "pop": 3184, "state": "MA", "_id": "01038"} -{"city": "HAYDENVILLE", "loc": [-72.703178, 42.381799], "pop": 1387, "state": "MA", "_id": "01039"} -{"city": "HOLYOKE", "loc": [-72.626193, 42.202007], "pop": 43704, "state": "MA", "_id": "01040"} -{"city": "HUNTINGTON", "loc": [-72.873341, 42.265301], "pop": 2084, "state": "MA", "_id": "01050"} -{"city": "LEEDS", "loc": [-72.703403, 42.354292], "pop": 1350, "state": "MA", "_id": "01053"} -{"city": "LEVERETT", "loc": [-72.499334, 42.46823], "pop": 1748, "state": "MA", "_id": "01054"} -{"city": "LUDLOW", "loc": [-72.471012, 42.172823], "pop": 18820, "state": "MA", "_id": "01056"} -{"city": "MONSON", "loc": [-72.319634, 42.101017], "pop": 8194, "state": "MA", "_id": "01057"} -{"city": "FLORENCE", "loc": [-72.654245, 42.324662], "pop": 27939, "state": "MA", "_id": "01060"} -{"city": "OAKHAM", "loc": [-72.051265, 42.348033], "pop": 1503, "state": "MA", "_id": "01068"} -{"city": "PALMER", "loc": [-72.328785, 42.176233], "pop": 9778, "state": "MA", "_id": "01069"} -{"city": "PLAINFIELD", "loc": [-72.918289, 42.514393], "pop": 571, "state": "MA", "_id": "01070"} -{"city": "RUSSELL", "loc": [-72.840343, 42.147063], "pop": 608, "state": "MA", "_id": "01071"} -{"city": "SHUTESBURY", "loc": [-72.421342, 42.481968], "pop": 1533, "state": "MA", "_id": "01072"} -{"city": "SOUTHAMPTON", "loc": [-72.719381, 42.224697], "pop": 4478, "state": "MA", "_id": "01073"} -{"city": "SOUTH HADLEY", "loc": [-72.581137, 42.237537], "pop": 16699, "state": "MA", "_id": "01075"} -{"city": "SOUTHWICK", "loc": [-72.770588, 42.051099], "pop": 7667, "state": "MA", "_id": "01077"} -{"city": "THREE RIVERS", "loc": [-72.362352, 42.181894], "pop": 2425, "state": "MA", "_id": "01080"} -{"city": "WALES", "loc": [-72.204592, 42.062734], "pop": 1732, "state": "MA", "_id": "01081"} -{"city": "WARE", "loc": [-72.258285, 42.261831], "pop": 9808, "state": "MA", "_id": "01082"} -{"city": "MONTGOMERY", "loc": [-72.754318, 42.129484], "pop": 40117, "state": "MA", "_id": "01085"} -{"city": "WEST SPRINGFIELD", "loc": [-72.641109, 42.115066], "pop": 27537, "state": "MA", "_id": "01089"} -{"city": "WEST WARREN", "loc": [-72.203639, 42.20734], "pop": 4441, "state": "MA", "_id": "01092"} -{"city": "WILBRAHAM", "loc": [-72.446415, 42.124506], "pop": 12635, "state": "MA", "_id": "01095"} -{"city": "WILLIAMSBURG", "loc": [-72.777989, 42.408522], "pop": 2295, "state": "MA", "_id": "01096"} -{"city": "WORTHINGTON", "loc": [-72.931427, 42.384293], "pop": 877, "state": "MA", "_id": "01098"} -{"city": "SPRINGFIELD", "loc": [-72.588735, 42.1029], "pop": 2323, "state": "MA", "_id": "01103"} -{"city": "SPRINGFIELD", "loc": [-72.577769, 42.128848], "pop": 22115, "state": "MA", "_id": "01104"} -{"city": "SPRINGFIELD", "loc": [-72.578312, 42.099931], "pop": 14970, "state": "MA", "_id": "01105"} -{"city": "LONGMEADOW", "loc": [-72.5676, 42.050658], "pop": 15688, "state": "MA", "_id": "01106"} -{"city": "SPRINGFIELD", "loc": [-72.606544, 42.117907], "pop": 12739, "state": "MA", "_id": "01107"} -{"city": "SPRINGFIELD", "loc": [-72.558432, 42.085314], "pop": 25519, "state": "MA", "_id": "01108"} -{"city": "SPRINGFIELD", "loc": [-72.554349, 42.114455], "pop": 32635, "state": "MA", "_id": "01109"} -{"city": "SPRINGFIELD", "loc": [-72.527445, 42.092937], "pop": 14618, "state": "MA", "_id": "01118"} -{"city": "SPRINGFIELD", "loc": [-72.51211, 42.12473], "pop": 13040, "state": "MA", "_id": "01119"} -{"city": "SPRINGFIELD", "loc": [-72.488903, 42.094397], "pop": 3272, "state": "MA", "_id": "01128"} -{"city": "SPRINGFIELD", "loc": [-72.487622, 42.122263], "pop": 6831, "state": "MA", "_id": "01129"} -{"city": "INDIAN ORCHARD", "loc": [-72.505048, 42.153225], "pop": 8702, "state": "MA", "_id": "01151"} -{"city": "PITTSFIELD", "loc": [-73.247088, 42.453086], "pop": 50655, "state": "MA", "_id": "01201"} -{"city": "ADAMS", "loc": [-73.117225, 42.622319], "pop": 9901, "state": "MA", "_id": "01220"} -{"city": "ASHLEY FALLS", "loc": [-73.320195, 42.059552], "pop": 561, "state": "MA", "_id": "01222"} -{"city": "BECKET", "loc": [-73.120325, 42.359363], "pop": 1070, "state": "MA", "_id": "01223"} -{"city": "CHESHIRE", "loc": [-73.157964, 42.561059], "pop": 3094, "state": "MA", "_id": "01225"} -{"city": "DALTON", "loc": [-73.160259, 42.475046], "pop": 7357, "state": "MA", "_id": "01226"} -{"city": "GREAT BARRINGTON", "loc": [-73.36065, 42.195922], "pop": 10603, "state": "MA", "_id": "01230"} -{"city": "PERU", "loc": [-73.092433, 42.434604], "pop": 2559, "state": "MA", "_id": "01235"} -{"city": "HOUSATONIC", "loc": [-73.374544, 42.265296], "pop": 802, "state": "MA", "_id": "01236"} -{"city": "HANCOCK", "loc": [-73.248737, 42.541961], "pop": 2328, "state": "MA", "_id": "01237"} -{"city": "LEE", "loc": [-73.231696, 42.298994], "pop": 6916, "state": "MA", "_id": "01238"} -{"city": "LENOX", "loc": [-73.271322, 42.364241], "pop": 5001, "state": "MA", "_id": "01240"} -{"city": "MIDDLEFIELD", "loc": [-73.006226, 42.34795], "pop": 384, "state": "MA", "_id": "01243"} -{"city": "WEST OTIS", "loc": [-73.213452, 42.187847], "pop": 329, "state": "MA", "_id": "01245"} -{"city": "CLARKSBURG", "loc": [-73.10999, 42.69865], "pop": 19054, "state": "MA", "_id": "01247"} -{"city": "OTIS", "loc": [-73.082093, 42.18988], "pop": 1060, "state": "MA", "_id": "01253"} -{"city": "RICHMOND", "loc": [-73.364457, 42.378398], "pop": 1134, "state": "MA", "_id": "01254"} -{"city": "SANDISFIELD", "loc": [-73.116285, 42.109429], "pop": 651, "state": "MA", "_id": "01255"} -{"city": "SAVOY", "loc": [-73.023281, 42.576964], "pop": 632, "state": "MA", "_id": "01256"} -{"city": "SHEFFIELD", "loc": [-73.361091, 42.100102], "pop": 1839, "state": "MA", "_id": "01257"} -{"city": "SOUTH EGREMONT", "loc": [-73.456575, 42.101153], "pop": 135, "state": "MA", "_id": "01258"} -{"city": "SOUTHFIELD", "loc": [-73.260933, 42.078014], "pop": 622, "state": "MA", "_id": "01259"} -{"city": "STOCKBRIDGE", "loc": [-73.322263, 42.30104], "pop": 2200, "state": "MA", "_id": "01262"} -{"city": "WEST STOCKBRIDGE", "loc": [-73.38251, 42.334752], "pop": 1173, "state": "MA", "_id": "01266"} -{"city": "WILLIAMSTOWN", "loc": [-73.20364, 42.708883], "pop": 8220, "state": "MA", "_id": "01267"} -{"city": "WINDSOR", "loc": [-73.04661, 42.509494], "pop": 770, "state": "MA", "_id": "01270"} -{"city": "LEYDEN", "loc": [-72.601847, 42.601222], "pop": 18968, "state": "MA", "_id": "01301"} -{"city": "ASHFIELD", "loc": [-72.810998, 42.523207], "pop": 1535, "state": "MA", "_id": "01330"} -{"city": "NEW SALEM", "loc": [-72.214644, 42.592065], "pop": 14077, "state": "MA", "_id": "01331"} -{"city": "LEYDEN", "loc": [-72.563439, 42.683784], "pop": 2426, "state": "MA", "_id": "01337"} -{"city": "BUCKLAND", "loc": [-72.764124, 42.615174], "pop": 16, "state": "MA", "_id": "01338"} -{"city": "HAWLEY", "loc": [-72.880162, 42.621802], "pop": 1325, "state": "MA", "_id": "01339"} -{"city": "COLRAIN", "loc": [-72.726508, 42.67905], "pop": 2050, "state": "MA", "_id": "01340"} -{"city": "CONWAY", "loc": [-72.702473, 42.513832], "pop": 1524, "state": "MA", "_id": "01341"} -{"city": "DEERFIELD", "loc": [-72.607234, 42.540636], "pop": 1281, "state": "MA", "_id": "01342"} -{"city": "ERVING", "loc": [-72.416638, 42.604957], "pop": 635, "state": "MA", "_id": "01344"} -{"city": "HEATH", "loc": [-72.839101, 42.685347], "pop": 174, "state": "MA", "_id": "01346"} -{"city": "MILLERS FALLS", "loc": [-72.494626, 42.576206], "pop": 1893, "state": "MA", "_id": "01349"} -{"city": "MONROE", "loc": [-72.960156, 42.723885], "pop": 97, "state": "MA", "_id": "01350"} -{"city": "MONTAGUE", "loc": [-72.532837, 42.542864], "pop": 1699, "state": "MA", "_id": "01351"} -{"city": "NEW SALEM", "loc": [-72.306241, 42.514643], "pop": 456, "state": "MA", "_id": "01355"} -{"city": "NORTHFIELD", "loc": [-72.450995, 42.688705], "pop": 2829, "state": "MA", "_id": "01360"} -{"city": "NEW SALEM", "loc": [-72.305867, 42.591231], "pop": 8544, "state": "MA", "_id": "01364"} -{"city": "PETERSHAM", "loc": [-72.189349, 42.489761], "pop": 1131, "state": "MA", "_id": "01366"} -{"city": "ROWE", "loc": [-72.925776, 42.695289], "pop": 630, "state": "MA", "_id": "01367"} -{"city": "SHELBURNE FALLS", "loc": [-72.739059, 42.602203], "pop": 4525, "state": "MA", "_id": "01370"} -{"city": "SOUTH DEERFIELD", "loc": [-72.615268, 42.475616], "pop": 5118, "state": "MA", "_id": "01373"} -{"city": "SUNDERLAND", "loc": [-72.567569, 42.453947], "pop": 3399, "state": "MA", "_id": "01375"} -{"city": "TURNERS FALLS", "loc": [-72.54701, 42.606521], "pop": 7100, "state": "MA", "_id": "01376"} -{"city": "WENDELL", "loc": [-72.400851, 42.565644], "pop": 393, "state": "MA", "_id": "01379"} -{"city": "FITCHBURG", "loc": [-71.803133, 42.579563], "pop": 41194, "state": "MA", "_id": "01420"} -{"city": "ASHBURNHAM", "loc": [-71.92666, 42.649614], "pop": 5433, "state": "MA", "_id": "01430"} -{"city": "ASHBY", "loc": [-71.817369, 42.674462], "pop": 2649, "state": "MA", "_id": "01431"} -{"city": "AYER", "loc": [-71.578763, 42.55914], "pop": 6871, "state": "MA", "_id": "01432"} -{"city": "FT DEVENS", "loc": [-71.621819, 42.532416], "pop": 8480, "state": "MA", "_id": "01433"} -{"city": "BALDWINVILLE", "loc": [-72.064647, 42.593568], "pop": 4386, "state": "MA", "_id": "01436"} -{"city": "GARDNER", "loc": [-71.9898, 42.57405], "pop": 20125, "state": "MA", "_id": "01440"} -{"city": "GROTON", "loc": [-71.558371, 42.612351], "pop": 7504, "state": "MA", "_id": "01450"} -{"city": "HARVARD", "loc": [-71.575293, 42.498565], "pop": 4445, "state": "MA", "_id": "01451"} -{"city": "HUBBARDSTON", "loc": [-72.001159, 42.486538], "pop": 2797, "state": "MA", "_id": "01452"} -{"city": "LEOMINSTER", "loc": [-71.756308, 42.52744], "pop": 38145, "state": "MA", "_id": "01453"} -{"city": "LITTLETON", "loc": [-71.487667, 42.540132], "pop": 7066, "state": "MA", "_id": "01460"} -{"city": "LUNENBURG", "loc": [-71.726642, 42.58843], "pop": 9117, "state": "MA", "_id": "01462"} -{"city": "PEPPERELL", "loc": [-71.593392, 42.668888], "pop": 10178, "state": "MA", "_id": "01463"} -{"city": "SHIRLEY CENTER", "loc": [-71.646444, 42.558653], "pop": 6118, "state": "MA", "_id": "01464"} -{"city": "TEMPLETON", "loc": [-72.064971, 42.545976], "pop": 2058, "state": "MA", "_id": "01468"} -{"city": "TOWNSEND", "loc": [-71.689646, 42.652511], "pop": 6112, "state": "MA", "_id": "01469"} -{"city": "WESTMINSTER", "loc": [-71.909599, 42.548319], "pop": 6191, "state": "MA", "_id": "01473"} -{"city": "W TOWNSEND", "loc": [-71.74057, 42.670404], "pop": 2452, "state": "MA", "_id": "01474"} -{"city": "WINCHENDON", "loc": [-72.047524, 42.678943], "pop": 8805, "state": "MA", "_id": "01475"} -{"city": "AUBURN", "loc": [-71.839144, 42.205502], "pop": 15007, "state": "MA", "_id": "01501"} -{"city": "BERLIN", "loc": [-71.635634, 42.384438], "pop": 2293, "state": "MA", "_id": "01503"} -{"city": "BLACKSTONE", "loc": [-71.52691, 42.028708], "pop": 8023, "state": "MA", "_id": "01504"} -{"city": "BOYLSTON", "loc": [-71.731042, 42.337727], "pop": 3517, "state": "MA", "_id": "01505"} -{"city": "BROOKFIELD", "loc": [-72.098887, 42.199141], "pop": 2968, "state": "MA", "_id": "01506"} -{"city": "CHARLTON", "loc": [-71.966384, 42.137902], "pop": 9576, "state": "MA", "_id": "01507"} -{"city": "CLINTON", "loc": [-71.682847, 42.418147], "pop": 13269, "state": "MA", "_id": "01510"} -{"city": "EAST BROOKFIELD", "loc": [-72.048078, 42.219308], "pop": 2033, "state": "MA", "_id": "01515"} -{"city": "EAST DOUGLAS", "loc": [-71.726611, 42.060566], "pop": 5594, "state": "MA", "_id": "01516"} -{"city": "FISKDALE", "loc": [-72.117764, 42.122762], "pop": 774, "state": "MA", "_id": "01518"} -{"city": "GRAFTON", "loc": [-71.686848, 42.200371], "pop": 4910, "state": "MA", "_id": "01519"} -{"city": "HOLDEN", "loc": [-71.84142, 42.341983], "pop": 12051, "state": "MA", "_id": "01520"} -{"city": "HOLLAND", "loc": [-72.154373, 42.040264], "pop": 747, "state": "MA", "_id": "01521"} -{"city": "JEFFERSON", "loc": [-71.87058, 42.375519], "pop": 2478, "state": "MA", "_id": "01522"} -{"city": "LANCASTER", "loc": [-71.686831, 42.450984], "pop": 6018, "state": "MA", "_id": "01523"} -{"city": "LEICESTER", "loc": [-71.918829, 42.237047], "pop": 6527, "state": "MA", "_id": "01524"} -{"city": "MILLBURY", "loc": [-71.764438, 42.196779], "pop": 12228, "state": "MA", "_id": "01527"} -{"city": "MILLVILLE", "loc": [-71.579813, 42.033102], "pop": 2236, "state": "MA", "_id": "01529"} -{"city": "NEW BRAINTREE", "loc": [-72.130642, 42.31977], "pop": 881, "state": "MA", "_id": "01531"} -{"city": "NORTHBOROUGH", "loc": [-71.646372, 42.318242], "pop": 11930, "state": "MA", "_id": "01532"} -{"city": "NORTHBRIDGE", "loc": [-71.656366, 42.1494], "pop": 4564, "state": "MA", "_id": "01534"} -{"city": "NORTH BROOKFIELD", "loc": [-72.082129, 42.266455], "pop": 4755, "state": "MA", "_id": "01535"} -{"city": "NORTH GRAFTON", "loc": [-71.703691, 42.229726], "pop": 5401, "state": "MA", "_id": "01536"} -{"city": "NORTH OXFORD", "loc": [-71.885953, 42.16549], "pop": 3031, "state": "MA", "_id": "01537"} -{"city": "OXFORD", "loc": [-71.868677, 42.11285], "pop": 9557, "state": "MA", "_id": "01540"} -{"city": "PRINCETON", "loc": [-71.876245, 42.450812], "pop": 3189, "state": "MA", "_id": "01541"} -{"city": "ROCHDALE", "loc": [-71.906882, 42.199685], "pop": 1154, "state": "MA", "_id": "01542"} -{"city": "RUTLAND", "loc": [-71.948951, 42.376199], "pop": 4936, "state": "MA", "_id": "01543"} -{"city": "SHREWSBURY", "loc": [-71.720503, 42.284801], "pop": 24146, "state": "MA", "_id": "01545"} -{"city": "SOUTHBRIDGE", "loc": [-72.035347, 42.075024], "pop": 17816, "state": "MA", "_id": "01550"} -{"city": "SOUTH GRAFTON", "loc": [-71.692725, 42.176042], "pop": 2719, "state": "MA", "_id": "01560"} -{"city": "SPENCER", "loc": [-71.990617, 42.244103], "pop": 11598, "state": "MA", "_id": "01562"} -{"city": "STERLING", "loc": [-71.775192, 42.435351], "pop": 6481, "state": "MA", "_id": "01564"} -{"city": "STURBRIDGE", "loc": [-72.084233, 42.112619], "pop": 7001, "state": "MA", "_id": "01566"} -{"city": "WEST UPTON", "loc": [-71.608014, 42.173275], "pop": 4682, "state": "MA", "_id": "01568"} -{"city": "UXBRIDGE", "loc": [-71.632869, 42.074426], "pop": 10364, "state": "MA", "_id": "01569"} -{"city": "DUDLEY HILL", "loc": [-71.839467, 42.047574], "pop": 3735, "state": "MA", "_id": "01570"} -{"city": "DUDLEY", "loc": [-71.893228, 42.048894], "pop": 22001, "state": "MA", "_id": "01571"} -{"city": "WESTBOROUGH", "loc": [-71.617604, 42.267891], "pop": 14132, "state": "MA", "_id": "01581"} -{"city": "WEST BOYLSTON", "loc": [-71.783822, 42.35836], "pop": 6611, "state": "MA", "_id": "01583"} -{"city": "WEST BROOKFIELD", "loc": [-72.151137, 42.244137], "pop": 3528, "state": "MA", "_id": "01585"} -{"city": "WHITINSVILLE", "loc": [-71.664357, 42.115319], "pop": 8807, "state": "MA", "_id": "01588"} -{"city": "WILKINSONVILLE", "loc": [-71.748416, 42.140586], "pop": 6719, "state": "MA", "_id": "01590"} -{"city": "WORCESTER", "loc": [-71.841678, 42.270251], "pop": 19988, "state": "MA", "_id": "01602"} -{"city": "WORCESTER", "loc": [-71.837995, 42.245033], "pop": 18605, "state": "MA", "_id": "01603"} -{"city": "WORCESTER", "loc": [-71.774626, 42.254084], "pop": 29036, "state": "MA", "_id": "01604"} -{"city": "WORCESTER", "loc": [-71.788795, 42.289391], "pop": 25695, "state": "MA", "_id": "01605"} -{"city": "WORCESTER", "loc": [-71.795774, 42.311029], "pop": 18213, "state": "MA", "_id": "01606"} -{"city": "WORCESTER", "loc": [-71.793837, 42.230294], "pop": 9048, "state": "MA", "_id": "01607"} -{"city": "WORCESTER", "loc": [-71.800262, 42.262425], "pop": 3646, "state": "MA", "_id": "01608"} -{"city": "WORCESTER", "loc": [-71.817456, 42.275387], "pop": 21905, "state": "MA", "_id": "01609"} -{"city": "WORCESTER", "loc": [-71.810798, 42.249186], "pop": 23720, "state": "MA", "_id": "01610"} -{"city": "CHERRY VALLEY", "loc": [-71.874971, 42.237287], "pop": 2510, "state": "MA", "_id": "01611"} -{"city": "PAXTON", "loc": [-71.920234, 42.306646], "pop": 4047, "state": "MA", "_id": "01612"} -{"city": "FRAMINGHAM", "loc": [-71.425486, 42.300665], "pop": 65046, "state": "MA", "_id": "01701"} -{"city": "VILLAGE OF NAGOG", "loc": [-71.422354, 42.514941], "pop": 2330, "state": "MA", "_id": "01718"} -{"city": "BOXBORO", "loc": [-71.518229, 42.486876], "pop": 3343, "state": "MA", "_id": "01719"} -{"city": "ACTON", "loc": [-71.448255, 42.475076], "pop": 15514, "state": "MA", "_id": "01720"} -{"city": "ASHLAND", "loc": [-71.458347, 42.253909], "pop": 12066, "state": "MA", "_id": "01721"} -{"city": "BEDFORD", "loc": [-71.276796, 42.484287], "pop": 16147, "state": "MA", "_id": "01730"} -{"city": "BOLTON", "loc": [-71.607593, 42.436523], "pop": 3134, "state": "MA", "_id": "01740"} -{"city": "CARLISLE", "loc": [-71.351892, 42.528562], "pop": 4333, "state": "MA", "_id": "01741"} -{"city": "CONCORD", "loc": [-71.374741, 42.456701], "pop": 17076, "state": "MA", "_id": "01742"} -{"city": "SOUTHBOROUGH", "loc": [-71.502256, 42.293221], "pop": 506, "state": "MA", "_id": "01745"} -{"city": "HOLLISTON", "loc": [-71.436059, 42.202641], "pop": 12917, "state": "MA", "_id": "01746"} -{"city": "HOPEDALE", "loc": [-71.537601, 42.126796], "pop": 5649, "state": "MA", "_id": "01747"} -{"city": "HOPKINTON", "loc": [-71.530178, 42.219046], "pop": 9191, "state": "MA", "_id": "01748"} -{"city": "HUDSON", "loc": [-71.560896, 42.391796], "pop": 17233, "state": "MA", "_id": "01749"} -{"city": "MARLBOROUGH", "loc": [-71.543355, 42.350861], "pop": 31813, "state": "MA", "_id": "01752"} -{"city": "MAYNARD", "loc": [-71.454975, 42.432118], "pop": 10325, "state": "MA", "_id": "01754"} -{"city": "MENDON", "loc": [-71.549882, 42.096744], "pop": 4010, "state": "MA", "_id": "01756"} -{"city": "MILFORD", "loc": [-71.527402, 42.151142], "pop": 25372, "state": "MA", "_id": "01757"} -{"city": "NATICK", "loc": [-71.35741, 42.287476], "pop": 30432, "state": "MA", "_id": "01760"} -{"city": "SHERBORN", "loc": [-71.378717, 42.233088], "pop": 3998, "state": "MA", "_id": "01770"} -{"city": "SOUTHBOROUGH", "loc": [-71.531997, 42.293919], "pop": 6122, "state": "MA", "_id": "01772"} -{"city": "LINCOLN", "loc": [-71.313723, 42.421723], "pop": 4515, "state": "MA", "_id": "01773"} -{"city": "STOW", "loc": [-71.515019, 42.430785], "pop": 5328, "state": "MA", "_id": "01775"} -{"city": "SUDBURY", "loc": [-71.428159, 42.383655], "pop": 14358, "state": "MA", "_id": "01776"} -{"city": "WAYLAND", "loc": [-71.358781, 42.348629], "pop": 11874, "state": "MA", "_id": "01778"} -{"city": "WOBURN", "loc": [-71.157404, 42.482894], "pop": 36152, "state": "MA", "_id": "01801"} -{"city": "BURLINGTON", "loc": [-71.200437, 42.508942], "pop": 23093, "state": "MA", "_id": "01803"} -{"city": "ANDOVER", "loc": [-71.156481, 42.64956], "pop": 29161, "state": "MA", "_id": "01810"} -{"city": "BILLERICA", "loc": [-71.251754, 42.551874], "pop": 28899, "state": "MA", "_id": "01821"} -{"city": "SOUTH CHELMSFORD", "loc": [-71.357521, 42.59356], "pop": 24457, "state": "MA", "_id": "01824"} -{"city": "DRACUT", "loc": [-71.318592, 42.676422], "pop": 25594, "state": "MA", "_id": "01826"} -{"city": "DUNSTABLE", "loc": [-71.495201, 42.673917], "pop": 2166, "state": "MA", "_id": "01827"} -{"city": "HAVERHILL", "loc": [-71.072057, 42.785605], "pop": 22445, "state": "MA", "_id": "01830"} -{"city": "HAVERHILL", "loc": [-71.109519, 42.779154], "pop": 16860, "state": "MA", "_id": "01832"} -{"city": "GEORGETOWN", "loc": [-70.982239, 42.728067], "pop": 6384, "state": "MA", "_id": "01833"} -{"city": "GROVELAND", "loc": [-71.027018, 42.753027], "pop": 5214, "state": "MA", "_id": "01834"} -{"city": "BRADFORD", "loc": [-71.08549, 42.758597], "pop": 12078, "state": "MA", "_id": "01835"} -{"city": "LAWRENCE", "loc": [-71.16381, 42.707958], "pop": 2728, "state": "MA", "_id": "01840"} -{"city": "LAWRENCE", "loc": [-71.166997, 42.711545], "pop": 45555, "state": "MA", "_id": "01841"} -{"city": "LAWRENCE", "loc": [-71.160506, 42.691053], "pop": 22285, "state": "MA", "_id": "01843"} -{"city": "METHUEN", "loc": [-71.181031, 42.728019], "pop": 39664, "state": "MA", "_id": "01844"} -{"city": "NORTH ANDOVER", "loc": [-71.109004, 42.682583], "pop": 22792, "state": "MA", "_id": "01845"} -{"city": "LOWELL", "loc": [-71.305078, 42.656035], "pop": 15434, "state": "MA", "_id": "01850"} -{"city": "LOWELL", "loc": [-71.332882, 42.631548], "pop": 28154, "state": "MA", "_id": "01851"} -{"city": "LOWELL", "loc": [-71.298331, 42.634413], "pop": 33379, "state": "MA", "_id": "01852"} -{"city": "LOWELL", "loc": [-71.335464, 42.649254], "pop": 26472, "state": "MA", "_id": "01854"} -{"city": "MERRIMAC", "loc": [-71.004658, 42.834629], "pop": 5196, "state": "MA", "_id": "01860"} -{"city": "NORTH BILLERICA", "loc": [-71.290217, 42.575694], "pop": 8720, "state": "MA", "_id": "01862"} -{"city": "NORTH CHELMSFORD", "loc": [-71.390834, 42.634737], "pop": 7878, "state": "MA", "_id": "01863"} -{"city": "NORTH READING", "loc": [-71.094711, 42.581898], "pop": 12002, "state": "MA", "_id": "01864"} -{"city": "READING", "loc": [-71.109021, 42.527986], "pop": 22539, "state": "MA", "_id": "01867"} -{"city": "TEWKSBURY", "loc": [-71.223224, 42.60283], "pop": 27269, "state": "MA", "_id": "01876"} -{"city": "TYNGSBORO", "loc": [-71.415766, 42.672383], "pop": 8643, "state": "MA", "_id": "01879"} -{"city": "WAKEFIELD", "loc": [-71.068471, 42.500886], "pop": 24830, "state": "MA", "_id": "01880"} -{"city": "GRANITEVILLE", "loc": [-71.438143, 42.589959], "pop": 16430, "state": "MA", "_id": "01886"} -{"city": "WILMINGTON", "loc": [-71.172306, 42.558143], "pop": 17647, "state": "MA", "_id": "01887"} -{"city": "WINCHESTER", "loc": [-71.14407, 42.453028], "pop": 20232, "state": "MA", "_id": "01890"} -{"city": "LYNN", "loc": [-70.945516, 42.463378], "pop": 1187, "state": "MA", "_id": "01901"} -{"city": "LYNN", "loc": [-70.941989, 42.469814], "pop": 41625, "state": "MA", "_id": "01902"} -{"city": "EAST LYNN", "loc": [-70.962798, 42.487453], "pop": 17073, "state": "MA", "_id": "01904"} -{"city": "WEST LYNN", "loc": [-70.973825, 42.46453], "pop": 21360, "state": "MA", "_id": "01905"} -{"city": "SAUGUS", "loc": [-71.011093, 42.463344], "pop": 25487, "state": "MA", "_id": "01906"} -{"city": "SWAMPSCOTT", "loc": [-70.909774, 42.474611], "pop": 13650, "state": "MA", "_id": "01907"} -{"city": "NAHANT", "loc": [-70.927739, 42.426098], "pop": 3828, "state": "MA", "_id": "01908"} -{"city": "AMESBURY", "loc": [-70.936681, 42.855879], "pop": 14970, "state": "MA", "_id": "01913"} -{"city": "BEVERLY", "loc": [-70.875939, 42.560825], "pop": 38259, "state": "MA", "_id": "01915"} -{"city": "BOXFORD", "loc": [-71.011372, 42.679719], "pop": 6249, "state": "MA", "_id": "01921"} -{"city": "BYFIELD", "loc": [-70.935053, 42.756792], "pop": 2006, "state": "MA", "_id": "01922"} -{"city": "DANVERS", "loc": [-70.942461, 42.569402], "pop": 23977, "state": "MA", "_id": "01923"} -{"city": "ESSEX", "loc": [-70.782794, 42.628629], "pop": 3260, "state": "MA", "_id": "01929"} -{"city": "GLOUCESTER", "loc": [-70.672149, 42.620836], "pop": 28716, "state": "MA", "_id": "01930"} -{"city": "IPSWICH", "loc": [-70.849353, 42.680877], "pop": 11864, "state": "MA", "_id": "01938"} -{"city": "LYNNFIELD", "loc": [-71.033873, 42.532711], "pop": 11274, "state": "MA", "_id": "01940"} -{"city": "MANCHESTER", "loc": [-70.767434, 42.57963], "pop": 5286, "state": "MA", "_id": "01944"} -{"city": "MARBLEHEAD", "loc": [-70.865291, 42.498431], "pop": 19971, "state": "MA", "_id": "01945"} -{"city": "MIDDLETON", "loc": [-71.013004, 42.594184], "pop": 4921, "state": "MA", "_id": "01949"} -{"city": "NEWBURYPORT", "loc": [-70.884668, 42.812964], "pop": 16317, "state": "MA", "_id": "01950"} -{"city": "NEWBURY", "loc": [-70.847377, 42.783475], "pop": 3710, "state": "MA", "_id": "01951"} -{"city": "SALISBURY", "loc": [-70.858822, 42.850678], "pop": 6879, "state": "MA", "_id": "01952"} -{"city": "PEABODY", "loc": [-70.961194, 42.532579], "pop": 47685, "state": "MA", "_id": "01960"} -{"city": "ROCKPORT", "loc": [-70.619424, 42.657973], "pop": 7482, "state": "MA", "_id": "01966"} -{"city": "ROWLEY", "loc": [-70.90696, 42.713753], "pop": 4368, "state": "MA", "_id": "01969"} -{"city": "SALEM", "loc": [-70.900343, 42.515114], "pop": 37642, "state": "MA", "_id": "01970"} -{"city": "SOUTH HAMILTON", "loc": [-70.856132, 42.618478], "pop": 7288, "state": "MA", "_id": "01982"} -{"city": "TOPSFIELD", "loc": [-70.948843, 42.641546], "pop": 5763, "state": "MA", "_id": "01983"} -{"city": "WENHAM", "loc": [-70.878622, 42.60166], "pop": 4148, "state": "MA", "_id": "01984"} -{"city": "WEST NEWBURY", "loc": [-70.977811, 42.794865], "pop": 3421, "state": "MA", "_id": "01985"} -{"city": "BELLINGHAM", "loc": [-71.476829, 42.074573], "pop": 14873, "state": "MA", "_id": "02019"} -{"city": "CANTON", "loc": [-71.135536, 42.164454], "pop": 18530, "state": "MA", "_id": "02021"} -{"city": "COHASSET", "loc": [-70.812788, 42.239484], "pop": 7075, "state": "MA", "_id": "02025"} -{"city": "DEDHAM", "loc": [-71.163741, 42.243685], "pop": 23782, "state": "MA", "_id": "02026"} -{"city": "DOVER", "loc": [-71.285363, 42.236233], "pop": 4915, "state": "MA", "_id": "02030"} -{"city": "EAST WALPOLE", "loc": [-71.2179, 42.15324], "pop": 3844, "state": "MA", "_id": "02032"} -{"city": "FOXBORO", "loc": [-71.244127, 42.064938], "pop": 14293, "state": "MA", "_id": "02035"} -{"city": "FRANKLIN", "loc": [-71.405786, 42.09347], "pop": 22128, "state": "MA", "_id": "02038"} -{"city": "HINGHAM", "loc": [-70.891051, 42.224485], "pop": 19821, "state": "MA", "_id": "02043"} -{"city": "HULL", "loc": [-70.875442, 42.285346], "pop": 10466, "state": "MA", "_id": "02045"} -{"city": "MANSFIELD", "loc": [-71.217775, 42.021238], "pop": 16676, "state": "MA", "_id": "02048"} -{"city": "MARSHFIELD", "loc": [-70.69931, 42.106177], "pop": 21782, "state": "MA", "_id": "02050"} -{"city": "MEDFIELD", "loc": [-71.304813, 42.184525], "pop": 10531, "state": "MA", "_id": "02052"} -{"city": "MEDWAY", "loc": [-71.421715, 42.151363], "pop": 9902, "state": "MA", "_id": "02053"} -{"city": "MILLIS", "loc": [-71.360693, 42.166938], "pop": 7613, "state": "MA", "_id": "02054"} -{"city": "NORFOLK", "loc": [-71.326934, 42.117746], "pop": 9259, "state": "MA", "_id": "02056"} -{"city": "NORWELL", "loc": [-70.82172, 42.159574], "pop": 9279, "state": "MA", "_id": "02061"} -{"city": "NORWOOD", "loc": [-71.203313, 42.186843], "pop": 28700, "state": "MA", "_id": "02062"} -{"city": "SCITUATE", "loc": [-70.752476, 42.203235], "pop": 16535, "state": "MA", "_id": "02066"} -{"city": "SHARON", "loc": [-71.175872, 42.109388], "pop": 15873, "state": "MA", "_id": "02067"} -{"city": "SOUTH WALPOLE", "loc": [-71.275235, 42.099203], "pop": 752, "state": "MA", "_id": "02071"} -{"city": "STOUGHTON", "loc": [-71.107357, 42.125279], "pop": 26777, "state": "MA", "_id": "02072"} -{"city": "WALPOLE", "loc": [-71.254391, 42.144413], "pop": 15615, "state": "MA", "_id": "02081"} -{"city": "WESTWOOD", "loc": [-71.210426, 42.214824], "pop": 12557, "state": "MA", "_id": "02090"} -{"city": "WRENTHAM", "loc": [-71.339568, 42.061746], "pop": 9006, "state": "MA", "_id": "02093"} -{"city": "BOSTON", "loc": [-71.068432, 42.357603], "pop": 3697, "state": "MA", "_id": "02108"} -{"city": "BOSTON", "loc": [-71.053386, 42.362963], "pop": 3926, "state": "MA", "_id": "02109"} -{"city": "BOSTON", "loc": [-71.051417, 42.357636], "pop": 957, "state": "MA", "_id": "02110"} -{"city": "BOSTON", "loc": [-71.0629, 42.350348], "pop": 3759, "state": "MA", "_id": "02111"} -{"city": "BOSTON", "loc": [-71.055958, 42.365656], "pop": 6698, "state": "MA", "_id": "02113"} -{"city": "BOSTON", "loc": [-71.06823, 42.361111], "pop": 10246, "state": "MA", "_id": "02114"} -{"city": "BOSTON", "loc": [-71.092215, 42.342706], "pop": 25597, "state": "MA", "_id": "02115"} -{"city": "BOSTON", "loc": [-71.076798, 42.349201], "pop": 17459, "state": "MA", "_id": "02116"} -{"city": "ROXBURY", "loc": [-71.075627, 42.340154], "pop": 21914, "state": "MA", "_id": "02118"} -{"city": "ROXBURY", "loc": [-71.086923, 42.322414], "pop": 25207, "state": "MA", "_id": "02119"} -{"city": "ROXBURY", "loc": [-71.097978, 42.332844], "pop": 14212, "state": "MA", "_id": "02120"} -{"city": "DORCHESTER", "loc": [-71.08305, 42.307503], "pop": 25602, "state": "MA", "_id": "02121"} -{"city": "DORCHESTER", "loc": [-71.058304, 42.297278], "pop": 21266, "state": "MA", "_id": "02122"} -{"city": "DORCHESTER", "loc": [-71.072898, 42.287984], "pop": 48560, "state": "MA", "_id": "02124"} -{"city": "DORCHESTER", "loc": [-71.061924, 42.315305], "pop": 31393, "state": "MA", "_id": "02125"} -{"city": "MATTAPAN", "loc": [-71.093871, 42.273889], "pop": 27808, "state": "MA", "_id": "02126"} -{"city": "SOUTH BOSTON", "loc": [-71.043792, 42.333454], "pop": 29170, "state": "MA", "_id": "02127"} -{"city": "EAST BOSTON", "loc": [-71.028682, 42.378137], "pop": 32941, "state": "MA", "_id": "02128"} -{"city": "CHARLESTOWN", "loc": [-71.062715, 42.377815], "pop": 14775, "state": "MA", "_id": "02129"} -{"city": "JAMAICA PLAIN", "loc": [-71.111495, 42.312596], "pop": 36571, "state": "MA", "_id": "02130"} -{"city": "ROSLINDALE", "loc": [-71.129543, 42.283615], "pop": 32677, "state": "MA", "_id": "02131"} -{"city": "WEST ROXBURY", "loc": [-71.158868, 42.27868], "pop": 26366, "state": "MA", "_id": "02132"} -{"city": "ALLSTON", "loc": [-71.132866, 42.353519], "pop": 23775, "state": "MA", "_id": "02134"} -{"city": "BRIGHTON", "loc": [-71.156599, 42.34779], "pop": 35011, "state": "MA", "_id": "02135"} -{"city": "HYDE PARK", "loc": [-71.126052, 42.253989], "pop": 24260, "state": "MA", "_id": "02136"} -{"city": "CAMBRIDGE", "loc": [-71.125611, 42.377045], "pop": 33841, "state": "MA", "_id": "02138"} -{"city": "CAMBRIDGE", "loc": [-71.104155, 42.364688], "pop": 33149, "state": "MA", "_id": "02139"} -{"city": "NORTH CAMBRIDGE", "loc": [-71.129379, 42.391366], "pop": 16313, "state": "MA", "_id": "02140"} -{"city": "EAST CAMBRIDGE", "loc": [-71.088277, 42.370701], "pop": 10392, "state": "MA", "_id": "02141"} -{"city": "CAMBRIDGE", "loc": [-71.083011, 42.362025], "pop": 1336, "state": "MA", "_id": "02142"} -{"city": "SOMERVILLE", "loc": [-71.102814, 42.382945], "pop": 25597, "state": "MA", "_id": "02143"} -{"city": "SOMERVILLE", "loc": [-71.122059, 42.40032], "pop": 26374, "state": "MA", "_id": "02144"} -{"city": "SOMERVILLE", "loc": [-71.092944, 42.390678], "pop": 24422, "state": "MA", "_id": "02145"} -{"city": "BROOKLINE", "loc": [-71.128917, 42.339158], "pop": 56614, "state": "MA", "_id": "02146"} -{"city": "MALDEN", "loc": [-71.060507, 42.42911], "pop": 54114, "state": "MA", "_id": "02148"} -{"city": "EVERETT", "loc": [-71.051448, 42.411199], "pop": 35493, "state": "MA", "_id": "02149"} -{"city": "CHELSEA", "loc": [-71.032521, 42.396252], "pop": 28790, "state": "MA", "_id": "02150"} -{"city": "REVERE", "loc": [-71.005165, 42.413767], "pop": 42766, "state": "MA", "_id": "02151"} -{"city": "WINTHROP", "loc": [-70.980043, 42.376294], "pop": 18907, "state": "MA", "_id": "02152"} -{"city": "NORTH WALTHAM", "loc": [-71.236497, 42.382492], "pop": 57871, "state": "MA", "_id": "02154"} -{"city": "MEDFORD", "loc": [-71.108686, 42.417335], "pop": 57338, "state": "MA", "_id": "02155"} -{"city": "NEWTONVILLE", "loc": [-71.1902, 42.353835], "pop": 13271, "state": "MA", "_id": "02158"} -{"city": "NEWTON CENTER", "loc": [-71.191839, 42.318889], "pop": 18726, "state": "MA", "_id": "02159"} -{"city": "NEWTONVILLE", "loc": [-71.208771, 42.351961], "pop": 8872, "state": "MA", "_id": "02160"} -{"city": "NEWTON HIGHLANDS", "loc": [-71.209347, 42.318512], "pop": 6657, "state": "MA", "_id": "02161"} -{"city": "NEWTONVILLE", "loc": [-71.258025, 42.330296], "pop": 1427, "state": "MA", "_id": "02162"} -{"city": "CAMBRIDGE", "loc": [-71.141879, 42.364005], "pop": 0, "state": "MA", "_id": "02163"} -{"city": "NEWTON UPPER FAL", "loc": [-71.221615, 42.312562], "pop": 2597, "state": "MA", "_id": "02164"} -{"city": "NEWTONVILLE", "loc": [-71.22795, 42.352366], "pop": 12027, "state": "MA", "_id": "02165"} -{"city": "AUBURNDALE", "loc": [-71.247598, 42.345928], "pop": 6123, "state": "MA", "_id": "02166"} -{"city": "BOSTON COLLEGE", "loc": [-71.16272, 42.31903], "pop": 15619, "state": "MA", "_id": "02167"} -{"city": "WABAN", "loc": [-71.230703, 42.327049], "pop": 5759, "state": "MA", "_id": "02168"} -{"city": "QUINCY", "loc": [-70.997816, 42.249133], "pop": 48920, "state": "MA", "_id": "02169"} -{"city": "QUINCY", "loc": [-71.018644, 42.26713], "pop": 18330, "state": "MA", "_id": "02170"} -{"city": "QUINCY", "loc": [-71.024141, 42.282519], "pop": 18251, "state": "MA", "_id": "02171"} -{"city": "EAST WATERTOWN", "loc": [-71.180266, 42.371497], "pop": 33930, "state": "MA", "_id": "02172"} -{"city": "LEXINGTON", "loc": [-71.225916, 42.445384], "pop": 28994, "state": "MA", "_id": "02173"} -{"city": "ARLINGTON", "loc": [-71.162517, 42.417098], "pop": 44539, "state": "MA", "_id": "02174"} -{"city": "MELROSE", "loc": [-71.063191, 42.458066], "pop": 28228, "state": "MA", "_id": "02176"} -{"city": "BELMONT", "loc": [-71.174647, 42.389656], "pop": 24733, "state": "MA", "_id": "02178"} -{"city": "STONEHAM", "loc": [-71.0978, 42.482778], "pop": 22147, "state": "MA", "_id": "02180"} -{"city": "WELLESLEY", "loc": [-71.287966, 42.305593], "pop": 26615, "state": "MA", "_id": "02181"} -{"city": "BRAINTREE", "loc": [-70.996304, 42.209284], "pop": 33836, "state": "MA", "_id": "02184"} -{"city": "MILTON", "loc": [-71.077051, 42.253663], "pop": 25558, "state": "MA", "_id": "02186"} -{"city": "WEYMOUTH", "loc": [-70.958248, 42.211327], "pop": 13187, "state": "MA", "_id": "02188"} -{"city": "WEYMOUTH", "loc": [-70.931671, 42.211606], "pop": 14055, "state": "MA", "_id": "02189"} -{"city": "WEYMOUTH", "loc": [-70.94869, 42.172817], "pop": 17668, "state": "MA", "_id": "02190"} -{"city": "WEYMOUTH", "loc": [-70.944318, 42.243564], "pop": 9153, "state": "MA", "_id": "02191"} -{"city": "NEEDHAM", "loc": [-71.235172, 42.278908], "pop": 19570, "state": "MA", "_id": "02192"} -{"city": "WESTON", "loc": [-71.300291, 42.359422], "pop": 10221, "state": "MA", "_id": "02193"} -{"city": "NEEDHAM", "loc": [-71.234363, 42.297702], "pop": 8006, "state": "MA", "_id": "02194"} -{"city": "BOSTON", "loc": [-71.082543, 42.347873], "pop": 886, "state": "MA", "_id": "02199"} -{"city": "BOSTON", "loc": [-71.046511, 42.348921], "pop": 308, "state": "MA", "_id": "02210"} -{"city": "BOSTON", "loc": [-71.102689, 42.347088], "pop": 17769, "state": "MA", "_id": "02215"} -{"city": "AVON", "loc": [-71.043738, 42.125825], "pop": 4594, "state": "MA", "_id": "02322"} -{"city": "BRIDGEWATER", "loc": [-70.97234, 41.977341], "pop": 21198, "state": "MA", "_id": "02324"} -{"city": "CARVER", "loc": [-70.767754, 41.888265], "pop": 10573, "state": "MA", "_id": "02330"} -{"city": "DUXBURY", "loc": [-70.716257, 42.039936], "pop": 13913, "state": "MA", "_id": "02332"} -{"city": "EAST BRIDGEWATER", "loc": [-70.944964, 42.031478], "pop": 11104, "state": "MA", "_id": "02333"} -{"city": "HALIFAX", "loc": [-70.844794, 42.000159], "pop": 6526, "state": "MA", "_id": "02338"} -{"city": "HANOVER", "loc": [-70.857006, 42.121406], "pop": 11912, "state": "MA", "_id": "02339"} -{"city": "HANSON", "loc": [-70.865053, 42.061627], "pop": 9037, "state": "MA", "_id": "02341"} -{"city": "HOLBROOK", "loc": [-71.008273, 42.14641], "pop": 11041, "state": "MA", "_id": "02343"} -{"city": "MIDDLEBORO", "loc": [-70.892965, 41.888396], "pop": 17867, "state": "MA", "_id": "02346"} -{"city": "LAKEVILLE", "loc": [-70.958195, 41.837377], "pop": 7785, "state": "MA", "_id": "02347"} -{"city": "ABINGTON", "loc": [-70.954293, 42.116715], "pop": 13849, "state": "MA", "_id": "02351"} -{"city": "NORTH EASTON", "loc": [-71.112337, 42.058956], "pop": 10397, "state": "MA", "_id": "02356"} -{"city": "PEMBROKE", "loc": [-70.804404, 42.062072], "pop": 14535, "state": "MA", "_id": "02359"} -{"city": "PLYMOUTH", "loc": [-70.642004, 41.910404], "pop": 45629, "state": "MA", "_id": "02360"} -{"city": "KINGSTON", "loc": [-70.740993, 41.995022], "pop": 9045, "state": "MA", "_id": "02364"} -{"city": "PLYMPTON", "loc": [-70.804582, 41.96549], "pop": 2384, "state": "MA", "_id": "02367"} -{"city": "RANDOLPH", "loc": [-71.051392, 42.173587], "pop": 30057, "state": "MA", "_id": "02368"} -{"city": "ROCKLAND", "loc": [-70.913263, 42.129286], "pop": 16123, "state": "MA", "_id": "02370"} -{"city": "SOUTH EASTON", "loc": [-71.098814, 42.025704], "pop": 9247, "state": "MA", "_id": "02375"} -{"city": "WEST BRIDGEWATER", "loc": [-71.016054, 42.025511], "pop": 6440, "state": "MA", "_id": "02379"} -{"city": "WHITMAN", "loc": [-70.938127, 42.081603], "pop": 13208, "state": "MA", "_id": "02382"} -{"city": "BROCKTON", "loc": [-71.034348, 42.081571], "pop": 59498, "state": "MA", "_id": "02401"} -{"city": "BROCKTON", "loc": [-71.001947, 42.088396], "pop": 33290, "state": "MA", "_id": "02402"} -{"city": "ONSET", "loc": [-70.593168, 41.752918], "pop": 12047, "state": "MA", "_id": "02532"} -{"city": "CHILMARK", "loc": [-70.741613, 41.357523], "pop": 952, "state": "MA", "_id": "02535"} -{"city": "TEATICKET", "loc": [-70.565174, 41.58504], "pop": 15976, "state": "MA", "_id": "02536"} -{"city": "EAST SANDWICH", "loc": [-70.46822, 41.684603], "pop": 7254, "state": "MA", "_id": "02537"} -{"city": "EAST WAREHAM", "loc": [-70.653237, 41.768247], "pop": 4778, "state": "MA", "_id": "02538"} -{"city": "EDGARTOWN", "loc": [-70.533893, 41.388856], "pop": 3062, "state": "MA", "_id": "02539"} -{"city": "FALMOUTH", "loc": [-70.621663, 41.564754], "pop": 8588, "state": "MA", "_id": "02540"} -{"city": "OTIS A F B", "loc": [-70.57383, 41.660927], "pop": 2078, "state": "MA", "_id": "02542"} -{"city": "WOODS HOLE", "loc": [-70.66431, 41.526272], "pop": 833, "state": "MA", "_id": "02543"} -{"city": "NANTUCKET", "loc": [-70.093216, 41.272529], "pop": 6012, "state": "MA", "_id": "02554"} -{"city": "NORTH FALMOUTH", "loc": [-70.623043, 41.641677], "pop": 2651, "state": "MA", "_id": "02556"} -{"city": "POCASSET", "loc": [-70.610512, 41.688115], "pop": 3907, "state": "MA", "_id": "02559"} -{"city": "SANDWICH", "loc": [-70.469325, 41.698304], "pop": 9007, "state": "MA", "_id": "02563"} -{"city": "VINEYARD HAVEN", "loc": [-70.593737, 41.449955], "pop": 5924, "state": "MA", "_id": "02568"} -{"city": "WAREHAM", "loc": [-70.711594, 41.754084], "pop": 9304, "state": "MA", "_id": "02571"} -{"city": "WEST TISBURY", "loc": [-70.655802, 41.413717], "pop": 1603, "state": "MA", "_id": "02575"} -{"city": "WEST WAREHAM", "loc": [-70.764179, 41.779617], "pop": 3919, "state": "MA", "_id": "02576"} -{"city": "WEST YARMOUTH", "loc": [-70.298176, 41.653682], "pop": 14543, "state": "MA", "_id": "02601"} -{"city": "BARNSTABLE", "loc": [-70.300067, 41.698289], "pop": 1776, "state": "MA", "_id": "02630"} -{"city": "BREWSTER", "loc": [-70.069868, 41.749179], "pop": 8535, "state": "MA", "_id": "02631"} -{"city": "CENTERVILLE", "loc": [-70.353196, 41.660585], "pop": 10636, "state": "MA", "_id": "02632"} -{"city": "SOUTH CHATHAM", "loc": [-69.980758, 41.687634], "pop": 4744, "state": "MA", "_id": "02633"} -{"city": "COTUIT", "loc": [-70.433431, 41.696025], "pop": 3266, "state": "MA", "_id": "02635"} -{"city": "DENNIS", "loc": [-70.191054, 41.732166], "pop": 3216, "state": "MA", "_id": "02638"} -{"city": "DENNIS PORT", "loc": [-70.132711, 41.664873], "pop": 2510, "state": "MA", "_id": "02639"} -{"city": "EASTHAM", "loc": [-69.984865, 41.840781], "pop": 4582, "state": "MA", "_id": "02642"} -{"city": "FORESTDALE", "loc": [-70.514317, 41.682695], "pop": 2712, "state": "MA", "_id": "02644"} -{"city": "HARWICH", "loc": [-70.057929, 41.70082], "pop": 7363, "state": "MA", "_id": "02645"} -{"city": "HARWICH PORT", "loc": [-70.076755, 41.67128], "pop": 1843, "state": "MA", "_id": "02646"} -{"city": "MARSTONS MILLS", "loc": [-70.416321, 41.670274], "pop": 5777, "state": "MA", "_id": "02648"} -{"city": "MASHPEE", "loc": [-70.485361, 41.618116], "pop": 4469, "state": "MA", "_id": "02649"} -{"city": "NORTH CHATHAM", "loc": [-69.966607, 41.70298], "pop": 995, "state": "MA", "_id": "02650"} -{"city": "NORTH TRURO", "loc": [-70.08751, 42.033779], "pop": 834, "state": "MA", "_id": "02652"} -{"city": "ORLEANS", "loc": [-69.982198, 41.779161], "pop": 5860, "state": "MA", "_id": "02653"} -{"city": "OSTERVILLE", "loc": [-70.383726, 41.63005], "pop": 2330, "state": "MA", "_id": "02655"} -{"city": "PROVINCETOWN", "loc": [-70.186504, 42.053364], "pop": 3561, "state": "MA", "_id": "02657"} -{"city": "SOUTH CHATHAM", "loc": [-70.024106, 41.680126], "pop": 840, "state": "MA", "_id": "02659"} -{"city": "SOUTH DENNIS", "loc": [-70.15851, 41.709711], "pop": 6680, "state": "MA", "_id": "02660"} -{"city": "BASS RIVER", "loc": [-70.19731, 41.672805], "pop": 8514, "state": "MA", "_id": "02664"} -{"city": "TRURO", "loc": [-70.056362, 41.998792], "pop": 739, "state": "MA", "_id": "02666"} -{"city": "WELLFLEET", "loc": [-70.018587, 41.928934], "pop": 2373, "state": "MA", "_id": "02667"} -{"city": "WEST BARNSTABLE", "loc": [-70.371985, 41.700212], "pop": 2311, "state": "MA", "_id": "02668"} -{"city": "WEST DENNIS", "loc": [-70.168092, 41.662557], "pop": 1347, "state": "MA", "_id": "02670"} -{"city": "WEST HARWICH", "loc": [-70.113501, 41.669367], "pop": 1061, "state": "MA", "_id": "02671"} -{"city": "WEST YARMOUTH", "loc": [-70.236297, 41.661367], "pop": 6972, "state": "MA", "_id": "02673"} -{"city": "YARMOUTH PORT", "loc": [-70.227014, 41.705149], "pop": 5735, "state": "MA", "_id": "02675"} -{"city": "ASSONET", "loc": [-71.060736, 41.797458], "pop": 3614, "state": "MA", "_id": "02702"} -{"city": "ATTLEBORO", "loc": [-71.30092, 41.929599], "pop": 38528, "state": "MA", "_id": "02703"} -{"city": "CUTTYHUNK", "loc": [-70.87854, 41.443601], "pop": 98, "state": "MA", "_id": "02713"} -{"city": "DIGHTON", "loc": [-71.142723, 41.812505], "pop": 1828, "state": "MA", "_id": "02715"} -{"city": "EAST FREETOWN", "loc": [-70.967709, 41.763455], "pop": 4883, "state": "MA", "_id": "02717"} -{"city": "EAST TAUNTON", "loc": [-71.019225, 41.873585], "pop": 4800, "state": "MA", "_id": "02718"} -{"city": "FAIRHAVEN", "loc": [-70.889608, 41.640924], "pop": 16141, "state": "MA", "_id": "02719"} -{"city": "FALL RIVER", "loc": [-71.139991, 41.718221], "pop": 30600, "state": "MA", "_id": "02720"} -{"city": "FALL RIVER", "loc": [-71.157424, 41.688305], "pop": 26884, "state": "MA", "_id": "02721"} -{"city": "FALL RIVER", "loc": [-71.133214, 41.692612], "pop": 16801, "state": "MA", "_id": "02723"} -{"city": "FALL RIVER", "loc": [-71.174822, 41.684975], "pop": 18141, "state": "MA", "_id": "02724"} -{"city": "SOMERSET", "loc": [-71.177971, 41.722299], "pop": 2528, "state": "MA", "_id": "02725"} -{"city": "SOMERSET", "loc": [-71.149206, 41.756012], "pop": 15117, "state": "MA", "_id": "02726"} -{"city": "MARION", "loc": [-70.761261, 41.709526], "pop": 4496, "state": "MA", "_id": "02738"} -{"city": "MATTAPOISETT", "loc": [-70.816357, 41.661845], "pop": 5850, "state": "MA", "_id": "02739"} -{"city": "NEW BEDFORD", "loc": [-70.9372, 41.634749], "pop": 46426, "state": "MA", "_id": "02740"} -{"city": "ACUSHNET", "loc": [-70.908652, 41.6997], "pop": 9601, "state": "MA", "_id": "02743"} -{"city": "NEW BEDFORD", "loc": [-70.916746, 41.612716], "pop": 13424, "state": "MA", "_id": "02744"} -{"city": "NEW BEDFORD", "loc": [-70.935545, 41.691337], "pop": 23661, "state": "MA", "_id": "02745"} -{"city": "NEW BEDFORD", "loc": [-70.93243, 41.659972], "pop": 16236, "state": "MA", "_id": "02746"} -{"city": "NORTH DARTMOUTH", "loc": [-70.995769, 41.633789], "pop": 16383, "state": "MA", "_id": "02747"} -{"city": "PADANARAM VILLAG", "loc": [-70.956521, 41.591728], "pop": 10980, "state": "MA", "_id": "02748"} -{"city": "NORTH ATTLEBORO", "loc": [-71.329757, 41.977542], "pop": 22289, "state": "MA", "_id": "02760"} -{"city": "PLAINVILLE", "loc": [-71.327454, 42.012403], "pop": 6874, "state": "MA", "_id": "02762"} -{"city": "NORTH ATTLEBORO", "loc": [-71.310353, 41.970979], "pop": 2737, "state": "MA", "_id": "02763"} -{"city": "NORTH DIGHTON", "loc": [-71.148523, 41.852874], "pop": 3779, "state": "MA", "_id": "02764"} -{"city": "NORTON", "loc": [-71.189441, 41.971801], "pop": 14329, "state": "MA", "_id": "02766"} -{"city": "RAYNHAM", "loc": [-71.046856, 41.932361], "pop": 9804, "state": "MA", "_id": "02767"} -{"city": "REHOBOTH", "loc": [-71.254453, 41.85152], "pop": 7762, "state": "MA", "_id": "02769"} -{"city": "ROCHESTER", "loc": [-70.852257, 41.759082], "pop": 3270, "state": "MA", "_id": "02770"} -{"city": "SEEKONK", "loc": [-71.322406, 41.837835], "pop": 13375, "state": "MA", "_id": "02771"} -{"city": "SWANSEA", "loc": [-71.212167, 41.74734], "pop": 15865, "state": "MA", "_id": "02777"} -{"city": "BERKLEY", "loc": [-71.076534, 41.835325], "pop": 4438, "state": "MA", "_id": "02779"} -{"city": "TAUNTON", "loc": [-71.10261, 41.905007], "pop": 44894, "state": "MA", "_id": "02780"} -{"city": "WESTPORT", "loc": [-71.089003, 41.621127], "pop": 14154, "state": "MA", "_id": "02790"} -{"city": "PEARL BEACH", "loc": [-82.560159, 42.630704], "pop": 11783, "state": "MI", "_id": "48001"} -{"city": "BERLIN", "loc": [-82.886809, 42.919864], "pop": 1333, "state": "MI", "_id": "48002"} -{"city": "ALMONT", "loc": [-83.036221, 42.926007], "pop": 5315, "state": "MI", "_id": "48003"} -{"city": "ANCHORVILLE", "loc": [-82.709392, 42.711817], "pop": 5360, "state": "MI", "_id": "48004"} -{"city": "ARMADA", "loc": [-82.889905, 42.840903], "pop": 4819, "state": "MI", "_id": "48005"} -{"city": "GREENWOOD", "loc": [-82.678141, 43.056424], "pop": 2325, "state": "MI", "_id": "48006"} -{"city": "BIRMINGHAM", "loc": [-83.213255, 42.544396], "pop": 19611, "state": "MI", "_id": "48009"} -{"city": "MUSSEY", "loc": [-82.925188, 43.019976], "pop": 4304, "state": "MI", "_id": "48014"} -{"city": "CENTER LINE", "loc": [-83.02477, 42.47879], "pop": 8923, "state": "MI", "_id": "48015"} -{"city": "CLAWSON", "loc": [-83.150317, 42.536468], "pop": 14008, "state": "MI", "_id": "48017"} -{"city": "EASTPOINTE", "loc": [-82.945896, 42.465756], "pop": 35073, "state": "MI", "_id": "48021"} -{"city": "EMMETT", "loc": [-82.785402, 42.987239], "pop": 2335, "state": "MI", "_id": "48022"} -{"city": "IRA", "loc": [-82.637673, 42.680651], "pop": 3781, "state": "MI", "_id": "48023"} -{"city": "FRANKLIN", "loc": [-83.251852, 42.521891], "pop": 13880, "state": "MI", "_id": "48025"} -{"city": "FRASER", "loc": [-82.946964, 42.542252], "pop": 20605, "state": "MI", "_id": "48026"} -{"city": "WALES", "loc": [-82.621728, 43.000058], "pop": 3285, "state": "MI", "_id": "48027"} -{"city": "HARSENS ISLAND", "loc": [-82.586049, 42.585043], "pop": 1091, "state": "MI", "_id": "48028"} -{"city": "HAZEL PARK", "loc": [-83.098182, 42.460768], "pop": 20218, "state": "MI", "_id": "48030"} -{"city": "GRANT TOWNSHIP", "loc": [-82.554734, 43.134584], "pop": 1498, "state": "MI", "_id": "48032"} -{"city": "SOUTHFIELD", "loc": [-83.288295, 42.477676], "pop": 28647, "state": "MI", "_id": "48034"} -{"city": "COTTRELLVILLE", "loc": [-82.514034, 42.721291], "pop": 10057, "state": "MI", "_id": "48039"} -{"city": "MARYSVILLE", "loc": [-82.481344, 42.913534], "pop": 8515, "state": "MI", "_id": "48040"} -{"city": "RILEY", "loc": [-82.769623, 42.905955], "pop": 3328, "state": "MI", "_id": "48041"} -{"city": "MOUNT CLEMENS", "loc": [-82.894052, 42.577562], "pop": 67489, "state": "MI", "_id": "48043"} -{"city": "MACOMB", "loc": [-82.946845, 42.616456], "pop": 51044, "state": "MI", "_id": "48044"} -{"city": "SELFRIDGE A N G", "loc": [-82.836395, 42.602743], "pop": 34104, "state": "MI", "_id": "48045"} -{"city": "CHESTERFIELD", "loc": [-82.780102, 42.675344], "pop": 22480, "state": "MI", "_id": "48047"} -{"city": "LENOX", "loc": [-82.820248, 42.732958], "pop": 4800, "state": "MI", "_id": "48048"} -{"city": "RUBY", "loc": [-82.538252, 43.026861], "pop": 2316, "state": "MI", "_id": "48049"} -{"city": "PORT HURON", "loc": [-82.459938, 42.995843], "pop": 58197, "state": "MI", "_id": "48060"} -{"city": "RICHMOND", "loc": [-82.730052, 42.812743], "pop": 12587, "state": "MI", "_id": "48062"} -{"city": "BRUCE", "loc": [-83.019204, 42.803513], "pop": 13552, "state": "MI", "_id": "48065"} -{"city": "ROSEVILLE", "loc": [-82.93868, 42.503423], "pop": 51539, "state": "MI", "_id": "48066"} -{"city": "ROYAL OAK", "loc": [-83.136584, 42.490579], "pop": 27820, "state": "MI", "_id": "48067"} -{"city": "PLEASANT RIDGE", "loc": [-83.143771, 42.47104], "pop": 2895, "state": "MI", "_id": "48069"} -{"city": "HUNTINGTON WOODS", "loc": [-83.174906, 42.482538], "pop": 9107, "state": "MI", "_id": "48070"} -{"city": "MADISON HEIGHTS", "loc": [-83.102699, 42.501605], "pop": 32196, "state": "MI", "_id": "48071"} -{"city": "BERKLEY", "loc": [-83.188683, 42.502755], "pop": 22323, "state": "MI", "_id": "48072"} -{"city": "ROYAL OAK", "loc": [-83.157027, 42.519047], "pop": 32093, "state": "MI", "_id": "48073"} -{"city": "KIMBALL", "loc": [-82.574516, 42.922072], "pop": 4496, "state": "MI", "_id": "48074"} -{"city": "SOUTHFIELD", "loc": [-83.225539, 42.463831], "pop": 22758, "state": "MI", "_id": "48075"} -{"city": "LATHRUP VILLAGE", "loc": [-83.22971, 42.499915], "pop": 29081, "state": "MI", "_id": "48076"} -{"city": "SAINT CLAIR", "loc": [-82.513256, 42.825453], "pop": 11681, "state": "MI", "_id": "48079"} -{"city": "SAINT CLAIR SHOR", "loc": [-82.900674, 42.463474], "pop": 25179, "state": "MI", "_id": "48080"} -{"city": "SAINT CLAIR SHOR", "loc": [-82.899954, 42.49538], "pop": 23479, "state": "MI", "_id": "48081"} -{"city": "SAINT CLAIR SHOR", "loc": [-82.886538, 42.526627], "pop": 19975, "state": "MI", "_id": "48082"} -{"city": "TROY", "loc": [-83.113771, 42.559668], "pop": 20459, "state": "MI", "_id": "48083"} -{"city": "TROY", "loc": [-83.179947, 42.562696], "pop": 13010, "state": "MI", "_id": "48084"} -{"city": "WARREN", "loc": [-82.997385, 42.468494], "pop": 35861, "state": "MI", "_id": "48089"} -{"city": "WARREN", "loc": [-83.059263, 42.466463], "pop": 33165, "state": "MI", "_id": "48091"} -{"city": "WARREN", "loc": [-83.064278, 42.512459], "pop": 25781, "state": "MI", "_id": "48092"} -{"city": "WARREN", "loc": [-82.996764, 42.514943], "pop": 50327, "state": "MI", "_id": "48093"} -{"city": "WASHINGTON", "loc": [-83.026805, 42.726202], "pop": 10975, "state": "MI", "_id": "48094"} -{"city": "BROCKWAY", "loc": [-82.797899, 43.122429], "pop": 5665, "state": "MI", "_id": "48097"} -{"city": "TROY", "loc": [-83.145001, 42.598118], "pop": 39379, "state": "MI", "_id": "48098"} -{"city": "ALLEN PARK", "loc": [-83.212001, 42.25223], "pop": 31167, "state": "MI", "_id": "48101"} -{"city": "ANN ARBOR", "loc": [-83.783998, 42.279379], "pop": 41263, "state": "MI", "_id": "48103"} -{"city": "ANN ARBOR", "loc": [-83.728156, 42.26939], "pop": 47564, "state": "MI", "_id": "48104"} -{"city": "ANN ARBOR", "loc": [-83.706756, 42.304247], "pop": 28543, "state": "MI", "_id": "48105"} -{"city": "ANN ARBOR", "loc": [-83.701481, 42.232782], "pop": 17948, "state": "MI", "_id": "48108"} -{"city": "ANN ARBOR", "loc": [-83.715363, 42.293], "pop": 0, "state": "MI", "_id": "48109"} -{"city": "BELLEVILLE", "loc": [-83.485425, 42.194858], "pop": 35436, "state": "MI", "_id": "48111"} -{"city": "BRIGHTON", "loc": [-83.775628, 42.537069], "pop": 37205, "state": "MI", "_id": "48116"} -{"city": "CARLETON", "loc": [-83.375502, 42.052941], "pop": 8144, "state": "MI", "_id": "48117"} -{"city": "CHELSEA", "loc": [-84.033392, 42.320692], "pop": 9504, "state": "MI", "_id": "48118"} -{"city": "DEARBORN", "loc": [-83.160488, 42.305295], "pop": 6325, "state": "MI", "_id": "48120"} -{"city": "MELVINDALE", "loc": [-83.182573, 42.281229], "pop": 11226, "state": "MI", "_id": "48122"} -{"city": "DEARBORN", "loc": [-83.253565, 42.294141], "pop": 34078, "state": "MI", "_id": "48124"} -{"city": "DEARBORN HEIGHTS", "loc": [-83.260603, 42.276824], "pop": 24715, "state": "MI", "_id": "48125"} -{"city": "DEARBORN", "loc": [-83.180065, 42.334882], "pop": 37807, "state": "MI", "_id": "48126"} -{"city": "DEARBORN HEIGHTS", "loc": [-83.286383, 42.335317], "pop": 36123, "state": "MI", "_id": "48127"} -{"city": "DEARBORN", "loc": [-83.270131, 42.319981], "pop": 11076, "state": "MI", "_id": "48128"} -{"city": "DEXTER", "loc": [-83.900028, 42.35832], "pop": 8216, "state": "MI", "_id": "48130"} -{"city": "DUNDEE", "loc": [-83.652165, 41.951435], "pop": 6521, "state": "MI", "_id": "48131"} -{"city": "ERIE", "loc": [-83.495797, 41.782935], "pop": 5101, "state": "MI", "_id": "48133"} -{"city": "FLAT ROCK", "loc": [-83.279525, 42.105521], "pop": 11180, "state": "MI", "_id": "48134"} -{"city": "GARDEN CITY", "loc": [-83.340236, 42.32415], "pop": 31846, "state": "MI", "_id": "48135"} -{"city": "GREGORY", "loc": [-84.046588, 42.450671], "pop": 3323, "state": "MI", "_id": "48137"} -{"city": "GROSSE ILE", "loc": [-83.153828, 42.13465], "pop": 9783, "state": "MI", "_id": "48138"} -{"city": "IDA", "loc": [-83.591561, 41.854928], "pop": 2690, "state": "MI", "_id": "48140"} -{"city": "INKSTER", "loc": [-83.31463, 42.294041], "pop": 30772, "state": "MI", "_id": "48141"} -{"city": "LAMBERTVILLE", "loc": [-83.625865, 41.753055], "pop": 7959, "state": "MI", "_id": "48144"} -{"city": "LA SALLE", "loc": [-83.471488, 41.858489], "pop": 3800, "state": "MI", "_id": "48145"} -{"city": "LINCOLN PARK", "loc": [-83.180688, 42.242211], "pop": 41763, "state": "MI", "_id": "48146"} -{"city": "LIVONIA", "loc": [-83.36494, 42.361503], "pop": 27644, "state": "MI", "_id": "48150"} -{"city": "LIVONIA", "loc": [-83.363603, 42.425793], "pop": 30199, "state": "MI", "_id": "48152"} -{"city": "LIVONIA", "loc": [-83.377157, 42.395796], "pop": 43007, "state": "MI", "_id": "48154"} -{"city": "LUNA PIER", "loc": [-83.436165, 41.815368], "pop": 1758, "state": "MI", "_id": "48157"} -{"city": "MANCHESTER", "loc": [-84.033247, 42.155545], "pop": 6163, "state": "MI", "_id": "48158"} -{"city": "MAYBEE", "loc": [-83.517902, 42.028822], "pop": 3822, "state": "MI", "_id": "48159"} -{"city": "MILAN", "loc": [-83.677636, 42.091373], "pop": 12411, "state": "MI", "_id": "48160"} -{"city": "DETROIT BEACH", "loc": [-83.404848, 41.92751], "pop": 55630, "state": "MI", "_id": "48161"} -{"city": "NEW BOSTON", "loc": [-83.358855, 42.144899], "pop": 9809, "state": "MI", "_id": "48164"} -{"city": "NEW HUDSON", "loc": [-83.634233, 42.507647], "pop": 4233, "state": "MI", "_id": "48165"} -{"city": "NEWPORT", "loc": [-83.280438, 41.976582], "pop": 5651, "state": "MI", "_id": "48166"} -{"city": "NORTHVILLE", "loc": [-83.479355, 42.426245], "pop": 30177, "state": "MI", "_id": "48167"} -{"city": "PINCKNEY", "loc": [-83.909918, 42.459579], "pop": 13071, "state": "MI", "_id": "48169"} -{"city": "PLYMOUTH", "loc": [-83.479946, 42.36882], "pop": 35389, "state": "MI", "_id": "48170"} -{"city": "GIBRALTAR", "loc": [-83.216196, 42.07918], "pop": 9594, "state": "MI", "_id": "48173"} -{"city": "ROMULUS", "loc": [-83.358288, 42.220304], "pop": 23471, "state": "MI", "_id": "48174"} -{"city": "SALINE", "loc": [-83.784936, 42.169844], "pop": 13356, "state": "MI", "_id": "48176"} -{"city": "SOUTH LYON", "loc": [-83.658951, 42.456678], "pop": 15616, "state": "MI", "_id": "48178"} -{"city": "SOUTH ROCKWOOD", "loc": [-83.266302, 42.062405], "pop": 3204, "state": "MI", "_id": "48179"} -{"city": "TAYLOR", "loc": [-83.267269, 42.231738], "pop": 70811, "state": "MI", "_id": "48180"} -{"city": "TEMPERANCE", "loc": [-83.579739, 41.768229], "pop": 15581, "state": "MI", "_id": "48182"} -{"city": "WOODHAVEN", "loc": [-83.218142, 42.134304], "pop": 38874, "state": "MI", "_id": "48183"} -{"city": "WAYNE", "loc": [-83.375812, 42.276805], "pop": 19911, "state": "MI", "_id": "48184"} -{"city": "WESTLAND", "loc": [-83.374908, 42.318882], "pop": 84712, "state": "MI", "_id": "48185"} -{"city": "CANTON", "loc": [-83.469524, 42.332013], "pop": 39308, "state": "MI", "_id": "48187"} -{"city": "CANTON", "loc": [-83.465007, 42.290997], "pop": 17741, "state": "MI", "_id": "48188"} -{"city": "WHITMORE LAKE", "loc": [-83.78282, 42.428904], "pop": 11639, "state": "MI", "_id": "48189"} -{"city": "WILLIS", "loc": [-83.568741, 42.129249], "pop": 2574, "state": "MI", "_id": "48191"} -{"city": "RIVERVIEW", "loc": [-83.182112, 42.196065], "pop": 50509, "state": "MI", "_id": "48192"} -{"city": "SOUTHGATE", "loc": [-83.199919, 42.204434], "pop": 30771, "state": "MI", "_id": "48195"} -{"city": "YPSILANTI", "loc": [-83.633621, 42.232544], "pop": 46790, "state": "MI", "_id": "48197"} -{"city": "YPSILANTI", "loc": [-83.582972, 42.24388], "pop": 39534, "state": "MI", "_id": "48198"} -{"city": "DETROIT", "loc": [-83.060398, 42.347429], "pop": 15920, "state": "MI", "_id": "48201"} -{"city": "DETROIT", "loc": [-83.079613, 42.377033], "pop": 24565, "state": "MI", "_id": "48202"} -{"city": "HIGHLAND PARK", "loc": [-83.100909, 42.421155], "pop": 53352, "state": "MI", "_id": "48203"} -{"city": "DETROIT", "loc": [-83.142151, 42.366098], "pop": 48856, "state": "MI", "_id": "48204"} -{"city": "DETROIT", "loc": [-82.981279, 42.431259], "pop": 65127, "state": "MI", "_id": "48205"} -{"city": "DETROIT", "loc": [-83.108695, 42.374893], "pop": 38035, "state": "MI", "_id": "48206"} -{"city": "DETROIT", "loc": [-83.027101, 42.352373], "pop": 25703, "state": "MI", "_id": "48207"} -{"city": "DETROIT", "loc": [-83.092711, 42.34947], "pop": 14925, "state": "MI", "_id": "48208"} -{"city": "DETROIT", "loc": [-83.115464, 42.309746], "pop": 38839, "state": "MI", "_id": "48209"} -{"city": "DETROIT", "loc": [-83.130281, 42.337603], "pop": 39833, "state": "MI", "_id": "48210"} -{"city": "DETROIT", "loc": [-83.040945, 42.380922], "pop": 13911, "state": "MI", "_id": "48211"} -{"city": "HAMTRAMCK", "loc": [-83.058265, 42.408117], "pop": 42830, "state": "MI", "_id": "48212"} -{"city": "DETROIT", "loc": [-82.99253, 42.39816], "pop": 52700, "state": "MI", "_id": "48213"} -{"city": "DETROIT", "loc": [-82.993798, 42.366944], "pop": 39584, "state": "MI", "_id": "48214"} -{"city": "DETROIT", "loc": [-82.951319, 42.377272], "pop": 24493, "state": "MI", "_id": "48215"} -{"city": "DETROIT", "loc": [-83.082656, 42.327467], "pop": 8592, "state": "MI", "_id": "48216"} -{"city": "DETROIT", "loc": [-83.154545, 42.271914], "pop": 11634, "state": "MI", "_id": "48217"} -{"city": "RIVER ROUGE", "loc": [-83.136432, 42.269229], "pop": 11314, "state": "MI", "_id": "48218"} -{"city": "DETROIT", "loc": [-83.249495, 42.426033], "pop": 63058, "state": "MI", "_id": "48219"} -{"city": "FERNDALE", "loc": [-83.13626, 42.458564], "pop": 28698, "state": "MI", "_id": "48220"} -{"city": "DETROIT", "loc": [-83.149976, 42.425998], "pop": 48068, "state": "MI", "_id": "48221"} -{"city": "DETROIT", "loc": [-83.245403, 42.394453], "pop": 39612, "state": "MI", "_id": "48223"} -{"city": "DETROIT", "loc": [-82.944061, 42.409808], "pop": 52938, "state": "MI", "_id": "48224"} -{"city": "HARPER WOODS", "loc": [-82.928885, 42.437658], "pop": 14937, "state": "MI", "_id": "48225"} -{"city": "DETROIT", "loc": [-83.048432, 42.333346], "pop": 5502, "state": "MI", "_id": "48226"} -{"city": "DETROIT", "loc": [-83.193732, 42.388303], "pop": 68390, "state": "MI", "_id": "48227"} -{"city": "DETROIT", "loc": [-83.216753, 42.35473], "pop": 67215, "state": "MI", "_id": "48228"} -{"city": "ECORSE", "loc": [-83.148943, 42.251881], "pop": 12164, "state": "MI", "_id": "48229"} -{"city": "GROSSE POINTE", "loc": [-82.924394, 42.384694], "pop": 19302, "state": "MI", "_id": "48230"} -{"city": "DETROIT", "loc": [-83.043383, 42.4337], "pop": 47768, "state": "MI", "_id": "48234"} -{"city": "DETROIT", "loc": [-83.195124, 42.426098], "pop": 57165, "state": "MI", "_id": "48235"} -{"city": "GROSSE POINTE", "loc": [-82.900248, 42.427404], "pop": 32076, "state": "MI", "_id": "48236"} -{"city": "OAK PARK", "loc": [-83.183993, 42.466151], "pop": 28884, "state": "MI", "_id": "48237"} -{"city": "DETROIT", "loc": [-83.141145, 42.395932], "pop": 50599, "state": "MI", "_id": "48238"} -{"city": "REDFORD", "loc": [-83.28895, 42.375554], "pop": 39218, "state": "MI", "_id": "48239"} -{"city": "REDFORD", "loc": [-83.30166, 42.426354], "pop": 20297, "state": "MI", "_id": "48240"} -{"city": "DETROIT", "loc": [-83.377081, 42.220718], "pop": 211, "state": "MI", "_id": "48242"} -{"city": "BLOOMFIELD TOWNS", "loc": [-83.2771, 42.545044], "pop": 15338, "state": "MI", "_id": "48301"} -{"city": "BLOOMFIELD TOWNS", "loc": [-83.296271, 42.583237], "pop": 17017, "state": "MI", "_id": "48302"} -{"city": "BLOOMFIELD TOWNS", "loc": [-83.234011, 42.593764], "pop": 16057, "state": "MI", "_id": "48304"} -{"city": "ROCHESTER HILLS", "loc": [-83.164215, 42.710684], "pop": 15755, "state": "MI", "_id": "48306"} -{"city": "ROCHESTER HILLS", "loc": [-83.129124, 42.660185], "pop": 31734, "state": "MI", "_id": "48307"} -{"city": "ROCHESTER HILLS", "loc": [-83.181842, 42.666848], "pop": 27450, "state": "MI", "_id": "48309"} -{"city": "STERLING HEIGHTS", "loc": [-83.070135, 42.564782], "pop": 42255, "state": "MI", "_id": "48310"} -{"city": "STERLING HEIGHTS", "loc": [-83.002896, 42.559203], "pop": 33163, "state": "MI", "_id": "48312"} -{"city": "STERLING HEIGHTS", "loc": [-82.999766, 42.600498], "pop": 33890, "state": "MI", "_id": "48313"} -{"city": "STERLING HEIGHTS", "loc": [-83.034455, 42.612352], "pop": 8502, "state": "MI", "_id": "48314"} -{"city": "SHELBY TOWNSHIP", "loc": [-82.996547, 42.663694], "pop": 11783, "state": "MI", "_id": "48315"} -{"city": "SHELBY TOWNSHIP", "loc": [-83.060928, 42.682668], "pop": 15039, "state": "MI", "_id": "48316"} -{"city": "SHELBY TOWNSHIP", "loc": [-83.048109, 42.640462], "pop": 24775, "state": "MI", "_id": "48317"} -{"city": "SYLVAN LAKE", "loc": [-83.339551, 42.610449], "pop": 4688, "state": "MI", "_id": "48320"} -{"city": "WEST BLOOMFIELD", "loc": [-83.379313, 42.542366], "pop": 26119, "state": "MI", "_id": "48322"} -{"city": "ORCHARD LAKE", "loc": [-83.369342, 42.570171], "pop": 15797, "state": "MI", "_id": "48323"} -{"city": "ORCHARD LAKE", "loc": [-83.395536, 42.598109], "pop": 13719, "state": "MI", "_id": "48324"} -{"city": "AUBURN HILLS", "loc": [-83.237489, 42.658345], "pop": 16184, "state": "MI", "_id": "48326"} -{"city": "WATERFORD", "loc": [-83.407602, 42.643751], "pop": 17213, "state": "MI", "_id": "48327"} -{"city": "WATERFORD", "loc": [-83.354624, 42.642944], "pop": 24330, "state": "MI", "_id": "48328"} -{"city": "WATERFORD", "loc": [-83.387869, 42.687663], "pop": 25125, "state": "MI", "_id": "48329"} -{"city": "FARMINGTON HILLS", "loc": [-83.405433, 42.510042], "pop": 19626, "state": "MI", "_id": "48331"} -{"city": "FARMINGTON HILLS", "loc": [-83.35198, 42.506798], "pop": 17513, "state": "MI", "_id": "48334"} -{"city": "FARMINGTON HILLS", "loc": [-83.400134, 42.463055], "pop": 19715, "state": "MI", "_id": "48335"} -{"city": "FARMINGTON HILLS", "loc": [-83.345465, 42.460938], "pop": 25680, "state": "MI", "_id": "48336"} -{"city": "PONTIAC", "loc": [-83.289335, 42.667955], "pop": 23663, "state": "MI", "_id": "48340"} -{"city": "PONTIAC", "loc": [-83.304149, 42.629449], "pop": 22685, "state": "MI", "_id": "48341"} -{"city": "PONTIAC", "loc": [-83.279236, 42.643856], "pop": 24663, "state": "MI", "_id": "48342"} -{"city": "INDEPENDENCE", "loc": [-83.405658, 42.721637], "pop": 17459, "state": "MI", "_id": "48346"} -{"city": "INDEPENDENCE", "loc": [-83.390568, 42.772414], "pop": 14635, "state": "MI", "_id": "48348"} -{"city": "SPRINGFIELD", "loc": [-83.520022, 42.75413], "pop": 5949, "state": "MI", "_id": "48350"} -{"city": "HARTLAND", "loc": [-83.714674, 42.63561], "pop": 4584, "state": "MI", "_id": "48353"} -{"city": "HIGHLAND", "loc": [-83.58951, 42.669187], "pop": 8161, "state": "MI", "_id": "48356"} -{"city": "HIGHLAND", "loc": [-83.637013, 42.659453], "pop": 7376, "state": "MI", "_id": "48357"} -{"city": "ORION", "loc": [-83.291701, 42.720779], "pop": 5264, "state": "MI", "_id": "48359"} -{"city": "ORION", "loc": [-83.282792, 42.742212], "pop": 6725, "state": "MI", "_id": "48360"} -{"city": "ORION", "loc": [-83.253208, 42.780598], "pop": 11862, "state": "MI", "_id": "48362"} -{"city": "OAKLAND", "loc": [-83.171116, 42.773179], "pop": 3716, "state": "MI", "_id": "48363"} -{"city": "ADDISON TOWNSHIP", "loc": [-83.138089, 42.836423], "pop": 3963, "state": "MI", "_id": "48367"} -{"city": "OXFORD", "loc": [-83.200455, 42.826451], "pop": 1267, "state": "MI", "_id": "48370"} -{"city": "OXFORD", "loc": [-83.282892, 42.822272], "pop": 13306, "state": "MI", "_id": "48371"} -{"city": "NOVI", "loc": [-83.522423, 42.473495], "pop": 5302, "state": "MI", "_id": "48374"} -{"city": "NOVI", "loc": [-83.457741, 42.460354], "pop": 19067, "state": "MI", "_id": "48375"} -{"city": "NOVI", "loc": [-83.472838, 42.513616], "pop": 6011, "state": "MI", "_id": "48377"} -{"city": "MILFORD", "loc": [-83.650796, 42.601951], "pop": 4050, "state": "MI", "_id": "48380"} -{"city": "MILFORD", "loc": [-83.592404, 42.575841], "pop": 10297, "state": "MI", "_id": "48381"} -{"city": "COMMERCE TOWNSHI", "loc": [-83.49467, 42.589424], "pop": 16939, "state": "MI", "_id": "48382"} -{"city": "WHITE LAKE", "loc": [-83.539838, 42.658004], "pop": 7528, "state": "MI", "_id": "48383"} -{"city": "WHITE LAKE", "loc": [-83.473809, 42.641003], "pop": 14778, "state": "MI", "_id": "48386"} -{"city": "WOLVERINE LAKE", "loc": [-83.479623, 42.550384], "pop": 15904, "state": "MI", "_id": "48390"} -{"city": "WIXOM", "loc": [-83.528486, 42.534037], "pop": 9337, "state": "MI", "_id": "48393"} -{"city": "APPLEGATE", "loc": [-82.647865, 43.361899], "pop": 1338, "state": "MI", "_id": "48401"} -{"city": "ATTICA", "loc": [-83.166842, 43.054673], "pop": 5780, "state": "MI", "_id": "48412"} -{"city": "BAD AXE", "loc": [-83.005378, 43.806745], "pop": 7321, "state": "MI", "_id": "48413"} -{"city": "BANCROFT", "loc": [-84.120725, 42.881957], "pop": 7199, "state": "MI", "_id": "48414"} -{"city": "BIRCH RUN", "loc": [-83.790287, 43.264868], "pop": 7961, "state": "MI", "_id": "48415"} -{"city": "BROWN CITY", "loc": [-82.997836, 43.217073], "pop": 4646, "state": "MI", "_id": "48416"} -{"city": "BURT", "loc": [-83.951073, 43.24043], "pop": 5487, "state": "MI", "_id": "48417"} -{"city": "BYRON", "loc": [-83.97297, 42.805928], "pop": 3085, "state": "MI", "_id": "48418"} -{"city": "CARSONVILLE", "loc": [-82.602169, 43.425805], "pop": 2621, "state": "MI", "_id": "48419"} -{"city": "CLIO", "loc": [-83.724949, 43.177885], "pop": 21345, "state": "MI", "_id": "48420"} -{"city": "COLUMBIAVILLE", "loc": [-83.381055, 43.150334], "pop": 6499, "state": "MI", "_id": "48421"} -{"city": "CROSWELL", "loc": [-82.633721, 43.262245], "pop": 5931, "state": "MI", "_id": "48422"} -{"city": "DAVISON", "loc": [-83.526771, 43.034777], "pop": 26713, "state": "MI", "_id": "48423"} -{"city": "DECKER", "loc": [-83.063791, 43.477532], "pop": 480, "state": "MI", "_id": "48426"} -{"city": "DECKERVILLE", "loc": [-82.719118, 43.515087], "pop": 4623, "state": "MI", "_id": "48427"} -{"city": "DRYDEN", "loc": [-83.150066, 42.937772], "pop": 3095, "state": "MI", "_id": "48428"} -{"city": "DURAND", "loc": [-83.987651, 42.91171], "pop": 7797, "state": "MI", "_id": "48429"} -{"city": "FENTON", "loc": [-83.729351, 42.785098], "pop": 25337, "state": "MI", "_id": "48430"} -{"city": "FILION", "loc": [-82.982483, 43.901362], "pop": 988, "state": "MI", "_id": "48432"} -{"city": "FLUSHING", "loc": [-83.842391, 43.071954], "pop": 23082, "state": "MI", "_id": "48433"} -{"city": "FOSTORIA", "loc": [-83.379593, 43.264504], "pop": 2242, "state": "MI", "_id": "48435"} -{"city": "GAINES", "loc": [-83.885488, 42.881333], "pop": 2931, "state": "MI", "_id": "48436"} -{"city": "GOODRICH", "loc": [-83.484459, 42.914734], "pop": 5184, "state": "MI", "_id": "48438"} -{"city": "GRAND BLANC", "loc": [-83.626414, 42.928163], "pop": 30329, "state": "MI", "_id": "48439"} -{"city": "HARBOR BEACH", "loc": [-82.688608, 43.831249], "pop": 4046, "state": "MI", "_id": "48441"} -{"city": "HOLLY", "loc": [-83.612737, 42.790494], "pop": 15119, "state": "MI", "_id": "48442"} -{"city": "IMLAY CITY", "loc": [-83.070799, 43.042512], "pop": 6493, "state": "MI", "_id": "48444"} -{"city": "KINDE", "loc": [-82.975529, 43.948003], "pop": 1082, "state": "MI", "_id": "48445"} -{"city": "LAPEER", "loc": [-83.333153, 43.057879], "pop": 27632, "state": "MI", "_id": "48446"} -{"city": "LENNON", "loc": [-83.927908, 42.969323], "pop": 3778, "state": "MI", "_id": "48449"} -{"city": "LEXINGTON", "loc": [-82.530103, 43.24348], "pop": 3815, "state": "MI", "_id": "48450"} -{"city": "LINDEN", "loc": [-83.799281, 42.810379], "pop": 10127, "state": "MI", "_id": "48451"} -{"city": "MARLETTE", "loc": [-83.057253, 43.339882], "pop": 4869, "state": "MI", "_id": "48453"} -{"city": "MELVIN", "loc": [-82.839277, 43.19304], "pop": 816, "state": "MI", "_id": "48454"} -{"city": "METAMORA", "loc": [-83.318371, 42.942365], "pop": 6719, "state": "MI", "_id": "48455"} -{"city": "MINDEN CITY", "loc": [-82.729863, 43.681393], "pop": 1637, "state": "MI", "_id": "48456"} -{"city": "MONTROSE", "loc": [-83.882411, 43.175381], "pop": 7181, "state": "MI", "_id": "48457"} -{"city": "MOUNT MORRIS", "loc": [-83.689523, 43.11601], "pop": 27347, "state": "MI", "_id": "48458"} -{"city": "NEW LOTHROP", "loc": [-83.985144, 43.138781], "pop": 3206, "state": "MI", "_id": "48460"} -{"city": "NORTH BRANCH", "loc": [-83.226664, 43.206887], "pop": 6106, "state": "MI", "_id": "48461"} -{"city": "ORTONVILLE", "loc": [-83.428811, 42.840943], "pop": 10315, "state": "MI", "_id": "48462"} -{"city": "OTISVILLE", "loc": [-83.517187, 43.170584], "pop": 4130, "state": "MI", "_id": "48463"} -{"city": "OTTER LAKE", "loc": [-83.424219, 43.218334], "pop": 2442, "state": "MI", "_id": "48464"} -{"city": "PALMS", "loc": [-82.701737, 43.625671], "pop": 611, "state": "MI", "_id": "48465"} -{"city": "PECK", "loc": [-82.81929, 43.26938], "pop": 1658, "state": "MI", "_id": "48466"} -{"city": "PORT AUSTIN", "loc": [-82.998427, 44.022292], "pop": 2508, "state": "MI", "_id": "48467"} -{"city": "PORT HOPE", "loc": [-82.752893, 43.927989], "pop": 1490, "state": "MI", "_id": "48468"} -{"city": "PORT SANILAC", "loc": [-82.546794, 43.43292], "pop": 254, "state": "MI", "_id": "48469"} -{"city": "RUTH", "loc": [-82.741396, 43.740436], "pop": 1162, "state": "MI", "_id": "48470"} -{"city": "SANDUSKY", "loc": [-82.840939, 43.405541], "pop": 4198, "state": "MI", "_id": "48471"} -{"city": "SNOVER", "loc": [-82.930063, 43.488649], "pop": 2301, "state": "MI", "_id": "48472"} -{"city": "SWARTZ CREEK", "loc": [-83.817005, 42.946776], "pop": 18263, "state": "MI", "_id": "48473"} -{"city": "UBLY", "loc": [-82.964013, 43.689631], "pop": 2802, "state": "MI", "_id": "48475"} -{"city": "FLINT", "loc": [-83.687768, 43.012321], "pop": 1359, "state": "MI", "_id": "48502"} -{"city": "FLINT", "loc": [-83.691429, 43.012836], "pop": 33451, "state": "MI", "_id": "48503"} -{"city": "NORTHWEST", "loc": [-83.729908, 43.04247], "pop": 40445, "state": "MI", "_id": "48504"} -{"city": "FLINT", "loc": [-83.700093, 43.063369], "pop": 42423, "state": "MI", "_id": "48505"} -{"city": "NORTHEAST", "loc": [-83.640192, 43.052596], "pop": 35154, "state": "MI", "_id": "48506"} -{"city": "FLINT", "loc": [-83.688999, 42.97303], "pop": 37656, "state": "MI", "_id": "48507"} -{"city": "NORTHEAST", "loc": [-83.606295, 43.024493], "pop": 9432, "state": "MI", "_id": "48509"} -{"city": "SOUTHEAST", "loc": [-83.610424, 42.993847], "pop": 6081, "state": "MI", "_id": "48519"} -{"city": "SOUTHEAST", "loc": [-83.671064, 42.97268], "pop": 11092, "state": "MI", "_id": "48529"} -{"city": "NORTHWEST", "loc": [-83.768576, 43.01021], "pop": 20367, "state": "MI", "_id": "48532"} -{"city": "SAGINAW", "loc": [-83.915626, 43.404692], "pop": 55547, "state": "MI", "_id": "48601"} -{"city": "SAGINAW", "loc": [-83.974455, 43.424838], "pop": 34096, "state": "MI", "_id": "48602"} -{"city": "SAGINAW", "loc": [-84.03028, 43.43251], "pop": 49303, "state": "MI", "_id": "48603"} -{"city": "SAGINAW", "loc": [-83.951421, 43.473223], "pop": 11937, "state": "MI", "_id": "48604"} -{"city": "SAGINAW", "loc": [-83.931872, 43.430141], "pop": 3436, "state": "MI", "_id": "48607"} -{"city": "ALGER", "loc": [-84.18719, 44.139488], "pop": 2015, "state": "MI", "_id": "48610"} -{"city": "AUBURN", "loc": [-84.10267, 43.607988], "pop": 6154, "state": "MI", "_id": "48611"} -{"city": "BEAVERTON", "loc": [-84.424059, 43.886576], "pop": 9682, "state": "MI", "_id": "48612"} -{"city": "BENTLEY", "loc": [-84.144738, 43.886028], "pop": 1022, "state": "MI", "_id": "48613"} -{"city": "BRANT", "loc": [-84.297849, 43.25484], "pop": 1572, "state": "MI", "_id": "48614"} -{"city": "BRECKENRIDGE", "loc": [-84.502319, 43.393463], "pop": 2266, "state": "MI", "_id": "48615"} -{"city": "CHESANING", "loc": [-84.112156, 43.182387], "pop": 4567, "state": "MI", "_id": "48616"} -{"city": "CLARE", "loc": [-84.763463, 43.822318], "pop": 6635, "state": "MI", "_id": "48617"} -{"city": "COLEMAN", "loc": [-84.591058, 43.749397], "pop": 5866, "state": "MI", "_id": "48618"} -{"city": "COMINS", "loc": [-84.026061, 44.826354], "pop": 515, "state": "MI", "_id": "48619"} -{"city": "EDENVILLE", "loc": [-84.396227, 43.802757], "pop": 237, "state": "MI", "_id": "48620"} -{"city": "FAIRVIEW", "loc": [-84.052532, 44.72046], "pop": 1785, "state": "MI", "_id": "48621"} -{"city": "FARWELL", "loc": [-84.875402, 43.834163], "pop": 4456, "state": "MI", "_id": "48622"} -{"city": "FREELAND", "loc": [-84.182173, 43.516134], "pop": 10892, "state": "MI", "_id": "48623"} -{"city": "GLADWIN", "loc": [-84.496801, 44.029618], "pop": 11790, "state": "MI", "_id": "48624"} -{"city": "HARRISON", "loc": [-84.77289, 44.028478], "pop": 9223, "state": "MI", "_id": "48625"} -{"city": "HEMLOCK", "loc": [-84.226563, 43.409911], "pop": 5711, "state": "MI", "_id": "48626"} -{"city": "HOPE", "loc": [-84.329605, 43.788167], "pop": 1339, "state": "MI", "_id": "48628"} -{"city": "HOUGHTON LAKE", "loc": [-84.742175, 44.341327], "pop": 5625, "state": "MI", "_id": "48629"} -{"city": "KAWKAWLIN", "loc": [-83.992657, 43.679399], "pop": 3935, "state": "MI", "_id": "48631"} -{"city": "LAKE", "loc": [-84.978597, 43.850164], "pop": 7779, "state": "MI", "_id": "48632"} -{"city": "LINWOOD", "loc": [-84.013341, 43.737448], "pop": 4719, "state": "MI", "_id": "48634"} -{"city": "LUPTON", "loc": [-83.990473, 44.397578], "pop": 1784, "state": "MI", "_id": "48635"} -{"city": "LUZERNE", "loc": [-84.246742, 44.629594], "pop": 744, "state": "MI", "_id": "48636"} -{"city": "MERRILL", "loc": [-84.330757, 43.393892], "pop": 2702, "state": "MI", "_id": "48637"} -{"city": "MIDLAND", "loc": [-84.26796, 43.637562], "pop": 26370, "state": "MI", "_id": "48640"} -{"city": "MIDLAND", "loc": [-84.197941, 43.637488], "pop": 24643, "state": "MI", "_id": "48642"} -{"city": "MIO", "loc": [-84.135264, 44.666481], "pop": 3508, "state": "MI", "_id": "48647"} -{"city": "OAKLEY", "loc": [-84.209379, 43.150533], "pop": 3506, "state": "MI", "_id": "48649"} -{"city": "PINCONNING", "loc": [-84.008162, 43.849079], "pop": 8659, "state": "MI", "_id": "48650"} -{"city": "PRUDENVILLE", "loc": [-84.662747, 44.297394], "pop": 4484, "state": "MI", "_id": "48651"} -{"city": "RHODES", "loc": [-84.213402, 43.851745], "pop": 2034, "state": "MI", "_id": "48652"} -{"city": "ROSCOMMON", "loc": [-84.660092, 44.483908], "pop": 8311, "state": "MI", "_id": "48653"} -{"city": "ROSE CITY", "loc": [-84.125562, 44.41672], "pop": 2962, "state": "MI", "_id": "48654"} -{"city": "SAINT CHARLES", "loc": [-84.159785, 43.286271], "pop": 6272, "state": "MI", "_id": "48655"} -{"city": "SAINT HELEN", "loc": [-84.424704, 44.366489], "pop": 3686, "state": "MI", "_id": "48656"} -{"city": "SANFORD", "loc": [-84.395446, 43.720352], "pop": 5154, "state": "MI", "_id": "48657"} -{"city": "STANDISH", "loc": [-83.943297, 43.973287], "pop": 5314, "state": "MI", "_id": "48658"} -{"city": "STERLING", "loc": [-84.012567, 44.067837], "pop": 2855, "state": "MI", "_id": "48659"} -{"city": "WEST BRANCH", "loc": [-84.228623, 44.279032], "pop": 7739, "state": "MI", "_id": "48661"} -{"city": "WHEELER", "loc": [-84.424335, 43.396224], "pop": 2144, "state": "MI", "_id": "48662"} -{"city": "AKRON", "loc": [-83.539262, 43.584373], "pop": 1694, "state": "MI", "_id": "48701"} -{"city": "AU GRES", "loc": [-83.702024, 44.033802], "pop": 1568, "state": "MI", "_id": "48703"} -{"city": "BARTON CITY", "loc": [-83.599372, 44.701956], "pop": 755, "state": "MI", "_id": "48705"} -{"city": "UNIVERSITY CENTE", "loc": [-83.919885, 43.612165], "pop": 41677, "state": "MI", "_id": "48706"} -{"city": "BAY CITY", "loc": [-83.878073, 43.58205], "pop": 29918, "state": "MI", "_id": "48708"} -{"city": "BAY PORT", "loc": [-83.352461, 43.837744], "pop": 1693, "state": "MI", "_id": "48720"} -{"city": "BLACK RIVER", "loc": [-83.34071, 44.813842], "pop": 373, "state": "MI", "_id": "48721"} -{"city": "BRIDGEPORT", "loc": [-83.854906, 43.355309], "pop": 3792, "state": "MI", "_id": "48722"} -{"city": "CARO", "loc": [-83.383469, 43.483272], "pop": 11389, "state": "MI", "_id": "48723"} -{"city": "CASEVILLE", "loc": [-83.265924, 43.94292], "pop": 2939, "state": "MI", "_id": "48725"} -{"city": "CASS CITY", "loc": [-83.173264, 43.579677], "pop": 7414, "state": "MI", "_id": "48726"} -{"city": "CLIFFORD", "loc": [-83.174105, 43.309953], "pop": 1444, "state": "MI", "_id": "48727"} -{"city": "CURRAN", "loc": [-83.831989, 44.733606], "pop": 188, "state": "MI", "_id": "48728"} -{"city": "DEFORD", "loc": [-83.170244, 43.473482], "pop": 556, "state": "MI", "_id": "48729"} -{"city": "EAST TAWAS", "loc": [-83.477629, 44.300823], "pop": 5006, "state": "MI", "_id": "48730"} -{"city": "ELKTON", "loc": [-83.178642, 43.834437], "pop": 2292, "state": "MI", "_id": "48731"} -{"city": "ESSEXVILLE", "loc": [-83.821659, 43.606908], "pop": 12019, "state": "MI", "_id": "48732"} -{"city": "FAIRGROVE", "loc": [-83.583534, 43.512574], "pop": 3175, "state": "MI", "_id": "48733"} -{"city": "FRANKENMUTH", "loc": [-83.747482, 43.340965], "pop": 6931, "state": "MI", "_id": "48734"} -{"city": "GAGETOWN", "loc": [-83.262788, 43.654251], "pop": 759, "state": "MI", "_id": "48735"} -{"city": "GLENNIE", "loc": [-83.689948, 44.558234], "pop": 1153, "state": "MI", "_id": "48737"} -{"city": "GREENBUSH", "loc": [-83.326883, 44.548044], "pop": 1121, "state": "MI", "_id": "48738"} -{"city": "HALE", "loc": [-83.835941, 44.38189], "pop": 3317, "state": "MI", "_id": "48739"} -{"city": "HARRISVILLE", "loc": [-83.34243, 44.654595], "pop": 2250, "state": "MI", "_id": "48740"} -{"city": "KINGSTON", "loc": [-83.184727, 43.398153], "pop": 2475, "state": "MI", "_id": "48741"} -{"city": "LINCOLN", "loc": [-83.394669, 44.711124], "pop": 1167, "state": "MI", "_id": "48742"} -{"city": "LONG LAKE", "loc": [-83.817058, 44.448465], "pop": 349, "state": "MI", "_id": "48743"} -{"city": "MAYVILLE", "loc": [-83.372529, 43.356156], "pop": 4271, "state": "MI", "_id": "48744"} -{"city": "MIKADO", "loc": [-83.435518, 44.583275], "pop": 1392, "state": "MI", "_id": "48745"} -{"city": "MILLINGTON", "loc": [-83.561944, 43.271772], "pop": 8752, "state": "MI", "_id": "48746"} -{"city": "MUNGER", "loc": [-83.767221, 43.528585], "pop": 1898, "state": "MI", "_id": "48747"} -{"city": "NATIONAL CITY", "loc": [-83.683948, 44.313746], "pop": 1823, "state": "MI", "_id": "48748"} -{"city": "OMER", "loc": [-83.842956, 44.049939], "pop": 965, "state": "MI", "_id": "48749"} -{"city": "OSCODA", "loc": [-83.361908, 44.446485], "pop": 14188, "state": "MI", "_id": "48750"} -{"city": "OWENDALE", "loc": [-83.230703, 43.720648], "pop": 1833, "state": "MI", "_id": "48754"} -{"city": "PIGEON", "loc": [-83.275508, 43.817909], "pop": 1891, "state": "MI", "_id": "48755"} -{"city": "PRESCOTT", "loc": [-84.021197, 44.20999], "pop": 4816, "state": "MI", "_id": "48756"} -{"city": "REESE", "loc": [-83.701529, 43.453094], "pop": 2830, "state": "MI", "_id": "48757"} -{"city": "SEBEWAING", "loc": [-83.436622, 43.728888], "pop": 3203, "state": "MI", "_id": "48759"} -{"city": "SILVERWOOD", "loc": [-83.271974, 43.31413], "pop": 964, "state": "MI", "_id": "48760"} -{"city": "SOUTH BRANCH", "loc": [-83.868574, 44.501403], "pop": 968, "state": "MI", "_id": "48761"} -{"city": "SPRUCE", "loc": [-83.504391, 44.822443], "pop": 1531, "state": "MI", "_id": "48762"} -{"city": "TAWAS CITY", "loc": [-83.544905, 44.267485], "pop": 3868, "state": "MI", "_id": "48763"} -{"city": "TURNER", "loc": [-83.650679, 44.10543], "pop": 2664, "state": "MI", "_id": "48765"} -{"city": "TWINING", "loc": [-83.849118, 44.12929], "pop": 934, "state": "MI", "_id": "48766"} -{"city": "UNIONVILLE", "loc": [-83.469898, 43.647341], "pop": 2111, "state": "MI", "_id": "48767"} -{"city": "VASSAR", "loc": [-83.584449, 43.369052], "pop": 9979, "state": "MI", "_id": "48768"} -{"city": "WHITTEMORE", "loc": [-83.806842, 44.232514], "pop": 1754, "state": "MI", "_id": "48770"} -{"city": "ALMA", "loc": [-84.663484, 43.380877], "pop": 11018, "state": "MI", "_id": "48801"} -{"city": "ASHLEY", "loc": [-84.48796, 43.189103], "pop": 2278, "state": "MI", "_id": "48806"} -{"city": "BANNISTER", "loc": [-84.359679, 43.161537], "pop": 1499, "state": "MI", "_id": "48807"} -{"city": "BATH", "loc": [-84.454547, 42.820563], "pop": 3695, "state": "MI", "_id": "48808"} -{"city": "BELDING", "loc": [-85.231272, 43.088546], "pop": 8995, "state": "MI", "_id": "48809"} -{"city": "CARSON CITY", "loc": [-84.865334, 43.169496], "pop": 4190, "state": "MI", "_id": "48811"} -{"city": "CHARLOTTE", "loc": [-84.835181, 42.570169], "pop": 17424, "state": "MI", "_id": "48813"} -{"city": "CLARKSVILLE", "loc": [-85.249384, 42.830177], "pop": 2027, "state": "MI", "_id": "48815"} -{"city": "CORUNNA", "loc": [-84.027618, 43.041402], "pop": 3906, "state": "MI", "_id": "48817"} -{"city": "CRYSTAL", "loc": [-84.899328, 43.262382], "pop": 2279, "state": "MI", "_id": "48818"} -{"city": "DANSVILLE", "loc": [-84.293932, 42.550485], "pop": 2338, "state": "MI", "_id": "48819"} -{"city": "DEWITT", "loc": [-84.579654, 42.842784], "pop": 10811, "state": "MI", "_id": "48820"} -{"city": "DIMONDALE", "loc": [-84.648593, 42.650094], "pop": 5131, "state": "MI", "_id": "48821"} -{"city": "EAGLE", "loc": [-84.758971, 42.826306], "pop": 1937, "state": "MI", "_id": "48822"} -{"city": "EAST LANSING", "loc": [-84.476409, 42.738805], "pop": 61997, "state": "MI", "_id": "48823"} -{"city": "EATON RAPIDS", "loc": [-84.656545, 42.516624], "pop": 12987, "state": "MI", "_id": "48827"} -{"city": "EDMORE", "loc": [-85.028003, 43.411578], "pop": 2515, "state": "MI", "_id": "48829"} -{"city": "CARLAND", "loc": [-84.390965, 43.086918], "pop": 2487, "state": "MI", "_id": "48831"} -{"city": "ELWELL", "loc": [-84.763103, 43.410565], "pop": 1006, "state": "MI", "_id": "48832"} -{"city": "FENWICK", "loc": [-85.066566, 43.149682], "pop": 1440, "state": "MI", "_id": "48834"} -{"city": "FOWLER", "loc": [-84.759969, 42.994144], "pop": 2253, "state": "MI", "_id": "48835"} -{"city": "FOWLERVILLE", "loc": [-84.072085, 42.661438], "pop": 8519, "state": "MI", "_id": "48836"} -{"city": "GRAND LEDGE", "loc": [-84.737314, 42.752924], "pop": 16000, "state": "MI", "_id": "48837"} -{"city": "GREENVILLE", "loc": [-85.249705, 43.17926], "pop": 12208, "state": "MI", "_id": "48838"} -{"city": "HASLETT", "loc": [-84.398887, 42.753088], "pop": 10679, "state": "MI", "_id": "48840"} -{"city": "HENDERSON", "loc": [-84.185777, 43.081708], "pop": 534, "state": "MI", "_id": "48841"} -{"city": "HOLT", "loc": [-84.524232, 42.639401], "pop": 15960, "state": "MI", "_id": "48842"} -{"city": "HOWELL", "loc": [-83.92481, 42.615933], "pop": 28075, "state": "MI", "_id": "48843"} -{"city": "HUBBARDSTON", "loc": [-84.817282, 43.082776], "pop": 1407, "state": "MI", "_id": "48845"} -{"city": "IONIA", "loc": [-85.070985, 42.98592], "pop": 18602, "state": "MI", "_id": "48846"} -{"city": "ITHACA", "loc": [-84.60883, 43.282808], "pop": 6418, "state": "MI", "_id": "48847"} -{"city": "LAINGSBURG", "loc": [-84.352991, 42.86271], "pop": 8214, "state": "MI", "_id": "48848"} -{"city": "LAKE ODESSA", "loc": [-85.135667, 42.786335], "pop": 5386, "state": "MI", "_id": "48849"} -{"city": "LAKEVIEW", "loc": [-85.292421, 43.42694], "pop": 6000, "state": "MI", "_id": "48850"} -{"city": "LYONS", "loc": [-84.920943, 42.963429], "pop": 2488, "state": "MI", "_id": "48851"} -{"city": "MASON", "loc": [-84.45609, 42.579588], "pop": 17286, "state": "MI", "_id": "48854"} -{"city": "MIDDLETON", "loc": [-84.755221, 43.168911], "pop": 1181, "state": "MI", "_id": "48856"} -{"city": "MORRICE", "loc": [-84.176771, 42.83851], "pop": 718, "state": "MI", "_id": "48857"} -{"city": "MOUNT PLEASANT", "loc": [-84.773571, 43.601295], "pop": 33732, "state": "MI", "_id": "48858"} -{"city": "MUIR", "loc": [-84.93909, 43.043864], "pop": 1885, "state": "MI", "_id": "48860"} -{"city": "MULLIKEN", "loc": [-84.897911, 42.737657], "pop": 1903, "state": "MI", "_id": "48861"} -{"city": "OKEMOS", "loc": [-84.418696, 42.705341], "pop": 17587, "state": "MI", "_id": "48864"} -{"city": "ORLEANS", "loc": [-85.116547, 43.089459], "pop": 1678, "state": "MI", "_id": "48865"} -{"city": "OVID", "loc": [-84.364939, 42.996927], "pop": 5004, "state": "MI", "_id": "48866"} -{"city": "OWOSSO", "loc": [-84.159486, 42.993407], "pop": 29958, "state": "MI", "_id": "48867"} -{"city": "PERRINTON", "loc": [-84.665984, 43.16492], "pop": 1791, "state": "MI", "_id": "48871"} -{"city": "PERRY", "loc": [-84.231346, 42.820012], "pop": 5746, "state": "MI", "_id": "48872"} -{"city": "PEWAMO", "loc": [-84.849217, 43.000747], "pop": 654, "state": "MI", "_id": "48873"} -{"city": "PORTLAND", "loc": [-84.913933, 42.862414], "pop": 8824, "state": "MI", "_id": "48875"} -{"city": "POTTERVILLE", "loc": [-84.734589, 42.639779], "pop": 2795, "state": "MI", "_id": "48876"} -{"city": "RIVERDALE", "loc": [-84.826607, 43.409817], "pop": 1211, "state": "MI", "_id": "48877"} -{"city": "ROSEBUSH", "loc": [-84.783299, 43.68427], "pop": 2025, "state": "MI", "_id": "48878"} -{"city": "SAINT JOHNS", "loc": [-84.571934, 43.005924], "pop": 16472, "state": "MI", "_id": "48879"} -{"city": "SAINT LOUIS", "loc": [-84.595239, 43.42777], "pop": 7552, "state": "MI", "_id": "48880"} -{"city": "SARANAC", "loc": [-85.229938, 42.928534], "pop": 5911, "state": "MI", "_id": "48881"} -{"city": "SHEPHERD", "loc": [-84.587317, 43.565668], "pop": 17397, "state": "MI", "_id": "48883"} -{"city": "SHERIDAN", "loc": [-85.046838, 43.212632], "pop": 3030, "state": "MI", "_id": "48884"} -{"city": "SIDNEY", "loc": [-85.120671, 43.23579], "pop": 642, "state": "MI", "_id": "48885"} -{"city": "SIX LAKES", "loc": [-85.141569, 43.433714], "pop": 2132, "state": "MI", "_id": "48886"} -{"city": "STANTON", "loc": [-85.099548, 43.305841], "pop": 6997, "state": "MI", "_id": "48888"} -{"city": "SUMNER", "loc": [-84.790662, 43.309144], "pop": 2771, "state": "MI", "_id": "48889"} -{"city": "SUNFIELD", "loc": [-84.981305, 42.769314], "pop": 2282, "state": "MI", "_id": "48890"} -{"city": "VESTABURG", "loc": [-84.908168, 43.387025], "pop": 3540, "state": "MI", "_id": "48891"} -{"city": "WEBBERVILLE", "loc": [-84.17013, 42.662981], "pop": 4561, "state": "MI", "_id": "48892"} -{"city": "WEIDMAN", "loc": [-85.004567, 43.645284], "pop": 708, "state": "MI", "_id": "48893"} -{"city": "WESTPHALIA", "loc": [-84.785567, 42.912308], "pop": 2099, "state": "MI", "_id": "48894"} -{"city": "WILLIAMSTON", "loc": [-84.292596, 42.696652], "pop": 9270, "state": "MI", "_id": "48895"} -{"city": "WOODLAND", "loc": [-85.13261, 42.705654], "pop": 1405, "state": "MI", "_id": "48897"} -{"city": "LANSING", "loc": [-84.558043, 42.763464], "pop": 28434, "state": "MI", "_id": "48906"} -{"city": "LANSING", "loc": [-84.549005, 42.700784], "pop": 37654, "state": "MI", "_id": "48910"} -{"city": "LANSING", "loc": [-84.577168, 42.679727], "pop": 39930, "state": "MI", "_id": "48911"} -{"city": "LANSING", "loc": [-84.524414, 42.737115], "pop": 19898, "state": "MI", "_id": "48912"} -{"city": "LANSING", "loc": [-84.570398, 42.739074], "pop": 11703, "state": "MI", "_id": "48915"} -{"city": "LANSING", "loc": [-84.62439, 42.737621], "pop": 28475, "state": "MI", "_id": "48917"} -{"city": "LANSING", "loc": [-84.557142, 42.733429], "pop": 2780, "state": "MI", "_id": "48933"} -{"city": "KALAMAZOO", "loc": [-85.545653, 42.273565], "pop": 45278, "state": "MI", "_id": "49001"} -{"city": "KALAMAZOO", "loc": [-85.595691, 42.207482], "pop": 40439, "state": "MI", "_id": "49002"} -{"city": "PARCHMENT", "loc": [-85.541959, 42.326538], "pop": 15968, "state": "MI", "_id": "49004"} -{"city": "KALAMAZOO", "loc": [-85.613722, 42.295688], "pop": 44854, "state": "MI", "_id": "49007"} -{"city": "KALAMAZOO", "loc": [-85.609645, 42.262432], "pop": 19435, "state": "MI", "_id": "49008"} -{"city": "KALAMAZOO", "loc": [-85.686333, 42.280947], "pop": 22218, "state": "MI", "_id": "49009"} -{"city": "ALLEGAN", "loc": [-85.86608, 42.525609], "pop": 15532, "state": "MI", "_id": "49010"} -{"city": "ATHENS", "loc": [-85.231742, 42.102962], "pop": 2436, "state": "MI", "_id": "49011"} -{"city": "AUGUSTA", "loc": [-85.354012, 42.356313], "pop": 2896, "state": "MI", "_id": "49012"} -{"city": "BANGOR", "loc": [-86.131096, 42.33122], "pop": 8743, "state": "MI", "_id": "49013"} -{"city": "BATTLE CREEK", "loc": [-85.212825, 42.302806], "pop": 29828, "state": "MI", "_id": "49015"} -{"city": "BATTLE CREEK", "loc": [-85.181106, 42.332218], "pop": 62035, "state": "MI", "_id": "49017"} -{"city": "BELLEVUE", "loc": [-85.048867, 42.452474], "pop": 6911, "state": "MI", "_id": "49021"} -{"city": "BENTON HARBOR", "loc": [-86.423417, 42.108594], "pop": 37550, "state": "MI", "_id": "49022"} -{"city": "BLOOMINGDALE", "loc": [-85.956757, 42.384232], "pop": 1958, "state": "MI", "_id": "49026"} -{"city": "BRONSON", "loc": [-85.183767, 41.864316], "pop": 7024, "state": "MI", "_id": "49028"} -{"city": "BURLINGTON", "loc": [-85.105, 42.123859], "pop": 1898, "state": "MI", "_id": "49029"} -{"city": "BURR OAK", "loc": [-85.334536, 41.845898], "pop": 3192, "state": "MI", "_id": "49030"} -{"city": "CASSOPOLIS", "loc": [-85.992273, 41.896805], "pop": 6622, "state": "MI", "_id": "49031"} -{"city": "CENTREVILLE", "loc": [-85.496299, 41.921685], "pop": 3606, "state": "MI", "_id": "49032"} -{"city": "CERESCO", "loc": [-85.11285, 42.212741], "pop": 1128, "state": "MI", "_id": "49033"} -{"city": "CLIMAX", "loc": [-85.323832, 42.233979], "pop": 983, "state": "MI", "_id": "49034"} -{"city": "COLDWATER", "loc": [-85.005682, 41.925464], "pop": 20278, "state": "MI", "_id": "49036"} -{"city": "COLOMA", "loc": [-86.32247, 42.202952], "pop": 9987, "state": "MI", "_id": "49038"} -{"city": "COLON", "loc": [-85.330588, 41.957605], "pop": 2800, "state": "MI", "_id": "49040"} -{"city": "CONSTANTINE", "loc": [-85.657094, 41.846029], "pop": 5737, "state": "MI", "_id": "49042"} -{"city": "COVERT", "loc": [-86.274294, 42.291074], "pop": 2544, "state": "MI", "_id": "49043"} -{"city": "DECATUR", "loc": [-86.033808, 42.101219], "pop": 8320, "state": "MI", "_id": "49045"} -{"city": "DELTON", "loc": [-85.406706, 42.514102], "pop": 7306, "state": "MI", "_id": "49046"} -{"city": "DOWAGIAC", "loc": [-86.116766, 41.990965], "pop": 14921, "state": "MI", "_id": "49047"} -{"city": "DOWLING", "loc": [-85.249452, 42.501478], "pop": 909, "state": "MI", "_id": "49050"} -{"city": "EAST LEROY", "loc": [-85.231083, 42.196125], "pop": 1915, "state": "MI", "_id": "49051"} -{"city": "FULTON", "loc": [-85.322659, 42.139086], "pop": 720, "state": "MI", "_id": "49052"} -{"city": "GALESBURG", "loc": [-85.423665, 42.294843], "pop": 4820, "state": "MI", "_id": "49053"} -{"city": "GOBLES", "loc": [-85.853649, 42.370182], "pop": 4476, "state": "MI", "_id": "49055"} -{"city": "GRAND JUNCTION", "loc": [-86.054052, 42.376081], "pop": 2100, "state": "MI", "_id": "49056"} -{"city": "HARTFORD", "loc": [-86.168703, 42.208807], "pop": 5826, "state": "MI", "_id": "49057"} -{"city": "HASTINGS", "loc": [-85.293687, 42.643007], "pop": 15043, "state": "MI", "_id": "49058"} -{"city": "HICKORY CORNERS", "loc": [-85.399784, 42.423682], "pop": 1823, "state": "MI", "_id": "49060"} -{"city": "JONES", "loc": [-85.834111, 41.912903], "pop": 2121, "state": "MI", "_id": "49061"} -{"city": "LAWRENCE", "loc": [-86.052543, 42.207635], "pop": 3030, "state": "MI", "_id": "49064"} -{"city": "LAWTON", "loc": [-85.828966, 42.154462], "pop": 5933, "state": "MI", "_id": "49065"} -{"city": "LEONIDAS", "loc": [-85.349683, 42.029394], "pop": 765, "state": "MI", "_id": "49066"} -{"city": "MARCELLUS", "loc": [-85.798776, 42.027461], "pop": 3481, "state": "MI", "_id": "49067"} -{"city": "MARSHALL", "loc": [-84.95828, 42.272047], "pop": 14844, "state": "MI", "_id": "49068"} -{"city": "MARTIN", "loc": [-85.610646, 42.548321], "pop": 2366, "state": "MI", "_id": "49070"} -{"city": "MATTAWAN", "loc": [-85.794281, 42.245069], "pop": 6461, "state": "MI", "_id": "49071"} -{"city": "MENDON", "loc": [-85.472697, 42.014262], "pop": 3598, "state": "MI", "_id": "49072"} -{"city": "NASHVILLE", "loc": [-85.122029, 42.593721], "pop": 5517, "state": "MI", "_id": "49073"} -{"city": "OLIVET", "loc": [-84.897312, 42.445947], "pop": 4084, "state": "MI", "_id": "49076"} -{"city": "OTSEGO", "loc": [-85.703497, 42.472334], "pop": 10614, "state": "MI", "_id": "49078"} -{"city": "PAW PAW", "loc": [-85.900488, 42.234931], "pop": 11455, "state": "MI", "_id": "49079"} -{"city": "PLAINWELL", "loc": [-85.59936, 42.454379], "pop": 15308, "state": "MI", "_id": "49080"} -{"city": "QUINCY", "loc": [-84.849295, 41.970694], "pop": 8671, "state": "MI", "_id": "49082"} -{"city": "RICHLAND", "loc": [-85.444651, 42.375689], "pop": 5676, "state": "MI", "_id": "49083"} -{"city": "SAINT JOSEPH", "loc": [-86.478341, 42.063959], "pop": 22984, "state": "MI", "_id": "49085"} -{"city": "SCHOOLCRAFT", "loc": [-85.663741, 42.132857], "pop": 5324, "state": "MI", "_id": "49087"} -{"city": "SCOTTS", "loc": [-85.468492, 42.181892], "pop": 3249, "state": "MI", "_id": "49088"} -{"city": "SHERWOOD", "loc": [-85.240797, 42.010736], "pop": 2310, "state": "MI", "_id": "49089"} -{"city": "SOUTH HAVEN", "loc": [-86.254207, 42.404096], "pop": 12604, "state": "MI", "_id": "49090"} -{"city": "STURGIS", "loc": [-85.426357, 41.808934], "pop": 16597, "state": "MI", "_id": "49091"} -{"city": "TEKONSHA", "loc": [-84.992602, 42.086326], "pop": 2747, "state": "MI", "_id": "49092"} -{"city": "THREE RIVERS", "loc": [-85.637125, 41.959598], "pop": 17021, "state": "MI", "_id": "49093"} -{"city": "UNION CITY", "loc": [-85.135637, 42.055134], "pop": 3600, "state": "MI", "_id": "49094"} -{"city": "VANDALIA", "loc": [-85.875546, 41.895506], "pop": 1839, "state": "MI", "_id": "49095"} -{"city": "VERMONTVILLE", "loc": [-85.010984, 42.63921], "pop": 2978, "state": "MI", "_id": "49096"} -{"city": "VICKSBURG", "loc": [-85.502376, 42.120896], "pop": 8152, "state": "MI", "_id": "49097"} -{"city": "WATERVLIET", "loc": [-86.26037, 42.193804], "pop": 4793, "state": "MI", "_id": "49098"} -{"city": "WHITE PIGEON", "loc": [-85.67501, 41.792891], "pop": 6161, "state": "MI", "_id": "49099"} -{"city": "BARODA", "loc": [-86.491274, 41.948818], "pop": 3030, "state": "MI", "_id": "49101"} -{"city": "BERRIEN CENTER", "loc": [-86.285039, 41.948439], "pop": 2324, "state": "MI", "_id": "49102"} -{"city": "BERRIEN SPRINGS", "loc": [-86.354013, 41.948002], "pop": 11600, "state": "MI", "_id": "49103"} -{"city": "BRIDGMAN", "loc": [-86.554334, 41.936199], "pop": 4073, "state": "MI", "_id": "49106"} -{"city": "BUCHANAN", "loc": [-86.370844, 41.83269], "pop": 10342, "state": "MI", "_id": "49107"} -{"city": "EAU CLAIRE", "loc": [-86.297154, 42.015134], "pop": 4007, "state": "MI", "_id": "49111"} -{"city": "EDWARDSBURG", "loc": [-86.026252, 41.791258], "pop": 8926, "state": "MI", "_id": "49112"} -{"city": "GALIEN", "loc": [-86.503544, 41.819758], "pop": 2775, "state": "MI", "_id": "49113"} -{"city": "LAKESIDE", "loc": [-86.669354, 41.848459], "pop": 269, "state": "MI", "_id": "49116"} -{"city": "GRAND BEACH", "loc": [-86.74663, 41.785591], "pop": 4389, "state": "MI", "_id": "49117"} -{"city": "NILES", "loc": [-86.236789, 41.820168], "pop": 33750, "state": "MI", "_id": "49120"} -{"city": "SAWYER", "loc": [-86.588508, 41.882866], "pop": 2704, "state": "MI", "_id": "49125"} -{"city": "SODUS", "loc": [-86.3921, 42.052082], "pop": 1404, "state": "MI", "_id": "49126"} -{"city": "STEVENSVILLE", "loc": [-86.511862, 42.021968], "pop": 9354, "state": "MI", "_id": "49127"} -{"city": "THREE OAKS", "loc": [-86.615411, 41.814976], "pop": 3994, "state": "MI", "_id": "49128"} -{"city": "UNION PIER", "loc": [-86.691146, 41.82555], "pop": 979, "state": "MI", "_id": "49129"} -{"city": "UNION", "loc": [-85.852905, 41.782678], "pop": 1617, "state": "MI", "_id": "49130"} -{"city": "JACKSON", "loc": [-84.387476, 42.254522], "pop": 42076, "state": "MI", "_id": "49201"} -{"city": "JACKSON", "loc": [-84.408348, 42.263431], "pop": 20387, "state": "MI", "_id": "49202"} -{"city": "JACKSON", "loc": [-84.41321, 42.228963], "pop": 38466, "state": "MI", "_id": "49203"} -{"city": "ADDISON", "loc": [-84.312229, 42.003604], "pop": 2506, "state": "MI", "_id": "49220"} -{"city": "ADRIAN", "loc": [-84.044556, 41.900516], "pop": 33769, "state": "MI", "_id": "49221"} -{"city": "ALBION", "loc": [-84.756052, 42.258073], "pop": 13514, "state": "MI", "_id": "49224"} -{"city": "ALLEN", "loc": [-84.760782, 41.926419], "pop": 1953, "state": "MI", "_id": "49227"} -{"city": "BLISSFIELD", "loc": [-83.877257, 41.827636], "pop": 5635, "state": "MI", "_id": "49228"} -{"city": "BRITTON", "loc": [-83.837722, 41.988713], "pop": 2553, "state": "MI", "_id": "49229"} -{"city": "BROOKLYN", "loc": [-84.241353, 42.10436], "pop": 7155, "state": "MI", "_id": "49230"} -{"city": "CAMDEN", "loc": [-84.724497, 41.736149], "pop": 1500, "state": "MI", "_id": "49232"} -{"city": "CEMENT CITY", "loc": [-84.325423, 42.060395], "pop": 1313, "state": "MI", "_id": "49233"} -{"city": "CLARKLAKE", "loc": [-84.352056, 42.123503], "pop": 2418, "state": "MI", "_id": "49234"} -{"city": "CLAYTON", "loc": [-84.177519, 41.868648], "pop": 2000, "state": "MI", "_id": "49235"} -{"city": "CLINTON", "loc": [-83.944152, 42.063886], "pop": 5222, "state": "MI", "_id": "49236"} -{"city": "CONCORD", "loc": [-84.652928, 42.187501], "pop": 3375, "state": "MI", "_id": "49237"} -{"city": "DEERFIELD", "loc": [-83.784739, 41.890912], "pop": 1427, "state": "MI", "_id": "49238"} -{"city": "GRASS LAKE", "loc": [-84.194041, 42.271071], "pop": 6321, "state": "MI", "_id": "49240"} -{"city": "HANOVER", "loc": [-84.584809, 42.102521], "pop": 2809, "state": "MI", "_id": "49241"} -{"city": "HILLSDALE", "loc": [-84.620812, 41.923954], "pop": 10382, "state": "MI", "_id": "49242"} -{"city": "HOMER", "loc": [-84.81572, 42.141561], "pop": 4934, "state": "MI", "_id": "49245"} -{"city": "HORTON", "loc": [-84.497593, 42.119092], "pop": 3099, "state": "MI", "_id": "49246"} -{"city": "HUDSON", "loc": [-84.338031, 41.858101], "pop": 5953, "state": "MI", "_id": "49247"} -{"city": "JASPER", "loc": [-83.951549, 41.758956], "pop": 1240, "state": "MI", "_id": "49248"} -{"city": "JEROME", "loc": [-84.445483, 42.048375], "pop": 2686, "state": "MI", "_id": "49249"} -{"city": "JONESVILLE", "loc": [-84.645083, 41.979833], "pop": 5671, "state": "MI", "_id": "49250"} -{"city": "LESLIE", "loc": [-84.420653, 42.460279], "pop": 5493, "state": "MI", "_id": "49251"} -{"city": "LITCHFIELD", "loc": [-84.636095, 42.054039], "pop": 840, "state": "MI", "_id": "49252"} -{"city": "MANITOU BEACH", "loc": [-84.276706, 41.967055], "pop": 2714, "state": "MI", "_id": "49253"} -{"city": "MICHIGAN CENTER", "loc": [-84.321315, 42.226294], "pop": 4013, "state": "MI", "_id": "49254"} -{"city": "MONTGOMERY", "loc": [-84.849641, 41.792764], "pop": 2260, "state": "MI", "_id": "49255"} -{"city": "MORENCI", "loc": [-84.219204, 41.738612], "pop": 4699, "state": "MI", "_id": "49256"} -{"city": "MUNITH", "loc": [-84.248486, 42.370317], "pop": 3078, "state": "MI", "_id": "49259"} -{"city": "NORTH ADAMS", "loc": [-84.520824, 41.971484], "pop": 750, "state": "MI", "_id": "49262"} -{"city": "ONONDAGA", "loc": [-84.553501, 42.448726], "pop": 1358, "state": "MI", "_id": "49264"} -{"city": "ONSTED", "loc": [-84.183892, 42.029533], "pop": 4601, "state": "MI", "_id": "49265"} -{"city": "OSSEO", "loc": [-84.597388, 41.838413], "pop": 7025, "state": "MI", "_id": "49266"} -{"city": "OTTAWA LAKE", "loc": [-83.685559, 41.768276], "pop": 4531, "state": "MI", "_id": "49267"} -{"city": "PALMYRA", "loc": [-83.965679, 41.872976], "pop": 2289, "state": "MI", "_id": "49268"} -{"city": "PARMA", "loc": [-84.599886, 42.273879], "pop": 6185, "state": "MI", "_id": "49269"} -{"city": "PETERSBURG", "loc": [-83.687673, 41.875655], "pop": 4832, "state": "MI", "_id": "49270"} -{"city": "PITTSFORD", "loc": [-84.444016, 41.896262], "pop": 2552, "state": "MI", "_id": "49271"} -{"city": "PLEASANT LAKE", "loc": [-84.342791, 42.390343], "pop": 1685, "state": "MI", "_id": "49272"} -{"city": "READING", "loc": [-84.76475, 41.844194], "pop": 2253, "state": "MI", "_id": "49274"} -{"city": "RIDGEWAY", "loc": [-83.778429, 42.010554], "pop": 23, "state": "MI", "_id": "49275"} -{"city": "RIGA", "loc": [-83.801144, 41.795309], "pop": 961, "state": "MI", "_id": "49276"} -{"city": "RIVES JUNCTION", "loc": [-84.458868, 42.38871], "pop": 3676, "state": "MI", "_id": "49277"} -{"city": "SAND CREEK", "loc": [-84.075502, 41.77928], "pop": 1197, "state": "MI", "_id": "49279"} -{"city": "SOMERSET", "loc": [-84.39472, 42.044732], "pop": 1556, "state": "MI", "_id": "49281"} -{"city": "SPRING ARBOR", "loc": [-84.550127, 42.206861], "pop": 2852, "state": "MI", "_id": "49283"} -{"city": "SPRINGPORT", "loc": [-84.703888, 42.38055], "pop": 3153, "state": "MI", "_id": "49284"} -{"city": "STOCKBRIDGE", "loc": [-84.175178, 42.460404], "pop": 6047, "state": "MI", "_id": "49285"} -{"city": "TECUMSEH", "loc": [-83.955485, 41.995297], "pop": 12645, "state": "MI", "_id": "49286"} -{"city": "TIPTON", "loc": [-84.07684, 42.036911], "pop": 2473, "state": "MI", "_id": "49287"} -{"city": "WALDRON", "loc": [-84.449636, 41.738608], "pop": 2151, "state": "MI", "_id": "49288"} -{"city": "ADA", "loc": [-85.480959, 42.95661], "pop": 9568, "state": "MI", "_id": "49301"} -{"city": "ALTO", "loc": [-85.425554, 42.824312], "pop": 5391, "state": "MI", "_id": "49302"} -{"city": "BAILEY", "loc": [-85.831318, 43.276768], "pop": 670, "state": "MI", "_id": "49303"} -{"city": "BALDWIN", "loc": [-85.881874, 43.889409], "pop": 3387, "state": "MI", "_id": "49304"} -{"city": "BARRYTON", "loc": [-85.154654, 43.750651], "pop": 1824, "state": "MI", "_id": "49305"} -{"city": "BELMONT", "loc": [-85.586769, 43.077934], "pop": 6782, "state": "MI", "_id": "49306"} -{"city": "BIG RAPIDS", "loc": [-85.479705, 43.689679], "pop": 18049, "state": "MI", "_id": "49307"} -{"city": "BITELY", "loc": [-85.877799, 43.732132], "pop": 1828, "state": "MI", "_id": "49309"} -{"city": "BLANCHARD", "loc": [-85.058856, 43.522778], "pop": 2473, "state": "MI", "_id": "49310"} -{"city": "BYRON CENTER", "loc": [-85.713602, 42.801614], "pop": 6861, "state": "MI", "_id": "49315"} -{"city": "DUTTON", "loc": [-85.562167, 42.796395], "pop": 8634, "state": "MI", "_id": "49316"} -{"city": "CASNOVIA", "loc": [-85.825394, 43.23815], "pop": 1283, "state": "MI", "_id": "49318"} -{"city": "CEDAR SPRINGS", "loc": [-85.545238, 43.22148], "pop": 9743, "state": "MI", "_id": "49319"} -{"city": "COMSTOCK PARK", "loc": [-85.684468, 43.057813], "pop": 11913, "state": "MI", "_id": "49321"} -{"city": "CORAL", "loc": [-85.379245, 43.364458], "pop": 895, "state": "MI", "_id": "49322"} -{"city": "DORR", "loc": [-85.762798, 42.723113], "pop": 7782, "state": "MI", "_id": "49323"} -{"city": "FREEPORT", "loc": [-85.279134, 42.729703], "pop": 1546, "state": "MI", "_id": "49325"} -{"city": "GOWEN", "loc": [-85.315941, 43.223884], "pop": 6144, "state": "MI", "_id": "49326"} -{"city": "GRANT", "loc": [-85.836827, 43.339197], "pop": 6898, "state": "MI", "_id": "49327"} -{"city": "HOPKINS", "loc": [-85.732195, 42.64209], "pop": 2350, "state": "MI", "_id": "49328"} -{"city": "HOWARD CITY", "loc": [-85.485609, 43.408326], "pop": 3028, "state": "MI", "_id": "49329"} -{"city": "KENT CITY", "loc": [-85.739949, 43.236235], "pop": 3742, "state": "MI", "_id": "49330"} -{"city": "LOWELL", "loc": [-85.365305, 42.95497], "pop": 12732, "state": "MI", "_id": "49331"} -{"city": "MECOSTA", "loc": [-85.228034, 43.681317], "pop": 1939, "state": "MI", "_id": "49332"} -{"city": "MIDDLEVILLE", "loc": [-85.475901, 42.693247], "pop": 8596, "state": "MI", "_id": "49333"} -{"city": "MORLEY", "loc": [-85.447318, 43.505951], "pop": 2875, "state": "MI", "_id": "49336"} -{"city": "NEWAYGO", "loc": [-85.759381, 43.419799], "pop": 8091, "state": "MI", "_id": "49337"} -{"city": "PARIS", "loc": [-85.521348, 43.767704], "pop": 3359, "state": "MI", "_id": "49338"} -{"city": "PIERSON", "loc": [-85.513359, 43.335514], "pop": 2177, "state": "MI", "_id": "49339"} -{"city": "REMUS", "loc": [-85.157365, 43.603249], "pop": 4990, "state": "MI", "_id": "49340"} -{"city": "ROCKFORD", "loc": [-85.513606, 43.115217], "pop": 19778, "state": "MI", "_id": "49341"} -{"city": "RODNEY", "loc": [-85.321884, 43.73791], "pop": 1202, "state": "MI", "_id": "49342"} -{"city": "SAND LAKE", "loc": [-85.536655, 43.298109], "pop": 4951, "state": "MI", "_id": "49343"} -{"city": "SHELBYVILLE", "loc": [-85.628239, 42.593581], "pop": 259, "state": "MI", "_id": "49344"} -{"city": "SPARTA", "loc": [-85.687668, 43.161867], "pop": 11522, "state": "MI", "_id": "49345"} -{"city": "STANWOOD", "loc": [-85.444607, 43.601728], "pop": 3068, "state": "MI", "_id": "49346"} -{"city": "TRUFANT", "loc": [-85.368489, 43.315611], "pop": 1002, "state": "MI", "_id": "49347"} -{"city": "WAYLAND", "loc": [-85.619112, 42.664268], "pop": 6756, "state": "MI", "_id": "49348"} -{"city": "WHITE CLOUD", "loc": [-85.75864, 43.539803], "pop": 8668, "state": "MI", "_id": "49349"} -{"city": "ALLENDALE", "loc": [-85.924913, 42.971066], "pop": 7311, "state": "MI", "_id": "49401"} -{"city": "BRANCH", "loc": [-86.131369, 43.923296], "pop": 1741, "state": "MI", "_id": "49402"} -{"city": "CONKLIN", "loc": [-85.853753, 43.149399], "pop": 2720, "state": "MI", "_id": "49403"} -{"city": "COOPERSVILLE", "loc": [-85.951722, 43.06013], "pop": 5962, "state": "MI", "_id": "49404"} -{"city": "CUSTER", "loc": [-86.220437, 43.94424], "pop": 1002, "state": "MI", "_id": "49405"} -{"city": "FENNVILLE", "loc": [-86.124876, 42.577669], "pop": 6486, "state": "MI", "_id": "49408"} -{"city": "FOUNTAIN", "loc": [-86.216861, 44.036342], "pop": 1030, "state": "MI", "_id": "49410"} -{"city": "FREE SOIL", "loc": [-86.265158, 44.112036], "pop": 903, "state": "MI", "_id": "49411"} -{"city": "FREMONT", "loc": [-85.962554, 43.465196], "pop": 7946, "state": "MI", "_id": "49412"} -{"city": "FRUITPORT", "loc": [-86.138832, 43.144282], "pop": 4183, "state": "MI", "_id": "49415"} -{"city": "GRAND HAVEN", "loc": [-86.191227, 43.037838], "pop": 24103, "state": "MI", "_id": "49417"} -{"city": "GRANDVILLE", "loc": [-85.761874, 42.89387], "pop": 19467, "state": "MI", "_id": "49418"} -{"city": "HAMILTON", "loc": [-85.974731, 42.688097], "pop": 4586, "state": "MI", "_id": "49419"} -{"city": "HART", "loc": [-86.314152, 43.70676], "pop": 5548, "state": "MI", "_id": "49420"} -{"city": "HESPERIA", "loc": [-86.060739, 43.596513], "pop": 5102, "state": "MI", "_id": "49421"} -{"city": "HOLLAND", "loc": [-86.116362, 42.769211], "pop": 40325, "state": "MI", "_id": "49423"} -{"city": "HOLLAND", "loc": [-86.142631, 42.813514], "pop": 25798, "state": "MI", "_id": "49424"} -{"city": "HOLTON", "loc": [-86.150853, 43.441454], "pop": 4251, "state": "MI", "_id": "49425"} -{"city": "HUDSONVILLE", "loc": [-85.8751, 42.874805], "pop": 20122, "state": "MI", "_id": "49426"} -{"city": "JENISON", "loc": [-85.827603, 42.910423], "pop": 23358, "state": "MI", "_id": "49428"} -{"city": "LUDINGTON", "loc": [-86.440253, 43.968823], "pop": 13169, "state": "MI", "_id": "49431"} -{"city": "MARNE", "loc": [-85.841958, 43.053185], "pop": 2715, "state": "MI", "_id": "49435"} -{"city": "MEARS", "loc": [-86.453307, 43.682494], "pop": 1302, "state": "MI", "_id": "49436"} -{"city": "MONTAGUE", "loc": [-86.373992, 43.424163], "pop": 4953, "state": "MI", "_id": "49437"} -{"city": "MUSKEGON", "loc": [-86.249191, 43.232589], "pop": 1033, "state": "MI", "_id": "49440"} -{"city": "MUSKEGON", "loc": [-86.273819, 43.196184], "pop": 36169, "state": "MI", "_id": "49441"} -{"city": "MUSKEGON", "loc": [-86.188467, 43.232876], "pop": 39272, "state": "MI", "_id": "49442"} -{"city": "MUSKEGON HEIGHTS", "loc": [-86.216208, 43.195046], "pop": 29295, "state": "MI", "_id": "49444"} -{"city": "NORTH MUSKEGON", "loc": [-86.273297, 43.282873], "pop": 18797, "state": "MI", "_id": "49445"} -{"city": "NEW ERA", "loc": [-86.344803, 43.555578], "pop": 2539, "state": "MI", "_id": "49446"} -{"city": "NEW RICHMOND", "loc": [-86.037985, 42.662383], "pop": 438, "state": "MI", "_id": "49447"} -{"city": "NUNICA", "loc": [-86.102768, 43.088306], "pop": 3795, "state": "MI", "_id": "49448"} -{"city": "PENTWATER", "loc": [-86.386818, 43.823713], "pop": 3915, "state": "MI", "_id": "49449"} -{"city": "PULLMAN", "loc": [-86.079909, 42.465131], "pop": 2672, "state": "MI", "_id": "49450"} -{"city": "RAVENNA", "loc": [-85.964816, 43.209128], "pop": 5106, "state": "MI", "_id": "49451"} -{"city": "ROTHBURY", "loc": [-86.34464, 43.511633], "pop": 1618, "state": "MI", "_id": "49452"} -{"city": "SAUGATUCK", "loc": [-86.194093, 42.642562], "pop": 3870, "state": "MI", "_id": "49453"} -{"city": "SCOTTVILLE", "loc": [-86.315615, 43.976692], "pop": 4229, "state": "MI", "_id": "49454"} -{"city": "SHELBY", "loc": [-86.361503, 43.607942], "pop": 4420, "state": "MI", "_id": "49455"} -{"city": "SPRING LAKE", "loc": [-86.19151, 43.088741], "pop": 14602, "state": "MI", "_id": "49456"} -{"city": "TWIN LAKE", "loc": [-86.163316, 43.341378], "pop": 7212, "state": "MI", "_id": "49457"} -{"city": "WALKERVILLE", "loc": [-86.082991, 43.762559], "pop": 309, "state": "MI", "_id": "49459"} -{"city": "WEST OLIVE", "loc": [-86.131726, 42.909912], "pop": 6722, "state": "MI", "_id": "49460"} -{"city": "WHITEHALL", "loc": [-86.331453, 43.390358], "pop": 6925, "state": "MI", "_id": "49461"} -{"city": "ZEELAND", "loc": [-86.010408, 42.825586], "pop": 15649, "state": "MI", "_id": "49464"} -{"city": "GRAND RAPIDS", "loc": [-85.65273, 42.965879], "pop": 32876, "state": "MI", "_id": "49503"} -{"city": "WALKER", "loc": [-85.725543, 42.98392], "pop": 63454, "state": "MI", "_id": "49504"} -{"city": "GRAND RAPIDS", "loc": [-85.630931, 43.012025], "pop": 52883, "state": "MI", "_id": "49505"} -{"city": "GRAND RAPIDS", "loc": [-85.621317, 42.943978], "pop": 36668, "state": "MI", "_id": "49506"} -{"city": "GRAND RAPIDS", "loc": [-85.65417, 42.931788], "pop": 37681, "state": "MI", "_id": "49507"} -{"city": "KENTWOOD", "loc": [-85.624179, 42.875653], "pop": 32830, "state": "MI", "_id": "49508"} -{"city": "WYOMING", "loc": [-85.705775, 42.900867], "pop": 57419, "state": "MI", "_id": "49509"} -{"city": "KENTWOOD", "loc": [-85.578156, 42.89269], "pop": 7823, "state": "MI", "_id": "49512"} -{"city": "GRAND RAPIDS", "loc": [-85.548346, 42.928029], "pop": 25471, "state": "MI", "_id": "49546"} -{"city": "KENTWOOD", "loc": [-85.660767, 42.867048], "pop": 32054, "state": "MI", "_id": "49548"} -{"city": "CADILLAC", "loc": [-85.430046, 44.250447], "pop": 18182, "state": "MI", "_id": "49601"} -{"city": "ALDEN", "loc": [-85.2241, 44.877887], "pop": 1536, "state": "MI", "_id": "49612"} -{"city": "ARCADIA", "loc": [-86.206057, 44.523157], "pop": 937, "state": "MI", "_id": "49613"} -{"city": "BEAR LAKE", "loc": [-86.142497, 44.431125], "pop": 1965, "state": "MI", "_id": "49614"} -{"city": "BELLAIRE", "loc": [-85.226495, 44.976469], "pop": 2031, "state": "MI", "_id": "49615"} -{"city": "BENZONIA", "loc": [-86.096091, 44.595502], "pop": 1833, "state": "MI", "_id": "49616"} -{"city": "BEULAH", "loc": [-86.038346, 44.646002], "pop": 2617, "state": "MI", "_id": "49617"} -{"city": "BOON", "loc": [-85.614383, 44.291555], "pop": 388, "state": "MI", "_id": "49618"} -{"city": "BRETHREN", "loc": [-85.996396, 44.296596], "pop": 966, "state": "MI", "_id": "49619"} -{"city": "BUCKLEY", "loc": [-85.693541, 44.519856], "pop": 1788, "state": "MI", "_id": "49620"} -{"city": "CEDAR", "loc": [-85.745344, 44.836953], "pop": 2853, "state": "MI", "_id": "49621"} -{"city": "CENTRAL LAKE", "loc": [-85.267285, 45.074796], "pop": 1970, "state": "MI", "_id": "49622"} -{"city": "CHASE", "loc": [-85.619639, 43.868172], "pop": 1016, "state": "MI", "_id": "49623"} -{"city": "COPEMISH", "loc": [-85.887859, 44.450276], "pop": 927, "state": "MI", "_id": "49625"} -{"city": "ELK RAPIDS", "loc": [-85.408226, 44.895473], "pop": 1493, "state": "MI", "_id": "49629"} -{"city": "EMPIRE", "loc": [-85.992533, 44.84106], "pop": 609, "state": "MI", "_id": "49630"} -{"city": "EVART", "loc": [-85.264935, 43.888772], "pop": 4253, "state": "MI", "_id": "49631"} -{"city": "FALMOUTH", "loc": [-85.017002, 44.252861], "pop": 1891, "state": "MI", "_id": "49632"} -{"city": "FIFE LAKE", "loc": [-85.253122, 44.572064], "pop": 2566, "state": "MI", "_id": "49633"} -{"city": "FRANKFORT", "loc": [-86.221201, 44.631122], "pop": 3168, "state": "MI", "_id": "49635"} -{"city": "GLEN ARBOR", "loc": [-86.035339, 44.842927], "pop": 641, "state": "MI", "_id": "49636"} -{"city": "GRAWN", "loc": [-85.71129, 44.642779], "pop": 1869, "state": "MI", "_id": "49637"} -{"city": "HARRIETTA", "loc": [-85.739573, 44.297163], "pop": 651, "state": "MI", "_id": "49638"} -{"city": "HERSEY", "loc": [-85.405926, 43.84806], "pop": 1915, "state": "MI", "_id": "49639"} -{"city": "HONOR", "loc": [-86.037582, 44.69539], "pop": 945, "state": "MI", "_id": "49640"} -{"city": "IDLEWILD", "loc": [-85.716036, 43.912621], "pop": 1132, "state": "MI", "_id": "49642"} -{"city": "INTERLOCHEN", "loc": [-85.802156, 44.651769], "pop": 3429, "state": "MI", "_id": "49643"} -{"city": "IRONS", "loc": [-85.939008, 44.096638], "pop": 1245, "state": "MI", "_id": "49644"} -{"city": "KALEVA", "loc": [-86.046737, 44.369168], "pop": 1533, "state": "MI", "_id": "49645"} -{"city": "KALKASKA", "loc": [-85.120655, 44.73546], "pop": 7387, "state": "MI", "_id": "49646"} -{"city": "KARLIN", "loc": [-85.79406, 44.569128], "pop": 80, "state": "MI", "_id": "49647"} -{"city": "KEWADIN", "loc": [-85.353941, 45.012574], "pop": 1303, "state": "MI", "_id": "49648"} -{"city": "KINGSLEY", "loc": [-85.526235, 44.575644], "pop": 3823, "state": "MI", "_id": "49649"} -{"city": "LAKE ANN", "loc": [-85.85284, 44.731736], "pop": 1448, "state": "MI", "_id": "49650"} -{"city": "MOORESTOWN", "loc": [-85.207352, 44.361932], "pop": 6225, "state": "MI", "_id": "49651"} -{"city": "LAKE LEELANAU", "loc": [-85.732866, 44.985602], "pop": 2053, "state": "MI", "_id": "49653"} -{"city": "LELAND", "loc": [-86.092924, 45.010215], "pop": 0, "state": "MI", "_id": "49654"} -{"city": "LEROY", "loc": [-85.430001, 44.045656], "pop": 3386, "state": "MI", "_id": "49655"} -{"city": "LUTHER", "loc": [-85.682631, 44.054494], "pop": 1540, "state": "MI", "_id": "49656"} -{"city": "MC BAIN", "loc": [-85.228252, 44.21718], "pop": 3431, "state": "MI", "_id": "49657"} -{"city": "MANCELONA", "loc": [-85.063399, 44.911596], "pop": 5447, "state": "MI", "_id": "49659"} -{"city": "STRONACH", "loc": [-86.28503, 44.228323], "pop": 14319, "state": "MI", "_id": "49660"} -{"city": "MANTON", "loc": [-85.42017, 44.415222], "pop": 3516, "state": "MI", "_id": "49663"} -{"city": "MAPLE CITY", "loc": [-85.881392, 44.859035], "pop": 1917, "state": "MI", "_id": "49664"} -{"city": "MARION", "loc": [-85.155031, 44.046314], "pop": 5710, "state": "MI", "_id": "49665"} -{"city": "MERRITT", "loc": [-85.015508, 44.36843], "pop": 600, "state": "MI", "_id": "49667"} -{"city": "MESICK", "loc": [-85.706122, 44.407451], "pop": 2864, "state": "MI", "_id": "49668"} -{"city": "NORTHPORT", "loc": [-85.617287, 45.115699], "pop": 1663, "state": "MI", "_id": "49670"} -{"city": "ONEKAMA", "loc": [-86.209959, 44.364576], "pop": 1089, "state": "MI", "_id": "49675"} -{"city": "RAPID CITY", "loc": [-85.294318, 44.844863], "pop": 2741, "state": "MI", "_id": "49676"} -{"city": "REED CITY", "loc": [-85.513028, 43.886808], "pop": 5056, "state": "MI", "_id": "49677"} -{"city": "SEARS", "loc": [-85.188246, 43.901747], "pop": 150, "state": "MI", "_id": "49679"} -{"city": "SOUTH BOARDMAN", "loc": [-85.2472, 44.640143], "pop": 1427, "state": "MI", "_id": "49680"} -{"city": "SUTTONS BAY", "loc": [-85.642264, 44.965613], "pop": 3422, "state": "MI", "_id": "49682"} -{"city": "THOMPSONVILLE", "loc": [-85.945973, 44.519833], "pop": 1231, "state": "MI", "_id": "49683"} -{"city": "TRAVERSE CITY", "loc": [-85.618869, 44.74365], "pop": 53415, "state": "MI", "_id": "49684"} -{"city": "TUSTIN", "loc": [-85.490143, 44.107181], "pop": 1213, "state": "MI", "_id": "49688"} -{"city": "WELLSTON", "loc": [-85.955475, 44.213266], "pop": 1043, "state": "MI", "_id": "49689"} -{"city": "WILLIAMSBURG", "loc": [-85.434732, 44.801952], "pop": 4987, "state": "MI", "_id": "49690"} -{"city": "AFTON", "loc": [-84.469453, 45.363684], "pop": 605, "state": "MI", "_id": "49705"} -{"city": "ALANSON", "loc": [-84.792421, 45.440545], "pop": 3681, "state": "MI", "_id": "49706"} -{"city": "ALPENA", "loc": [-83.4602, 45.079018], "pop": 23530, "state": "MI", "_id": "49707"} -{"city": "ATLANTA", "loc": [-84.147982, 44.992371], "pop": 2299, "state": "MI", "_id": "49709"} -{"city": "BARBEAU", "loc": [-84.217332, 46.284753], "pop": 368, "state": "MI", "_id": "49710"} -{"city": "BOYNE CITY", "loc": [-85.018294, 45.205203], "pop": 7244, "state": "MI", "_id": "49712"} -{"city": "BOYNE FALLS", "loc": [-84.891584, 45.211563], "pop": 2009, "state": "MI", "_id": "49713"} -{"city": "RACO", "loc": [-84.563841, 46.411117], "pop": 871, "state": "MI", "_id": "49715"} -{"city": "BRUTUS", "loc": [-84.746693, 45.506785], "pop": 586, "state": "MI", "_id": "49716"} -{"city": "CARP LAKE", "loc": [-84.773453, 45.742364], "pop": 1049, "state": "MI", "_id": "49718"} -{"city": "CEDARVILLE", "loc": [-84.348823, 46.002449], "pop": 1615, "state": "MI", "_id": "49719"} -{"city": "CHARLEVOIX", "loc": [-85.245298, 45.29934], "pop": 7690, "state": "MI", "_id": "49720"} -{"city": "CHEBOYGAN", "loc": [-84.48672, 45.608038], "pop": 13854, "state": "MI", "_id": "49721"} -{"city": "DAFTER", "loc": [-84.399679, 46.349196], "pop": 2040, "state": "MI", "_id": "49724"} -{"city": "DE TOUR VILLAGE", "loc": [-83.939628, 45.993852], "pop": 719, "state": "MI", "_id": "49725"} -{"city": "DRUMMOND ISLAND", "loc": [-83.736706, 46.005521], "pop": 835, "state": "MI", "_id": "49726"} -{"city": "EAST JORDAN", "loc": [-85.138655, 45.153858], "pop": 5532, "state": "MI", "_id": "49727"} -{"city": "ECKERMAN", "loc": [-85.162606, 46.346638], "pop": 208, "state": "MI", "_id": "49728"} -{"city": "ELLSWORTH", "loc": [-85.263292, 45.159929], "pop": 1223, "state": "MI", "_id": "49729"} -{"city": "ELMIRA", "loc": [-84.830321, 45.034582], "pop": 2807, "state": "MI", "_id": "49730"} -{"city": "FREDERIC", "loc": [-84.68241, 44.838217], "pop": 3967, "state": "MI", "_id": "49733"} -{"city": "GAYLORD", "loc": [-84.67228, 45.012523], "pop": 11598, "state": "MI", "_id": "49735"} -{"city": "GOETZVILLE", "loc": [-84.059271, 46.100976], "pop": 123, "state": "MI", "_id": "49736"} -{"city": "GRAYLING", "loc": [-84.691321, 44.670959], "pop": 7591, "state": "MI", "_id": "49738"} -{"city": "HARBOR POINT", "loc": [-84.980303, 45.464535], "pop": 5473, "state": "MI", "_id": "49740"} -{"city": "HAWKS", "loc": [-83.854219, 45.297467], "pop": 876, "state": "MI", "_id": "49743"} -{"city": "HERRON", "loc": [-83.65348, 45.012367], "pop": 978, "state": "MI", "_id": "49744"} -{"city": "HILLMAN", "loc": [-83.946781, 45.069104], "pop": 3902, "state": "MI", "_id": "49746"} -{"city": "HUBBARD LAKE", "loc": [-83.605905, 44.89467], "pop": 1359, "state": "MI", "_id": "49747"} -{"city": "INDIAN RIVER", "loc": [-84.595578, 45.426984], "pop": 2965, "state": "MI", "_id": "49749"} -{"city": "JOHANNESBURG", "loc": [-84.385708, 45.015207], "pop": 2062, "state": "MI", "_id": "49751"} -{"city": "KINROSS", "loc": [-84.751122, 46.416612], "pop": 1070, "state": "MI", "_id": "49752"} -{"city": "LACHINE", "loc": [-83.749071, 45.042946], "pop": 1764, "state": "MI", "_id": "49753"} -{"city": "LEVERING", "loc": [-84.798597, 45.637711], "pop": 1251, "state": "MI", "_id": "49755"} -{"city": "LEWISTON", "loc": [-84.297281, 44.853758], "pop": 3327, "state": "MI", "_id": "49756"} -{"city": "MACKINAC ISLAND", "loc": [-84.624527, 45.857837], "pop": 469, "state": "MI", "_id": "49757"} -{"city": "MILLERSBURG", "loc": [-84.122246, 45.402164], "pop": 2048, "state": "MI", "_id": "49759"} -{"city": "MORAN", "loc": [-85.007835, 46.044106], "pop": 113, "state": "MI", "_id": "49760"} -{"city": "NAUBINWAY", "loc": [-85.44619, 46.126021], "pop": 1297, "state": "MI", "_id": "49762"} -{"city": "ONAWAY", "loc": [-84.212272, 45.33654], "pop": 1926, "state": "MI", "_id": "49765"} -{"city": "OSSINEKE", "loc": [-83.459186, 44.91038], "pop": 2294, "state": "MI", "_id": "49766"} -{"city": "PARADISE", "loc": [-85.056247, 46.598321], "pop": 515, "state": "MI", "_id": "49768"} -{"city": "PELLSTON", "loc": [-84.842502, 45.570285], "pop": 1360, "state": "MI", "_id": "49769"} -{"city": "BAY VIEW", "loc": [-84.940263, 45.361268], "pop": 12806, "state": "MI", "_id": "49770"} -{"city": "PICKFORD", "loc": [-84.366312, 46.155858], "pop": 624, "state": "MI", "_id": "49774"} -{"city": "POINTE AUX PINS", "loc": [-84.447035, 45.754888], "pop": 59, "state": "MI", "_id": "49775"} -{"city": "POSEN", "loc": [-83.639271, 45.223335], "pop": 3830, "state": "MI", "_id": "49776"} -{"city": "ROGERS CITY", "loc": [-83.835483, 45.412306], "pop": 5463, "state": "MI", "_id": "49779"} -{"city": "FIBRE", "loc": [-84.549343, 46.200568], "pop": 3242, "state": "MI", "_id": "49780"} -{"city": "SAINT IGNACE", "loc": [-84.709409, 45.921527], "pop": 5411, "state": "MI", "_id": "49781"} -{"city": "SAINT JAMES", "loc": [-85.531581, 45.722061], "pop": 404, "state": "MI", "_id": "49782"} -{"city": "SAULT SAINTE MAR", "loc": [-84.346746, 46.473932], "pop": 17974, "state": "MI", "_id": "49783"} -{"city": "KINCHELOE", "loc": [-84.462584, 46.26271], "pop": 6046, "state": "MI", "_id": "49788"} -{"city": "STALWART", "loc": [-84.155861, 46.138876], "pop": 214, "state": "MI", "_id": "49789"} -{"city": "TOWER", "loc": [-84.303496, 45.341619], "pop": 929, "state": "MI", "_id": "49792"} -{"city": "VANDERBILT", "loc": [-84.651134, 45.153247], "pop": 1541, "state": "MI", "_id": "49795"} -{"city": "WOLVERINE", "loc": [-84.606477, 45.286148], "pop": 1857, "state": "MI", "_id": "49799"} -{"city": "IRON MOUNTAIN", "loc": [-88.068257, 45.821894], "pop": 18423, "state": "MI", "_id": "49801"} -{"city": "AU TRAIN", "loc": [-86.779327, 46.428231], "pop": 796, "state": "MI", "_id": "49806"} -{"city": "HARDWOOD", "loc": [-87.240522, 45.705411], "pop": 4037, "state": "MI", "_id": "49807"} -{"city": "CARNEY", "loc": [-87.543896, 45.591738], "pop": 1014, "state": "MI", "_id": "49812"} -{"city": "CEDAR RIVER", "loc": [-87.365994, 45.448475], "pop": 185, "state": "MI", "_id": "49813"} -{"city": "CHAMPION", "loc": [-87.830587, 46.468497], "pop": 2774, "state": "MI", "_id": "49814"} -{"city": "CHANNING", "loc": [-88.077221, 46.154562], "pop": 522, "state": "MI", "_id": "49815"} -{"city": "LIMESTONE", "loc": [-86.758934, 46.294821], "pop": 251, "state": "MI", "_id": "49816"} -{"city": "COOKS", "loc": [-86.530718, 45.899665], "pop": 2, "state": "MI", "_id": "49817"} -{"city": "CORNELL", "loc": [-87.223702, 45.910373], "pop": 671, "state": "MI", "_id": "49818"} -{"city": "CURTIS", "loc": [-85.786466, 46.204763], "pop": 523, "state": "MI", "_id": "49820"} -{"city": "DAGGETT", "loc": [-87.607858, 45.488738], "pop": 1184, "state": "MI", "_id": "49821"} -{"city": "DEERTON", "loc": [-87.04064, 46.483979], "pop": 290, "state": "MI", "_id": "49822"} -{"city": "EBEN JUNCTION", "loc": [-87.032799, 46.351726], "pop": 456, "state": "MI", "_id": "49825"} -{"city": "RUMELY", "loc": [-86.935068, 46.341867], "pop": 823, "state": "MI", "_id": "49826"} -{"city": "ENGADINE", "loc": [-85.600468, 46.204009], "pop": 327, "state": "MI", "_id": "49827"} -{"city": "ESCANABA", "loc": [-87.088985, 45.76587], "pop": 21065, "state": "MI", "_id": "49829"} -{"city": "LITTLE LAKE", "loc": [-87.411448, 46.053711], "pop": 437, "state": "MI", "_id": "49833"} -{"city": "FOSTER CITY", "loc": [-87.804768, 46.004769], "pop": 1249, "state": "MI", "_id": "49834"} -{"city": "GARDEN", "loc": [-86.577576, 45.754867], "pop": 934, "state": "MI", "_id": "49835"} -{"city": "GERMFASK", "loc": [-85.91589, 46.236261], "pop": 542, "state": "MI", "_id": "49836"} -{"city": "BRAMPTON", "loc": [-87.027284, 45.856402], "pop": 5707, "state": "MI", "_id": "49837"} -{"city": "GOULD CITY", "loc": [-85.725889, 46.134544], "pop": 615, "state": "MI", "_id": "49838"} -{"city": "GRAND MARAIS", "loc": [-85.983633, 46.652916], "pop": 508, "state": "MI", "_id": "49839"} -{"city": "GULLIVER", "loc": [-86.021433, 46.011491], "pop": 822, "state": "MI", "_id": "49840"} -{"city": "PRINCETON", "loc": [-87.43016, 46.291938], "pop": 5377, "state": "MI", "_id": "49841"} -{"city": "K I SAWYER A F B", "loc": [-87.37593, 46.330811], "pop": 5613, "state": "MI", "_id": "49843"} -{"city": "HERMANSVILLE", "loc": [-87.622659, 45.714902], "pop": 1090, "state": "MI", "_id": "49847"} -{"city": "INGALLS", "loc": [-87.620607, 45.374774], "pop": 340, "state": "MI", "_id": "49848"} -{"city": "NORTH LAKE", "loc": [-87.682638, 46.490165], "pop": 11725, "state": "MI", "_id": "49849"} -{"city": "MC MILLAN", "loc": [-85.730715, 46.297027], "pop": 897, "state": "MI", "_id": "49853"} -{"city": "THOMPSON", "loc": [-86.275513, 45.982978], "pop": 6753, "state": "MI", "_id": "49854"} -{"city": "BEAVER GROVE", "loc": [-87.389443, 46.53124], "pop": 32361, "state": "MI", "_id": "49855"} -{"city": "MENOMINEE", "loc": [-87.621475, 45.13427], "pop": 13149, "state": "MI", "_id": "49858"} -{"city": "MICHIGAMME", "loc": [-87.892386, 46.668143], "pop": 1230, "state": "MI", "_id": "49861"} -{"city": "CHRISTMAS", "loc": [-86.626846, 46.413863], "pop": 3762, "state": "MI", "_id": "49862"} -{"city": "NEGAUNEE", "loc": [-87.582915, 46.500662], "pop": 8204, "state": "MI", "_id": "49866"} -{"city": "NEWBERRY", "loc": [-85.514951, 46.343763], "pop": 4866, "state": "MI", "_id": "49868"} -{"city": "NORWAY", "loc": [-87.904693, 45.786186], "pop": 3300, "state": "MI", "_id": "49870"} -{"city": "PERRONVILLE", "loc": [-87.398903, 45.826409], "pop": 360, "state": "MI", "_id": "49873"} -{"city": "POWERS", "loc": [-87.531853, 45.679238], "pop": 559, "state": "MI", "_id": "49874"} -{"city": "QUINNESEC", "loc": [-87.992151, 45.799866], "pop": 1313, "state": "MI", "_id": "49876"} -{"city": "RAPID RIVER", "loc": [-86.88449, 45.910662], "pop": 3345, "state": "MI", "_id": "49878"} -{"city": "REPUBLIC", "loc": [-87.993823, 46.368046], "pop": 1192, "state": "MI", "_id": "49879"} -{"city": "ROCK", "loc": [-87.133068, 46.050262], "pop": 1601, "state": "MI", "_id": "49880"} -{"city": "SAGOLA", "loc": [-88.067644, 46.081274], "pop": 392, "state": "MI", "_id": "49881"} -{"city": "SENEY", "loc": [-85.970847, 46.387989], "pop": 185, "state": "MI", "_id": "49883"} -{"city": "SHINGLETON", "loc": [-86.482261, 46.351202], "pop": 589, "state": "MI", "_id": "49884"} -{"city": "SKANDIA", "loc": [-87.248094, 46.349049], "pop": 1975, "state": "MI", "_id": "49885"} -{"city": "SPALDING", "loc": [-87.4964, 45.695747], "pop": 922, "state": "MI", "_id": "49886"} -{"city": "STEPHENSON", "loc": [-87.62579, 45.416594], "pop": 2202, "state": "MI", "_id": "49887"} -{"city": "TRAUNIK", "loc": [-86.988891, 46.258534], "pop": 334, "state": "MI", "_id": "49890"} -{"city": "TRENARY", "loc": [-86.950183, 46.194738], "pop": 563, "state": "MI", "_id": "49891"} -{"city": "VULCAN", "loc": [-87.816811, 45.762257], "pop": 1841, "state": "MI", "_id": "49892"} -{"city": "WALLACE", "loc": [-87.580457, 45.301657], "pop": 2103, "state": "MI", "_id": "49893"} -{"city": "WELLS", "loc": [-87.073443, 45.784866], "pop": 1093, "state": "MI", "_id": "49894"} -{"city": "WETMORE", "loc": [-86.634537, 46.353518], "pop": 646, "state": "MI", "_id": "49895"} -{"city": "WILSON", "loc": [-87.399734, 45.664855], "pop": 1112, "state": "MI", "_id": "49896"} -{"city": "ATLANTIC MINE", "loc": [-88.661318, 47.093663], "pop": 3379, "state": "MI", "_id": "49905"} -{"city": "KEWEENAW BAY", "loc": [-88.495489, 46.790213], "pop": 1759, "state": "MI", "_id": "49908"} -{"city": "BERGLAND", "loc": [-89.597296, 46.588124], "pop": 618, "state": "MI", "_id": "49910"} -{"city": "BESSEMER", "loc": [-90.051544, 46.480188], "pop": 3603, "state": "MI", "_id": "49911"} -{"city": "BRUCE CROSSING", "loc": [-89.168028, 46.527956], "pop": 1089, "state": "MI", "_id": "49912"} -{"city": "LAURIUM", "loc": [-88.44272, 47.242721], "pop": 7656, "state": "MI", "_id": "49913"} -{"city": "CHASSELL", "loc": [-88.554554, 47.002038], "pop": 3096, "state": "MI", "_id": "49916"} -{"city": "COVINGTON", "loc": [-88.524452, 46.557194], "pop": 260, "state": "MI", "_id": "49919"} -{"city": "CRYSTAL FALLS", "loc": [-88.350517, 46.109255], "pop": 4804, "state": "MI", "_id": "49920"} -{"city": "DODGEVILLE", "loc": [-88.580874, 47.091443], "pop": 390, "state": "MI", "_id": "49921"} -{"city": "EWEN", "loc": [-89.314494, 46.540412], "pop": 772, "state": "MI", "_id": "49925"} -{"city": "GAASTRA", "loc": [-88.591728, 46.052638], "pop": 489, "state": "MI", "_id": "49927"} -{"city": "HANCOCK", "loc": [-88.565385, 47.136731], "pop": 7683, "state": "MI", "_id": "49930"} -{"city": "HOUGHTON", "loc": [-88.558024, 47.115801], "pop": 8639, "state": "MI", "_id": "49931"} -{"city": "IRON RIVER", "loc": [-88.645974, 46.093017], "pop": 7882, "state": "MI", "_id": "49935"} -{"city": "IRONWOOD", "loc": [-90.158844, 46.463793], "pop": 9402, "state": "MI", "_id": "49938"} -{"city": "KENTON", "loc": [-88.812933, 46.491565], "pop": 304, "state": "MI", "_id": "49943"} -{"city": "GAY", "loc": [-88.411829, 47.171387], "pop": 3365, "state": "MI", "_id": "49945"} -{"city": "LANSE", "loc": [-88.420959, 46.770407], "pop": 3929, "state": "MI", "_id": "49946"} -{"city": "MARENISCO", "loc": [-89.677422, 46.355107], "pop": 956, "state": "MI", "_id": "49947"} -{"city": "MASS CITY", "loc": [-89.064864, 46.747447], "pop": 805, "state": "MI", "_id": "49948"} -{"city": "EAGLE HARBOR", "loc": [-88.332572, 47.320067], "pop": 1701, "state": "MI", "_id": "49950"} -{"city": "NISULA", "loc": [-88.83397, 46.76952], "pop": 73, "state": "MI", "_id": "49952"} -{"city": "ONTONAGON", "loc": [-89.348474, 46.827484], "pop": 5090, "state": "MI", "_id": "49953"} -{"city": "PELKIE", "loc": [-88.625083, 46.794932], "pop": 1583, "state": "MI", "_id": "49958"} -{"city": "SKANEE", "loc": [-88.173189, 46.874732], "pop": 310, "state": "MI", "_id": "49962"} -{"city": "TOIVOLA", "loc": [-88.860657, 46.985447], "pop": 352, "state": "MI", "_id": "49965"} -{"city": "TROUT CREEK", "loc": [-89.026962, 46.486426], "pop": 480, "state": "MI", "_id": "49967"} -{"city": "WAKEFIELD", "loc": [-89.942446, 46.475184], "pop": 3043, "state": "MI", "_id": "49968"} -{"city": "WATERSMEET", "loc": [-89.210836, 46.25416], "pop": 1048, "state": "MI", "_id": "49969"} -{"city": "WATTON", "loc": [-88.599298, 46.513698], "pop": 391, "state": "MI", "_id": "49970"} -{"city": "AFTON", "loc": [-92.823358, 44.86965], "pop": 4357, "state": "MN", "_id": "55001"} -{"city": "BAYPORT", "loc": [-92.784375, 45.021405], "pop": 3200, "state": "MN", "_id": "55003"} -{"city": "EAST BETHEL", "loc": [-93.233088, 45.394114], "pop": 2821, "state": "MN", "_id": "55005"} -{"city": "BRAHAM", "loc": [-93.203695, 45.717547], "pop": 1944, "state": "MN", "_id": "55006"} -{"city": "QUAMBA", "loc": [-92.98347, 45.938694], "pop": 1222, "state": "MN", "_id": "55007"} -{"city": "CAMBRIDGE", "loc": [-93.288935, 45.557591], "pop": 14444, "state": "MN", "_id": "55008"} -{"city": "CANNON FALLS", "loc": [-92.863995, 44.495985], "pop": 7590, "state": "MN", "_id": "55009"} -{"city": "CASTLE ROCK", "loc": [-93.109241, 44.594216], "pop": 1480, "state": "MN", "_id": "55010"} -{"city": "CEDAR EAST BETHE", "loc": [-93.265834, 45.336494], "pop": 6874, "state": "MN", "_id": "55011"} -{"city": "CHISAGO CITY", "loc": [-92.892134, 45.361144], "pop": 2905, "state": "MN", "_id": "55013"} -{"city": "CIRCLE PINES", "loc": [-93.143984, 45.152841], "pop": 19791, "state": "MN", "_id": "55014"} -{"city": "COTTAGE GROVE", "loc": [-92.939283, 44.830824], "pop": 22265, "state": "MN", "_id": "55016"} -{"city": "DALBO", "loc": [-93.441918, 45.678735], "pop": 616, "state": "MN", "_id": "55017"} -{"city": "STANTON", "loc": [-92.974779, 44.423884], "pop": 2009, "state": "MN", "_id": "55018"} -{"city": "DUNDAS", "loc": [-93.20369, 44.395537], "pop": 803, "state": "MN", "_id": "55019"} -{"city": "ELKO", "loc": [-93.383555, 44.58945], "pop": 4146, "state": "MN", "_id": "55020"} -{"city": "FARIBAULT", "loc": [-93.28179, 44.294457], "pop": 21450, "state": "MN", "_id": "55021"} -{"city": "FARMINGTON", "loc": [-93.153891, 44.662799], "pop": 10145, "state": "MN", "_id": "55024"} -{"city": "FOREST LAKE", "loc": [-92.974894, 45.268499], "pop": 14749, "state": "MN", "_id": "55025"} -{"city": "FRONTENAC", "loc": [-92.328103, 44.487176], "pop": 1698, "state": "MN", "_id": "55026"} -{"city": "GOODHUE", "loc": [-92.571743, 44.402178], "pop": 1928, "state": "MN", "_id": "55027"} -{"city": "GRASSTON", "loc": [-93.196226, 45.772926], "pop": 1007, "state": "MN", "_id": "55030"} -{"city": "HAMPTON", "loc": [-92.946673, 44.602848], "pop": 1780, "state": "MN", "_id": "55031"} -{"city": "HARRIS", "loc": [-93.039545, 45.596205], "pop": 2026, "state": "MN", "_id": "55032"} -{"city": "WELCH", "loc": [-92.862566, 44.718932], "pop": 20769, "state": "MN", "_id": "55033"} -{"city": "HINCKLEY", "loc": [-92.899333, 46.018876], "pop": 2611, "state": "MN", "_id": "55037"} -{"city": "CENTERVILLE", "loc": [-93.003493, 45.1628], "pop": 6520, "state": "MN", "_id": "55038"} -{"city": "ISANTI", "loc": [-93.226565, 45.468212], "pop": 8004, "state": "MN", "_id": "55040"} -{"city": "LAKE CITY", "loc": [-92.283778, 44.430493], "pop": 5154, "state": "MN", "_id": "55041"} -{"city": "LAKE ELMO", "loc": [-92.90565, 44.994609], "pop": 5903, "state": "MN", "_id": "55042"} -{"city": "LAKELAND", "loc": [-92.771592, 44.939384], "pop": 3792, "state": "MN", "_id": "55043"} -{"city": "LAKEVILLE", "loc": [-93.257802, 44.674865], "pop": 17741, "state": "MN", "_id": "55044"} -{"city": "LINDSTROM", "loc": [-92.842076, 45.387265], "pop": 5991, "state": "MN", "_id": "55045"} -{"city": "VESELI", "loc": [-93.423594, 44.462888], "pop": 4020, "state": "MN", "_id": "55046"} -{"city": "MARINE ON SAINT", "loc": [-92.822952, 45.183119], "pop": 4310, "state": "MN", "_id": "55047"} -{"city": "55048", "loc": [-92.450672, 46.076684], "pop": 454, "state": "MN", "_id": "55048"} -{"city": "MEDFORD", "loc": [-93.243721, 44.17415], "pop": 1305, "state": "MN", "_id": "55049"} -{"city": "MORA", "loc": [-93.294234, 45.895786], "pop": 10652, "state": "MN", "_id": "55051"} -{"city": "MORRISTOWN", "loc": [-93.452512, 44.234183], "pop": 1425, "state": "MN", "_id": "55052"} -{"city": "NERSTRAND", "loc": [-93.0855, 44.353769], "pop": 687, "state": "MN", "_id": "55053"} -{"city": "NEWPORT", "loc": [-92.998603, 44.872522], "pop": 3720, "state": "MN", "_id": "55055"} -{"city": "NORTH BRANCH", "loc": [-92.937097, 45.516479], "pop": 6024, "state": "MN", "_id": "55056"} -{"city": "NORTHFIELD", "loc": [-93.166826, 44.458724], "pop": 17995, "state": "MN", "_id": "55057"} -{"city": "OWATONNA", "loc": [-93.21914, 44.080478], "pop": 23860, "state": "MN", "_id": "55060"} -{"city": "BEROUN", "loc": [-92.982604, 45.832273], "pop": 6858, "state": "MN", "_id": "55063"} -{"city": "RANDOLPH", "loc": [-93.019619, 44.527386], "pop": 370, "state": "MN", "_id": "55065"} -{"city": "RED WING", "loc": [-92.548559, 44.552779], "pop": 17033, "state": "MN", "_id": "55066"} -{"city": "ROCK CREEK", "loc": [-92.930826, 45.762404], "pop": 1040, "state": "MN", "_id": "55067"} -{"city": "ROSEMOUNT", "loc": [-93.158844, 44.728919], "pop": 14747, "state": "MN", "_id": "55068"} -{"city": "RUSH CITY", "loc": [-92.997876, 45.684496], "pop": 3566, "state": "MN", "_id": "55069"} -{"city": "SAINT FRANCIS", "loc": [-93.359832, 45.390284], "pop": 2789, "state": "MN", "_id": "55070"} -{"city": "SAINT PAUL PARK", "loc": [-92.987331, 44.834404], "pop": 5517, "state": "MN", "_id": "55071"} -{"city": "MARKVILLE", "loc": [-92.871001, 46.121355], "pop": 3299, "state": "MN", "_id": "55072"} -{"city": "SCANDIA", "loc": [-92.829158, 45.269666], "pop": 2024, "state": "MN", "_id": "55073"} -{"city": "SHAFER", "loc": [-92.760889, 45.357561], "pop": 1519, "state": "MN", "_id": "55074"} -{"city": "SOUTH SAINT PAUL", "loc": [-93.046013, 44.888128], "pop": 20027, "state": "MN", "_id": "55075"} -{"city": "INVER GROVE HEIG", "loc": [-93.034746, 44.841966], "pop": 14444, "state": "MN", "_id": "55076"} -{"city": "INVER GROVE HEIG", "loc": [-93.073938, 44.850123], "pop": 8309, "state": "MN", "_id": "55077"} -{"city": "STACY", "loc": [-93.017743, 45.397509], "pop": 5936, "state": "MN", "_id": "55079"} -{"city": "STANCHFIELD", "loc": [-93.243302, 45.667513], "pop": 913, "state": "MN", "_id": "55080"} -{"city": "OAK PARK HEIGHTS", "loc": [-92.830032, 45.050007], "pop": 25247, "state": "MN", "_id": "55082"} -{"city": "TAYLORS FALLS", "loc": [-92.690593, 45.41825], "pop": 1421, "state": "MN", "_id": "55084"} -{"city": "WARSAW", "loc": [-93.364181, 44.250439], "pop": 1236, "state": "MN", "_id": "55087"} -{"city": "WEBSTER", "loc": [-93.334418, 44.502633], "pop": 1452, "state": "MN", "_id": "55088"} -{"city": "WELCH", "loc": [-92.726259, 44.603006], "pop": 678, "state": "MN", "_id": "55089"} -{"city": "EAST BETHEL", "loc": [-93.068565, 45.324337], "pop": 7514, "state": "MN", "_id": "55092"} -{"city": "SAINT PAUL", "loc": [-93.083167, 44.969963], "pop": 18352, "state": "MN", "_id": "55101"} -{"city": "SAINT PAUL", "loc": [-93.120852, 44.937228], "pop": 18585, "state": "MN", "_id": "55102"} -{"city": "SAINT PAUL", "loc": [-93.121594, 44.960798], "pop": 12169, "state": "MN", "_id": "55103"} -{"city": "SAINT PAUL", "loc": [-93.15797, 44.953179], "pop": 46396, "state": "MN", "_id": "55104"} -{"city": "SAINT PAUL", "loc": [-93.165148, 44.934723], "pop": 26216, "state": "MN", "_id": "55105"} -{"city": "SAINT PAUL", "loc": [-93.048817, 44.968384], "pop": 47905, "state": "MN", "_id": "55106"} -{"city": "WEST SAINT PAUL", "loc": [-93.086157, 44.927235], "pop": 15825, "state": "MN", "_id": "55107"} -{"city": "LAUDERDALE", "loc": [-93.17458, 44.982217], "pop": 17285, "state": "MN", "_id": "55108"} -{"city": "NORTH SAINT PAUL", "loc": [-93.017072, 45.011859], "pop": 28263, "state": "MN", "_id": "55109"} -{"city": "WHITE BEAR LAKE", "loc": [-93.011299, 45.074527], "pop": 38649, "state": "MN", "_id": "55110"} -{"city": "FORT SNELLING", "loc": [-93.202579, 44.901548], "pop": 97, "state": "MN", "_id": "55111"} -{"city": "NEW BRIGHTON", "loc": [-93.199691, 45.074129], "pop": 44128, "state": "MN", "_id": "55112"} -{"city": "ROSEVILLE", "loc": [-93.149245, 45.012876], "pop": 37302, "state": "MN", "_id": "55113"} -{"city": "SAINT PAUL", "loc": [-93.198067, 44.967968], "pop": 1402, "state": "MN", "_id": "55114"} -{"city": "WHITE BEAR LAKE", "loc": [-92.954847, 45.061132], "pop": 6201, "state": "MN", "_id": "55115"} -{"city": "SAINT PAUL", "loc": [-93.172747, 44.914007], "pop": 23868, "state": "MN", "_id": "55116"} -{"city": "LITTLE CANADA", "loc": [-93.103659, 44.992165], "pop": 38771, "state": "MN", "_id": "55117"} -{"city": "WEST SAINT PAUL", "loc": [-93.096435, 44.902691], "pop": 25866, "state": "MN", "_id": "55118"} -{"city": "MAPLEWOOD", "loc": [-93.008019, 44.955384], "pop": 33887, "state": "MN", "_id": "55119"} -{"city": "EAGAN", "loc": [-93.12902, 44.873825], "pop": 3113, "state": "MN", "_id": "55120"} -{"city": "EAGAN", "loc": [-93.16753, 44.843039], "pop": 6047, "state": "MN", "_id": "55121"} -{"city": "EAGAN", "loc": [-93.196937, 44.803593], "pop": 23875, "state": "MN", "_id": "55122"} -{"city": "EAGAN", "loc": [-93.14135, 44.809764], "pop": 17487, "state": "MN", "_id": "55123"} -{"city": "APPLE VALLEY", "loc": [-93.20776, 44.746147], "pop": 34598, "state": "MN", "_id": "55124"} -{"city": "WOODBURY", "loc": [-92.951413, 44.916195], "pop": 20075, "state": "MN", "_id": "55125"} -{"city": "SHOREVIEW", "loc": [-93.134367, 45.083334], "pop": 24597, "state": "MN", "_id": "55126"} -{"city": "VADNAIS HEIGHTS", "loc": [-93.07875, 45.070839], "pop": 14628, "state": "MN", "_id": "55127"} -{"city": "OAKDALE", "loc": [-92.968128, 44.984648], "pop": 19099, "state": "MN", "_id": "55128"} -{"city": "MENDOTA", "loc": [-93.161221, 44.885796], "pop": 164, "state": "MN", "_id": "55150"} -{"city": "ALBERTVILLE", "loc": [-93.646899, 45.253425], "pop": 3132, "state": "MN", "_id": "55301"} -{"city": "ANNANDALE", "loc": [-94.106072, 45.248272], "pop": 6174, "state": "MN", "_id": "55302"} -{"city": "RAMSEY", "loc": [-93.407822, 45.238537], "pop": 33161, "state": "MN", "_id": "55303"} -{"city": "HAM LAKE", "loc": [-93.284531, 45.25068], "pop": 25122, "state": "MN", "_id": "55304"} -{"city": "ARLINGTON", "loc": [-94.076195, 44.615279], "pop": 2997, "state": "MN", "_id": "55307"} -{"city": "BECKER", "loc": [-93.840966, 45.436539], "pop": 3589, "state": "MN", "_id": "55308"} -{"city": "BIG LAKE", "loc": [-93.739917, 45.350648], "pop": 6703, "state": "MN", "_id": "55309"} -{"city": "BIRD ISLAND", "loc": [-94.871634, 44.750729], "pop": 1885, "state": "MN", "_id": "55310"} -{"city": "BROWNTON", "loc": [-94.330611, 44.728145], "pop": 1686, "state": "MN", "_id": "55312"} -{"city": "BUFFALO", "loc": [-93.863479, 45.181371], "pop": 8942, "state": "MN", "_id": "55313"} -{"city": "BUFFALO LAKE", "loc": [-94.591207, 44.770885], "pop": 1431, "state": "MN", "_id": "55314"} -{"city": "CARVER", "loc": [-93.687949, 44.716898], "pop": 1181, "state": "MN", "_id": "55315"} -{"city": "CHAMPLIN", "loc": [-93.381927, 45.170042], "pop": 16849, "state": "MN", "_id": "55316"} -{"city": "CHANHASSEN", "loc": [-93.535906, 44.867924], "pop": 8499, "state": "MN", "_id": "55317"} -{"city": "CHASKA", "loc": [-93.608314, 44.806071], "pop": 13907, "state": "MN", "_id": "55318"} -{"city": "CLEAR LAKE", "loc": [-93.968391, 45.479477], "pop": 3280, "state": "MN", "_id": "55319"} -{"city": "CLEARWATER", "loc": [-94.045246, 45.387694], "pop": 1752, "state": "MN", "_id": "55320"} -{"city": "COKATO", "loc": [-94.191054, 45.074761], "pop": 4053, "state": "MN", "_id": "55321"} -{"city": "COLOGNE", "loc": [-93.798249, 44.764591], "pop": 2298, "state": "MN", "_id": "55322"} -{"city": "DARWIN", "loc": [-94.424926, 45.102949], "pop": 933, "state": "MN", "_id": "55324"} -{"city": "DASSEL", "loc": [-94.330475, 45.103208], "pop": 4818, "state": "MN", "_id": "55325"} -{"city": "DAYTON", "loc": [-93.466795, 45.198164], "pop": 4251, "state": "MN", "_id": "55327"} -{"city": "DELANO", "loc": [-93.801644, 45.034222], "pop": 5574, "state": "MN", "_id": "55328"} -{"city": "EDEN VALLEY", "loc": [-94.603911, 45.302006], "pop": 1955, "state": "MN", "_id": "55329"} -{"city": "ELK RIVER", "loc": [-93.5814, 45.313601], "pop": 17703, "state": "MN", "_id": "55330"} -{"city": "EXCELSIOR", "loc": [-93.579132, 44.900704], "pop": 15860, "state": "MN", "_id": "55331"} -{"city": "FAIRFAX", "loc": [-94.725626, 44.532942], "pop": 2250, "state": "MN", "_id": "55332"} -{"city": "FRANKLIN", "loc": [-94.893094, 44.542132], "pop": 702, "state": "MN", "_id": "55333"} -{"city": "GAYLORD", "loc": [-94.195543, 44.546298], "pop": 3099, "state": "MN", "_id": "55334"} -{"city": "GIBBON", "loc": [-94.54668, 44.560587], "pop": 1718, "state": "MN", "_id": "55335"} -{"city": "GLENCOE", "loc": [-94.161554, 44.778001], "pop": 6275, "state": "MN", "_id": "55336"} -{"city": "BURNSVILLE", "loc": [-93.275283, 44.76086], "pop": 51421, "state": "MN", "_id": "55337"} -{"city": "GREEN ISLE", "loc": [-93.932635, 44.668665], "pop": 1353, "state": "MN", "_id": "55338"} -{"city": "HAMBURG", "loc": [-93.964317, 44.731452], "pop": 696, "state": "MN", "_id": "55339"} -{"city": "HAMEL", "loc": [-93.575957, 45.079951], "pop": 4930, "state": "MN", "_id": "55340"} -{"city": "HECTOR", "loc": [-94.705824, 44.748489], "pop": 1896, "state": "MN", "_id": "55342"} -{"city": "EDEN PRAIRIE", "loc": [-93.416452, 44.933318], "pop": 40067, "state": "MN", "_id": "55343"} -{"city": "EDEN PRAIRIE", "loc": [-93.437573, 44.857426], "pop": 7901, "state": "MN", "_id": "55344"} -{"city": "MINNETONKA", "loc": [-93.484997, 44.913808], "pop": 22535, "state": "MN", "_id": "55345"} -{"city": "EDEN PRAIRIE", "loc": [-93.483028, 44.877106], "pop": 16269, "state": "MN", "_id": "55346"} -{"city": "EDEN PRAIRIE", "loc": [-93.438934, 44.834217], "pop": 15141, "state": "MN", "_id": "55347"} -{"city": "HOWARD LAKE", "loc": [-94.069515, 45.061594], "pop": 3443, "state": "MN", "_id": "55349"} -{"city": "HUTCHINSON", "loc": [-94.384691, 44.894554], "pop": 15549, "state": "MN", "_id": "55350"} -{"city": "JORDAN", "loc": [-93.61946, 44.671321], "pop": 4679, "state": "MN", "_id": "55352"} -{"city": "KIMBALL", "loc": [-94.302837, 45.343584], "pop": 2226, "state": "MN", "_id": "55353"} -{"city": "LESTER PRAIRIE", "loc": [-94.055094, 44.873898], "pop": 2160, "state": "MN", "_id": "55354"} -{"city": "LITCHFIELD", "loc": [-94.526805, 45.126272], "pop": 8528, "state": "MN", "_id": "55355"} -{"city": "LONG LAKE", "loc": [-93.581798, 44.991228], "pop": 5700, "state": "MN", "_id": "55356"} -{"city": "LORETTO", "loc": [-93.669165, 45.106099], "pop": 1492, "state": "MN", "_id": "55357"} -{"city": "MAPLE LAKE", "loc": [-93.963793, 45.220236], "pop": 4084, "state": "MN", "_id": "55358"} -{"city": "MAPLE PLAIN", "loc": [-93.700214, 44.978686], "pop": 6885, "state": "MN", "_id": "55359"} -{"city": "MAYER", "loc": [-93.885925, 44.902231], "pop": 1233, "state": "MN", "_id": "55360"} -{"city": "MONTICELLO", "loc": [-93.802252, 45.295557], "pop": 8919, "state": "MN", "_id": "55362"} -{"city": "MONTROSE", "loc": [-93.931762, 45.089737], "pop": 2847, "state": "MN", "_id": "55363"} -{"city": "MOUND", "loc": [-93.656087, 44.938158], "pop": 13251, "state": "MN", "_id": "55364"} -{"city": "NEW AUBURN", "loc": [-94.20567, 44.675919], "pop": 784, "state": "MN", "_id": "55366"} -{"city": "NEW GERMANY", "loc": [-93.970131, 44.899431], "pop": 1136, "state": "MN", "_id": "55367"} -{"city": "NORWOOD", "loc": [-93.930067, 44.766786], "pop": 1761, "state": "MN", "_id": "55368"} -{"city": "MAPLE GROVE", "loc": [-93.43987, 45.108636], "pop": 41581, "state": "MN", "_id": "55369"} -{"city": "PLATO", "loc": [-94.050583, 44.765728], "pop": 990, "state": "MN", "_id": "55370"} -{"city": "PRINCETON", "loc": [-93.596143, 45.585115], "pop": 8975, "state": "MN", "_id": "55371"} -{"city": "PRIOR LAKE", "loc": [-93.410057, 44.710694], "pop": 18539, "state": "MN", "_id": "55372"} -{"city": "ROCKFORD", "loc": [-93.765832, 45.103074], "pop": 6268, "state": "MN", "_id": "55373"} -{"city": "ROGERS", "loc": [-93.581445, 45.171484], "pop": 4957, "state": "MN", "_id": "55374"} -{"city": "SAINT MICHAEL", "loc": [-93.659253, 45.206409], "pop": 5441, "state": "MN", "_id": "55376"} -{"city": "SAVAGE", "loc": [-93.343445, 44.761547], "pop": 7764, "state": "MN", "_id": "55378"} -{"city": "SHAKOPEE", "loc": [-93.519748, 44.7793], "pop": 14755, "state": "MN", "_id": "55379"} -{"city": "SILVER CREEK", "loc": [-93.949622, 45.326474], "pop": 1835, "state": "MN", "_id": "55380"} -{"city": "SILVER LAKE", "loc": [-94.197383, 44.913945], "pop": 2012, "state": "MN", "_id": "55381"} -{"city": "SOUTH HAVEN", "loc": [-94.162768, 45.346008], "pop": 4377, "state": "MN", "_id": "55382"} -{"city": "SPRING PARK", "loc": [-93.634104, 44.935566], "pop": 1571, "state": "MN", "_id": "55384"} -{"city": "STEWART", "loc": [-94.451572, 44.725928], "pop": 1410, "state": "MN", "_id": "55385"} -{"city": "VICTORIA", "loc": [-93.656094, 44.858223], "pop": 1367, "state": "MN", "_id": "55386"} -{"city": "WACONIA", "loc": [-93.7784, 44.851029], "pop": 5844, "state": "MN", "_id": "55387"} -{"city": "WATERTOWN", "loc": [-93.848159, 44.959533], "pop": 3520, "state": "MN", "_id": "55388"} -{"city": "WATKINS", "loc": [-94.429677, 45.308652], "pop": 1998, "state": "MN", "_id": "55389"} -{"city": "WAVERLY", "loc": [-93.954538, 45.041265], "pop": 1691, "state": "MN", "_id": "55390"} -{"city": "WAYZATA", "loc": [-93.520581, 44.958189], "pop": 15196, "state": "MN", "_id": "55391"} -{"city": "WINSTED", "loc": [-94.055168, 44.958459], "pop": 2548, "state": "MN", "_id": "55395"} -{"city": "WINTHROP", "loc": [-94.369847, 44.543566], "pop": 2630, "state": "MN", "_id": "55396"} -{"city": "YOUNG AMERICA", "loc": [-93.918049, 44.792905], "pop": 2527, "state": "MN", "_id": "55397"} -{"city": "ZIMMERMAN", "loc": [-93.587859, 45.455321], "pop": 5786, "state": "MN", "_id": "55398"} -{"city": "MINNEAPOLIS", "loc": [-93.268251, 44.983473], "pop": 2716, "state": "MN", "_id": "55401"} -{"city": "MINNEAPOLIS", "loc": [-93.275871, 44.976184], "pop": 261, "state": "MN", "_id": "55402"} -{"city": "MINNEAPOLIS", "loc": [-93.282841, 44.967345], "pop": 14098, "state": "MN", "_id": "55403"} -{"city": "MINNEAPOLIS", "loc": [-93.26416, 44.960891], "pop": 19903, "state": "MN", "_id": "55404"} -{"city": "MINNEAPOLIS", "loc": [-93.299096, 44.968734], "pop": 16148, "state": "MN", "_id": "55405"} -{"city": "MINNEAPOLIS", "loc": [-93.221357, 44.938359], "pop": 31760, "state": "MN", "_id": "55406"} -{"city": "MINNEAPOLIS", "loc": [-93.2545, 44.937787], "pop": 34059, "state": "MN", "_id": "55407"} -{"city": "MINNEAPOLIS", "loc": [-93.286173, 44.946575], "pop": 29685, "state": "MN", "_id": "55408"} -{"city": "MINNEAPOLIS", "loc": [-93.28182, 44.926378], "pop": 20415, "state": "MN", "_id": "55409"} -{"city": "EDINA", "loc": [-93.318187, 44.915366], "pop": 16834, "state": "MN", "_id": "55410"} -{"city": "MINNEAPOLIS", "loc": [-93.300548, 44.999601], "pop": 30088, "state": "MN", "_id": "55411"} -{"city": "MINNEAPOLIS", "loc": [-93.302033, 45.024236], "pop": 21329, "state": "MN", "_id": "55412"} -{"city": "MINNEAPOLIS", "loc": [-93.255194, 44.997994], "pop": 12201, "state": "MN", "_id": "55413"} -{"city": "MINNEAPOLIS", "loc": [-93.219904, 44.977908], "pop": 10535, "state": "MN", "_id": "55414"} -{"city": "MINNEAPOLIS", "loc": [-93.264403, 44.971455], "pop": 4401, "state": "MN", "_id": "55415"} -{"city": "SAINT LOUIS PARK", "loc": [-93.340344, 44.946899], "pop": 26211, "state": "MN", "_id": "55416"} -{"city": "MINNEAPOLIS", "loc": [-93.23606, 44.905371], "pop": 25445, "state": "MN", "_id": "55417"} -{"city": "MINNEAPOLIS", "loc": [-93.240108, 45.01923], "pop": 30904, "state": "MN", "_id": "55418"} -{"city": "MINNEAPOLIS", "loc": [-93.288618, 44.902567], "pop": 19740, "state": "MN", "_id": "55419"} -{"city": "BLOOMINGTON", "loc": [-93.276034, 44.837284], "pop": 22028, "state": "MN", "_id": "55420"} -{"city": "COLUMBIA HEIGHTS", "loc": [-93.246095, 45.049582], "pop": 25668, "state": "MN", "_id": "55421"} -{"city": "ROBBINSDALE", "loc": [-93.339769, 45.016722], "pop": 28922, "state": "MN", "_id": "55422"} -{"city": "RICHFIELD", "loc": [-93.281351, 44.875731], "pop": 35710, "state": "MN", "_id": "55423"} -{"city": "EDINA", "loc": [-93.335005, 44.904385], "pop": 12342, "state": "MN", "_id": "55424"} -{"city": "BLOOMINGTON", "loc": [-93.249413, 44.843198], "pop": 8501, "state": "MN", "_id": "55425"} -{"city": "SAINT LOUIS PARK", "loc": [-93.379627, 44.954448], "pop": 25951, "state": "MN", "_id": "55426"} -{"city": "GOLDEN VALLEY", "loc": [-93.381585, 45.010374], "pop": 24406, "state": "MN", "_id": "55427"} -{"city": "CRYSTAL", "loc": [-93.376908, 45.060299], "pop": 33233, "state": "MN", "_id": "55428"} -{"city": "BROOKLYN CENTER", "loc": [-93.340203, 45.067667], "pop": 22597, "state": "MN", "_id": "55429"} -{"city": "BROOKLYN CENTER", "loc": [-93.299068, 45.061106], "pop": 21561, "state": "MN", "_id": "55430"} -{"city": "BLOOMINGTON", "loc": [-93.312322, 44.827776], "pop": 19428, "state": "MN", "_id": "55431"} -{"city": "FRIDLEY", "loc": [-93.253905, 45.095695], "pop": 31132, "state": "MN", "_id": "55432"} -{"city": "COON RAPIDS", "loc": [-93.326253, 45.168192], "pop": 27580, "state": "MN", "_id": "55433"} -{"city": "BLAINE", "loc": [-93.242557, 45.168083], "pop": 33410, "state": "MN", "_id": "55434"} -{"city": "EDINA", "loc": [-93.371452, 44.877143], "pop": 8926, "state": "MN", "_id": "55435"} -{"city": "EDINA", "loc": [-93.370997, 44.902305], "pop": 11750, "state": "MN", "_id": "55436"} -{"city": "BLOOMINGTON", "loc": [-93.343499, 44.823279], "pop": 19472, "state": "MN", "_id": "55437"} -{"city": "BLOOMINGTON", "loc": [-93.380141, 44.823924], "pop": 16896, "state": "MN", "_id": "55438"} -{"city": "EDINA", "loc": [-93.332169, 44.873716], "pop": 10571, "state": "MN", "_id": "55439"} -{"city": "PLYMOUTH", "loc": [-93.422782, 45.009836], "pop": 16840, "state": "MN", "_id": "55441"} -{"city": "PLYMOUTH", "loc": [-93.426316, 45.045151], "pop": 9893, "state": "MN", "_id": "55442"} -{"city": "BROOKLYN CENTER", "loc": [-93.340184, 45.105586], "pop": 20896, "state": "MN", "_id": "55443"} -{"city": "BROOKLYN CENTER", "loc": [-93.302455, 45.100172], "pop": 12538, "state": "MN", "_id": "55444"} -{"city": "BROOKLYN PARK", "loc": [-93.373495, 45.103956], "pop": 6398, "state": "MN", "_id": "55445"} -{"city": "PLYMOUTH", "loc": [-93.472323, 45.032446], "pop": 7704, "state": "MN", "_id": "55446"} -{"city": "PLYMOUTH", "loc": [-93.494695, 44.998593], "pop": 16668, "state": "MN", "_id": "55447"} -{"city": "COON RAPIDS", "loc": [-93.289699, 45.180626], "pop": 25234, "state": "MN", "_id": "55448"} -{"city": "MINNEAPOLIS", "loc": [-93.247414, 44.865883], "pop": 0, "state": "MN", "_id": "55450"} -{"city": "MINNEAPOLIS", "loc": [-93.242898, 44.968161], "pop": 8815, "state": "MN", "_id": "55454"} -{"city": "MINNEAPOLIS", "loc": [-93.23928, 44.981562], "pop": 12216, "state": "MN", "_id": "55455"} -{"city": "LORETTO", "loc": [-93.664534, 45.05604], "pop": 541, "state": "MN", "_id": "55599"} -{"city": "BRIMSON", "loc": [-91.862521, 47.314162], "pop": 176, "state": "MN", "_id": "55602"} -{"city": "FINLAND", "loc": [-91.209597, 47.419716], "pop": 565, "state": "MN", "_id": "55603"} -{"city": "GRAND MARAIS", "loc": [-90.339114, 47.77577], "pop": 2404, "state": "MN", "_id": "55604"} -{"city": "GRAND PORTAGE", "loc": [-89.698862, 47.959065], "pop": 350, "state": "MN", "_id": "55605"} -{"city": "HOVLAND", "loc": [-90.04757, 47.83415], "pop": 218, "state": "MN", "_id": "55606"} -{"city": "ISABELLA", "loc": [-91.517327, 47.626719], "pop": 402, "state": "MN", "_id": "55607"} -{"city": "LUTSEN", "loc": [-90.638059, 47.683066], "pop": 381, "state": "MN", "_id": "55612"} -{"city": "SCHROEDER", "loc": [-90.933807, 47.542185], "pop": 174, "state": "MN", "_id": "55613"} -{"city": "LITTLE MARAIS", "loc": [-91.277753, 47.299905], "pop": 2552, "state": "MN", "_id": "55614"} -{"city": "TOFTE", "loc": [-90.783135, 47.760792], "pop": 341, "state": "MN", "_id": "55615"} -{"city": "TWO HARBORS", "loc": [-91.678264, 47.039364], "pop": 6511, "state": "MN", "_id": "55616"} -{"city": "ALBORN", "loc": [-92.557937, 46.978229], "pop": 601, "state": "MN", "_id": "55702"} -{"city": "ANGORA", "loc": [-92.641332, 47.757738], "pop": 291, "state": "MN", "_id": "55703"} -{"city": "ASKOV", "loc": [-92.752812, 46.196408], "pop": 919, "state": "MN", "_id": "55704"} -{"city": "AURORA", "loc": [-92.241486, 47.495096], "pop": 3674, "state": "MN", "_id": "55705"} -{"city": "BABBITT", "loc": [-91.956951, 47.709121], "pop": 2014, "state": "MN", "_id": "55706"} -{"city": "BARNUM", "loc": [-92.629167, 46.519616], "pop": 2028, "state": "MN", "_id": "55707"} -{"city": "BOVEY", "loc": [-93.372322, 47.286788], "pop": 5077, "state": "MN", "_id": "55709"} -{"city": "BRITT", "loc": [-92.632062, 47.65496], "pop": 1229, "state": "MN", "_id": "55710"} -{"city": "BROOKSTON", "loc": [-92.643005, 46.838425], "pop": 621, "state": "MN", "_id": "55711"} -{"city": "BRUNO", "loc": [-92.618994, 46.284496], "pop": 259, "state": "MN", "_id": "55712"} -{"city": "CANYON", "loc": [-92.459361, 47.078525], "pop": 230, "state": "MN", "_id": "55717"} -{"city": "CARLTON", "loc": [-92.470984, 46.6484], "pop": 3401, "state": "MN", "_id": "55718"} -{"city": "CHISHOLM", "loc": [-92.861693, 47.500744], "pop": 7412, "state": "MN", "_id": "55719"} -{"city": "CLOQUET", "loc": [-92.45282, 46.726041], "pop": 13503, "state": "MN", "_id": "55720"} -{"city": "COHASSET", "loc": [-93.639154, 47.269112], "pop": 3694, "state": "MN", "_id": "55721"} -{"city": "COOK", "loc": [-92.721038, 47.844193], "pop": 2705, "state": "MN", "_id": "55723"} -{"city": "KELSEY", "loc": [-92.444921, 47.163803], "pop": 430, "state": "MN", "_id": "55724"} -{"city": "CRANE LAKE", "loc": [-92.489596, 48.259387], "pop": 74, "state": "MN", "_id": "55725"} -{"city": "CROMWELL", "loc": [-92.873942, 46.671784], "pop": 1048, "state": "MN", "_id": "55726"} -{"city": "CULVER", "loc": [-92.552403, 46.911886], "pop": 309, "state": "MN", "_id": "55727"} -{"city": "ELY", "loc": [-91.857042, 47.903435], "pop": 5685, "state": "MN", "_id": "55731"} -{"city": "EMBARRASS", "loc": [-92.21011, 47.665847], "pop": 1143, "state": "MN", "_id": "55732"} -{"city": "ESKO", "loc": [-92.356918, 46.712551], "pop": 4102, "state": "MN", "_id": "55733"} -{"city": "EVELETH", "loc": [-92.528037, 47.451047], "pop": 6135, "state": "MN", "_id": "55734"} -{"city": "FINLAYSON", "loc": [-92.938924, 46.212111], "pop": 1264, "state": "MN", "_id": "55735"} -{"city": "FLOODWOOD", "loc": [-92.916682, 46.907589], "pop": 1475, "state": "MN", "_id": "55736"} -{"city": "GHEEN", "loc": [-92.906892, 47.946475], "pop": 255, "state": "MN", "_id": "55740"} -{"city": "GILBERT", "loc": [-92.402464, 47.488487], "pop": 4721, "state": "MN", "_id": "55741"} -{"city": "GOODLAND", "loc": [-93.146914, 47.192426], "pop": 437, "state": "MN", "_id": "55742"} -{"city": "LA PRAIRIE", "loc": [-93.527672, 47.223472], "pop": 18067, "state": "MN", "_id": "55744"} -{"city": "HIBBING", "loc": [-92.935582, 47.4156], "pop": 20816, "state": "MN", "_id": "55746"} -{"city": "HILL CITY", "loc": [-93.599397, 46.996817], "pop": 762, "state": "MN", "_id": "55748"} -{"city": "HOLYOKE", "loc": [-92.374955, 46.466445], "pop": 160, "state": "MN", "_id": "55749"} -{"city": "HOYT LAKES", "loc": [-92.140046, 47.514957], "pop": 2348, "state": "MN", "_id": "55750"} -{"city": "IRON", "loc": [-92.619502, 47.411455], "pop": 1210, "state": "MN", "_id": "55751"} -{"city": "JACOBSON", "loc": [-93.306359, 46.977187], "pop": 387, "state": "MN", "_id": "55752"} -{"city": "55755", "loc": [-92.612956, 47.157817], "pop": 175, "state": "MN", "_id": "55755"} -{"city": "KERRICK", "loc": [-92.578, 46.379151], "pop": 487, "state": "MN", "_id": "55756"} -{"city": "KETTLE RIVER", "loc": [-92.904746, 46.502899], "pop": 1136, "state": "MN", "_id": "55757"} -{"city": "MC GREGOR", "loc": [-93.29562, 46.686424], "pop": 1897, "state": "MN", "_id": "55760"} -{"city": "MAHTOWA", "loc": [-92.606505, 46.582588], "pop": 833, "state": "MN", "_id": "55762"} -{"city": "MAKINEN", "loc": [-92.344584, 47.341603], "pop": 1028, "state": "MN", "_id": "55763"} -{"city": "MEADOWLANDS", "loc": [-92.788357, 47.102064], "pop": 1172, "state": "MN", "_id": "55765"} -{"city": "MELRUDE", "loc": [-92.42644, 47.242318], "pop": 121, "state": "MN", "_id": "55766"} -{"city": "MOOSE LAKE", "loc": [-92.746648, 46.44724], "pop": 2699, "state": "MN", "_id": "55767"} -{"city": "MOUNTAIN IRON", "loc": [-92.624274, 47.513336], "pop": 1790, "state": "MN", "_id": "55768"} -{"city": "NASHWAUK", "loc": [-93.216818, 47.42003], "pop": 4058, "state": "MN", "_id": "55769"} -{"city": "BUYCK", "loc": [-92.845555, 47.933881], "pop": 1252, "state": "MN", "_id": "55771"} -{"city": "PARKVILLE", "loc": [-92.584051, 47.510296], "pop": 1787, "state": "MN", "_id": "55773"} -{"city": "PENGILLY", "loc": [-93.193667, 47.315089], "pop": 1173, "state": "MN", "_id": "55775"} -{"city": "SAGINAW", "loc": [-92.391723, 46.879462], "pop": 3185, "state": "MN", "_id": "55779"} -{"city": "SAWYER", "loc": [-92.609612, 46.711799], "pop": 833, "state": "MN", "_id": "55780"} -{"city": "STURGEON LAKE", "loc": [-92.818246, 46.383685], "pop": 1883, "state": "MN", "_id": "55783"} -{"city": "SWAN RIVER", "loc": [-93.196436, 47.07394], "pop": 231, "state": "MN", "_id": "55784"} -{"city": "SWATARA", "loc": [-93.668483, 46.929871], "pop": 331, "state": "MN", "_id": "55785"} -{"city": "TAMARACK", "loc": [-93.1342, 46.617873], "pop": 559, "state": "MN", "_id": "55787"} -{"city": "TOGO", "loc": [-93.201545, 47.76843], "pop": 632, "state": "MN", "_id": "55788"} -{"city": "TOWER", "loc": [-92.287781, 47.808926], "pop": 2280, "state": "MN", "_id": "55790"} -{"city": "VIRGINIA", "loc": [-92.528525, 47.537078], "pop": 11153, "state": "MN", "_id": "55792"} -{"city": "WARBA", "loc": [-93.276417, 47.13611], "pop": 431, "state": "MN", "_id": "55793"} -{"city": "WILLOW RIVER", "loc": [-92.830931, 46.294883], "pop": 968, "state": "MN", "_id": "55795"} -{"city": "WRENSHALL", "loc": [-92.371772, 46.592114], "pop": 839, "state": "MN", "_id": "55797"} -{"city": "WRIGHT", "loc": [-93.002863, 46.675324], "pop": 417, "state": "MN", "_id": "55798"} -{"city": "ZIM", "loc": [-92.629606, 47.317591], "pop": 507, "state": "MN", "_id": "55799"} -{"city": "DULUTH", "loc": [-91.846731, 47.094431], "pop": 230, "state": "MN", "_id": "55801"} -{"city": "DULUTH", "loc": [-92.086497, 46.768475], "pop": 2639, "state": "MN", "_id": "55802"} -{"city": "DULUTH", "loc": [-92.094057, 46.874913], "pop": 14740, "state": "MN", "_id": "55803"} -{"city": "DULUTH", "loc": [-92.007433, 46.855131], "pop": 14207, "state": "MN", "_id": "55804"} -{"city": "DULUTH", "loc": [-92.094553, 46.798733], "pop": 10849, "state": "MN", "_id": "55805"} -{"city": "DULUTH", "loc": [-92.127871, 46.771457], "pop": 9723, "state": "MN", "_id": "55806"} -{"city": "DULUTH", "loc": [-92.169821, 46.740783], "pop": 10257, "state": "MN", "_id": "55807"} -{"city": "DULUTH", "loc": [-92.22261, 46.681002], "pop": 5903, "state": "MN", "_id": "55808"} -{"city": "PROCTOR", "loc": [-92.232332, 46.74459], "pop": 6881, "state": "MN", "_id": "55810"} -{"city": "HERMANTOWN", "loc": [-92.168225, 46.81341], "pop": 23478, "state": "MN", "_id": "55811"} -{"city": "DULUTH", "loc": [-92.076693, 46.810598], "pop": 10296, "state": "MN", "_id": "55812"} -{"city": "ROCHESTER", "loc": [-92.48962, 44.049572], "pop": 33744, "state": "MN", "_id": "55901"} -{"city": "ROCHESTER", "loc": [-92.483519, 44.003217], "pop": 13594, "state": "MN", "_id": "55902"} -{"city": "ROCHESTER", "loc": [-92.397276, 44.010545], "pop": 7854, "state": "MN", "_id": "55904"} -{"city": "ROCHESTER", "loc": [-92.446874, 44.021001], "pop": 29174, "state": "MN", "_id": "55906"} -{"city": "ADAMS", "loc": [-92.730494, 43.559119], "pop": 1214, "state": "MN", "_id": "55909"} -{"city": "ALTURA", "loc": [-91.974474, 44.136114], "pop": 2913, "state": "MN", "_id": "55910"} -{"city": "AUSTIN", "loc": [-92.978374, 43.669538], "pop": 25655, "state": "MN", "_id": "55912"} -{"city": "BLOOMING PRAIRIE", "loc": [-93.06081, 43.897732], "pop": 3922, "state": "MN", "_id": "55917"} -{"city": "BROWNSDALE", "loc": [-92.873752, 43.724761], "pop": 1443, "state": "MN", "_id": "55918"} -{"city": "BROWNSVILLE", "loc": [-91.301226, 43.670732], "pop": 995, "state": "MN", "_id": "55919"} -{"city": "BYRON", "loc": [-92.630753, 44.037333], "pop": 7129, "state": "MN", "_id": "55920"} -{"city": "CALEDONIA", "loc": [-91.483657, 43.622079], "pop": 5049, "state": "MN", "_id": "55921"} -{"city": "CANTON", "loc": [-91.912957, 43.566687], "pop": 1390, "state": "MN", "_id": "55922"} -{"city": "CHATFIELD", "loc": [-92.157351, 43.836201], "pop": 3949, "state": "MN", "_id": "55923"} -{"city": "CLAREMONT", "loc": [-92.988839, 44.05223], "pop": 979, "state": "MN", "_id": "55924"} -{"city": "DAKOTA", "loc": [-91.39404, 43.914806], "pop": 725, "state": "MN", "_id": "55925"} -{"city": "DEXTER", "loc": [-92.726764, 43.715962], "pop": 588, "state": "MN", "_id": "55926"} -{"city": "DODGE CENTER", "loc": [-92.855376, 44.032514], "pop": 3319, "state": "MN", "_id": "55927"} -{"city": "DOVER", "loc": [-92.141511, 44.001457], "pop": 1265, "state": "MN", "_id": "55929"} -{"city": "ELGIN", "loc": [-92.253493, 44.135837], "pop": 1494, "state": "MN", "_id": "55932"} -{"city": "ELKTON", "loc": [-92.710407, 43.634806], "pop": 721, "state": "MN", "_id": "55933"} -{"city": "VIOLA", "loc": [-92.244068, 44.004294], "pop": 2579, "state": "MN", "_id": "55934"} -{"city": "FOUNTAIN", "loc": [-92.142288, 43.728404], "pop": 662, "state": "MN", "_id": "55935"} -{"city": "GRAND MEADOW", "loc": [-92.569203, 43.710065], "pop": 1653, "state": "MN", "_id": "55936"} -{"city": "GRANGER", "loc": [-92.156115, 43.544873], "pop": 385, "state": "MN", "_id": "55937"} -{"city": "HARMONY", "loc": [-92.014511, 43.566268], "pop": 1853, "state": "MN", "_id": "55939"} -{"city": "HAYFIELD", "loc": [-92.817489, 43.892259], "pop": 2254, "state": "MN", "_id": "55940"} -{"city": "HOKAH", "loc": [-91.345472, 43.750856], "pop": 1289, "state": "MN", "_id": "55941"} -{"city": "HOUSTON", "loc": [-91.562565, 43.792904], "pop": 3337, "state": "MN", "_id": "55943"} -{"city": "KASSON", "loc": [-92.74642, 44.024029], "pop": 4420, "state": "MN", "_id": "55944"} -{"city": "THEILMAN", "loc": [-92.00839, 44.305396], "pop": 738, "state": "MN", "_id": "55945"} -{"city": "KENYON", "loc": [-93.019661, 44.255237], "pop": 3437, "state": "MN", "_id": "55946"} -{"city": "LA CRESCENT", "loc": [-91.326325, 43.830686], "pop": 6700, "state": "MN", "_id": "55947"} -{"city": "LANESBORO", "loc": [-91.987719, 43.717447], "pop": 1191, "state": "MN", "_id": "55949"} -{"city": "LE ROY", "loc": [-92.506464, 43.531533], "pop": 1478, "state": "MN", "_id": "55951"} -{"city": "LEWISTON", "loc": [-91.866162, 43.970193], "pop": 2038, "state": "MN", "_id": "55952"} -{"city": "LYLE", "loc": [-92.932818, 43.530868], "pop": 1314, "state": "MN", "_id": "55953"} -{"city": "MABEL", "loc": [-91.780636, 43.544611], "pop": 1470, "state": "MN", "_id": "55954"} -{"city": "MANTORVILLE", "loc": [-92.757962, 44.070195], "pop": 1324, "state": "MN", "_id": "55955"} -{"city": "MAZEPPA", "loc": [-92.520683, 44.264568], "pop": 1560, "state": "MN", "_id": "55956"} -{"city": "MILLVILLE", "loc": [-92.267188, 44.235862], "pop": 579, "state": "MN", "_id": "55957"} -{"city": "MINNESOTA CITY", "loc": [-91.741808, 44.083222], "pop": 1185, "state": "MN", "_id": "55959"} -{"city": "ORONOCO", "loc": [-92.48496, 44.148861], "pop": 3319, "state": "MN", "_id": "55960"} -{"city": "OSTRANDER", "loc": [-92.415744, 43.597188], "pop": 654, "state": "MN", "_id": "55961"} -{"city": "PETERSON", "loc": [-91.844338, 43.77691], "pop": 901, "state": "MN", "_id": "55962"} -{"city": "PINE ISLAND", "loc": [-92.661347, 44.211009], "pop": 3832, "state": "MN", "_id": "55963"} -{"city": "PLAINVIEW", "loc": [-92.162125, 44.16373], "pop": 3303, "state": "MN", "_id": "55964"} -{"city": "PRESTON", "loc": [-92.096039, 43.664132], "pop": 1866, "state": "MN", "_id": "55965"} -{"city": "RACINE", "loc": [-92.531013, 43.78997], "pop": 1107, "state": "MN", "_id": "55967"} -{"city": "ROLLINGSTONE", "loc": [-91.815795, 44.102501], "pop": 1085, "state": "MN", "_id": "55969"} -{"city": "ROSE CREEK", "loc": [-92.862082, 43.627423], "pop": 952, "state": "MN", "_id": "55970"} -{"city": "RUSHFORD", "loc": [-91.75365, 43.821626], "pop": 2809, "state": "MN", "_id": "55971"} -{"city": "SAINT CHARLES", "loc": [-92.051738, 43.958481], "pop": 3704, "state": "MN", "_id": "55972"} -{"city": "SARGEANT", "loc": [-92.759517, 43.808976], "pop": 349, "state": "MN", "_id": "55973"} -{"city": "SPRING GROVE", "loc": [-91.636788, 43.562339], "pop": 2333, "state": "MN", "_id": "55974"} -{"city": "SPRING VALLEY", "loc": [-92.367985, 43.682288], "pop": 4506, "state": "MN", "_id": "55975"} -{"city": "STEWARTVILLE", "loc": [-92.454654, 43.867345], "pop": 6847, "state": "MN", "_id": "55976"} -{"city": "TAOPI", "loc": [-92.633455, 43.545783], "pop": 350, "state": "MN", "_id": "55977"} -{"city": "THEILMAN", "loc": [-92.211579, 44.30278], "pop": 402, "state": "MN", "_id": "55978"} -{"city": "UTICA", "loc": [-91.941737, 43.958727], "pop": 599, "state": "MN", "_id": "55979"} -{"city": "WABASHA", "loc": [-92.036058, 44.370273], "pop": 3743, "state": "MN", "_id": "55981"} -{"city": "WALTHAM", "loc": [-92.873446, 43.806958], "pop": 561, "state": "MN", "_id": "55982"} -{"city": "WANAMINGO", "loc": [-92.810258, 44.31214], "pop": 1319, "state": "MN", "_id": "55983"} -{"city": "WEST CONCORD", "loc": [-92.882495, 44.151954], "pop": 2040, "state": "MN", "_id": "55985"} -{"city": "WHALAN", "loc": [-91.918863, 43.716528], "pop": 269, "state": "MN", "_id": "55986"} -{"city": "GOODVIEW", "loc": [-91.653348, 44.039132], "pop": 34518, "state": "MN", "_id": "55987"} -{"city": "WYKOFF", "loc": [-92.267921, 43.71464], "pop": 954, "state": "MN", "_id": "55990"} -{"city": "HAMMOND", "loc": [-92.403967, 44.248812], "pop": 1720, "state": "MN", "_id": "55991"} -{"city": "ZUMBROTA", "loc": [-92.671863, 44.303179], "pop": 3535, "state": "MN", "_id": "55992"} -{"city": "MANKATO", "loc": [-93.996044, 44.153809], "pop": 38417, "state": "MN", "_id": "56001"} -{"city": "NORTH MANKATO", "loc": [-94.031476, 44.177541], "pop": 11629, "state": "MN", "_id": "56003"} -{"city": "ALBERT LEA", "loc": [-93.370672, 43.653678], "pop": 21186, "state": "MN", "_id": "56007"} -{"city": "ALDEN", "loc": [-93.582307, 43.646586], "pop": 1660, "state": "MN", "_id": "56009"} -{"city": "AMBOY", "loc": [-94.177353, 43.890326], "pop": 1449, "state": "MN", "_id": "56010"} -{"city": "BELLE PLAINE", "loc": [-93.760394, 44.613852], "pop": 4623, "state": "MN", "_id": "56011"} -{"city": "BLUE EARTH", "loc": [-94.092379, 43.639426], "pop": 5090, "state": "MN", "_id": "56013"} -{"city": "BRICELYN", "loc": [-93.821081, 43.574628], "pop": 912, "state": "MN", "_id": "56014"} -{"city": "CLARKS GROVE", "loc": [-93.323222, 43.762971], "pop": 956, "state": "MN", "_id": "56016"} -{"city": "CLEVELAND", "loc": [-93.828622, 44.32014], "pop": 1258, "state": "MN", "_id": "56017"} -{"city": "COMFREY", "loc": [-94.91345, 44.111069], "pop": 981, "state": "MN", "_id": "56019"} -{"city": "CONGER", "loc": [-93.522358, 43.611417], "pop": 277, "state": "MN", "_id": "56020"} -{"city": "COURTLAND", "loc": [-94.348229, 44.279083], "pop": 1122, "state": "MN", "_id": "56021"} -{"city": "DARFUR", "loc": [-94.813416, 44.061034], "pop": 331, "state": "MN", "_id": "56022"} -{"city": "DELAVAN", "loc": [-94.022488, 43.771859], "pop": 310, "state": "MN", "_id": "56023"} -{"city": "EAGLE LAKE", "loc": [-93.871912, 44.154587], "pop": 2218, "state": "MN", "_id": "56024"} -{"city": "EASTON", "loc": [-93.933994, 43.758126], "pop": 793, "state": "MN", "_id": "56025"} -{"city": "ELLENDALE", "loc": [-93.319492, 43.882591], "pop": 1051, "state": "MN", "_id": "56026"} -{"city": "ELMORE", "loc": [-94.098368, 43.520065], "pop": 1125, "state": "MN", "_id": "56027"} -{"city": "ELYSIAN", "loc": [-93.696498, 44.22313], "pop": 1303, "state": "MN", "_id": "56028"} -{"city": "EMMONS", "loc": [-93.482441, 43.508851], "pop": 599, "state": "MN", "_id": "56029"} -{"city": "ESSIG", "loc": [-94.56823, 44.323754], "pop": 522, "state": "MN", "_id": "56030"} -{"city": "FAIRMONT", "loc": [-94.45095, 43.637592], "pop": 12732, "state": "MN", "_id": "56031"} -{"city": "FREEBORN", "loc": [-93.574543, 43.784763], "pop": 632, "state": "MN", "_id": "56032"} -{"city": "FROST", "loc": [-93.936099, 43.563796], "pop": 388, "state": "MN", "_id": "56033"} -{"city": "GARDEN CITY", "loc": [-94.179084, 44.046748], "pop": 460, "state": "MN", "_id": "56034"} -{"city": "GENEVA", "loc": [-93.267096, 43.82354], "pop": 524, "state": "MN", "_id": "56035"} -{"city": "GLENVILLE", "loc": [-93.261761, 43.557749], "pop": 1254, "state": "MN", "_id": "56036"} -{"city": "GOOD THUNDER", "loc": [-94.067662, 43.995187], "pop": 941, "state": "MN", "_id": "56037"} -{"city": "GRANADA", "loc": [-94.330731, 43.706082], "pop": 681, "state": "MN", "_id": "56039"} -{"city": "HANSKA", "loc": [-94.493267, 44.152678], "pop": 1184, "state": "MN", "_id": "56041"} -{"city": "HARTLAND", "loc": [-93.476953, 43.803989], "pop": 612, "state": "MN", "_id": "56042"} -{"city": "HAYWARD", "loc": [-93.237727, 43.63864], "pop": 705, "state": "MN", "_id": "56043"} -{"city": "HENDERSON", "loc": [-93.934487, 44.534398], "pop": 1785, "state": "MN", "_id": "56044"} -{"city": "HOLLANDALE", "loc": [-93.167989, 43.752842], "pop": 2346, "state": "MN", "_id": "56045"} -{"city": "HOPE", "loc": [-93.345823, 43.979723], "pop": 520, "state": "MN", "_id": "56046"} -{"city": "HUNTLEY", "loc": [-94.201567, 43.723746], "pop": 469, "state": "MN", "_id": "56047"} -{"city": "JANESVILLE", "loc": [-93.709462, 44.116778], "pop": 2996, "state": "MN", "_id": "56048"} -{"city": "KASOTA", "loc": [-93.945319, 44.284188], "pop": 1958, "state": "MN", "_id": "56050"} -{"city": "KIESTER", "loc": [-93.710191, 43.541445], "pop": 923, "state": "MN", "_id": "56051"} -{"city": "KILKENNY", "loc": [-93.516397, 44.318532], "pop": 1249, "state": "MN", "_id": "56052"} -{"city": "LAFAYETTE", "loc": [-94.436463, 44.407315], "pop": 2268, "state": "MN", "_id": "56054"} -{"city": "LAKE CRYSTAL", "loc": [-94.218385, 44.120189], "pop": 3350, "state": "MN", "_id": "56055"} -{"city": "LE CENTER", "loc": [-93.721428, 44.385348], "pop": 3284, "state": "MN", "_id": "56057"} -{"city": "LE SUEUR", "loc": [-93.885588, 44.458154], "pop": 5491, "state": "MN", "_id": "56058"} -{"city": "LEWISVILLE", "loc": [-94.428854, 43.920922], "pop": 568, "state": "MN", "_id": "56060"} -{"city": "LONDON", "loc": [-93.116527, 43.543455], "pop": 437, "state": "MN", "_id": "56061"} -{"city": "MADELIA", "loc": [-94.410994, 44.049949], "pop": 3012, "state": "MN", "_id": "56062"} -{"city": "MADISON LAKE", "loc": [-93.828893, 44.222074], "pop": 2135, "state": "MN", "_id": "56063"} -{"city": "MANCHESTER", "loc": [-93.460654, 43.716762], "pop": 552, "state": "MN", "_id": "56064"} -{"city": "MAPLETON", "loc": [-93.954247, 43.933065], "pop": 2299, "state": "MN", "_id": "56065"} -{"city": "MERIDEN", "loc": [-93.351167, 44.069875], "pop": 693, "state": "MN", "_id": "56067"} -{"city": "MINNESOTA LAKE", "loc": [-93.828201, 43.829465], "pop": 944, "state": "MN", "_id": "56068"} -{"city": "MONTGOMERY", "loc": [-93.581024, 44.435591], "pop": 3026, "state": "MN", "_id": "56069"} -{"city": "NEW PRAGUE", "loc": [-93.580473, 44.540239], "pop": 6601, "state": "MN", "_id": "56071"} -{"city": "NEW RICHLAND", "loc": [-93.499541, 43.893724], "pop": 1964, "state": "MN", "_id": "56072"} -{"city": "NEW ULM", "loc": [-94.464421, 44.304378], "pop": 15142, "state": "MN", "_id": "56073"} -{"city": "NICOLLET", "loc": [-94.186701, 44.272373], "pop": 1654, "state": "MN", "_id": "56074"} -{"city": "NORTHROP", "loc": [-94.43495, 43.721164], "pop": 724, "state": "MN", "_id": "56075"} -{"city": "OAKLAND", "loc": [-93.111325, 43.632981], "pop": 394, "state": "MN", "_id": "56076"} -{"city": "OTISCO", "loc": [-93.474097, 43.981513], "pop": 623, "state": "MN", "_id": "56077"} -{"city": "PEMBERTON", "loc": [-93.818449, 43.959955], "pop": 880, "state": "MN", "_id": "56078"} -{"city": "SAINT CLAIR", "loc": [-93.844932, 44.077775], "pop": 1083, "state": "MN", "_id": "56080"} -{"city": "SAINT JAMES", "loc": [-94.622935, 43.987519], "pop": 6472, "state": "MN", "_id": "56081"} -{"city": "SAINT PETER", "loc": [-93.981061, 44.335107], "pop": 11277, "state": "MN", "_id": "56082"} -{"city": "SANBORN", "loc": [-95.132793, 44.218291], "pop": 1030, "state": "MN", "_id": "56083"} -{"city": "SLEEPY EYE", "loc": [-94.727251, 44.282089], "pop": 6092, "state": "MN", "_id": "56085"} -{"city": "SPRINGFIELD", "loc": [-94.979204, 44.232905], "pop": 2915, "state": "MN", "_id": "56087"} -{"city": "TRUMAN", "loc": [-94.431862, 43.820008], "pop": 2319, "state": "MN", "_id": "56088"} -{"city": "TWIN LAKES", "loc": [-93.388667, 43.553951], "pop": 926, "state": "MN", "_id": "56089"} -{"city": "VERNON CENTER", "loc": [-94.214212, 43.970557], "pop": 942, "state": "MN", "_id": "56090"} -{"city": "WALDORF", "loc": [-93.704264, 43.939868], "pop": 962, "state": "MN", "_id": "56091"} -{"city": "WALTERS", "loc": [-93.70102, 43.623868], "pop": 400, "state": "MN", "_id": "56092"} -{"city": "WASECA", "loc": [-93.510828, 44.0834], "pop": 11534, "state": "MN", "_id": "56093"} -{"city": "WATERVILLE", "loc": [-93.575063, 44.223796], "pop": 2390, "state": "MN", "_id": "56096"} -{"city": "WELLS", "loc": [-93.732069, 43.743396], "pop": 3585, "state": "MN", "_id": "56097"} -{"city": "WINNEBAGO", "loc": [-94.16324, 43.77549], "pop": 1998, "state": "MN", "_id": "56098"} -{"city": "WILDER", "loc": [-95.151531, 43.879022], "pop": 6224, "state": "MN", "_id": "56101"} -{"city": "ADRIAN", "loc": [-95.927261, 43.619683], "pop": 1899, "state": "MN", "_id": "56110"} -{"city": "ALPHA", "loc": [-94.905088, 43.594594], "pop": 674, "state": "MN", "_id": "56111"} -{"city": "AMIRET", "loc": [-95.719046, 44.325134], "pop": 556, "state": "MN", "_id": "56112"} -{"city": "ARCO", "loc": [-96.199913, 44.409147], "pop": 515, "state": "MN", "_id": "56113"} -{"city": "AVOCA", "loc": [-95.600152, 43.970553], "pop": 572, "state": "MN", "_id": "56114"} -{"city": "BALATON", "loc": [-95.88377, 44.225253], "pop": 1305, "state": "MN", "_id": "56115"} -{"city": "BEAVER CREEK", "loc": [-96.369771, 43.622343], "pop": 694, "state": "MN", "_id": "56116"} -{"city": "BIGELOW", "loc": [-95.651527, 43.533612], "pop": 633, "state": "MN", "_id": "56117"} -{"city": "BINGHAM LAKE", "loc": [-95.04571, 43.894155], "pop": 432, "state": "MN", "_id": "56118"} -{"city": "BREWSTER", "loc": [-95.480676, 43.703223], "pop": 800, "state": "MN", "_id": "56119"} -{"city": "BUTTERFIELD", "loc": [-94.795627, 43.965417], "pop": 894, "state": "MN", "_id": "56120"} -{"city": "CEYLON", "loc": [-94.614908, 43.538243], "pop": 1051, "state": "MN", "_id": "56121"} -{"city": "CHANDLER", "loc": [-95.929616, 43.916308], "pop": 808, "state": "MN", "_id": "56122"} -{"city": "CURRIE", "loc": [-95.695323, 44.094616], "pop": 1254, "state": "MN", "_id": "56123"} -{"city": "DELFT", "loc": [-95.047234, 43.978502], "pop": 363, "state": "MN", "_id": "56124"} -{"city": "DOVRAY", "loc": [-95.527082, 44.05967], "pop": 277, "state": "MN", "_id": "56125"} -{"city": "DUNDEE", "loc": [-95.499286, 43.819052], "pop": 414, "state": "MN", "_id": "56126"} -{"city": "DUNNELL", "loc": [-94.787558, 43.553052], "pop": 452, "state": "MN", "_id": "56127"} -{"city": "EDGERTON", "loc": [-96.146333, 43.882419], "pop": 2045, "state": "MN", "_id": "56128"} -{"city": "ELLSWORTH", "loc": [-96.011248, 43.526539], "pop": 852, "state": "MN", "_id": "56129"} -{"city": "56130", "loc": [-96.032692, 44.240346], "pop": 228, "state": "MN", "_id": "56130"} -{"city": "FULDA", "loc": [-95.597937, 43.875298], "pop": 1792, "state": "MN", "_id": "56131"} -{"city": "GARVIN", "loc": [-95.767215, 44.229318], "pop": 471, "state": "MN", "_id": "56132"} -{"city": "HADLEY", "loc": [-95.869025, 44.03105], "pop": 429, "state": "MN", "_id": "56133"} -{"city": "HARDWICK", "loc": [-96.21607, 43.791072], "pop": 461, "state": "MN", "_id": "56134"} -{"city": "56135", "loc": [-96.19247, 43.958423], "pop": 66, "state": "MN", "_id": "56135"} -{"city": "HENDRICKS", "loc": [-96.407753, 44.499514], "pop": 1305, "state": "MN", "_id": "56136"} -{"city": "HERON LAKE", "loc": [-95.326736, 43.79788], "pop": 1190, "state": "MN", "_id": "56137"} -{"city": "HILLS", "loc": [-96.364527, 43.533548], "pop": 1072, "state": "MN", "_id": "56138"} -{"city": "HOLLAND", "loc": [-96.190277, 44.075904], "pop": 682, "state": "MN", "_id": "56139"} -{"city": "IONA", "loc": [-95.771769, 43.902631], "pop": 434, "state": "MN", "_id": "56141"} -{"city": "IVANHOE", "loc": [-96.226059, 44.507119], "pop": 1683, "state": "MN", "_id": "56142"} -{"city": "JACKSON", "loc": [-94.993827, 43.645732], "pop": 5119, "state": "MN", "_id": "56143"} -{"city": "JASPER", "loc": [-96.385035, 43.856827], "pop": 1220, "state": "MN", "_id": "56144"} -{"city": "JEFFERS", "loc": [-95.154664, 44.073678], "pop": 1000, "state": "MN", "_id": "56145"} -{"city": "KANARANZI", "loc": [-96.111306, 43.54979], "pop": 320, "state": "MN", "_id": "56146"} -{"city": "KENNETH", "loc": [-96.10886, 43.763738], "pop": 523, "state": "MN", "_id": "56147"} -{"city": "LAKE BENTON", "loc": [-96.291035, 44.278211], "pop": 1323, "state": "MN", "_id": "56149"} -{"city": "LAKEFIELD", "loc": [-95.184834, 43.654456], "pop": 3233, "state": "MN", "_id": "56150"} -{"city": "LAKE WILSON", "loc": [-95.97973, 44.009529], "pop": 749, "state": "MN", "_id": "56151"} -{"city": "LAMBERTON", "loc": [-95.273946, 44.237776], "pop": 1736, "state": "MN", "_id": "56152"} -{"city": "LEOTA", "loc": [-96.005392, 43.822628], "pop": 504, "state": "MN", "_id": "56153"} -{"city": "LISMORE", "loc": [-95.968178, 43.733857], "pop": 494, "state": "MN", "_id": "56155"} -{"city": "LUVERNE", "loc": [-96.221562, 43.661366], "pop": 5436, "state": "MN", "_id": "56156"} -{"city": "LYND", "loc": [-95.923284, 44.40322], "pop": 1005, "state": "MN", "_id": "56157"} -{"city": "MAGNOLIA", "loc": [-96.100825, 43.638211], "pop": 458, "state": "MN", "_id": "56158"} -{"city": "MOUNTAIN LAKE", "loc": [-94.927313, 43.938998], "pop": 2506, "state": "MN", "_id": "56159"} -{"city": "ODIN", "loc": [-94.77402, 43.882402], "pop": 382, "state": "MN", "_id": "56160"} -{"city": "OKABENA", "loc": [-95.338112, 43.72302], "pop": 606, "state": "MN", "_id": "56161"} -{"city": "ORMSBY", "loc": [-94.687255, 43.861639], "pop": 221, "state": "MN", "_id": "56162"} -{"city": "HATFIELD", "loc": [-96.322762, 44.009516], "pop": 5923, "state": "MN", "_id": "56164"} -{"city": "READING", "loc": [-95.738945, 43.713604], "pop": 400, "state": "MN", "_id": "56165"} -{"city": "REVERE", "loc": [-95.399588, 44.271326], "pop": 459, "state": "MN", "_id": "56166"} -{"city": "ROUND LAKE", "loc": [-95.450151, 43.562045], "pop": 1236, "state": "MN", "_id": "56167"} -{"city": "RUSHMORE", "loc": [-95.776728, 43.624011], "pop": 726, "state": "MN", "_id": "56168"} -{"city": "RUSSELL", "loc": [-95.942564, 44.320129], "pop": 781, "state": "MN", "_id": "56169"} -{"city": "FLORENCE", "loc": [-96.079028, 44.163836], "pop": 737, "state": "MN", "_id": "56170"} -{"city": "SHERBURN", "loc": [-94.726877, 43.661089], "pop": 2275, "state": "MN", "_id": "56171"} -{"city": "SLAYTON", "loc": [-95.755448, 43.985596], "pop": 2535, "state": "MN", "_id": "56172"} -{"city": "STEEN", "loc": [-96.243868, 43.531728], "pop": 526, "state": "MN", "_id": "56173"} -{"city": "STORDEN", "loc": [-95.301926, 44.052383], "pop": 537, "state": "MN", "_id": "56174"} -{"city": "TRACY", "loc": [-95.621301, 44.234201], "pop": 2390, "state": "MN", "_id": "56175"} -{"city": "TRIMONT", "loc": [-94.71863, 43.782702], "pop": 1349, "state": "MN", "_id": "56176"} -{"city": "TYLER", "loc": [-96.130235, 44.277342], "pop": 1940, "state": "MN", "_id": "56178"} -{"city": "VERDI", "loc": [-96.372429, 44.234078], "pop": 234, "state": "MN", "_id": "56179"} -{"city": "WALNUT GROVE", "loc": [-95.496451, 44.229375], "pop": 1277, "state": "MN", "_id": "56180"} -{"city": "WELCOME", "loc": [-94.588593, 43.67052], "pop": 1435, "state": "MN", "_id": "56181"} -{"city": "WESTBROOK", "loc": [-95.423208, 44.065381], "pop": 1362, "state": "MN", "_id": "56183"} -{"city": "WILMONT", "loc": [-95.832421, 43.7692], "pop": 1101, "state": "MN", "_id": "56185"} -{"city": "WOODSTOCK", "loc": [-96.119198, 43.987338], "pop": 451, "state": "MN", "_id": "56186"} -{"city": "WORTHINGTON", "loc": [-95.605907, 43.628626], "pop": 11556, "state": "MN", "_id": "56187"} -{"city": "WILLMAR", "loc": [-95.052315, 45.139264], "pop": 22069, "state": "MN", "_id": "56201"} -{"city": "ALBERTA", "loc": [-96.049772, 45.557086], "pop": 296, "state": "MN", "_id": "56207"} -{"city": "APPLETON", "loc": [-95.994872, 45.20543], "pop": 2294, "state": "MN", "_id": "56208"} -{"city": "ATWATER", "loc": [-94.793779, 45.111645], "pop": 1841, "state": "MN", "_id": "56209"} -{"city": "BARRY", "loc": [-96.560527, 45.547446], "pop": 135, "state": "MN", "_id": "56210"} -{"city": "BEARDSLEY", "loc": [-96.706013, 45.553855], "pop": 494, "state": "MN", "_id": "56211"} -{"city": "BELLINGHAM", "loc": [-96.32235, 45.155626], "pop": 715, "state": "MN", "_id": "56212"} -{"city": "BELVIEW", "loc": [-95.317757, 44.605486], "pop": 750, "state": "MN", "_id": "56214"} -{"city": "BENSON", "loc": [-95.576644, 45.312904], "pop": 5107, "state": "MN", "_id": "56215"} -{"city": "SVEA", "loc": [-95.063955, 44.939512], "pop": 668, "state": "MN", "_id": "56216"} -{"city": "BOYD", "loc": [-95.942102, 44.850725], "pop": 668, "state": "MN", "_id": "56218"} -{"city": "BROWNS VALLEY", "loc": [-96.80625, 45.606934], "pop": 1128, "state": "MN", "_id": "56219"} -{"city": "CANBY", "loc": [-96.278394, 44.720187], "pop": 3043, "state": "MN", "_id": "56220"} -{"city": "CHOKIO", "loc": [-96.17331, 45.552369], "pop": 785, "state": "MN", "_id": "56221"} -{"city": "CLARA CITY", "loc": [-95.349902, 44.963912], "pop": 1879, "state": "MN", "_id": "56222"} -{"city": "CLARKFIELD", "loc": [-95.830405, 44.774261], "pop": 1623, "state": "MN", "_id": "56223"} -{"city": "CLEMENTS", "loc": [-95.047447, 44.39432], "pop": 398, "state": "MN", "_id": "56224"} -{"city": "CLINTON", "loc": [-96.418161, 45.457659], "pop": 843, "state": "MN", "_id": "56225"} -{"city": "CLONTARF", "loc": [-95.678629, 45.373418], "pop": 279, "state": "MN", "_id": "56226"} -{"city": "CORRELL", "loc": [-96.175266, 45.290742], "pop": 397, "state": "MN", "_id": "56227"} -{"city": "COSMOS", "loc": [-94.697827, 44.958969], "pop": 1217, "state": "MN", "_id": "56228"} -{"city": "COTTONWOOD", "loc": [-95.692508, 44.600251], "pop": 1552, "state": "MN", "_id": "56229"} -{"city": "DANUBE", "loc": [-95.078366, 44.795567], "pop": 1179, "state": "MN", "_id": "56230"} -{"city": "DANVERS", "loc": [-95.886194, 45.341853], "pop": 710, "state": "MN", "_id": "56231"} -{"city": "DAWSON", "loc": [-96.015014, 44.931737], "pop": 2496, "state": "MN", "_id": "56232"} -{"city": "DE GRAFF", "loc": [-95.446588, 45.270845], "pop": 336, "state": "MN", "_id": "56233"} -{"city": "DONNELLY", "loc": [-96.064937, 45.70137], "pop": 526, "state": "MN", "_id": "56235"} -{"city": "DUMONT", "loc": [-96.406123, 45.671742], "pop": 727, "state": "MN", "_id": "56236"} -{"city": "ECHO", "loc": [-95.418223, 44.631046], "pop": 797, "state": "MN", "_id": "56237"} -{"city": "EVAN", "loc": [-94.816956, 44.351114], "pop": 222, "state": "MN", "_id": "56238"} -{"city": "GHENT", "loc": [-95.893642, 44.507843], "pop": 661, "state": "MN", "_id": "56239"} -{"city": "GRACEVILLE", "loc": [-96.420925, 45.560248], "pop": 1019, "state": "MN", "_id": "56240"} -{"city": "GRANITE FALLS", "loc": [-95.551557, 44.808749], "pop": 4051, "state": "MN", "_id": "56241"} -{"city": "GROVE CITY", "loc": [-94.687843, 45.153172], "pop": 1428, "state": "MN", "_id": "56243"} -{"city": "HANCOCK", "loc": [-95.800775, 45.498464], "pop": 1255, "state": "MN", "_id": "56244"} -{"city": "HANLEY FALLS", "loc": [-95.682492, 44.678862], "pop": 652, "state": "MN", "_id": "56245"} -{"city": "HAWICK", "loc": [-94.820762, 45.361775], "pop": 536, "state": "MN", "_id": "56246"} -{"city": "HAZEL RUN", "loc": [-95.712187, 44.749688], "pop": 328, "state": "MN", "_id": "56247"} -{"city": "HERMAN", "loc": [-96.099188, 45.807104], "pop": 928, "state": "MN", "_id": "56248"} -{"city": "HOLLOWAY", "loc": [-95.916374, 45.270805], "pop": 283, "state": "MN", "_id": "56249"} -{"city": "JOHNSON", "loc": [-96.266277, 45.573007], "pop": 12, "state": "MN", "_id": "56250"} -{"city": "KANDIYOHI", "loc": [-94.947274, 45.123162], "pop": 1169, "state": "MN", "_id": "56251"} -{"city": "KERKHOVEN", "loc": [-95.311465, 45.209626], "pop": 1267, "state": "MN", "_id": "56252"} -{"city": "LAKE LILLIAN", "loc": [-94.901443, 44.96603], "pop": 1106, "state": "MN", "_id": "56253"} -{"city": "LOUISBURG", "loc": [-96.171539, 45.130556], "pop": 307, "state": "MN", "_id": "56254"} -{"city": "LUCAN", "loc": [-95.411934, 44.413237], "pop": 487, "state": "MN", "_id": "56255"} -{"city": "MADISON", "loc": [-96.164507, 44.994618], "pop": 3857, "state": "MN", "_id": "56256"} -{"city": "MARIETTA", "loc": [-96.409448, 44.963582], "pop": 588, "state": "MN", "_id": "56257"} -{"city": "MARSHALL", "loc": [-95.779454, 44.448127], "pop": 13489, "state": "MN", "_id": "56258"} -{"city": "MAYNARD", "loc": [-95.484508, 44.922615], "pop": 1004, "state": "MN", "_id": "56260"} -{"city": "MILAN", "loc": [-95.869045, 45.114317], "pop": 864, "state": "MN", "_id": "56262"} -{"city": "MILROY", "loc": [-95.554471, 44.436494], "pop": 907, "state": "MN", "_id": "56263"} -{"city": "MINNEOTA", "loc": [-95.976714, 44.558732], "pop": 2056, "state": "MN", "_id": "56264"} -{"city": "MONTEVIDEO", "loc": [-95.676473, 44.968829], "pop": 8405, "state": "MN", "_id": "56265"} -{"city": "MORGAN", "loc": [-94.921783, 44.392554], "pop": 2257, "state": "MN", "_id": "56266"} -{"city": "MORRIS", "loc": [-95.917234, 45.592095], "pop": 7772, "state": "MN", "_id": "56267"} -{"city": "MORTON", "loc": [-95.026817, 44.566189], "pop": 962, "state": "MN", "_id": "56270"} -{"city": "MURDOCK", "loc": [-95.404897, 45.216147], "pop": 448, "state": "MN", "_id": "56271"} -{"city": "NASSAU", "loc": [-96.413307, 45.107554], "pop": 293, "state": "MN", "_id": "56272"} -{"city": "NEW LONDON", "loc": [-94.948008, 45.294936], "pop": 4077, "state": "MN", "_id": "56273"} -{"city": "NORCROSS", "loc": [-96.134168, 45.885839], "pop": 303, "state": "MN", "_id": "56274"} -{"city": "ODESSA", "loc": [-96.310154, 45.299766], "pop": 457, "state": "MN", "_id": "56276"} -{"city": "OLIVIA", "loc": [-94.972747, 44.770627], "pop": 3829, "state": "MN", "_id": "56277"} -{"city": "ORTONVILLE", "loc": [-96.45965, 45.329621], "pop": 2928, "state": "MN", "_id": "56278"} -{"city": "PENNOCK", "loc": [-95.175333, 45.131031], "pop": 915, "state": "MN", "_id": "56279"} -{"city": "PORTER", "loc": [-96.15812, 44.656532], "pop": 425, "state": "MN", "_id": "56280"} -{"city": "PRINSBURG", "loc": [-95.18654, 44.937088], "pop": 931, "state": "MN", "_id": "56281"} -{"city": "RAYMOND", "loc": [-95.220788, 45.018097], "pop": 1095, "state": "MN", "_id": "56282"} -{"city": "DELHI", "loc": [-95.10713, 44.531753], "pop": 6705, "state": "MN", "_id": "56283"} -{"city": "RENVILLE", "loc": [-95.198879, 44.777609], "pop": 2027, "state": "MN", "_id": "56284"} -{"city": "SACRED HEART", "loc": [-95.353801, 44.797536], "pop": 1725, "state": "MN", "_id": "56285"} -{"city": "SAINT LEO", "loc": [-96.042546, 44.711091], "pop": 483, "state": "MN", "_id": "56286"} -{"city": "SEAFORTH", "loc": [-95.297768, 44.490536], "pop": 327, "state": "MN", "_id": "56287"} -{"city": "SPICER", "loc": [-94.911572, 45.224112], "pop": 3440, "state": "MN", "_id": "56288"} -{"city": "SUNBURG", "loc": [-95.204915, 45.358316], "pop": 408, "state": "MN", "_id": "56289"} -{"city": "56290", "loc": [-95.052128, 45.019708], "pop": 506, "state": "MN", "_id": "56290"} -{"city": "TAUNTON", "loc": [-96.052594, 44.595076], "pop": 349, "state": "MN", "_id": "56291"} -{"city": "VESTA", "loc": [-95.411801, 44.505052], "pop": 525, "state": "MN", "_id": "56292"} -{"city": "WABASSO", "loc": [-95.2632, 44.405865], "pop": 991, "state": "MN", "_id": "56293"} -{"city": "WANDA", "loc": [-95.178, 44.32295], "pop": 401, "state": "MN", "_id": "56294"} -{"city": "WATSON", "loc": [-95.794203, 45.019443], "pop": 436, "state": "MN", "_id": "56295"} -{"city": "WHEATON", "loc": [-96.486598, 45.811104], "pop": 2338, "state": "MN", "_id": "56296"} -{"city": "WOOD LAKE", "loc": [-95.540908, 44.637915], "pop": 922, "state": "MN", "_id": "56297"} -{"city": "SAINT CLOUD", "loc": [-94.181857, 45.540972], "pop": 36182, "state": "MN", "_id": "56301"} -{"city": "SAINT CLOUD", "loc": [-94.203634, 45.571298], "pop": 14039, "state": "MN", "_id": "56303"} -{"city": "SAINT CLOUD", "loc": [-94.128447, 45.552113], "pop": 13570, "state": "MN", "_id": "56304"} -{"city": "ALBANY", "loc": [-94.574022, 45.615114], "pop": 3064, "state": "MN", "_id": "56307"} -{"city": "ALEXANDRIA", "loc": [-95.381994, 45.881747], "pop": 17548, "state": "MN", "_id": "56308"} -{"city": "ASHBY", "loc": [-95.821417, 46.078066], "pop": 869, "state": "MN", "_id": "56309"} -{"city": "AVON", "loc": [-94.436029, 45.612168], "pop": 4355, "state": "MN", "_id": "56310"} -{"city": "BARRETT", "loc": [-95.875391, 45.899555], "pop": 799, "state": "MN", "_id": "56311"} -{"city": "BELGRADE", "loc": [-94.969877, 45.486522], "pop": 1661, "state": "MN", "_id": "56312"} -{"city": "BOCK", "loc": [-93.553658, 45.784427], "pop": 115, "state": "MN", "_id": "56313"} -{"city": "BOWLUS", "loc": [-94.417533, 45.81212], "pop": 1183, "state": "MN", "_id": "56314"} -{"city": "BRANDON", "loc": [-95.578769, 46.0039], "pop": 1453, "state": "MN", "_id": "56315"} -{"city": "BROOTEN", "loc": [-95.090049, 45.493171], "pop": 1287, "state": "MN", "_id": "56316"} -{"city": "BURTRUM", "loc": [-94.696214, 45.88803], "pop": 854, "state": "MN", "_id": "56318"} -{"city": "CARLOS", "loc": [-95.310468, 45.972572], "pop": 2136, "state": "MN", "_id": "56319"} -{"city": "COLD SPRING", "loc": [-94.437823, 45.449976], "pop": 5162, "state": "MN", "_id": "56320"} -{"city": "CYRUS", "loc": [-95.706965, 45.569483], "pop": 988, "state": "MN", "_id": "56323"} -{"city": "DALTON", "loc": [-95.850017, 46.154414], "pop": 1305, "state": "MN", "_id": "56324"} -{"city": "EVANSVILLE", "loc": [-95.695067, 46.01525], "pop": 1149, "state": "MN", "_id": "56326"} -{"city": "FARWELL", "loc": [-95.665583, 45.724424], "pop": 394, "state": "MN", "_id": "56327"} -{"city": "FLENSBURG", "loc": [-94.530767, 45.950899], "pop": 213, "state": "MN", "_id": "56328"} -{"city": "FOLEY", "loc": [-93.868459, 45.708687], "pop": 4078, "state": "MN", "_id": "56329"} -{"city": "FORESTON", "loc": [-93.695755, 45.702842], "pop": 1353, "state": "MN", "_id": "56330"} -{"city": "FREEPORT", "loc": [-94.678529, 45.673146], "pop": 3865, "state": "MN", "_id": "56331"} -{"city": "GARFIELD", "loc": [-95.450044, 45.995309], "pop": 1430, "state": "MN", "_id": "56332"} -{"city": "GILMAN", "loc": [-93.946876, 45.774178], "pop": 945, "state": "MN", "_id": "56333"} -{"city": "GLENWOOD", "loc": [-95.38681, 45.642911], "pop": 5272, "state": "MN", "_id": "56334"} -{"city": "GREY EAGLE", "loc": [-94.832075, 45.81069], "pop": 2188, "state": "MN", "_id": "56336"} -{"city": "HILLMAN", "loc": [-93.881224, 46.06776], "pop": 724, "state": "MN", "_id": "56338"} -{"city": "HOFFMAN", "loc": [-95.795482, 45.823292], "pop": 852, "state": "MN", "_id": "56339"} -{"city": "HOLDINGFORD", "loc": [-94.458091, 45.724924], "pop": 1721, "state": "MN", "_id": "56340"} -{"city": "HOLMES CITY", "loc": [-95.56464, 45.810706], "pop": 614, "state": "MN", "_id": "56341"} -{"city": "ISLE", "loc": [-93.474062, 46.169605], "pop": 1109, "state": "MN", "_id": "56342"} -{"city": "KENSINGTON", "loc": [-95.694452, 45.81761], "pop": 770, "state": "MN", "_id": "56343"} -{"city": "LITTLE FALLS", "loc": [-94.360428, 45.98108], "pop": 13558, "state": "MN", "_id": "56345"} -{"city": "LITTLE SAUK", "loc": [-94.898389, 45.962417], "pop": 6436, "state": "MN", "_id": "56347"} -{"city": "LOWRY", "loc": [-95.53221, 45.710469], "pop": 429, "state": "MN", "_id": "56349"} -{"city": "MC GRATH", "loc": [-93.241619, 46.244116], "pop": 933, "state": "MN", "_id": "56350"} -{"city": "MELROSE", "loc": [-94.819768, 45.658183], "pop": 4739, "state": "MN", "_id": "56352"} -{"city": "MILACA", "loc": [-93.645266, 45.779481], "pop": 4927, "state": "MN", "_id": "56353"} -{"city": "MILTONA", "loc": [-95.325767, 46.054519], "pop": 866, "state": "MN", "_id": "56354"} -{"city": "NELSON", "loc": [-95.226032, 45.8516], "pop": 858, "state": "MN", "_id": "56355"} -{"city": "OAK PARK", "loc": [-93.921417, 45.671403], "pop": 2636, "state": "MN", "_id": "56357"} -{"city": "OGILVIE", "loc": [-93.435921, 45.847632], "pop": 1143, "state": "MN", "_id": "56358"} -{"city": "ONAMIA", "loc": [-93.68668, 46.09022], "pop": 3111, "state": "MN", "_id": "56359"} -{"city": "OSAKIS", "loc": [-95.13317, 45.876836], "pop": 2099, "state": "MN", "_id": "56360"} -{"city": "PARKERS PRAIRIE", "loc": [-95.360764, 46.176925], "pop": 3067, "state": "MN", "_id": "56361"} -{"city": "PAYNESVILLE", "loc": [-94.715709, 45.398798], "pop": 4967, "state": "MN", "_id": "56362"} -{"city": "PEASE", "loc": [-93.649247, 45.697872], "pop": 178, "state": "MN", "_id": "56363"} -{"city": "PIERZ", "loc": [-94.085306, 46.008059], "pop": 4300, "state": "MN", "_id": "56364"} -{"city": "RICE", "loc": [-94.165752, 45.736383], "pop": 4560, "state": "MN", "_id": "56367"} -{"city": "RICHMOND", "loc": [-94.546072, 45.423817], "pop": 3718, "state": "MN", "_id": "56368"} -{"city": "ROYALTON", "loc": [-94.221063, 45.858928], "pop": 2571, "state": "MN", "_id": "56373"} -{"city": "SAINT JOSEPH", "loc": [-94.336688, 45.565124], "pop": 9480, "state": "MN", "_id": "56374"} -{"city": "SAINT STEPHEN", "loc": [-94.281662, 45.711815], "pop": 2868, "state": "MN", "_id": "56375"} -{"city": "SARTELL", "loc": [-94.213569, 45.631827], "pop": 4966, "state": "MN", "_id": "56377"} -{"city": "SAUK CENTRE", "loc": [-94.968166, 45.728079], "pop": 6025, "state": "MN", "_id": "56378"} -{"city": "SAUK RAPIDS", "loc": [-94.159088, 45.604032], "pop": 12505, "state": "MN", "_id": "56379"} -{"city": "SEDAN", "loc": [-95.263989, 45.51069], "pop": 968, "state": "MN", "_id": "56380"} -{"city": "STARBUCK", "loc": [-95.542125, 45.592587], "pop": 1964, "state": "MN", "_id": "56381"} -{"city": "SWANVILLE", "loc": [-94.628921, 45.943148], "pop": 1917, "state": "MN", "_id": "56382"} -{"city": "UPSALA", "loc": [-94.575531, 45.809723], "pop": 1001, "state": "MN", "_id": "56384"} -{"city": "VILLARD", "loc": [-95.241439, 45.711823], "pop": 730, "state": "MN", "_id": "56385"} -{"city": "WAHKON", "loc": [-93.497174, 46.11214], "pop": 656, "state": "MN", "_id": "56386"} -{"city": "WAITE PARK", "loc": [-94.224481, 45.54972], "pop": 5227, "state": "MN", "_id": "56387"} -{"city": "WEST UNION", "loc": [-95.08328, 45.798451], "pop": 54, "state": "MN", "_id": "56389"} -{"city": "EAST GULL LAKE", "loc": [-94.201873, 46.357219], "pop": 23504, "state": "MN", "_id": "56401"} -{"city": "AITKIN", "loc": [-93.645413, 46.479929], "pop": 6388, "state": "MN", "_id": "56431"} -{"city": "AKELEY", "loc": [-94.72343, 47.000987], "pop": 1097, "state": "MN", "_id": "56433"} -{"city": "ALDRICH", "loc": [-94.992017, 46.402538], "pop": 1105, "state": "MN", "_id": "56434"} -{"city": "BACKUS", "loc": [-94.395918, 46.869905], "pop": 2594, "state": "MN", "_id": "56435"} -{"city": "BERTHA", "loc": [-95.035733, 46.251457], "pop": 1365, "state": "MN", "_id": "56437"} -{"city": "BROWERVILLE", "loc": [-94.834581, 46.090525], "pop": 2179, "state": "MN", "_id": "56438"} -{"city": "CLARISSA", "loc": [-94.95719, 46.137305], "pop": 1222, "state": "MN", "_id": "56440"} -{"city": "CROSBY", "loc": [-93.987448, 46.509084], "pop": 5601, "state": "MN", "_id": "56441"} -{"city": "CROSSLAKE", "loc": [-94.114256, 46.678644], "pop": 1132, "state": "MN", "_id": "56442"} -{"city": "CUSHING", "loc": [-94.618452, 46.202194], "pop": 2122, "state": "MN", "_id": "56443"} -{"city": "DEERWOOD", "loc": [-93.884863, 46.444571], "pop": 2225, "state": "MN", "_id": "56444"} -{"city": "EAGLE BEND", "loc": [-95.096061, 46.117017], "pop": 1714, "state": "MN", "_id": "56446"} -{"city": "EMILY", "loc": [-93.948385, 46.747791], "pop": 699, "state": "MN", "_id": "56447"} -{"city": "FIFTY LAKES", "loc": [-94.065555, 46.746997], "pop": 289, "state": "MN", "_id": "56448"} -{"city": "FORT RIPLEY", "loc": [-94.252696, 46.20988], "pop": 1377, "state": "MN", "_id": "56449"} -{"city": "GARRISON", "loc": [-93.936343, 46.263725], "pop": 1771, "state": "MN", "_id": "56450"} -{"city": "HACKENSACK", "loc": [-94.503324, 46.988442], "pop": 1382, "state": "MN", "_id": "56452"} -{"city": "HEWITT", "loc": [-95.050361, 46.32497], "pop": 1213, "state": "MN", "_id": "56453"} -{"city": "IRONTON", "loc": [-94.043655, 46.464135], "pop": 145, "state": "MN", "_id": "56455"} -{"city": "JENKINS", "loc": [-94.332485, 46.650983], "pop": 262, "state": "MN", "_id": "56456"} -{"city": "LAKE GEORGE", "loc": [-95.002148, 47.214992], "pop": 485, "state": "MN", "_id": "56458"} -{"city": "LAKE ITASCA", "loc": [-95.252149, 47.275725], "pop": 208, "state": "MN", "_id": "56460"} -{"city": "LAPORTE", "loc": [-94.777161, 47.236752], "pop": 1624, "state": "MN", "_id": "56461"} -{"city": "MANHATTAN BEACH", "loc": [-94.140068, 46.733563], "pop": 71, "state": "MN", "_id": "56463"} -{"city": "MENAHGA", "loc": [-95.071429, 46.757233], "pop": 1940, "state": "MN", "_id": "56464"} -{"city": "MERRIFIELD", "loc": [-94.204268, 46.494309], "pop": 1552, "state": "MN", "_id": "56465"} -{"city": "LEADER", "loc": [-94.628086, 46.321568], "pop": 943, "state": "MN", "_id": "56466"} -{"city": "NEVIS", "loc": [-94.846005, 46.931892], "pop": 1252, "state": "MN", "_id": "56467"} -{"city": "LAKE SHORE", "loc": [-94.296562, 46.484828], "pop": 2184, "state": "MN", "_id": "56468"} -{"city": "PALISADE", "loc": [-93.562741, 46.689492], "pop": 1168, "state": "MN", "_id": "56469"} -{"city": "PARK RAPIDS", "loc": [-95.038299, 46.937682], "pop": 8074, "state": "MN", "_id": "56470"} -{"city": "PEQUOT LAKES", "loc": [-94.268426, 46.62569], "pop": 3437, "state": "MN", "_id": "56472"} -{"city": "PILLAGER", "loc": [-94.392287, 46.398395], "pop": 3481, "state": "MN", "_id": "56473"} -{"city": "PINE RIVER", "loc": [-94.447691, 46.693796], "pop": 4095, "state": "MN", "_id": "56474"} -{"city": "RANDALL", "loc": [-94.500543, 46.073361], "pop": 1621, "state": "MN", "_id": "56475"} -{"city": "SEBEKA", "loc": [-95.068055, 46.630615], "pop": 3089, "state": "MN", "_id": "56477"} -{"city": "STAPLES", "loc": [-94.763299, 46.353552], "pop": 5352, "state": "MN", "_id": "56479"} -{"city": "VERNDALE", "loc": [-94.853268, 46.426995], "pop": 1899, "state": "MN", "_id": "56481"} -{"city": "WADENA", "loc": [-95.128283, 46.440121], "pop": 5818, "state": "MN", "_id": "56482"} -{"city": "WALKER", "loc": [-94.584675, 47.08775], "pop": 2431, "state": "MN", "_id": "56484"} -{"city": "WHIPHOLT", "loc": [-94.350974, 47.040953], "pop": 138, "state": "MN", "_id": "56485"} -{"city": "DETROIT LAKES", "loc": [-95.800606, 46.834877], "pop": 15501, "state": "MN", "_id": "56501"} -{"city": "LOCKHART", "loc": [-96.503569, 47.315597], "pop": 2500, "state": "MN", "_id": "56510"} -{"city": "AUDUBON", "loc": [-95.988136, 46.871887], "pop": 1083, "state": "MN", "_id": "56511"} -{"city": "BAKER", "loc": [-96.604955, 46.685764], "pop": 267, "state": "MN", "_id": "56513"} -{"city": "DOWNER", "loc": [-96.37269, 46.677296], "pop": 3357, "state": "MN", "_id": "56514"} -{"city": "BATTLE LAKE", "loc": [-95.714395, 46.314175], "pop": 2143, "state": "MN", "_id": "56515"} -{"city": "BEJOU", "loc": [-95.945423, 47.44909], "pop": 358, "state": "MN", "_id": "56516"} -{"city": "BELTRAMI", "loc": [-96.454864, 47.572118], "pop": 517, "state": "MN", "_id": "56517"} -{"city": "BLUFFTON", "loc": [-95.223592, 46.491795], "pop": 623, "state": "MN", "_id": "56518"} -{"city": "BORUP", "loc": [-96.552969, 47.189641], "pop": 329, "state": "MN", "_id": "56519"} -{"city": "BRECKENRIDGE", "loc": [-96.56222, 46.27966], "pop": 4776, "state": "MN", "_id": "56520"} -{"city": "CALLAWAY", "loc": [-95.943964, 47.007642], "pop": 761, "state": "MN", "_id": "56521"} -{"city": "DORAN", "loc": [-96.437169, 46.129645], "pop": 718, "state": "MN", "_id": "56522"} -{"city": "ELDRED", "loc": [-96.802647, 47.653011], "pop": 731, "state": "MN", "_id": "56523"} -{"city": "CLITHERALL", "loc": [-95.585822, 46.31693], "pop": 555, "state": "MN", "_id": "56524"} -{"city": "COMSTOCK", "loc": [-96.739752, 46.666703], "pop": 260, "state": "MN", "_id": "56525"} -{"city": "DEER CREEK", "loc": [-95.267265, 46.411193], "pop": 1431, "state": "MN", "_id": "56527"} -{"city": "DENT", "loc": [-95.764357, 46.555417], "pop": 2064, "state": "MN", "_id": "56528"} -{"city": "DILWORTH", "loc": [-96.70222, 46.878168], "pop": 2546, "state": "MN", "_id": "56529"} -{"city": "ELBOW LAKE", "loc": [-95.967149, 45.995267], "pop": 2087, "state": "MN", "_id": "56531"} -{"city": "ELIZABETH", "loc": [-96.085093, 46.406218], "pop": 738, "state": "MN", "_id": "56533"} -{"city": "ERHARD", "loc": [-96.059264, 46.494817], "pop": 846, "state": "MN", "_id": "56534"} -{"city": "ERSKINE", "loc": [-96.012173, 47.661817], "pop": 993, "state": "MN", "_id": "56535"} -{"city": "FELTON", "loc": [-96.505225, 47.070528], "pop": 431, "state": "MN", "_id": "56536"} -{"city": "CARLISLE", "loc": [-96.064338, 46.289723], "pop": 18010, "state": "MN", "_id": "56537"} -{"city": "FERTILE", "loc": [-96.237127, 47.521367], "pop": 1933, "state": "MN", "_id": "56540"} -{"city": "FOSSTON", "loc": [-95.743082, 47.581584], "pop": 2469, "state": "MN", "_id": "56542"} -{"city": "FOXHOME", "loc": [-96.317579, 46.258366], "pop": 309, "state": "MN", "_id": "56543"} -{"city": "FRAZEE", "loc": [-95.521159, 46.756469], "pop": 3166, "state": "MN", "_id": "56544"} -{"city": "GARY", "loc": [-96.215233, 47.367491], "pop": 518, "state": "MN", "_id": "56545"} -{"city": "GEORGETOWN", "loc": [-96.726996, 47.099865], "pop": 425, "state": "MN", "_id": "56546"} -{"city": "GLYNDON", "loc": [-96.558338, 46.882023], "pop": 2198, "state": "MN", "_id": "56547"} -{"city": "HALSTAD", "loc": [-96.798038, 47.355114], "pop": 898, "state": "MN", "_id": "56548"} -{"city": "ROLLAG", "loc": [-96.31115, 46.884185], "pop": 3105, "state": "MN", "_id": "56549"} -{"city": "HENDRUM", "loc": [-96.79875, 47.26889], "pop": 467, "state": "MN", "_id": "56550"} -{"city": "HENNING", "loc": [-95.385162, 46.325606], "pop": 1770, "state": "MN", "_id": "56551"} -{"city": "HITTERDAL", "loc": [-96.28884, 47.001354], "pop": 613, "state": "MN", "_id": "56552"} -{"city": "KENT", "loc": [-96.685181, 46.437811], "pop": 168, "state": "MN", "_id": "56553"} -{"city": "LAKE PARK", "loc": [-96.067047, 46.817623], "pop": 3001, "state": "MN", "_id": "56554"} -{"city": "MCINTOSH", "loc": [-95.886252, 47.652165], "pop": 1088, "state": "MN", "_id": "56556"} -{"city": "MAHNOMEN", "loc": [-95.885609, 47.336155], "pop": 2940, "state": "MN", "_id": "56557"} -{"city": "MOORHEAD", "loc": [-96.757197, 46.867748], "pop": 35056, "state": "MN", "_id": "56560"} -{"city": "NASHUA", "loc": [-96.316009, 46.053731], "pop": 153, "state": "MN", "_id": "56565"} -{"city": "NAYTAHWAUSH", "loc": [-95.628324, 47.26007], "pop": 785, "state": "MN", "_id": "56566"} -{"city": "NEW YORK MILLS", "loc": [-95.404717, 46.555867], "pop": 3681, "state": "MN", "_id": "56567"} -{"city": "NIELSVILLE", "loc": [-96.757433, 47.537336], "pop": 299, "state": "MN", "_id": "56568"} -{"city": "OGEMA", "loc": [-95.915124, 47.102865], "pop": 1129, "state": "MN", "_id": "56569"} -{"city": "OSAGE", "loc": [-95.235284, 46.932896], "pop": 626, "state": "MN", "_id": "56570"} -{"city": "OTTERTAIL", "loc": [-95.543571, 46.417722], "pop": 1080, "state": "MN", "_id": "56571"} -{"city": "PELICAN RAPIDS", "loc": [-96.066227, 46.599444], "pop": 4481, "state": "MN", "_id": "56572"} -{"city": "PERHAM", "loc": [-95.581763, 46.603106], "pop": 3238, "state": "MN", "_id": "56573"} -{"city": "PERLEY", "loc": [-96.777722, 47.18308], "pop": 303, "state": "MN", "_id": "56574"} -{"city": "PONSFORD", "loc": [-95.319717, 47.013348], "pop": 1072, "state": "MN", "_id": "56575"} -{"city": "RICHVILLE", "loc": [-95.592891, 46.500807], "pop": 898, "state": "MN", "_id": "56576"} -{"city": "RICHWOOD", "loc": [-95.850094, 46.93609], "pop": 594, "state": "MN", "_id": "56577"} -{"city": "ROCHERT", "loc": [-95.600443, 46.855904], "pop": 688, "state": "MN", "_id": "56578"} -{"city": "ROTHSAY", "loc": [-96.288726, 46.509412], "pop": 916, "state": "MN", "_id": "56579"} -{"city": "SABIN", "loc": [-96.59852, 46.770257], "pop": 1225, "state": "MN", "_id": "56580"} -{"city": "SHELLY", "loc": [-96.783802, 47.455375], "pop": 445, "state": "MN", "_id": "56581"} -{"city": "TENNEY", "loc": [-96.387049, 45.990196], "pop": 270, "state": "MN", "_id": "56583"} -{"city": "TWIN VALLEY", "loc": [-96.246382, 47.250905], "pop": 2108, "state": "MN", "_id": "56584"} -{"city": "ULEN", "loc": [-96.28239, 47.090263], "pop": 939, "state": "MN", "_id": "56585"} -{"city": "UNDERWOOD", "loc": [-95.84155, 46.32652], "pop": 1932, "state": "MN", "_id": "56586"} -{"city": "VERGAS", "loc": [-95.794812, 46.670645], "pop": 1338, "state": "MN", "_id": "56587"} -{"city": "VINING", "loc": [-95.588263, 46.256135], "pop": 463, "state": "MN", "_id": "56588"} -{"city": "WAUBUN", "loc": [-95.887125, 47.192009], "pop": 961, "state": "MN", "_id": "56589"} -{"city": "WENDELL", "loc": [-96.114407, 46.056174], "pop": 408, "state": "MN", "_id": "56590"} -{"city": "WINGER", "loc": [-95.994085, 47.537516], "pop": 406, "state": "MN", "_id": "56592"} -{"city": "WOLF LAKE", "loc": [-95.360706, 46.833417], "pop": 260, "state": "MN", "_id": "56593"} -{"city": "WOLVERTON", "loc": [-96.61487, 46.555017], "pop": 834, "state": "MN", "_id": "56594"} -{"city": "BEMIDJI", "loc": [-94.848809, 47.488071], "pop": 25278, "state": "MN", "_id": "56601"} -{"city": "BAGLEY", "loc": [-95.41334, 47.487024], "pop": 4063, "state": "MN", "_id": "56621"} -{"city": "BAUDETTE", "loc": [-94.599479, 48.692724], "pop": 2065, "state": "MN", "_id": "56623"} -{"city": "56625", "loc": [-95.039719, 47.357339], "pop": 688, "state": "MN", "_id": "56625"} -{"city": "BENA", "loc": [-94.251921, 47.347732], "pop": 461, "state": "MN", "_id": "56626"} -{"city": "BIG FALLS", "loc": [-93.729629, 48.156028], "pop": 652, "state": "MN", "_id": "56627"} -{"city": "BIGFORK", "loc": [-93.670699, 47.750227], "pop": 819, "state": "MN", "_id": "56628"} -{"city": "BIRCHDALE", "loc": [-94.167652, 48.624696], "pop": 374, "state": "MN", "_id": "56629"} -{"city": "BLACKDUCK", "loc": [-94.496072, 47.738136], "pop": 1428, "state": "MN", "_id": "56630"} -{"city": "BOY RIVER", "loc": [-94.102587, 47.181841], "pop": 132, "state": "MN", "_id": "56632"} -{"city": "CASS LAKE", "loc": [-94.611896, 47.35155], "pop": 2866, "state": "MN", "_id": "56633"} -{"city": "CLEARBROOK", "loc": [-95.375185, 47.714581], "pop": 1208, "state": "MN", "_id": "56634"} -{"city": "DEER RIVER", "loc": [-93.84979, 47.382867], "pop": 3254, "state": "MN", "_id": "56636"} -{"city": "TALMOON", "loc": [-93.774913, 47.588764], "pop": 155, "state": "MN", "_id": "56637"} -{"city": "EFFIE", "loc": [-93.579905, 47.847187], "pop": 346, "state": "MN", "_id": "56639"} -{"city": "FEDERAL DAM", "loc": [-94.257519, 47.206914], "pop": 347, "state": "MN", "_id": "56641"} -{"city": "GONVICK", "loc": [-95.499014, 47.749009], "pop": 931, "state": "MN", "_id": "56644"} -{"city": "GULLY", "loc": [-95.641034, 47.769745], "pop": 511, "state": "MN", "_id": "56646"} -{"city": "HINES", "loc": [-94.647666, 47.732429], "pop": 1083, "state": "MN", "_id": "56647"} -{"city": "INTERNATIONAL FA", "loc": [-93.40609, 48.583398], "pop": 11124, "state": "MN", "_id": "56649"} -{"city": "KELLIHER", "loc": [-94.538166, 47.927173], "pop": 1092, "state": "MN", "_id": "56650"} -{"city": "LENGBY", "loc": [-95.627475, 47.536998], "pop": 514, "state": "MN", "_id": "56651"} -{"city": "LEONARD", "loc": [-95.370775, 47.628905], "pop": 1022, "state": "MN", "_id": "56652"} -{"city": "LITTLEFORK", "loc": [-93.539932, 48.385233], "pop": 1769, "state": "MN", "_id": "56653"} -{"city": "LOMAN", "loc": [-93.840673, 48.516406], "pop": 198, "state": "MN", "_id": "56654"} -{"city": "LONGVILLE", "loc": [-94.198182, 47.024842], "pop": 757, "state": "MN", "_id": "56655"} -{"city": "MARCELL", "loc": [-93.678425, 47.634527], "pop": 449, "state": "MN", "_id": "56657"} -{"city": "MAX", "loc": [-93.972031, 47.628919], "pop": 140, "state": "MN", "_id": "56659"} -{"city": "MIZPAH", "loc": [-94.146748, 47.949525], "pop": 321, "state": "MN", "_id": "56660"} -{"city": "NORTHOME", "loc": [-94.204274, 47.844039], "pop": 908, "state": "MN", "_id": "56661"} -{"city": "OUTING", "loc": [-93.944252, 46.837528], "pop": 348, "state": "MN", "_id": "56662"} -{"city": "PENNINGTON", "loc": [-94.483361, 47.465121], "pop": 176, "state": "MN", "_id": "56663"} -{"city": "PITT", "loc": [-94.728252, 48.782986], "pop": 678, "state": "MN", "_id": "56665"} -{"city": "PONEMAH", "loc": [-94.917048, 48.037168], "pop": 790, "state": "MN", "_id": "56666"} -{"city": "PUPOSKY", "loc": [-94.980294, 47.7436], "pop": 967, "state": "MN", "_id": "56667"} -{"city": "RANIER", "loc": [-93.312457, 48.611847], "pop": 1145, "state": "MN", "_id": "56668"} -{"city": "RAY", "loc": [-93.089549, 48.421834], "pop": 365, "state": "MN", "_id": "56669"} -{"city": "REDBY", "loc": [-94.940409, 47.868888], "pop": 1394, "state": "MN", "_id": "56670"} -{"city": "REDLAKE", "loc": [-95.06432, 47.865378], "pop": 1358, "state": "MN", "_id": "56671"} -{"city": "REMER", "loc": [-93.919647, 47.087422], "pop": 1466, "state": "MN", "_id": "56672"} -{"city": "ROOSEVELT", "loc": [-95.179389, 48.814807], "pop": 907, "state": "MN", "_id": "56673"} -{"city": "SAUM", "loc": [-94.651203, 47.974492], "pop": 150, "state": "MN", "_id": "56674"} -{"city": "SHEVLIN", "loc": [-95.245021, 47.500364], "pop": 879, "state": "MN", "_id": "56676"} -{"city": "SOLWAY", "loc": [-95.120475, 47.547972], "pop": 971, "state": "MN", "_id": "56678"} -{"city": "SPRING LAKE", "loc": [-93.838215, 47.588811], "pop": 383, "state": "MN", "_id": "56680"} -{"city": "SQUAW LAKE", "loc": [-94.1475, 47.636162], "pop": 337, "state": "MN", "_id": "56681"} -{"city": "SWIFT", "loc": [-95.297499, 48.847573], "pop": 789, "state": "MN", "_id": "56682"} -{"city": "TENSTRIKE", "loc": [-94.682432, 47.661146], "pop": 184, "state": "MN", "_id": "56683"} -{"city": "TRAIL", "loc": [-95.760668, 47.747769], "pop": 353, "state": "MN", "_id": "56684"} -{"city": "WASKISH", "loc": [-94.503707, 48.176971], "pop": 115, "state": "MN", "_id": "56685"} -{"city": "WILLIAMS", "loc": [-94.955586, 48.802218], "pop": 1257, "state": "MN", "_id": "56686"} -{"city": "WILTON", "loc": [-94.986238, 47.535254], "pop": 976, "state": "MN", "_id": "56687"} -{"city": "WIRT", "loc": [-93.981079, 47.716428], "pop": 84, "state": "MN", "_id": "56688"} -{"city": "THIEF RIVER FALL", "loc": [-96.167019, 48.110391], "pop": 11401, "state": "MN", "_id": "56701"} -{"city": "ALVARADO", "loc": [-96.991457, 48.201968], "pop": 483, "state": "MN", "_id": "56710"} -{"city": "ANGLE INLET", "loc": [-95.090248, 49.324924], "pop": 50, "state": "MN", "_id": "56711"} -{"city": "ANGUS", "loc": [-96.656978, 48.092675], "pop": 336, "state": "MN", "_id": "56712"} -{"city": "ARGYLE", "loc": [-96.867096, 48.331418], "pop": 989, "state": "MN", "_id": "56713"} -{"city": "BADGER", "loc": [-96.096523, 48.791294], "pop": 1068, "state": "MN", "_id": "56714"} -{"city": "BROOKS", "loc": [-96.011663, 47.812942], "pop": 350, "state": "MN", "_id": "56715"} -{"city": "CROOKSTON", "loc": [-96.593078, 47.779694], "pop": 9976, "state": "MN", "_id": "56716"} -{"city": "DONALDSON", "loc": [-96.857894, 48.579792], "pop": 117, "state": "MN", "_id": "56720"} -{"city": "EAST GRAND FORKS", "loc": [-97.021262, 47.931802], "pop": 8830, "state": "MN", "_id": "56721"} -{"city": "EUCLID", "loc": [-96.921496, 48.033804], "pop": 1483, "state": "MN", "_id": "56722"} -{"city": "FISHER", "loc": [-96.880312, 47.838145], "pop": 1473, "state": "MN", "_id": "56723"} -{"city": "GATZKE", "loc": [-95.790305, 48.410416], "pop": 131, "state": "MN", "_id": "56724"} -{"city": "GOODRIDGE", "loc": [-95.728344, 48.068586], "pop": 923, "state": "MN", "_id": "56725"} -{"city": "GREENBUSH", "loc": [-96.187091, 48.695667], "pop": 1022, "state": "MN", "_id": "56726"} -{"city": "GRYGLA", "loc": [-95.639805, 48.307068], "pop": 1315, "state": "MN", "_id": "56727"} -{"city": "HALLOCK", "loc": [-96.944486, 48.774855], "pop": 1729, "state": "MN", "_id": "56728"} -{"city": "HALMA", "loc": [-96.596899, 48.666895], "pop": 146, "state": "MN", "_id": "56729"} -{"city": "KARLSTAD", "loc": [-96.551116, 48.591592], "pop": 1461, "state": "MN", "_id": "56732"} -{"city": "KENNEDY", "loc": [-96.961407, 48.633686], "pop": 673, "state": "MN", "_id": "56733"} -{"city": "LAKE BRONSON", "loc": [-96.643145, 48.77868], "pop": 548, "state": "MN", "_id": "56734"} -{"city": "ORLEANS", "loc": [-96.813729, 48.881968], "pop": 785, "state": "MN", "_id": "56735"} -{"city": "MENTOR", "loc": [-96.177823, 47.657597], "pop": 993, "state": "MN", "_id": "56736"} -{"city": "MIDDLE RIVER", "loc": [-96.12325, 48.442593], "pop": 948, "state": "MN", "_id": "56737"} -{"city": "NEWFOLDEN", "loc": [-96.255018, 48.28945], "pop": 1700, "state": "MN", "_id": "56738"} -{"city": "NOYES", "loc": [-97.149032, 48.969223], "pop": 67, "state": "MN", "_id": "56740"} -{"city": "OAK ISLAND", "loc": [-94.849209, 49.313466], "pop": 21, "state": "MN", "_id": "56741"} -{"city": "OKLEE", "loc": [-95.861693, 47.82861], "pop": 628, "state": "MN", "_id": "56742"} -{"city": "OSLO", "loc": [-97.116252, 48.20657], "pop": 552, "state": "MN", "_id": "56744"} -{"city": "PLUMMER", "loc": [-95.964558, 47.911324], "pop": 847, "state": "MN", "_id": "56748"} -{"city": "RED LAKE FALLS", "loc": [-96.268097, 47.882285], "pop": 2700, "state": "MN", "_id": "56750"} -{"city": "PENCER", "loc": [-95.756709, 48.826809], "pop": 5473, "state": "MN", "_id": "56751"} -{"city": "SAINT HILAIRE", "loc": [-96.224914, 48.010871], "pop": 873, "state": "MN", "_id": "56754"} -{"city": "SAINT VINCENT", "loc": [-97.170307, 48.945825], "pop": 241, "state": "MN", "_id": "56755"} -{"city": "SALOL", "loc": [-95.535594, 48.852213], "pop": 478, "state": "MN", "_id": "56756"} -{"city": "STEPHEN", "loc": [-96.867392, 48.452488], "pop": 1340, "state": "MN", "_id": "56757"} -{"city": "STRANDQUIST", "loc": [-96.472266, 48.45256], "pop": 969, "state": "MN", "_id": "56758"} -{"city": "STRATHCONA", "loc": [-96.109604, 48.626556], "pop": 860, "state": "MN", "_id": "56759"} -{"city": "VIKING", "loc": [-96.474926, 48.234881], "pop": 627, "state": "MN", "_id": "56760"} -{"city": "WANNASKA", "loc": [-95.7072, 48.64515], "pop": 684, "state": "MN", "_id": "56761"} -{"city": "RADIUM", "loc": [-96.759702, 48.20784], "pop": 2404, "state": "MN", "_id": "56762"} -{"city": "WARROAD", "loc": [-95.353843, 48.911144], "pop": 3766, "state": "MN", "_id": "56763"} -{"city": "ABBEVILLE", "loc": [-89.568835, 34.455553], "pop": 4649, "state": "MS", "_id": "38601"} -{"city": "CANNON", "loc": [-89.205428, 34.79456], "pop": 3226, "state": "MS", "_id": "38603"} -{"city": "BATESVILLE", "loc": [-89.91418, 34.331651], "pop": 9879, "state": "MS", "_id": "38606"} -{"city": "BLUE MOUNTAIN", "loc": [-89.008775, 34.670986], "pop": 3857, "state": "MS", "_id": "38610"} -{"city": "BYHALIA", "loc": [-89.676332, 34.885351], "pop": 11054, "state": "MS", "_id": "38611"} -{"city": "STOVALL", "loc": [-90.577755, 34.204984], "pop": 26774, "state": "MS", "_id": "38614"} -{"city": "COAHOMA", "loc": [-90.472748, 34.36247], "pop": 3055, "state": "MS", "_id": "38617"} -{"city": "COLDWATER", "loc": [-89.986882, 34.692362], "pop": 7247, "state": "MS", "_id": "38618"} -{"city": "COMO", "loc": [-89.915508, 34.513728], "pop": 3568, "state": "MS", "_id": "38619"} -{"city": "COURTLAND", "loc": [-89.939591, 34.25958], "pop": 6765, "state": "MS", "_id": "38620"} -{"city": "ASKEW", "loc": [-90.185524, 34.453966], "pop": 4693, "state": "MS", "_id": "38621"} -{"city": "DUMAS", "loc": [-88.807229, 34.64915], "pop": 2219, "state": "MS", "_id": "38625"} -{"city": "DUNDEE", "loc": [-90.38907, 34.528181], "pop": 1443, "state": "MS", "_id": "38626"} -{"city": "ETTA", "loc": [-89.176706, 34.435231], "pop": 1560, "state": "MS", "_id": "38627"} -{"city": "FALKNER", "loc": [-88.952493, 34.841682], "pop": 1408, "state": "MS", "_id": "38629"} -{"city": "HERNANDO", "loc": [-90.009487, 34.809588], "pop": 10894, "state": "MS", "_id": "38632"} -{"city": "HICKORY FLAT", "loc": [-89.186229, 34.624362], "pop": 1692, "state": "MS", "_id": "38633"} -{"city": "HOLLY SPRINGS", "loc": [-89.489714, 34.747061], "pop": 12519, "state": "MS", "_id": "38635"} -{"city": "HORN LAKE", "loc": [-90.050719, 34.9519], "pop": 14436, "state": "MS", "_id": "38637"} -{"city": "LAKE CORMORANT", "loc": [-90.160795, 34.893733], "pop": 1587, "state": "MS", "_id": "38641"} -{"city": "LAMAR", "loc": [-89.31633, 34.927072], "pop": 2761, "state": "MS", "_id": "38642"} -{"city": "LAMBERT", "loc": [-90.262991, 34.183719], "pop": 3378, "state": "MS", "_id": "38643"} -{"city": "LYON", "loc": [-90.498147, 34.247369], "pop": 1132, "state": "MS", "_id": "38645"} -{"city": "MARKS", "loc": [-90.281602, 34.260735], "pop": 4715, "state": "MS", "_id": "38646"} -{"city": "MICHIGAN CITY", "loc": [-89.136173, 34.931849], "pop": 1574, "state": "MS", "_id": "38647"} -{"city": "MYRTLE", "loc": [-89.115701, 34.540209], "pop": 2613, "state": "MS", "_id": "38650"} -{"city": "NESBIT", "loc": [-90.012199, 34.899189], "pop": 4098, "state": "MS", "_id": "38651"} -{"city": "NEW ALBANY", "loc": [-89.003058, 34.485051], "pop": 14109, "state": "MS", "_id": "38652"} -{"city": "OLIVE BRANCH", "loc": [-89.854427, 34.94414], "pop": 14069, "state": "MS", "_id": "38654"} -{"city": "LAFAYETTE", "loc": [-89.49692, 34.354354], "pop": 22599, "state": "MS", "_id": "38655"} -{"city": "PLEASANT GROVE", "loc": [-90.102494, 34.469951], "pop": 80, "state": "MS", "_id": "38657"} -{"city": "POPE", "loc": [-90.002735, 34.190559], "pop": 1860, "state": "MS", "_id": "38658"} -{"city": "POTTS CAMP", "loc": [-89.315073, 34.604742], "pop": 1580, "state": "MS", "_id": "38659"} -{"city": "RED BANKS", "loc": [-89.519839, 34.875039], "pop": 4860, "state": "MS", "_id": "38661"} -{"city": "RIPLEY", "loc": [-88.923973, 34.750912], "pop": 8895, "state": "MS", "_id": "38663"} -{"city": "ROBINSONVILLE", "loc": [-90.305174, 34.809329], "pop": 547, "state": "MS", "_id": "38664"} -{"city": "SAVAGE", "loc": [-90.138193, 34.611981], "pop": 1936, "state": "MS", "_id": "38665"} -{"city": "SARDIS", "loc": [-89.922083, 34.427573], "pop": 5023, "state": "MS", "_id": "38666"} -{"city": "SENATOBIA", "loc": [-89.885501, 34.632306], "pop": 12249, "state": "MS", "_id": "38668"} -{"city": "SLEDGE", "loc": [-90.20904, 34.382347], "pop": 201, "state": "MS", "_id": "38670"} -{"city": "SOUTHAVEN", "loc": [-89.999173, 34.977074], "pop": 19174, "state": "MS", "_id": "38671"} -{"city": "TAYLOR", "loc": [-89.627278, 34.284878], "pop": 2374, "state": "MS", "_id": "38673"} -{"city": "TIPLERSVILLE", "loc": [-88.915684, 34.90294], "pop": 909, "state": "MS", "_id": "38674"} -{"city": "TUNICA", "loc": [-90.368515, 34.688355], "pop": 6174, "state": "MS", "_id": "38676"} -{"city": "UNIVERSITY", "loc": [-89.582742, 34.364513], "pop": 1902, "state": "MS", "_id": "38677"} -{"city": "WALLS", "loc": [-90.120789, 34.964051], "pop": 2181, "state": "MS", "_id": "38680"} -{"city": "WALNUT", "loc": [-88.905337, 34.952732], "pop": 2553, "state": "MS", "_id": "38683"} -{"city": "WATERFORD", "loc": [-89.468298, 34.640248], "pop": 892, "state": "MS", "_id": "38685"} -{"city": "GREENVILLE", "loc": [-91.046793, 33.378737], "pop": 35884, "state": "MS", "_id": "38701"} -{"city": "GREENVILLE", "loc": [-91.022795, 33.408494], "pop": 18560, "state": "MS", "_id": "38703"} -{"city": "ALLIGATOR", "loc": [-90.738214, 34.129425], "pop": 969, "state": "MS", "_id": "38720"} -{"city": "ANGUILLA", "loc": [-90.809535, 33.01213], "pop": 2557, "state": "MS", "_id": "38721"} -{"city": "BENOIT", "loc": [-91.033765, 33.644795], "pop": 1554, "state": "MS", "_id": "38725"} -{"city": "BEULAH", "loc": [-90.979545, 33.787954], "pop": 467, "state": "MS", "_id": "38726"} -{"city": "BOYLE", "loc": [-90.733503, 33.685369], "pop": 1934, "state": "MS", "_id": "38730"} -{"city": "CLEVELAND", "loc": [-90.730871, 33.743002], "pop": 20953, "state": "MS", "_id": "38732"} -{"city": "DODDSVILLE", "loc": [-90.52637, 33.630045], "pop": 309, "state": "MS", "_id": "38736"} -{"city": "DREW", "loc": [-90.540606, 33.867031], "pop": 7680, "state": "MS", "_id": "38737"} -{"city": "DUNCAN", "loc": [-90.765191, 34.046966], "pop": 885, "state": "MS", "_id": "38740"} -{"city": "GLEN ALLAN", "loc": [-91.009193, 33.025361], "pop": 734, "state": "MS", "_id": "38744"} -{"city": "GUNNISON", "loc": [-90.931899, 33.943886], "pop": 1248, "state": "MS", "_id": "38746"} -{"city": "PERCY", "loc": [-90.84857, 33.177917], "pop": 4499, "state": "MS", "_id": "38748"} -{"city": "BAIRD", "loc": [-90.654144, 33.449075], "pop": 13001, "state": "MS", "_id": "38751"} -{"city": "INVERNESS", "loc": [-90.605065, 33.348964], "pop": 2140, "state": "MS", "_id": "38753"} -{"city": "ISOLA", "loc": [-90.604027, 33.247112], "pop": 1674, "state": "MS", "_id": "38754"} -{"city": "ELIZABETH", "loc": [-90.892801, 33.410025], "pop": 8047, "state": "MS", "_id": "38756"} -{"city": "MERIGOLD", "loc": [-90.725571, 33.837252], "pop": 738, "state": "MS", "_id": "38759"} -{"city": "MOORHEAD", "loc": [-90.51425, 33.478325], "pop": 4623, "state": "MS", "_id": "38761"} -{"city": "MOUND BAYOU", "loc": [-90.730247, 33.886023], "pop": 3485, "state": "MS", "_id": "38762"} -{"city": "ROSEDALE", "loc": [-90.992289, 33.843166], "pop": 3726, "state": "MS", "_id": "38769"} -{"city": "RULEVILLE", "loc": [-90.552657, 33.724052], "pop": 4153, "state": "MS", "_id": "38771"} -{"city": "SHAW", "loc": [-90.814275, 33.587388], "pop": 3596, "state": "MS", "_id": "38773"} -{"city": "SHELBY", "loc": [-90.762461, 33.949058], "pop": 3037, "state": "MS", "_id": "38774"} -{"city": "SUNFLOWER", "loc": [-90.638732, 33.584696], "pop": 961, "state": "MS", "_id": "38778"} -{"city": "WAYSIDE", "loc": [-91.024801, 33.261023], "pop": 198, "state": "MS", "_id": "38780"} -{"city": "TUPELO", "loc": [-88.720853, 34.253834], "pop": 40381, "state": "MS", "_id": "38801"} -{"city": "AMORY", "loc": [-88.470917, 33.9844], "pop": 11765, "state": "MS", "_id": "38821"} -{"city": "BALDWYN", "loc": [-88.63748, 34.527959], "pop": 6965, "state": "MS", "_id": "38824"} -{"city": "BELDEN", "loc": [-88.816011, 34.278663], "pop": 4441, "state": "MS", "_id": "38826"} -{"city": "BELMONT", "loc": [-88.230912, 34.51025], "pop": 2511, "state": "MS", "_id": "38827"} -{"city": "BLUE SPRINGS", "loc": [-88.859004, 34.427286], "pop": 2808, "state": "MS", "_id": "38828"} -{"city": "BOONEVILLE", "loc": [-88.544299, 34.669431], "pop": 16363, "state": "MS", "_id": "38829"} -{"city": "BURNSVILLE", "loc": [-88.306962, 34.857885], "pop": 3574, "state": "MS", "_id": "38833"} -{"city": "KOSSUTH", "loc": [-88.543884, 34.93236], "pop": 22984, "state": "MS", "_id": "38834"} -{"city": "DENNIS", "loc": [-88.21167, 34.549735], "pop": 1315, "state": "MS", "_id": "38838"} -{"city": "ECRU", "loc": [-88.955956, 34.337106], "pop": 3862, "state": "MS", "_id": "38841"} -{"city": "FULTON", "loc": [-88.379262, 34.274543], "pop": 14877, "state": "MS", "_id": "38843"} -{"city": "GATTMAN", "loc": [-88.258779, 33.87417], "pop": 408, "state": "MS", "_id": "38844"} -{"city": "GLEN", "loc": [-88.435859, 34.877981], "pop": 5578, "state": "MS", "_id": "38846"} -{"city": "GOLDEN", "loc": [-88.184159, 34.481214], "pop": 639, "state": "MS", "_id": "38847"} -{"city": "GREENWOOD SPRING", "loc": [-88.312333, 33.99344], "pop": 1934, "state": "MS", "_id": "38848"} -{"city": "GUNTOWN", "loc": [-88.701059, 34.429882], "pop": 4504, "state": "MS", "_id": "38849"} -{"city": "HOULKA", "loc": [-89.053114, 34.056145], "pop": 2571, "state": "MS", "_id": "38850"} -{"city": "HOUSTON", "loc": [-88.967005, 33.902554], "pop": 8781, "state": "MS", "_id": "38851"} -{"city": "IUKA", "loc": [-88.198289, 34.808891], "pop": 7497, "state": "MS", "_id": "38852"} -{"city": "MANTACHIE", "loc": [-88.496034, 34.358677], "pop": 4273, "state": "MS", "_id": "38855"} -{"city": "MARIETTA", "loc": [-88.449764, 34.501089], "pop": 1054, "state": "MS", "_id": "38856"} -{"city": "MOOREVILLE", "loc": [-88.59504, 34.280792], "pop": 3880, "state": "MS", "_id": "38857"} -{"city": "NETTLETON", "loc": [-88.605201, 34.082065], "pop": 6781, "state": "MS", "_id": "38858"} -{"city": "NEW SITE", "loc": [-88.435021, 34.561693], "pop": 1187, "state": "MS", "_id": "38859"} -{"city": "EGYPT", "loc": [-88.759843, 33.989015], "pop": 5258, "state": "MS", "_id": "38860"} -{"city": "PLANTERSVILLE", "loc": [-88.633456, 34.201073], "pop": 2913, "state": "MS", "_id": "38862"} -{"city": "PONTOTOC", "loc": [-88.986829, 34.217004], "pop": 10723, "state": "MS", "_id": "38863"} -{"city": "SAREPTA", "loc": [-89.163625, 34.178943], "pop": 2365, "state": "MS", "_id": "38864"} -{"city": "RIENZI", "loc": [-88.579454, 34.796789], "pop": 3162, "state": "MS", "_id": "38865"} -{"city": "SALTILLO", "loc": [-88.66736, 34.383128], "pop": 2324, "state": "MS", "_id": "38866"} -{"city": "SHANNON", "loc": [-88.734206, 34.121035], "pop": 4454, "state": "MS", "_id": "38868"} -{"city": "SMITHVILLE", "loc": [-88.390027, 34.064813], "pop": 1375, "state": "MS", "_id": "38870"} -{"city": "THAXTON", "loc": [-89.151468, 34.313563], "pop": 3171, "state": "MS", "_id": "38871"} -{"city": "TISHOMINGO", "loc": [-88.219395, 34.647753], "pop": 2143, "state": "MS", "_id": "38873"} -{"city": "TREMONT", "loc": [-88.239212, 34.233516], "pop": 871, "state": "MS", "_id": "38876"} -{"city": "VARDAMAN", "loc": [-89.192117, 33.919042], "pop": 2866, "state": "MS", "_id": "38878"} -{"city": "GRENADA", "loc": [-89.808738, 33.775094], "pop": 12322, "state": "MS", "_id": "38901"} -{"city": "AVALON", "loc": [-90.022077, 33.633683], "pop": 782, "state": "MS", "_id": "38912"} -{"city": "BANNER", "loc": [-89.415383, 34.094911], "pop": 819, "state": "MS", "_id": "38913"} -{"city": "BIG CREEK", "loc": [-89.437833, 33.851359], "pop": 461, "state": "MS", "_id": "38914"} -{"city": "BRUCE", "loc": [-89.348387, 34.006628], "pop": 4085, "state": "MS", "_id": "38915"} -{"city": "CALHOUN CITY", "loc": [-89.317809, 33.840776], "pop": 5416, "state": "MS", "_id": "38916"} -{"city": "CARROLLTON", "loc": [-89.950633, 33.520765], "pop": 2683, "state": "MS", "_id": "38917"} -{"city": "CASCILLA", "loc": [-90.036163, 33.907905], "pop": 2101, "state": "MS", "_id": "38920"} -{"city": "CHARLESTON", "loc": [-90.111559, 33.972621], "pop": 7077, "state": "MS", "_id": "38921"} -{"city": "COFFEEVILLE", "loc": [-89.678177, 33.921468], "pop": 6937, "state": "MS", "_id": "38922"} -{"city": "COILA", "loc": [-89.991516, 33.373111], "pop": 1529, "state": "MS", "_id": "38923"} -{"city": "CRUGER", "loc": [-90.231577, 33.310977], "pop": 897, "state": "MS", "_id": "38924"} -{"city": "DUCK HILL", "loc": [-89.733425, 33.686475], "pop": 4223, "state": "MS", "_id": "38925"} -{"city": "ENID", "loc": [-90.004392, 34.126345], "pop": 916, "state": "MS", "_id": "38927"} -{"city": "GORE SPRINGS", "loc": [-89.575449, 33.724529], "pop": 261, "state": "MS", "_id": "38929"} -{"city": "GREENWOOD", "loc": [-90.172589, 33.515884], "pop": 27931, "state": "MS", "_id": "38930"} -{"city": "HOLCOMB", "loc": [-89.901958, 33.760385], "pop": 3690, "state": "MS", "_id": "38940"} -{"city": "ITTA BENA", "loc": [-90.339421, 33.479838], "pop": 6178, "state": "MS", "_id": "38941"} -{"city": "MC CARLEY", "loc": [-89.851281, 33.586383], "pop": 941, "state": "MS", "_id": "38943"} -{"city": "MINTER CITY", "loc": [-90.31327, 33.751525], "pop": 1064, "state": "MS", "_id": "38944"} -{"city": "OAKLAND", "loc": [-89.887748, 34.075552], "pop": 1276, "state": "MS", "_id": "38948"} -{"city": "WATER VALLEY", "loc": [-89.421408, 34.176817], "pop": 302, "state": "MS", "_id": "38949"} -{"city": "PHILIPP", "loc": [-90.209086, 33.757391], "pop": 520, "state": "MS", "_id": "38950"} -{"city": "PITTSBORO", "loc": [-89.337622, 33.944326], "pop": 1053, "state": "MS", "_id": "38951"} -{"city": "SCHLATER", "loc": [-90.361976, 33.624439], "pop": 915, "state": "MS", "_id": "38952"} -{"city": "SCOBEY", "loc": [-89.891019, 33.92526], "pop": 228, "state": "MS", "_id": "38953"} -{"city": "SIDON", "loc": [-90.177937, 33.40946], "pop": 2212, "state": "MS", "_id": "38954"} -{"city": "TILLATOBA", "loc": [-89.894461, 33.985229], "pop": 621, "state": "MS", "_id": "38961"} -{"city": "TUTWILER", "loc": [-90.375341, 33.98195], "pop": 4627, "state": "MS", "_id": "38963"} -{"city": "VANCE", "loc": [-90.375246, 34.093639], "pop": 299, "state": "MS", "_id": "38964"} -{"city": "WATER VALLEY", "loc": [-89.637986, 34.152466], "pop": 5850, "state": "MS", "_id": "38965"} -{"city": "WINONA", "loc": [-89.727655, 33.485753], "pop": 7877, "state": "MS", "_id": "38967"} -{"city": "BELZONI", "loc": [-90.492435, 33.18421], "pop": 7600, "state": "MS", "_id": "39038"} -{"city": "BENTON", "loc": [-90.281516, 32.815772], "pop": 1114, "state": "MS", "_id": "39039"} -{"city": "BENTONIA", "loc": [-90.372156, 32.700015], "pop": 4325, "state": "MS", "_id": "39040"} -{"city": "BOLTON", "loc": [-90.447436, 32.377751], "pop": 3484, "state": "MS", "_id": "39041"} -{"city": "BRANDON", "loc": [-89.964029, 32.303803], "pop": 41141, "state": "MS", "_id": "39042"} -{"city": "BRAXTON", "loc": [-89.967225, 32.001581], "pop": 1704, "state": "MS", "_id": "39044"} -{"city": "CAMDEN", "loc": [-89.892838, 32.794832], "pop": 1835, "state": "MS", "_id": "39045"} -{"city": "CANTON", "loc": [-90.006079, 32.620543], "pop": 21725, "state": "MS", "_id": "39046"} -{"city": "CARLISLE", "loc": [-90.779904, 32.101322], "pop": 334, "state": "MS", "_id": "39049"} -{"city": "EDINBURG", "loc": [-89.535274, 32.788635], "pop": 11122, "state": "MS", "_id": "39051"} -{"city": "CHURCH HILL", "loc": [-91.214903, 31.67864], "pop": 1079, "state": "MS", "_id": "39055"} -{"city": "CLINTON", "loc": [-90.322893, 32.341079], "pop": 25262, "state": "MS", "_id": "39056"} -{"city": "CONEHATTA", "loc": [-89.268956, 32.466974], "pop": 2117, "state": "MS", "_id": "39057"} -{"city": "CRYSTAL SPRINGS", "loc": [-90.374439, 31.993329], "pop": 10787, "state": "MS", "_id": "39059"} -{"city": "DURANT", "loc": [-89.86116, 33.082756], "pop": 3459, "state": "MS", "_id": "39063"} -{"city": "EDWARDS", "loc": [-90.598392, 32.316008], "pop": 4144, "state": "MS", "_id": "39066"} -{"city": "ETHEL", "loc": [-89.494707, 33.152587], "pop": 2047, "state": "MS", "_id": "39067"} -{"city": "FAYETTE", "loc": [-91.058226, 31.712058], "pop": 6319, "state": "MS", "_id": "39069"} -{"city": "FLORA", "loc": [-90.323506, 32.557832], "pop": 3973, "state": "MS", "_id": "39071"} -{"city": "FLORENCE", "loc": [-90.121672, 32.153002], "pop": 14251, "state": "MS", "_id": "39073"} -{"city": "FOREST", "loc": [-89.467201, 32.346999], "pop": 6588, "state": "MS", "_id": "39074"} -{"city": "GEORGETOWN", "loc": [-90.212736, 31.855198], "pop": 2065, "state": "MS", "_id": "39078"} -{"city": "GOODMAN", "loc": [-89.982145, 32.972133], "pop": 3487, "state": "MS", "_id": "39079"} -{"city": "HARRISVILLE", "loc": [-90.105359, 31.967552], "pop": 2885, "state": "MS", "_id": "39082"} -{"city": "HAZLEHURST", "loc": [-90.405078, 31.856188], "pop": 12269, "state": "MS", "_id": "39083"} -{"city": "HERMANVILLE", "loc": [-90.844588, 31.956374], "pop": 1683, "state": "MS", "_id": "39086"} -{"city": "HOLLY BLUFF", "loc": [-90.704486, 32.824216], "pop": 218, "state": "MS", "_id": "39088"} -{"city": "KOSCIUSKO", "loc": [-89.572351, 33.0446], "pop": 11597, "state": "MS", "_id": "39090"} -{"city": "LAKE", "loc": [-89.341354, 32.345909], "pop": 704, "state": "MS", "_id": "39092"} -{"city": "LENA", "loc": [-89.549435, 32.644588], "pop": 3622, "state": "MS", "_id": "39094"} -{"city": "LEXINGTON", "loc": [-90.055674, 33.125043], "pop": 7252, "state": "MS", "_id": "39095"} -{"city": "LORMAN", "loc": [-91.095691, 31.838257], "pop": 668, "state": "MS", "_id": "39096"} -{"city": "LOUISE", "loc": [-90.589321, 32.991945], "pop": 1670, "state": "MS", "_id": "39097"} -{"city": "MC COOL", "loc": [-89.326806, 33.163206], "pop": 1438, "state": "MS", "_id": "39108"} -{"city": "MADDEN", "loc": [-89.381701, 32.708665], "pop": 1586, "state": "MS", "_id": "39109"} -{"city": "MADISON", "loc": [-90.108744, 32.467065], "pop": 12100, "state": "MS", "_id": "39110"} -{"city": "MAGEE", "loc": [-89.750325, 31.849513], "pop": 7590, "state": "MS", "_id": "39111"} -{"city": "MAYERSVILLE", "loc": [-91.028628, 32.901276], "pop": 1657, "state": "MS", "_id": "39113"} -{"city": "MENDENHALL", "loc": [-89.809494, 31.949425], "pop": 10110, "state": "MS", "_id": "39114"} -{"city": "MIZE", "loc": [-89.574135, 31.847285], "pop": 2858, "state": "MS", "_id": "39116"} -{"city": "MORTON", "loc": [-89.551222, 32.436105], "pop": 15400, "state": "MS", "_id": "39117"} -{"city": "MOUNT OLIVE", "loc": [-89.672387, 31.734582], "pop": 3060, "state": "MS", "_id": "39119"} -{"city": "NATCHEZ", "loc": [-91.364214, 31.54924], "pop": 34738, "state": "MS", "_id": "39120"} -{"city": "NEWHEBRON", "loc": [-90.01526, 31.73004], "pop": 1184, "state": "MS", "_id": "39140"} -{"city": "PATTISON", "loc": [-90.828306, 31.851025], "pop": 855, "state": "MS", "_id": "39144"} -{"city": "PELAHATCHIE", "loc": [-89.79135, 32.317994], "pop": 1661, "state": "MS", "_id": "39145"} -{"city": "PICKENS", "loc": [-89.973707, 32.890519], "pop": 1574, "state": "MS", "_id": "39146"} -{"city": "PINOLA", "loc": [-90.008775, 31.827732], "pop": 1664, "state": "MS", "_id": "39149"} -{"city": "PORT GIBSON", "loc": [-91.02303, 31.926604], "pop": 8498, "state": "MS", "_id": "39150"} -{"city": "PULASKI", "loc": [-89.650521, 32.270845], "pop": 1447, "state": "MS", "_id": "39152"} -{"city": "RALEIGH", "loc": [-89.508818, 32.05084], "pop": 8944, "state": "MS", "_id": "39153"} -{"city": "LEARNED", "loc": [-90.424088, 32.23922], "pop": 7530, "state": "MS", "_id": "39154"} -{"city": "REDWOOD", "loc": [-90.785899, 32.486699], "pop": 912, "state": "MS", "_id": "39156"} -{"city": "RIDGELAND", "loc": [-90.120697, 32.412165], "pop": 13226, "state": "MS", "_id": "39157"} -{"city": "ROLLING FORK", "loc": [-90.883274, 32.873317], "pop": 4509, "state": "MS", "_id": "39159"} -{"city": "SALLIS", "loc": [-89.755182, 33.000981], "pop": 3093, "state": "MS", "_id": "39160"} -{"city": "SATARTIA", "loc": [-90.596458, 32.612515], "pop": 496, "state": "MS", "_id": "39162"} -{"city": "SILVER CITY", "loc": [-90.492798, 33.05071], "pop": 1190, "state": "MS", "_id": "39166"} -{"city": "TAYLORSVILLE", "loc": [-89.404875, 31.839388], "pop": 2996, "state": "MS", "_id": "39168"} -{"city": "TCHULA", "loc": [-90.250012, 33.162388], "pop": 3976, "state": "MS", "_id": "39169"} -{"city": "TERRY", "loc": [-90.324143, 32.114683], "pop": 5538, "state": "MS", "_id": "39170"} -{"city": "UTICA", "loc": [-90.604724, 32.122897], "pop": 4630, "state": "MS", "_id": "39175"} -{"city": "VAIDEN", "loc": [-89.757023, 33.33433], "pop": 1987, "state": "MS", "_id": "39176"} -{"city": "VALLEY PARK", "loc": [-90.854209, 32.632258], "pop": 252, "state": "MS", "_id": "39177"} -{"city": "PICKENS", "loc": [-90.085477, 32.817424], "pop": 1417, "state": "MS", "_id": "39179"} -{"city": "VICKSBURG", "loc": [-90.85065, 32.325824], "pop": 46968, "state": "MS", "_id": "39180"} -{"city": "WALNUT GROVE", "loc": [-89.410538, 32.613418], "pop": 2098, "state": "MS", "_id": "39189"} -{"city": "WESSON", "loc": [-90.413106, 31.690055], "pop": 4187, "state": "MS", "_id": "39191"} -{"city": "WEST", "loc": [-89.769526, 33.1883], "pop": 2069, "state": "MS", "_id": "39192"} -{"city": "YAZOO CITY", "loc": [-90.403145, 32.85937], "pop": 17936, "state": "MS", "_id": "39194"} -{"city": "JACKSON", "loc": [-90.186655, 32.293502], "pop": 771, "state": "MS", "_id": "39201"} -{"city": "JACKSON", "loc": [-90.178194, 32.314883], "pop": 10979, "state": "MS", "_id": "39202"} -{"city": "JACKSON", "loc": [-90.202064, 32.308145], "pop": 15641, "state": "MS", "_id": "39203"} -{"city": "JACKSON", "loc": [-90.230579, 32.283162], "pop": 19655, "state": "MS", "_id": "39204"} -{"city": "JACKSON", "loc": [-90.173787, 32.369956], "pop": 25312, "state": "MS", "_id": "39206"} -{"city": "PEARL", "loc": [-90.102714, 32.276837], "pop": 25545, "state": "MS", "_id": "39208"} -{"city": "JACKSON", "loc": [-90.244626, 32.318422], "pop": 34407, "state": "MS", "_id": "39209"} -{"city": "JACKSON", "loc": [-90.129297, 32.373924], "pop": 25567, "state": "MS", "_id": "39211"} -{"city": "JACKSON", "loc": [-90.261201, 32.24347], "pop": 35321, "state": "MS", "_id": "39212"} -{"city": "JACKSON", "loc": [-90.217099, 32.355288], "pop": 33311, "state": "MS", "_id": "39213"} -{"city": "JACKSON", "loc": [-90.170814, 32.338574], "pop": 3824, "state": "MS", "_id": "39216"} -{"city": "RICHLAND", "loc": [-90.156231, 32.215224], "pop": 4563, "state": "MS", "_id": "39218"} -{"city": "JACKSON", "loc": [-90.188503, 32.30085], "pop": 0, "state": "MS", "_id": "39269"} -{"city": "MERIDIAN", "loc": [-88.655973, 32.357441], "pop": 28014, "state": "MS", "_id": "39301"} -{"city": "MERIDIAN", "loc": [-88.678322, 32.440129], "pop": 16175, "state": "MS", "_id": "39305"} -{"city": "MERIDIAN", "loc": [-88.743598, 32.373591], "pop": 20402, "state": "MS", "_id": "39307"} -{"city": "BAILEY", "loc": [-88.699086, 32.475417], "pop": 2495, "state": "MS", "_id": "39320"} -{"city": "BUCKATUNNA", "loc": [-88.52573, 31.507976], "pop": 2477, "state": "MS", "_id": "39322"} -{"city": "CHUNKY", "loc": [-88.946968, 32.247156], "pop": 79, "state": "MS", "_id": "39323"} -{"city": "COLLINSVILLE", "loc": [-88.815532, 32.518278], "pop": 4385, "state": "MS", "_id": "39325"} -{"city": "DALEVILLE", "loc": [-88.660942, 32.551124], "pop": 549, "state": "MS", "_id": "39326"} -{"city": "DECATUR", "loc": [-89.116832, 32.435798], "pop": 3595, "state": "MS", "_id": "39327"} -{"city": "DE KALB", "loc": [-88.733158, 32.716235], "pop": 5219, "state": "MS", "_id": "39328"} -{"city": "ENTERPRISE", "loc": [-88.84735, 32.156266], "pop": 1444, "state": "MS", "_id": "39330"} -{"city": "HICKORY", "loc": [-89.009254, 32.320012], "pop": 2504, "state": "MS", "_id": "39332"} -{"city": "LAUDERDALE", "loc": [-88.495569, 32.502186], "pop": 2190, "state": "MS", "_id": "39335"} -{"city": "LAWRENCE", "loc": [-89.272432, 32.287614], "pop": 86, "state": "MS", "_id": "39336"} -{"city": "LITTLE ROCK", "loc": [-89.181382, 32.540288], "pop": 896, "state": "MS", "_id": "39337"} -{"city": "LOUIN", "loc": [-89.219935, 32.099713], "pop": 1991, "state": "MS", "_id": "39338"} -{"city": "LOUISVILLE", "loc": [-89.028653, 33.105824], "pop": 16290, "state": "MS", "_id": "39339"} -{"city": "MACON", "loc": [-88.578093, 33.102674], "pop": 6634, "state": "MS", "_id": "39341"} -{"city": "NEWTON", "loc": [-89.183951, 32.324448], "pop": 6779, "state": "MS", "_id": "39345"} -{"city": "NOXAPATER", "loc": [-89.122076, 32.97895], "pop": 2870, "state": "MS", "_id": "39346"} -{"city": "PACHUTA", "loc": [-88.865293, 32.008105], "pop": 916, "state": "MS", "_id": "39347"} -{"city": "PAULDING", "loc": [-89.058441, 32.016744], "pop": 485, "state": "MS", "_id": "39348"} -{"city": "PHILADELPHIA", "loc": [-89.115371, 32.757224], "pop": 24316, "state": "MS", "_id": "39350"} -{"city": "PORTERVILLE", "loc": [-88.498425, 32.636833], "pop": 1202, "state": "MS", "_id": "39352"} -{"city": "PRESTON", "loc": [-88.813013, 32.853659], "pop": 1937, "state": "MS", "_id": "39354"} -{"city": "QUITMAN", "loc": [-88.678803, 32.066845], "pop": 8822, "state": "MS", "_id": "39355"} -{"city": "ROSE HILL", "loc": [-89.001053, 32.148931], "pop": 1725, "state": "MS", "_id": "39356"} -{"city": "SCOOBA", "loc": [-88.488272, 32.840197], "pop": 2087, "state": "MS", "_id": "39358"} -{"city": "MATHERVILLE", "loc": [-88.725866, 31.903082], "pop": 3442, "state": "MS", "_id": "39360"} -{"city": "SHUQUALAK", "loc": [-88.559393, 32.976933], "pop": 1323, "state": "MS", "_id": "39361"} -{"city": "STATE LINE", "loc": [-88.51711, 31.391619], "pop": 1382, "state": "MS", "_id": "39362"} -{"city": "STONEWALL", "loc": [-88.774827, 32.156396], "pop": 2690, "state": "MS", "_id": "39363"} -{"city": "TOOMSUBA", "loc": [-88.489225, 32.429583], "pop": 1346, "state": "MS", "_id": "39364"} -{"city": "UNION", "loc": [-89.049522, 32.537329], "pop": 4684, "state": "MS", "_id": "39365"} -{"city": "VOSSBURG", "loc": [-88.969598, 31.972301], "pop": 1357, "state": "MS", "_id": "39366"} -{"city": "WAYNESBORO", "loc": [-88.678221, 31.675447], "pop": 17046, "state": "MS", "_id": "39367"} -{"city": "HATTIESBURG", "loc": [-89.306471, 31.314553], "pop": 41866, "state": "MS", "_id": "39401"} -{"city": "HATTIESBURG", "loc": [-89.37751, 31.309753], "pop": 25479, "state": "MS", "_id": "39402"} -{"city": "BASSFIELD", "loc": [-89.702735, 31.503695], "pop": 3605, "state": "MS", "_id": "39421"} -{"city": "BAY SPRINGS", "loc": [-89.233768, 31.944939], "pop": 6778, "state": "MS", "_id": "39422"} -{"city": "BEAUMONT", "loc": [-88.905492, 31.136763], "pop": 1883, "state": "MS", "_id": "39423"} -{"city": "BROOKLYN", "loc": [-89.23372, 30.998852], "pop": 2080, "state": "MS", "_id": "39425"} -{"city": "CARRIERE", "loc": [-89.57793, 30.617772], "pop": 12978, "state": "MS", "_id": "39426"} -{"city": "CARSON", "loc": [-89.775821, 31.558501], "pop": 1381, "state": "MS", "_id": "39427"} -{"city": "COLLINS", "loc": [-89.543809, 31.670712], "pop": 9457, "state": "MS", "_id": "39428"} -{"city": "COLUMBIA", "loc": [-89.799785, 31.255877], "pop": 19891, "state": "MS", "_id": "39429"} -{"city": "ELLISVILLE", "loc": [-89.223073, 31.579663], "pop": 10632, "state": "MS", "_id": "39437"} -{"city": "HEIDELBERG", "loc": [-88.998052, 31.882031], "pop": 3416, "state": "MS", "_id": "39439"} -{"city": "LAUREL", "loc": [-89.131155, 31.705444], "pop": 45040, "state": "MS", "_id": "39440"} -{"city": "LEAKESVILLE", "loc": [-88.559546, 31.123825], "pop": 4858, "state": "MS", "_id": "39451"} -{"city": "AGRICOLA", "loc": [-88.593391, 30.866722], "pop": 17844, "state": "MS", "_id": "39452"} -{"city": "LUMBERTON", "loc": [-89.454254, 31.053356], "pop": 6096, "state": "MS", "_id": "39455"} -{"city": "LEAF", "loc": [-88.81947, 31.078449], "pop": 1061, "state": "MS", "_id": "39456"} -{"city": "MOSELLE", "loc": [-89.320633, 31.483626], "pop": 2350, "state": "MS", "_id": "39459"} -{"city": "NEELY", "loc": [-88.71983, 31.179325], "pop": 1059, "state": "MS", "_id": "39461"} -{"city": "NEW AUGUSTA", "loc": [-89.028611, 31.130907], "pop": 2152, "state": "MS", "_id": "39462"} -{"city": "OVETT", "loc": [-89.039613, 31.47377], "pop": 1113, "state": "MS", "_id": "39464"} -{"city": "PETAL", "loc": [-89.222239, 31.347181], "pop": 15799, "state": "MS", "_id": "39465"} -{"city": "PICAYUNE", "loc": [-89.691021, 30.541796], "pop": 20116, "state": "MS", "_id": "39466"} -{"city": "POPLARVILLE", "loc": [-89.564751, 30.852362], "pop": 7682, "state": "MS", "_id": "39470"} -{"city": "PRENTISS", "loc": [-89.873497, 31.605655], "pop": 9065, "state": "MS", "_id": "39474"} -{"city": "PURVIS", "loc": [-89.462301, 31.149562], "pop": 4493, "state": "MS", "_id": "39475"} -{"city": "RICHTON", "loc": [-88.910079, 31.343364], "pop": 7378, "state": "MS", "_id": "39476"} -{"city": "SANDY HOOK", "loc": [-89.818343, 31.047904], "pop": 448, "state": "MS", "_id": "39478"} -{"city": "SEMINARY", "loc": [-89.481585, 31.528485], "pop": 4010, "state": "MS", "_id": "39479"} -{"city": "SOSO", "loc": [-89.308223, 31.75941], "pop": 2890, "state": "MS", "_id": "39480"} -{"city": "STRINGER", "loc": [-89.262144, 31.844721], "pop": 1367, "state": "MS", "_id": "39481"} -{"city": "SUMRALL", "loc": [-89.577723, 31.359972], "pop": 4741, "state": "MS", "_id": "39482"} -{"city": "FOXWORTH", "loc": [-89.931075, 31.247325], "pop": 4317, "state": "MS", "_id": "39483"} -{"city": "GULFPORT", "loc": [-89.097618, 30.382556], "pop": 25894, "state": "MS", "_id": "39501"} -{"city": "GULFPORT", "loc": [-89.088552, 30.460105], "pop": 26830, "state": "MS", "_id": "39503"} -{"city": "GULFPORT", "loc": [-89.035347, 30.396248], "pop": 18144, "state": "MS", "_id": "39507"} -{"city": "DIAMONDHEAD", "loc": [-89.397356, 30.324275], "pop": 20156, "state": "MS", "_id": "39520"} -{"city": "BILOXI", "loc": [-88.897143, 30.403478], "pop": 22245, "state": "MS", "_id": "39530"} -{"city": "BILOXI", "loc": [-88.960499, 30.40334], "pop": 18541, "state": "MS", "_id": "39531"} -{"city": "NORTH BAY", "loc": [-88.918846, 30.452031], "pop": 25296, "state": "MS", "_id": "39532"} -{"city": "GAUTIER", "loc": [-88.641173, 30.398032], "pop": 13240, "state": "MS", "_id": "39553"} -{"city": "LONG BEACH", "loc": [-89.164577, 30.359756], "pop": 17826, "state": "MS", "_id": "39560"} -{"city": "MC HENRY", "loc": [-89.153631, 30.697463], "pop": 375, "state": "MS", "_id": "39561"} -{"city": "KREOLE", "loc": [-88.526015, 30.402773], "pop": 15657, "state": "MS", "_id": "39563"} -{"city": "OCEAN SPRINGS", "loc": [-88.780099, 30.440433], "pop": 34238, "state": "MS", "_id": "39564"} -{"city": "PASCAGOULA", "loc": [-88.516472, 30.530301], "pop": 16025, "state": "MS", "_id": "39567"} -{"city": "PASS CHRISTIAN", "loc": [-89.284269, 30.398918], "pop": 14427, "state": "MS", "_id": "39571"} -{"city": "PERKINSTON", "loc": [-89.139985, 30.76686], "pop": 3743, "state": "MS", "_id": "39573"} -{"city": "SAUCIER", "loc": [-89.147729, 30.596082], "pop": 6520, "state": "MS", "_id": "39574"} -{"city": "WAVELAND", "loc": [-89.383679, 30.29138], "pop": 5967, "state": "MS", "_id": "39576"} -{"city": "WIGGINS", "loc": [-89.132369, 30.86095], "pop": 6632, "state": "MS", "_id": "39577"} -{"city": "PASCAGOULA", "loc": [-88.52886, 30.366377], "pop": 27619, "state": "MS", "_id": "39581"} -{"city": "BROOKHAVEN", "loc": [-90.451995, 31.58581], "pop": 20910, "state": "MS", "_id": "39601"} -{"city": "BOGUE CHITTO", "loc": [-90.473183, 31.456344], "pop": 7068, "state": "MS", "_id": "39629"} -{"city": "CENTREVILLE", "loc": [-91.095985, 31.089329], "pop": 4055, "state": "MS", "_id": "39631"} -{"city": "CROSBY", "loc": [-91.023712, 31.299406], "pop": 574, "state": "MS", "_id": "39633"} -{"city": "GLOSTER", "loc": [-90.924045, 31.19033], "pop": 6389, "state": "MS", "_id": "39638"} -{"city": "JAYESS", "loc": [-90.186386, 31.403577], "pop": 1621, "state": "MS", "_id": "39641"} -{"city": "KOKOMO", "loc": [-89.982419, 31.177688], "pop": 888, "state": "MS", "_id": "39643"} -{"city": "LIBERTY", "loc": [-90.713349, 31.138427], "pop": 2950, "state": "MS", "_id": "39645"} -{"city": "MC CALL CREEK", "loc": [-90.783581, 31.490772], "pop": 3017, "state": "MS", "_id": "39647"} -{"city": "MC COMB", "loc": [-90.432449, 31.221594], "pop": 23551, "state": "MS", "_id": "39648"} -{"city": "MAGNOLIA", "loc": [-90.48311, 31.121745], "pop": 7831, "state": "MS", "_id": "39652"} -{"city": "MEADVILLE", "loc": [-90.857835, 31.428489], "pop": 1673, "state": "MS", "_id": "39653"} -{"city": "MONTICELLO", "loc": [-90.12796, 31.523776], "pop": 4517, "state": "MS", "_id": "39654"} -{"city": "OAK VALE", "loc": [-89.98273, 31.441953], "pop": 321, "state": "MS", "_id": "39656"} -{"city": "OSYKA", "loc": [-90.478977, 31.021747], "pop": 1143, "state": "MS", "_id": "39657"} -{"city": "ROXIE", "loc": [-91.062475, 31.504043], "pop": 3526, "state": "MS", "_id": "39661"} -{"city": "RUTH", "loc": [-90.286673, 31.379495], "pop": 415, "state": "MS", "_id": "39662"} -{"city": "SILVER CREEK", "loc": [-90.017332, 31.575535], "pop": 2862, "state": "MS", "_id": "39663"} -{"city": "SMITHDALE", "loc": [-90.675236, 31.367203], "pop": 1247, "state": "MS", "_id": "39664"} -{"city": "SONTAG", "loc": [-90.180125, 31.645313], "pop": 1953, "state": "MS", "_id": "39665"} -{"city": "SUMMIT", "loc": [-90.465427, 31.29752], "pop": 6696, "state": "MS", "_id": "39666"} -{"city": "TYLERTOWN", "loc": [-90.116918, 31.146552], "pop": 14352, "state": "MS", "_id": "39667"} -{"city": "UNION CHURCH", "loc": [-90.825922, 31.710474], "pop": 587, "state": "MS", "_id": "39668"} -{"city": "WOODVILLE", "loc": [-91.307596, 31.145913], "pop": 6400, "state": "MS", "_id": "39669"} -{"city": "COLUMBUS", "loc": [-88.426194, 33.537699], "pop": 32609, "state": "MS", "_id": "39701"} -{"city": "COLUMBUS", "loc": [-88.355387, 33.481175], "pop": 21004, "state": "MS", "_id": "39702"} -{"city": "ABERDEEN", "loc": [-88.538033, 33.828439], "pop": 15769, "state": "MS", "_id": "39730"} -{"city": "ACKERMAN", "loc": [-89.20139, 33.351684], "pop": 5027, "state": "MS", "_id": "39735"} -{"city": "BROOKSVILLE", "loc": [-88.501351, 33.213961], "pop": 4598, "state": "MS", "_id": "39739"} -{"city": "CALEDONIA", "loc": [-88.314537, 33.686461], "pop": 2008, "state": "MS", "_id": "39740"} -{"city": "CEDARBLUFF", "loc": [-88.826712, 33.61616], "pop": 1063, "state": "MS", "_id": "39741"} -{"city": "CRAWFORD", "loc": [-88.567119, 33.321922], "pop": 2310, "state": "MS", "_id": "39743"} -{"city": "TOMNOLEN", "loc": [-89.304542, 33.568908], "pop": 5686, "state": "MS", "_id": "39744"} -{"city": "FRENCH CAMP", "loc": [-89.394585, 33.338319], "pop": 1426, "state": "MS", "_id": "39745"} -{"city": "HAMILTON", "loc": [-88.44784, 33.801141], "pop": 726, "state": "MS", "_id": "39746"} -{"city": "KILMICHAEL", "loc": [-89.569577, 33.416476], "pop": 2351, "state": "MS", "_id": "39747"} -{"city": "MABEN", "loc": [-89.065887, 33.526179], "pop": 1286, "state": "MS", "_id": "39750"} -{"city": "MANTEE", "loc": [-89.122906, 33.665493], "pop": 1813, "state": "MS", "_id": "39751"} -{"city": "MATHISTON", "loc": [-89.127479, 33.541493], "pop": 3213, "state": "MS", "_id": "39752"} -{"city": "PHEBA", "loc": [-88.951842, 33.596056], "pop": 740, "state": "MS", "_id": "39755"} -{"city": "PRAIRIE", "loc": [-88.633463, 33.795113], "pop": 135, "state": "MS", "_id": "39756"} -{"city": "SESSUMS", "loc": [-88.817637, 33.450125], "pop": 36268, "state": "MS", "_id": "39759"} -{"city": "STEENS", "loc": [-88.327755, 33.56708], "pop": 1377, "state": "MS", "_id": "39766"} -{"city": "STEWART", "loc": [-89.479, 33.510097], "pop": 892, "state": "MS", "_id": "39767"} -{"city": "STURGIS", "loc": [-89.04727, 33.35702], "pop": 814, "state": "MS", "_id": "39769"} -{"city": "WEIR", "loc": [-89.280419, 33.264464], "pop": 1402, "state": "MS", "_id": "39772"} -{"city": "WEST POINT", "loc": [-88.68226, 33.626496], "pop": 19317, "state": "MS", "_id": "39773"} -{"city": "WOODLAND", "loc": [-89.025557, 33.80661], "pop": 2276, "state": "MS", "_id": "39776"} -{"city": "CHESTERFIELD", "loc": [-90.614185, 38.631832], "pop": 7770, "state": "MO", "_id": "63005"} -{"city": "ARNOLD", "loc": [-90.387046, 38.430484], "pop": 29195, "state": "MO", "_id": "63010"} -{"city": "MANCHESTER", "loc": [-90.55213, 38.604132], "pop": 36722, "state": "MO", "_id": "63011"} -{"city": "BARNHART", "loc": [-90.41417, 38.338425], "pop": 7689, "state": "MO", "_id": "63012"} -{"city": "BEAUFORT", "loc": [-91.170929, 38.429352], "pop": 460, "state": "MO", "_id": "63013"} -{"city": "BERGER", "loc": [-91.337412, 38.644449], "pop": 1092, "state": "MO", "_id": "63014"} -{"city": "CATAWISSA", "loc": [-90.761703, 38.436161], "pop": 831, "state": "MO", "_id": "63015"} -{"city": "CEDAR HILL", "loc": [-90.649777, 38.357319], "pop": 7211, "state": "MO", "_id": "63016"} -{"city": "TOWN AND COUNTRY", "loc": [-90.53969, 38.647241], "pop": 40848, "state": "MO", "_id": "63017"} -{"city": "CRYSTAL CITY", "loc": [-90.382525, 38.23002], "pop": 4112, "state": "MO", "_id": "63019"} -{"city": "DE SOTO", "loc": [-90.554621, 38.120421], "pop": 13331, "state": "MO", "_id": "63020"} -{"city": "BALLWIN", "loc": [-90.525527, 38.577032], "pop": 46397, "state": "MO", "_id": "63021"} -{"city": "DITTMER", "loc": [-90.691101, 38.315465], "pop": 2410, "state": "MO", "_id": "63023"} -{"city": "CRESCENT", "loc": [-90.626277, 38.484832], "pop": 6053, "state": "MO", "_id": "63025"} -{"city": "FENTON", "loc": [-90.468299, 38.501489], "pop": 38020, "state": "MO", "_id": "63026"} -{"city": "FESTUS", "loc": [-90.42859, 38.187889], "pop": 22497, "state": "MO", "_id": "63028"} -{"city": "FLETCHER", "loc": [-90.734456, 38.180141], "pop": 241, "state": "MO", "_id": "63030"} -{"city": "FLORISSANT", "loc": [-90.340097, 38.806865], "pop": 52659, "state": "MO", "_id": "63031"} -{"city": "FLORISSANT", "loc": [-90.283062, 38.794711], "pop": 44480, "state": "MO", "_id": "63033"} -{"city": "FLORISSANT", "loc": [-90.293617, 38.833841], "pop": 13972, "state": "MO", "_id": "63034"} -{"city": "FRENCH VILLAGE", "loc": [-90.400507, 37.996101], "pop": 697, "state": "MO", "_id": "63036"} -{"city": "GERALD", "loc": [-91.293062, 38.350719], "pop": 4664, "state": "MO", "_id": "63037"} -{"city": "GLENCOE", "loc": [-90.643971, 38.5745], "pop": 6093, "state": "MO", "_id": "63038"} -{"city": "GRAY SUMMIT", "loc": [-90.829184, 38.503044], "pop": 752, "state": "MO", "_id": "63039"} -{"city": "GROVER", "loc": [-90.646112, 38.573316], "pop": 497, "state": "MO", "_id": "63040"} -{"city": "HAZELWOOD", "loc": [-90.366925, 38.780875], "pop": 20004, "state": "MO", "_id": "63042"} -{"city": "MARYLAND HEIGHTS", "loc": [-90.447403, 38.722896], "pop": 21268, "state": "MO", "_id": "63043"} -{"city": "BRIDGETON", "loc": [-90.416101, 38.750627], "pop": 17695, "state": "MO", "_id": "63044"} -{"city": "BRIDGETON", "loc": [-90.458062, 38.7561], "pop": 0, "state": "MO", "_id": "63045"} -{"city": "HERCULANEUM", "loc": [-90.387095, 38.260087], "pop": 2490, "state": "MO", "_id": "63048"} -{"city": "HIGH RIDGE", "loc": [-90.528127, 38.472783], "pop": 12915, "state": "MO", "_id": "63049"} -{"city": "HILLSBORO", "loc": [-90.578196, 38.258582], "pop": 14095, "state": "MO", "_id": "63050"} -{"city": "HOUSE SPRINGS", "loc": [-90.557539, 38.413068], "pop": 10035, "state": "MO", "_id": "63051"} -{"city": "ANTONIA", "loc": [-90.431134, 38.392733], "pop": 17913, "state": "MO", "_id": "63052"} -{"city": "LABADIE", "loc": [-90.876966, 38.520031], "pop": 498, "state": "MO", "_id": "63055"} -{"city": "LESLIE", "loc": [-91.195164, 38.458228], "pop": 1238, "state": "MO", "_id": "63056"} -{"city": "LONEDELL", "loc": [-90.822216, 38.274982], "pop": 1153, "state": "MO", "_id": "63060"} -{"city": "LUEBBERING", "loc": [-90.802713, 38.261499], "pop": 224, "state": "MO", "_id": "63061"} -{"city": "NEW HAVEN", "loc": [-91.22905, 38.573995], "pop": 4353, "state": "MO", "_id": "63068"} -{"city": "PACIFIC", "loc": [-90.747968, 38.492168], "pop": 12874, "state": "MO", "_id": "63069"} -{"city": "PEVELY", "loc": [-90.411075, 38.279911], "pop": 5221, "state": "MO", "_id": "63070"} -{"city": "RICHWOODS", "loc": [-90.83099, 38.149639], "pop": 1079, "state": "MO", "_id": "63071"} -{"city": "ROBERTSVILLE", "loc": [-90.801619, 38.381602], "pop": 4250, "state": "MO", "_id": "63072"} -{"city": "SAINT ANN", "loc": [-90.386418, 38.725928], "pop": 16528, "state": "MO", "_id": "63074"} -{"city": "SAINT CLAIR", "loc": [-90.971346, 38.329927], "pop": 10668, "state": "MO", "_id": "63077"} -{"city": "SULLIVAN", "loc": [-91.156662, 38.230706], "pop": 7541, "state": "MO", "_id": "63080"} -{"city": "UNION", "loc": [-91.020596, 38.445629], "pop": 14183, "state": "MO", "_id": "63084"} -{"city": "VALLEY PARK", "loc": [-90.492422, 38.557619], "pop": 5014, "state": "MO", "_id": "63088"} -{"city": "VILLA RIDGE", "loc": [-90.882198, 38.460108], "pop": 4954, "state": "MO", "_id": "63089"} -{"city": "WASHINGTON", "loc": [-91.019346, 38.545851], "pop": 15437, "state": "MO", "_id": "63090"} -{"city": "ROSEBUD", "loc": [-91.397407, 38.385301], "pop": 607, "state": "MO", "_id": "63091"} -{"city": "SAINT LOUIS", "loc": [-90.191313, 38.634616], "pop": 931, "state": "MO", "_id": "63101"} -{"city": "SAINT LOUIS", "loc": [-90.18736, 38.630803], "pop": 731, "state": "MO", "_id": "63102"} -{"city": "SAINT LOUIS", "loc": [-90.216444, 38.633176], "pop": 6710, "state": "MO", "_id": "63103"} -{"city": "SAINT LOUIS", "loc": [-90.218512, 38.612819], "pop": 20885, "state": "MO", "_id": "63104"} -{"city": "CLAYTON", "loc": [-90.324189, 38.642574], "pop": 15732, "state": "MO", "_id": "63105"} -{"city": "SAINT LOUIS", "loc": [-90.208198, 38.644246], "pop": 15156, "state": "MO", "_id": "63106"} -{"city": "SAINT LOUIS", "loc": [-90.21249, 38.664522], "pop": 23263, "state": "MO", "_id": "63107"} -{"city": "SAINT LOUIS", "loc": [-90.254397, 38.644526], "pop": 20993, "state": "MO", "_id": "63108"} -{"city": "SAINT LOUIS", "loc": [-90.292918, 38.585452], "pop": 30029, "state": "MO", "_id": "63109"} -{"city": "SAINT LOUIS", "loc": [-90.256381, 38.618534], "pop": 23697, "state": "MO", "_id": "63110"} -{"city": "SAINT LOUIS", "loc": [-90.249452, 38.563349], "pop": 22733, "state": "MO", "_id": "63111"} -{"city": "SAINT LOUIS", "loc": [-90.28187, 38.661619], "pop": 28841, "state": "MO", "_id": "63112"} -{"city": "SAINT LOUIS", "loc": [-90.249633, 38.65896], "pop": 23360, "state": "MO", "_id": "63113"} -{"city": "OVERLAND", "loc": [-90.363304, 38.704425], "pop": 40522, "state": "MO", "_id": "63114"} -{"city": "SAINT LOUIS", "loc": [-90.238478, 38.675618], "pop": 30748, "state": "MO", "_id": "63115"} -{"city": "SAINT LOUIS", "loc": [-90.262543, 38.581356], "pop": 49014, "state": "MO", "_id": "63116"} -{"city": "RICHMOND HEIGHTS", "loc": [-90.324817, 38.629202], "pop": 10263, "state": "MO", "_id": "63117"} -{"city": "SAINT LOUIS", "loc": [-90.230911, 38.594265], "pop": 33259, "state": "MO", "_id": "63118"} -{"city": "WEBSTER GROVES", "loc": [-90.350807, 38.588853], "pop": 33698, "state": "MO", "_id": "63119"} -{"city": "SAINT LOUIS", "loc": [-90.25945, 38.690914], "pop": 17815, "state": "MO", "_id": "63120"} -{"city": "NORMANDY", "loc": [-90.296719, 38.705086], "pop": 31649, "state": "MO", "_id": "63121"} -{"city": "KIRKWOOD", "loc": [-90.410042, 38.58486], "pop": 39452, "state": "MO", "_id": "63122"} -{"city": "AFFTON", "loc": [-90.325304, 38.550594], "pop": 47127, "state": "MO", "_id": "63123"} -{"city": "LADUE", "loc": [-90.375468, 38.642383], "pop": 9517, "state": "MO", "_id": "63124"} -{"city": "LEMAY", "loc": [-90.295909, 38.521899], "pop": 33874, "state": "MO", "_id": "63125"} -{"city": "SAPPINGTON", "loc": [-90.378679, 38.550349], "pop": 16311, "state": "MO", "_id": "63126"} -{"city": "SAPPINGTON", "loc": [-90.405967, 38.540369], "pop": 4770, "state": "MO", "_id": "63127"} -{"city": "SAPPINGTON", "loc": [-90.372275, 38.498285], "pop": 28366, "state": "MO", "_id": "63128"} -{"city": "SOUTH COUNTY", "loc": [-90.32139, 38.468864], "pop": 45920, "state": "MO", "_id": "63129"} -{"city": "UNIVERSITY CITY", "loc": [-90.321896, 38.663941], "pop": 33986, "state": "MO", "_id": "63130"} -{"city": "DES PERES", "loc": [-90.44264, 38.612479], "pop": 16955, "state": "MO", "_id": "63131"} -{"city": "OLIVETTE", "loc": [-90.369642, 38.672823], "pop": 15193, "state": "MO", "_id": "63132"} -{"city": "SAINT LOUIS", "loc": [-90.303272, 38.6779], "pop": 11141, "state": "MO", "_id": "63133"} -{"city": "BERKELEY", "loc": [-90.337834, 38.739614], "pop": 18068, "state": "MO", "_id": "63134"} -{"city": "FERGUSON", "loc": [-90.302241, 38.748429], "pop": 23173, "state": "MO", "_id": "63135"} -{"city": "JENNINGS", "loc": [-90.260189, 38.738878], "pop": 54994, "state": "MO", "_id": "63136"} -{"city": "NORTH COUNTY", "loc": [-90.217778, 38.74885], "pop": 21055, "state": "MO", "_id": "63137"} -{"city": "NORTH COUNTY", "loc": [-90.211582, 38.787041], "pop": 20801, "state": "MO", "_id": "63138"} -{"city": "SAINT LOUIS", "loc": [-90.292045, 38.610776], "pop": 25310, "state": "MO", "_id": "63139"} -{"city": "BERKELEY", "loc": [-90.322846, 38.738482], "pop": 2478, "state": "MO", "_id": "63140"} -{"city": "CREVE COEUR", "loc": [-90.457072, 38.661741], "pop": 20120, "state": "MO", "_id": "63141"} -{"city": "MAPLEWOOD", "loc": [-90.319611, 38.613116], "pop": 12025, "state": "MO", "_id": "63143"} -{"city": "BRENTWOOD", "loc": [-90.350944, 38.620839], "pop": 9770, "state": "MO", "_id": "63144"} -{"city": "WEST COUNTY", "loc": [-90.448251, 38.688418], "pop": 29946, "state": "MO", "_id": "63146"} -{"city": "SAINT LOUIS", "loc": [-90.237512, 38.713889], "pop": 13186, "state": "MO", "_id": "63147"} -{"city": "SAINT CHARLES", "loc": [-90.506503, 38.801424], "pop": 47255, "state": "MO", "_id": "63301"} -{"city": "SAINT CHARLES", "loc": [-90.547059, 38.762237], "pop": 40675, "state": "MO", "_id": "63303"} -{"city": "SAINT CHARLES", "loc": [-90.623443, 38.737769], "pop": 27477, "state": "MO", "_id": "63304"} -{"city": "ANNADA", "loc": [-90.822355, 39.255758], "pop": 199, "state": "MO", "_id": "63330"} -{"city": "AUGUSTA", "loc": [-90.881471, 38.572767], "pop": 302, "state": "MO", "_id": "63332"} -{"city": "BELLFLOWER", "loc": [-91.348915, 39.0012], "pop": 689, "state": "MO", "_id": "63333"} -{"city": "BOWLING GREEN", "loc": [-91.196232, 39.334639], "pop": 5258, "state": "MO", "_id": "63334"} -{"city": "CLARKSVILLE", "loc": [-90.936205, 39.346477], "pop": 1195, "state": "MO", "_id": "63336"} -{"city": "CURRYVILLE", "loc": [-91.349301, 39.325702], "pop": 1604, "state": "MO", "_id": "63339"} -{"city": "DEFIANCE", "loc": [-90.830231, 38.661557], "pop": 2861, "state": "MO", "_id": "63341"} -{"city": "ELSBERRY", "loc": [-90.815956, 39.158953], "pop": 3739, "state": "MO", "_id": "63343"} -{"city": "EOLIA", "loc": [-91.009322, 39.243097], "pop": 533, "state": "MO", "_id": "63344"} -{"city": "FARBER", "loc": [-91.579843, 39.27424], "pop": 546, "state": "MO", "_id": "63345"} -{"city": "FOLEY", "loc": [-90.777468, 39.053019], "pop": 2518, "state": "MO", "_id": "63347"} -{"city": "FORISTELL", "loc": [-90.934328, 38.762557], "pop": 2993, "state": "MO", "_id": "63348"} -{"city": "HAWK POINT", "loc": [-91.121067, 38.976576], "pop": 1550, "state": "MO", "_id": "63349"} -{"city": "HIGH HILL", "loc": [-91.371473, 38.890238], "pop": 614, "state": "MO", "_id": "63350"} -{"city": "JONESBURG", "loc": [-91.30193, 38.871658], "pop": 1055, "state": "MO", "_id": "63351"} -{"city": "LADDONIA", "loc": [-91.689348, 39.259328], "pop": 1023, "state": "MO", "_id": "63352"} -{"city": "LOUISIANA", "loc": [-91.066385, 39.43359], "pop": 5428, "state": "MO", "_id": "63353"} -{"city": "LAKE SHERWOOD", "loc": [-91.055459, 38.648619], "pop": 3690, "state": "MO", "_id": "63357"} -{"city": "MIDDLETOWN", "loc": [-91.387312, 39.105494], "pop": 853, "state": "MO", "_id": "63359"} -{"city": "MONTGOMERY CITY", "loc": [-91.508486, 38.983949], "pop": 3160, "state": "MO", "_id": "63361"} -{"city": "MOSCOW MILLS", "loc": [-90.963001, 38.922927], "pop": 3538, "state": "MO", "_id": "63362"} -{"city": "NEW FLORENCE", "loc": [-91.490861, 38.902309], "pop": 1745, "state": "MO", "_id": "63363"} -{"city": "NEW HARTFORD", "loc": [-91.292984, 39.188103], "pop": 503, "state": "MO", "_id": "63364"} -{"city": "SAINT PAUL", "loc": [-90.720159, 38.800101], "pop": 28243, "state": "MO", "_id": "63366"} -{"city": "LAKE SAINT LOUIS", "loc": [-90.785407, 38.79355], "pop": 7785, "state": "MO", "_id": "63367"} -{"city": "OLD MONROE", "loc": [-90.778196, 38.934581], "pop": 1591, "state": "MO", "_id": "63369"} -{"city": "OLNEY", "loc": [-91.211314, 39.087509], "pop": 351, "state": "MO", "_id": "63370"} -{"city": "PAYNESVILLE", "loc": [-90.907268, 39.258679], "pop": 182, "state": "MO", "_id": "63371"} -{"city": "PORTAGE DES SIOU", "loc": [-90.353115, 38.927629], "pop": 777, "state": "MO", "_id": "63373"} -{"city": "SAINT PETERS", "loc": [-90.622765, 38.78024], "pop": 43133, "state": "MO", "_id": "63376"} -{"city": "SILEX", "loc": [-91.036988, 39.116617], "pop": 2913, "state": "MO", "_id": "63377"} -{"city": "TROY", "loc": [-90.962449, 39.001212], "pop": 7636, "state": "MO", "_id": "63379"} -{"city": "TRUXTON", "loc": [-91.212618, 38.967002], "pop": 794, "state": "MO", "_id": "63381"} -{"city": "VANDALIA", "loc": [-91.488299, 39.294937], "pop": 3441, "state": "MO", "_id": "63382"} -{"city": "WARRENTON", "loc": [-91.174047, 38.805042], "pop": 9464, "state": "MO", "_id": "63383"} -{"city": "WELLSVILLE", "loc": [-91.564519, 39.076482], "pop": 1899, "state": "MO", "_id": "63384"} -{"city": "WENTZVILLE", "loc": [-90.85344, 38.801963], "pop": 10238, "state": "MO", "_id": "63385"} -{"city": "WEST ALTON", "loc": [-90.238363, 38.875796], "pop": 1172, "state": "MO", "_id": "63386"} -{"city": "WILLIAMSBURG", "loc": [-91.679188, 38.898143], "pop": 228, "state": "MO", "_id": "63388"} -{"city": "WINFIELD", "loc": [-90.821319, 38.989727], "pop": 4791, "state": "MO", "_id": "63389"} -{"city": "WRIGHT CITY", "loc": [-91.03293, 38.809677], "pop": 5852, "state": "MO", "_id": "63390"} -{"city": "HANNIBAL", "loc": [-91.38387, 39.70636], "pop": 20086, "state": "MO", "_id": "63401"} -{"city": "ALEXANDRIA", "loc": [-91.515437, 40.344514], "pop": 753, "state": "MO", "_id": "63430"} -{"city": "ANABEL", "loc": [-92.351142, 39.736793], "pop": 289, "state": "MO", "_id": "63431"} -{"city": "ARBELA", "loc": [-92.004741, 40.486299], "pop": 564, "state": "MO", "_id": "63432"} -{"city": "ASHBURN", "loc": [-91.187651, 39.52904], "pop": 235, "state": "MO", "_id": "63433"} -{"city": "BETHEL", "loc": [-92.031619, 39.892199], "pop": 377, "state": "MO", "_id": "63434"} -{"city": "CANTON", "loc": [-91.547987, 40.143695], "pop": 3822, "state": "MO", "_id": "63435"} -{"city": "CENTER", "loc": [-91.539842, 39.515397], "pop": 971, "state": "MO", "_id": "63436"} -{"city": "CLARENCE", "loc": [-92.25297, 39.736648], "pop": 1600, "state": "MO", "_id": "63437"} -{"city": "DURHAM", "loc": [-91.670415, 39.962486], "pop": 548, "state": "MO", "_id": "63438"} -{"city": "EMDEN", "loc": [-91.880926, 39.85013], "pop": 364, "state": "MO", "_id": "63439"} -{"city": "EWING", "loc": [-91.721627, 39.996237], "pop": 1142, "state": "MO", "_id": "63440"} -{"city": "FRANKFORD", "loc": [-91.303086, 39.489246], "pop": 831, "state": "MO", "_id": "63441"} -{"city": "HUNNEWELL", "loc": [-91.883231, 39.701831], "pop": 528, "state": "MO", "_id": "63443"} -{"city": "KAHOKA", "loc": [-91.725033, 40.426576], "pop": 3922, "state": "MO", "_id": "63445"} -{"city": "KNOX CITY", "loc": [-92.007703, 40.138357], "pop": 547, "state": "MO", "_id": "63446"} -{"city": "LA BELLE", "loc": [-91.917125, 40.116383], "pop": 893, "state": "MO", "_id": "63447"} -{"city": "LA GRANGE", "loc": [-91.518247, 40.039126], "pop": 1715, "state": "MO", "_id": "63448"} -{"city": "LENTNER", "loc": [-92.148916, 39.712269], "pop": 198, "state": "MO", "_id": "63450"} -{"city": "LEONARD", "loc": [-92.194679, 39.907577], "pop": 312, "state": "MO", "_id": "63451"} -{"city": "LEWISTOWN", "loc": [-91.815663, 40.086666], "pop": 824, "state": "MO", "_id": "63452"} -{"city": "LURAY", "loc": [-91.891227, 40.493096], "pop": 523, "state": "MO", "_id": "63453"} -{"city": "MAYWOOD", "loc": [-91.614769, 39.933289], "pop": 939, "state": "MO", "_id": "63454"} -{"city": "MONROE CITY", "loc": [-91.722999, 39.654586], "pop": 4039, "state": "MO", "_id": "63456"} -{"city": "MONTICELLO", "loc": [-91.712267, 40.10402], "pop": 515, "state": "MO", "_id": "63457"} -{"city": "NEWARK", "loc": [-91.99236, 39.997428], "pop": 195, "state": "MO", "_id": "63458"} -{"city": "NEW LONDON", "loc": [-91.39599, 39.591672], "pop": 4801, "state": "MO", "_id": "63459"} -{"city": "NOVELTY", "loc": [-92.243783, 40.035997], "pop": 542, "state": "MO", "_id": "63460"} -{"city": "PALMYRA", "loc": [-91.536817, 39.791309], "pop": 5344, "state": "MO", "_id": "63461"} -{"city": "PERRY", "loc": [-91.664125, 39.420721], "pop": 1268, "state": "MO", "_id": "63462"} -{"city": "PHILADELPHIA", "loc": [-91.753803, 39.835931], "pop": 674, "state": "MO", "_id": "63463"} -{"city": "PLEVNA", "loc": [-92.111716, 39.992561], "pop": 149, "state": "MO", "_id": "63464"} -{"city": "SAINT PATRICK", "loc": [-91.654677, 40.319851], "pop": 455, "state": "MO", "_id": "63466"} -{"city": "SHELBINA", "loc": [-92.03706, 39.694718], "pop": 2714, "state": "MO", "_id": "63468"} -{"city": "SHELBYVILLE", "loc": [-92.049782, 39.810792], "pop": 929, "state": "MO", "_id": "63469"} -{"city": "STEFFENVILLE", "loc": [-91.85945, 39.993976], "pop": 310, "state": "MO", "_id": "63470"} -{"city": "TAYLOR", "loc": [-91.527834, 39.914486], "pop": 667, "state": "MO", "_id": "63471"} -{"city": "WAYLAND", "loc": [-91.607864, 40.442574], "pop": 1252, "state": "MO", "_id": "63472"} -{"city": "WILLIAMSTOWN", "loc": [-91.854809, 40.21559], "pop": 343, "state": "MO", "_id": "63473"} -{"city": "WYACONDA", "loc": [-91.907077, 40.372083], "pop": 642, "state": "MO", "_id": "63474"} -{"city": "KIRKSVILLE", "loc": [-92.585634, 40.190765], "pop": 20717, "state": "MO", "_id": "63501"} -{"city": "ATLANTA", "loc": [-92.475019, 39.914639], "pop": 977, "state": "MO", "_id": "63530"} -{"city": "BARING", "loc": [-92.231074, 40.250864], "pop": 440, "state": "MO", "_id": "63531"} -{"city": "BEVIER", "loc": [-92.561925, 39.749707], "pop": 1273, "state": "MO", "_id": "63532"} -{"city": "BRASHEAR", "loc": [-92.433253, 40.195891], "pop": 1444, "state": "MO", "_id": "63533"} -{"city": "CALLAO", "loc": [-92.635144, 39.744849], "pop": 878, "state": "MO", "_id": "63534"} -{"city": "COATSVILLE", "loc": [-92.638801, 40.566473], "pop": 148, "state": "MO", "_id": "63535"} -{"city": "DOWNING", "loc": [-92.391762, 40.479828], "pop": 953, "state": "MO", "_id": "63536"} -{"city": "EDINA", "loc": [-92.145549, 40.179452], "pop": 2101, "state": "MO", "_id": "63537"} -{"city": "ELMER", "loc": [-92.642413, 39.941456], "pop": 308, "state": "MO", "_id": "63538"} -{"city": "ETHEL", "loc": [-92.766396, 39.914605], "pop": 374, "state": "MO", "_id": "63539"} -{"city": "GIBBS", "loc": [-92.443495, 40.083962], "pop": 389, "state": "MO", "_id": "63540"} -{"city": "GLENWOOD", "loc": [-92.588642, 40.514935], "pop": 333, "state": "MO", "_id": "63541"} -{"city": "GORIN", "loc": [-92.01397, 40.362217], "pop": 428, "state": "MO", "_id": "63543"} -{"city": "GREEN CASTLE", "loc": [-92.877966, 40.270325], "pop": 335, "state": "MO", "_id": "63544"} -{"city": "GREEN CITY", "loc": [-92.953244, 40.260125], "pop": 1189, "state": "MO", "_id": "63545"} -{"city": "GREENTOP", "loc": [-92.55667, 40.344593], "pop": 859, "state": "MO", "_id": "63546"} -{"city": "HURDLAND", "loc": [-92.279066, 40.160909], "pop": 508, "state": "MO", "_id": "63547"} -{"city": "LANCASTER", "loc": [-92.526403, 40.525265], "pop": 1060, "state": "MO", "_id": "63548"} -{"city": "LA PLATA", "loc": [-92.507669, 40.020831], "pop": 2022, "state": "MO", "_id": "63549"} -{"city": "LIVONIA", "loc": [-92.724084, 40.511237], "pop": 398, "state": "MO", "_id": "63551"} -{"city": "MACON", "loc": [-92.462163, 39.748089], "pop": 7557, "state": "MO", "_id": "63552"} -{"city": "MEMPHIS", "loc": [-92.185083, 40.46191], "pop": 3546, "state": "MO", "_id": "63555"} -{"city": "MILAN", "loc": [-93.136149, 40.184435], "pop": 3171, "state": "MO", "_id": "63556"} -{"city": "NEW BOSTON", "loc": [-92.915967, 39.933234], "pop": 184, "state": "MO", "_id": "63557"} -{"city": "NEW CAMBRIA", "loc": [-92.769469, 39.75509], "pop": 953, "state": "MO", "_id": "63558"} -{"city": "NOVINGER", "loc": [-92.717236, 40.268513], "pop": 1770, "state": "MO", "_id": "63559"} -{"city": "POLLOCK", "loc": [-93.111506, 40.33852], "pop": 638, "state": "MO", "_id": "63560"} -{"city": "QUEEN CITY", "loc": [-92.566263, 40.415175], "pop": 1269, "state": "MO", "_id": "63561"} -{"city": "RUTLEDGE", "loc": [-92.097559, 40.328984], "pop": 284, "state": "MO", "_id": "63563"} -{"city": "UNIONVILLE", "loc": [-92.995094, 40.481464], "pop": 3515, "state": "MO", "_id": "63565"} -{"city": "WINIGAN", "loc": [-92.93007, 40.03706], "pop": 335, "state": "MO", "_id": "63566"} -{"city": "WORTHINGTON", "loc": [-92.740139, 40.424001], "pop": 438, "state": "MO", "_id": "63567"} -{"city": "DESLOGE", "loc": [-90.52735, 37.85555], "pop": 14716, "state": "MO", "_id": "63601"} -{"city": "ANNAPOLIS", "loc": [-90.670235, 37.39809], "pop": 1282, "state": "MO", "_id": "63620"} -{"city": "ARCADIA", "loc": [-90.592768, 37.570054], "pop": 896, "state": "MO", "_id": "63621"} -{"city": "BELGRADE", "loc": [-90.861308, 37.78886], "pop": 1000, "state": "MO", "_id": "63622"} -{"city": "BELLEVIEW", "loc": [-90.799075, 37.681981], "pop": 1206, "state": "MO", "_id": "63623"} -{"city": "DESLOGE", "loc": [-90.620732, 37.769865], "pop": 2282, "state": "MO", "_id": "63624"} -{"city": "BLACK", "loc": [-90.991735, 37.547275], "pop": 671, "state": "MO", "_id": "63625"} -{"city": "BLACKWELL", "loc": [-90.709538, 38.071694], "pop": 1012, "state": "MO", "_id": "63626"} -{"city": "BLOOMSDALE", "loc": [-90.280548, 38.045112], "pop": 3035, "state": "MO", "_id": "63627"} -{"city": "BONNE TERRE", "loc": [-90.525052, 37.931423], "pop": 10579, "state": "MO", "_id": "63628"} -{"city": "BUNKER", "loc": [-91.192735, 37.477227], "pop": 882, "state": "MO", "_id": "63629"} -{"city": "CADET", "loc": [-90.743932, 38.012534], "pop": 3277, "state": "MO", "_id": "63630"} -{"city": "CALEDONIA", "loc": [-90.740936, 37.763881], "pop": 808, "state": "MO", "_id": "63631"} -{"city": "CENTERVILLE", "loc": [-90.975657, 37.428537], "pop": 505, "state": "MO", "_id": "63633"} -{"city": "DES ARC", "loc": [-90.62787, 37.29546], "pop": 640, "state": "MO", "_id": "63636"} -{"city": "DOE RUN", "loc": [-90.496842, 37.734818], "pop": 1866, "state": "MO", "_id": "63637"} -{"city": "ELLINGTON", "loc": [-90.958851, 37.239797], "pop": 3329, "state": "MO", "_id": "63638"} -{"city": "FARMINGTON", "loc": [-90.409377, 37.777299], "pop": 19031, "state": "MO", "_id": "63640"} -{"city": "MILLCREEK", "loc": [-90.310527, 37.543716], "pop": 10043, "state": "MO", "_id": "63645"} -{"city": "IRONDALE", "loc": [-90.698389, 37.82962], "pop": 1793, "state": "MO", "_id": "63648"} -{"city": "IRON MOUNTAIN", "loc": [-90.635635, 37.614702], "pop": 5610, "state": "MO", "_id": "63650"} -{"city": "LEADWOOD", "loc": [-90.591007, 37.864081], "pop": 1331, "state": "MO", "_id": "63653"} -{"city": "LESTERVILLE", "loc": [-90.842544, 37.482004], "pop": 733, "state": "MO", "_id": "63654"} -{"city": "MARQUAND", "loc": [-90.174092, 37.427403], "pop": 1296, "state": "MO", "_id": "63655"} -{"city": "MIDDLE BROOK", "loc": [-90.673504, 37.670161], "pop": 509, "state": "MO", "_id": "63656"} -{"city": "MINERAL POINT", "loc": [-90.719324, 37.915555], "pop": 4032, "state": "MO", "_id": "63660"} -{"city": "PATTON", "loc": [-90.050045, 37.473347], "pop": 1622, "state": "MO", "_id": "63662"} -{"city": "POTOSI", "loc": [-90.841467, 37.954942], "pop": 7274, "state": "MO", "_id": "63664"} -{"city": "REDFORD", "loc": [-90.921994, 37.323518], "pop": 407, "state": "MO", "_id": "63665"} -{"city": "LAKE FOREST ESTA", "loc": [-90.09604, 37.950356], "pop": 9405, "state": "MO", "_id": "63670"} -{"city": "SAINT MARY", "loc": [-89.976989, 37.832481], "pop": 2730, "state": "MO", "_id": "63673"} -{"city": "VULCAN", "loc": [-90.710608, 37.305963], "pop": 153, "state": "MO", "_id": "63675"} -{"city": "CAPE GIRARDEAU", "loc": [-89.545861, 37.31685], "pop": 37993, "state": "MO", "_id": "63701"} -{"city": "ADVANCE", "loc": [-89.910614, 37.092219], "pop": 2448, "state": "MO", "_id": "63730"} -{"city": "NEW WELLS", "loc": [-89.581453, 37.630633], "pop": 576, "state": "MO", "_id": "63732"} -{"city": "ARAB", "loc": [-90.080448, 37.064804], "pop": 139, "state": "MO", "_id": "63733"} -{"city": "BELL CITY", "loc": [-89.798417, 37.011608], "pop": 1219, "state": "MO", "_id": "63735"} -{"city": "BENTON", "loc": [-89.566404, 37.069674], "pop": 3349, "state": "MO", "_id": "63736"} -{"city": "BURFORDVILLE", "loc": [-89.820632, 37.363231], "pop": 819, "state": "MO", "_id": "63739"} -{"city": "CHAFFEE", "loc": [-89.64565, 37.172612], "pop": 4830, "state": "MO", "_id": "63740"} -{"city": "DAISY", "loc": [-89.821327, 37.515064], "pop": 100, "state": "MO", "_id": "63743"} -{"city": "DELTA", "loc": [-89.76086, 37.184417], "pop": 1418, "state": "MO", "_id": "63744"} -{"city": "FRIEDHEIM", "loc": [-89.837552, 37.566904], "pop": 133, "state": "MO", "_id": "63747"} -{"city": "FROHNA", "loc": [-89.661903, 37.66855], "pop": 1012, "state": "MO", "_id": "63748"} -{"city": "GIPSY", "loc": [-90.194077, 37.131092], "pop": 92, "state": "MO", "_id": "63750"} -{"city": "GLENALLEN", "loc": [-90.051495, 37.323119], "pop": 572, "state": "MO", "_id": "63751"} -{"city": "GRASSY", "loc": [-90.160838, 37.259347], "pop": 375, "state": "MO", "_id": "63753"} -{"city": "JACKSON", "loc": [-89.651939, 37.387885], "pop": 14335, "state": "MO", "_id": "63755"} -{"city": "LEOPOLD", "loc": [-89.922735, 37.260903], "pop": 303, "state": "MO", "_id": "63760"} -{"city": "MC GEE", "loc": [-90.190847, 37.044139], "pop": 308, "state": "MO", "_id": "63763"} -{"city": "SCOPUS", "loc": [-89.962732, 37.313464], "pop": 5211, "state": "MO", "_id": "63764"} -{"city": "MILLERSVILLE", "loc": [-89.795035, 37.440138], "pop": 1060, "state": "MO", "_id": "63766"} -{"city": "63768", "loc": [-89.612833, 37.488177], "pop": 2485, "state": "MO", "_id": "63768"} -{"city": "OAK RIDGE", "loc": [-89.750818, 37.525787], "pop": 1184, "state": "MO", "_id": "63769"} -{"city": "OLD APPLETON", "loc": [-89.707091, 37.583014], "pop": 200, "state": "MO", "_id": "63770"} -{"city": "ORAN", "loc": [-89.673364, 37.086977], "pop": 1827, "state": "MO", "_id": "63771"} -{"city": "PERRYVILLE", "loc": [-89.873664, 37.717437], "pop": 13296, "state": "MO", "_id": "63775"} -{"city": "SCOTT CITY", "loc": [-89.518081, 37.20771], "pop": 6340, "state": "MO", "_id": "63780"} -{"city": "SEDGEWICKVILLE", "loc": [-89.927246, 37.536962], "pop": 772, "state": "MO", "_id": "63781"} -{"city": "STURDIVANT", "loc": [-90.009215, 37.071011], "pop": 220, "state": "MO", "_id": "63782"} -{"city": "UNIONTOWN", "loc": [-89.723744, 37.610708], "pop": 440, "state": "MO", "_id": "63783"} -{"city": "WHITEWATER", "loc": [-89.736338, 37.286495], "pop": 1906, "state": "MO", "_id": "63785"} -{"city": "WITTENBERG", "loc": [-89.564261, 37.679256], "pop": 140, "state": "MO", "_id": "63786"} -{"city": "ZALMA", "loc": [-90.075711, 37.13648], "pop": 898, "state": "MO", "_id": "63787"} -{"city": "SIKESTON", "loc": [-89.58197, 36.891111], "pop": 22618, "state": "MO", "_id": "63801"} -{"city": "ARBYRD", "loc": [-90.228355, 36.050104], "pop": 891, "state": "MO", "_id": "63821"} -{"city": "BERNIE", "loc": [-89.987758, 36.672703], "pop": 3200, "state": "MO", "_id": "63822"} -{"city": "BERTRAND", "loc": [-89.448259, 36.892746], "pop": 1455, "state": "MO", "_id": "63823"} -{"city": "BLOOMFIELD", "loc": [-89.945564, 36.898892], "pop": 5938, "state": "MO", "_id": "63825"} -{"city": "BRAGG CITY", "loc": [-89.873624, 36.273236], "pop": 711, "state": "MO", "_id": "63827"} -{"city": "CARDWELL", "loc": [-90.290696, 36.04345], "pop": 1552, "state": "MO", "_id": "63829"} -{"city": "CARUTHERSVILLE", "loc": [-89.668306, 36.180237], "pop": 8376, "state": "MO", "_id": "63830"} -{"city": "CATRON", "loc": [-89.770647, 36.687578], "pop": 174, "state": "MO", "_id": "63833"} -{"city": "CHARLESTON", "loc": [-89.334207, 36.921341], "pop": 7209, "state": "MO", "_id": "63834"} -{"city": "CLARKTON", "loc": [-89.972852, 36.447812], "pop": 1672, "state": "MO", "_id": "63837"} -{"city": "DEXTER", "loc": [-89.963933, 36.788458], "pop": 11641, "state": "MO", "_id": "63841"} -{"city": "EAST PRAIRIE", "loc": [-89.372629, 36.777609], "pop": 6184, "state": "MO", "_id": "63845"} -{"city": "ESSEX", "loc": [-89.836649, 36.810859], "pop": 1212, "state": "MO", "_id": "63846"} -{"city": "GIDEON", "loc": [-89.913546, 36.45378], "pop": 1599, "state": "MO", "_id": "63848"} -{"city": "GOBLER", "loc": [-89.934907, 36.158987], "pop": 144, "state": "MO", "_id": "63849"} -{"city": "HAYTI HEIGHTS", "loc": [-89.751607, 36.239475], "pop": 5073, "state": "MO", "_id": "63851"} -{"city": "HOLCOMB", "loc": [-90.020795, 36.38846], "pop": 1335, "state": "MO", "_id": "63852"} -{"city": "HORNERSVILLE", "loc": [-90.081643, 36.062694], "pop": 1685, "state": "MO", "_id": "63855"} -{"city": "KENNETT", "loc": [-90.049057, 36.240656], "pop": 12742, "state": "MO", "_id": "63857"} -{"city": "LILBOURN", "loc": [-89.611241, 36.585313], "pop": 2258, "state": "MO", "_id": "63862"} -{"city": "MALDEN", "loc": [-89.973679, 36.567209], "pop": 6735, "state": "MO", "_id": "63863"} -{"city": "MARSTON", "loc": [-89.628819, 36.508448], "pop": 1160, "state": "MO", "_id": "63866"} -{"city": "MATTHEWS", "loc": [-89.576833, 36.807528], "pop": 2675, "state": "MO", "_id": "63867"} -{"city": "MOREHOUSE", "loc": [-89.685191, 36.819041], "pop": 1856, "state": "MO", "_id": "63868"} -{"city": "NEW MADRID", "loc": [-89.536645, 36.607284], "pop": 4289, "state": "MO", "_id": "63869"} -{"city": "PARMA", "loc": [-89.818971, 36.585629], "pop": 2308, "state": "MO", "_id": "63870"} -{"city": "PORTAGEVILLE", "loc": [-89.700234, 36.427945], "pop": 5400, "state": "MO", "_id": "63873"} -{"city": "SENATH", "loc": [-90.163224, 36.132428], "pop": 2234, "state": "MO", "_id": "63876"} -{"city": "STEELE", "loc": [-89.834585, 36.091528], "pop": 5905, "state": "MO", "_id": "63877"} -{"city": "HOMESTOWN", "loc": [-89.816362, 36.347215], "pop": 1003, "state": "MO", "_id": "63879"} -{"city": "POPLAR BLUFF", "loc": [-90.416647, 36.766235], "pop": 31363, "state": "MO", "_id": "63901"} -{"city": "BRIAR", "loc": [-90.92777, 36.639115], "pop": 617, "state": "MO", "_id": "63931"} -{"city": "BROSELEY", "loc": [-90.240472, 36.707553], "pop": 2173, "state": "MO", "_id": "63932"} -{"city": "CAMPBELL", "loc": [-90.082859, 36.519714], "pop": 4358, "state": "MO", "_id": "63933"} -{"city": "CLUBB", "loc": [-90.359638, 37.2302], "pop": 88, "state": "MO", "_id": "63934"} -{"city": "POYNOR", "loc": [-90.81953, 36.621094], "pop": 7690, "state": "MO", "_id": "63935"} -{"city": "DUDLEY", "loc": [-90.120974, 36.810981], "pop": 1061, "state": "MO", "_id": "63936"} -{"city": "ELLSINORE", "loc": [-90.74853, 36.945263], "pop": 1573, "state": "MO", "_id": "63937"} -{"city": "FAIRDEALING", "loc": [-90.633515, 36.670404], "pop": 617, "state": "MO", "_id": "63939"} -{"city": "FISK", "loc": [-90.216758, 36.783625], "pop": 590, "state": "MO", "_id": "63940"} -{"city": "FREMONT", "loc": [-91.143984, 36.917164], "pop": 570, "state": "MO", "_id": "63941"} -{"city": "GATEWOOD", "loc": [-91.070333, 36.562608], "pop": 342, "state": "MO", "_id": "63942"} -{"city": "GRANDIN", "loc": [-90.794175, 36.827961], "pop": 2228, "state": "MO", "_id": "63943"} -{"city": "GREENVILLE", "loc": [-90.451362, 37.110838], "pop": 1012, "state": "MO", "_id": "63944"} -{"city": "HARVIELL", "loc": [-90.558304, 36.672318], "pop": 726, "state": "MO", "_id": "63945"} -{"city": "HIRAM", "loc": [-90.289556, 37.228158], "pop": 467, "state": "MO", "_id": "63947"} -{"city": "LODI", "loc": [-90.463225, 37.242046], "pop": 52, "state": "MO", "_id": "63950"} -{"city": "LOWNDES", "loc": [-90.254437, 37.134318], "pop": 101, "state": "MO", "_id": "63951"} -{"city": "MILL SPRING", "loc": [-90.674625, 37.067477], "pop": 732, "state": "MO", "_id": "63952"} -{"city": "NAYLOR", "loc": [-90.612354, 36.584337], "pop": 1390, "state": "MO", "_id": "63953"} -{"city": "NEELYVILLE", "loc": [-90.499491, 36.571005], "pop": 1556, "state": "MO", "_id": "63954"} -{"city": "OXLY", "loc": [-90.691973, 36.587385], "pop": 596, "state": "MO", "_id": "63955"} -{"city": "PATTERSON", "loc": [-90.577171, 37.195992], "pop": 1378, "state": "MO", "_id": "63956"} -{"city": "PIEDMONT", "loc": [-90.69902, 37.15727], "pop": 3719, "state": "MO", "_id": "63957"} -{"city": "63959", "loc": [-90.901556, 36.528175], "pop": 258, "state": "MO", "_id": "63959"} -{"city": "PUXICO", "loc": [-90.162258, 36.942163], "pop": 2085, "state": "MO", "_id": "63960"} -{"city": "QULIN", "loc": [-90.261674, 36.581769], "pop": 1646, "state": "MO", "_id": "63961"} -{"city": "SHOOK", "loc": [-90.30059, 37.076976], "pop": 222, "state": "MO", "_id": "63963"} -{"city": "SILVA", "loc": [-90.437472, 37.211892], "pop": 933, "state": "MO", "_id": "63964"} -{"city": "VAN BUREN", "loc": [-91.000681, 37.00153], "pop": 2417, "state": "MO", "_id": "63965"} -{"city": "WAPPAPELLO", "loc": [-90.284805, 36.976566], "pop": 1353, "state": "MO", "_id": "63966"} -{"city": "WILLIAMSVILLE", "loc": [-90.487851, 36.963789], "pop": 1657, "state": "MO", "_id": "63967"} -{"city": "ALMA", "loc": [-93.542853, 39.10484], "pop": 718, "state": "MO", "_id": "64001"} -{"city": "BATES CITY", "loc": [-94.07984, 39.021887], "pop": 1393, "state": "MO", "_id": "64011"} -{"city": "BELTON", "loc": [-94.532785, 38.816118], "pop": 20208, "state": "MO", "_id": "64012"} -{"city": "BLUE SPRINGS", "loc": [-94.260429, 39.015186], "pop": 19140, "state": "MO", "_id": "64014"} -{"city": "LAKE TAPAWINGO", "loc": [-94.292848, 39.018378], "pop": 23758, "state": "MO", "_id": "64015"} -{"city": "BUCKNER", "loc": [-94.206219, 39.130328], "pop": 4156, "state": "MO", "_id": "64016"} -{"city": "CAMDEN", "loc": [-94.025913, 39.204806], "pop": 641, "state": "MO", "_id": "64017"} -{"city": "CAMDEN POINT", "loc": [-94.744438, 39.451549], "pop": 678, "state": "MO", "_id": "64018"} -{"city": "CENTERVIEW", "loc": [-93.870234, 38.78971], "pop": 1890, "state": "MO", "_id": "64019"} -{"city": "CONCORDIA", "loc": [-93.581205, 38.977553], "pop": 3240, "state": "MO", "_id": "64020"} -{"city": "CORDER", "loc": [-93.639127, 39.102401], "pop": 649, "state": "MO", "_id": "64021"} -{"city": "DOVER", "loc": [-93.668377, 39.192619], "pop": 512, "state": "MO", "_id": "64022"} -{"city": "EXCELSIOR SPRING", "loc": [-94.223614, 39.33491], "pop": 18308, "state": "MO", "_id": "64024"} -{"city": "GRAIN VALLEY", "loc": [-94.20874, 39.027361], "pop": 2865, "state": "MO", "_id": "64029"} -{"city": "GRANDVIEW", "loc": [-94.520542, 38.881936], "pop": 24926, "state": "MO", "_id": "64030"} -{"city": "LAKE WINNEBAGO", "loc": [-94.334767, 38.842813], "pop": 5578, "state": "MO", "_id": "64034"} -{"city": "HARDIN", "loc": [-93.840869, 39.350433], "pop": 2019, "state": "MO", "_id": "64035"} -{"city": "HENRIETTA", "loc": [-93.936873, 39.236732], "pop": 438, "state": "MO", "_id": "64036"} -{"city": "HIGGINSVILLE", "loc": [-93.713265, 39.070504], "pop": 5994, "state": "MO", "_id": "64037"} -{"city": "HOLDEN", "loc": [-93.985571, 38.718595], "pop": 3088, "state": "MO", "_id": "64040"} -{"city": "HOLT", "loc": [-94.368876, 39.428867], "pop": 1712, "state": "MO", "_id": "64048"} -{"city": "INDEPENDENCE", "loc": [-94.411072, 39.098288], "pop": 24188, "state": "MO", "_id": "64050"} -{"city": "INDEPENDENCE", "loc": [-94.449945, 39.074984], "pop": 22793, "state": "MO", "_id": "64052"} -{"city": "INDEPENDENCE", "loc": [-94.462461, 39.105041], "pop": 6152, "state": "MO", "_id": "64053"} -{"city": "SUGAR CREEK", "loc": [-94.441496, 39.107234], "pop": 4574, "state": "MO", "_id": "64054"} -{"city": "INDEPENDENCE", "loc": [-94.403902, 39.054504], "pop": 30654, "state": "MO", "_id": "64055"} -{"city": "INDEPENDENCE", "loc": [-94.359637, 39.11773], "pop": 14962, "state": "MO", "_id": "64056"} -{"city": "INDEPENDENCE", "loc": [-94.353284, 39.073099], "pop": 9209, "state": "MO", "_id": "64057"} -{"city": "INDEPENDENCE", "loc": [-94.351526, 39.141233], "pop": 5752, "state": "MO", "_id": "64058"} -{"city": "KEARNEY", "loc": [-94.362104, 39.365175], "pop": 4419, "state": "MO", "_id": "64060"} -{"city": "KINGSVILLE", "loc": [-94.046188, 38.817755], "pop": 3447, "state": "MO", "_id": "64061"} -{"city": "LAWSON", "loc": [-94.196588, 39.440129], "pop": 4898, "state": "MO", "_id": "64062"} -{"city": "LAKE LOTAWANA", "loc": [-94.348744, 38.921094], "pop": 30114, "state": "MO", "_id": "64063"} -{"city": "LEES SUMMIT", "loc": [-94.365192, 38.995336], "pop": 10649, "state": "MO", "_id": "64064"} -{"city": "LEXINGTON", "loc": [-93.871438, 39.174249], "pop": 6294, "state": "MO", "_id": "64067"} -{"city": "PLEASANT VALLEY", "loc": [-94.433664, 39.241916], "pop": 25473, "state": "MO", "_id": "64068"} -{"city": "LONE JACK", "loc": [-94.161453, 38.891837], "pop": 1918, "state": "MO", "_id": "64070"} -{"city": "MAYVIEW", "loc": [-93.835306, 39.045898], "pop": 746, "state": "MO", "_id": "64071"} -{"city": "NAPOLEON", "loc": [-94.070911, 39.114034], "pop": 545, "state": "MO", "_id": "64074"} -{"city": "OAK GROVE", "loc": [-94.139946, 38.998456], "pop": 7851, "state": "MO", "_id": "64075"} -{"city": "ODESSA", "loc": [-93.975731, 38.982938], "pop": 8790, "state": "MO", "_id": "64076"} -{"city": "ORRICK", "loc": [-94.123863, 39.211577], "pop": 1186, "state": "MO", "_id": "64077"} -{"city": "PECULIAR", "loc": [-94.44053, 38.716453], "pop": 5207, "state": "MO", "_id": "64078"} -{"city": "PLATTE CITY", "loc": [-94.788972, 39.360171], "pop": 4280, "state": "MO", "_id": "64079"} -{"city": "PLEASANT HILL", "loc": [-94.243961, 38.785856], "pop": 6057, "state": "MO", "_id": "64080"} -{"city": "LEES SUMMIT", "loc": [-94.407302, 38.914169], "pop": 9637, "state": "MO", "_id": "64081"} -{"city": "LEES SUMMIT", "loc": [-94.394368, 38.851803], "pop": 2580, "state": "MO", "_id": "64082"} -{"city": "RAYMORE", "loc": [-94.452893, 38.801896], "pop": 8112, "state": "MO", "_id": "64083"} -{"city": "RAYVILLE", "loc": [-94.02841, 39.385291], "pop": 1727, "state": "MO", "_id": "64084"} -{"city": "RICHMOND", "loc": [-93.979163, 39.279298], "pop": 7080, "state": "MO", "_id": "64085"} -{"city": "SIBLEY", "loc": [-94.199019, 39.162658], "pop": 1527, "state": "MO", "_id": "64088"} -{"city": "SMITHVILLE", "loc": [-94.559239, 39.391739], "pop": 3469, "state": "MO", "_id": "64089"} -{"city": "WARRENSBURG", "loc": [-93.727341, 38.766695], "pop": 21993, "state": "MO", "_id": "64093"} -{"city": "WAVERLY", "loc": [-93.525652, 39.205523], "pop": 1036, "state": "MO", "_id": "64096"} -{"city": "WELLINGTON", "loc": [-93.985513, 39.125845], "pop": 1167, "state": "MO", "_id": "64097"} -{"city": "WESTON", "loc": [-94.914453, 39.445294], "pop": 3269, "state": "MO", "_id": "64098"} -{"city": "KANSAS CITY", "loc": [-94.601849, 39.10005], "pop": 0, "state": "MO", "_id": "64101"} -{"city": "KANSAS CITY", "loc": [-94.606596, 39.086067], "pop": 0, "state": "MO", "_id": "64102"} -{"city": "KANSAS CITY", "loc": [-94.590092, 39.102459], "pop": 1733, "state": "MO", "_id": "64105"} -{"city": "KANSAS CITY", "loc": [-94.569858, 39.105186], "pop": 7323, "state": "MO", "_id": "64106"} -{"city": "KANSAS CITY", "loc": [-94.586826, 39.0837], "pop": 7167, "state": "MO", "_id": "64108"} -{"city": "KANSAS CITY", "loc": [-94.567372, 39.066286], "pop": 13553, "state": "MO", "_id": "64109"} -{"city": "KANSAS CITY", "loc": [-94.572206, 39.036088], "pop": 19532, "state": "MO", "_id": "64110"} -{"city": "KANSAS CITY", "loc": [-94.592942, 39.056483], "pop": 19929, "state": "MO", "_id": "64111"} -{"city": "KANSAS CITY", "loc": [-94.592873, 39.038191], "pop": 8891, "state": "MO", "_id": "64112"} -{"city": "KANSAS CITY", "loc": [-94.593828, 39.01234], "pop": 11744, "state": "MO", "_id": "64113"} -{"city": "KANSAS CITY", "loc": [-94.595941, 38.962147], "pop": 24416, "state": "MO", "_id": "64114"} -{"city": "NORTH KANSAS CIT", "loc": [-94.569882, 39.163189], "pop": 14697, "state": "MO", "_id": "64116"} -{"city": "RANDOLPH", "loc": [-94.527367, 39.168111], "pop": 14295, "state": "MO", "_id": "64117"} -{"city": "GLADSTONE", "loc": [-94.570448, 39.213842], "pop": 35772, "state": "MO", "_id": "64118"} -{"city": "KANSAS CITY", "loc": [-94.519873, 39.19785], "pop": 24046, "state": "MO", "_id": "64119"} -{"city": "KANSAS CITY", "loc": [-94.54873, 39.122206], "pop": 731, "state": "MO", "_id": "64120"} -{"city": "KANSAS CITY", "loc": [-94.523545, 39.113593], "pop": 9910, "state": "MO", "_id": "64123"} -{"city": "KANSAS CITY", "loc": [-94.539402, 39.106832], "pop": 11683, "state": "MO", "_id": "64124"} -{"city": "KANSAS CITY", "loc": [-94.492328, 39.104157], "pop": 2011, "state": "MO", "_id": "64125"} -{"city": "KANSAS CITY", "loc": [-94.50466, 39.092255], "pop": 6929, "state": "MO", "_id": "64126"} -{"city": "KANSAS CITY", "loc": [-94.536636, 39.088303], "pop": 22469, "state": "MO", "_id": "64127"} -{"city": "KANSAS CITY", "loc": [-94.538634, 39.065932], "pop": 18185, "state": "MO", "_id": "64128"} -{"city": "KANSAS CITY", "loc": [-94.49513, 39.040093], "pop": 12006, "state": "MO", "_id": "64129"} -{"city": "KANSAS CITY", "loc": [-94.546674, 39.035106], "pop": 30403, "state": "MO", "_id": "64130"} -{"city": "KANSAS CITY", "loc": [-94.57741, 38.971303], "pop": 24033, "state": "MO", "_id": "64131"} -{"city": "KANSAS CITY", "loc": [-94.552156, 38.991073], "pop": 17552, "state": "MO", "_id": "64132"} -{"city": "RAYTOWN", "loc": [-94.459229, 39.014909], "pop": 33484, "state": "MO", "_id": "64133"} -{"city": "KANSAS CITY", "loc": [-94.500908, 38.929633], "pop": 23346, "state": "MO", "_id": "64134"} -{"city": "KANSAS CITY", "loc": [-94.400774, 39.018684], "pop": 1033, "state": "MO", "_id": "64136"} -{"city": "KANSAS CITY", "loc": [-94.540487, 38.92988], "pop": 11216, "state": "MO", "_id": "64137"} -{"city": "RAYTOWN", "loc": [-94.479361, 38.96871], "pop": 25590, "state": "MO", "_id": "64138"} -{"city": "KANSAS CITY", "loc": [-94.406086, 38.965891], "pop": 465, "state": "MO", "_id": "64139"} -{"city": "KANSAS CITY", "loc": [-94.597607, 38.89767], "pop": 4830, "state": "MO", "_id": "64145"} -{"city": "KANSAS CITY", "loc": [-94.57638, 38.897264], "pop": 1417, "state": "MO", "_id": "64146"} -{"city": "MARTIN CITY", "loc": [-94.529717, 38.861352], "pop": 600, "state": "MO", "_id": "64147"} -{"city": "KANSAS CITY", "loc": [-94.463554, 38.860646], "pop": 314, "state": "MO", "_id": "64149"} -{"city": "KANSAS CITY", "loc": [-94.616669, 39.177927], "pop": 2140, "state": "MO", "_id": "64150"} -{"city": "LAKE WAUKOMIS", "loc": [-94.63318, 39.213876], "pop": 18196, "state": "MO", "_id": "64151"} -{"city": "PARKVILLE", "loc": [-94.691313, 39.220954], "pop": 18719, "state": "MO", "_id": "64152"} -{"city": "KANSAS CITY", "loc": [-94.697008, 39.262746], "pop": 1582, "state": "MO", "_id": "64153"} -{"city": "KANSAS CITY", "loc": [-94.635444, 39.254728], "pop": 3379, "state": "MO", "_id": "64154"} -{"city": "KANSAS CITY", "loc": [-94.570401, 39.275831], "pop": 11562, "state": "MO", "_id": "64155"} -{"city": "KANSAS CITY", "loc": [-94.533614, 39.290052], "pop": 921, "state": "MO", "_id": "64156"} -{"city": "KANSAS CITY", "loc": [-94.459456, 39.276673], "pop": 1084, "state": "MO", "_id": "64157"} -{"city": "KANSAS CITY", "loc": [-94.472036, 39.228428], "pop": 158, "state": "MO", "_id": "64158"} -{"city": "RANDOLPH", "loc": [-94.459829, 39.161506], "pop": 470, "state": "MO", "_id": "64161"} -{"city": "FERRELVIEW", "loc": [-94.719315, 39.359756], "pop": 1479, "state": "MO", "_id": "64163"} -{"city": "KANSAS CITY", "loc": [-94.644643, 39.3426], "pop": 1459, "state": "MO", "_id": "64164"} -{"city": "KANSAS CITY", "loc": [-94.572966, 39.340054], "pop": 320, "state": "MO", "_id": "64165"} -{"city": "KANSAS CITY", "loc": [-94.519858, 39.329399], "pop": 466, "state": "MO", "_id": "64166"} -{"city": "KANSAS CITY", "loc": [-94.465291, 39.309643], "pop": 244, "state": "MO", "_id": "64167"} -{"city": "AGENCY", "loc": [-94.717017, 39.666229], "pop": 1773, "state": "MO", "_id": "64401"} -{"city": "ALBANY", "loc": [-94.326977, 40.251282], "pop": 2600, "state": "MO", "_id": "64402"} -{"city": "AMAZONIA", "loc": [-94.911344, 39.909197], "pop": 864, "state": "MO", "_id": "64421"} -{"city": "AMITY", "loc": [-94.513558, 39.883697], "pop": 651, "state": "MO", "_id": "64422"} -{"city": "BARNARD", "loc": [-94.805558, 40.186176], "pop": 561, "state": "MO", "_id": "64423"} -{"city": "BETHANY", "loc": [-94.018863, 40.260055], "pop": 4148, "state": "MO", "_id": "64424"} -{"city": "64425", "loc": [-95.328892, 40.095518], "pop": 269, "state": "MO", "_id": "64425"} -{"city": "BLYTHEDALE", "loc": [-93.895447, 40.502352], "pop": 335, "state": "MO", "_id": "64426"} -{"city": "BOLCKOW", "loc": [-94.884741, 40.100673], "pop": 684, "state": "MO", "_id": "64427"} -{"city": "BURLINGTON JUNCT", "loc": [-95.051663, 40.442125], "pop": 922, "state": "MO", "_id": "64428"} -{"city": "CAMERON", "loc": [-94.243723, 39.730892], "pop": 4660, "state": "MO", "_id": "64429"} -{"city": "CLARKSDALE", "loc": [-94.54212, 39.813675], "pop": 546, "state": "MO", "_id": "64430"} -{"city": "CLEARMONT", "loc": [-95.005478, 40.517489], "pop": 524, "state": "MO", "_id": "64431"} -{"city": "CLYDE", "loc": [-94.654563, 40.264475], "pop": 221, "state": "MO", "_id": "64432"} -{"city": "CONCEPTION", "loc": [-94.684302, 40.230929], "pop": 281, "state": "MO", "_id": "64433"} -{"city": "CONCEPTION JUNCT", "loc": [-94.696797, 40.266017], "pop": 334, "state": "MO", "_id": "64434"} -{"city": "CORNING", "loc": [-95.436128, 40.24463], "pop": 178, "state": "MO", "_id": "64435"} -{"city": "COSBY", "loc": [-94.697678, 39.855534], "pop": 727, "state": "MO", "_id": "64436"} -{"city": "BIGELOW", "loc": [-95.377047, 40.193873], "pop": 580, "state": "MO", "_id": "64437"} -{"city": "DARLINGTON", "loc": [-94.404899, 40.195517], "pop": 184, "state": "MO", "_id": "64438"} -{"city": "DEARBORN", "loc": [-94.766368, 39.517248], "pop": 1556, "state": "MO", "_id": "64439"} -{"city": "DE KALB", "loc": [-94.927027, 39.583361], "pop": 584, "state": "MO", "_id": "64440"} -{"city": "DENVER", "loc": [-94.306624, 40.418074], "pop": 181, "state": "MO", "_id": "64441"} -{"city": "EAGLEVILLE", "loc": [-93.995058, 40.491197], "pop": 669, "state": "MO", "_id": "64442"} -{"city": "EASTON", "loc": [-94.658194, 39.751709], "pop": 1189, "state": "MO", "_id": "64443"} -{"city": "EDGERTON", "loc": [-94.635217, 39.474213], "pop": 1427, "state": "MO", "_id": "64444"} -{"city": "ELMO", "loc": [-95.123564, 40.518492], "pop": 481, "state": "MO", "_id": "64445"} -{"city": "FAIRFAX", "loc": [-95.375075, 40.330193], "pop": 1413, "state": "MO", "_id": "64446"} -{"city": "FAUCETT", "loc": [-94.791276, 39.589124], "pop": 1077, "state": "MO", "_id": "64448"} -{"city": "FILLMORE", "loc": [-94.955496, 40.014212], "pop": 514, "state": "MO", "_id": "64449"} -{"city": "FOREST CITY", "loc": [-95.191594, 39.989708], "pop": 450, "state": "MO", "_id": "64451"} -{"city": "FORTESCUE", "loc": [-95.331584, 40.054712], "pop": 208, "state": "MO", "_id": "64452"} -{"city": "GENTRY", "loc": [-94.414198, 40.341112], "pop": 256, "state": "MO", "_id": "64453"} -{"city": "GOWER", "loc": [-94.596526, 39.602009], "pop": 2115, "state": "MO", "_id": "64454"} -{"city": "GRAHAM", "loc": [-95.012118, 40.201204], "pop": 543, "state": "MO", "_id": "64455"} -{"city": "GRANT CITY", "loc": [-94.397908, 40.492358], "pop": 1401, "state": "MO", "_id": "64456"} -{"city": "GUILFORD", "loc": [-94.695076, 40.174614], "pop": 388, "state": "MO", "_id": "64457"} -{"city": "HATFIELD", "loc": [-94.169059, 40.521853], "pop": 141, "state": "MO", "_id": "64458"} -{"city": "HELENA", "loc": [-94.69212, 39.925122], "pop": 1218, "state": "MO", "_id": "64459"} -{"city": "HOPKINS", "loc": [-94.818948, 40.548328], "pop": 869, "state": "MO", "_id": "64461"} -{"city": "KING CITY", "loc": [-94.523412, 40.065184], "pop": 1382, "state": "MO", "_id": "64463"} -{"city": "LATHROP", "loc": [-94.309271, 39.517727], "pop": 4461, "state": "MO", "_id": "64465"} -{"city": "MAITLAND", "loc": [-95.092706, 40.199111], "pop": 582, "state": "MO", "_id": "64466"} -{"city": "MARTINSVILLE", "loc": [-94.164463, 40.366206], "pop": 271, "state": "MO", "_id": "64467"} -{"city": "MARYVILLE", "loc": [-94.873479, 40.343399], "pop": 13687, "state": "MO", "_id": "64468"} -{"city": "MAYSVILLE", "loc": [-94.354821, 39.911189], "pop": 1948, "state": "MO", "_id": "64469"} -{"city": "MOUND CITY", "loc": [-95.213837, 40.136238], "pop": 2166, "state": "MO", "_id": "64470"} -{"city": "NEW HAMPTON", "loc": [-94.178565, 40.244439], "pop": 666, "state": "MO", "_id": "64471"} -{"city": "OREGON", "loc": [-95.123358, 39.980906], "pop": 1601, "state": "MO", "_id": "64473"} -{"city": "OSBORN", "loc": [-94.247677, 39.768436], "pop": 4084, "state": "MO", "_id": "64474"} -{"city": "PARNELL", "loc": [-94.659519, 40.472288], "pop": 456, "state": "MO", "_id": "64475"} -{"city": "PICKERING", "loc": [-94.841119, 40.459073], "pop": 525, "state": "MO", "_id": "64476"} -{"city": "PLATTSBURG", "loc": [-94.433814, 39.57047], "pop": 3786, "state": "MO", "_id": "64477"} -{"city": "QUITMAN", "loc": [-95.072909, 40.3701], "pop": 302, "state": "MO", "_id": "64478"} -{"city": "RAVENWOOD", "loc": [-94.68051, 40.357902], "pop": 942, "state": "MO", "_id": "64479"} -{"city": "REA", "loc": [-94.700184, 40.059263], "pop": 690, "state": "MO", "_id": "64480"} -{"city": "RIDGEWAY", "loc": [-93.957527, 40.399896], "pop": 870, "state": "MO", "_id": "64481"} -{"city": "ROCK PORT", "loc": [-95.527403, 40.430581], "pop": 2704, "state": "MO", "_id": "64482"} -{"city": "ROSENDALE", "loc": [-94.832808, 40.039853], "pop": 630, "state": "MO", "_id": "64483"} -{"city": "RUSHVILLE", "loc": [-95.041259, 39.565272], "pop": 1068, "state": "MO", "_id": "64484"} -{"city": "SAVANNAH", "loc": [-94.826733, 39.916774], "pop": 7599, "state": "MO", "_id": "64485"} -{"city": "SHERIDAN", "loc": [-94.570149, 40.492822], "pop": 664, "state": "MO", "_id": "64486"} -{"city": "SKIDMORE", "loc": [-95.079241, 40.28961], "pop": 638, "state": "MO", "_id": "64487"} -{"city": "STANBERRY", "loc": [-94.538691, 40.229285], "pop": 1879, "state": "MO", "_id": "64489"} -{"city": "HEMPLE", "loc": [-94.51779, 39.745465], "pop": 2110, "state": "MO", "_id": "64490"} -{"city": "TARKIO", "loc": [-95.378586, 40.441841], "pop": 2553, "state": "MO", "_id": "64491"} -{"city": "TRIMBLE", "loc": [-94.551234, 39.487067], "pop": 944, "state": "MO", "_id": "64492"} -{"city": "TURNEY", "loc": [-94.297241, 39.631772], "pop": 319, "state": "MO", "_id": "64493"} -{"city": "UNION STAR", "loc": [-94.578732, 39.984645], "pop": 745, "state": "MO", "_id": "64494"} -{"city": "WATSON", "loc": [-95.61931, 40.477985], "pop": 215, "state": "MO", "_id": "64496"} -{"city": "WEATHERBY", "loc": [-94.254959, 39.888699], "pop": 584, "state": "MO", "_id": "64497"} -{"city": "WESTBORO", "loc": [-95.313419, 40.535748], "pop": 572, "state": "MO", "_id": "64498"} -{"city": "WORTH", "loc": [-94.437084, 40.4173], "pop": 229, "state": "MO", "_id": "64499"} -{"city": "SAINT JOSEPH", "loc": [-94.838488, 39.768755], "pop": 12978, "state": "MO", "_id": "64501"} -{"city": "SAINT JOSEPH", "loc": [-94.817125, 39.733987], "pop": 14190, "state": "MO", "_id": "64503"} -{"city": "SAINT JOSEPH", "loc": [-94.867749, 39.707566], "pop": 11412, "state": "MO", "_id": "64504"} -{"city": "SAINT JOSEPH", "loc": [-94.844341, 39.796532], "pop": 10627, "state": "MO", "_id": "64505"} -{"city": "SAINT JOSEPH", "loc": [-94.804314, 39.789292], "pop": 18009, "state": "MO", "_id": "64506"} -{"city": "SAINT JOSEPH", "loc": [-94.817303, 39.755052], "pop": 11194, "state": "MO", "_id": "64507"} -{"city": "CHILLICOTHE", "loc": [-93.550887, 39.796569], "pop": 11189, "state": "MO", "_id": "64601"} -{"city": "ALTAMONT", "loc": [-94.128155, 39.905017], "pop": 533, "state": "MO", "_id": "64620"} -{"city": "AVALON", "loc": [-93.47288, 39.668183], "pop": 388, "state": "MO", "_id": "64621"} -{"city": "BOGARD", "loc": [-93.537387, 39.499354], "pop": 831, "state": "MO", "_id": "64622"} -{"city": "BOSWORTH", "loc": [-93.333521, 39.476692], "pop": 583, "state": "MO", "_id": "64623"} -{"city": "BRAYMER", "loc": [-93.78868, 39.591454], "pop": 1671, "state": "MO", "_id": "64624"} -{"city": "BRECKENRIDGE", "loc": [-93.806794, 39.758205], "pop": 608, "state": "MO", "_id": "64625"} -{"city": "BROOKFIELD", "loc": [-93.071948, 39.784612], "pop": 5564, "state": "MO", "_id": "64628"} -{"city": "BROWNING", "loc": [-93.160661, 40.028983], "pop": 360, "state": "MO", "_id": "64630"} -{"city": "BUCKLIN", "loc": [-92.89281, 39.800563], "pop": 969, "state": "MO", "_id": "64631"} -{"city": "CAINSVILLE", "loc": [-93.759047, 40.457768], "pop": 870, "state": "MO", "_id": "64632"} -{"city": "CARROLLTON", "loc": [-93.492626, 39.367334], "pop": 5694, "state": "MO", "_id": "64633"} -{"city": "CHULA", "loc": [-93.484095, 39.922606], "pop": 685, "state": "MO", "_id": "64635"} -{"city": "COFFEY", "loc": [-94.025151, 40.099928], "pop": 359, "state": "MO", "_id": "64636"} -{"city": "COWGILL", "loc": [-93.929469, 39.564314], "pop": 532, "state": "MO", "_id": "64637"} -{"city": "DAWN", "loc": [-93.596405, 39.666506], "pop": 468, "state": "MO", "_id": "64638"} -{"city": "DE WITT", "loc": [-93.301583, 39.358978], "pop": 592, "state": "MO", "_id": "64639"} -{"city": "GALLATIN", "loc": [-93.978748, 39.902486], "pop": 3260, "state": "MO", "_id": "64640"} -{"city": "GALT", "loc": [-93.395261, 40.14398], "pop": 607, "state": "MO", "_id": "64641"} -{"city": "GILMAN CITY", "loc": [-93.831979, 40.145005], "pop": 680, "state": "MO", "_id": "64642"} -{"city": "HALE", "loc": [-93.34448, 39.595302], "pop": 771, "state": "MO", "_id": "64643"} -{"city": "HAMILTON", "loc": [-93.990898, 39.736412], "pop": 2706, "state": "MO", "_id": "64644"} -{"city": "HARRIS", "loc": [-93.350106, 40.307519], "pop": 127, "state": "MO", "_id": "64645"} -{"city": "HUMPHREYS", "loc": [-93.301455, 40.114448], "pop": 368, "state": "MO", "_id": "64646"} -{"city": "JAMESON", "loc": [-93.959747, 40.004615], "pop": 401, "state": "MO", "_id": "64647"} -{"city": "JAMESPORT", "loc": [-93.780048, 39.983724], "pop": 1933, "state": "MO", "_id": "64648"} -{"city": "KIDDER", "loc": [-94.130005, 39.757039], "pop": 708, "state": "MO", "_id": "64649"} -{"city": "KINGSTON", "loc": [-94.082694, 39.6508], "pop": 868, "state": "MO", "_id": "64650"} -{"city": "LACLEDE", "loc": [-93.167971, 39.7837], "pop": 744, "state": "MO", "_id": "64651"} -{"city": "LAREDO", "loc": [-93.440676, 40.014397], "pop": 485, "state": "MO", "_id": "64652"} -{"city": "LINNEUS", "loc": [-93.188529, 39.909945], "pop": 1421, "state": "MO", "_id": "64653"} -{"city": "LOCK SPRINGS", "loc": [-93.797697, 39.907396], "pop": 576, "state": "MO", "_id": "64654"} -{"city": "LUCERNE", "loc": [-93.286654, 40.438182], "pop": 206, "state": "MO", "_id": "64655"} -{"city": "LUDLOW", "loc": [-93.704578, 39.655064], "pop": 355, "state": "MO", "_id": "64656"} -{"city": "MC FALL", "loc": [-94.300259, 40.105071], "pop": 547, "state": "MO", "_id": "64657"} -{"city": "MARCELINE", "loc": [-92.945502, 39.712485], "pop": 3517, "state": "MO", "_id": "64658"} -{"city": "MEADVILLE", "loc": [-93.301389, 39.779468], "pop": 774, "state": "MO", "_id": "64659"} -{"city": "MENDON", "loc": [-93.089168, 39.582849], "pop": 568, "state": "MO", "_id": "64660"} -{"city": "MERCER", "loc": [-93.524197, 40.516917], "pop": 975, "state": "MO", "_id": "64661"} -{"city": "MOORESVILLE", "loc": [-93.71693, 39.74249], "pop": 320, "state": "MO", "_id": "64664"} -{"city": "MOUNT MORIAH", "loc": [-93.814252, 40.329164], "pop": 247, "state": "MO", "_id": "64665"} -{"city": "NEWTOWN", "loc": [-93.307279, 40.361227], "pop": 313, "state": "MO", "_id": "64667"} -{"city": "NORBORNE", "loc": [-93.67609, 39.329903], "pop": 1669, "state": "MO", "_id": "64668"} -{"city": "PATTONSBURG", "loc": [-94.134295, 40.042756], "pop": 855, "state": "MO", "_id": "64670"} -{"city": "POLO", "loc": [-94.074303, 39.564697], "pop": 1496, "state": "MO", "_id": "64671"} -{"city": "POWERSVILLE", "loc": [-93.284436, 40.527155], "pop": 284, "state": "MO", "_id": "64672"} -{"city": "PRINCETON", "loc": [-93.577394, 40.385533], "pop": 2467, "state": "MO", "_id": "64673"} -{"city": "PURDIN", "loc": [-93.17002, 39.952948], "pop": 297, "state": "MO", "_id": "64674"} -{"city": "ROTHVILLE", "loc": [-93.046683, 39.662666], "pop": 440, "state": "MO", "_id": "64676"} -{"city": "SAINT CATHARINE", "loc": [-92.99295, 39.800017], "pop": 288, "state": "MO", "_id": "64677"} -{"city": "SPICKARD", "loc": [-93.550291, 40.239985], "pop": 659, "state": "MO", "_id": "64679"} -{"city": "SUMNER", "loc": [-93.224128, 39.654992], "pop": 307, "state": "MO", "_id": "64681"} -{"city": "TINA", "loc": [-93.46475, 39.551751], "pop": 399, "state": "MO", "_id": "64682"} -{"city": "TRENTON", "loc": [-93.608634, 40.082335], "pop": 8465, "state": "MO", "_id": "64683"} -{"city": "UTICA", "loc": [-93.628897, 39.741742], "pop": 361, "state": "MO", "_id": "64686"} -{"city": "WHEELING", "loc": [-93.386835, 39.801137], "pop": 481, "state": "MO", "_id": "64688"} -{"city": "WINSTON", "loc": [-94.148747, 39.84995], "pop": 567, "state": "MO", "_id": "64689"} -{"city": "HARRISONVILLE", "loc": [-94.32852, 38.64193], "pop": 12242, "state": "MO", "_id": "64701"} -{"city": "ADRIAN", "loc": [-94.368336, 38.412473], "pop": 2254, "state": "MO", "_id": "64720"} -{"city": "AMORET", "loc": [-94.573423, 38.260508], "pop": 409, "state": "MO", "_id": "64722"} -{"city": "AMSTERDAM", "loc": [-94.576282, 38.395385], "pop": 884, "state": "MO", "_id": "64723"} -{"city": "APPLETON CITY", "loc": [-94.0229, 38.184779], "pop": 1584, "state": "MO", "_id": "64724"} -{"city": "ARCHIE", "loc": [-94.362992, 38.498612], "pop": 1912, "state": "MO", "_id": "64725"} -{"city": "BLAIRSTOWN", "loc": [-93.964102, 38.522701], "pop": 803, "state": "MO", "_id": "64726"} -{"city": "BRONAUGH", "loc": [-94.48603, 37.69], "pop": 493, "state": "MO", "_id": "64728"} -{"city": "BUTLER", "loc": [-94.31375, 38.271245], "pop": 8449, "state": "MO", "_id": "64730"} -{"city": "CHILHOWEE", "loc": [-93.865325, 38.612604], "pop": 951, "state": "MO", "_id": "64733"} -{"city": "CLEVELAND", "loc": [-94.5695, 38.68977], "pop": 2586, "state": "MO", "_id": "64734"} -{"city": "TIGHTWAD", "loc": [-93.758906, 38.368811], "pop": 12259, "state": "MO", "_id": "64735"} -{"city": "COLLINS", "loc": [-93.660849, 37.880998], "pop": 844, "state": "MO", "_id": "64738"} -{"city": "CREIGHTON", "loc": [-94.092604, 38.507754], "pop": 711, "state": "MO", "_id": "64739"} -{"city": "DEEPWATER", "loc": [-93.730281, 38.243164], "pop": 1233, "state": "MO", "_id": "64740"} -{"city": "DEERFIELD", "loc": [-94.484845, 37.82109], "pop": 966, "state": "MO", "_id": "64741"} -{"city": "DREXEL", "loc": [-94.592765, 38.495625], "pop": 1181, "state": "MO", "_id": "64742"} -{"city": "EL DORADO SPRING", "loc": [-94.030091, 37.858517], "pop": 6787, "state": "MO", "_id": "64744"} -{"city": "FOSTER", "loc": [-94.537067, 38.17338], "pop": 464, "state": "MO", "_id": "64745"} -{"city": "FREEMAN", "loc": [-94.495561, 38.624549], "pop": 1509, "state": "MO", "_id": "64746"} -{"city": "GARDEN CITY", "loc": [-94.182532, 38.568082], "pop": 1837, "state": "MO", "_id": "64747"} -{"city": "GOLDEN CITY", "loc": [-94.102683, 37.399535], "pop": 1127, "state": "MO", "_id": "64748"} -{"city": "HARWOOD", "loc": [-94.140194, 37.953028], "pop": 189, "state": "MO", "_id": "64750"} -{"city": "HORTON", "loc": [-94.396438, 37.995078], "pop": 551, "state": "MO", "_id": "64751"} -{"city": "STOTESBURY", "loc": [-94.561, 38.059635], "pop": 728, "state": "MO", "_id": "64752"} -{"city": "JASPER", "loc": [-94.273126, 37.318224], "pop": 2085, "state": "MO", "_id": "64755"} -{"city": "JERICO SPRINGS", "loc": [-94.012912, 37.661343], "pop": 966, "state": "MO", "_id": "64756"} -{"city": "IANTHA", "loc": [-94.270253, 37.500221], "pop": 7847, "state": "MO", "_id": "64759"} -{"city": "LATOUR", "loc": [-94.045721, 38.634847], "pop": 745, "state": "MO", "_id": "64760"} -{"city": "LEETON", "loc": [-93.712321, 38.614113], "pop": 1723, "state": "MO", "_id": "64761"} -{"city": "LIBERAL", "loc": [-94.520377, 37.572453], "pop": 1448, "state": "MO", "_id": "64762"} -{"city": "LOWRY CITY", "loc": [-93.711352, 38.140443], "pop": 1345, "state": "MO", "_id": "64763"} -{"city": "MILO", "loc": [-94.304632, 37.744559], "pop": 439, "state": "MO", "_id": "64767"} -{"city": "MINDENMINES", "loc": [-94.575573, 37.451987], "pop": 544, "state": "MO", "_id": "64769"} -{"city": "MONTROSE", "loc": [-93.995239, 38.259702], "pop": 643, "state": "MO", "_id": "64770"} -{"city": "MOUNDVILLE", "loc": [-94.449645, 37.747637], "pop": 478, "state": "MO", "_id": "64771"} -{"city": "NEVADA", "loc": [-94.357127, 37.840853], "pop": 11918, "state": "MO", "_id": "64772"} -{"city": "OSCEOLA", "loc": [-93.753621, 38.028553], "pop": 4684, "state": "MO", "_id": "64776"} -{"city": "RICHARDS", "loc": [-94.559227, 37.906111], "pop": 207, "state": "MO", "_id": "64778"} -{"city": "RICH HILL", "loc": [-94.363454, 38.09438], "pop": 1630, "state": "MO", "_id": "64779"} -{"city": "ROCKVILLE", "loc": [-94.129904, 38.076583], "pop": 456, "state": "MO", "_id": "64780"} -{"city": "SCHELL CITY", "loc": [-94.15791, 38.009403], "pop": 809, "state": "MO", "_id": "64783"} -{"city": "SHELDON", "loc": [-94.254835, 37.683969], "pop": 1281, "state": "MO", "_id": "64784"} -{"city": "URICH", "loc": [-93.97847, 38.44489], "pop": 949, "state": "MO", "_id": "64788"} -{"city": "WALKER", "loc": [-94.229301, 37.893023], "pop": 544, "state": "MO", "_id": "64790"} -{"city": "JOPLIN", "loc": [-94.505144, 37.096858], "pop": 29617, "state": "MO", "_id": "64801"} -{"city": "JOPLIN", "loc": [-94.510252, 37.046454], "pop": 30661, "state": "MO", "_id": "64804"} -{"city": "ANDERSON", "loc": [-94.476648, 36.669224], "pop": 4477, "state": "MO", "_id": "64831"} -{"city": "ASBURY", "loc": [-94.565484, 37.294168], "pop": 603, "state": "MO", "_id": "64832"} -{"city": "AVILLA", "loc": [-94.117043, 37.194709], "pop": 678, "state": "MO", "_id": "64833"} -{"city": "CARL JUNCTION", "loc": [-94.55502, 37.179479], "pop": 4158, "state": "MO", "_id": "64834"} -{"city": "CARTERVILLE", "loc": [-94.435938, 37.150734], "pop": 2397, "state": "MO", "_id": "64835"} -{"city": "CARTHAGE", "loc": [-94.311232, 37.159686], "pop": 19516, "state": "MO", "_id": "64836"} -{"city": "DIAMOND", "loc": [-94.32041, 37.005993], "pop": 2736, "state": "MO", "_id": "64840"} -{"city": "FAIRVIEW", "loc": [-94.091203, 36.825419], "pop": 568, "state": "MO", "_id": "64842"} -{"city": "GOODMAN", "loc": [-94.39855, 36.732335], "pop": 2025, "state": "MO", "_id": "64843"} -{"city": "GRANBY", "loc": [-94.264349, 36.906562], "pop": 3571, "state": "MO", "_id": "64844"} -{"city": "LANAGAN", "loc": [-94.4551, 36.605956], "pop": 755, "state": "MO", "_id": "64847"} -{"city": "LA RUSSELL", "loc": [-94.032874, 37.17387], "pop": 817, "state": "MO", "_id": "64848"} -{"city": "NEOSHO", "loc": [-94.386218, 36.870634], "pop": 15863, "state": "MO", "_id": "64850"} -{"city": "NOEL", "loc": [-94.490627, 36.541668], "pop": 2239, "state": "MO", "_id": "64854"} -{"city": "ORONOGO", "loc": [-94.446246, 37.243288], "pop": 3942, "state": "MO", "_id": "64855"} -{"city": "JANE", "loc": [-94.351461, 36.580243], "pop": 2783, "state": "MO", "_id": "64856"} -{"city": "REEDS", "loc": [-94.161272, 37.122477], "pop": 222, "state": "MO", "_id": "64859"} -{"city": "ROCKY COMFORT", "loc": [-94.109066, 36.717546], "pop": 543, "state": "MO", "_id": "64861"} -{"city": "SARCOXIE", "loc": [-94.11514, 37.072382], "pop": 2133, "state": "MO", "_id": "64862"} -{"city": "SOUTH WEST CITY", "loc": [-94.596141, 36.531924], "pop": 1201, "state": "MO", "_id": "64863"} -{"city": "SENECA", "loc": [-94.559367, 36.844169], "pop": 5419, "state": "MO", "_id": "64865"} -{"city": "STARK CITY", "loc": [-94.154811, 36.878538], "pop": 967, "state": "MO", "_id": "64866"} -{"city": "STELLA", "loc": [-94.20858, 36.751553], "pop": 2154, "state": "MO", "_id": "64867"} -{"city": "TIFF CITY", "loc": [-94.593437, 36.664447], "pop": 364, "state": "MO", "_id": "64868"} -{"city": "WEBB CITY", "loc": [-94.472683, 37.144], "pop": 7474, "state": "MO", "_id": "64870"} -{"city": "WENTWORTH", "loc": [-94.132169, 36.980702], "pop": 1211, "state": "MO", "_id": "64873"} -{"city": "WHEATON", "loc": [-94.04916, 36.765145], "pop": 970, "state": "MO", "_id": "64874"} -{"city": "ARGYLE", "loc": [-92.015456, 38.298619], "pop": 384, "state": "MO", "_id": "65001"} -{"city": "ASHLAND", "loc": [-92.253662, 38.787771], "pop": 2989, "state": "MO", "_id": "65010"} -{"city": "BARNETT", "loc": [-92.668594, 38.396696], "pop": 660, "state": "MO", "_id": "65011"} -{"city": "BELLE", "loc": [-91.730299, 38.271108], "pop": 2874, "state": "MO", "_id": "65013"} -{"city": "BLAND", "loc": [-91.626334, 38.307383], "pop": 2284, "state": "MO", "_id": "65014"} -{"city": "BONNOTS MILL", "loc": [-91.929341, 38.553282], "pop": 1373, "state": "MO", "_id": "65016"} -{"city": "BRUMLEY", "loc": [-92.474732, 38.070914], "pop": 1127, "state": "MO", "_id": "65017"} -{"city": "CALIFORNIA", "loc": [-92.545558, 38.622369], "pop": 6224, "state": "MO", "_id": "65018"} -{"city": "CAMDENTON", "loc": [-92.767744, 38.018534], "pop": 8006, "state": "MO", "_id": "65020"} -{"city": "CENTERTOWN", "loc": [-92.399521, 38.629705], "pop": 1184, "state": "MO", "_id": "65023"} -{"city": "CHAMOIS", "loc": [-91.76968, 38.652683], "pop": 986, "state": "MO", "_id": "65024"} -{"city": "CLARKSBURG", "loc": [-92.672925, 38.643156], "pop": 784, "state": "MO", "_id": "65025"} -{"city": "ELDON", "loc": [-92.573619, 38.340127], "pop": 8673, "state": "MO", "_id": "65026"} -{"city": "EUGENE", "loc": [-92.368853, 38.359849], "pop": 1181, "state": "MO", "_id": "65032"} -{"city": "FORTUNA", "loc": [-92.784948, 38.574391], "pop": 310, "state": "MO", "_id": "65034"} -{"city": "FREEBURG", "loc": [-91.927605, 38.355085], "pop": 1373, "state": "MO", "_id": "65035"} -{"city": "GRAVOIS MILLS", "loc": [-92.823029, 38.258445], "pop": 5653, "state": "MO", "_id": "65037"} -{"city": "HARTSBURG", "loc": [-92.286377, 38.715902], "pop": 1945, "state": "MO", "_id": "65039"} -{"city": "HENLEY", "loc": [-92.312303, 38.388133], "pop": 952, "state": "MO", "_id": "65040"} -{"city": "BAY", "loc": [-91.467717, 38.668381], "pop": 4842, "state": "MO", "_id": "65041"} -{"city": "HIGH POINT", "loc": [-92.600514, 38.478237], "pop": 509, "state": "MO", "_id": "65042"} -{"city": "HOLTS SUMMIT", "loc": [-92.116345, 38.632784], "pop": 6642, "state": "MO", "_id": "65043"} -{"city": "JAMESTOWN", "loc": [-92.480651, 38.779292], "pop": 1018, "state": "MO", "_id": "65046"} -{"city": "KAISER", "loc": [-92.579869, 38.163673], "pop": 1213, "state": "MO", "_id": "65047"} -{"city": "KOELTZTOWN", "loc": [-92.048365, 38.323526], "pop": 185, "state": "MO", "_id": "65048"} -{"city": "FOUR SEASONS", "loc": [-92.677555, 38.209127], "pop": 4404, "state": "MO", "_id": "65049"} -{"city": "LATHAM", "loc": [-92.653647, 38.550035], "pop": 937, "state": "MO", "_id": "65050"} -{"city": "LINN", "loc": [-91.81945, 38.473855], "pop": 3836, "state": "MO", "_id": "65051"} -{"city": "LINN CREEK", "loc": [-92.683077, 38.060524], "pop": 2308, "state": "MO", "_id": "65052"} -{"city": "LOHMAN", "loc": [-92.384216, 38.548434], "pop": 356, "state": "MO", "_id": "65053"} -{"city": "LOOSE CREEK", "loc": [-91.959126, 38.471715], "pop": 1089, "state": "MO", "_id": "65054"} -{"city": "META", "loc": [-92.135797, 38.250039], "pop": 1003, "state": "MO", "_id": "65058"} -{"city": "MOKANE", "loc": [-91.886815, 38.699839], "pop": 881, "state": "MO", "_id": "65059"} -{"city": "MORRISON", "loc": [-91.657972, 38.605967], "pop": 267, "state": "MO", "_id": "65061"} -{"city": "MOUNT STERLING", "loc": [-91.610315, 38.538786], "pop": 285, "state": "MO", "_id": "65062"} -{"city": "NEW BLOOMFIELD", "loc": [-92.082983, 38.709996], "pop": 2588, "state": "MO", "_id": "65063"} -{"city": "OLEAN", "loc": [-92.53031, 38.400059], "pop": 882, "state": "MO", "_id": "65064"} -{"city": "OSAGE BEACH", "loc": [-92.666427, 38.13805], "pop": 3601, "state": "MO", "_id": "65065"} -{"city": "OWENSVILLE", "loc": [-91.486679, 38.351119], "pop": 5980, "state": "MO", "_id": "65066"} -{"city": "PORTLAND", "loc": [-91.700567, 38.784701], "pop": 439, "state": "MO", "_id": "65067"} -{"city": "PRAIRIE HOME", "loc": [-92.597376, 38.825331], "pop": 533, "state": "MO", "_id": "65068"} -{"city": "RHINELAND", "loc": [-91.515105, 38.763811], "pop": 1340, "state": "MO", "_id": "65069"} -{"city": "ROCKY MOUNT", "loc": [-92.705921, 38.291057], "pop": 157, "state": "MO", "_id": "65072"} -{"city": "RUSSELLVILLE", "loc": [-92.429122, 38.500534], "pop": 2043, "state": "MO", "_id": "65074"} -{"city": "SAINT ELIZABETH", "loc": [-92.263503, 38.271244], "pop": 701, "state": "MO", "_id": "65075"} -{"city": "SAINT THOMAS", "loc": [-92.189382, 38.391246], "pop": 1232, "state": "MO", "_id": "65076"} -{"city": "STEEDMAN", "loc": [-91.788837, 38.756604], "pop": 458, "state": "MO", "_id": "65077"} -{"city": "STOVER", "loc": [-92.994667, 38.441352], "pop": 1946, "state": "MO", "_id": "65078"} -{"city": "SUNRISE BEACH", "loc": [-92.785395, 38.155845], "pop": 3635, "state": "MO", "_id": "65079"} -{"city": "TEBBETTS", "loc": [-91.967316, 38.640193], "pop": 413, "state": "MO", "_id": "65080"} -{"city": "TIPTON", "loc": [-92.781448, 38.654839], "pop": 2516, "state": "MO", "_id": "65081"} -{"city": "TUSCUMBIA", "loc": [-92.491739, 38.238881], "pop": 1181, "state": "MO", "_id": "65082"} -{"city": "ULMAN", "loc": [-92.463979, 38.134141], "pop": 517, "state": "MO", "_id": "65083"} -{"city": "VERSAILLES", "loc": [-92.825835, 38.436491], "pop": 5581, "state": "MO", "_id": "65084"} -{"city": "WESTPHALIA", "loc": [-92.039221, 38.426968], "pop": 992, "state": "MO", "_id": "65085"} -{"city": "JEFFERSON CITY", "loc": [-92.152462, 38.546212], "pop": 25992, "state": "MO", "_id": "65101"} -{"city": "JEFFERSON CITY", "loc": [-92.244298, 38.577272], "pop": 31418, "state": "MO", "_id": "65109"} -{"city": "COLUMBIA", "loc": [-92.304865, 38.938176], "pop": 33668, "state": "MO", "_id": "65201"} -{"city": "COLUMBIA", "loc": [-92.311204, 38.995019], "pop": 27394, "state": "MO", "_id": "65202"} -{"city": "COLUMBIA", "loc": [-92.363865, 38.93482], "pop": 34914, "state": "MO", "_id": "65203"} -{"city": "ARMSTRONG", "loc": [-92.708976, 39.256585], "pop": 736, "state": "MO", "_id": "65230"} -{"city": "AUXVASSE", "loc": [-91.885801, 39.012184], "pop": 2698, "state": "MO", "_id": "65231"} -{"city": "BENTON CITY", "loc": [-91.766124, 39.120886], "pop": 300, "state": "MO", "_id": "65232"} -{"city": "BOONVILLE", "loc": [-92.744973, 38.95364], "pop": 9330, "state": "MO", "_id": "65233"} -{"city": "BRUNSWICK", "loc": [-93.118694, 39.437424], "pop": 1610, "state": "MO", "_id": "65236"} -{"city": "BUNCETON", "loc": [-92.792695, 38.746754], "pop": 1343, "state": "MO", "_id": "65237"} -{"city": "CAIRO", "loc": [-92.440027, 39.51141], "pop": 1143, "state": "MO", "_id": "65239"} -{"city": "CENTRALIA", "loc": [-92.147244, 39.196089], "pop": 5171, "state": "MO", "_id": "65240"} -{"city": "CLARK", "loc": [-92.382706, 39.315293], "pop": 4672, "state": "MO", "_id": "65243"} -{"city": "CLIFTON HILL", "loc": [-92.667676, 39.425985], "pop": 341, "state": "MO", "_id": "65244"} -{"city": "DALTON", "loc": [-92.994366, 39.40346], "pop": 209, "state": "MO", "_id": "65246"} -{"city": "EXCELLO", "loc": [-92.475705, 39.645543], "pop": 873, "state": "MO", "_id": "65247"} -{"city": "FAYETTE", "loc": [-92.658287, 39.143041], "pop": 4805, "state": "MO", "_id": "65248"} -{"city": "FRANKLIN", "loc": [-92.831596, 39.066818], "pop": 680, "state": "MO", "_id": "65250"} -{"city": "FULTON", "loc": [-91.96055, 38.851821], "pop": 17506, "state": "MO", "_id": "65251"} -{"city": "GLASGOW", "loc": [-92.831812, 39.225736], "pop": 1757, "state": "MO", "_id": "65254"} -{"city": "HALLSVILLE", "loc": [-92.223855, 39.105429], "pop": 2476, "state": "MO", "_id": "65255"} -{"city": "HARRISBURG", "loc": [-92.440955, 39.120312], "pop": 928, "state": "MO", "_id": "65256"} -{"city": "HIGBEE", "loc": [-92.516309, 39.305521], "pop": 932, "state": "MO", "_id": "65257"} -{"city": "HOLLIDAY", "loc": [-92.131759, 39.490436], "pop": 286, "state": "MO", "_id": "65258"} -{"city": "HUNTSVILLE", "loc": [-92.552992, 39.435371], "pop": 3210, "state": "MO", "_id": "65259"} -{"city": "JACKSONVILLE", "loc": [-92.431536, 39.579076], "pop": 473, "state": "MO", "_id": "65260"} -{"city": "KEYTESVILLE", "loc": [-92.930187, 39.479388], "pop": 1354, "state": "MO", "_id": "65261"} -{"city": "KINGDOM CITY", "loc": [-91.952008, 38.95508], "pop": 322, "state": "MO", "_id": "65262"} -{"city": "MADISON", "loc": [-92.228715, 39.461543], "pop": 2000, "state": "MO", "_id": "65263"} -{"city": "MARTINSBURG", "loc": [-91.66463, 39.09675], "pop": 644, "state": "MO", "_id": "65264"} -{"city": "MEXICO", "loc": [-91.889473, 39.171233], "pop": 14785, "state": "MO", "_id": "65265"} -{"city": "MOBERLY", "loc": [-92.435793, 39.420239], "pop": 14884, "state": "MO", "_id": "65270"} -{"city": "NEW FRANKLIN", "loc": [-92.738601, 39.020042], "pop": 1595, "state": "MO", "_id": "65274"} -{"city": "PARIS", "loc": [-92.011323, 39.493219], "pop": 3157, "state": "MO", "_id": "65275"} -{"city": "PILOT GROVE", "loc": [-92.930461, 38.871168], "pop": 1492, "state": "MO", "_id": "65276"} -{"city": "ROCHEPORT", "loc": [-92.507854, 38.975578], "pop": 1437, "state": "MO", "_id": "65279"} -{"city": "RUSH HILL", "loc": [-91.688825, 39.193163], "pop": 646, "state": "MO", "_id": "65280"} -{"city": "SALISBURY", "loc": [-92.80144, 39.431853], "pop": 4022, "state": "MO", "_id": "65281"} -{"city": "SANTA FE", "loc": [-91.829377, 39.398929], "pop": 712, "state": "MO", "_id": "65282"} -{"city": "STOUTSVILLE", "loc": [-91.829661, 39.559425], "pop": 361, "state": "MO", "_id": "65283"} -{"city": "STURGEON", "loc": [-92.295264, 39.205705], "pop": 1812, "state": "MO", "_id": "65284"} -{"city": "THOMPSON", "loc": [-92.025945, 39.20258], "pop": 1230, "state": "MO", "_id": "65285"} -{"city": "TRIPLETT", "loc": [-93.192771, 39.501098], "pop": 192, "state": "MO", "_id": "65286"} -{"city": "WOOLDRIDGE", "loc": [-92.582584, 38.919475], "pop": 571, "state": "MO", "_id": "65287"} -{"city": "SEDALIA", "loc": [-93.232268, 38.696076], "pop": 28813, "state": "MO", "_id": "65301"} -{"city": "WHITEMAN AFB", "loc": [-93.572514, 38.731683], "pop": 4222, "state": "MO", "_id": "65305"} -{"city": "BLACKBURN", "loc": [-93.428124, 39.098776], "pop": 777, "state": "MO", "_id": "65321"} -{"city": "BLACKWATER", "loc": [-92.968291, 38.972659], "pop": 713, "state": "MO", "_id": "65322"} -{"city": "CALHOUN", "loc": [-93.645929, 38.485842], "pop": 795, "state": "MO", "_id": "65323"} -{"city": "CLIMAX SPRINGS", "loc": [-92.95371, 38.139586], "pop": 1840, "state": "MO", "_id": "65324"} -{"city": "COLE CAMP", "loc": [-93.191529, 38.453124], "pop": 2570, "state": "MO", "_id": "65325"} -{"city": "EDWARDS", "loc": [-93.147104, 38.190564], "pop": 1320, "state": "MO", "_id": "65326"} -{"city": "FLORENCE", "loc": [-92.998606, 38.610056], "pop": 847, "state": "MO", "_id": "65329"} -{"city": "GILLIAM", "loc": [-92.993222, 39.244708], "pop": 476, "state": "MO", "_id": "65330"} -{"city": "GREEN RIDGE", "loc": [-93.437395, 38.618983], "pop": 1198, "state": "MO", "_id": "65332"} -{"city": "HOUSTONIA", "loc": [-93.332531, 38.910581], "pop": 699, "state": "MO", "_id": "65333"} -{"city": "HUGHESVILLE", "loc": [-93.215895, 38.84944], "pop": 1279, "state": "MO", "_id": "65334"} -{"city": "IONIA", "loc": [-93.322358, 38.501887], "pop": 175, "state": "MO", "_id": "65335"} -{"city": "KNOB NOSTER", "loc": [-93.556422, 38.746932], "pop": 4455, "state": "MO", "_id": "65336"} -{"city": "LA MONTE", "loc": [-93.431266, 38.775256], "pop": 1570, "state": "MO", "_id": "65337"} -{"city": "LINCOLN", "loc": [-93.3668, 38.407271], "pop": 1810, "state": "MO", "_id": "65338"} -{"city": "GRAND PASS", "loc": [-93.381325, 39.195108], "pop": 622, "state": "MO", "_id": "65339"} -{"city": "NAPTON", "loc": [-93.196017, 39.10983], "pop": 15086, "state": "MO", "_id": "65340"} -{"city": "MIAMI", "loc": [-93.19678, 39.282095], "pop": 569, "state": "MO", "_id": "65344"} -{"city": "MORA", "loc": [-93.227232, 38.520726], "pop": 132, "state": "MO", "_id": "65345"} -{"city": "NELSON", "loc": [-93.031102, 39.010447], "pop": 836, "state": "MO", "_id": "65347"} -{"city": "OTTERVILLE", "loc": [-93.010819, 38.71607], "pop": 853, "state": "MO", "_id": "65348"} -{"city": "SLATER", "loc": [-93.054674, 39.216273], "pop": 2872, "state": "MO", "_id": "65349"} -{"city": "SMITHTON", "loc": [-93.108072, 38.649973], "pop": 1669, "state": "MO", "_id": "65350"} -{"city": "SWEET SPRINGS", "loc": [-93.424683, 38.965953], "pop": 2385, "state": "MO", "_id": "65351"} -{"city": "SYRACUSE", "loc": [-92.882265, 38.637423], "pop": 730, "state": "MO", "_id": "65354"} -{"city": "WARSAW", "loc": [-93.337279, 38.250186], "pop": 8108, "state": "MO", "_id": "65355"} -{"city": "WINDSOR", "loc": [-93.526864, 38.52719], "pop": 3453, "state": "MO", "_id": "65360"} -{"city": "ROLLA", "loc": [-91.760348, 37.948527], "pop": 24377, "state": "MO", "_id": "65401"} -{"city": "BENDAVIS", "loc": [-92.228174, 37.246803], "pop": 44, "state": "MO", "_id": "65433"} -{"city": "BEULAH", "loc": [-91.878714, 37.644469], "pop": 546, "state": "MO", "_id": "65436"} -{"city": "BIRCH TREE", "loc": [-91.50082, 36.947589], "pop": 2586, "state": "MO", "_id": "65438"} -{"city": "BIXBY", "loc": [-91.083461, 37.672244], "pop": 464, "state": "MO", "_id": "65439"} -{"city": "BOSS", "loc": [-91.210905, 37.637282], "pop": 457, "state": "MO", "_id": "65440"} -{"city": "BOURBON", "loc": [-91.22254, 38.172039], "pop": 4456, "state": "MO", "_id": "65441"} -{"city": "BRINKTOWN", "loc": [-92.123106, 38.067282], "pop": 946, "state": "MO", "_id": "65443"} -{"city": "BUCYRUS", "loc": [-92.046536, 37.397197], "pop": 1416, "state": "MO", "_id": "65444"} -{"city": "CHERRYVILLE", "loc": [-91.234807, 37.806601], "pop": 861, "state": "MO", "_id": "65446"} -{"city": "COOK STATION", "loc": [-91.461631, 37.855234], "pop": 347, "state": "MO", "_id": "65449"} -{"city": "65451", "loc": [-91.001597, 37.7904], "pop": 228, "state": "MO", "_id": "65451"} -{"city": "CROCKER", "loc": [-92.269998, 37.944611], "pop": 2954, "state": "MO", "_id": "65452"} -{"city": "CUBA", "loc": [-91.40814, 38.092553], "pop": 7466, "state": "MO", "_id": "65453"} -{"city": "DAVISVILLE", "loc": [-91.132872, 37.792596], "pop": 286, "state": "MO", "_id": "65456"} -{"city": "DEVILS ELBOW", "loc": [-92.085693, 37.85026], "pop": 1717, "state": "MO", "_id": "65457"} -{"city": "DIXON", "loc": [-92.089662, 37.984766], "pop": 4779, "state": "MO", "_id": "65459"} -{"city": "DUKE", "loc": [-91.993063, 37.655001], "pop": 166, "state": "MO", "_id": "65461"} -{"city": "EDGAR SPRINGS", "loc": [-91.890579, 37.736505], "pop": 1026, "state": "MO", "_id": "65462"} -{"city": "ELDRIDGE", "loc": [-92.738202, 37.83456], "pop": 892, "state": "MO", "_id": "65463"} -{"city": "ELK CREEK", "loc": [-91.998812, 37.217087], "pop": 540, "state": "MO", "_id": "65464"} -{"city": "EMINENCE", "loc": [-91.451933, 37.162556], "pop": 2450, "state": "MO", "_id": "65466"} -{"city": "EUNICE", "loc": [-91.723191, 37.212277], "pop": 215, "state": "MO", "_id": "65468"} -{"city": "FALCON", "loc": [-92.346562, 37.606099], "pop": 561, "state": "MO", "_id": "65470"} -{"city": "FORT LEONARD WOO", "loc": [-92.126345, 37.759856], "pop": 16140, "state": "MO", "_id": "65473"} -{"city": "HARTSHORN", "loc": [-91.683418, 37.284306], "pop": 306, "state": "MO", "_id": "65479"} -{"city": "HOUSTON", "loc": [-91.952982, 37.321829], "pop": 4497, "state": "MO", "_id": "65483"} -{"city": "HUGGINS", "loc": [-92.148605, 37.266224], "pop": 593, "state": "MO", "_id": "65484"} -{"city": "IBERIA", "loc": [-92.298897, 38.121926], "pop": 3704, "state": "MO", "_id": "65486"} -{"city": "JADWIN", "loc": [-91.515887, 37.473619], "pop": 374, "state": "MO", "_id": "65501"} -{"city": "JEROME", "loc": [-91.99057, 37.92543], "pop": 391, "state": "MO", "_id": "65529"} -{"city": "LAQUEY", "loc": [-92.337901, 37.749378], "pop": 798, "state": "MO", "_id": "65534"} -{"city": "LEASBURG", "loc": [-91.2488, 38.081054], "pop": 1061, "state": "MO", "_id": "65535"} -{"city": "LEBANON", "loc": [-92.655029, 37.685049], "pop": 20859, "state": "MO", "_id": "65536"} -{"city": "ANUTT", "loc": [-91.715862, 37.743731], "pop": 635, "state": "MO", "_id": "65540"} -{"city": "LENOX", "loc": [-91.727735, 37.640757], "pop": 640, "state": "MO", "_id": "65541"} -{"city": "LICKING", "loc": [-91.855585, 37.509082], "pop": 3545, "state": "MO", "_id": "65542"} -{"city": "LYNCHBURG", "loc": [-92.320196, 37.505357], "pop": 297, "state": "MO", "_id": "65543"} -{"city": "MOUNTAIN VIEW", "loc": [-91.709937, 36.989197], "pop": 3721, "state": "MO", "_id": "65548"} -{"city": "NEWBURG", "loc": [-91.880683, 37.900778], "pop": 2972, "state": "MO", "_id": "65550"} -{"city": "PLATO", "loc": [-92.170913, 37.512231], "pop": 1479, "state": "MO", "_id": "65552"} -{"city": "RAYMONDVILLE", "loc": [-91.813089, 37.356913], "pop": 1145, "state": "MO", "_id": "65555"} -{"city": "RICHLAND", "loc": [-92.396245, 37.852752], "pop": 3643, "state": "MO", "_id": "65556"} -{"city": "ROBY", "loc": [-92.122188, 37.496993], "pop": 184, "state": "MO", "_id": "65557"} -{"city": "SAINT JAMES", "loc": [-91.607565, 38.005622], "pop": 6315, "state": "MO", "_id": "65559"} -{"city": "SALEM", "loc": [-91.525809, 37.616952], "pop": 12088, "state": "MO", "_id": "65560"} -{"city": "SOLO", "loc": [-91.959905, 37.167052], "pop": 309, "state": "MO", "_id": "65564"} -{"city": "BERRYMAN", "loc": [-91.329337, 37.962998], "pop": 4323, "state": "MO", "_id": "65565"} -{"city": "VIBURNUM", "loc": [-91.128922, 37.715066], "pop": 844, "state": "MO", "_id": "65566"} -{"city": "STOUTLAND", "loc": [-92.511373, 37.869338], "pop": 1637, "state": "MO", "_id": "65567"} -{"city": "SUCCESS", "loc": [-92.003621, 37.480533], "pop": 210, "state": "MO", "_id": "65570"} -{"city": "SUMMERSVILLE", "loc": [-91.68277, 37.148352], "pop": 1199, "state": "MO", "_id": "65571"} -{"city": "TERESITA", "loc": [-91.596553, 36.999728], "pop": 573, "state": "MO", "_id": "65573"} -{"city": "VICHY", "loc": [-91.77875, 38.097958], "pop": 977, "state": "MO", "_id": "65580"} -{"city": "VIENNA", "loc": [-91.942208, 38.191926], "pop": 1956, "state": "MO", "_id": "65582"} -{"city": "SAINT ROBERT", "loc": [-92.198944, 37.817515], "pop": 12431, "state": "MO", "_id": "65583"} -{"city": "WESCO", "loc": [-91.349028, 37.873185], "pop": 460, "state": "MO", "_id": "65586"} -{"city": "WINONA", "loc": [-91.312059, 37.016869], "pop": 2065, "state": "MO", "_id": "65588"} -{"city": "YUKON", "loc": [-91.824377, 37.231108], "pop": 476, "state": "MO", "_id": "65589"} -{"city": "LONG LANE", "loc": [-92.930853, 37.580769], "pop": 1589, "state": "MO", "_id": "65590"} -{"city": "MONTREAL", "loc": [-92.547031, 37.985108], "pop": 759, "state": "MO", "_id": "65591"} -{"city": "ALDRICH", "loc": [-93.557642, 37.505667], "pop": 1006, "state": "MO", "_id": "65601"} -{"city": "ARCOLA", "loc": [-93.858718, 37.531254], "pop": 420, "state": "MO", "_id": "65603"} -{"city": "ASH GROVE", "loc": [-93.57813, 37.315972], "pop": 1909, "state": "MO", "_id": "65604"} -{"city": "JENKINS", "loc": [-93.71238, 36.947661], "pop": 10006, "state": "MO", "_id": "65605"} -{"city": "RIVERTON", "loc": [-91.392743, 36.702543], "pop": 3224, "state": "MO", "_id": "65606"} -{"city": "AVA", "loc": [-92.676544, 36.940717], "pop": 8246, "state": "MO", "_id": "65608"} -{"city": "BAKERSFIELD", "loc": [-92.150679, 36.53323], "pop": 560, "state": "MO", "_id": "65609"} -{"city": "BILLINGS", "loc": [-93.547629, 37.062841], "pop": 2408, "state": "MO", "_id": "65610"} -{"city": "BLUE EYE", "loc": [-93.429944, 36.54321], "pop": 2148, "state": "MO", "_id": "65611"} -{"city": "BOIS D ARC", "loc": [-93.544722, 37.221417], "pop": 2410, "state": "MO", "_id": "65612"} -{"city": "BOLIVAR", "loc": [-93.412631, 37.608502], "pop": 10671, "state": "MO", "_id": "65613"} -{"city": "BRADLEYVILLE", "loc": [-92.915116, 36.765908], "pop": 428, "state": "MO", "_id": "65614"} -{"city": "MARVEL CAVE PARK", "loc": [-93.243789, 36.655755], "pop": 7658, "state": "MO", "_id": "65616"} -{"city": "BRIGHTON", "loc": [-93.360279, 37.472807], "pop": 920, "state": "MO", "_id": "65617"} -{"city": "BRIXEY", "loc": [-92.402645, 36.758966], "pop": 198, "state": "MO", "_id": "65618"} -{"city": "BROOKLINE STATIO", "loc": [-93.383467, 37.127184], "pop": 4541, "state": "MO", "_id": "65619"} -{"city": "BRUNER", "loc": [-92.969241, 36.999002], "pop": 737, "state": "MO", "_id": "65620"} -{"city": "BUFFALO", "loc": [-93.104228, 37.549842], "pop": 4176, "state": "MO", "_id": "65622"} -{"city": "BUTTERFIELD", "loc": [-93.912609, 36.745066], "pop": 684, "state": "MO", "_id": "65623"} -{"city": "CAPE FAIR", "loc": [-93.524255, 36.693776], "pop": 1783, "state": "MO", "_id": "65624"} -{"city": "CASSVILLE", "loc": [-93.84667, 36.678386], "pop": 5416, "state": "MO", "_id": "65625"} -{"city": "CAULFIELD", "loc": [-92.067773, 36.60353], "pop": 1941, "state": "MO", "_id": "65626"} -{"city": "CEDARCREEK", "loc": [-93.017239, 36.571013], "pop": 407, "state": "MO", "_id": "65627"} -{"city": "CHADWICK", "loc": [-93.045082, 36.922041], "pop": 326, "state": "MO", "_id": "65629"} -{"city": "CHESTNUTRIDGE", "loc": [-93.101217, 36.835283], "pop": 188, "state": "MO", "_id": "65630"} -{"city": "CLEVER", "loc": [-93.44747, 37.034796], "pop": 1728, "state": "MO", "_id": "65631"} -{"city": "CONWAY", "loc": [-92.7891, 37.508472], "pop": 1451, "state": "MO", "_id": "65632"} -{"city": "CRANE", "loc": [-93.530339, 36.925771], "pop": 2918, "state": "MO", "_id": "65633"} -{"city": "CROSS TIMBERS", "loc": [-93.19781, 38.023873], "pop": 612, "state": "MO", "_id": "65634"} -{"city": "DADEVILLE", "loc": [-93.695562, 37.510458], "pop": 774, "state": "MO", "_id": "65635"} -{"city": "DORA", "loc": [-92.237796, 36.756009], "pop": 1163, "state": "MO", "_id": "65637"} -{"city": "DRURY", "loc": [-92.366396, 36.93193], "pop": 735, "state": "MO", "_id": "65638"} -{"city": "DUNNEGAN", "loc": [-93.521605, 37.703137], "pop": 755, "state": "MO", "_id": "65640"} -{"city": "EAGLE ROCK", "loc": [-93.733609, 36.548035], "pop": 1062, "state": "MO", "_id": "65641"} -{"city": "ELKLAND", "loc": [-93.020983, 37.433591], "pop": 1136, "state": "MO", "_id": "65644"} -{"city": "EVERTON", "loc": [-93.689434, 37.235428], "pop": 675, "state": "MO", "_id": "65646"} -{"city": "EXETER", "loc": [-93.970209, 36.681498], "pop": 2264, "state": "MO", "_id": "65647"} -{"city": "FAIR GROVE", "loc": [-93.142824, 37.372143], "pop": 2079, "state": "MO", "_id": "65648"} -{"city": "FAIR PLAY", "loc": [-93.606355, 37.633496], "pop": 1533, "state": "MO", "_id": "65649"} -{"city": "FLEMINGTON", "loc": [-93.447131, 37.780268], "pop": 642, "state": "MO", "_id": "65650"} -{"city": "FORDLAND", "loc": [-92.911148, 37.14474], "pop": 2080, "state": "MO", "_id": "65652"} -{"city": "FORSYTH", "loc": [-93.114968, 36.69548], "pop": 3870, "state": "MO", "_id": "65653"} -{"city": "FREISTATT", "loc": [-93.896144, 37.017563], "pop": 515, "state": "MO", "_id": "65654"} -{"city": "GAINESVILLE", "loc": [-92.416151, 36.590101], "pop": 1987, "state": "MO", "_id": "65655"} -{"city": "GALENA", "loc": [-93.48113, 36.819835], "pop": 2307, "state": "MO", "_id": "65656"} -{"city": "GARRISON", "loc": [-92.9985, 36.860997], "pop": 362, "state": "MO", "_id": "65657"} -{"city": "GOLDEN", "loc": [-93.622988, 36.562486], "pop": 1663, "state": "MO", "_id": "65658"} -{"city": "GOODSON", "loc": [-93.237528, 37.707534], "pop": 379, "state": "MO", "_id": "65659"} -{"city": "GRAFF", "loc": [-92.264726, 37.326214], "pop": 271, "state": "MO", "_id": "65660"} -{"city": "GREENFIELD", "loc": [-93.840689, 37.419662], "pop": 2041, "state": "MO", "_id": "65661"} -{"city": "GROVESPRING", "loc": [-92.600141, 37.494667], "pop": 2121, "state": "MO", "_id": "65662"} -{"city": "HALF WAY", "loc": [-93.241989, 37.601795], "pop": 1270, "state": "MO", "_id": "65663"} -{"city": "HARDENVILLE", "loc": [-92.367766, 36.592625], "pop": 267, "state": "MO", "_id": "65666"} -{"city": "HARTVILLE", "loc": [-92.518058, 37.273453], "pop": 3040, "state": "MO", "_id": "65667"} -{"city": "HERMITAGE", "loc": [-93.297902, 37.896853], "pop": 2575, "state": "MO", "_id": "65668"} -{"city": "HIGHLANDVILLE", "loc": [-93.26802, 36.94077], "pop": 2106, "state": "MO", "_id": "65669"} -{"city": "HOLLISTER", "loc": [-93.228585, 36.610727], "pop": 5515, "state": "MO", "_id": "65672"} -{"city": "HUMANSVILLE", "loc": [-93.579531, 37.792282], "pop": 1541, "state": "MO", "_id": "65674"} -{"city": "HURLEY", "loc": [-93.476803, 36.925425], "pop": 766, "state": "MO", "_id": "65675"} -{"city": "ISABELLA", "loc": [-92.605277, 36.574924], "pop": 563, "state": "MO", "_id": "65676"} -{"city": "KIRBYVILLE", "loc": [-93.168256, 36.642615], "pop": 1794, "state": "MO", "_id": "65679"} -{"city": "KISSEE MILLS", "loc": [-93.037733, 36.670446], "pop": 826, "state": "MO", "_id": "65680"} -{"city": "LAMPE", "loc": [-93.451602, 36.576725], "pop": 1087, "state": "MO", "_id": "65681"} -{"city": "LOCKWOOD", "loc": [-93.86715, 37.386605], "pop": 4042, "state": "MO", "_id": "65682"} -{"city": "LOUISBURG", "loc": [-93.100699, 37.682843], "pop": 4530, "state": "MO", "_id": "65685"} -{"city": "KIMBERLING CITY", "loc": [-93.43721, 36.63928], "pop": 3208, "state": "MO", "_id": "65686"} -{"city": "BRANDSVILLE", "loc": [-91.735536, 36.584925], "pop": 794, "state": "MO", "_id": "65688"} -{"city": "CABOOL", "loc": [-92.114409, 37.131366], "pop": 4127, "state": "MO", "_id": "65689"} -{"city": "COUCH", "loc": [-91.330991, 36.565268], "pop": 995, "state": "MO", "_id": "65690"} -{"city": "KOSHKONONG", "loc": [-91.630411, 36.605553], "pop": 727, "state": "MO", "_id": "65692"} -{"city": "MC CLURG", "loc": [-92.818018, 36.751465], "pop": 76, "state": "MO", "_id": "65701"} -{"city": "MACOMB", "loc": [-92.48207, 37.104947], "pop": 533, "state": "MO", "_id": "65702"} -{"city": "MANSFIELD", "loc": [-92.593553, 37.1273], "pop": 3368, "state": "MO", "_id": "65704"} -{"city": "MARIONVILLE", "loc": [-93.641345, 37.000906], "pop": 3305, "state": "MO", "_id": "65705"} -{"city": "MARSHFIELD", "loc": [-92.925033, 37.331178], "pop": 10026, "state": "MO", "_id": "65706"} -{"city": "MILLER", "loc": [-93.842188, 37.222454], "pop": 1986, "state": "MO", "_id": "65707"} -{"city": "MONETT", "loc": [-93.925766, 36.921242], "pop": 9297, "state": "MO", "_id": "65708"} -{"city": "MORRISVILLE", "loc": [-93.427486, 37.468588], "pop": 689, "state": "MO", "_id": "65710"} -{"city": "MOUNTAIN GROVE", "loc": [-92.283893, 37.162638], "pop": 7623, "state": "MO", "_id": "65711"} -{"city": "MOUNT VERNON", "loc": [-93.797632, 37.104466], "pop": 6685, "state": "MO", "_id": "65712"} -{"city": "NIANGUA", "loc": [-92.776337, 37.398537], "pop": 2628, "state": "MO", "_id": "65713"} -{"city": "NIXA", "loc": [-93.297176, 37.051154], "pop": 11346, "state": "MO", "_id": "65714"} -{"city": "NOBLE", "loc": [-92.57686, 36.744052], "pop": 103, "state": "MO", "_id": "65715"} -{"city": "NORWOOD", "loc": [-92.408209, 37.068696], "pop": 1535, "state": "MO", "_id": "65717"} -{"city": "OLDFIELD", "loc": [-93.032526, 36.970416], "pop": 409, "state": "MO", "_id": "65720"} -{"city": "OZARK", "loc": [-93.202211, 37.016926], "pop": 8437, "state": "MO", "_id": "65721"} -{"city": "PHILLIPSBURG", "loc": [-92.741631, 37.583177], "pop": 1369, "state": "MO", "_id": "65722"} -{"city": "PIERCE CITY", "loc": [-94.00241, 36.972992], "pop": 2795, "state": "MO", "_id": "65723"} -{"city": "PITTSBURG", "loc": [-93.335615, 37.8442], "pop": 610, "state": "MO", "_id": "65724"} -{"city": "PLEASANT HOPE", "loc": [-93.261733, 37.4615], "pop": 2079, "state": "MO", "_id": "65725"} -{"city": "POLK", "loc": [-93.294065, 37.752928], "pop": 725, "state": "MO", "_id": "65727"} -{"city": "PONCE DE LEON", "loc": [-93.367055, 36.89054], "pop": 435, "state": "MO", "_id": "65728"} -{"city": "PONTIAC", "loc": [-92.561221, 36.524804], "pop": 293, "state": "MO", "_id": "65729"} -{"city": "POWELL", "loc": [-94.167541, 36.556252], "pop": 1681, "state": "MO", "_id": "65730"} -{"city": "POWERSITE", "loc": [-93.090015, 36.630656], "pop": 742, "state": "MO", "_id": "65731"} -{"city": "PRESTON", "loc": [-93.171295, 37.939025], "pop": 753, "state": "MO", "_id": "65732"} -{"city": "PROTEM", "loc": [-92.829689, 36.546336], "pop": 375, "state": "MO", "_id": "65733"} -{"city": "PURDY", "loc": [-93.916376, 36.806917], "pop": 2906, "state": "MO", "_id": "65734"} -{"city": "QUINCY", "loc": [-93.471281, 38.002925], "pop": 241, "state": "MO", "_id": "65735"} -{"city": "BRANSON WEST", "loc": [-93.372182, 36.694658], "pop": 4426, "state": "MO", "_id": "65737"} -{"city": "REPUBLIC", "loc": [-93.480041, 37.123017], "pop": 8312, "state": "MO", "_id": "65738"} -{"city": "RIDGEDALE", "loc": [-93.277808, 36.524662], "pop": 809, "state": "MO", "_id": "65739"} -{"city": "ROCKAWAY BEACH", "loc": [-93.171535, 36.713686], "pop": 1354, "state": "MO", "_id": "65740"} -{"city": "ROGERSVILLE", "loc": [-93.096449, 37.131047], "pop": 9409, "state": "MO", "_id": "65742"} -{"city": "RUETER", "loc": [-92.918792, 36.631415], "pop": 180, "state": "MO", "_id": "65744"} -{"city": "SELIGMAN", "loc": [-93.935851, 36.527593], "pop": 1392, "state": "MO", "_id": "65745"} -{"city": "SEYMOUR", "loc": [-92.785659, 37.166726], "pop": 5311, "state": "MO", "_id": "65746"} -{"city": "SHELL KNOB", "loc": [-93.624855, 36.616672], "pop": 821, "state": "MO", "_id": "65747"} -{"city": "65751", "loc": [-92.503184, 36.735365], "pop": 455, "state": "MO", "_id": "65751"} -{"city": "SOUTH GREENFIELD", "loc": [-93.844482, 37.374256], "pop": 248, "state": "MO", "_id": "65752"} -{"city": "SPARTA", "loc": [-93.106483, 36.977524], "pop": 1879, "state": "MO", "_id": "65753"} -{"city": "SPOKANE", "loc": [-93.275447, 36.863639], "pop": 968, "state": "MO", "_id": "65754"} -{"city": "SQUIRES", "loc": [-92.584443, 36.850406], "pop": 536, "state": "MO", "_id": "65755"} -{"city": "STOTTS CITY", "loc": [-93.954329, 37.103148], "pop": 497, "state": "MO", "_id": "65756"} -{"city": "STRAFFORD", "loc": [-93.10663, 37.279718], "pop": 3226, "state": "MO", "_id": "65757"} -{"city": "SYCAMORE", "loc": [-92.354355, 36.67179], "pop": 97, "state": "MO", "_id": "65758"} -{"city": "TANEYVILLE", "loc": [-93.027951, 36.740497], "pop": 731, "state": "MO", "_id": "65759"} -{"city": "TECUMSEH", "loc": [-92.259782, 36.58751], "pop": 444, "state": "MO", "_id": "65760"} -{"city": "DUGGINSVILLE", "loc": [-92.701861, 36.569019], "pop": 962, "state": "MO", "_id": "65761"} -{"city": "NOTTINGHILL", "loc": [-92.657789, 36.707546], "pop": 617, "state": "MO", "_id": "65762"} -{"city": "TUNAS", "loc": [-92.980788, 37.839196], "pop": 827, "state": "MO", "_id": "65764"} -{"city": "UDALL", "loc": [-92.23345, 36.525291], "pop": 184, "state": "MO", "_id": "65766"} -{"city": "URBANA", "loc": [-93.150972, 37.85233], "pop": 755, "state": "MO", "_id": "65767"} -{"city": "VANZANT", "loc": [-92.210068, 36.977829], "pop": 1048, "state": "MO", "_id": "65768"} -{"city": "VERONA", "loc": [-93.800505, 36.936964], "pop": 2021, "state": "MO", "_id": "65769"} -{"city": "WALNUT GROVE", "loc": [-93.504356, 37.394253], "pop": 2374, "state": "MO", "_id": "65770"} -{"city": "WALNUT SHADE", "loc": [-93.214824, 36.770395], "pop": 722, "state": "MO", "_id": "65771"} -{"city": "WASHBURN", "loc": [-93.991974, 36.580703], "pop": 1526, "state": "MO", "_id": "65772"} -{"city": "SOUDER", "loc": [-92.58991, 36.782631], "pop": 215, "state": "MO", "_id": "65773"} -{"city": "WEAUBLEAU", "loc": [-93.534355, 37.880393], "pop": 814, "state": "MO", "_id": "65774"} -{"city": "WEST PLAINS", "loc": [-91.871681, 36.728418], "pop": 16362, "state": "MO", "_id": "65775"} -{"city": "SOUTH FORK", "loc": [-91.912484, 36.629312], "pop": 194, "state": "MO", "_id": "65776"} -{"city": "MOODY", "loc": [-91.989756, 36.533018], "pop": 328, "state": "MO", "_id": "65777"} -{"city": "MYRTLE", "loc": [-91.270584, 36.521697], "pop": 404, "state": "MO", "_id": "65778"} -{"city": "WHEATLAND", "loc": [-93.398129, 37.910283], "pop": 1730, "state": "MO", "_id": "65779"} -{"city": "WILLARD", "loc": [-93.425861, 37.296153], "pop": 3690, "state": "MO", "_id": "65781"} -{"city": "WINDYVILLE", "loc": [-92.937891, 37.718209], "pop": 769, "state": "MO", "_id": "65783"} -{"city": "ZANONI", "loc": [-92.304677, 36.692518], "pop": 274, "state": "MO", "_id": "65784"} -{"city": "STOCKTON", "loc": [-93.796013, 37.72408], "pop": 4635, "state": "MO", "_id": "65785"} -{"city": "MACKS CREEK", "loc": [-92.960349, 37.961563], "pop": 1741, "state": "MO", "_id": "65786"} -{"city": "ROACH", "loc": [-92.807456, 38.008825], "pop": 1542, "state": "MO", "_id": "65787"} -{"city": "PEACE VALLEY", "loc": [-91.769598, 36.837974], "pop": 1074, "state": "MO", "_id": "65788"} -{"city": "POMONA", "loc": [-91.913668, 36.84409], "pop": 1309, "state": "MO", "_id": "65789"} -{"city": "POTTERSVILLE", "loc": [-92.044044, 36.706822], "pop": 1227, "state": "MO", "_id": "65790"} -{"city": "THAYER", "loc": [-91.541803, 36.532714], "pop": 3473, "state": "MO", "_id": "65791"} -{"city": "WILLOW SPRINGS", "loc": [-91.940545, 36.995812], "pop": 5968, "state": "MO", "_id": "65793"} -{"city": "SPRINGFIELD", "loc": [-93.29903, 37.211663], "pop": 33216, "state": "MO", "_id": "65802"} -{"city": "SPRINGFIELD", "loc": [-93.291232, 37.259327], "pop": 41361, "state": "MO", "_id": "65803"} -{"city": "SPRINGFIELD", "loc": [-93.252154, 37.165361], "pop": 33507, "state": "MO", "_id": "65804"} -{"city": "SPRINGFIELD", "loc": [-93.297108, 37.203057], "pop": 11386, "state": "MO", "_id": "65806"} -{"city": "SPRINGFIELD", "loc": [-93.308457, 37.166799], "pop": 46691, "state": "MO", "_id": "65807"} -{"city": "SPRINGFIELD", "loc": [-93.205742, 37.185223], "pop": 5606, "state": "MO", "_id": "65809"} -{"city": "SPRINGFIELD", "loc": [-93.289594, 37.113647], "pop": 3982, "state": "MO", "_id": "65810"} -{"city": "ABSAROKEE", "loc": [-109.469171, 45.515356], "pop": 1330, "state": "MT", "_id": "59001"} -{"city": "ACTON", "loc": [-108.680975, 45.936997], "pop": 55, "state": "MT", "_id": "59002"} -{"city": "ASHLAND", "loc": [-106.279722, 45.58275], "pop": 353, "state": "MT", "_id": "59003"} -{"city": "BALLANTINE", "loc": [-108.123133, 45.954699], "pop": 730, "state": "MT", "_id": "59006"} -{"city": "BEARCREEK", "loc": [-109.044704, 45.15226], "pop": 312, "state": "MT", "_id": "59007"} -{"city": "BELFRY", "loc": [-109.078777, 45.049827], "pop": 64, "state": "MT", "_id": "59008"} -{"city": "BIGHORN", "loc": [-107.205942, 46.238286], "pop": 573, "state": "MT", "_id": "59010"} -{"city": "BIG TIMBER", "loc": [-109.963094, 45.82827], "pop": 2296, "state": "MT", "_id": "59011"} -{"city": "BIRNEY", "loc": [-106.509496, 45.286204], "pop": 138, "state": "MT", "_id": "59012"} -{"city": "BRIDGER", "loc": [-108.908217, 45.285682], "pop": 1524, "state": "MT", "_id": "59014"} -{"city": "BROADVIEW", "loc": [-108.809062, 46.082101], "pop": 303, "state": "MT", "_id": "59015"} -{"city": "BUSBY", "loc": [-106.872311, 45.554139], "pop": 1077, "state": "MT", "_id": "59016"} -{"city": "CAT CREEK", "loc": [-108.259094, 47.154789], "pop": 155, "state": "MT", "_id": "59017"} -{"city": "COLUMBUS", "loc": [-109.257126, 45.626171], "pop": 2438, "state": "MT", "_id": "59019"} -{"city": "CROW AGENCY", "loc": [-107.497251, 45.629594], "pop": 2267, "state": "MT", "_id": "59022"} -{"city": "CUSTER", "loc": [-107.59585, 46.13019], "pop": 304, "state": "MT", "_id": "59024"} -{"city": "DECKER", "loc": [-106.87206, 45.179698], "pop": 164, "state": "MT", "_id": "59025"} -{"city": "EMIGRANT", "loc": [-110.798928, 45.127557], "pop": 2058, "state": "MT", "_id": "59027"} -{"city": "FISHTAIL", "loc": [-109.582078, 45.400217], "pop": 348, "state": "MT", "_id": "59028"} -{"city": "FROMBERG", "loc": [-108.905658, 45.40269], "pop": 627, "state": "MT", "_id": "59029"} -{"city": "GARDINER", "loc": [-110.196258, 45.0493], "pop": 168, "state": "MT", "_id": "59030"} -{"city": "GARRYOWEN", "loc": [-107.364925, 45.508557], "pop": 428, "state": "MT", "_id": "59031"} -{"city": "GRASS RANGE", "loc": [-108.827059, 47.025894], "pop": 494, "state": "MT", "_id": "59032"} -{"city": "GREYCLIFF", "loc": [-109.674601, 45.792631], "pop": 216, "state": "MT", "_id": "59033"} -{"city": "HARDIN", "loc": [-107.607457, 45.749843], "pop": 3889, "state": "MT", "_id": "59034"} -{"city": "HUNTLEY", "loc": [-108.284981, 45.89049], "pop": 1006, "state": "MT", "_id": "59037"} -{"city": "HYSHAM", "loc": [-107.307196, 46.276483], "pop": 301, "state": "MT", "_id": "59038"} -{"city": "INGOMAR", "loc": [-107.551749, 46.654196], "pop": 118, "state": "MT", "_id": "59039"} -{"city": "SILESIA", "loc": [-108.94804, 45.498763], "pop": 1739, "state": "MT", "_id": "59041"} -{"city": "LAME DEER", "loc": [-106.565424, 45.603167], "pop": 2846, "state": "MT", "_id": "59043"} -{"city": "LAUREL", "loc": [-108.769008, 45.67451], "pop": 8328, "state": "MT", "_id": "59044"} -{"city": "LAVINA", "loc": [-108.995853, 46.329058], "pop": 410, "state": "MT", "_id": "59046"} -{"city": "LIVINGSTON", "loc": [-110.560907, 45.654587], "pop": 9980, "state": "MT", "_id": "59047"} -{"city": "LODGE GRASS", "loc": [-107.734105, 45.33218], "pop": 2938, "state": "MT", "_id": "59050"} -{"city": "LUTHER", "loc": [-109.475326, 45.279485], "pop": 11, "state": "MT", "_id": "59051"} -{"city": "MC LEOD", "loc": [-109.935698, 45.596334], "pop": 226, "state": "MT", "_id": "59052"} -{"city": "MARTINSDALE", "loc": [-110.432397, 46.458149], "pop": 246, "state": "MT", "_id": "59053"} -{"city": "MELVILLE", "loc": [-109.880885, 46.036446], "pop": 416, "state": "MT", "_id": "59055"} -{"city": "MOLT", "loc": [-108.973073, 45.861503], "pop": 524, "state": "MT", "_id": "59057"} -{"city": "MOSBY", "loc": [-107.789149, 46.900453], "pop": 7, "state": "MT", "_id": "59058"} -{"city": "MUSSELSHELL", "loc": [-108.003122, 46.517125], "pop": 584, "state": "MT", "_id": "59059"} -{"city": "NYE", "loc": [-109.827137, 45.441022], "pop": 236, "state": "MT", "_id": "59061"} -{"city": "OTTER", "loc": [-106.008667, 45.445803], "pop": 404, "state": "MT", "_id": "59062"} -{"city": "PARK CITY", "loc": [-108.929279, 45.632864], "pop": 1398, "state": "MT", "_id": "59063"} -{"city": "POMPEYS PILLAR", "loc": [-107.915406, 45.983847], "pop": 207, "state": "MT", "_id": "59064"} -{"city": "PRAY", "loc": [-110.686399, 45.419315], "pop": 1094, "state": "MT", "_id": "59065"} -{"city": "RAPELJE", "loc": [-109.276093, 45.97948], "pop": 219, "state": "MT", "_id": "59067"} -{"city": "RED LODGE", "loc": [-109.268812, 45.196522], "pop": 2875, "state": "MT", "_id": "59068"} -{"city": "REEDPOINT", "loc": [-109.468889, 45.648462], "pop": 301, "state": "MT", "_id": "59069"} -{"city": "ROBERTS", "loc": [-109.176888, 45.367235], "pop": 652, "state": "MT", "_id": "59070"} -{"city": "ROSCOE", "loc": [-109.44644, 45.377232], "pop": 159, "state": "MT", "_id": "59071"} -{"city": "ROUNDUP", "loc": [-108.543846, 46.422487], "pop": 3522, "state": "MT", "_id": "59072"} -{"city": "RYEGATE", "loc": [-109.276121, 46.272367], "pop": 502, "state": "MT", "_id": "59074"} -{"city": "SAINT XAVIER", "loc": [-107.709242, 45.488996], "pop": 199, "state": "MT", "_id": "59075"} -{"city": "SAND SPRINGS", "loc": [-107.288623, 47.032287], "pop": 95, "state": "MT", "_id": "59077"} -{"city": "SHAWMUT", "loc": [-109.597361, 46.386921], "pop": 249, "state": "MT", "_id": "59078"} -{"city": "SHEPHERD", "loc": [-108.342634, 45.94608], "pop": 2078, "state": "MT", "_id": "59079"} -{"city": "59080", "loc": [-108.835978, 45.577707], "pop": 117, "state": "MT", "_id": "59080"} -{"city": "TWODOT", "loc": [-109.899483, 46.446513], "pop": 1694, "state": "MT", "_id": "59085"} -{"city": "WILSALL", "loc": [-110.606147, 45.948351], "pop": 1293, "state": "MT", "_id": "59086"} -{"city": "WINNETT", "loc": [-108.318373, 46.943788], "pop": 364, "state": "MT", "_id": "59087"} -{"city": "WORDEN", "loc": [-108.153284, 45.977937], "pop": 1025, "state": "MT", "_id": "59088"} -{"city": "WYOLA", "loc": [-107.430258, 45.108904], "pop": 375, "state": "MT", "_id": "59089"} -{"city": "BILLINGS", "loc": [-108.500452, 45.774489], "pop": 33061, "state": "MT", "_id": "59101"} -{"city": "BILLINGS", "loc": [-108.572662, 45.781265], "pop": 40121, "state": "MT", "_id": "59102"} -{"city": "BILLINGS HEIGHTS", "loc": [-108.474726, 45.828443], "pop": 20320, "state": "MT", "_id": "59105"} -{"city": "BILLINGS", "loc": [-108.65191, 45.775306], "pop": 5623, "state": "MT", "_id": "59106"} -{"city": "WOLF POINT", "loc": [-105.629318, 48.111879], "pop": 4845, "state": "MT", "_id": "59201"} -{"city": "ANTELOPE", "loc": [-104.452883, 48.696813], "pop": 152, "state": "MT", "_id": "59211"} -{"city": "BAINVILLE", "loc": [-104.199532, 48.157989], "pop": 354, "state": "MT", "_id": "59212"} -{"city": "BROCKTON", "loc": [-104.85479, 48.210063], "pop": 959, "state": "MT", "_id": "59213"} -{"city": "BROCKWAY", "loc": [-105.777649, 47.248497], "pop": 233, "state": "MT", "_id": "59214"} -{"city": "CIRCLE", "loc": [-105.614771, 47.426389], "pop": 1271, "state": "MT", "_id": "59215"} -{"city": "CULBERTSON", "loc": [-104.513212, 48.149536], "pop": 949, "state": "MT", "_id": "59218"} -{"city": "DAGMAR", "loc": [-104.240123, 48.609337], "pop": 317, "state": "MT", "_id": "59219"} -{"city": "FAIRVIEW", "loc": [-104.230194, 47.891598], "pop": 1668, "state": "MT", "_id": "59221"} -{"city": "FLAXVILLE", "loc": [-105.163746, 48.747222], "pop": 312, "state": "MT", "_id": "59222"} -{"city": "FORT PECK", "loc": [-106.516646, 48.053844], "pop": 714, "state": "MT", "_id": "59223"} -{"city": "LUSTRE", "loc": [-105.991929, 48.161166], "pop": 819, "state": "MT", "_id": "59225"} -{"city": "FROID", "loc": [-104.451765, 48.320113], "pop": 479, "state": "MT", "_id": "59226"} -{"city": "GLASGOW", "loc": [-106.609419, 48.203385], "pop": 5192, "state": "MT", "_id": "59230"} -{"city": "HINSDALE", "loc": [-107.009836, 48.400705], "pop": 704, "state": "MT", "_id": "59241"} -{"city": "HOMESTEAD", "loc": [-104.591805, 48.429616], "pop": 7, "state": "MT", "_id": "59242"} -{"city": "LAMBERT", "loc": [-104.598746, 47.745908], "pop": 655, "state": "MT", "_id": "59243"} -{"city": "LARSLAN", "loc": [-106.283503, 48.58218], "pop": 120, "state": "MT", "_id": "59244"} -{"city": "MEDICINE LAKE", "loc": [-104.437545, 48.485179], "pop": 629, "state": "MT", "_id": "59247"} -{"city": "NASHUA", "loc": [-106.24407, 48.18653], "pop": 206, "state": "MT", "_id": "59248"} -{"city": "OPHEIM", "loc": [-106.365832, 48.87016], "pop": 322, "state": "MT", "_id": "59250"} -{"city": "OUTLOOK", "loc": [-104.741526, 48.881673], "pop": 241, "state": "MT", "_id": "59252"} -{"city": "PEERLESS", "loc": [-105.800567, 48.780767], "pop": 395, "state": "MT", "_id": "59253"} -{"city": "PLENTYWOOD", "loc": [-104.560032, 48.778825], "pop": 2557, "state": "MT", "_id": "59254"} -{"city": "POPLAR", "loc": [-105.187021, 48.130713], "pop": 3407, "state": "MT", "_id": "59255"} -{"city": "RAYMOND", "loc": [-104.629763, 48.968612], "pop": 29, "state": "MT", "_id": "59256"} -{"city": "REDSTONE", "loc": [-104.935259, 48.835511], "pop": 158, "state": "MT", "_id": "59257"} -{"city": "RESERVE", "loc": [-104.627875, 48.590519], "pop": 127, "state": "MT", "_id": "59258"} -{"city": "RICHEY", "loc": [-105.017017, 47.622874], "pop": 462, "state": "MT", "_id": "59259"} -{"city": "RICHLAND", "loc": [-106.223696, 48.727326], "pop": 162, "state": "MT", "_id": "59260"} -{"city": "SACO", "loc": [-107.429324, 48.638938], "pop": 178, "state": "MT", "_id": "59261"} -{"city": "SAVAGE", "loc": [-104.284487, 47.519375], "pop": 1170, "state": "MT", "_id": "59262"} -{"city": "SCOBEY", "loc": [-105.417016, 48.785356], "pop": 1415, "state": "MT", "_id": "59263"} -{"city": "SIDNEY", "loc": [-104.163445, 47.713017], "pop": 7229, "state": "MT", "_id": "59270"} -{"city": "VIDA", "loc": [-105.599595, 47.894727], "pop": 772, "state": "MT", "_id": "59274"} -{"city": "WESTBY", "loc": [-104.124708, 48.858695], "pop": 515, "state": "MT", "_id": "59275"} -{"city": "WHITETAIL", "loc": [-105.297667, 48.924968], "pop": 144, "state": "MT", "_id": "59276"} -{"city": "MILES CITY", "loc": [-105.833193, 46.407459], "pop": 10604, "state": "MT", "_id": "59301"} -{"city": "ALZADA", "loc": [-104.261747, 45.156244], "pop": 101, "state": "MT", "_id": "59311"} -{"city": "ANGELA", "loc": [-106.315732, 46.771929], "pop": 35, "state": "MT", "_id": "59312"} -{"city": "BAKER", "loc": [-104.266707, 46.355219], "pop": 2631, "state": "MT", "_id": "59313"} -{"city": "BIDDLE", "loc": [-105.290138, 45.256977], "pop": 606, "state": "MT", "_id": "59314"} -{"city": "BLOOMFIELD", "loc": [-104.878235, 47.340813], "pop": 569, "state": "MT", "_id": "59315"} -{"city": "BOYES", "loc": [-104.884118, 45.490003], "pop": 90, "state": "MT", "_id": "59316"} -{"city": "BELLE CREEK", "loc": [-105.424968, 45.46057], "pop": 946, "state": "MT", "_id": "59317"} -{"city": "BRUSETT", "loc": [-107.599864, 47.342341], "pop": 229, "state": "MT", "_id": "59318"} -{"city": "CAPITOL", "loc": [-104.146212, 45.473162], "pop": 73, "state": "MT", "_id": "59319"} -{"city": "COHAGEN", "loc": [-106.498065, 47.126296], "pop": 372, "state": "MT", "_id": "59322"} -{"city": "EKALAKA", "loc": [-104.503958, 45.88054], "pop": 881, "state": "MT", "_id": "59324"} -{"city": "FALLON", "loc": [-105.116055, 46.786632], "pop": 388, "state": "MT", "_id": "59326"} -{"city": "FORSYTH", "loc": [-106.699086, 46.2819], "pop": 3148, "state": "MT", "_id": "59327"} -{"city": "GLENDIVE", "loc": [-104.728716, 47.100813], "pop": 8364, "state": "MT", "_id": "59330"} -{"city": "HAMMOND", "loc": [-104.615444, 45.291872], "pop": 358, "state": "MT", "_id": "59332"} -{"city": "ISMAY", "loc": [-105.209064, 46.413223], "pop": 317, "state": "MT", "_id": "59336"} -{"city": "JORDAN", "loc": [-106.922076, 47.369419], "pop": 886, "state": "MT", "_id": "59337"} -{"city": "KINSEY", "loc": [-105.74458, 46.577008], "pop": 346, "state": "MT", "_id": "59338"} -{"city": "LINDSAY", "loc": [-105.208878, 47.202383], "pop": 110, "state": "MT", "_id": "59339"} -{"city": "MILDRED", "loc": [-104.7891, 46.751671], "pop": 83, "state": "MT", "_id": "59341"} -{"city": "OLIVE", "loc": [-105.668741, 45.546641], "pop": 59, "state": "MT", "_id": "59343"} -{"city": "PLEVNA", "loc": [-104.571289, 46.411225], "pop": 433, "state": "MT", "_id": "59344"} -{"city": "POWDERVILLE", "loc": [-105.276098, 45.739183], "pop": 24, "state": "MT", "_id": "59345"} -{"city": "ROSEBUD", "loc": [-106.598123, 45.935826], "pop": 3867, "state": "MT", "_id": "59347"} -{"city": "TERRY", "loc": [-105.37059, 46.828972], "pop": 912, "state": "MT", "_id": "59349"} -{"city": "VOLBORG", "loc": [-105.721444, 46.073609], "pop": 481, "state": "MT", "_id": "59351"} -{"city": "WIBAUX", "loc": [-104.189715, 46.964596], "pop": 1191, "state": "MT", "_id": "59353"} -{"city": "WILLARD", "loc": [-104.446662, 46.114064], "pop": 39, "state": "MT", "_id": "59354"} -{"city": "GREAT FALLS", "loc": [-111.273397, 47.509812], "pop": 13361, "state": "MT", "_id": "59401"} -{"city": "GREAT FALLS", "loc": [-111.340496, 47.509755], "pop": 23133, "state": "MT", "_id": "59404"} -{"city": "GREAT FALLS", "loc": [-111.250227, 47.495016], "pop": 32774, "state": "MT", "_id": "59405"} -{"city": "AUGUSTA", "loc": [-112.388304, 47.453739], "pop": 839, "state": "MT", "_id": "59410"} -{"city": "BABB", "loc": [-113.368132, 48.878781], "pop": 224, "state": "MT", "_id": "59411"} -{"city": "BELT", "loc": [-110.908099, 47.38211], "pop": 1383, "state": "MT", "_id": "59412"} -{"city": "BLACK EAGLE", "loc": [-111.276366, 47.526197], "pop": 927, "state": "MT", "_id": "59414"} -{"city": "BRADY", "loc": [-111.755013, 48.031244], "pop": 334, "state": "MT", "_id": "59416"} -{"city": "SAINT MARY", "loc": [-113.019697, 48.54926], "pop": 6712, "state": "MT", "_id": "59417"} -{"city": "BUFFALO", "loc": [-109.723122, 46.805593], "pop": 105, "state": "MT", "_id": "59418"} -{"city": "BYNUM", "loc": [-112.276177, 47.990017], "pop": 130, "state": "MT", "_id": "59419"} -{"city": "CARTER", "loc": [-110.978593, 47.780964], "pop": 99, "state": "MT", "_id": "59420"} -{"city": "CASCADE", "loc": [-111.722321, 47.29117], "pop": 2050, "state": "MT", "_id": "59421"} -{"city": "CHOTEAU", "loc": [-112.202136, 47.837951], "pop": 2846, "state": "MT", "_id": "59422"} -{"city": "COFFEE CREEK", "loc": [-110.052784, 47.339642], "pop": 175, "state": "MT", "_id": "59424"} -{"city": "CONRAD", "loc": [-111.939665, 48.178346], "pop": 3843, "state": "MT", "_id": "59425"} -{"city": "CUT BANK", "loc": [-112.365354, 48.660284], "pop": 5249, "state": "MT", "_id": "59427"} -{"city": "DENTON", "loc": [-109.878877, 47.319081], "pop": 604, "state": "MT", "_id": "59430"} -{"city": "DUTTON", "loc": [-111.689967, 47.860082], "pop": 710, "state": "MT", "_id": "59433"} -{"city": "EAST GLACIER PAR", "loc": [-113.317304, 48.45998], "pop": 35, "state": "MT", "_id": "59434"} -{"city": "FAIRFIELD", "loc": [-112.001502, 47.614255], "pop": 1598, "state": "MT", "_id": "59436"} -{"city": "FLOWEREE", "loc": [-111.121384, 47.658441], "pop": 188, "state": "MT", "_id": "59440"} -{"city": "FORESTGROVE", "loc": [-109.023294, 46.913727], "pop": 143, "state": "MT", "_id": "59441"} -{"city": "FORT BENTON", "loc": [-110.671487, 47.809406], "pop": 2795, "state": "MT", "_id": "59442"} -{"city": "FORT SHAW", "loc": [-111.805655, 47.563862], "pop": 531, "state": "MT", "_id": "59443"} -{"city": "GALATA", "loc": [-111.419772, 48.458657], "pop": 156, "state": "MT", "_id": "59444"} -{"city": "GERALDINE", "loc": [-110.276537, 47.602365], "pop": 369, "state": "MT", "_id": "59446"} -{"city": "GEYSER", "loc": [-110.483877, 47.259816], "pop": 289, "state": "MT", "_id": "59447"} -{"city": "HEART BUTTE", "loc": [-112.845591, 48.277743], "pop": 642, "state": "MT", "_id": "59448"} -{"city": "HIGHWOOD", "loc": [-110.788656, 47.581587], "pop": 387, "state": "MT", "_id": "59450"} -{"city": "HILGER", "loc": [-109.456246, 47.269546], "pop": 719, "state": "MT", "_id": "59451"} -{"city": "UTICA", "loc": [-109.951824, 46.947633], "pop": 632, "state": "MT", "_id": "59452"} -{"city": "JUDITH GAP", "loc": [-109.675475, 46.662301], "pop": 303, "state": "MT", "_id": "59453"} -{"city": "KEVIN", "loc": [-111.970829, 48.750786], "pop": 254, "state": "MT", "_id": "59454"} -{"city": "LEDGER", "loc": [-111.756384, 48.277569], "pop": 42, "state": "MT", "_id": "59456"} -{"city": "LEWISTOWN", "loc": [-109.420297, 47.056324], "pop": 8545, "state": "MT", "_id": "59457"} -{"city": "LOMA", "loc": [-110.499487, 47.954576], "pop": 201, "state": "MT", "_id": "59460"} -{"city": "MOCCASIN", "loc": [-109.890066, 47.09164], "pop": 187, "state": "MT", "_id": "59462"} -{"city": "MONARCH", "loc": [-110.871027, 47.07223], "pop": 150, "state": "MT", "_id": "59463"} -{"city": "MOORE", "loc": [-109.653838, 46.989811], "pop": 569, "state": "MT", "_id": "59464"} -{"city": "NEIHART", "loc": [-110.732827, 46.939086], "pop": 58, "state": "MT", "_id": "59465"} -{"city": "PENDROY", "loc": [-112.326082, 48.087892], "pop": 214, "state": "MT", "_id": "59467"} -{"city": "POWER", "loc": [-111.716898, 47.679846], "pop": 862, "state": "MT", "_id": "59468"} -{"city": "RAYNESFORD", "loc": [-110.704747, 47.260425], "pop": 208, "state": "MT", "_id": "59469"} -{"city": "ROY", "loc": [-108.863422, 47.367821], "pop": 353, "state": "MT", "_id": "59471"} -{"city": "SAND COULEE", "loc": [-111.16606, 47.402117], "pop": 583, "state": "MT", "_id": "59472"} -{"city": "SHELBY", "loc": [-111.839122, 48.503666], "pop": 3375, "state": "MT", "_id": "59474"} -{"city": "STANFORD", "loc": [-110.196111, 47.14886], "pop": 966, "state": "MT", "_id": "59479"} -{"city": "STOCKETT", "loc": [-111.12868, 47.321712], "pop": 554, "state": "MT", "_id": "59480"} -{"city": "SUNBURST", "loc": [-111.744195, 48.851585], "pop": 912, "state": "MT", "_id": "59482"} -{"city": "SUN RIVER", "loc": [-111.724214, 47.480975], "pop": 1083, "state": "MT", "_id": "59483"} -{"city": "SWEETGRASS", "loc": [-112.020436, 48.971305], "pop": 349, "state": "MT", "_id": "59484"} -{"city": "VALIER", "loc": [-112.303275, 48.279504], "pop": 1473, "state": "MT", "_id": "59486"} -{"city": "VAUGHN", "loc": [-111.576955, 47.562445], "pop": 869, "state": "MT", "_id": "59487"} -{"city": "WINIFRED", "loc": [-109.340931, 47.589903], "pop": 376, "state": "MT", "_id": "59489"} -{"city": "HAVRE", "loc": [-109.687974, 48.556121], "pop": 13961, "state": "MT", "_id": "59501"} -{"city": "BIG SANDY", "loc": [-110.07762, 48.149677], "pop": 1554, "state": "MT", "_id": "59520"} -{"city": "BOX ELDER", "loc": [-109.820548, 48.284066], "pop": 2012, "state": "MT", "_id": "59521"} -{"city": "CHESTER", "loc": [-110.97982, 48.454125], "pop": 1530, "state": "MT", "_id": "59522"} -{"city": "CHINOOK", "loc": [-109.22246, 48.57985], "pop": 2607, "state": "MT", "_id": "59523"} -{"city": "DODSON", "loc": [-108.629967, 48.207526], "pop": 1870, "state": "MT", "_id": "59524"} -{"city": "GILDFORD", "loc": [-110.283633, 48.592716], "pop": 330, "state": "MT", "_id": "59525"} -{"city": "HARLEM", "loc": [-108.769253, 48.539802], "pop": 1177, "state": "MT", "_id": "59526"} -{"city": "HAYS", "loc": [-108.768422, 48.380374], "pop": 638, "state": "MT", "_id": "59527"} -{"city": "HINGHAM", "loc": [-110.427548, 48.587029], "pop": 333, "state": "MT", "_id": "59528"} -{"city": "HOGELAND", "loc": [-108.667704, 48.857071], "pop": 143, "state": "MT", "_id": "59529"} -{"city": "INVERNESS", "loc": [-110.68796, 48.593009], "pop": 231, "state": "MT", "_id": "59530"} -{"city": "JOPLIN", "loc": [-110.79145, 48.649795], "pop": 496, "state": "MT", "_id": "59531"} -{"city": "KREMLIN", "loc": [-110.051315, 48.559955], "pop": 311, "state": "MT", "_id": "59532"} -{"city": "LLOYD", "loc": [-109.294264, 48.148632], "pop": 212, "state": "MT", "_id": "59535"} -{"city": "LORING", "loc": [-107.868603, 48.798333], "pop": 243, "state": "MT", "_id": "59537"} -{"city": "MALTA", "loc": [-107.840784, 48.369167], "pop": 4049, "state": "MT", "_id": "59538"} -{"city": "RUDYARD", "loc": [-110.555235, 48.586001], "pop": 476, "state": "MT", "_id": "59540"} -{"city": "TURNER", "loc": [-108.396079, 48.832766], "pop": 270, "state": "MT", "_id": "59542"} -{"city": "WHITEWATER", "loc": [-107.40543, 48.834909], "pop": 110, "state": "MT", "_id": "59544"} -{"city": "WHITLASH", "loc": [-111.107512, 48.911037], "pop": 269, "state": "MT", "_id": "59545"} -{"city": "ZORTMAN", "loc": [-108.349523, 47.874326], "pop": 394, "state": "MT", "_id": "59546"} -{"city": "HELENA", "loc": [-112.021283, 46.613066], "pop": 40102, "state": "MT", "_id": "59601"} -{"city": "BOULDER", "loc": [-112.113757, 46.230647], "pop": 1737, "state": "MT", "_id": "59632"} -{"city": "CANYON CREEK", "loc": [-112.279496, 46.762662], "pop": 648, "state": "MT", "_id": "59633"} -{"city": "MONTANA CITY", "loc": [-111.992565, 46.474492], "pop": 3496, "state": "MT", "_id": "59634"} -{"city": "EAST HELENA", "loc": [-111.905089, 46.597324], "pop": 3901, "state": "MT", "_id": "59635"} -{"city": "LINCOLN", "loc": [-112.66514, 46.957458], "pop": 1015, "state": "MT", "_id": "59639"} -{"city": "RADERSBURG", "loc": [-111.572186, 46.079317], "pop": 391, "state": "MT", "_id": "59641"} -{"city": "RINGLING", "loc": [-110.824214, 46.285468], "pop": 97, "state": "MT", "_id": "59642"} -{"city": "TOSTON", "loc": [-111.425974, 46.20437], "pop": 168, "state": "MT", "_id": "59643"} -{"city": "TOWNSEND", "loc": [-111.491906, 46.334571], "pop": 2343, "state": "MT", "_id": "59644"} -{"city": "WHITE SULPHUR SP", "loc": [-110.934413, 46.566323], "pop": 1476, "state": "MT", "_id": "59645"} -{"city": "WINSTON", "loc": [-111.644671, 46.431485], "pop": 416, "state": "MT", "_id": "59647"} -{"city": "WOLF CREEK", "loc": [-111.883316, 46.839567], "pop": 995, "state": "MT", "_id": "59648"} -{"city": "WALKERVILLE", "loc": [-112.517807, 45.991579], "pop": 33096, "state": "MT", "_id": "59701"} -{"city": "ANACONDA", "loc": [-112.97388, 46.129863], "pop": 8611, "state": "MT", "_id": "59711"} -{"city": "BELGRADE", "loc": [-111.143927, 45.780126], "pop": 9060, "state": "MT", "_id": "59714"} -{"city": "BOZEMAN", "loc": [-111.043057, 45.669269], "pop": 31218, "state": "MT", "_id": "59715"} -{"city": "CAMERON", "loc": [-111.650778, 45.139021], "pop": 269, "state": "MT", "_id": "59720"} -{"city": "CARDWELL", "loc": [-111.780946, 45.894071], "pop": 109, "state": "MT", "_id": "59721"} -{"city": "DEER LODGE", "loc": [-112.747589, 46.38807], "pop": 5220, "state": "MT", "_id": "59722"} -{"city": "DELL", "loc": [-112.950401, 44.95877], "pop": 387, "state": "MT", "_id": "59724"} -{"city": "DILLON", "loc": [-112.640452, 45.23394], "pop": 6972, "state": "MT", "_id": "59725"} -{"city": "DIVIDE", "loc": [-112.719551, 45.716055], "pop": 316, "state": "MT", "_id": "59727"} -{"city": "ENNIS", "loc": [-111.687033, 45.354456], "pop": 1749, "state": "MT", "_id": "59729"} -{"city": "GALLATIN GATEWAY", "loc": [-111.173407, 45.609963], "pop": 2807, "state": "MT", "_id": "59730"} -{"city": "GARRISON", "loc": [-112.617322, 46.572363], "pop": 817, "state": "MT", "_id": "59731"} -{"city": "GOLD CREEK", "loc": [-112.97057, 46.590025], "pop": 66, "state": "MT", "_id": "59733"} -{"city": "HARRISON", "loc": [-111.846135, 45.742333], "pop": 434, "state": "MT", "_id": "59735"} -{"city": "JACKSON", "loc": [-113.465862, 45.430725], "pop": 208, "state": "MT", "_id": "59736"} -{"city": "LIMA", "loc": [-112.562492, 44.644057], "pop": 434, "state": "MT", "_id": "59739"} -{"city": "MANHATTAN", "loc": [-111.314577, 45.79799], "pop": 3461, "state": "MT", "_id": "59741"} -{"city": "NORRIS", "loc": [-111.694284, 45.532271], "pop": 148, "state": "MT", "_id": "59745"} -{"city": "PONY", "loc": [-111.961859, 45.574502], "pop": 252, "state": "MT", "_id": "59747"} -{"city": "RAMSAY", "loc": [-112.619628, 46.119573], "pop": 89, "state": "MT", "_id": "59748"} -{"city": "SHERIDAN", "loc": [-112.173543, 45.422968], "pop": 1524, "state": "MT", "_id": "59749"} -{"city": "BUTTE", "loc": [-112.71586, 46.003281], "pop": 440, "state": "MT", "_id": "59750"} -{"city": "SILVER STAR", "loc": [-112.177604, 45.757105], "pop": 310, "state": "MT", "_id": "59751"} -{"city": "THREE FORKS", "loc": [-111.543643, 45.881068], "pop": 1951, "state": "MT", "_id": "59752"} -{"city": "TWIN BRIDGES", "loc": [-112.349461, 45.531055], "pop": 1041, "state": "MT", "_id": "59754"} -{"city": "VIRGINIA CITY", "loc": [-112.002619, 45.247216], "pop": 268, "state": "MT", "_id": "59755"} -{"city": "WARMSPRINGS", "loc": [-112.820041, 46.162593], "pop": 1667, "state": "MT", "_id": "59756"} -{"city": "WEST YELLOWSTONE", "loc": [-111.18595, 44.912502], "pop": 1987, "state": "MT", "_id": "59758"} -{"city": "WHITEHALL", "loc": [-112.124535, 45.877146], "pop": 2591, "state": "MT", "_id": "59759"} -{"city": "WISDOM", "loc": [-113.472926, 45.651915], "pop": 224, "state": "MT", "_id": "59761"} -{"city": "WISE RIVER", "loc": [-112.996285, 45.742397], "pop": 199, "state": "MT", "_id": "59762"} -{"city": "MISSOULA", "loc": [-114.025207, 46.856274], "pop": 33811, "state": "MT", "_id": "59801"} -{"city": "MISSOULA", "loc": [-114.002732, 46.900615], "pop": 22650, "state": "MT", "_id": "59802"} -{"city": "MISSOULA", "loc": [-114.026528, 46.822362], "pop": 10444, "state": "MT", "_id": "59803"} -{"city": "ALBERTON", "loc": [-114.492139, 46.98061], "pop": 885, "state": "MT", "_id": "59820"} -{"city": "ARLEE", "loc": [-114.075978, 47.186035], "pop": 1432, "state": "MT", "_id": "59821"} -{"city": "BONNER", "loc": [-113.746254, 46.860138], "pop": 1625, "state": "MT", "_id": "59823"} -{"city": "MOIESE", "loc": [-114.15931, 47.433449], "pop": 1638, "state": "MT", "_id": "59824"} -{"city": "CLINTON", "loc": [-113.703764, 46.767281], "pop": 829, "state": "MT", "_id": "59825"} -{"city": "CONDON", "loc": [-113.707477, 47.509696], "pop": 534, "state": "MT", "_id": "59826"} -{"city": "CONNER", "loc": [-114.179966, 45.912762], "pop": 579, "state": "MT", "_id": "59827"} -{"city": "CORVALLIS", "loc": [-114.095995, 46.314193], "pop": 2987, "state": "MT", "_id": "59828"} -{"city": "DARBY", "loc": [-114.193784, 46.028033], "pop": 1657, "state": "MT", "_id": "59829"} -{"city": "DIXON", "loc": [-114.30557, 47.31313], "pop": 454, "state": "MT", "_id": "59831"} -{"city": "DRUMMOND", "loc": [-113.242649, 46.664676], "pop": 800, "state": "MT", "_id": "59832"} -{"city": "FLORENCE", "loc": [-114.094487, 46.63102], "pop": 3184, "state": "MT", "_id": "59833"} -{"city": "FRENCHTOWN", "loc": [-114.268308, 47.047112], "pop": 1679, "state": "MT", "_id": "59834"} -{"city": "GREENOUGH", "loc": [-113.427017, 46.943361], "pop": 372, "state": "MT", "_id": "59836"} -{"city": "HALL", "loc": [-113.208725, 46.582539], "pop": 276, "state": "MT", "_id": "59837"} -{"city": "HAMILTON", "loc": [-114.167869, 46.23953], "pop": 9548, "state": "MT", "_id": "59840"} -{"city": "HELMVILLE", "loc": [-112.941296, 46.829968], "pop": 155, "state": "MT", "_id": "59843"} -{"city": "HERON", "loc": [-115.940668, 48.053668], "pop": 559, "state": "MT", "_id": "59844"} -{"city": "HOT SPRINGS", "loc": [-114.659712, 47.591408], "pop": 858, "state": "MT", "_id": "59845"} -{"city": "HUSON", "loc": [-114.421939, 47.065953], "pop": 562, "state": "MT", "_id": "59846"} -{"city": "LOLO", "loc": [-114.109732, 46.7585], "pop": 3783, "state": "MT", "_id": "59847"} -{"city": "LONEPINE", "loc": [-114.63718, 47.711209], "pop": 236, "state": "MT", "_id": "59848"} -{"city": "NIARADA", "loc": [-114.656218, 47.8031], "pop": 58, "state": "MT", "_id": "59852"} -{"city": "NOXON", "loc": [-115.780658, 48.030166], "pop": 530, "state": "MT", "_id": "59853"} -{"city": "OVANDO", "loc": [-113.090528, 47.006719], "pop": 362, "state": "MT", "_id": "59854"} -{"city": "PHILIPSBURG", "loc": [-113.3126, 46.318899], "pop": 1445, "state": "MT", "_id": "59858"} -{"city": "PLAINS", "loc": [-114.893014, 47.473448], "pop": 2590, "state": "MT", "_id": "59859"} -{"city": "POLSON", "loc": [-114.140444, 47.687574], "pop": 6294, "state": "MT", "_id": "59860"} -{"city": "RONAN", "loc": [-114.105385, 47.552457], "pop": 5682, "state": "MT", "_id": "59864"} -{"city": "SAINT IGNATIUS", "loc": [-114.075822, 47.330014], "pop": 2283, "state": "MT", "_id": "59865"} -{"city": "SAINT REGIS", "loc": [-115.170323, 47.336899], "pop": 962, "state": "MT", "_id": "59866"} -{"city": "SEELEY LAKE", "loc": [-113.481019, 47.178928], "pop": 1240, "state": "MT", "_id": "59868"} -{"city": "STEVENSVILLE", "loc": [-114.047846, 46.526723], "pop": 5250, "state": "MT", "_id": "59870"} -{"city": "SULA", "loc": [-114.042968, 45.827701], "pop": 422, "state": "MT", "_id": "59871"} -{"city": "SUPERIOR", "loc": [-114.888483, 47.172103], "pop": 1825, "state": "MT", "_id": "59872"} -{"city": "THOMPSON FALLS", "loc": [-115.360236, 47.601572], "pop": 2311, "state": "MT", "_id": "59873"} -{"city": "TROUT CREEK", "loc": [-115.559185, 47.811138], "pop": 1095, "state": "MT", "_id": "59874"} -{"city": "VICTOR", "loc": [-114.166534, 46.400489], "pop": 2211, "state": "MT", "_id": "59875"} -{"city": "EVERGREEN", "loc": [-114.289163, 48.220939], "pop": 33469, "state": "MT", "_id": "59901"} -{"city": "BIG ARM", "loc": [-114.207049, 47.758514], "pop": 1089, "state": "MT", "_id": "59910"} -{"city": "SWAN LAKE", "loc": [-114.01993, 48.039725], "pop": 5119, "state": "MT", "_id": "59911"} -{"city": "COLUMBIA FALLS", "loc": [-114.178394, 48.353394], "pop": 9275, "state": "MT", "_id": "59912"} -{"city": "DAYTON", "loc": [-114.280918, 47.860749], "pop": 226, "state": "MT", "_id": "59914"} -{"city": "ELMO", "loc": [-114.343938, 47.818541], "pop": 243, "state": "MT", "_id": "59915"} -{"city": "ESSEX", "loc": [-113.946678, 48.494028], "pop": 98, "state": "MT", "_id": "59916"} -{"city": "EUREKA", "loc": [-115.004938, 48.842766], "pop": 3747, "state": "MT", "_id": "59917"} -{"city": "KILA", "loc": [-114.510402, 48.074437], "pop": 578, "state": "MT", "_id": "59920"} -{"city": "LAKESIDE", "loc": [-114.226562, 48.021469], "pop": 1027, "state": "MT", "_id": "59922"} -{"city": "LIBBY", "loc": [-115.539101, 48.377311], "pop": 10148, "state": "MT", "_id": "59923"} -{"city": "MARION", "loc": [-114.744625, 48.083596], "pop": 475, "state": "MT", "_id": "59925"} -{"city": "POLEBRIDGE", "loc": [-114.383558, 48.820585], "pop": 97, "state": "MT", "_id": "59928"} -{"city": "PROCTOR", "loc": [-114.383193, 47.940371], "pop": 66, "state": "MT", "_id": "59929"} -{"city": "REXFORD", "loc": [-115.212859, 48.917947], "pop": 440, "state": "MT", "_id": "59930"} -{"city": "ROLLINS", "loc": [-114.224986, 47.918207], "pop": 205, "state": "MT", "_id": "59931"} -{"city": "SOMERS", "loc": [-114.23548, 48.079329], "pop": 1104, "state": "MT", "_id": "59932"} -{"city": "TROY", "loc": [-115.881684, 48.479119], "pop": 3146, "state": "MT", "_id": "59935"} -{"city": "WHITEFISH", "loc": [-114.350859, 48.403999], "pop": 9837, "state": "MT", "_id": "59937"} -{"city": "ABIE", "loc": [-96.956282, 41.347901], "pop": 282, "state": "NE", "_id": "68001"} -{"city": "ARLINGTON", "loc": [-96.306974, 41.441654], "pop": 2075, "state": "NE", "_id": "68002"} -{"city": "ASHLAND", "loc": [-96.39041, 41.05411], "pop": 3296, "state": "NE", "_id": "68003"} -{"city": "BANCROFT", "loc": [-96.617146, 42.026716], "pop": 913, "state": "NE", "_id": "68004"} -{"city": "BELLEVUE", "loc": [-95.909932, 41.149663], "pop": 24133, "state": "NE", "_id": "68005"} -{"city": "BENNINGTON", "loc": [-96.157525, 41.362262], "pop": 1372, "state": "NE", "_id": "68007"} -{"city": "BLAIR", "loc": [-96.161666, 41.545376], "pop": 9059, "state": "NE", "_id": "68008"} -{"city": "BRUNO", "loc": [-96.964612, 41.271771], "pop": 366, "state": "NE", "_id": "68014"} -{"city": "CEDAR BLUFFS", "loc": [-96.569115, 41.383492], "pop": 1911, "state": "NE", "_id": "68015"} -{"city": "CERESCO", "loc": [-96.639769, 41.06807], "pop": 1237, "state": "NE", "_id": "68017"} -{"city": "COLON", "loc": [-96.614132, 41.288261], "pop": 290, "state": "NE", "_id": "68018"} -{"city": "CRAIG", "loc": [-96.392398, 41.771497], "pop": 574, "state": "NE", "_id": "68019"} -{"city": "DECATUR", "loc": [-96.259456, 41.996553], "pop": 975, "state": "NE", "_id": "68020"} -{"city": "ELKHORN", "loc": [-96.24308, 41.275647], "pop": 8508, "state": "NE", "_id": "68022"} -{"city": "FORT CALHOUN", "loc": [-96.032375, 41.437281], "pop": 2833, "state": "NE", "_id": "68023"} -{"city": "FREMONT", "loc": [-96.494468, 41.441637], "pop": 26498, "state": "NE", "_id": "68025"} -{"city": "GRETNA", "loc": [-96.245837, 41.134458], "pop": 3236, "state": "NE", "_id": "68028"} -{"city": "HERMAN", "loc": [-96.286942, 41.652381], "pop": 988, "state": "NE", "_id": "68029"} -{"city": "HOOPER", "loc": [-96.523231, 41.641371], "pop": 1821, "state": "NE", "_id": "68031"} -{"city": "ITHACA", "loc": [-96.529785, 41.174849], "pop": 359, "state": "NE", "_id": "68033"} -{"city": "KENNARD", "loc": [-96.161927, 41.471139], "pop": 1652, "state": "NE", "_id": "68034"} -{"city": "LESHARA", "loc": [-96.449805, 41.346597], "pop": 492, "state": "NE", "_id": "68035"} -{"city": "LINWOOD", "loc": [-96.939873, 41.412926], "pop": 175, "state": "NE", "_id": "68036"} -{"city": "LOUISVILLE", "loc": [-96.194817, 40.996667], "pop": 1998, "state": "NE", "_id": "68037"} -{"city": "LYONS", "loc": [-96.466136, 41.94416], "pop": 1613, "state": "NE", "_id": "68038"} -{"city": "MACY", "loc": [-96.358865, 42.117739], "pop": 1464, "state": "NE", "_id": "68039"} -{"city": "MALMO", "loc": [-96.732718, 41.298424], "pop": 584, "state": "NE", "_id": "68040"} -{"city": "MEAD", "loc": [-96.496141, 41.23929], "pop": 786, "state": "NE", "_id": "68041"} -{"city": "NICKERSON", "loc": [-96.490423, 41.523293], "pop": 691, "state": "NE", "_id": "68044"} -{"city": "OAKLAND", "loc": [-96.467064, 41.838426], "pop": 1855, "state": "NE", "_id": "68045"} -{"city": "PAPILLION", "loc": [-96.037052, 41.152257], "pop": 7177, "state": "NE", "_id": "68046"} -{"city": "PENDER", "loc": [-96.718907, 42.117719], "pop": 1818, "state": "NE", "_id": "68047"} -{"city": "PLATTSMOUTH", "loc": [-95.913933, 40.999194], "pop": 10387, "state": "NE", "_id": "68048"} -{"city": "PRAGUE", "loc": [-96.830084, 41.303598], "pop": 819, "state": "NE", "_id": "68050"} -{"city": "RICHFIELD", "loc": [-96.045627, 41.090522], "pop": 822, "state": "NE", "_id": "68054"} -{"city": "ROSALIE", "loc": [-96.492866, 42.057435], "pop": 424, "state": "NE", "_id": "68055"} -{"city": "SCRIBNER", "loc": [-96.644095, 41.663035], "pop": 1485, "state": "NE", "_id": "68057"} -{"city": "SPRINGFIELD", "loc": [-96.143811, 41.07649], "pop": 2961, "state": "NE", "_id": "68059"} -{"city": "TEKAMAH", "loc": [-96.228144, 41.781898], "pop": 2881, "state": "NE", "_id": "68061"} -{"city": "THURSTON", "loc": [-96.690356, 42.188161], "pop": 362, "state": "NE", "_id": "68062"} -{"city": "VALLEY", "loc": [-96.346288, 41.318581], "pop": 3067, "state": "NE", "_id": "68064"} -{"city": "VALPARAISO", "loc": [-96.809149, 41.084304], "pop": 1095, "state": "NE", "_id": "68065"} -{"city": "WAHOO", "loc": [-96.621946, 41.211723], "pop": 4258, "state": "NE", "_id": "68066"} -{"city": "WALTHILL", "loc": [-96.480294, 42.148543], "pop": 1212, "state": "NE", "_id": "68067"} -{"city": "WATERLOO", "loc": [-96.306318, 41.27024], "pop": 1468, "state": "NE", "_id": "68069"} -{"city": "WESTON", "loc": [-96.769075, 41.181083], "pop": 834, "state": "NE", "_id": "68070"} -{"city": "WINNEBAGO", "loc": [-96.468507, 42.233851], "pop": 1449, "state": "NE", "_id": "68071"} -{"city": "YUTAN", "loc": [-96.393247, 41.23403], "pop": 1838, "state": "NE", "_id": "68073"} -{"city": "OMAHA", "loc": [-95.940909, 41.258961], "pop": 4963, "state": "NE", "_id": "68102"} -{"city": "OMAHA", "loc": [-95.999888, 41.29186], "pop": 35325, "state": "NE", "_id": "68104"} -{"city": "OMAHA", "loc": [-95.962938, 41.243502], "pop": 23007, "state": "NE", "_id": "68105"} -{"city": "OMAHA", "loc": [-95.997972, 41.240322], "pop": 20622, "state": "NE", "_id": "68106"} -{"city": "OMAHA", "loc": [-95.955877, 41.206783], "pop": 23890, "state": "NE", "_id": "68107"} -{"city": "OMAHA", "loc": [-95.933557, 41.238198], "pop": 12721, "state": "NE", "_id": "68108"} -{"city": "OMAHA", "loc": [-95.936072, 41.293342], "pop": 8718, "state": "NE", "_id": "68110"} -{"city": "OMAHA", "loc": [-95.965045, 41.296212], "pop": 28453, "state": "NE", "_id": "68111"} -{"city": "OMAHA", "loc": [-95.959684, 41.329614], "pop": 12075, "state": "NE", "_id": "68112"} -{"city": "OFFUTT A F B", "loc": [-95.907601, 41.114755], "pop": 2894, "state": "NE", "_id": "68113"} -{"city": "OMAHA", "loc": [-96.049306, 41.265624], "pop": 16573, "state": "NE", "_id": "68114"} -{"city": "OMAHA", "loc": [-96.149462, 41.287854], "pop": 875, "state": "NE", "_id": "68116"} -{"city": "OMAHA", "loc": [-95.995301, 41.206403], "pop": 8347, "state": "NE", "_id": "68117"} -{"city": "OMAHA", "loc": [-96.166118, 41.260636], "pop": 3593, "state": "NE", "_id": "68118"} -{"city": "OMAHA", "loc": [-96.045772, 41.333312], "pop": 2556, "state": "NE", "_id": "68122"} -{"city": "OMAHA", "loc": [-95.95599, 41.122265], "pop": 20362, "state": "NE", "_id": "68123"} -{"city": "OMAHA", "loc": [-96.049515, 41.233814], "pop": 16340, "state": "NE", "_id": "68124"} -{"city": "RALSTON", "loc": [-96.055019, 41.201782], "pop": 22434, "state": "NE", "_id": "68127"} -{"city": "PAPILLION", "loc": [-96.040256, 41.171983], "pop": 15882, "state": "NE", "_id": "68128"} -{"city": "OMAHA", "loc": [-96.168815, 41.235452], "pop": 7291, "state": "NE", "_id": "68130"} -{"city": "OMAHA", "loc": [-95.963891, 41.264658], "pop": 14069, "state": "NE", "_id": "68131"} -{"city": "OMAHA", "loc": [-95.995954, 41.265746], "pop": 13730, "state": "NE", "_id": "68132"} -{"city": "PAPILLION", "loc": [-96.013076, 41.141564], "pop": 842, "state": "NE", "_id": "68133"} -{"city": "OMAHA", "loc": [-96.054569, 41.294917], "pop": 27571, "state": "NE", "_id": "68134"} -{"city": "OMAHA", "loc": [-96.169827, 41.210419], "pop": 2503, "state": "NE", "_id": "68135"} -{"city": "OMAHA", "loc": [-96.209633, 41.168343], "pop": 226, "state": "NE", "_id": "68136"} -{"city": "MILLARD", "loc": [-96.124462, 41.201067], "pop": 23894, "state": "NE", "_id": "68137"} -{"city": "PAPILLION", "loc": [-96.129718, 41.177724], "pop": 8845, "state": "NE", "_id": "68138"} -{"city": "OMAHA", "loc": [-96.090109, 41.335904], "pop": 1249, "state": "NE", "_id": "68142"} -{"city": "MILLARD", "loc": [-96.116772, 41.235599], "pop": 26450, "state": "NE", "_id": "68144"} -{"city": "OMAHA", "loc": [-95.959156, 41.181508], "pop": 9205, "state": "NE", "_id": "68147"} -{"city": "OMAHA", "loc": [-96.000295, 41.334557], "pop": 6518, "state": "NE", "_id": "68152"} -{"city": "OMAHA", "loc": [-96.120611, 41.264167], "pop": 21847, "state": "NE", "_id": "68154"} -{"city": "PAPILLION", "loc": [-95.995378, 41.183423], "pop": 6112, "state": "NE", "_id": "68157"} -{"city": "OMAHA", "loc": [-96.100793, 41.29552], "pop": 16301, "state": "NE", "_id": "68164"} -{"city": "ADAMS", "loc": [-96.53956, 40.457571], "pop": 1127, "state": "NE", "_id": "68301"} -{"city": "ALEXANDRIA", "loc": [-97.403855, 40.261446], "pop": 429, "state": "NE", "_id": "68303"} -{"city": "ALVO", "loc": [-96.403557, 40.899185], "pop": 447, "state": "NE", "_id": "68304"} -{"city": "AUBURN", "loc": [-95.852646, 40.378889], "pop": 4296, "state": "NE", "_id": "68305"} -{"city": "AVOCA", "loc": [-96.095736, 40.815941], "pop": 444, "state": "NE", "_id": "68307"} -{"city": "BEATRICE", "loc": [-96.743494, 40.270509], "pop": 15528, "state": "NE", "_id": "68310"} -{"city": "BEAVER CROSSING", "loc": [-97.291356, 40.788658], "pop": 827, "state": "NE", "_id": "68313"} -{"city": "BEE", "loc": [-97.074469, 41.000675], "pop": 447, "state": "NE", "_id": "68314"} -{"city": "BELVIDERE", "loc": [-97.555416, 40.25101], "pop": 233, "state": "NE", "_id": "68315"} -{"city": "BENEDICT", "loc": [-97.602921, 41.003268], "pop": 646, "state": "NE", "_id": "68316"} -{"city": "BENNET", "loc": [-96.5134, 40.63822], "pop": 1611, "state": "NE", "_id": "68317"} -{"city": "BLUE SPRINGS", "loc": [-96.659037, 40.140684], "pop": 504, "state": "NE", "_id": "68318"} -{"city": "BRADSHAW", "loc": [-97.760699, 40.920718], "pop": 716, "state": "NE", "_id": "68319"} -{"city": "BROCK", "loc": [-95.980145, 40.477227], "pop": 400, "state": "NE", "_id": "68320"} -{"city": "BROWNVILLE", "loc": [-95.69348, 40.399447], "pop": 475, "state": "NE", "_id": "68321"} -{"city": "BRUNING", "loc": [-97.557276, 40.330157], "pop": 496, "state": "NE", "_id": "68322"} -{"city": "BURCHARD", "loc": [-96.348806, 40.105743], "pop": 507, "state": "NE", "_id": "68323"} -{"city": "BURR", "loc": [-96.238401, 40.560057], "pop": 500, "state": "NE", "_id": "68324"} -{"city": "BYRON", "loc": [-97.761236, 40.02688], "pop": 357, "state": "NE", "_id": "68325"} -{"city": "CARLETON", "loc": [-97.671909, 40.300534], "pop": 323, "state": "NE", "_id": "68326"} -{"city": "CHESTER", "loc": [-97.61969, 40.028954], "pop": 527, "state": "NE", "_id": "68327"} -{"city": "CLATONIA", "loc": [-96.855513, 40.47236], "pop": 502, "state": "NE", "_id": "68328"} -{"city": "COOK", "loc": [-96.15262, 40.498628], "pop": 592, "state": "NE", "_id": "68329"} -{"city": "CORDOVA", "loc": [-97.340721, 40.718477], "pop": 241, "state": "NE", "_id": "68330"} -{"city": "CORTLAND", "loc": [-96.716627, 40.497599], "pop": 691, "state": "NE", "_id": "68331"} -{"city": "CRAB ORCHARD", "loc": [-96.411569, 40.316648], "pop": 169, "state": "NE", "_id": "68332"} -{"city": "CRETE", "loc": [-96.956676, 40.619302], "pop": 6390, "state": "NE", "_id": "68333"} -{"city": "DAVENPORT", "loc": [-97.80496, 40.310782], "pop": 488, "state": "NE", "_id": "68335"} -{"city": "DAVEY", "loc": [-96.641379, 41.002543], "pop": 413, "state": "NE", "_id": "68336"} -{"city": "DAWSON", "loc": [-95.834097, 40.137697], "pop": 359, "state": "NE", "_id": "68337"} -{"city": "DAYKIN", "loc": [-97.304306, 40.31916], "pop": 324, "state": "NE", "_id": "68338"} -{"city": "DENTON", "loc": [-96.853157, 40.700905], "pop": 1035, "state": "NE", "_id": "68339"} -{"city": "DESHLER", "loc": [-97.730007, 40.138668], "pop": 1108, "state": "NE", "_id": "68340"} -{"city": "DE WITT", "loc": [-96.933766, 40.39438], "pop": 821, "state": "NE", "_id": "68341"} -{"city": "DILLER", "loc": [-96.949487, 40.119201], "pop": 507, "state": "NE", "_id": "68342"} -{"city": "DORCHESTER", "loc": [-97.105562, 40.649858], "pop": 832, "state": "NE", "_id": "68343"} -{"city": "DOUGLAS", "loc": [-96.396958, 40.583824], "pop": 369, "state": "NE", "_id": "68344"} -{"city": "DU BOIS", "loc": [-96.057477, 40.037486], "pop": 315, "state": "NE", "_id": "68345"} -{"city": "DUNBAR", "loc": [-96.013575, 40.659156], "pop": 619, "state": "NE", "_id": "68346"} -{"city": "EAGLE", "loc": [-96.428965, 40.816909], "pop": 1561, "state": "NE", "_id": "68347"} -{"city": "ELK CREEK", "loc": [-96.142128, 40.2977], "pop": 356, "state": "NE", "_id": "68348"} -{"city": "ELMWOOD", "loc": [-96.294395, 40.837811], "pop": 879, "state": "NE", "_id": "68349"} -{"city": "ENDICOTT", "loc": [-97.093787, 40.073407], "pop": 282, "state": "NE", "_id": "68350"} -{"city": "EXETER", "loc": [-97.442388, 40.634062], "pop": 934, "state": "NE", "_id": "68351"} -{"city": "FAIRBURY", "loc": [-97.183918, 40.148817], "pop": 5763, "state": "NE", "_id": "68352"} -{"city": "FAIRMONT", "loc": [-97.58729, 40.6404], "pop": 968, "state": "NE", "_id": "68354"} -{"city": "FALLS CITY", "loc": [-95.593148, 40.074193], "pop": 6069, "state": "NE", "_id": "68355"} -{"city": "FILLEY", "loc": [-96.530132, 40.294606], "pop": 352, "state": "NE", "_id": "68357"} -{"city": "FIRTH", "loc": [-96.614023, 40.558595], "pop": 1095, "state": "NE", "_id": "68358"} -{"city": "FRIEND", "loc": [-97.273928, 40.636792], "pop": 1707, "state": "NE", "_id": "68359"} -{"city": "GARLAND", "loc": [-96.97019, 40.941189], "pop": 795, "state": "NE", "_id": "68360"} -{"city": "GENEVA", "loc": [-97.609625, 40.527731], "pop": 3224, "state": "NE", "_id": "68361"} -{"city": "GILEAD", "loc": [-97.42714, 40.146986], "pop": 169, "state": "NE", "_id": "68362"} -{"city": "GOEHNER", "loc": [-97.205456, 40.830208], "pop": 457, "state": "NE", "_id": "68364"} -{"city": "GRAFTON", "loc": [-97.740263, 40.640193], "pop": 302, "state": "NE", "_id": "68365"} -{"city": "GREENWOOD", "loc": [-96.42584, 40.97064], "pop": 813, "state": "NE", "_id": "68366"} -{"city": "GRESHAM", "loc": [-97.407937, 41.020952], "pop": 426, "state": "NE", "_id": "68367"} -{"city": "HALLAM", "loc": [-96.754518, 40.554734], "pop": 663, "state": "NE", "_id": "68368"} -{"city": "HEBRON", "loc": [-97.605228, 40.172807], "pop": 2305, "state": "NE", "_id": "68370"} -{"city": "HENDERSON", "loc": [-97.797865, 40.781374], "pop": 1615, "state": "NE", "_id": "68371"} -{"city": "HOLLAND", "loc": [-96.624924, 40.624558], "pop": 1495, "state": "NE", "_id": "68372"} -{"city": "HOLMESVILLE", "loc": [-96.632635, 40.220245], "pop": 353, "state": "NE", "_id": "68374"} -{"city": "HUBBELL", "loc": [-97.473467, 40.045581], "pop": 200, "state": "NE", "_id": "68375"} -{"city": "HUMBOLDT", "loc": [-95.931079, 40.156595], "pop": 1806, "state": "NE", "_id": "68376"} -{"city": "JANSEN", "loc": [-97.024426, 40.207425], "pop": 579, "state": "NE", "_id": "68377"} -{"city": "JOHNSON", "loc": [-95.988276, 40.401623], "pop": 747, "state": "NE", "_id": "68378"} -{"city": "JULIAN", "loc": [-95.85046, 40.486056], "pop": 313, "state": "NE", "_id": "68379"} -{"city": "LEWISTON", "loc": [-96.404941, 40.230879], "pop": 189, "state": "NE", "_id": "68380"} -{"city": "LIBERTY", "loc": [-96.523668, 40.075286], "pop": 422, "state": "NE", "_id": "68381"} -{"city": "MC COOL JUNCTION", "loc": [-97.593666, 40.744181], "pop": 820, "state": "NE", "_id": "68401"} -{"city": "MALCOLM", "loc": [-96.859966, 40.90913], "pop": 954, "state": "NE", "_id": "68402"} -{"city": "MARTELL", "loc": [-96.744227, 40.651434], "pop": 864, "state": "NE", "_id": "68404"} -{"city": "MILFORD", "loc": [-97.05763, 40.763153], "pop": 3258, "state": "NE", "_id": "68405"} -{"city": "MILLIGAN", "loc": [-97.399684, 40.495163], "pop": 477, "state": "NE", "_id": "68406"} -{"city": "MURDOCK", "loc": [-96.284827, 40.919034], "pop": 622, "state": "NE", "_id": "68407"} -{"city": "MURRAY", "loc": [-95.922668, 40.91999], "pop": 1145, "state": "NE", "_id": "68409"} -{"city": "NEBRASKA CITY", "loc": [-95.8619, 40.674746], "pop": 7668, "state": "NE", "_id": "68410"} -{"city": "NEHAWKA", "loc": [-95.993045, 40.832978], "pop": 423, "state": "NE", "_id": "68413"} -{"city": "NEMAHA", "loc": [-95.69133, 40.319818], "pop": 428, "state": "NE", "_id": "68414"} -{"city": "ODELL", "loc": [-96.801924, 40.045128], "pop": 642, "state": "NE", "_id": "68415"} -{"city": "OHIOWA", "loc": [-97.442712, 40.406507], "pop": 274, "state": "NE", "_id": "68416"} -{"city": "OTOE", "loc": [-96.132867, 40.735485], "pop": 696, "state": "NE", "_id": "68417"} -{"city": "PALMYRA", "loc": [-96.399903, 40.704964], "pop": 1012, "state": "NE", "_id": "68418"} -{"city": "PAWNEE CITY", "loc": [-96.150926, 40.109291], "pop": 1434, "state": "NE", "_id": "68420"} -{"city": "PERU", "loc": [-95.731166, 40.476643], "pop": 1321, "state": "NE", "_id": "68421"} -{"city": "PICKRELL", "loc": [-96.734444, 40.382117], "pop": 512, "state": "NE", "_id": "68422"} -{"city": "PLEASANT DALE", "loc": [-96.95128, 40.813329], "pop": 678, "state": "NE", "_id": "68423"} -{"city": "PLYMOUTH", "loc": [-97.001169, 40.303896], "pop": 872, "state": "NE", "_id": "68424"} -{"city": "AGNEW", "loc": [-96.782942, 40.985331], "pop": 1218, "state": "NE", "_id": "68428"} -{"city": "REYNOLDS", "loc": [-97.318243, 40.059372], "pop": 184, "state": "NE", "_id": "68429"} -{"city": "ROCA", "loc": [-96.639085, 40.670195], "pop": 517, "state": "NE", "_id": "68430"} -{"city": "RULO", "loc": [-95.429218, 40.053619], "pop": 312, "state": "NE", "_id": "68431"} -{"city": "SAINT MARY", "loc": [-96.289972, 40.444756], "pop": 259, "state": "NE", "_id": "68432"} -{"city": "SALEM", "loc": [-95.727261, 40.061953], "pop": 280, "state": "NE", "_id": "68433"} -{"city": "SEWARD", "loc": [-97.096618, 40.906609], "pop": 7082, "state": "NE", "_id": "68434"} -{"city": "SHICKLEY", "loc": [-97.714298, 40.407677], "pop": 722, "state": "NE", "_id": "68436"} -{"city": "SHUBERT", "loc": [-95.689454, 40.232479], "pop": 320, "state": "NE", "_id": "68437"} -{"city": "STAPLEHURST", "loc": [-97.185859, 40.984455], "pop": 597, "state": "NE", "_id": "68439"} -{"city": "STEELE CITY", "loc": [-96.990712, 40.042492], "pop": 248, "state": "NE", "_id": "68440"} -{"city": "STEINAUER", "loc": [-96.230215, 40.216885], "pop": 378, "state": "NE", "_id": "68441"} -{"city": "STELLA", "loc": [-95.767989, 40.230345], "pop": 342, "state": "NE", "_id": "68442"} -{"city": "STERLING", "loc": [-96.386655, 40.463743], "pop": 840, "state": "NE", "_id": "68443"} -{"city": "STRANG", "loc": [-97.552132, 40.398105], "pop": 202, "state": "NE", "_id": "68444"} -{"city": "SWANTON", "loc": [-97.08055, 40.384389], "pop": 266, "state": "NE", "_id": "68445"} -{"city": "SYRACUSE", "loc": [-96.182407, 40.661386], "pop": 1937, "state": "NE", "_id": "68446"} -{"city": "TABLE ROCK", "loc": [-96.081822, 40.187437], "pop": 494, "state": "NE", "_id": "68447"} -{"city": "TALMAGE", "loc": [-96.013814, 40.558646], "pop": 731, "state": "NE", "_id": "68448"} -{"city": "TECUMSEH", "loc": [-96.204951, 40.369702], "pop": 2457, "state": "NE", "_id": "68450"} -{"city": "ONG", "loc": [-97.861018, 40.396806], "pop": 150, "state": "NE", "_id": "68452"} -{"city": "TOBIAS", "loc": [-97.318444, 40.426171], "pop": 464, "state": "NE", "_id": "68453"} -{"city": "UNADILLA", "loc": [-96.282454, 40.691675], "pop": 720, "state": "NE", "_id": "68454"} -{"city": "UNION", "loc": [-95.903739, 40.824452], "pop": 689, "state": "NE", "_id": "68455"} -{"city": "UTICA", "loc": [-97.334431, 40.91685], "pop": 1068, "state": "NE", "_id": "68456"} -{"city": "VERDON", "loc": [-95.716157, 40.142507], "pop": 449, "state": "NE", "_id": "68457"} -{"city": "VIRGINIA", "loc": [-96.512145, 40.230877], "pop": 226, "state": "NE", "_id": "68458"} -{"city": "WACO", "loc": [-97.453352, 40.919826], "pop": 613, "state": "NE", "_id": "68460"} -{"city": "WALTON", "loc": [-96.535943, 40.797467], "pop": 268, "state": "NE", "_id": "68461"} -{"city": "WAVERLY", "loc": [-96.525988, 40.92224], "pop": 2545, "state": "NE", "_id": "68462"} -{"city": "WEEPING WATER", "loc": [-96.152825, 40.873096], "pop": 1910, "state": "NE", "_id": "68463"} -{"city": "WESTERN", "loc": [-97.197649, 40.415157], "pop": 552, "state": "NE", "_id": "68464"} -{"city": "WILBER", "loc": [-96.975713, 40.482141], "pop": 2016, "state": "NE", "_id": "68465"} -{"city": "WYMORE", "loc": [-96.661326, 40.112955], "pop": 1935, "state": "NE", "_id": "68466"} -{"city": "YORK", "loc": [-97.582482, 40.866678], "pop": 9592, "state": "NE", "_id": "68467"} -{"city": "LINCOLN", "loc": [-96.693763, 40.789282], "pop": 27576, "state": "NE", "_id": "68502"} -{"city": "LINCOLN", "loc": [-96.676623, 40.823339], "pop": 14093, "state": "NE", "_id": "68503"} -{"city": "LINCOLN", "loc": [-96.653248, 40.839226], "pop": 12756, "state": "NE", "_id": "68504"} -{"city": "LINCOLN", "loc": [-96.625193, 40.824674], "pop": 13461, "state": "NE", "_id": "68505"} -{"city": "LINCOLN", "loc": [-96.643052, 40.784796], "pop": 25903, "state": "NE", "_id": "68506"} -{"city": "LINCOLN", "loc": [-96.628874, 40.847265], "pop": 12296, "state": "NE", "_id": "68507"} -{"city": "LINCOLN", "loc": [-96.700907, 40.814503], "pop": 13716, "state": "NE", "_id": "68508"} -{"city": "LINCOLN", "loc": [-96.654458, 40.806345], "pop": 20948, "state": "NE", "_id": "68510"} -{"city": "LINCOLN", "loc": [-96.694606, 40.756487], "pop": 6139, "state": "NE", "_id": "68512"} -{"city": "LINCOLN", "loc": [-96.661082, 40.925792], "pop": 200, "state": "NE", "_id": "68514"} -{"city": "LINCOLN", "loc": [-96.652304, 40.756807], "pop": 21213, "state": "NE", "_id": "68516"} -{"city": "LINCOLN", "loc": [-96.604509, 40.931743], "pop": 485, "state": "NE", "_id": "68517"} -{"city": "LINCOLN", "loc": [-96.569341, 40.774441], "pop": 1460, "state": "NE", "_id": "68520"} -{"city": "LINCOLN", "loc": [-96.711006, 40.851044], "pop": 16067, "state": "NE", "_id": "68521"} -{"city": "LINCOLN", "loc": [-96.747871, 40.793407], "pop": 4991, "state": "NE", "_id": "68522"} -{"city": "LINCOLN", "loc": [-96.758339, 40.740766], "pop": 685, "state": "NE", "_id": "68523"} -{"city": "LINCOLN", "loc": [-96.794345, 40.852913], "pop": 4321, "state": "NE", "_id": "68524"} -{"city": "LINCOLN", "loc": [-96.587817, 40.731386], "pop": 526, "state": "NE", "_id": "68526"} -{"city": "LINCOLN", "loc": [-96.540053, 40.834708], "pop": 617, "state": "NE", "_id": "68527"} -{"city": "LINCOLN", "loc": [-96.754496, 40.819541], "pop": 2476, "state": "NE", "_id": "68528"} -{"city": "LINCOLN", "loc": [-96.715572, 40.899397], "pop": 310, "state": "NE", "_id": "68531"} -{"city": "LINCOLN", "loc": [-96.85509, 40.792159], "pop": 391, "state": "NE", "_id": "68532"} -{"city": "RICHLAND", "loc": [-97.356469, 41.437225], "pop": 23684, "state": "NE", "_id": "68601"} -{"city": "ALBION", "loc": [-97.999116, 41.704947], "pop": 3788, "state": "NE", "_id": "68620"} -{"city": "AMES", "loc": [-96.646344, 41.461253], "pop": 141, "state": "NE", "_id": "68621"} -{"city": "BARTLETT", "loc": [-98.556668, 41.8697], "pop": 262, "state": "NE", "_id": "68622"} -{"city": "BELGRADE", "loc": [-98.086638, 41.461077], "pop": 268, "state": "NE", "_id": "68623"} -{"city": "BELLWOOD", "loc": [-97.274657, 41.347389], "pop": 1199, "state": "NE", "_id": "68624"} -{"city": "BRAINARD", "loc": [-96.988226, 41.183151], "pop": 570, "state": "NE", "_id": "68626"} -{"city": "CEDAR RAPIDS", "loc": [-98.15538, 41.556407], "pop": 728, "state": "NE", "_id": "68627"} -{"city": "CLARKS", "loc": [-97.846143, 41.232797], "pop": 749, "state": "NE", "_id": "68628"} -{"city": "CLARKSON", "loc": [-97.105059, 41.696064], "pop": 1235, "state": "NE", "_id": "68629"} -{"city": "CRESTON", "loc": [-97.368692, 41.660695], "pop": 910, "state": "NE", "_id": "68631"} -{"city": "GARRISON", "loc": [-97.126229, 41.237315], "pop": 3798, "state": "NE", "_id": "68632"} -{"city": "DODGE", "loc": [-96.818077, 41.681609], "pop": 2151, "state": "NE", "_id": "68633"} -{"city": "DWIGHT", "loc": [-96.993143, 41.089388], "pop": 462, "state": "NE", "_id": "68635"} -{"city": "ELGIN", "loc": [-98.075082, 41.973239], "pop": 1367, "state": "NE", "_id": "68636"} -{"city": "ERICSON", "loc": [-98.645323, 41.782804], "pop": 311, "state": "NE", "_id": "68637"} -{"city": "FULLERTON", "loc": [-98.005362, 41.366042], "pop": 2087, "state": "NE", "_id": "68638"} -{"city": "GENOA", "loc": [-97.764011, 41.446776], "pop": 2116, "state": "NE", "_id": "68640"} -{"city": "HOWELLS", "loc": [-96.985811, 41.696441], "pop": 1122, "state": "NE", "_id": "68641"} -{"city": "HUMPHREY", "loc": [-97.498649, 41.670184], "pop": 1818, "state": "NE", "_id": "68642"} -{"city": "LEIGH", "loc": [-97.232001, 41.673877], "pop": 1225, "state": "NE", "_id": "68643"} -{"city": "LINDSAY", "loc": [-97.671134, 41.692634], "pop": 973, "state": "NE", "_id": "68644"} -{"city": "MONROE", "loc": [-97.606075, 41.478255], "pop": 799, "state": "NE", "_id": "68647"} -{"city": "MORSE BLUFF", "loc": [-96.786171, 41.417831], "pop": 486, "state": "NE", "_id": "68648"} -{"city": "NORTH BEND", "loc": [-96.781328, 41.468924], "pop": 1651, "state": "NE", "_id": "68649"} -{"city": "OCTAVIA", "loc": [-97.07021, 41.355323], "pop": 361, "state": "NE", "_id": "68650"} -{"city": "OSCEOLA", "loc": [-97.557079, 41.196565], "pop": 1398, "state": "NE", "_id": "68651"} -{"city": "PETERSBURG", "loc": [-98.084791, 41.858954], "pop": 784, "state": "NE", "_id": "68652"} -{"city": "PLATTE CENTER", "loc": [-97.457616, 41.524114], "pop": 1399, "state": "NE", "_id": "68653"} -{"city": "POLK", "loc": [-97.752372, 41.117671], "pop": 988, "state": "NE", "_id": "68654"} -{"city": "PRIMROSE", "loc": [-98.235455, 41.64054], "pop": 255, "state": "NE", "_id": "68655"} -{"city": "RISING CITY", "loc": [-97.303174, 41.208219], "pop": 697, "state": "NE", "_id": "68658"} -{"city": "ROGERS", "loc": [-96.949735, 41.469536], "pop": 208, "state": "NE", "_id": "68659"} -{"city": "SAINT EDWARD", "loc": [-97.880116, 41.57076], "pop": 1112, "state": "NE", "_id": "68660"} -{"city": "SCHUYLER", "loc": [-97.062832, 41.459128], "pop": 5243, "state": "NE", "_id": "68661"} -{"city": "SHELBY", "loc": [-97.429536, 41.243544], "pop": 1495, "state": "NE", "_id": "68662"} -{"city": "SILVER CREEK", "loc": [-97.667106, 41.318999], "pop": 627, "state": "NE", "_id": "68663"} -{"city": "SPALDING", "loc": [-98.371468, 41.687409], "pop": 1053, "state": "NE", "_id": "68665"} -{"city": "STROMSBURG", "loc": [-97.574183, 41.111834], "pop": 1765, "state": "NE", "_id": "68666"} -{"city": "ULYSSES", "loc": [-97.309013, 41.089922], "pop": 250, "state": "NE", "_id": "68667"} -{"city": "ULYSSES", "loc": [-97.198388, 41.079059], "pop": 440, "state": "NE", "_id": "68669"} -{"city": "NORFOLK", "loc": [-97.422898, 42.032914], "pop": 25728, "state": "NE", "_id": "68701"} -{"city": "ALLEN", "loc": [-96.8574, 42.443667], "pop": 1007, "state": "NE", "_id": "68710"} -{"city": "AMELIA", "loc": [-99.008036, 42.18088], "pop": 196, "state": "NE", "_id": "68711"} -{"city": "ATKINSON", "loc": [-98.976087, 42.548279], "pop": 2321, "state": "NE", "_id": "68713"} -{"city": "BASSETT", "loc": [-99.538732, 42.576293], "pop": 1344, "state": "NE", "_id": "68714"} -{"city": "BATTLE CREEK", "loc": [-97.598153, 41.994283], "pop": 1448, "state": "NE", "_id": "68715"} -{"city": "BEEMER", "loc": [-96.815008, 41.937422], "pop": 948, "state": "NE", "_id": "68716"} -{"city": "BELDEN", "loc": [-97.195628, 42.402185], "pop": 346, "state": "NE", "_id": "68717"} -{"city": "BLOOMFIELD", "loc": [-97.654494, 42.597824], "pop": 1873, "state": "NE", "_id": "68718"} -{"city": "BRISTOW", "loc": [-98.60267, 42.880575], "pop": 355, "state": "NE", "_id": "68719"} -{"city": "BRUNSWICK", "loc": [-97.944008, 42.351023], "pop": 907, "state": "NE", "_id": "68720"} -{"city": "BUTTE", "loc": [-98.845903, 42.912377], "pop": 667, "state": "NE", "_id": "68722"} -{"city": "CARROLL", "loc": [-97.192612, 42.277009], "pop": 576, "state": "NE", "_id": "68723"} -{"city": "CENTER", "loc": [-97.883567, 42.602451], "pop": 264, "state": "NE", "_id": "68724"} -{"city": "CHAMBERS", "loc": [-98.737846, 42.191606], "pop": 815, "state": "NE", "_id": "68725"} -{"city": "CLEARWATER", "loc": [-98.186761, 42.126562], "pop": 919, "state": "NE", "_id": "68726"} -{"city": "COLERIDGE", "loc": [-97.178564, 42.522992], "pop": 1185, "state": "NE", "_id": "68727"} -{"city": "CONCORD", "loc": [-96.981035, 42.381982], "pop": 262, "state": "NE", "_id": "68728"} -{"city": "CREIGHTON", "loc": [-97.89323, 42.468053], "pop": 1604, "state": "NE", "_id": "68729"} -{"city": "CROFTON", "loc": [-97.540589, 42.737028], "pop": 1717, "state": "NE", "_id": "68730"} -{"city": "DAKOTA CITY", "loc": [-96.453608, 42.38074], "pop": 3022, "state": "NE", "_id": "68731"} -{"city": "DIXON", "loc": [-96.977381, 42.41853], "pop": 226, "state": "NE", "_id": "68732"} -{"city": "EMERSON", "loc": [-96.71587, 42.285247], "pop": 1471, "state": "NE", "_id": "68733"} -{"city": "EMMET", "loc": [-98.823777, 42.474708], "pop": 190, "state": "NE", "_id": "68734"} -{"city": "EWING", "loc": [-98.398137, 42.185107], "pop": 1300, "state": "NE", "_id": "68735"} -{"city": "FORDYCE", "loc": [-97.356665, 42.730889], "pop": 1059, "state": "NE", "_id": "68736"} -{"city": "FOSTER", "loc": [-97.658038, 42.269932], "pop": 216, "state": "NE", "_id": "68737"} -{"city": "HARTINGTON", "loc": [-97.283678, 42.623494], "pop": 2806, "state": "NE", "_id": "68739"} -{"city": "HOSKINS", "loc": [-97.308435, 42.140839], "pop": 784, "state": "NE", "_id": "68740"} -{"city": "HUBBARD", "loc": [-96.622425, 42.413139], "pop": 705, "state": "NE", "_id": "68741"} -{"city": "INMAN", "loc": [-98.538366, 42.376149], "pop": 439, "state": "NE", "_id": "68742"} -{"city": "JACKSON", "loc": [-96.574335, 42.452877], "pop": 406, "state": "NE", "_id": "68743"} -{"city": "LAUREL", "loc": [-97.087381, 42.427036], "pop": 1519, "state": "NE", "_id": "68745"} -{"city": "LYNCH", "loc": [-98.450433, 42.837275], "pop": 496, "state": "NE", "_id": "68746"} -{"city": "MCLEAN", "loc": [-97.475113, 42.392082], "pop": 232, "state": "NE", "_id": "68747"} -{"city": "MADISON", "loc": [-97.47195, 41.830786], "pop": 3203, "state": "NE", "_id": "68748"} -{"city": "MAGNET", "loc": [-97.440722, 42.475294], "pop": 218, "state": "NE", "_id": "68749"} -{"city": "MASKELL", "loc": [-96.966978, 42.670245], "pop": 218, "state": "NE", "_id": "68751"} -{"city": "MEADOW GROVE", "loc": [-97.733426, 42.010184], "pop": 939, "state": "NE", "_id": "68752"} -{"city": "MILLS", "loc": [-99.446647, 42.922527], "pop": 329, "state": "NE", "_id": "68753"} -{"city": "NAPER", "loc": [-99.071027, 42.952091], "pop": 521, "state": "NE", "_id": "68755"} -{"city": "NELIGH", "loc": [-98.01507, 42.138926], "pop": 2375, "state": "NE", "_id": "68756"} -{"city": "NEWCASTLE", "loc": [-96.87085, 42.620672], "pop": 763, "state": "NE", "_id": "68757"} -{"city": "NEWMAN GROVE", "loc": [-97.773966, 41.74977], "pop": 1482, "state": "NE", "_id": "68758"} -{"city": "NEWPORT", "loc": [-99.335887, 42.600089], "pop": 344, "state": "NE", "_id": "68759"} -{"city": "VERDEL", "loc": [-97.912734, 42.776942], "pop": 1051, "state": "NE", "_id": "68760"} -{"city": "OAKDALE", "loc": [-97.918644, 42.053585], "pop": 1016, "state": "NE", "_id": "68761"} -{"city": "OBERT", "loc": [-97.070709, 42.65767], "pop": 204, "state": "NE", "_id": "68762"} -{"city": "ONEILL", "loc": [-98.645565, 42.485733], "pop": 5782, "state": "NE", "_id": "68763"} -{"city": "ORCHARD", "loc": [-98.240853, 42.33987], "pop": 1044, "state": "NE", "_id": "68764"} -{"city": "OSMOND", "loc": [-97.581998, 42.353936], "pop": 1418, "state": "NE", "_id": "68765"} -{"city": "PAGE", "loc": [-98.39639, 42.411882], "pop": 544, "state": "NE", "_id": "68766"} -{"city": "PIERCE", "loc": [-97.525604, 42.194323], "pop": 2942, "state": "NE", "_id": "68767"} -{"city": "PILGER", "loc": [-97.268472, 42.040413], "pop": 3122, "state": "NE", "_id": "68768"} -{"city": "PLAINVIEW", "loc": [-97.778615, 42.346701], "pop": 1978, "state": "NE", "_id": "68769"} -{"city": "PONCA", "loc": [-96.712793, 42.56931], "pop": 1316, "state": "NE", "_id": "68770"} -{"city": "RANDOLPH", "loc": [-97.346443, 42.379778], "pop": 1826, "state": "NE", "_id": "68771"} -{"city": "ROSE", "loc": [-99.444755, 42.256782], "pop": 331, "state": "NE", "_id": "68772"} -{"city": "ROYAL", "loc": [-98.124626, 42.294614], "pop": 479, "state": "NE", "_id": "68773"} -{"city": "SAINT HELENA", "loc": [-97.358616, 42.818145], "pop": 888, "state": "NE", "_id": "68774"} -{"city": "SOUTH SIOUX CITY", "loc": [-96.418161, 42.465615], "pop": 12037, "state": "NE", "_id": "68776"} -{"city": "SPENCER", "loc": [-98.705853, 42.884936], "pop": 796, "state": "NE", "_id": "68777"} -{"city": "SPRINGVIEW", "loc": [-99.806148, 42.848785], "pop": 700, "state": "NE", "_id": "68778"} -{"city": "STANTON", "loc": [-97.213955, 41.907559], "pop": 3122, "state": "NE", "_id": "68779"} -{"city": "STUART", "loc": [-99.139563, 42.571307], "pop": 1331, "state": "NE", "_id": "68780"} -{"city": "TILDEN", "loc": [-97.822297, 42.049629], "pop": 883, "state": "NE", "_id": "68781"} -{"city": "68782", "loc": [-98.17297, 42.78358], "pop": 246, "state": "NE", "_id": "68782"} -{"city": "VERDIGRE", "loc": [-98.079278, 42.610111], "pop": 1184, "state": "NE", "_id": "68783"} -{"city": "WAKEFIELD", "loc": [-96.877645, 42.273271], "pop": 1858, "state": "NE", "_id": "68784"} -{"city": "WATERBURY", "loc": [-96.74478, 42.460431], "pop": 241, "state": "NE", "_id": "68785"} -{"city": "WAUSA", "loc": [-97.557616, 42.497265], "pop": 1058, "state": "NE", "_id": "68786"} -{"city": "WAYNE", "loc": [-97.018579, 42.230439], "pop": 6454, "state": "NE", "_id": "68787"} -{"city": "WEST POINT", "loc": [-96.731763, 41.84503], "pop": 6463, "state": "NE", "_id": "68788"} -{"city": "WINNETOON", "loc": [-98.025408, 42.494565], "pop": 395, "state": "NE", "_id": "68789"} -{"city": "WINSIDE", "loc": [-97.182505, 42.167817], "pop": 808, "state": "NE", "_id": "68790"} -{"city": "WISNER", "loc": [-96.916975, 41.997994], "pop": 1793, "state": "NE", "_id": "68791"} -{"city": "WYNOT", "loc": [-97.167809, 42.739304], "pop": 609, "state": "NE", "_id": "68792"} -{"city": "GRAND ISLAND", "loc": [-98.341062, 40.921858], "pop": 24173, "state": "NE", "_id": "68801"} -{"city": "GRAND ISLAND", "loc": [-98.387271, 40.928608], "pop": 19337, "state": "NE", "_id": "68803"} -{"city": "ALDA", "loc": [-98.455135, 40.856923], "pop": 872, "state": "NE", "_id": "68810"} -{"city": "AMHERST", "loc": [-99.260949, 40.849494], "pop": 560, "state": "NE", "_id": "68812"} -{"city": "MILBURN", "loc": [-99.799149, 41.636214], "pop": 566, "state": "NE", "_id": "68813"} -{"city": "ANSLEY", "loc": [-99.36452, 41.30193], "pop": 853, "state": "NE", "_id": "68814"} -{"city": "ARCADIA", "loc": [-99.120468, 41.429305], "pop": 609, "state": "NE", "_id": "68815"} -{"city": "ARCHER", "loc": [-98.118151, 41.178156], "pop": 237, "state": "NE", "_id": "68816"} -{"city": "ASHTON", "loc": [-98.803352, 41.267184], "pop": 505, "state": "NE", "_id": "68817"} -{"city": "AURORA", "loc": [-98.020107, 40.852838], "pop": 5954, "state": "NE", "_id": "68818"} -{"city": "BERWYN", "loc": [-99.501573, 41.347015], "pop": 318, "state": "NE", "_id": "68819"} -{"city": "BOELUS", "loc": [-98.697551, 41.100278], "pop": 533, "state": "NE", "_id": "68820"} -{"city": "BREWSTER", "loc": [-99.829196, 41.946705], "pop": 246, "state": "NE", "_id": "68821"} -{"city": "BROKEN BOW", "loc": [-99.635452, 41.412586], "pop": 4864, "state": "NE", "_id": "68822"} -{"city": "BURWELL", "loc": [-99.099527, 41.807982], "pop": 2141, "state": "NE", "_id": "68823"} -{"city": "CAIRO", "loc": [-98.616538, 41.000183], "pop": 976, "state": "NE", "_id": "68824"} -{"city": "CALLAWAY", "loc": [-99.993185, 41.248499], "pop": 1100, "state": "NE", "_id": "68825"} -{"city": "CENTRAL CITY", "loc": [-98.001693, 41.121259], "pop": 3783, "state": "NE", "_id": "68826"} -{"city": "CHAPMAN", "loc": [-98.221723, 40.985592], "pop": 1503, "state": "NE", "_id": "68827"} -{"city": "COMSTOCK", "loc": [-99.275281, 41.555254], "pop": 331, "state": "NE", "_id": "68828"} -{"city": "COTESFIELD", "loc": [-98.655344, 41.34301], "pop": 216, "state": "NE", "_id": "68829"} -{"city": "DANNEBROG", "loc": [-98.554564, 41.119156], "pop": 890, "state": "NE", "_id": "68831"} -{"city": "DONIPHAN", "loc": [-98.37901, 40.770031], "pop": 1496, "state": "NE", "_id": "68832"} -{"city": "DUNNING", "loc": [-100.087271, 41.813107], "pop": 225, "state": "NE", "_id": "68833"} -{"city": "EDDYVILLE", "loc": [-99.681009, 41.0079], "pop": 229, "state": "NE", "_id": "68834"} -{"city": "ELBA", "loc": [-98.575622, 41.28644], "pop": 310, "state": "NE", "_id": "68835"} -{"city": "ELM CREEK", "loc": [-99.372786, 40.730079], "pop": 1256, "state": "NE", "_id": "68836"} -{"city": "ELYRIA", "loc": [-99.046656, 41.695655], "pop": 305, "state": "NE", "_id": "68837"} -{"city": "FARWELL", "loc": [-98.648144, 41.220378], "pop": 313, "state": "NE", "_id": "68838"} -{"city": "GIBBON", "loc": [-98.85435, 40.744445], "pop": 1901, "state": "NE", "_id": "68840"} -{"city": "GILTNER", "loc": [-98.14344, 40.765402], "pop": 522, "state": "NE", "_id": "68841"} -{"city": "GREELEY", "loc": [-98.529951, 41.552578], "pop": 832, "state": "NE", "_id": "68842"} -{"city": "HAMPTON", "loc": [-97.884146, 40.923609], "pop": 897, "state": "NE", "_id": "68843"} -{"city": "HAZARD", "loc": [-99.071859, 41.093368], "pop": 285, "state": "NE", "_id": "68844"} -{"city": "HORDVILLE", "loc": [-97.888145, 41.081253], "pop": 405, "state": "NE", "_id": "68846"} -{"city": "KEARNEY", "loc": [-99.077883, 40.713608], "pop": 28674, "state": "NE", "_id": "68847"} -{"city": "LEXINGTON", "loc": [-99.751515, 40.785002], "pop": 8970, "state": "NE", "_id": "68850"} -{"city": "LITCHFIELD", "loc": [-99.141452, 41.168639], "pop": 629, "state": "NE", "_id": "68852"} -{"city": "LOUP CITY", "loc": [-98.975149, 41.284531], "pop": 1810, "state": "NE", "_id": "68853"} -{"city": "MARQUETTE", "loc": [-97.999955, 41.010017], "pop": 563, "state": "NE", "_id": "68854"} -{"city": "MASON CITY", "loc": [-99.304937, 41.185319], "pop": 473, "state": "NE", "_id": "68855"} -{"city": "MERNA", "loc": [-99.803595, 41.443482], "pop": 913, "state": "NE", "_id": "68856"} -{"city": "MILLER", "loc": [-99.373987, 40.942236], "pop": 337, "state": "NE", "_id": "68858"} -{"city": "NORTH LOUP", "loc": [-98.785836, 41.49717], "pop": 626, "state": "NE", "_id": "68859"} -{"city": "OCONTO", "loc": [-99.695112, 41.138075], "pop": 539, "state": "NE", "_id": "68860"} -{"city": "ODESSA", "loc": [-99.254082, 40.709726], "pop": 379, "state": "NE", "_id": "68861"} -{"city": "ORD", "loc": [-98.941783, 41.596187], "pop": 3629, "state": "NE", "_id": "68862"} -{"city": "OVERTON", "loc": [-99.527824, 40.751875], "pop": 1090, "state": "NE", "_id": "68863"} -{"city": "PALMER", "loc": [-98.241146, 41.178757], "pop": 1142, "state": "NE", "_id": "68864"} -{"city": "PHILLIPS", "loc": [-98.21286, 40.898197], "pop": 517, "state": "NE", "_id": "68865"} -{"city": "PLEASANTON", "loc": [-99.128278, 40.981848], "pop": 654, "state": "NE", "_id": "68866"} -{"city": "PROSSER", "loc": [-98.559471, 40.659175], "pop": 297, "state": "NE", "_id": "68868"} -{"city": "RAVENNA", "loc": [-98.904129, 41.023271], "pop": 1646, "state": "NE", "_id": "68869"} -{"city": "RIVERDALE", "loc": [-99.138147, 40.762684], "pop": 784, "state": "NE", "_id": "68870"} -{"city": "ROCKVILLE", "loc": [-98.857757, 41.110832], "pop": 489, "state": "NE", "_id": "68871"} -{"city": "SAINT LIBORY", "loc": [-98.35888, 41.08669], "pop": 705, "state": "NE", "_id": "68872"} -{"city": "SAINT PAUL", "loc": [-98.443987, 41.224212], "pop": 3088, "state": "NE", "_id": "68873"} -{"city": "SARGENT", "loc": [-99.381624, 41.650845], "pop": 1042, "state": "NE", "_id": "68874"} -{"city": "SCOTIA", "loc": [-98.689256, 41.483724], "pop": 711, "state": "NE", "_id": "68875"} -{"city": "SHELTON", "loc": [-98.743453, 40.771703], "pop": 1256, "state": "NE", "_id": "68876"} -{"city": "SUMNER", "loc": [-99.519951, 40.950384], "pop": 448, "state": "NE", "_id": "68878"} -{"city": "ALMERIA", "loc": [-99.415404, 41.822859], "pop": 683, "state": "NE", "_id": "68879"} -{"city": "WESTERVILLE", "loc": [-99.384369, 41.419282], "pop": 172, "state": "NE", "_id": "68881"} -{"city": "WOLBACH", "loc": [-98.399456, 41.424373], "pop": 466, "state": "NE", "_id": "68882"} -{"city": "WOOD RIVER", "loc": [-98.606509, 40.810635], "pop": 2071, "state": "NE", "_id": "68883"} -{"city": "HASTINGS", "loc": [-98.391146, 40.587654], "pop": 25562, "state": "NE", "_id": "68901"} -{"city": "ALMA", "loc": [-99.360073, 40.118853], "pop": 1587, "state": "NE", "_id": "68920"} -{"city": "ARAPAHOE", "loc": [-99.899697, 40.302662], "pop": 1246, "state": "NE", "_id": "68922"} -{"city": "ATLANTA", "loc": [-99.484354, 40.384901], "pop": 261, "state": "NE", "_id": "68923"} -{"city": "AXTELL", "loc": [-99.116903, 40.526907], "pop": 1547, "state": "NE", "_id": "68924"} -{"city": "AYR", "loc": [-98.438982, 40.441054], "pop": 616, "state": "NE", "_id": "68925"} -{"city": "BEAVER CITY", "loc": [-99.806532, 40.129036], "pop": 959, "state": "NE", "_id": "68926"} -{"city": "BERTRAND", "loc": [-99.575891, 40.560843], "pop": 1566, "state": "NE", "_id": "68927"} -{"city": "BLADEN", "loc": [-98.604625, 40.298996], "pop": 579, "state": "NE", "_id": "68928"} -{"city": "BLOOMINGTON", "loc": [-99.009438, 40.138257], "pop": 431, "state": "NE", "_id": "68929"} -{"city": "BLUE HILL", "loc": [-98.426955, 40.31102], "pop": 1291, "state": "NE", "_id": "68930"} -{"city": "CAMPBELL", "loc": [-98.737012, 40.296684], "pop": 525, "state": "NE", "_id": "68932"} -{"city": "CLAY CENTER", "loc": [-98.038649, 40.511301], "pop": 1115, "state": "NE", "_id": "68933"} -{"city": "DEWEESE", "loc": [-98.177199, 40.379234], "pop": 342, "state": "NE", "_id": "68934"} -{"city": "EDGAR", "loc": [-97.972679, 40.365166], "pop": 795, "state": "NE", "_id": "68935"} -{"city": "EDISON", "loc": [-99.785988, 40.280176], "pop": 246, "state": "NE", "_id": "68936"} -{"city": "ELWOOD", "loc": [-99.825812, 40.574738], "pop": 956, "state": "NE", "_id": "68937"} -{"city": "FAIRFIELD", "loc": [-98.106272, 40.428511], "pop": 569, "state": "NE", "_id": "68938"} -{"city": "FRANKLIN", "loc": [-98.946862, 40.105172], "pop": 1305, "state": "NE", "_id": "68939"} -{"city": "FUNK", "loc": [-99.244992, 40.502031], "pop": 558, "state": "NE", "_id": "68940"} -{"city": "GLENVIL", "loc": [-98.246475, 40.493145], "pop": 447, "state": "NE", "_id": "68941"} -{"city": "GUIDE ROCK", "loc": [-98.339064, 40.081186], "pop": 530, "state": "NE", "_id": "68942"} -{"city": "HARDY", "loc": [-97.927057, 40.028452], "pop": 462, "state": "NE", "_id": "68943"} -{"city": "HARVARD", "loc": [-98.084574, 40.626463], "pop": 1400, "state": "NE", "_id": "68944"} -{"city": "HEARTWELL", "loc": [-98.78514, 40.571601], "pop": 197, "state": "NE", "_id": "68945"} -{"city": "HENDLEY", "loc": [-99.971537, 40.106312], "pop": 217, "state": "NE", "_id": "68946"} -{"city": "HILDRETH", "loc": [-99.057128, 40.321743], "pop": 663, "state": "NE", "_id": "68947"} -{"city": "HOLBROOK", "loc": [-100.013593, 40.301893], "pop": 369, "state": "NE", "_id": "68948"} -{"city": "HOLDREGE", "loc": [-99.367233, 40.447527], "pop": 6744, "state": "NE", "_id": "68949"} -{"city": "HOLSTEIN", "loc": [-98.653777, 40.454169], "pop": 413, "state": "NE", "_id": "68950"} -{"city": "HUNTLEY", "loc": [-99.284786, 40.210254], "pop": 89, "state": "NE", "_id": "68951"} -{"city": "INAVALE", "loc": [-98.661191, 40.095886], "pop": 249, "state": "NE", "_id": "68952"} -{"city": "INLAND", "loc": [-98.223379, 40.589934], "pop": 113, "state": "NE", "_id": "68954"} -{"city": "JUNIATA", "loc": [-98.515017, 40.586652], "pop": 1058, "state": "NE", "_id": "68955"} -{"city": "KENESAW", "loc": [-98.657241, 40.61645], "pop": 1076, "state": "NE", "_id": "68956"} -{"city": "LAWRENCE", "loc": [-98.240243, 40.27672], "pop": 594, "state": "NE", "_id": "68957"} -{"city": "LOOMIS", "loc": [-99.497851, 40.479155], "pop": 586, "state": "NE", "_id": "68958"} -{"city": "MINDEN", "loc": [-98.938297, 40.509142], "pop": 4365, "state": "NE", "_id": "68959"} -{"city": "NAPONEE", "loc": [-99.127725, 40.125807], "pop": 361, "state": "NE", "_id": "68960"} -{"city": "NORA", "loc": [-98.058417, 40.209739], "pop": 1084, "state": "NE", "_id": "68961"} -{"city": "OAK", "loc": [-97.884114, 40.260489], "pop": 312, "state": "NE", "_id": "68964"} -{"city": "ORLEANS", "loc": [-99.457186, 40.148435], "pop": 760, "state": "NE", "_id": "68966"} -{"city": "OXFORD", "loc": [-99.630197, 40.256049], "pop": 1306, "state": "NE", "_id": "68967"} -{"city": "RAGAN", "loc": [-99.24891, 40.309594], "pop": 148, "state": "NE", "_id": "68969"} -{"city": "RED CLOUD", "loc": [-98.518655, 40.09516], "pop": 1630, "state": "NE", "_id": "68970"} -{"city": "REPUBLICAN CITY", "loc": [-99.232189, 40.103401], "pop": 429, "state": "NE", "_id": "68971"} -{"city": "RIVERTON", "loc": [-98.785819, 40.101626], "pop": 394, "state": "NE", "_id": "68972"} -{"city": "ROSELAND", "loc": [-98.555059, 40.459058], "pop": 603, "state": "NE", "_id": "68973"} -{"city": "RUSKIN", "loc": [-97.87203, 40.138318], "pop": 324, "state": "NE", "_id": "68974"} -{"city": "SARONVILLE", "loc": [-97.87385, 40.600625], "pop": 1704, "state": "NE", "_id": "68975"} -{"city": "SMITHFIELD", "loc": [-99.823862, 40.583567], "pop": 972, "state": "NE", "_id": "68976"} -{"city": "STAMFORD", "loc": [-99.581405, 40.11625], "pop": 396, "state": "NE", "_id": "68977"} -{"city": "SUPERIOR", "loc": [-98.077872, 40.031537], "pop": 2902, "state": "NE", "_id": "68978"} -{"city": "SUTTON", "loc": [-97.873476, 40.653955], "pop": 215, "state": "NE", "_id": "68979"} -{"city": "TRUMBULL", "loc": [-98.257414, 40.66918], "pop": 381, "state": "NE", "_id": "68980"} -{"city": "UPLAND", "loc": [-98.896577, 40.317073], "pop": 259, "state": "NE", "_id": "68981"} -{"city": "WILCOX", "loc": [-99.153927, 40.373523], "pop": 520, "state": "NE", "_id": "68982"} -{"city": "MC COOK", "loc": [-100.627948, 40.204905], "pop": 9513, "state": "NE", "_id": "69001"} -{"city": "BARTLEY", "loc": [-100.29075, 40.258113], "pop": 519, "state": "NE", "_id": "69020"} -{"city": "BENKELMAN", "loc": [-101.534354, 40.098049], "pop": 1813, "state": "NE", "_id": "69021"} -{"city": "CAMBRIDGE", "loc": [-100.167579, 40.280767], "pop": 1347, "state": "NE", "_id": "69022"} -{"city": "CHAMPION", "loc": [-101.74849, 40.460039], "pop": 288, "state": "NE", "_id": "69023"} -{"city": "CULBERTSON", "loc": [-100.850043, 40.223707], "pop": 1560, "state": "NE", "_id": "69024"} -{"city": "CURTIS", "loc": [-100.510406, 40.613067], "pop": 1095, "state": "NE", "_id": "69025"} -{"city": "DANBURY", "loc": [-100.424228, 40.037689], "pop": 283, "state": "NE", "_id": "69026"} -{"city": "ENDERS", "loc": [-101.522156, 40.465492], "pop": 217, "state": "NE", "_id": "69027"} -{"city": "EUSTIS", "loc": [-100.054694, 40.626745], "pop": 829, "state": "NE", "_id": "69028"} -{"city": "FARNAM", "loc": [-100.206878, 40.712881], "pop": 243, "state": "NE", "_id": "69029"} -{"city": "HAIGLER", "loc": [-101.937106, 40.064179], "pop": 398, "state": "NE", "_id": "69030"} -{"city": "HAMLET", "loc": [-101.234106, 40.399363], "pop": 222, "state": "NE", "_id": "69031"} -{"city": "HAYES CENTER", "loc": [-101.025208, 40.517316], "pop": 1000, "state": "NE", "_id": "69032"} -{"city": "IMPERIAL", "loc": [-101.646775, 40.525124], "pop": 2372, "state": "NE", "_id": "69033"} -{"city": "INDIANOLA", "loc": [-100.429758, 40.235718], "pop": 1213, "state": "NE", "_id": "69034"} -{"city": "LAMAR", "loc": [-101.903297, 40.538653], "pop": 613, "state": "NE", "_id": "69035"} -{"city": "LEBANON", "loc": [-100.26122, 40.07522], "pop": 182, "state": "NE", "_id": "69036"} -{"city": "MAX", "loc": [-101.391607, 40.106884], "pop": 164, "state": "NE", "_id": "69037"} -{"city": "MAYWOOD", "loc": [-100.642135, 40.589262], "pop": 689, "state": "NE", "_id": "69038"} -{"city": "MOOREFIELD", "loc": [-100.310773, 40.57856], "pop": 355, "state": "NE", "_id": "69039"} -{"city": "PALISADE", "loc": [-101.129464, 40.338668], "pop": 544, "state": "NE", "_id": "69040"} -{"city": "PARKS", "loc": [-101.739879, 40.139837], "pop": 207, "state": "NE", "_id": "69041"} -{"city": "STOCKVILLE", "loc": [-100.384886, 40.494834], "pop": 128, "state": "NE", "_id": "69042"} -{"city": "STRATTON", "loc": [-101.218275, 40.144462], "pop": 776, "state": "NE", "_id": "69043"} -{"city": "TRENTON", "loc": [-101.020073, 40.168096], "pop": 870, "state": "NE", "_id": "69044"} -{"city": "WAUNETA", "loc": [-101.381195, 40.440906], "pop": 891, "state": "NE", "_id": "69045"} -{"city": "WILSONVILLE", "loc": [-100.1211, 40.108895], "pop": 264, "state": "NE", "_id": "69046"} -{"city": "NORTH PLATTE", "loc": [-100.774631, 41.132595], "pop": 26416, "state": "NE", "_id": "69101"} -{"city": "ARNOLD", "loc": [-100.156731, 41.445583], "pop": 1099, "state": "NE", "_id": "69120"} -{"city": "ARTHUR", "loc": [-101.693123, 41.573952], "pop": 462, "state": "NE", "_id": "69121"} -{"city": "BIG SPRINGS", "loc": [-102.093273, 41.069621], "pop": 792, "state": "NE", "_id": "69122"} -{"city": "BRADY", "loc": [-100.373649, 41.051525], "pop": 1243, "state": "NE", "_id": "69123"} -{"city": "BROADWATER", "loc": [-102.822521, 41.582547], "pop": 411, "state": "NE", "_id": "69125"} -{"city": "BRULE", "loc": [-101.909933, 41.100186], "pop": 792, "state": "NE", "_id": "69127"} -{"city": "BUSHNELL", "loc": [-103.907494, 41.213876], "pop": 355, "state": "NE", "_id": "69128"} -{"city": "CHAPPELL", "loc": [-102.452344, 41.096586], "pop": 1445, "state": "NE", "_id": "69129"} -{"city": "COZAD", "loc": [-99.992091, 40.861934], "pop": 5221, "state": "NE", "_id": "69130"} -{"city": "DALTON", "loc": [-102.972609, 41.406778], "pop": 511, "state": "NE", "_id": "69131"} -{"city": "DICKENS", "loc": [-101.01495, 40.804089], "pop": 132, "state": "NE", "_id": "69132"} -{"city": "DIX", "loc": [-103.479603, 41.226993], "pop": 426, "state": "NE", "_id": "69133"} -{"city": "ELSIE", "loc": [-101.369994, 40.859647], "pop": 448, "state": "NE", "_id": "69134"} -{"city": "ELSMERE", "loc": [-100.282412, 42.264974], "pop": 132, "state": "NE", "_id": "69135"} -{"city": "GOTHENBURG", "loc": [-100.154707, 40.940035], "pop": 3739, "state": "NE", "_id": "69138"} -{"city": "GRANT", "loc": [-101.719589, 40.851069], "pop": 1896, "state": "NE", "_id": "69140"} -{"city": "GURLEY", "loc": [-102.982325, 41.28771], "pop": 624, "state": "NE", "_id": "69141"} -{"city": "HALSEY", "loc": [-100.295515, 41.929095], "pop": 166, "state": "NE", "_id": "69142"} -{"city": "HERSHEY", "loc": [-101.001157, 41.155303], "pop": 1685, "state": "NE", "_id": "69143"} -{"city": "KEYSTONE", "loc": [-101.628218, 41.262129], "pop": 247, "state": "NE", "_id": "69144"} -{"city": "KIMBALL", "loc": [-103.660236, 41.23208], "pop": 3327, "state": "NE", "_id": "69145"} -{"city": "LEMOYNE", "loc": [-101.894677, 41.304017], "pop": 303, "state": "NE", "_id": "69146"} -{"city": "LEWELLEN", "loc": [-102.139622, 41.343461], "pop": 612, "state": "NE", "_id": "69147"} -{"city": "LISCO", "loc": [-102.54983, 41.511398], "pop": 214, "state": "NE", "_id": "69148"} -{"city": "LODGEPOLE", "loc": [-102.657034, 41.169745], "pop": 642, "state": "NE", "_id": "69149"} -{"city": "MADRID", "loc": [-101.537067, 40.85443], "pop": 546, "state": "NE", "_id": "69150"} -{"city": "MAXWELL", "loc": [-100.526993, 41.058831], "pop": 722, "state": "NE", "_id": "69151"} -{"city": "MULLEN", "loc": [-101.054185, 42.016292], "pop": 793, "state": "NE", "_id": "69152"} -{"city": "OGALLALA", "loc": [-101.710742, 41.127505], "pop": 6329, "state": "NE", "_id": "69153"} -{"city": "OSHKOSH", "loc": [-102.345699, 41.445057], "pop": 1634, "state": "NE", "_id": "69154"} -{"city": "PAXTON", "loc": [-101.358544, 41.126763], "pop": 913, "state": "NE", "_id": "69155"} -{"city": "POTTER", "loc": [-103.306112, 41.234688], "pop": 658, "state": "NE", "_id": "69156"} -{"city": "PURDUM", "loc": [-100.15685, 41.966475], "pop": 217, "state": "NE", "_id": "69157"} -{"city": "SENECA", "loc": [-100.807315, 41.99012], "pop": 169, "state": "NE", "_id": "69161"} -{"city": "SIDNEY", "loc": [-102.985573, 41.138001], "pop": 7059, "state": "NE", "_id": "69162"} -{"city": "STAPLETON", "loc": [-100.483105, 41.48877], "pop": 878, "state": "NE", "_id": "69163"} -{"city": "SUTHERLAND", "loc": [-101.136029, 41.15575], "pop": 1472, "state": "NE", "_id": "69165"} -{"city": "BROWNLEE", "loc": [-100.573834, 41.973575], "pop": 503, "state": "NE", "_id": "69166"} -{"city": "TRYON", "loc": [-101.017508, 41.573175], "pop": 546, "state": "NE", "_id": "69167"} -{"city": "VENANGO", "loc": [-101.983894, 40.807324], "pop": 433, "state": "NE", "_id": "69168"} -{"city": "WALLACE", "loc": [-101.173767, 40.830423], "pop": 571, "state": "NE", "_id": "69169"} -{"city": "WELLFLEET", "loc": [-100.711915, 40.798776], "pop": 311, "state": "NE", "_id": "69170"} -{"city": "VALENTINE", "loc": [-100.621542, 42.806166], "pop": 4504, "state": "NE", "_id": "69201"} -{"city": "AINSWORTH", "loc": [-99.861491, 42.54027], "pop": 2580, "state": "NE", "_id": "69210"} -{"city": "CODY", "loc": [-101.379846, 42.614556], "pop": 156, "state": "NE", "_id": "69211"} -{"city": "CROOKSTON", "loc": [-100.773473, 42.925506], "pop": 229, "state": "NE", "_id": "69212"} -{"city": "JOHNSTOWN", "loc": [-100.045322, 42.530857], "pop": 371, "state": "NE", "_id": "69214"} -{"city": "KILGORE", "loc": [-100.988447, 42.914587], "pop": 213, "state": "NE", "_id": "69216"} -{"city": "LONG PINE", "loc": [-99.723042, 42.533379], "pop": 706, "state": "NE", "_id": "69217"} -{"city": "MERRIMAN", "loc": [-101.758304, 42.641883], "pop": 873, "state": "NE", "_id": "69218"} -{"city": "WOOD LAKE", "loc": [-100.287175, 42.626394], "pop": 200, "state": "NE", "_id": "69221"} -{"city": "ALLIANCE", "loc": [-102.888045, 42.114943], "pop": 11903, "state": "NE", "_id": "69301"} -{"city": "ANGORA", "loc": [-103.085019, 41.893434], "pop": 84, "state": "NE", "_id": "69331"} -{"city": "ASHBY", "loc": [-101.963581, 41.977578], "pop": 151, "state": "NE", "_id": "69333"} -{"city": "BAYARD", "loc": [-103.301887, 41.757923], "pop": 2378, "state": "NE", "_id": "69334"} -{"city": "BINGHAM", "loc": [-102.133075, 42.260651], "pop": 218, "state": "NE", "_id": "69335"} -{"city": "BRIDGEPORT", "loc": [-103.070134, 41.676556], "pop": 2550, "state": "NE", "_id": "69336"} -{"city": "CHADRON", "loc": [-102.995331, 42.819268], "pop": 6661, "state": "NE", "_id": "69337"} -{"city": "CRAWFORD", "loc": [-103.405336, 42.67584], "pop": 1492, "state": "NE", "_id": "69339"} -{"city": "ELLSWORTH", "loc": [-102.47245, 42.169717], "pop": 310, "state": "NE", "_id": "69340"} -{"city": "GERING", "loc": [-103.662896, 41.821993], "pop": 11631, "state": "NE", "_id": "69341"} -{"city": "GORDON", "loc": [-102.204929, 42.806843], "pop": 2666, "state": "NE", "_id": "69343"} -{"city": "HARRISBURG", "loc": [-103.711141, 41.55306], "pop": 852, "state": "NE", "_id": "69345"} -{"city": "HARRISON", "loc": [-103.831803, 42.394594], "pop": 1525, "state": "NE", "_id": "69346"} -{"city": "HAY SPRINGS", "loc": [-102.675641, 42.640122], "pop": 1570, "state": "NE", "_id": "69347"} -{"city": "HEMINGFORD", "loc": [-103.064412, 42.33117], "pop": 1280, "state": "NE", "_id": "69348"} -{"city": "HENRY", "loc": [-104.034933, 41.993392], "pop": 208, "state": "NE", "_id": "69349"} -{"city": "HYANNIS", "loc": [-101.748296, 42.006967], "pop": 430, "state": "NE", "_id": "69350"} -{"city": "LAKESIDE", "loc": [-102.443655, 42.049595], "pop": 5, "state": "NE", "_id": "69351"} -{"city": "LYMAN", "loc": [-104.006569, 41.891765], "pop": 988, "state": "NE", "_id": "69352"} -{"city": "MARSLAND", "loc": [-103.051857, 42.598842], "pop": 605, "state": "NE", "_id": "69354"} -{"city": "MINATARE", "loc": [-103.489011, 41.849333], "pop": 2355, "state": "NE", "_id": "69356"} -{"city": "MITCHELL", "loc": [-103.795996, 41.945851], "pop": 2998, "state": "NE", "_id": "69357"} -{"city": "MORRILL", "loc": [-103.918216, 41.96807], "pop": 1469, "state": "NE", "_id": "69358"} -{"city": "RUSHVILLE", "loc": [-102.465738, 42.737934], "pop": 1955, "state": "NE", "_id": "69360"} -{"city": "SCOTTSBLUFF", "loc": [-103.661914, 41.871975], "pop": 16373, "state": "NE", "_id": "69361"} -{"city": "WHITMAN", "loc": [-101.521623, 41.958355], "pop": 188, "state": "NE", "_id": "69366"} -{"city": "WHITNEY", "loc": [-103.239552, 42.755881], "pop": 263, "state": "NE", "_id": "69367"} -{"city": "ALAMO", "loc": [-115.308032, 37.325866], "pop": 926, "state": "NV", "_id": "89001"} -{"city": "BOULDER CITY", "loc": [-114.834413, 35.972711], "pop": 12920, "state": "NV", "_id": "89005"} -{"city": "CALIENTE", "loc": [-114.509727, 37.612817], "pop": 1214, "state": "NV", "_id": "89008"} -{"city": "GOLDFIELD", "loc": [-117.269379, 37.834364], "pop": 712, "state": "NV", "_id": "89013"} -{"city": "HENDERSON", "loc": [-115.077968, 36.056435], "pop": 27761, "state": "NV", "_id": "89014"} -{"city": "HENDERSON", "loc": [-114.971809, 36.035705], "pop": 35694, "state": "NV", "_id": "89015"} -{"city": "HIKO", "loc": [-115.177158, 37.651695], "pop": 97, "state": "NV", "_id": "89017"} -{"city": "INDIAN SPRINGS", "loc": [-115.581067, 36.407223], "pop": 4114, "state": "NV", "_id": "89018"} -{"city": "GOODSPRINGS", "loc": [-115.469154, 35.854839], "pop": 1344, "state": "NV", "_id": "89019"} -{"city": "AMARGOSA VALLEY", "loc": [-116.57755, 36.944007], "pop": 3506, "state": "NV", "_id": "89020"} -{"city": "LAUGHLIN", "loc": [-114.636769, 35.132138], "pop": 4801, "state": "NV", "_id": "89029"} -{"city": "NORTH LAS VEGAS", "loc": [-114.851389, 36.4475], "pop": 16, "state": "NV", "_id": "89030"} -{"city": "NORTH LAS VEGAS", "loc": [-115.124832, 36.206228], "pop": 48113, "state": "NV", "_id": "89031"} -{"city": "OVERTON", "loc": [-114.378202, 36.637236], "pop": 8191, "state": "NV", "_id": "89040"} -{"city": "PAHRUMP", "loc": [-116.014661, 36.204039], "pop": 7440, "state": "NV", "_id": "89041"} -{"city": "PIOCHE", "loc": [-114.396824, 37.898097], "pop": 1538, "state": "NV", "_id": "89043"} -{"city": "ROUND MOUNTAIN", "loc": [-117.127989, 38.682886], "pop": 1302, "state": "NV", "_id": "89045"} -{"city": "COTTONWOOD COVE", "loc": [-114.924182, 35.447601], "pop": 948, "state": "NV", "_id": "89046"} -{"city": "SILVERPEAK", "loc": [-117.94592, 37.76444], "pop": 632, "state": "NV", "_id": "89047"} -{"city": "TONOPAH", "loc": [-117.089886, 38.204273], "pop": 4724, "state": "NV", "_id": "89049"} -{"city": "LAS VEGAS", "loc": [-115.122366, 36.172082], "pop": 40270, "state": "NV", "_id": "89101"} -{"city": "LAS VEGAS", "loc": [-115.200351, 36.143303], "pop": 48070, "state": "NV", "_id": "89102"} -{"city": "LAS VEGAS", "loc": [-115.216072, 36.114865], "pop": 33258, "state": "NV", "_id": "89103"} -{"city": "LAS VEGAS", "loc": [-115.109195, 36.15197], "pop": 26805, "state": "NV", "_id": "89104"} -{"city": "LAS VEGAS", "loc": [-115.161703, 36.184673], "pop": 21458, "state": "NV", "_id": "89106"} -{"city": "LAS VEGAS", "loc": [-115.217638, 36.170457], "pop": 32628, "state": "NV", "_id": "89107"} -{"city": "LAS VEGAS", "loc": [-115.223259, 36.204399], "pop": 46924, "state": "NV", "_id": "89108"} -{"city": "LAS VEGAS", "loc": [-115.145378, 36.125991], "pop": 35139, "state": "NV", "_id": "89109"} -{"city": "LAS VEGAS", "loc": [-115.066892, 36.173031], "pop": 43396, "state": "NV", "_id": "89110"} -{"city": "LAS VEGAS", "loc": [-115.256614, 36.085366], "pop": 1801, "state": "NV", "_id": "89113"} -{"city": "LAS VEGAS", "loc": [-115.067062, 36.215818], "pop": 51532, "state": "NV", "_id": "89115"} -{"city": "LAS VEGAS", "loc": [-115.275518, 36.130196], "pop": 30271, "state": "NV", "_id": "89117"} -{"city": "LAS VEGAS", "loc": [-115.216856, 36.081052], "pop": 9932, "state": "NV", "_id": "89118"} -{"city": "LAS VEGAS", "loc": [-115.136463, 36.100836], "pop": 38719, "state": "NV", "_id": "89119"} -{"city": "LAS VEGAS", "loc": [-115.088485, 36.091423], "pop": 16191, "state": "NV", "_id": "89120"} -{"city": "LAS VEGAS", "loc": [-115.090219, 36.12318], "pop": 50168, "state": "NV", "_id": "89121"} -{"city": "LAS VEGAS", "loc": [-115.052322, 36.120501], "pop": 27409, "state": "NV", "_id": "89122"} -{"city": "LAS VEGAS", "loc": [-115.146182, 36.038273], "pop": 6706, "state": "NV", "_id": "89123"} -{"city": "LAS VEGAS", "loc": [-115.095067, 35.963391], "pop": 2580, "state": "NV", "_id": "89124"} -{"city": "LAS VEGAS", "loc": [-115.256252, 36.175992], "pop": 18956, "state": "NV", "_id": "89128"} -{"city": "LAS VEGAS", "loc": [-115.274254, 36.245004], "pop": 5610, "state": "NV", "_id": "89129"} -{"city": "LAS VEGAS", "loc": [-115.221032, 36.247137], "pop": 4383, "state": "NV", "_id": "89130"} -{"city": "LAS VEGAS", "loc": [-115.241942, 36.295604], "pop": 1721, "state": "NV", "_id": "89131"} -{"city": "LAS VEGAS", "loc": [-115.294123, 36.209234], "pop": 3630, "state": "NV", "_id": "89134"} -{"city": "ELY", "loc": [-114.878011, 39.309356], "pop": 8558, "state": "NV", "_id": "89301"} -{"city": "AUSTIN", "loc": [-117.081063, 39.507986], "pop": 1026, "state": "NV", "_id": "89310"} -{"city": "BAKER", "loc": [-114.246165, 39.065996], "pop": 269, "state": "NV", "_id": "89311"} -{"city": "EUREKA", "loc": [-115.994308, 39.589661], "pop": 1118, "state": "NV", "_id": "89316"} -{"city": "LUND", "loc": [-115.044026, 38.904633], "pop": 437, "state": "NV", "_id": "89317"} -{"city": "DAYTON", "loc": [-119.52872, 39.280594], "pop": 2183, "state": "NV", "_id": "89403"} -{"city": "DENIO", "loc": [-118.731962, 41.704263], "pop": 199, "state": "NV", "_id": "89404"} -{"city": "EMPIRE", "loc": [-119.655265, 40.357703], "pop": 246, "state": "NV", "_id": "89405"} -{"city": "FALLON", "loc": [-118.786112, 39.470254], "pop": 17938, "state": "NV", "_id": "89406"} -{"city": "FERNLEY", "loc": [-119.235044, 39.601888], "pop": 5188, "state": "NV", "_id": "89408"} -{"city": "GABBS", "loc": [-117.868556, 38.880226], "pop": 809, "state": "NV", "_id": "89409"} -{"city": "GARDNERVILLE", "loc": [-119.726902, 38.898268], "pop": 15778, "state": "NV", "_id": "89410"} -{"city": "GERLACH", "loc": [-119.377441, 40.674645], "pop": 469, "state": "NV", "_id": "89412"} -{"city": "GLENBROOK", "loc": [-119.927776, 38.993752], "pop": 5705, "state": "NV", "_id": "89413"} -{"city": "GOLCONDA", "loc": [-117.334929, 40.968325], "pop": 411, "state": "NV", "_id": "89414"} -{"city": "HAWTHORNE", "loc": [-118.641143, 38.534658], "pop": 5172, "state": "NV", "_id": "89415"} -{"city": "UNIONVILLE", "loc": [-118.02115, 40.652217], "pop": 1060, "state": "NV", "_id": "89418"} -{"city": "LOVELOCK", "loc": [-118.468915, 40.1819], "pop": 3276, "state": "NV", "_id": "89419"} -{"city": "LUNING", "loc": [-118.157114, 38.406072], "pop": 501, "state": "NV", "_id": "89420"} -{"city": "MINDEN", "loc": [-119.731363, 39.021766], "pop": 3385, "state": "NV", "_id": "89423"} -{"city": "NIXON", "loc": [-119.464262, 39.885388], "pop": 610, "state": "NV", "_id": "89424"} -{"city": "OROVADA", "loc": [-117.775458, 41.822001], "pop": 1039, "state": "NV", "_id": "89425"} -{"city": "PARADISE VALLEY", "loc": [-117.572848, 41.505726], "pop": 244, "state": "NV", "_id": "89426"} -{"city": "SCHURZ", "loc": [-118.775578, 38.957527], "pop": 802, "state": "NV", "_id": "89427"} -{"city": "SILVER SPRINGS", "loc": [-119.270503, 39.380045], "pop": 3261, "state": "NV", "_id": "89429"} -{"city": "SMITH", "loc": [-119.389005, 38.917166], "pop": 70, "state": "NV", "_id": "89430"} -{"city": "SPARKS", "loc": [-119.755588, 39.547254], "pop": 33923, "state": "NV", "_id": "89431"} -{"city": "SUN VALLEY", "loc": [-119.775363, 39.595477], "pop": 11261, "state": "NV", "_id": "89433"} -{"city": "SPARKS", "loc": [-119.717754, 39.550229], "pop": 20249, "state": "NV", "_id": "89434"} -{"city": "SPARKS", "loc": [-119.708125, 39.626861], "pop": 4228, "state": "NV", "_id": "89436"} -{"city": "VIRGINIA CITY", "loc": [-119.596063, 39.387282], "pop": 2526, "state": "NV", "_id": "89440"} -{"city": "WADSWORTH", "loc": [-119.291778, 39.648069], "pop": 757, "state": "NV", "_id": "89442"} -{"city": "WELLINGTON", "loc": [-119.344901, 38.78762], "pop": 1004, "state": "NV", "_id": "89444"} -{"city": "WINNEMUCCA", "loc": [-117.746693, 40.966418], "pop": 10951, "state": "NV", "_id": "89445"} -{"city": "YERINGTON", "loc": [-119.159558, 38.986567], "pop": 6157, "state": "NV", "_id": "89447"} -{"city": "INCLINE VILLAGE", "loc": [-119.95206, 39.256352], "pop": 7807, "state": "NV", "_id": "89451"} -{"city": "RENO", "loc": [-119.811275, 39.526812], "pop": 2664, "state": "NV", "_id": "89501"} -{"city": "RENO", "loc": [-119.776395, 39.497239], "pop": 38332, "state": "NV", "_id": "89502"} -{"city": "RENO", "loc": [-119.837409, 39.5354], "pop": 23955, "state": "NV", "_id": "89503"} -{"city": "RENO", "loc": [-119.873505, 39.641168], "pop": 24254, "state": "NV", "_id": "89506"} -{"city": "RENO", "loc": [-119.823932, 39.498042], "pop": 36606, "state": "NV", "_id": "89509"} -{"city": "RENO", "loc": [-119.602678, 39.769919], "pop": 613, "state": "NV", "_id": "89510"} -{"city": "RENO", "loc": [-119.766846, 39.41512], "pop": 16412, "state": "NV", "_id": "89511"} -{"city": "RENO", "loc": [-119.795699, 39.548312], "pop": 21009, "state": "NV", "_id": "89512"} -{"city": "RENO", "loc": [-119.903065, 39.524917], "pop": 7697, "state": "NV", "_id": "89523"} -{"city": "CARSON CITY", "loc": [-119.745904, 39.150746], "pop": 18817, "state": "NV", "_id": "89701"} -{"city": "CARSON CITY", "loc": [-119.778242, 39.17036], "pop": 8047, "state": "NV", "_id": "89703"} -{"city": "CARSON CITY", "loc": [-119.828624, 39.089756], "pop": 82, "state": "NV", "_id": "89704"} -{"city": "CARSON CITY", "loc": [-119.782899, 39.089147], "pop": 2703, "state": "NV", "_id": "89705"} -{"city": "MOUNDHOUSE", "loc": [-119.742912, 39.210876], "pop": 19276, "state": "NV", "_id": "89706"} -{"city": "JIGGS", "loc": [-115.724679, 40.826247], "pop": 24461, "state": "NV", "_id": "89801"} -{"city": "BATTLE MOUNTAIN", "loc": [-116.955439, 40.621985], "pop": 5240, "state": "NV", "_id": "89820"} -{"city": "BEOWAWE", "loc": [-116.477387, 40.462398], "pop": 429, "state": "NV", "_id": "89821"} -{"city": "CARLIN", "loc": [-116.108208, 40.717216], "pop": 2272, "state": "NV", "_id": "89822"} -{"city": "DEETH", "loc": [-115.371729, 41.431033], "pop": 69, "state": "NV", "_id": "89823"} -{"city": "JACKPOT", "loc": [-115.112886, 41.839591], "pop": 95, "state": "NV", "_id": "89825"} -{"city": "MOUNTAIN CITY", "loc": [-116.113801, 41.870781], "pop": 1259, "state": "NV", "_id": "89831"} -{"city": "RUBY VALLEY", "loc": [-115.231219, 40.399514], "pop": 37, "state": "NV", "_id": "89833"} -{"city": "TUSCARORA", "loc": [-116.931141, 41.171527], "pop": 1, "state": "NV", "_id": "89834"} -{"city": "OASIS", "loc": [-114.532752, 41.129798], "pop": 5336, "state": "NV", "_id": "89835"} -{"city": "AMHERST", "loc": [-71.607536, 42.856944], "pop": 8998, "state": "NH", "_id": "03031"} -{"city": "AUBURN", "loc": [-71.344892, 42.992529], "pop": 4085, "state": "NH", "_id": "03032"} -{"city": "BROOKLINE", "loc": [-71.666254, 42.738442], "pop": 2410, "state": "NH", "_id": "03033"} -{"city": "CANDIA", "loc": [-71.304857, 43.058514], "pop": 3557, "state": "NH", "_id": "03034"} -{"city": "CHESTER", "loc": [-71.244962, 42.967756], "pop": 2691, "state": "NH", "_id": "03036"} -{"city": "DEERFIELD", "loc": [-71.251264, 43.137756], "pop": 3124, "state": "NH", "_id": "03037"} -{"city": "DERRY", "loc": [-71.301971, 42.887404], "pop": 29556, "state": "NH", "_id": "03038"} -{"city": "EPPING", "loc": [-71.076367, 43.041052], "pop": 6797, "state": "NH", "_id": "03042"} -{"city": "FRANCESTOWN", "loc": [-71.81131, 42.991952], "pop": 1219, "state": "NH", "_id": "03043"} -{"city": "FREMONT", "loc": [-71.121836, 42.984016], "pop": 2677, "state": "NH", "_id": "03044"} -{"city": "DUNBARTON", "loc": [-71.56264, 43.018224], "pop": 9428, "state": "NH", "_id": "03045"} -{"city": "GREENFIELD", "loc": [-71.872755, 42.949277], "pop": 1422, "state": "NH", "_id": "03047"} -{"city": "MASON", "loc": [-71.784487, 42.7489], "pop": 3443, "state": "NH", "_id": "03048"} -{"city": "HOLLIS", "loc": [-71.577206, 42.748513], "pop": 5705, "state": "NH", "_id": "03049"} -{"city": "HUDSON", "loc": [-71.412144, 42.769038], "pop": 26489, "state": "NH", "_id": "03051"} -{"city": "LONDONDERRY", "loc": [-71.37719, 42.865555], "pop": 19687, "state": "NH", "_id": "03053"} -{"city": "MERRIMACK", "loc": [-71.51278, 42.866689], "pop": 21632, "state": "NH", "_id": "03054"} -{"city": "MILFORD", "loc": [-71.660569, 42.828497], "pop": 11795, "state": "NH", "_id": "03055"} -{"city": "MONT VERNON", "loc": [-71.676243, 42.897597], "pop": 1812, "state": "NH", "_id": "03057"} -{"city": "NASHUA", "loc": [-71.466684, 42.756395], "pop": 41438, "state": "NH", "_id": "03060"} -{"city": "NASHUA", "loc": [-71.489282, 42.723472], "pop": 23927, "state": "NH", "_id": "03062"} -{"city": "NASHUA", "loc": [-71.513156, 42.771686], "pop": 14891, "state": "NH", "_id": "03063"} -{"city": "NEW BOSTON", "loc": [-71.686402, 42.97217], "pop": 2701, "state": "NH", "_id": "03070"} -{"city": "NEW IPSWICH", "loc": [-71.870318, 42.751142], "pop": 4014, "state": "NH", "_id": "03071"} -{"city": "PELHAM", "loc": [-71.304551, 42.72881], "pop": 6012, "state": "NH", "_id": "03076"} -{"city": "RAYMOND", "loc": [-71.191159, 43.032512], "pop": 9005, "state": "NH", "_id": "03077"} -{"city": "SALEM", "loc": [-71.21761, 42.78465], "pop": 25746, "state": "NH", "_id": "03079"} -{"city": "LYNDEBOROUGH", "loc": [-71.774373, 42.895449], "pop": 1294, "state": "NH", "_id": "03082"} -{"city": "TEMPLE", "loc": [-71.852347, 42.820035], "pop": 1194, "state": "NH", "_id": "03084"} -{"city": "WILTON", "loc": [-71.754066, 42.836761], "pop": 3122, "state": "NH", "_id": "03086"} -{"city": "WINDHAM", "loc": [-71.306735, 42.805106], "pop": 9000, "state": "NH", "_id": "03087"} -{"city": "MANCHESTER", "loc": [-71.463255, 42.992858], "pop": 2697, "state": "NH", "_id": "03101"} -{"city": "MANCHESTER", "loc": [-71.488433, 42.99442], "pop": 29308, "state": "NH", "_id": "03102"} -{"city": "MANCHESTER", "loc": [-71.449325, 42.965563], "pop": 36613, "state": "NH", "_id": "03103"} -{"city": "MANCHESTER", "loc": [-71.448233, 43.007307], "pop": 29950, "state": "NH", "_id": "03104"} -{"city": "HOOKSETT", "loc": [-71.444446, 43.061708], "pop": 8668, "state": "NH", "_id": "03106"} -{"city": "MANCHESTER", "loc": [-71.413474, 42.971349], "pop": 7884, "state": "NH", "_id": "03109"} -{"city": "BEDFORD", "loc": [-71.52127, 42.940307], "pop": 12468, "state": "NH", "_id": "03110"} -{"city": "ANDOVER", "loc": [-71.782952, 43.428668], "pop": 1638, "state": "NH", "_id": "03216"} -{"city": "ASHLAND", "loc": [-71.612085, 43.703428], "pop": 2056, "state": "NH", "_id": "03217"} -{"city": "BARNSTEAD", "loc": [-71.286946, 43.36513], "pop": 793, "state": "NH", "_id": "03218"} -{"city": "BELMONT", "loc": [-71.488991, 43.451189], "pop": 2997, "state": "NH", "_id": "03220"} -{"city": "BRADFORD", "loc": [-71.985048, 43.294343], "pop": 3273, "state": "NH", "_id": "03221"} -{"city": "BRISTOL", "loc": [-71.750664, 43.611994], "pop": 4288, "state": "NH", "_id": "03222"} -{"city": "BEEBE RIVER", "loc": [-71.636142, 43.888507], "pop": 2802, "state": "NH", "_id": "03223"} -{"city": "CANTERBURY", "loc": [-71.557008, 43.357041], "pop": 2085, "state": "NH", "_id": "03224"} -{"city": "CENTER BARNSTEAD", "loc": [-71.24244, 43.356563], "pop": 2307, "state": "NH", "_id": "03225"} -{"city": "CENTER HARBOR", "loc": [-71.47973, 43.710688], "pop": 470, "state": "NH", "_id": "03226"} -{"city": "CENTER SANDWICH", "loc": [-71.450614, 43.816169], "pop": 615, "state": "NH", "_id": "03227"} -{"city": "HOPKINTON", "loc": [-71.696299, 43.218898], "pop": 6071, "state": "NH", "_id": "03229"} -{"city": "DANBURY", "loc": [-71.869074, 43.5115], "pop": 1098, "state": "NH", "_id": "03230"} -{"city": "EAST ANDOVER", "loc": [-71.759606, 43.47766], "pop": 177, "state": "NH", "_id": "03231"} -{"city": "EAST HEBRON", "loc": [-71.767907, 43.696969], "pop": 47, "state": "NH", "_id": "03232"} -{"city": "EPSOM", "loc": [-71.354576, 43.217398], "pop": 2931, "state": "NH", "_id": "03234"} -{"city": "FRANKLIN", "loc": [-71.649122, 43.442569], "pop": 9780, "state": "NH", "_id": "03235"} -{"city": "GILMANTON", "loc": [-71.412063, 43.417476], "pop": 1308, "state": "NH", "_id": "03237"} -{"city": "GRAFTON", "loc": [-71.963389, 43.572743], "pop": 890, "state": "NH", "_id": "03240"} -{"city": "HEBRON", "loc": [-71.82696, 43.718571], "pop": 657, "state": "NH", "_id": "03241"} -{"city": "HENNIKER", "loc": [-71.815921, 43.179091], "pop": 4151, "state": "NH", "_id": "03242"} -{"city": "HILL", "loc": [-71.729168, 43.527422], "pop": 778, "state": "NH", "_id": "03243"} -{"city": "HILLSBORO", "loc": [-71.902818, 43.120709], "pop": 5246, "state": "NH", "_id": "03244"} -{"city": "GILFORD", "loc": [-71.452907, 43.538713], "pop": 24409, "state": "NH", "_id": "03246"} -{"city": "LINCOLN", "loc": [-71.672707, 44.058159], "pop": 1229, "state": "NH", "_id": "03251"} -{"city": "MEREDITH", "loc": [-71.511327, 43.650208], "pop": 5959, "state": "NH", "_id": "03253"} -{"city": "MOULTONBOROUGH", "loc": [-71.392245, 43.728133], "pop": 3208, "state": "NH", "_id": "03254"} -{"city": "NEW HAMPTON", "loc": [-71.643513, 43.618393], "pop": 1214, "state": "NH", "_id": "03256"} -{"city": "NEW LONDON", "loc": [-71.985674, 43.414501], "pop": 3280, "state": "NH", "_id": "03257"} -{"city": "NORTH SANDWICH", "loc": [-71.385025, 43.845182], "pop": 338, "state": "NH", "_id": "03259"} -{"city": "NORTHWOOD", "loc": [-71.200423, 43.206965], "pop": 3013, "state": "NH", "_id": "03261"} -{"city": "NORTH WOODSTOCK", "loc": [-71.697684, 44.019831], "pop": 1091, "state": "NH", "_id": "03262"} -{"city": "PITTSFIELD", "loc": [-71.33303, 43.287384], "pop": 5806, "state": "NH", "_id": "03263"} -{"city": "PLYMOUTH", "loc": [-71.684714, 43.763524], "pop": 8980, "state": "NH", "_id": "03264"} -{"city": "RUMNEY", "loc": [-71.848019, 43.804389], "pop": 1912, "state": "NH", "_id": "03266"} -{"city": "SALISBURY", "loc": [-71.704468, 43.406652], "pop": 140, "state": "NH", "_id": "03268"} -{"city": "SANBORNTON", "loc": [-71.600348, 43.549552], "pop": 699, "state": "NH", "_id": "03269"} -{"city": "ALLENSTOWN", "loc": [-71.439663, 43.147554], "pop": 11565, "state": "NH", "_id": "03275"} -{"city": "TILTON", "loc": [-71.577413, 43.46033], "pop": 7356, "state": "NH", "_id": "03276"} -{"city": "WARNER", "loc": [-71.835349, 43.303512], "pop": 3265, "state": "NH", "_id": "03278"} -{"city": "WARREN", "loc": [-71.89013, 43.944667], "pop": 886, "state": "NH", "_id": "03279"} -{"city": "WASHINGTON", "loc": [-72.082407, 43.174705], "pop": 628, "state": "NH", "_id": "03280"} -{"city": "WEARE", "loc": [-71.70376, 43.071422], "pop": 7481, "state": "NH", "_id": "03281"} -{"city": "WENTWORTH", "loc": [-71.909651, 43.868456], "pop": 556, "state": "NH", "_id": "03282"} -{"city": "WEST SPRINGFIELD", "loc": [-72.057855, 43.491615], "pop": 788, "state": "NH", "_id": "03284"} -{"city": "WILMOT FLAT", "loc": [-71.900983, 43.432177], "pop": 931, "state": "NH", "_id": "03287"} -{"city": "NOTTINGHAM", "loc": [-71.110983, 43.119632], "pop": 598, "state": "NH", "_id": "03290"} -{"city": "WEST NOTTINGHAM", "loc": [-71.111006, 43.133971], "pop": 27, "state": "NH", "_id": "03291"} -{"city": "CONCORD", "loc": [-71.527734, 43.218525], "pop": 34035, "state": "NH", "_id": "03301"} -{"city": "BOSCAWEN", "loc": [-71.612723, 43.285285], "pop": 12046, "state": "NH", "_id": "03303"} -{"city": "BOW", "loc": [-71.544814, 43.138788], "pop": 5500, "state": "NH", "_id": "03304"} -{"city": "SURRY", "loc": [-72.28954, 42.943127], "pop": 23882, "state": "NH", "_id": "03431"} -{"city": "ANTRIM", "loc": [-71.938698, 43.059547], "pop": 3379, "state": "NH", "_id": "03440"} -{"city": "ASHUELOT", "loc": [-72.434899, 42.785057], "pop": 285, "state": "NH", "_id": "03441"} -{"city": "BENNINGTON", "loc": [-71.91534, 43.010309], "pop": 1236, "state": "NH", "_id": "03442"} -{"city": "CHESTERFIELD", "loc": [-72.487219, 42.900785], "pop": 1455, "state": "NH", "_id": "03443"} -{"city": "DUBLIN", "loc": [-72.050538, 42.897198], "pop": 1474, "state": "NH", "_id": "03444"} -{"city": "EAST SULLIVAN", "loc": [-72.191778, 42.994005], "pop": 169, "state": "NH", "_id": "03445"} -{"city": "EAST SWANZEY", "loc": [-72.249298, 42.884137], "pop": 796, "state": "NH", "_id": "03446"} -{"city": "FITZWILLIAM", "loc": [-72.145014, 42.7611], "pop": 2016, "state": "NH", "_id": "03447"} -{"city": "GILSUM", "loc": [-72.263272, 43.043105], "pop": 745, "state": "NH", "_id": "03448"} -{"city": "HANCOCK", "loc": [-71.981858, 42.976817], "pop": 1526, "state": "NH", "_id": "03449"} -{"city": "HARRISVILLE", "loc": [-72.097243, 42.939874], "pop": 981, "state": "NH", "_id": "03450"} -{"city": "HINSDALE", "loc": [-72.501474, 42.802714], "pop": 3936, "state": "NH", "_id": "03451"} -{"city": "JAFFREY", "loc": [-72.027514, 42.81779], "pop": 5334, "state": "NH", "_id": "03452"} -{"city": "MARLBOROUGH", "loc": [-72.201292, 42.898804], "pop": 1927, "state": "NH", "_id": "03455"} -{"city": "MARLOW", "loc": [-72.210879, 43.132585], "pop": 650, "state": "NH", "_id": "03456"} -{"city": "MUNSONVILLE", "loc": [-72.133702, 42.998646], "pop": 535, "state": "NH", "_id": "03457"} -{"city": "PETERBOROUGH", "loc": [-71.946964, 42.88559], "pop": 5713, "state": "NH", "_id": "03458"} -{"city": "RINDGE", "loc": [-72.019038, 42.754391], "pop": 4968, "state": "NH", "_id": "03461"} -{"city": "SPOFFORD", "loc": [-72.410277, 42.911973], "pop": 1266, "state": "NH", "_id": "03462"} -{"city": "STODDARD", "loc": [-72.108811, 43.073944], "pop": 622, "state": "NH", "_id": "03464"} -{"city": "TROY", "loc": [-72.184753, 42.82697], "pop": 2097, "state": "NH", "_id": "03465"} -{"city": "WEST CHESTERFIEL", "loc": [-72.511216, 42.873152], "pop": 391, "state": "NH", "_id": "03466"} -{"city": "WESTMORELAND", "loc": [-72.435784, 42.968999], "pop": 1596, "state": "NH", "_id": "03467"} -{"city": "WEST SWANZEY", "loc": [-72.297688, 42.870313], "pop": 5440, "state": "NH", "_id": "03469"} -{"city": "RICHMOND", "loc": [-72.363672, 42.773922], "pop": 4625, "state": "NH", "_id": "03470"} -{"city": "LITTLETON", "loc": [-71.776816, 44.311187], "pop": 6663, "state": "NH", "_id": "03561"} -{"city": "BERLIN", "loc": [-71.189236, 44.48107], "pop": 12892, "state": "NH", "_id": "03570"} -{"city": "BETHLEHEM", "loc": [-71.682929, 44.280846], "pop": 1885, "state": "NH", "_id": "03574"} -{"city": "COLEBROOK", "loc": [-71.479341, 44.907776], "pop": 4232, "state": "NH", "_id": "03576"} -{"city": "ERROL", "loc": [-71.143612, 44.800273], "pop": 366, "state": "NH", "_id": "03579"} -{"city": "FRANCONIA", "loc": [-71.751822, 44.205273], "pop": 1090, "state": "NH", "_id": "03580"} -{"city": "GORHAM", "loc": [-71.179983, 44.399601], "pop": 3610, "state": "NH", "_id": "03581"} -{"city": "GROVETON", "loc": [-71.506421, 44.598367], "pop": 2527, "state": "NH", "_id": "03582"} -{"city": "JEFFERSON", "loc": [-71.451834, 44.399907], "pop": 986, "state": "NH", "_id": "03583"} -{"city": "LANCASTER", "loc": [-71.559115, 44.492074], "pop": 3825, "state": "NH", "_id": "03584"} -{"city": "LISBON", "loc": [-71.896565, 44.214837], "pop": 2295, "state": "NH", "_id": "03585"} -{"city": "MILAN", "loc": [-71.181031, 44.586968], "pop": 987, "state": "NH", "_id": "03588"} -{"city": "NORTH STRATFORD", "loc": [-71.564368, 44.714915], "pop": 927, "state": "NH", "_id": "03590"} -{"city": "PITTSBURG", "loc": [-71.363593, 45.086564], "pop": 1104, "state": "NH", "_id": "03592"} -{"city": "WHITEFIELD", "loc": [-71.603453, 44.36811], "pop": 3139, "state": "NH", "_id": "03598"} -{"city": "ALSTEAD", "loc": [-72.328052, 43.126484], "pop": 1721, "state": "NH", "_id": "03602"} -{"city": "CHARLESTOWN", "loc": [-72.40638, 43.257339], "pop": 4678, "state": "NH", "_id": "03603"} -{"city": "EAST LEMPSTER", "loc": [-72.166205, 43.21863], "pop": 323, "state": "NH", "_id": "03605"} -{"city": "SOUTH ACWORTH", "loc": [-72.341053, 43.176942], "pop": 1008, "state": "NH", "_id": "03607"} -{"city": "WALPOLE", "loc": [-72.415489, 43.076533], "pop": 2466, "state": "NH", "_id": "03608"} -{"city": "NORTH WALPOLE", "loc": [-72.448253, 43.142759], "pop": 744, "state": "NH", "_id": "03609"} -{"city": "BATH", "loc": [-71.956741, 44.173419], "pop": 155, "state": "NH", "_id": "03740"} -{"city": "CANAAN", "loc": [-72.029724, 43.660092], "pop": 3065, "state": "NH", "_id": "03741"} -{"city": "CLAREMONT", "loc": [-72.342186, 43.367942], "pop": 14820, "state": "NH", "_id": "03743"} -{"city": "CORNISH", "loc": [-72.339426, 43.496339], "pop": 2275, "state": "NH", "_id": "03745"} -{"city": "ENFIELD", "loc": [-72.127014, 43.625584], "pop": 4118, "state": "NH", "_id": "03748"} -{"city": "ETNA", "loc": [-72.212479, 43.711333], "pop": 944, "state": "NH", "_id": "03750"} -{"city": "GOSHEN", "loc": [-72.124117, 43.302623], "pop": 742, "state": "NH", "_id": "03752"} -{"city": "GRANTHAM", "loc": [-72.133437, 43.510324], "pop": 1247, "state": "NH", "_id": "03753"} -{"city": "HANOVER", "loc": [-72.28496, 43.704532], "pop": 7070, "state": "NH", "_id": "03755"} -{"city": "HAVERHILL", "loc": [-72.057276, 44.039438], "pop": 498, "state": "NH", "_id": "03765"} -{"city": "LEBANON", "loc": [-72.242818, 43.644688], "pop": 9032, "state": "NH", "_id": "03766"} -{"city": "LYME", "loc": [-72.161993, 43.791327], "pop": 2172, "state": "NH", "_id": "03768"} -{"city": "MERIDEN", "loc": [-72.275644, 43.529873], "pop": 126, "state": "NH", "_id": "03770"} -{"city": "MONROE", "loc": [-72.025028, 44.273358], "pop": 760, "state": "NH", "_id": "03771"} -{"city": "NEWPORT", "loc": [-72.183789, 43.353228], "pop": 8073, "state": "NH", "_id": "03773"} -{"city": "NORTH HAVERHILL", "loc": [-72.019112, 44.097841], "pop": 1744, "state": "NH", "_id": "03774"} -{"city": "ORFORD", "loc": [-72.097846, 43.894101], "pop": 1008, "state": "NH", "_id": "03777"} -{"city": "PIERMONT", "loc": [-72.081299, 43.990572], "pop": 431, "state": "NH", "_id": "03779"} -{"city": "PIKE", "loc": [-72.009587, 44.025511], "pop": 751, "state": "NH", "_id": "03780"} -{"city": "PLAINFIELD", "loc": [-72.270398, 43.551919], "pop": 1314, "state": "NH", "_id": "03781"} -{"city": "SUNAPEE", "loc": [-72.095044, 43.386816], "pop": 2570, "state": "NH", "_id": "03782"} -{"city": "WEST LEBANON", "loc": [-72.300735, 43.64401], "pop": 3784, "state": "NH", "_id": "03784"} -{"city": "WOODSVILLE", "loc": [-71.989215, 44.138549], "pop": 2292, "state": "NH", "_id": "03785"} -{"city": "NEWINGTON", "loc": [-70.780412, 43.066524], "pop": 27430, "state": "NH", "_id": "03801"} -{"city": "ALTON", "loc": [-71.229709, 43.46302], "pop": 2939, "state": "NH", "_id": "03809"} -{"city": "ALTON BAY", "loc": [-71.24888, 43.484468], "pop": 157, "state": "NH", "_id": "03810"} -{"city": "ATKINSON", "loc": [-71.1603, 42.836981], "pop": 5145, "state": "NH", "_id": "03811"} -{"city": "BARTLETT", "loc": [-71.2491, 44.08656], "pop": 1379, "state": "NH", "_id": "03812"} -{"city": "CENTER CONWAY", "loc": [-71.060677, 43.98776], "pop": 2394, "state": "NH", "_id": "03813"} -{"city": "CENTER OSSIPEE", "loc": [-71.134882, 43.768189], "pop": 2492, "state": "NH", "_id": "03814"} -{"city": "CENTER STRAFFORD", "loc": [-71.107122, 43.262888], "pop": 436, "state": "NH", "_id": "03815"} -{"city": "CENTER TUFTONBOR", "loc": [-71.265059, 43.690205], "pop": 885, "state": "NH", "_id": "03816"} -{"city": "CHOCORUA", "loc": [-71.240716, 43.890851], "pop": 70, "state": "NH", "_id": "03817"} -{"city": "CONWAY", "loc": [-71.15028, 43.974161], "pop": 1875, "state": "NH", "_id": "03818"} -{"city": "DANVILLE", "loc": [-71.120985, 42.923432], "pop": 2471, "state": "NH", "_id": "03819"} -{"city": "MADBURY", "loc": [-70.884881, 43.190006], "pop": 27182, "state": "NH", "_id": "03820"} -{"city": "LEE", "loc": [-70.952333, 43.133821], "pop": 15487, "state": "NH", "_id": "03824"} -{"city": "BARRINGTON", "loc": [-71.037675, 43.2027], "pop": 5842, "state": "NH", "_id": "03825"} -{"city": "EAST HAMPSTEAD", "loc": [-71.127978, 42.887656], "pop": 1880, "state": "NH", "_id": "03826"} -{"city": "SOUTH HAMPTON", "loc": [-70.976904, 42.911289], "pop": 3197, "state": "NH", "_id": "03827"} -{"city": "EAST WAKEFIELD", "loc": [-71.007712, 43.641022], "pop": 675, "state": "NH", "_id": "03830"} -{"city": "BRENTWOOD", "loc": [-70.964306, 42.977169], "pop": 14374, "state": "NH", "_id": "03833"} -{"city": "FARMINGTON", "loc": [-71.064693, 43.388373], "pop": 4676, "state": "NH", "_id": "03835"} -{"city": "FREEDOM", "loc": [-71.062815, 43.817242], "pop": 935, "state": "NH", "_id": "03836"} -{"city": "GILMANTON IRON W", "loc": [-71.330315, 43.425281], "pop": 1301, "state": "NH", "_id": "03837"} -{"city": "GLEN", "loc": [-71.192457, 44.101777], "pop": 84, "state": "NH", "_id": "03838"} -{"city": "GONIC", "loc": [-70.976642, 43.268374], "pop": 4474, "state": "NH", "_id": "03839"} -{"city": "GREENLAND", "loc": [-70.847476, 43.035294], "pop": 2450, "state": "NH", "_id": "03840"} -{"city": "HAMPSTEAD", "loc": [-71.175802, 42.881957], "pop": 5291, "state": "NH", "_id": "03841"} -{"city": "HAMPTON", "loc": [-70.824336, 42.935883], "pop": 12278, "state": "NH", "_id": "03842"} -{"city": "HAMPTON FALLS", "loc": [-70.887608, 42.926323], "pop": 1503, "state": "NH", "_id": "03844"} -{"city": "INTERVALE", "loc": [-71.119415, 44.095479], "pop": 1811, "state": "NH", "_id": "03845"} -{"city": "JACKSON", "loc": [-71.187808, 44.166989], "pop": 689, "state": "NH", "_id": "03846"} -{"city": "KINGSTON", "loc": [-71.063914, 42.913258], "pop": 6111, "state": "NH", "_id": "03848"} -{"city": "MADISON", "loc": [-71.125412, 43.915206], "pop": 1669, "state": "NH", "_id": "03849"} -{"city": "MILTON MILLS", "loc": [-70.969729, 43.502494], "pop": 514, "state": "NH", "_id": "03852"} -{"city": "MIRROR LAKE", "loc": [-71.272858, 43.636552], "pop": 696, "state": "NH", "_id": "03853"} -{"city": "NEW CASTLE", "loc": [-70.719922, 43.068114], "pop": 840, "state": "NH", "_id": "03854"} -{"city": "NEW DURHAM", "loc": [-71.140828, 43.443045], "pop": 2148, "state": "NH", "_id": "03855"} -{"city": "NEWMARKET", "loc": [-70.955317, 43.072628], "pop": 9049, "state": "NH", "_id": "03857"} -{"city": "NEWTON", "loc": [-71.042021, 42.867805], "pop": 2944, "state": "NH", "_id": "03858"} -{"city": "NORTH CONWAY", "loc": [-71.123811, 44.033613], "pop": 3458, "state": "NH", "_id": "03860"} -{"city": "NORTH HAMPTON", "loc": [-70.826738, 42.977625], "pop": 3637, "state": "NH", "_id": "03862"} -{"city": "OSSIPEE", "loc": [-71.112873, 43.694506], "pop": 1490, "state": "NH", "_id": "03864"} -{"city": "PLAISTOW", "loc": [-71.093397, 42.835551], "pop": 7124, "state": "NH", "_id": "03865"} -{"city": "ROCHESTER", "loc": [-71.055753, 43.309203], "pop": 3793, "state": "NH", "_id": "03867"} -{"city": "EAST ROCHESTER", "loc": [-70.968581, 43.31256], "pop": 20616, "state": "NH", "_id": "03868"} -{"city": "ROLLINSFORD", "loc": [-70.833207, 43.226936], "pop": 2395, "state": "NH", "_id": "03869"} -{"city": "RYE", "loc": [-70.765153, 43.009468], "pop": 4415, "state": "NH", "_id": "03870"} -{"city": "SANBORNVILLE", "loc": [-71.020005, 43.551278], "pop": 2382, "state": "NH", "_id": "03872"} -{"city": "SANDOWN", "loc": [-71.18606, 42.930819], "pop": 4060, "state": "NH", "_id": "03873"} -{"city": "SEABROOK", "loc": [-70.86464, 42.88536], "pop": 6503, "state": "NH", "_id": "03874"} -{"city": "SILVER LAKE", "loc": [-71.190501, 43.878974], "pop": 640, "state": "NH", "_id": "03875"} -{"city": "SOMERSWORTH", "loc": [-70.875589, 43.252546], "pop": 11170, "state": "NH", "_id": "03878"} -{"city": "SOUTH EFFINGHAM", "loc": [-71.002109, 43.721216], "pop": 201, "state": "NH", "_id": "03882"} -{"city": "SOUTH TAMWORTH", "loc": [-71.311654, 43.833613], "pop": 188, "state": "NH", "_id": "03883"} -{"city": "STRAFFORD", "loc": [-71.162475, 43.250575], "pop": 1618, "state": "NH", "_id": "03884"} -{"city": "STRATHAM", "loc": [-70.899714, 43.019432], "pop": 4967, "state": "NH", "_id": "03885"} -{"city": "TAMWORTH", "loc": [-71.264454, 43.862049], "pop": 1285, "state": "NH", "_id": "03886"} -{"city": "UNION", "loc": [-71.020857, 43.4382], "pop": 4229, "state": "NH", "_id": "03887"} -{"city": "WEST OSSIPEE", "loc": [-71.205141, 43.835956], "pop": 362, "state": "NH", "_id": "03890"} -{"city": "WOLFEBORO", "loc": [-71.190843, 43.594996], "pop": 5586, "state": "NH", "_id": "03894"} -{"city": "AVENEL", "loc": [-74.278522, 40.582568], "pop": 14953, "state": "NJ", "_id": "07001"} -{"city": "BAYONNE", "loc": [-74.119169, 40.666399], "pop": 61444, "state": "NJ", "_id": "07002"} -{"city": "BLOOMFIELD", "loc": [-74.189074, 40.803456], "pop": 46131, "state": "NJ", "_id": "07003"} -{"city": "FAIRFIELD", "loc": [-74.296027, 40.882178], "pop": 7567, "state": "NJ", "_id": "07004"} -{"city": "BOONTON", "loc": [-74.414035, 40.911528], "pop": 13961, "state": "NJ", "_id": "07005"} -{"city": "WEST CALDWELL", "loc": [-74.276771, 40.849059], "pop": 24946, "state": "NJ", "_id": "07006"} -{"city": "CARTERET", "loc": [-74.231345, 40.582278], "pop": 19025, "state": "NJ", "_id": "07008"} -{"city": "CEDAR GROVE", "loc": [-74.229672, 40.85344], "pop": 11787, "state": "NJ", "_id": "07009"} -{"city": "CLIFFSIDE PARK", "loc": [-73.987982, 40.822168], "pop": 20687, "state": "NJ", "_id": "07010"} -{"city": "CLIFTON", "loc": [-74.142459, 40.878876], "pop": 31569, "state": "NJ", "_id": "07011"} -{"city": "CLIFTON", "loc": [-74.161172, 40.848835], "pop": 10107, "state": "NJ", "_id": "07012"} -{"city": "CLIFTON", "loc": [-74.171144, 40.869334], "pop": 25503, "state": "NJ", "_id": "07013"} -{"city": "CLIFTON", "loc": [-74.137682, 40.834375], "pop": 4288, "state": "NJ", "_id": "07014"} -{"city": "CRANFORD", "loc": [-74.305685, 40.655357], "pop": 22866, "state": "NJ", "_id": "07016"} -{"city": "EAST ORANGE", "loc": [-74.207723, 40.769614], "pop": 41737, "state": "NJ", "_id": "07017"} -{"city": "EAST ORANGE", "loc": [-74.219822, 40.755799], "pop": 32618, "state": "NJ", "_id": "07018"} -{"city": "EDGEWATER", "loc": [-73.973821, 40.831654], "pop": 5001, "state": "NJ", "_id": "07020"} -{"city": "ESSEX FELLS", "loc": [-74.279705, 40.827924], "pop": 2102, "state": "NJ", "_id": "07021"} -{"city": "FAIRVIEW", "loc": [-73.999967, 40.816985], "pop": 10682, "state": "NJ", "_id": "07022"} -{"city": "FANWOOD", "loc": [-74.386762, 40.641856], "pop": 6528, "state": "NJ", "_id": "07023"} -{"city": "FORT LEE", "loc": [-73.974455, 40.850312], "pop": 32030, "state": "NJ", "_id": "07024"} -{"city": "GARFIELD", "loc": [-74.108141, 40.878886], "pop": 26727, "state": "NJ", "_id": "07026"} -{"city": "GARWOOD", "loc": [-74.323864, 40.65121], "pop": 4277, "state": "NJ", "_id": "07027"} -{"city": "GLEN RIDGE", "loc": [-74.205477, 40.804015], "pop": 7751, "state": "NJ", "_id": "07028"} -{"city": "KEARNY", "loc": [-74.155871, 40.74754], "pop": 15587, "state": "NJ", "_id": "07029"} -{"city": "HOBOKEN", "loc": [-74.032863, 40.7445], "pop": 33397, "state": "NJ", "_id": "07030"} -{"city": "NORTH ARLINGTON", "loc": [-74.134288, 40.78977], "pop": 13629, "state": "NJ", "_id": "07031"} -{"city": "KEARNY", "loc": [-74.147108, 40.76466], "pop": 34869, "state": "NJ", "_id": "07032"} -{"city": "KENILWORTH", "loc": [-74.294419, 40.675869], "pop": 7516, "state": "NJ", "_id": "07033"} -{"city": "LAKE HIAWATHA", "loc": [-74.383013, 40.88252], "pop": 9361, "state": "NJ", "_id": "07034"} -{"city": "LINCOLN PARK", "loc": [-74.299512, 40.920769], "pop": 10814, "state": "NJ", "_id": "07035"} -{"city": "LINDEN", "loc": [-74.255567, 40.635366], "pop": 37859, "state": "NJ", "_id": "07036"} -{"city": "LIVINGSTON", "loc": [-74.3202, 40.789633], "pop": 26677, "state": "NJ", "_id": "07039"} -{"city": "MAPLEWOOD", "loc": [-74.265573, 40.727906], "pop": 21588, "state": "NJ", "_id": "07040"} -{"city": "MILLBURN", "loc": [-74.301469, 40.722798], "pop": 6501, "state": "NJ", "_id": "07041"} -{"city": "MONTCLAIR", "loc": [-74.216467, 40.81307], "pop": 24938, "state": "NJ", "_id": "07042"} -{"city": "MONTCLAIR", "loc": [-74.201104, 40.843023], "pop": 11891, "state": "NJ", "_id": "07043"} -{"city": "VERONA", "loc": [-74.242847, 40.831928], "pop": 13583, "state": "NJ", "_id": "07044"} -{"city": "MONTVILLE", "loc": [-74.36456, 40.904914], "pop": 6571, "state": "NJ", "_id": "07045"} -{"city": "MOUNTAIN LAKES", "loc": [-74.441487, 40.890447], "pop": 4250, "state": "NJ", "_id": "07046"} -{"city": "NORTH BERGEN", "loc": [-74.017715, 40.793019], "pop": 50823, "state": "NJ", "_id": "07047"} -{"city": "ORANGE", "loc": [-74.2355, 40.769223], "pop": 28789, "state": "NJ", "_id": "07050"} -{"city": "WEST ORANGE", "loc": [-74.256765, 40.785926], "pop": 38639, "state": "NJ", "_id": "07052"} -{"city": "PARSIPPANY", "loc": [-74.411663, 40.862106], "pop": 27179, "state": "NJ", "_id": "07054"} -{"city": "PASSAIC", "loc": [-74.128348, 40.860132], "pop": 58325, "state": "NJ", "_id": "07055"} -{"city": "WALLINGTON", "loc": [-74.107937, 40.85356], "pop": 10816, "state": "NJ", "_id": "07057"} -{"city": "PINE BROOK", "loc": [-74.350009, 40.874207], "pop": 4345, "state": "NJ", "_id": "07058"} -{"city": "WARREN", "loc": [-74.510456, 40.631787], "pop": 10867, "state": "NJ", "_id": "07059"} -{"city": "NORTH PLAINFIELD", "loc": [-74.425298, 40.61978], "pop": 43471, "state": "NJ", "_id": "07060"} -{"city": "NORTH PLAINFIELD", "loc": [-74.406042, 40.631992], "pop": 12756, "state": "NJ", "_id": "07062"} -{"city": "NORTH PLAINFIELD", "loc": [-74.445325, 40.604838], "pop": 14285, "state": "NJ", "_id": "07063"} -{"city": "PORT READING", "loc": [-74.246643, 40.570935], "pop": 4083, "state": "NJ", "_id": "07064"} -{"city": "RAHWAY", "loc": [-74.281881, 40.608668], "pop": 25296, "state": "NJ", "_id": "07065"} -{"city": "CLARK", "loc": [-74.310581, 40.620256], "pop": 14973, "state": "NJ", "_id": "07066"} -{"city": "COLONIA", "loc": [-74.316368, 40.593743], "pop": 18488, "state": "NJ", "_id": "07067"} -{"city": "ROSELAND", "loc": [-74.304688, 40.82034], "pop": 4820, "state": "NJ", "_id": "07068"} -{"city": "RUTHERFORD", "loc": [-74.112146, 40.829245], "pop": 17790, "state": "NJ", "_id": "07070"} -{"city": "LYNDHURST", "loc": [-74.12453, 40.809433], "pop": 18423, "state": "NJ", "_id": "07071"} -{"city": "CARLSTADT", "loc": [-74.092498, 40.840298], "pop": 5541, "state": "NJ", "_id": "07072"} -{"city": "EAST RUTHERFORD", "loc": [-74.104069, 40.838527], "pop": 7874, "state": "NJ", "_id": "07073"} -{"city": "MOONACHIE", "loc": [-74.056646, 40.839352], "pop": 2817, "state": "NJ", "_id": "07074"} -{"city": "WOOD RIDGE", "loc": [-74.087845, 40.849348], "pop": 7548, "state": "NJ", "_id": "07075"} -{"city": "SCOTCH PLAINS", "loc": [-74.381663, 40.642162], "pop": 18514, "state": "NJ", "_id": "07076"} -{"city": "SEWAREN", "loc": [-74.260736, 40.554181], "pop": 2463, "state": "NJ", "_id": "07077"} -{"city": "SHORT HILLS", "loc": [-74.327085, 40.73678], "pop": 12129, "state": "NJ", "_id": "07078"} -{"city": "SOUTH ORANGE", "loc": [-74.257532, 40.746453], "pop": 16115, "state": "NJ", "_id": "07079"} -{"city": "SOUTH PLAINFIELD", "loc": [-74.414695, 40.583884], "pop": 20540, "state": "NJ", "_id": "07080"} -{"city": "SPRINGFIELD", "loc": [-74.322705, 40.701461], "pop": 13452, "state": "NJ", "_id": "07081"} -{"city": "TOWACO", "loc": [-74.342807, 40.927691], "pop": 4355, "state": "NJ", "_id": "07082"} -{"city": "UNION", "loc": [-74.267653, 40.695184], "pop": 46608, "state": "NJ", "_id": "07083"} -{"city": "WEEHAWKEN", "loc": [-74.030558, 40.768153], "pop": 69646, "state": "NJ", "_id": "07087"} -{"city": "VAUXHALL", "loc": [-74.282874, 40.717927], "pop": 3481, "state": "NJ", "_id": "07088"} -{"city": "WESTFIELD", "loc": [-74.345056, 40.647851], "pop": 31885, "state": "NJ", "_id": "07090"} -{"city": "MOUNTAINSIDE", "loc": [-74.358785, 40.678461], "pop": 6660, "state": "NJ", "_id": "07092"} -{"city": "GUTTENBERG", "loc": [-74.012859, 40.788192], "pop": 44735, "state": "NJ", "_id": "07093"} -{"city": "SECAUCUS", "loc": [-74.063416, 40.79101], "pop": 14061, "state": "NJ", "_id": "07094"} -{"city": "WOODBRIDGE", "loc": [-74.284542, 40.555973], "pop": 15827, "state": "NJ", "_id": "07095"} -{"city": "NEWARK", "loc": [-74.176505, 40.73201], "pop": 10328, "state": "NJ", "_id": "07102"} -{"city": "NEWARK", "loc": [-74.196364, 40.736975], "pop": 36949, "state": "NJ", "_id": "07103"} -{"city": "NEWARK", "loc": [-74.1695, 40.766446], "pop": 47183, "state": "NJ", "_id": "07104"} -{"city": "NEWARK", "loc": [-74.156346, 40.727086], "pop": 38104, "state": "NJ", "_id": "07105"} -{"city": "NEWARK", "loc": [-74.233023, 40.741485], "pop": 35013, "state": "NJ", "_id": "07106"} -{"city": "NEWARK", "loc": [-74.18816, 40.760656], "pop": 36649, "state": "NJ", "_id": "07107"} -{"city": "NEWARK", "loc": [-74.201538, 40.723647], "pop": 29730, "state": "NJ", "_id": "07108"} -{"city": "BELLEVILLE", "loc": [-74.163119, 40.79458], "pop": 34924, "state": "NJ", "_id": "07109"} -{"city": "NUTLEY", "loc": [-74.158934, 40.818548], "pop": 26441, "state": "NJ", "_id": "07110"} -{"city": "IRVINGTON", "loc": [-74.231271, 40.7261], "pop": 60986, "state": "NJ", "_id": "07111"} -{"city": "NEWARK", "loc": [-74.213073, 40.71071], "pop": 30356, "state": "NJ", "_id": "07112"} -{"city": "NEWARK", "loc": [-74.189105, 40.708246], "pop": 11260, "state": "NJ", "_id": "07114"} -{"city": "ELIZABETH", "loc": [-74.204335, 40.67169], "pop": 25826, "state": "NJ", "_id": "07201"} -{"city": "ELIZABETH", "loc": [-74.221544, 40.65652], "pop": 33981, "state": "NJ", "_id": "07202"} -{"city": "ROSELLE", "loc": [-74.261044, 40.652972], "pop": 20159, "state": "NJ", "_id": "07203"} -{"city": "ROSELLE PARK", "loc": [-74.267003, 40.665134], "pop": 12784, "state": "NJ", "_id": "07204"} -{"city": "HILLSIDE", "loc": [-74.228065, 40.696811], "pop": 20860, "state": "NJ", "_id": "07205"} -{"city": "ELIZABETH", "loc": [-74.192487, 40.653207], "pop": 23830, "state": "NJ", "_id": "07206"} -{"city": "ELIZABETH", "loc": [-74.22392, 40.674659], "pop": 26609, "state": "NJ", "_id": "07208"} -{"city": "JERSEY CITY", "loc": [-74.046878, 40.722126], "pop": 32876, "state": "NJ", "_id": "07302"} -{"city": "JERSEY CITY", "loc": [-74.075358, 40.717973], "pop": 42101, "state": "NJ", "_id": "07304"} -{"city": "JERSEY CITY", "loc": [-74.088998, 40.702007], "pop": 58655, "state": "NJ", "_id": "07305"} -{"city": "JERSEY CITY", "loc": [-74.066038, 40.732125], "pop": 53257, "state": "NJ", "_id": "07306"} -{"city": "JERSEY CITY", "loc": [-74.049752, 40.748167], "pop": 40704, "state": "NJ", "_id": "07307"} -{"city": "JERSEY CITY", "loc": [-74.043149, 40.732354], "pop": 944, "state": "NJ", "_id": "07310"} -{"city": "ALLENDALE", "loc": [-74.134185, 41.032654], "pop": 5907, "state": "NJ", "_id": "07401"} -{"city": "BLOOMINGDALE", "loc": [-74.333756, 41.012845], "pop": 7520, "state": "NJ", "_id": "07403"} -{"city": "KINNELON", "loc": [-74.364065, 40.992118], "pop": 15325, "state": "NJ", "_id": "07405"} -{"city": "ELMWOOD PARK", "loc": [-74.120896, 40.906896], "pop": 17649, "state": "NJ", "_id": "07407"} -{"city": "FAIR LAWN", "loc": [-74.1166, 40.934297], "pop": 30522, "state": "NJ", "_id": "07410"} -{"city": "FRANKLIN", "loc": [-74.58649, 41.116355], "pop": 5380, "state": "NJ", "_id": "07416"} -{"city": "FRANKLIN LAKES", "loc": [-74.211347, 41.008095], "pop": 9873, "state": "NJ", "_id": "07417"} -{"city": "GLENWOOD", "loc": [-74.488481, 41.235618], "pop": 2446, "state": "NJ", "_id": "07418"} -{"city": "HAMBURG", "loc": [-74.587379, 41.146714], "pop": 4198, "state": "NJ", "_id": "07419"} -{"city": "HASKELL", "loc": [-74.296542, 41.030111], "pop": 4813, "state": "NJ", "_id": "07420"} -{"city": "HEWITT", "loc": [-74.368566, 41.170867], "pop": 6907, "state": "NJ", "_id": "07421"} -{"city": "HIGHLAND LAKES", "loc": [-74.456442, 41.182622], "pop": 7615, "state": "NJ", "_id": "07422"} -{"city": "HO HO KUS", "loc": [-74.102532, 41.000412], "pop": 3967, "state": "NJ", "_id": "07423"} -{"city": "WEST PATERSON", "loc": [-74.21145, 40.885353], "pop": 22626, "state": "NJ", "_id": "07424"} -{"city": "MAHWAH", "loc": [-74.155974, 41.074473], "pop": 17991, "state": "NJ", "_id": "07430"} -{"city": "MIDLAND PARK", "loc": [-74.140904, 40.995668], "pop": 6693, "state": "NJ", "_id": "07432"} -{"city": "NEWFOUNDLAND", "loc": [-74.435857, 41.064691], "pop": 1624, "state": "NJ", "_id": "07435"} -{"city": "OAKLAND", "loc": [-74.233754, 41.029436], "pop": 11997, "state": "NJ", "_id": "07436"} -{"city": "MILTON", "loc": [-74.508801, 41.028401], "pop": 10453, "state": "NJ", "_id": "07438"} -{"city": "OGDENSBURG", "loc": [-74.598188, 41.076707], "pop": 2722, "state": "NJ", "_id": "07439"} -{"city": "PEQUANNOCK", "loc": [-74.29601, 40.947308], "pop": 4932, "state": "NJ", "_id": "07440"} -{"city": "POMPTON LAKES", "loc": [-74.287566, 40.999284], "pop": 10539, "state": "NJ", "_id": "07442"} -{"city": "POMPTON PLAINS", "loc": [-74.301602, 40.965515], "pop": 8028, "state": "NJ", "_id": "07444"} -{"city": "RAMSEY", "loc": [-74.144467, 41.057743], "pop": 13135, "state": "NJ", "_id": "07446"} -{"city": "RIDGEWOOD", "loc": [-74.113134, 40.982023], "pop": 24452, "state": "NJ", "_id": "07450"} -{"city": "GLEN ROCK", "loc": [-74.125367, 40.960183], "pop": 10942, "state": "NJ", "_id": "07452"} -{"city": "RINGWOOD", "loc": [-74.265872, 41.092816], "pop": 12645, "state": "NJ", "_id": "07456"} -{"city": "RIVERDALE", "loc": [-74.308756, 40.993109], "pop": 2365, "state": "NJ", "_id": "07457"} -{"city": "UPPER SADDLE RIV", "loc": [-74.096775, 41.053083], "pop": 10148, "state": "NJ", "_id": "07458"} -{"city": "STOCKHOLM", "loc": [-74.528256, 41.099204], "pop": 3375, "state": "NJ", "_id": "07460"} -{"city": "SUSSEX", "loc": [-74.599156, 41.229211], "pop": 15969, "state": "NJ", "_id": "07461"} -{"city": "VERNON", "loc": [-74.533196, 41.184981], "pop": 6460, "state": "NJ", "_id": "07462"} -{"city": "WALDWICK", "loc": [-74.124259, 41.012968], "pop": 9720, "state": "NJ", "_id": "07463"} -{"city": "WANAQUE", "loc": [-74.278968, 41.054447], "pop": 4941, "state": "NJ", "_id": "07465"} -{"city": "WAYNE", "loc": [-74.246565, 40.947112], "pop": 46815, "state": "NJ", "_id": "07470"} -{"city": "WEST MILFORD", "loc": [-74.374996, 41.091513], "pop": 15417, "state": "NJ", "_id": "07480"} -{"city": "WYCKOFF", "loc": [-74.166009, 40.997834], "pop": 15372, "state": "NJ", "_id": "07481"} -{"city": "PATERSON", "loc": [-74.167141, 40.914273], "pop": 33364, "state": "NJ", "_id": "07501"} -{"city": "PATERSON", "loc": [-74.193238, 40.919926], "pop": 12698, "state": "NJ", "_id": "07502"} -{"city": "PATERSON", "loc": [-74.157272, 40.897046], "pop": 18683, "state": "NJ", "_id": "07503"} -{"city": "PATERSON", "loc": [-74.145247, 40.912179], "pop": 12152, "state": "NJ", "_id": "07504"} -{"city": "PATERSON", "loc": [-74.171947, 40.915581], "pop": 1837, "state": "NJ", "_id": "07505"} -{"city": "HAWTHORNE", "loc": [-74.156897, 40.956355], "pop": 16920, "state": "NJ", "_id": "07506"} -{"city": "HALEDON", "loc": [-74.182599, 40.945689], "pop": 19757, "state": "NJ", "_id": "07508"} -{"city": "TOTOWA", "loc": [-74.21675, 40.904811], "pop": 10147, "state": "NJ", "_id": "07512"} -{"city": "PATERSON", "loc": [-74.152862, 40.906994], "pop": 10088, "state": "NJ", "_id": "07513"} -{"city": "PATERSON", "loc": [-74.146717, 40.924764], "pop": 18860, "state": "NJ", "_id": "07514"} -{"city": "PATERSON", "loc": [-74.178078, 40.925168], "pop": 21813, "state": "NJ", "_id": "07522"} -{"city": "PATERSON", "loc": [-74.155457, 40.930916], "pop": 11887, "state": "NJ", "_id": "07524"} -{"city": "HACKENSACK", "loc": [-74.050301, 40.888191], "pop": 36963, "state": "NJ", "_id": "07601"} -{"city": "BOGOTA", "loc": [-74.028122, 40.874441], "pop": 7824, "state": "NJ", "_id": "07603"} -{"city": "HASBROUCK HEIGHT", "loc": [-74.075971, 40.862241], "pop": 11499, "state": "NJ", "_id": "07604"} -{"city": "LEONIA", "loc": [-73.987873, 40.862929], "pop": 8326, "state": "NJ", "_id": "07605"} -{"city": "SOUTH HACKENSACK", "loc": [-74.045601, 40.863391], "pop": 2150, "state": "NJ", "_id": "07606"} -{"city": "MAYWOOD", "loc": [-74.062916, 40.902412], "pop": 9473, "state": "NJ", "_id": "07607"} -{"city": "TETERBORO", "loc": [-74.054204, 40.86175], "pop": 22, "state": "NJ", "_id": "07608"} -{"city": "ALPINE", "loc": [-73.930842, 40.951097], "pop": 1716, "state": "NJ", "_id": "07620"} -{"city": "BERGENFIELD", "loc": [-73.998918, 40.923837], "pop": 24613, "state": "NJ", "_id": "07621"} -{"city": "CLOSTER", "loc": [-73.958985, 40.972051], "pop": 8101, "state": "NJ", "_id": "07624"} -{"city": "CRESSKILL", "loc": [-73.965206, 40.941847], "pop": 7633, "state": "NJ", "_id": "07626"} -{"city": "DEMAREST", "loc": [-73.960221, 40.954775], "pop": 4718, "state": "NJ", "_id": "07627"} -{"city": "DUMONT", "loc": [-73.992139, 40.944692], "pop": 17187, "state": "NJ", "_id": "07628"} -{"city": "EMERSON", "loc": [-74.028515, 40.975459], "pop": 6519, "state": "NJ", "_id": "07630"} -{"city": "ENGLEWOOD", "loc": [-73.977182, 40.894251], "pop": 24869, "state": "NJ", "_id": "07631"} -{"city": "ENGLEWOOD CLIFFS", "loc": [-73.954449, 40.882043], "pop": 5644, "state": "NJ", "_id": "07632"} -{"city": "HARRINGTON PARK", "loc": [-73.980017, 40.991791], "pop": 4628, "state": "NJ", "_id": "07640"} -{"city": "HAWORTH", "loc": [-73.987376, 40.960808], "pop": 3384, "state": "NJ", "_id": "07641"} -{"city": "HILLSDALE", "loc": [-74.042625, 41.006945], "pop": 9771, "state": "NJ", "_id": "07642"} -{"city": "LITTLE FERRY", "loc": [-74.040502, 40.849319], "pop": 9970, "state": "NJ", "_id": "07643"} -{"city": "LODI", "loc": [-74.083827, 40.876363], "pop": 22345, "state": "NJ", "_id": "07644"} -{"city": "MONTVALE", "loc": [-74.038362, 41.049458], "pop": 6935, "state": "NJ", "_id": "07645"} -{"city": "NEW MILFORD", "loc": [-74.019517, 40.933115], "pop": 15799, "state": "NJ", "_id": "07646"} -{"city": "ROCKLEIGH", "loc": [-73.952375, 41.011196], "pop": 4833, "state": "NJ", "_id": "07647"} -{"city": "NORWOOD", "loc": [-73.95817, 40.995231], "pop": 4858, "state": "NJ", "_id": "07648"} -{"city": "ORADELL", "loc": [-74.033525, 40.953456], "pop": 8069, "state": "NJ", "_id": "07649"} -{"city": "PALISADES PARK", "loc": [-73.995436, 40.846238], "pop": 14536, "state": "NJ", "_id": "07650"} -{"city": "PARAMUS", "loc": [-74.06724, 40.947683], "pop": 25085, "state": "NJ", "_id": "07652"} -{"city": "PARK RIDGE", "loc": [-74.039574, 41.034349], "pop": 8120, "state": "NJ", "_id": "07656"} -{"city": "RIDGEFIELD", "loc": [-74.001531, 40.832568], "pop": 9753, "state": "NJ", "_id": "07657"} -{"city": "RIDGEFIELD PARK", "loc": [-74.022962, 40.856218], "pop": 12454, "state": "NJ", "_id": "07660"} -{"city": "RIVER EDGE", "loc": [-74.03924, 40.926476], "pop": 10603, "state": "NJ", "_id": "07661"} -{"city": "SADDLE BROOK", "loc": [-74.091296, 40.904928], "pop": 18883, "state": "NJ", "_id": "07662"} -{"city": "TEANECK", "loc": [-74.011928, 40.89148], "pop": 37822, "state": "NJ", "_id": "07666"} -{"city": "TENAFLY", "loc": [-73.965906, 40.921596], "pop": 13297, "state": "NJ", "_id": "07670"} -{"city": "OLD TAPPAN", "loc": [-74.032586, 41.001696], "pop": 39036, "state": "NJ", "_id": "07675"} -{"city": "SUBURBAN", "loc": [-74.080003, 40.354083], "pop": 22320, "state": "NJ", "_id": "07701"} -{"city": "SHREWSBURY", "loc": [-74.058892, 40.328198], "pop": 3096, "state": "NJ", "_id": "07702"} -{"city": "FORT MONMOUTH", "loc": [-74.039001, 40.317667], "pop": 919, "state": "NJ", "_id": "07703"} -{"city": "FAIR HAVEN", "loc": [-74.038891, 40.359873], "pop": 5176, "state": "NJ", "_id": "07704"} -{"city": "ALLENHURST", "loc": [-74.006706, 40.236675], "pop": 2030, "state": "NJ", "_id": "07711"} -{"city": "OCEAN", "loc": [-74.029486, 40.235571], "pop": 35939, "state": "NJ", "_id": "07712"} -{"city": "ATLANTIC HIGHLAN", "loc": [-74.032411, 40.40772], "pop": 8440, "state": "NJ", "_id": "07716"} -{"city": "AVON BY THE SEA", "loc": [-74.016737, 40.191796], "pop": 2165, "state": "NJ", "_id": "07717"} -{"city": "BELFORD", "loc": [-74.088887, 40.417281], "pop": 6601, "state": "NJ", "_id": "07718"} -{"city": "WALL", "loc": [-74.047247, 40.17434], "pop": 19146, "state": "NJ", "_id": "07719"} -{"city": "BRADLEY BEACH", "loc": [-74.013166, 40.202308], "pop": 4475, "state": "NJ", "_id": "07720"} -{"city": "CLIFFWOOD", "loc": [-74.235759, 40.435281], "pop": 2332, "state": "NJ", "_id": "07721"} -{"city": "COLTS NECK", "loc": [-74.177988, 40.301225], "pop": 8623, "state": "NJ", "_id": "07722"} -{"city": "DEAL", "loc": [-74.001998, 40.250568], "pop": 1722, "state": "NJ", "_id": "07723"} -{"city": "EATONTOWN", "loc": [-74.06978, 40.302815], "pop": 23682, "state": "NJ", "_id": "07724"} -{"city": "MANALAPAN", "loc": [-74.330613, 40.306274], "pop": 28928, "state": "NJ", "_id": "07726"} -{"city": "FARMINGDALE", "loc": [-74.177864, 40.204312], "pop": 4794, "state": "NJ", "_id": "07727"} -{"city": "FREEHOLD", "loc": [-74.276822, 40.245776], "pop": 42567, "state": "NJ", "_id": "07728"} -{"city": "HAZLET", "loc": [-74.179896, 40.422554], "pop": 17181, "state": "NJ", "_id": "07730"} -{"city": "HOWELL", "loc": [-74.213683, 40.148096], "pop": 31114, "state": "NJ", "_id": "07731"} -{"city": "FORT HANCOCK", "loc": [-73.990912, 40.402377], "pop": 4962, "state": "NJ", "_id": "07732"} -{"city": "HOLMDEL", "loc": [-74.173971, 40.385853], "pop": 11165, "state": "NJ", "_id": "07733"} -{"city": "KEANSBURG", "loc": [-74.130633, 40.441363], "pop": 21931, "state": "NJ", "_id": "07734"} -{"city": "KEYPORT", "loc": [-74.199563, 40.439862], "pop": 21082, "state": "NJ", "_id": "07735"} -{"city": "LEONARDO", "loc": [-74.062265, 40.417704], "pop": 5027, "state": "NJ", "_id": "07737"} -{"city": "LINCROFT", "loc": [-74.120469, 40.33689], "pop": 6214, "state": "NJ", "_id": "07738"} -{"city": "LITTLE SILVER", "loc": [-74.041319, 40.335393], "pop": 5683, "state": "NJ", "_id": "07739"} -{"city": "LONG BRANCH", "loc": [-73.991176, 40.299204], "pop": 29053, "state": "NJ", "_id": "07740"} -{"city": "MARLBORO", "loc": [-74.263871, 40.31825], "pop": 14956, "state": "NJ", "_id": "07746"} -{"city": "MATAWAN", "loc": [-74.237955, 40.410876], "pop": 26130, "state": "NJ", "_id": "07747"} -{"city": "NEW MONMOUTH", "loc": [-74.113908, 40.396018], "pop": 22727, "state": "NJ", "_id": "07748"} -{"city": "MONMOUTH BEACH", "loc": [-73.98089, 40.333032], "pop": 3329, "state": "NJ", "_id": "07750"} -{"city": "MORGANVILLE", "loc": [-74.277863, 40.352917], "pop": 11680, "state": "NJ", "_id": "07751"} -{"city": "NEPTUNE CITY", "loc": [-74.054045, 40.211153], "pop": 32147, "state": "NJ", "_id": "07753"} -{"city": "OAKHURST", "loc": [-74.018413, 40.26479], "pop": 5637, "state": "NJ", "_id": "07755"} -{"city": "OCEAN GROVE", "loc": [-74.009306, 40.211606], "pop": 4723, "state": "NJ", "_id": "07756"} -{"city": "OCEANPORT", "loc": [-74.016372, 40.31573], "pop": 5227, "state": "NJ", "_id": "07757"} -{"city": "PORT MONMOUTH", "loc": [-74.108259, 40.428886], "pop": 4698, "state": "NJ", "_id": "07758"} -{"city": "SEA BRIGHT", "loc": [-74.000618, 40.371829], "pop": 9700, "state": "NJ", "_id": "07760"} -{"city": "SPRING LAKE", "loc": [-74.037885, 40.154198], "pop": 9384, "state": "NJ", "_id": "07762"} -{"city": "WEST LONG BRANCH", "loc": [-74.016221, 40.287811], "pop": 7666, "state": "NJ", "_id": "07764"} -{"city": "MINE HILL", "loc": [-74.559702, 40.887218], "pop": 25588, "state": "NJ", "_id": "07801"} -{"city": "ANDOVER", "loc": [-74.752418, 40.961386], "pop": 8931, "state": "NJ", "_id": "07821"} -{"city": "AUGUSTA", "loc": [-74.684753, 41.145086], "pop": 1555, "state": "NJ", "_id": "07822"} -{"city": "BELVIDERE", "loc": [-75.050261, 40.830819], "pop": 5790, "state": "NJ", "_id": "07823"} -{"city": "BLAIRSTOWN", "loc": [-74.965097, 40.967386], "pop": 8815, "state": "NJ", "_id": "07825"} -{"city": "BRANCHVILLE", "loc": [-74.750025, 41.170512], "pop": 5009, "state": "NJ", "_id": "07826"} -{"city": "MONTAGUE", "loc": [-74.753956, 41.302259], "pop": 3228, "state": "NJ", "_id": "07827"} -{"city": "BUDD LAKE", "loc": [-74.742552, 40.873108], "pop": 11588, "state": "NJ", "_id": "07828"} -{"city": "CALIFON", "loc": [-74.815218, 40.716209], "pop": 5526, "state": "NJ", "_id": "07830"} -{"city": "COLUMBIA", "loc": [-75.054983, 40.938805], "pop": 3185, "state": "NJ", "_id": "07832"} -{"city": "DENVILLE", "loc": [-74.484379, 40.889735], "pop": 14075, "state": "NJ", "_id": "07834"} -{"city": "FLANDERS", "loc": [-74.70188, 40.845316], "pop": 10548, "state": "NJ", "_id": "07836"} -{"city": "GREAT MEADOWS", "loc": [-74.941806, 40.85196], "pop": 2303, "state": "NJ", "_id": "07838"} -{"city": "HACKETTSTOWN", "loc": [-74.834315, 40.852891], "pop": 23440, "state": "NJ", "_id": "07840"} -{"city": "HOPATCONG", "loc": [-74.66163, 40.938983], "pop": 11920, "state": "NJ", "_id": "07843"} -{"city": "KENVIL", "loc": [-74.620984, 40.881901], "pop": 1356, "state": "NJ", "_id": "07847"} -{"city": "LAFAYETTE", "loc": [-74.691223, 41.076099], "pop": 2992, "state": "NJ", "_id": "07848"} -{"city": "LAKE HOPATCONG", "loc": [-74.614438, 40.965882], "pop": 7241, "state": "NJ", "_id": "07849"} -{"city": "LANDING", "loc": [-74.655425, 40.908684], "pop": 6482, "state": "NJ", "_id": "07850"} -{"city": "LAYTON", "loc": [-74.817219, 41.205913], "pop": 807, "state": "NJ", "_id": "07851"} -{"city": "LEDGEWOOD", "loc": [-74.655405, 40.878028], "pop": 997, "state": "NJ", "_id": "07852"} -{"city": "LONG VALLEY", "loc": [-74.78698, 40.787817], "pop": 12345, "state": "NJ", "_id": "07853"} -{"city": "MOUNT ARLINGTON", "loc": [-74.636331, 40.928267], "pop": 2240, "state": "NJ", "_id": "07856"} -{"city": "NETCONG", "loc": [-74.698454, 40.898497], "pop": 3330, "state": "NJ", "_id": "07857"} -{"city": "FREDON TOWNSHIP", "loc": [-74.780191, 41.058275], "pop": 25022, "state": "NJ", "_id": "07860"} -{"city": "OXFORD", "loc": [-75.00194, 40.810537], "pop": 2610, "state": "NJ", "_id": "07863"} -{"city": "PORT MURRAY", "loc": [-74.91675, 40.790615], "pop": 2124, "state": "NJ", "_id": "07865"} -{"city": "ROCKAWAY", "loc": [-74.50937, 40.922916], "pop": 20388, "state": "NJ", "_id": "07866"} -{"city": "RANDOLPH", "loc": [-74.572519, 40.845557], "pop": 20870, "state": "NJ", "_id": "07869"} -{"city": "SPARTA", "loc": [-74.640701, 41.027697], "pop": 15324, "state": "NJ", "_id": "07871"} -{"city": "STANHOPE", "loc": [-74.70044, 40.921743], "pop": 9375, "state": "NJ", "_id": "07874"} -{"city": "SUCCASUNNA", "loc": [-74.653601, 40.85385], "pop": 10640, "state": "NJ", "_id": "07876"} -{"city": "WALLPACK CENTER", "loc": [-74.917595, 41.134332], "pop": 67, "state": "NJ", "_id": "07881"} -{"city": "WASHINGTON", "loc": [-74.991361, 40.75818], "pop": 13409, "state": "NJ", "_id": "07882"} -{"city": "WHARTON", "loc": [-74.58634, 40.913883], "pop": 7842, "state": "NJ", "_id": "07885"} -{"city": "SUMMIT", "loc": [-74.364159, 40.71494], "pop": 20151, "state": "NJ", "_id": "07901"} -{"city": "BASKING RIDGE", "loc": [-74.560463, 40.678937], "pop": 17810, "state": "NJ", "_id": "07920"} -{"city": "BEDMINSTER", "loc": [-74.643236, 40.657109], "pop": 6515, "state": "NJ", "_id": "07921"} -{"city": "BERKELEY HEIGHTS", "loc": [-74.434599, 40.67522], "pop": 11155, "state": "NJ", "_id": "07922"} -{"city": "BERNARDSVILLE", "loc": [-74.577812, 40.72251], "pop": 6454, "state": "NJ", "_id": "07924"} -{"city": "CEDAR KNOLLS", "loc": [-74.456861, 40.822335], "pop": 3449, "state": "NJ", "_id": "07927"} -{"city": "CHATHAM", "loc": [-74.401701, 40.730526], "pop": 16708, "state": "NJ", "_id": "07928"} -{"city": "CHESTER", "loc": [-74.677649, 40.789193], "pop": 6635, "state": "NJ", "_id": "07930"} -{"city": "FAR HILLS", "loc": [-74.653959, 40.704035], "pop": 2728, "state": "NJ", "_id": "07931"} -{"city": "FLORHAM PARK", "loc": [-74.392819, 40.775701], "pop": 8473, "state": "NJ", "_id": "07932"} -{"city": "GILLETTE", "loc": [-74.468134, 40.687678], "pop": 3296, "state": "NJ", "_id": "07933"} -{"city": "GLADSTONE", "loc": [-74.670656, 40.721948], "pop": 887, "state": "NJ", "_id": "07934"} -{"city": "GREEN VILLAGE", "loc": [-74.451685, 40.741618], "pop": 893, "state": "NJ", "_id": "07935"} -{"city": "EAST HANOVER", "loc": [-74.36357, 40.819165], "pop": 9904, "state": "NJ", "_id": "07936"} -{"city": "MADISON", "loc": [-74.417868, 40.759939], "pop": 15960, "state": "NJ", "_id": "07940"} -{"city": "MENDHAM", "loc": [-74.600035, 40.778919], "pop": 7756, "state": "NJ", "_id": "07945"} -{"city": "MILLINGTON", "loc": [-74.518292, 40.672716], "pop": 2506, "state": "NJ", "_id": "07946"} -{"city": "GREYSTONE PARK", "loc": [-74.479645, 40.843982], "pop": 16528, "state": "NJ", "_id": "07950"} -{"city": "MORRISTOWN", "loc": [-74.487288, 40.795236], "pop": 38516, "state": "NJ", "_id": "07960"} -{"city": "NEW PROVIDENCE", "loc": [-74.402291, 40.700403], "pop": 11838, "state": "NJ", "_id": "07974"} -{"city": "NEW VERNON", "loc": [-74.484471, 40.734685], "pop": 682, "state": "NJ", "_id": "07976"} -{"city": "STIRLING", "loc": [-74.49683, 40.677366], "pop": 2355, "state": "NJ", "_id": "07980"} -{"city": "WHIPPANY", "loc": [-74.419971, 40.821862], "pop": 7233, "state": "NJ", "_id": "07981"} -{"city": "CHERRY HILL", "loc": [-75.017538, 39.930808], "pop": 21271, "state": "NJ", "_id": "08002"} -{"city": "CHERRY HILL", "loc": [-74.970568, 39.880453], "pop": 29058, "state": "NJ", "_id": "08003"} -{"city": "WINSLOW", "loc": [-74.879368, 39.770909], "pop": 14312, "state": "NJ", "_id": "08004"} -{"city": "BARNEGAT", "loc": [-74.246988, 39.755248], "pop": 13036, "state": "NJ", "_id": "08005"} -{"city": "BARRINGTON", "loc": [-75.056361, 39.865062], "pop": 5185, "state": "NJ", "_id": "08007"} -{"city": "HARVEY CEDARS", "loc": [-74.189033, 39.636347], "pop": 8647, "state": "NJ", "_id": "08008"} -{"city": "BERLIN", "loc": [-74.930808, 39.778839], "pop": 14034, "state": "NJ", "_id": "08009"} -{"city": "BEVERLY", "loc": [-74.911363, 40.056452], "pop": 11361, "state": "NJ", "_id": "08010"} -{"city": "WASHINGTON", "loc": [-75.058747, 39.774104], "pop": 35874, "state": "NJ", "_id": "08012"} -{"city": "BRIDGEPORT", "loc": [-75.34782, 39.801616], "pop": 702, "state": "NJ", "_id": "08014"} -{"city": "BROWNS MILLS", "loc": [-74.565549, 39.95974], "pop": 21735, "state": "NJ", "_id": "08015"} -{"city": "BURLINGTON", "loc": [-74.845353, 40.068015], "pop": 25065, "state": "NJ", "_id": "08016"} -{"city": "CHATSWORTH", "loc": [-74.525619, 39.801945], "pop": 215, "state": "NJ", "_id": "08019"} -{"city": "CLARKSBORO", "loc": [-75.223655, 39.799228], "pop": 3169, "state": "NJ", "_id": "08020"} -{"city": "LAUREL SPRINGS", "loc": [-75.003997, 39.80703], "pop": 48953, "state": "NJ", "_id": "08021"} -{"city": "COLUMBUS", "loc": [-74.68988, 40.064238], "pop": 5709, "state": "NJ", "_id": "08022"} -{"city": "GIBBSBORO", "loc": [-74.970996, 39.836532], "pop": 2383, "state": "NJ", "_id": "08026"} -{"city": "GIBBSTOWN", "loc": [-75.275128, 39.82314], "pop": 5102, "state": "NJ", "_id": "08027"} -{"city": "GLASSBORO", "loc": [-75.117247, 39.706823], "pop": 15492, "state": "NJ", "_id": "08028"} -{"city": "GLENDORA", "loc": [-75.069744, 39.840376], "pop": 5205, "state": "NJ", "_id": "08029"} -{"city": "GLOUCESTER CITY", "loc": [-75.116962, 39.891105], "pop": 14454, "state": "NJ", "_id": "08030"} -{"city": "BELLMAWR", "loc": [-75.094368, 39.868878], "pop": 12568, "state": "NJ", "_id": "08031"} -{"city": "HADDONFIELD", "loc": [-75.041726, 39.895449], "pop": 17496, "state": "NJ", "_id": "08033"} -{"city": "CHERRY HILL", "loc": [-75.000762, 39.9074], "pop": 18836, "state": "NJ", "_id": "08034"} -{"city": "HADDON HEIGHTS", "loc": [-75.06639, 39.878832], "pop": 8034, "state": "NJ", "_id": "08035"} -{"city": "BATSTO", "loc": [-74.790735, 39.638878], "pop": 22484, "state": "NJ", "_id": "08037"} -{"city": "JOBSTOWN", "loc": [-74.687305, 40.038698], "pop": 115, "state": "NJ", "_id": "08041"} -{"city": "VOORHEES", "loc": [-74.964614, 39.850422], "pop": 21308, "state": "NJ", "_id": "08043"} -{"city": "LAWNSIDE", "loc": [-75.031681, 39.867601], "pop": 2840, "state": "NJ", "_id": "08045"} -{"city": "WILLINGBORO", "loc": [-74.883482, 40.028959], "pop": 36471, "state": "NJ", "_id": "08046"} -{"city": "LUMBERTON", "loc": [-74.806736, 39.96512], "pop": 170, "state": "NJ", "_id": "08048"} -{"city": "MAGNOLIA", "loc": [-75.039254, 39.853807], "pop": 5931, "state": "NJ", "_id": "08049"} -{"city": "MANAHAWKIN", "loc": [-74.260391, 39.705017], "pop": 12188, "state": "NJ", "_id": "08050"} -{"city": "MANTUA", "loc": [-75.178531, 39.786983], "pop": 8080, "state": "NJ", "_id": "08051"} -{"city": "MAPLE SHADE", "loc": [-74.994644, 39.951085], "pop": 19365, "state": "NJ", "_id": "08052"} -{"city": "MARLTON", "loc": [-74.90674, 39.884517], "pop": 33299, "state": "NJ", "_id": "08053"} -{"city": "MOUNT LAUREL", "loc": [-74.903588, 39.947808], "pop": 29805, "state": "NJ", "_id": "08054"} -{"city": "MEDFORD LAKES", "loc": [-74.823384, 39.868099], "pop": 25033, "state": "NJ", "_id": "08055"} -{"city": "MICKLETON", "loc": [-75.249777, 39.785653], "pop": 1850, "state": "NJ", "_id": "08056"} -{"city": "MOORESTOWN", "loc": [-74.953323, 39.9683], "pop": 16092, "state": "NJ", "_id": "08057"} -{"city": "MOUNT EPHRAIM", "loc": [-75.09289, 39.882749], "pop": 5267, "state": "NJ", "_id": "08059"} -{"city": "EASTAMPTON TWP", "loc": [-74.795522, 39.993028], "pop": 31606, "state": "NJ", "_id": "08060"} -{"city": "MOUNT ROYAL", "loc": [-75.208153, 39.809741], "pop": 239, "state": "NJ", "_id": "08061"} -{"city": "MULLICA HILL", "loc": [-75.20654, 39.725228], "pop": 8720, "state": "NJ", "_id": "08062"} -{"city": "NATIONAL PARK", "loc": [-75.179397, 39.866412], "pop": 3398, "state": "NJ", "_id": "08063"} -{"city": "PALMYRA", "loc": [-75.025685, 40.003654], "pop": 6916, "state": "NJ", "_id": "08065"} -{"city": "PAULSBORO", "loc": [-75.224233, 39.831157], "pop": 10269, "state": "NJ", "_id": "08066"} -{"city": "PEDRICKTOWN", "loc": [-75.412046, 39.743451], "pop": 1565, "state": "NJ", "_id": "08067"} -{"city": "PEMBERTON", "loc": [-74.667629, 39.971206], "pop": 11000, "state": "NJ", "_id": "08068"} -{"city": "CARNEYS POINT", "loc": [-75.465623, 39.717938], "pop": 14138, "state": "NJ", "_id": "08069"} -{"city": "PENNSVILLE", "loc": [-75.51553, 39.649107], "pop": 12717, "state": "NJ", "_id": "08070"} -{"city": "PITMAN", "loc": [-75.129679, 39.731162], "pop": 9697, "state": "NJ", "_id": "08071"} -{"city": "DELANCO", "loc": [-74.956128, 40.024684], "pop": 24468, "state": "NJ", "_id": "08075"} -{"city": "CINNAMINSON", "loc": [-74.995141, 39.996393], "pop": 17498, "state": "NJ", "_id": "08077"} -{"city": "RUNNEMEDE", "loc": [-75.074224, 39.850825], "pop": 8589, "state": "NJ", "_id": "08078"} -{"city": "SALEM", "loc": [-75.452096, 39.559124], "pop": 14129, "state": "NJ", "_id": "08079"} -{"city": "SEWELL", "loc": [-75.089852, 39.747345], "pop": 23121, "state": "NJ", "_id": "08080"} -{"city": "SICKLERVILLE", "loc": [-74.986385, 39.735385], "pop": 29949, "state": "NJ", "_id": "08081"} -{"city": "SOMERDALE", "loc": [-75.030913, 39.839988], "pop": 11913, "state": "NJ", "_id": "08083"} -{"city": "STRATFORD", "loc": [-75.014707, 39.828798], "pop": 7217, "state": "NJ", "_id": "08084"} -{"city": "SWEDESBORO", "loc": [-75.336202, 39.752853], "pop": 7968, "state": "NJ", "_id": "08085"} -{"city": "THOROFARE", "loc": [-75.176698, 39.859178], "pop": 3395, "state": "NJ", "_id": "08086"} -{"city": "TUCKERTON", "loc": [-74.364586, 39.588149], "pop": 17728, "state": "NJ", "_id": "08087"} -{"city": "SOUTHAMPTON", "loc": [-74.711025, 39.867631], "pop": 25480, "state": "NJ", "_id": "08088"} -{"city": "WATERFORD WORKS", "loc": [-74.860933, 39.721512], "pop": 4344, "state": "NJ", "_id": "08089"} -{"city": "WENONAH", "loc": [-75.153644, 39.799283], "pop": 9129, "state": "NJ", "_id": "08090"} -{"city": "WEST BERLIN", "loc": [-74.941678, 39.81959], "pop": 5244, "state": "NJ", "_id": "08091"} -{"city": "WEST CREEK", "loc": [-74.288508, 39.662665], "pop": 2502, "state": "NJ", "_id": "08092"} -{"city": "WESTVILLE", "loc": [-75.132278, 39.860494], "pop": 9696, "state": "NJ", "_id": "08093"} -{"city": "WILLIAMSTOWN", "loc": [-74.971027, 39.665006], "pop": 30978, "state": "NJ", "_id": "08094"} -{"city": "DEPTFORD", "loc": [-75.137966, 39.829477], "pop": 34147, "state": "NJ", "_id": "08096"} -{"city": "WOODBURY HEIGHTS", "loc": [-75.152972, 39.814162], "pop": 3625, "state": "NJ", "_id": "08097"} -{"city": "WOODSTOWN", "loc": [-75.324806, 39.645663], "pop": 8013, "state": "NJ", "_id": "08098"} -{"city": "CAMDEN", "loc": [-75.118644, 39.951244], "pop": 10107, "state": "NJ", "_id": "08102"} -{"city": "CAMDEN", "loc": [-75.111708, 39.935099], "pop": 17818, "state": "NJ", "_id": "08103"} -{"city": "CAMDEN", "loc": [-75.107835, 39.918575], "pop": 26375, "state": "NJ", "_id": "08104"} -{"city": "CAMDEN", "loc": [-75.086382, 39.948417], "pop": 34450, "state": "NJ", "_id": "08105"} -{"city": "AUDUBON", "loc": [-75.072425, 39.891035], "pop": 10355, "state": "NJ", "_id": "08106"} -{"city": "OAKLYN", "loc": [-75.08489, 39.90799], "pop": 14161, "state": "NJ", "_id": "08107"} -{"city": "COLLINGSWOOD", "loc": [-75.063383, 39.915682], "pop": 18125, "state": "NJ", "_id": "08108"} -{"city": "MERCHANTVILLE", "loc": [-75.048204, 39.95193], "pop": 22294, "state": "NJ", "_id": "08109"} -{"city": "DELAIR", "loc": [-75.063446, 39.962742], "pop": 15447, "state": "NJ", "_id": "08110"} -{"city": "SMITHVILLE", "loc": [-74.503106, 39.462368], "pop": 26645, "state": "NJ", "_id": "08201"} -{"city": "AVALON", "loc": [-74.726177, 39.095095], "pop": 1813, "state": "NJ", "_id": "08202"} -{"city": "BRIGANTINE", "loc": [-74.377644, 39.403709], "pop": 9388, "state": "NJ", "_id": "08203"} -{"city": "NORTH CAPE MAY", "loc": [-74.92624, 38.969208], "pop": 18084, "state": "NJ", "_id": "08204"} -{"city": "CAPE MAY COURT H", "loc": [-74.826846, 39.101434], "pop": 13521, "state": "NJ", "_id": "08210"} -{"city": "EGG HARBOR CITY", "loc": [-74.617709, 39.533123], "pop": 10362, "state": "NJ", "_id": "08215"} -{"city": "LINWOOD", "loc": [-74.580744, 39.346883], "pop": 9353, "state": "NJ", "_id": "08221"} -{"city": "MARMORA", "loc": [-74.659297, 39.258562], "pop": 4420, "state": "NJ", "_id": "08223"} -{"city": "NORTHFIELD", "loc": [-74.555237, 39.370256], "pop": 7503, "state": "NJ", "_id": "08225"} -{"city": "OCEAN CITY", "loc": [-74.587514, 39.270894], "pop": 15512, "state": "NJ", "_id": "08226"} -{"city": "OCEAN VIEW", "loc": [-74.707548, 39.215425], "pop": 4443, "state": "NJ", "_id": "08230"} -{"city": "PLEASANTVILLE", "loc": [-74.552365, 39.401518], "pop": 30292, "state": "NJ", "_id": "08232"} -{"city": "PORT REPUBLIC", "loc": [-74.490263, 39.527196], "pop": 992, "state": "NJ", "_id": "08241"} -{"city": "RIO GRANDE", "loc": [-74.875642, 39.019583], "pop": 2943, "state": "NJ", "_id": "08242"} -{"city": "TOWNSENDS INLET", "loc": [-74.701432, 39.144929], "pop": 2675, "state": "NJ", "_id": "08243"} -{"city": "SOMERS POINT", "loc": [-74.600796, 39.322308], "pop": 11227, "state": "NJ", "_id": "08244"} -{"city": "STONE HARBOR", "loc": [-74.762049, 39.053338], "pop": 1076, "state": "NJ", "_id": "08247"} -{"city": "STRATHMERE", "loc": [-74.655446, 39.199246], "pop": 163, "state": "NJ", "_id": "08248"} -{"city": "VILLAS", "loc": [-74.935396, 39.021943], "pop": 9308, "state": "NJ", "_id": "08251"} -{"city": "NORTH WILDWOOD", "loc": [-74.819096, 38.989901], "pop": 14194, "state": "NJ", "_id": "08260"} -{"city": "CORBIN CITY", "loc": [-74.802628, 39.248915], "pop": 7370, "state": "NJ", "_id": "08270"} -{"city": "SEABROOK", "loc": [-75.226728, 39.445294], "pop": 45485, "state": "NJ", "_id": "08302"} -{"city": "BUENA", "loc": [-74.889495, 39.536988], "pop": 1501, "state": "NJ", "_id": "08310"} -{"city": "CEDARVILLE", "loc": [-75.199362, 39.337028], "pop": 1521, "state": "NJ", "_id": "08311"} -{"city": "CLAYTON", "loc": [-75.094188, 39.658969], "pop": 6584, "state": "NJ", "_id": "08312"} -{"city": "DELMONT", "loc": [-74.970505, 39.202258], "pop": 135, "state": "NJ", "_id": "08314"} -{"city": "DOROTHY", "loc": [-74.831577, 39.40313], "pop": 1018, "state": "NJ", "_id": "08317"} -{"city": "ELMER", "loc": [-75.163023, 39.569146], "pop": 8050, "state": "NJ", "_id": "08318"} -{"city": "ESTELL MANOR", "loc": [-74.816512, 39.37825], "pop": 1130, "state": "NJ", "_id": "08319"} -{"city": "FRANKLINVILLE", "loc": [-75.04088, 39.615557], "pop": 10484, "state": "NJ", "_id": "08322"} -{"city": "HEISLERVILLE", "loc": [-74.993942, 39.245013], "pop": 264, "state": "NJ", "_id": "08324"} -{"city": "LANDISVILLE", "loc": [-74.937688, 39.523942], "pop": 1915, "state": "NJ", "_id": "08326"} -{"city": "LEESBURG", "loc": [-75.001211, 39.238724], "pop": 49, "state": "NJ", "_id": "08327"} -{"city": "MALAGA", "loc": [-75.058155, 39.575495], "pop": 1348, "state": "NJ", "_id": "08328"} -{"city": "MAYS LANDING", "loc": [-74.69619, 39.432011], "pop": 24487, "state": "NJ", "_id": "08330"} -{"city": "MILLVILLE", "loc": [-75.029311, 39.367313], "pop": 36591, "state": "NJ", "_id": "08332"} -{"city": "MILMAY", "loc": [-74.866671, 39.445078], "pop": 463, "state": "NJ", "_id": "08340"} -{"city": "MINOTOLA", "loc": [-74.946685, 39.515512], "pop": 1548, "state": "NJ", "_id": "08341"} -{"city": "MONROEVILLE", "loc": [-75.156848, 39.644224], "pop": 5068, "state": "NJ", "_id": "08343"} -{"city": "NEWFIELD", "loc": [-75.027558, 39.555257], "pop": 5363, "state": "NJ", "_id": "08344"} -{"city": "NEWPORT", "loc": [-75.171647, 39.283205], "pop": 1190, "state": "NJ", "_id": "08345"} -{"city": "NEWTONVILLE", "loc": [-74.859049, 39.564477], "pop": 682, "state": "NJ", "_id": "08346"} -{"city": "PORT NORRIS", "loc": [-75.050608, 39.256263], "pop": 2726, "state": "NJ", "_id": "08349"} -{"city": "RICHLAND", "loc": [-74.877615, 39.485048], "pop": 1046, "state": "NJ", "_id": "08350"} -{"city": "VINELAND", "loc": [-75.009087, 39.48177], "pop": 56319, "state": "NJ", "_id": "08360"} -{"city": "ATLANTIC CITY", "loc": [-74.431727, 39.366411], "pop": 31907, "state": "NJ", "_id": "08401"} -{"city": "MARGATE CITY", "loc": [-74.509038, 39.328621], "pop": 9484, "state": "NJ", "_id": "08402"} -{"city": "LONGPORT", "loc": [-74.542143, 39.309712], "pop": 318, "state": "NJ", "_id": "08403"} -{"city": "VENTNOR CITY", "loc": [-74.472343, 39.345992], "pop": 19050, "state": "NJ", "_id": "08406"} -{"city": "ALLENTOWN", "loc": [-74.59093, 40.158922], "pop": 4041, "state": "NJ", "_id": "08501"} -{"city": "BELLE MEAD", "loc": [-74.628993, 40.467732], "pop": 12677, "state": "NJ", "_id": "08502"} -{"city": "BORDENTOWN", "loc": [-74.703249, 40.143141], "pop": 12835, "state": "NJ", "_id": "08505"} -{"city": "CLARKSBURG", "loc": [-74.434387, 40.199751], "pop": 3364, "state": "NJ", "_id": "08510"} -{"city": "COOKSTOWN", "loc": [-74.559524, 40.048109], "pop": 262, "state": "NJ", "_id": "08511"} -{"city": "CRANBURY", "loc": [-74.506531, 40.303868], "pop": 13743, "state": "NJ", "_id": "08512"} -{"city": "CREAMRIDGE", "loc": [-74.499425, 40.127907], "pop": 3522, "state": "NJ", "_id": "08514"} -{"city": "CROSSWICKS", "loc": [-74.666838, 40.158353], "pop": 1700, "state": "NJ", "_id": "08515"} -{"city": "FLORENCE", "loc": [-74.805515, 40.118025], "pop": 5091, "state": "NJ", "_id": "08518"} -{"city": "HIGHTSTOWN", "loc": [-74.524993, 40.266885], "pop": 25674, "state": "NJ", "_id": "08520"} -{"city": "HOPEWELL", "loc": [-74.770963, 40.390224], "pop": 3858, "state": "NJ", "_id": "08525"} -{"city": "IMLAYSTOWN", "loc": [-74.510939, 40.166215], "pop": 17, "state": "NJ", "_id": "08526"} -{"city": "JACKSON", "loc": [-74.301709, 40.120964], "pop": 33075, "state": "NJ", "_id": "08527"} -{"city": "KINGSTON", "loc": [-74.616071, 40.380465], "pop": 160, "state": "NJ", "_id": "08528"} -{"city": "LAMBERTVILLE", "loc": [-74.926605, 40.373122], "pop": 6466, "state": "NJ", "_id": "08530"} -{"city": "NEW EGYPT", "loc": [-74.506721, 40.071343], "pop": 4601, "state": "NJ", "_id": "08533"} -{"city": "PENNINGTON", "loc": [-74.794352, 40.333858], "pop": 7690, "state": "NJ", "_id": "08534"} -{"city": "PERRINEVILLE", "loc": [-74.440381, 40.214971], "pop": 329, "state": "NJ", "_id": "08535"} -{"city": "PLAINSBORO", "loc": [-74.568836, 40.332432], "pop": 13008, "state": "NJ", "_id": "08536"} -{"city": "PRINCETON", "loc": [-74.640832, 40.366633], "pop": 33831, "state": "NJ", "_id": "08540"} -{"city": "PRINCETON", "loc": [-74.659378, 40.353545], "pop": 2675, "state": "NJ", "_id": "08542"} -{"city": "PRINCETON UNIVER", "loc": [-74.65754, 40.346029], "pop": 4227, "state": "NJ", "_id": "08544"} -{"city": "PRINCETON JUNCTI", "loc": [-74.614596, 40.297684], "pop": 5807, "state": "NJ", "_id": "08550"} -{"city": "RINGOES", "loc": [-74.828839, 40.44587], "pop": 5106, "state": "NJ", "_id": "08551"} -{"city": "ROCKY HILL", "loc": [-74.640042, 40.400985], "pop": 693, "state": "NJ", "_id": "08553"} -{"city": "ROEBLING", "loc": [-74.777224, 40.115352], "pop": 3826, "state": "NJ", "_id": "08554"} -{"city": "ROSEMONT", "loc": [-74.991928, 40.422005], "pop": 42, "state": "NJ", "_id": "08556"} -{"city": "SKILLMAN", "loc": [-74.693828, 40.417312], "pop": 3935, "state": "NJ", "_id": "08558"} -{"city": "STOCKTON", "loc": [-74.955419, 40.43974], "pop": 6090, "state": "NJ", "_id": "08559"} -{"city": "TITUSVILLE", "loc": [-74.865469, 40.307728], "pop": 3354, "state": "NJ", "_id": "08560"} -{"city": "WRIGHTSTOWN", "loc": [-74.573098, 40.071953], "pop": 3116, "state": "NJ", "_id": "08562"} -{"city": "TRENTON", "loc": [-74.762237, 40.220437], "pop": 827, "state": "NJ", "_id": "08608"} -{"city": "HAMILTON", "loc": [-74.742598, 40.223338], "pop": 15904, "state": "NJ", "_id": "08609"} -{"city": "HAMILTON", "loc": [-74.717205, 40.19894], "pop": 29852, "state": "NJ", "_id": "08610"} -{"city": "HAMILTON", "loc": [-74.751997, 40.207297], "pop": 25084, "state": "NJ", "_id": "08611"} -{"city": "TRENTON", "loc": [-74.782062, 40.237687], "pop": 40280, "state": "NJ", "_id": "08618"} -{"city": "MERCERVILLE", "loc": [-74.690377, 40.241977], "pop": 23627, "state": "NJ", "_id": "08619"} -{"city": "YARDVILLE", "loc": [-74.671699, 40.178477], "pop": 12373, "state": "NJ", "_id": "08620"} -{"city": "WEST TRENTON", "loc": [-74.826186, 40.267232], "pop": 8335, "state": "NJ", "_id": "08628"} -{"city": "HAMILTON", "loc": [-74.732764, 40.219843], "pop": 10670, "state": "NJ", "_id": "08629"} -{"city": "TRENTON", "loc": [-74.762699, 40.251006], "pop": 26982, "state": "NJ", "_id": "08638"} -{"city": "FORT DIX", "loc": [-74.618296, 40.009946], "pop": 10101, "state": "NJ", "_id": "08640"} -{"city": "MC GUIRE AFB", "loc": [-74.588195, 40.044026], "pop": 9971, "state": "NJ", "_id": "08641"} -{"city": "LAWRENCEVILLE", "loc": [-74.723956, 40.277646], "pop": 25497, "state": "NJ", "_id": "08648"} -{"city": "HAMILTON", "loc": [-74.659138, 40.223852], "pop": 21547, "state": "NJ", "_id": "08690"} -{"city": "HAMILTON", "loc": [-74.606262, 40.231785], "pop": 6524, "state": "NJ", "_id": "08691"} -{"city": "LAKEWOOD", "loc": [-74.204199, 40.085043], "pop": 47458, "state": "NJ", "_id": "08701"} -{"city": "BAYVILLE", "loc": [-74.190529, 39.914708], "pop": 18991, "state": "NJ", "_id": "08721"} -{"city": "BEACHWOOD", "loc": [-74.196145, 39.930246], "pop": 9324, "state": "NJ", "_id": "08722"} -{"city": "OSBORNSVILLE", "loc": [-74.12686, 40.040817], "pop": 28128, "state": "NJ", "_id": "08723"} -{"city": "BRICK", "loc": [-74.115237, 40.087432], "pop": 34507, "state": "NJ", "_id": "08724"} -{"city": "BRIELLE", "loc": [-74.063511, 40.107727], "pop": 4290, "state": "NJ", "_id": "08730"} -{"city": "FORKED RIVER", "loc": [-74.197301, 39.844425], "pop": 16664, "state": "NJ", "_id": "08731"} -{"city": "ISLAND HEIGHTS", "loc": [-74.146787, 39.943197], "pop": 1470, "state": "NJ", "_id": "08732"} -{"city": "LAKEHURST NAEC", "loc": [-74.290901, 40.020054], "pop": 14205, "state": "NJ", "_id": "08733"} -{"city": "LANOKA HARBOR", "loc": [-74.166765, 39.861993], "pop": 5473, "state": "NJ", "_id": "08734"} -{"city": "LAVALLETTE", "loc": [-74.070424, 39.977486], "pop": 3985, "state": "NJ", "_id": "08735"} -{"city": "MANASQUAN", "loc": [-74.061055, 40.121693], "pop": 10341, "state": "NJ", "_id": "08736"} -{"city": "MANTOLOKING", "loc": [-74.056188, 40.026125], "pop": 1186, "state": "NJ", "_id": "08738"} -{"city": "OCEAN GATE", "loc": [-74.135128, 39.925975], "pop": 2028, "state": "NJ", "_id": "08740"} -{"city": "PINE BEACH", "loc": [-74.167992, 39.934746], "pop": 2354, "state": "NJ", "_id": "08741"} -{"city": "BAY HEAD", "loc": [-74.065719, 40.080226], "pop": 25071, "state": "NJ", "_id": "08742"} -{"city": "SEA GIRT", "loc": [-74.043604, 40.134522], "pop": 3624, "state": "NJ", "_id": "08750"} -{"city": "SEASIDE HEIGHTS", "loc": [-74.076479, 39.946639], "pop": 4044, "state": "NJ", "_id": "08751"} -{"city": "SEASIDE PARK", "loc": [-74.079521, 39.922175], "pop": 2369, "state": "NJ", "_id": "08752"} -{"city": "TOMS RIVER", "loc": [-74.156508, 39.977083], "pop": 58001, "state": "NJ", "_id": "08753"} -{"city": "TOMS RIVER", "loc": [-74.222819, 39.999946], "pop": 14892, "state": "NJ", "_id": "08755"} -{"city": "TOMS RIVER", "loc": [-74.251168, 39.971471], "pop": 29794, "state": "NJ", "_id": "08757"} -{"city": "WARETOWN", "loc": [-74.195376, 39.789646], "pop": 4774, "state": "NJ", "_id": "08758"} -{"city": "WHITING", "loc": [-74.360713, 39.950983], "pop": 16321, "state": "NJ", "_id": "08759"} -{"city": "ANNANDALE", "loc": [-74.885512, 40.628731], "pop": 6465, "state": "NJ", "_id": "08801"} -{"city": "PATTENBURG", "loc": [-75.01585, 40.650175], "pop": 2176, "state": "NJ", "_id": "08802"} -{"city": "BLOOMSBURY", "loc": [-75.09664, 40.64366], "pop": 2111, "state": "NJ", "_id": "08804"} -{"city": "BOUND BROOK", "loc": [-74.539735, 40.568115], "pop": 11275, "state": "NJ", "_id": "08805"} -{"city": "BRIDGEWATER", "loc": [-74.626741, 40.590388], "pop": 26496, "state": "NJ", "_id": "08807"} -{"city": "CLINTON", "loc": [-74.908846, 40.641194], "pop": 2994, "state": "NJ", "_id": "08809"} -{"city": "DAYTON", "loc": [-74.511102, 40.38249], "pop": 6198, "state": "NJ", "_id": "08810"} -{"city": "GREEN BROOK", "loc": [-74.47187, 40.594237], "pop": 10892, "state": "NJ", "_id": "08812"} -{"city": "EAST BRUNSWICK", "loc": [-74.406381, 40.428395], "pop": 42319, "state": "NJ", "_id": "08816"} -{"city": "EDISON", "loc": [-74.397286, 40.517079], "pop": 41245, "state": "NJ", "_id": "08817"} -{"city": "EDISON", "loc": [-74.358863, 40.57804], "pop": 33581, "state": "NJ", "_id": "08820"} -{"city": "FLEMINGTON", "loc": [-74.845316, 40.517976], "pop": 23579, "state": "NJ", "_id": "08822"} -{"city": "FRANKLIN PARK", "loc": [-74.536908, 40.442097], "pop": 154, "state": "NJ", "_id": "08823"} -{"city": "KENDALL PARK", "loc": [-74.552921, 40.4208], "pop": 10272, "state": "NJ", "_id": "08824"} -{"city": "FRENCHTOWN", "loc": [-75.032468, 40.520795], "pop": 4383, "state": "NJ", "_id": "08825"} -{"city": "GLEN GARDNER", "loc": [-74.916207, 40.713437], "pop": 5525, "state": "NJ", "_id": "08826"} -{"city": "HAMPTON", "loc": [-74.962221, 40.677351], "pop": 4441, "state": "NJ", "_id": "08827"} -{"city": "HELMETTA", "loc": [-74.420393, 40.377742], "pop": 1211, "state": "NJ", "_id": "08828"} -{"city": "HIGH BRIDGE", "loc": [-74.893667, 40.668438], "pop": 3886, "state": "NJ", "_id": "08829"} -{"city": "ISELIN", "loc": [-74.316677, 40.571593], "pop": 16313, "state": "NJ", "_id": "08830"} -{"city": "JAMESBURG", "loc": [-74.433568, 40.342475], "pop": 15743, "state": "NJ", "_id": "08831"} -{"city": "KEASBEY", "loc": [-74.302119, 40.519171], "pop": 2746, "state": "NJ", "_id": "08832"} -{"city": "LEBANON", "loc": [-74.829035, 40.646623], "pop": 7270, "state": "NJ", "_id": "08833"} -{"city": "MANVILLE", "loc": [-74.593377, 40.539871], "pop": 10419, "state": "NJ", "_id": "08835"} -{"city": "MARTINSVILLE", "loc": [-74.557191, 40.599962], "pop": 3024, "state": "NJ", "_id": "08836"} -{"city": "EDISON", "loc": [-74.337503, 40.532476], "pop": 9447, "state": "NJ", "_id": "08837"} -{"city": "METUCHEN", "loc": [-74.351721, 40.54493], "pop": 20017, "state": "NJ", "_id": "08840"} -{"city": "MIDDLESEX", "loc": [-74.500835, 40.575882], "pop": 13055, "state": "NJ", "_id": "08846"} -{"city": "MILFORD", "loc": [-75.102519, 40.592942], "pop": 7586, "state": "NJ", "_id": "08848"} -{"city": "MILLTOWN", "loc": [-74.439046, 40.449346], "pop": 8362, "state": "NJ", "_id": "08850"} -{"city": "MONMOUTH JUNCTIO", "loc": [-74.547021, 40.394391], "pop": 6881, "state": "NJ", "_id": "08852"} -{"city": "NESHANIC STATION", "loc": [-74.71434, 40.509551], "pop": 9526, "state": "NJ", "_id": "08853"} -{"city": "PISCATAWAY", "loc": [-74.458996, 40.55152], "pop": 47038, "state": "NJ", "_id": "08854"} -{"city": "OLD BRIDGE", "loc": [-74.323553, 40.398045], "pop": 33884, "state": "NJ", "_id": "08857"} -{"city": "PARLIN", "loc": [-74.304981, 40.458701], "pop": 20773, "state": "NJ", "_id": "08859"} -{"city": "PERTH AMBOY", "loc": [-74.275427, 40.517551], "pop": 43920, "state": "NJ", "_id": "08861"} -{"city": "FORDS", "loc": [-74.311707, 40.53925], "pop": 13568, "state": "NJ", "_id": "08863"} -{"city": "ALPHA", "loc": [-75.1741, 40.692819], "pop": 28782, "state": "NJ", "_id": "08865"} -{"city": "PITTSTOWN", "loc": [-74.957587, 40.599169], "pop": 5635, "state": "NJ", "_id": "08867"} -{"city": "RARITAN", "loc": [-74.637691, 40.57109], "pop": 6306, "state": "NJ", "_id": "08869"} -{"city": "SAYREVILLE", "loc": [-74.347808, 40.459958], "pop": 14405, "state": "NJ", "_id": "08872"} -{"city": "SOMERSET", "loc": [-74.50126, 40.500743], "pop": 36850, "state": "NJ", "_id": "08873"} -{"city": "NORTH BRANCH", "loc": [-74.645926, 40.549393], "pop": 34212, "state": "NJ", "_id": "08876"} -{"city": "LAURENCE HARBOR", "loc": [-74.278635, 40.464733], "pop": 20993, "state": "NJ", "_id": "08879"} -{"city": "SOUTH BOUND BROO", "loc": [-74.529078, 40.552804], "pop": 4330, "state": "NJ", "_id": "08880"} -{"city": "SOUTH RIVER", "loc": [-74.380099, 40.444363], "pop": 13692, "state": "NJ", "_id": "08882"} -{"city": "SPOTSWOOD", "loc": [-74.389377, 40.384679], "pop": 13444, "state": "NJ", "_id": "08884"} -{"city": "STEWARTSVILLE", "loc": [-75.082517, 40.695703], "pop": 3582, "state": "NJ", "_id": "08886"} -{"city": "THREE BRIDGES", "loc": [-74.799805, 40.52156], "pop": 378, "state": "NJ", "_id": "08887"} -{"city": "WHITEHOUSE STATI", "loc": [-74.764129, 40.599872], "pop": 8303, "state": "NJ", "_id": "08889"} -{"city": "NEW BRUNSWICK", "loc": [-74.448193, 40.489073], "pop": 41710, "state": "NJ", "_id": "08901"} -{"city": "NORTH BRUNSWICK", "loc": [-74.482285, 40.453767], "pop": 31071, "state": "NJ", "_id": "08902"} -{"city": "HIGHLAND PARK", "loc": [-74.426602, 40.499141], "pop": 13165, "state": "NJ", "_id": "08904"} -{"city": "ALGODONES", "loc": [-106.616589, 35.428527], "pop": 0, "state": "NM", "_id": "87001"} -{"city": "BOYS RANCH", "loc": [-106.761215, 34.645562], "pop": 14826, "state": "NM", "_id": "87002"} -{"city": "BERNALILLO", "loc": [-106.530873, 35.328506], "pop": 7113, "state": "NM", "_id": "87004"} -{"city": "BLUEWATER", "loc": [-108.191663, 35.164724], "pop": 1611, "state": "NM", "_id": "87005"} -{"city": "BOSQUE", "loc": [-106.8038, 34.49956], "pop": 276, "state": "NM", "_id": "87006"} -{"city": "CASA BLANCA", "loc": [-107.433907, 35.035222], "pop": 2986, "state": "NM", "_id": "87007"} -{"city": "CEDAR CREST", "loc": [-106.361737, 35.128506], "pop": 1860, "state": "NM", "_id": "87008"} -{"city": "CEDARVALE", "loc": [-105.734516, 34.388028], "pop": 66, "state": "NM", "_id": "87009"} -{"city": "CERRILLOS", "loc": [-106.131683, 35.422965], "pop": 788, "state": "NM", "_id": "87010"} -{"city": "CUBA", "loc": [-107.188462, 36.048201], "pop": 2764, "state": "NM", "_id": "87013"} -{"city": "CUBERO", "loc": [-107.856722, 35.117372], "pop": 16704, "state": "NM", "_id": "87014"} -{"city": "EDGEWOOD", "loc": [-106.187207, 35.077603], "pop": 3032, "state": "NM", "_id": "87015"} -{"city": "ESTANCIA", "loc": [-106.135024, 34.769983], "pop": 2634, "state": "NM", "_id": "87016"} -{"city": "GALLINA", "loc": [-106.661388, 36.171429], "pop": 1003, "state": "NM", "_id": "87017"} -{"city": "COUNSELOR", "loc": [-106.949789, 36.118386], "pop": 566, "state": "NM", "_id": "87018"} -{"city": "GRANTS", "loc": [-107.935662, 35.359802], "pop": 428, "state": "NM", "_id": "87020"} -{"city": "JARALES", "loc": [-107.029912, 34.648156], "pop": 9, "state": "NM", "_id": "87023"} -{"city": "JEMEZ PUEBLO", "loc": [-106.721894, 35.624315], "pop": 2256, "state": "NM", "_id": "87024"} -{"city": "JEMEZ SPRINGS", "loc": [-106.771393, 35.892689], "pop": 1, "state": "NM", "_id": "87025"} -{"city": "CANONCITO", "loc": [-107.105843, 35.085305], "pop": 1272, "state": "NM", "_id": "87026"} -{"city": "LA JARA", "loc": [-106.992335, 36.119383], "pop": 5, "state": "NM", "_id": "87027"} -{"city": "LAJOYA", "loc": [-106.84279, 34.343444], "pop": 74, "state": "NM", "_id": "87028"} -{"city": "LINDRITH", "loc": [-106.925352, 36.336141], "pop": 574, "state": "NM", "_id": "87029"} -{"city": "LOS LUNAS", "loc": [-106.711537, 34.780607], "pop": 23560, "state": "NM", "_id": "87031"} -{"city": "MORIARTY", "loc": [-106.060871, 34.988878], "pop": 5570, "state": "NM", "_id": "87035"} -{"city": "MOUNTAINAIR", "loc": [-106.257738, 34.515775], "pop": 1442, "state": "NM", "_id": "87036"} -{"city": "COCHITI PUEBLO", "loc": [-106.343472, 35.603104], "pop": 1527, "state": "NM", "_id": "87041"} -{"city": "PERALTA", "loc": [-106.687159, 34.833188], "pop": 2672, "state": "NM", "_id": "87042"} -{"city": "PLACITAS", "loc": [-106.529279, 35.309175], "pop": 8357, "state": "NM", "_id": "87043"} -{"city": "PONDEROSA", "loc": [-106.654421, 35.797576], "pop": 1092, "state": "NM", "_id": "87044"} -{"city": "PREWITT", "loc": [-108.103798, 35.35466], "pop": 1114, "state": "NM", "_id": "87045"} -{"city": "REGINA", "loc": [-107.071932, 36.20283], "pop": 0, "state": "NM", "_id": "87046"} -{"city": "SANDIA PARK", "loc": [-106.323846, 35.168275], "pop": 2672, "state": "NM", "_id": "87047"} -{"city": "CORRALES", "loc": [-106.620034, 35.233888], "pop": 4900, "state": "NM", "_id": "87048"} -{"city": "SAN MATEO", "loc": [-107.70498, 35.222614], "pop": 912, "state": "NM", "_id": "87050"} -{"city": "SANTO DOMINGO PU", "loc": [-106.361275, 35.513892], "pop": 3030, "state": "NM", "_id": "87052"} -{"city": "ZIA PUEBLO", "loc": [-106.737467, 35.524241], "pop": 877, "state": "NM", "_id": "87053"} -{"city": "SEBOYETA", "loc": [-107.382281, 35.149322], "pop": 1356, "state": "NM", "_id": "87055"} -{"city": "STANLEY", "loc": [-106.028299, 35.12948], "pop": 552, "state": "NM", "_id": "87056"} -{"city": "TIJERAS", "loc": [-106.306226, 35.044563], "pop": 6861, "state": "NM", "_id": "87059"} -{"city": "VEGUITA", "loc": [-106.759148, 34.485032], "pop": 817, "state": "NM", "_id": "87062"} -{"city": "WILLARD", "loc": [-106.030706, 34.57543], "pop": 276, "state": "NM", "_id": "87063"} -{"city": "BOSQUE FARMS", "loc": [-106.697506, 34.876366], "pop": 6286, "state": "NM", "_id": "87068"} -{"city": "ALBUQUERQUE", "loc": [-106.648171, 35.081831], "pop": 20645, "state": "NM", "_id": "87102"} -{"city": "ALBUQUERQUE", "loc": [-106.671215, 35.103822], "pop": 11889, "state": "NM", "_id": "87104"} -{"city": "ALBUQUERQUE", "loc": [-106.689341, 35.044761], "pop": 50233, "state": "NM", "_id": "87105"} -{"city": "ALBUQUERQUE", "loc": [-106.616917, 35.079011], "pop": 26482, "state": "NM", "_id": "87106"} -{"city": "ALBUQUERQUE", "loc": [-106.642747, 35.134742], "pop": 30302, "state": "NM", "_id": "87107"} -{"city": "ALBUQUERQUE", "loc": [-106.574864, 35.072586], "pop": 36704, "state": "NM", "_id": "87108"} -{"city": "ALBUQUERQUE", "loc": [-106.569004, 35.15058], "pop": 39310, "state": "NM", "_id": "87109"} -{"city": "ALBUQUERQUE", "loc": [-106.578052, 35.110417], "pop": 42652, "state": "NM", "_id": "87110"} -{"city": "ALBUQUERQUE", "loc": [-106.522164, 35.134724], "pop": 47455, "state": "NM", "_id": "87111"} -{"city": "ALBUQUERQUE", "loc": [-106.518338, 35.101026], "pop": 45478, "state": "NM", "_id": "87112"} -{"city": "ALBUQUERQUE", "loc": [-106.601467, 35.175906], "pop": 3622, "state": "NM", "_id": "87113"} -{"city": "ALAMEDA", "loc": [-106.659138, 35.195612], "pop": 16352, "state": "NM", "_id": "87114"} -{"city": "KIRTLAND A F B E", "loc": [-106.513896, 34.904876], "pop": 0, "state": "NM", "_id": "87115"} -{"city": "ALBUQUERQUE", "loc": [-106.550605, 35.056116], "pop": 5761, "state": "NM", "_id": "87116"} -{"city": "ALBUQUERQUE", "loc": [-106.595802, 35.055443], "pop": 3067, "state": "NM", "_id": "87118"} -{"city": "ALBUQUERQUE", "loc": [-106.704137, 35.142146], "pop": 23796, "state": "NM", "_id": "87120"} -{"city": "ALBUQUERQUE", "loc": [-106.726861, 35.051209], "pop": 22253, "state": "NM", "_id": "87121"} -{"city": "ALBUQUERQUE", "loc": [-106.510176, 35.178715], "pop": 6127, "state": "NM", "_id": "87122"} -{"city": "ALBUQUERQUE", "loc": [-106.509003, 35.07166], "pop": 33808, "state": "NM", "_id": "87123"} -{"city": "RIO RANCHO", "loc": [-106.681756, 35.249347], "pop": 29586, "state": "NM", "_id": "87124"} -{"city": "GALLUP", "loc": [-108.741352, 35.506475], "pop": 32311, "state": "NM", "_id": "87301"} -{"city": "BRIMHALL", "loc": [-108.581469, 35.800503], "pop": 119, "state": "NM", "_id": "87310"} -{"city": "CONTINENTAL DIVI", "loc": [-108.469256, 35.504295], "pop": 704, "state": "NM", "_id": "87312"} -{"city": "CROWNPOINT", "loc": [-108.02709, 35.720557], "pop": 6400, "state": "NM", "_id": "87313"} -{"city": "FENCE LAKE", "loc": [-108.693392, 34.734385], "pop": 112, "state": "NM", "_id": "87315"} -{"city": "MEXICAN SPRINGS", "loc": [-108.818964, 35.783727], "pop": 562, "state": "NM", "_id": "87320"} -{"city": "RAMAH", "loc": [-108.491951, 35.132375], "pop": 517, "state": "NM", "_id": "87321"} -{"city": "THOREAU", "loc": [-107.830218, 35.57869], "pop": 4254, "state": "NM", "_id": "87323"} -{"city": "TOADLENA", "loc": [-108.750647, 36.073202], "pop": 2097, "state": "NM", "_id": "87324"} -{"city": "TOHATCHI", "loc": [-108.687818, 35.768609], "pop": 5648, "state": "NM", "_id": "87325"} -{"city": "ZUNI", "loc": [-108.833611, 35.06845], "pop": 7382, "state": "NM", "_id": "87327"} -{"city": "NAVAJO", "loc": [-109.022621, 35.894193], "pop": 2715, "state": "NM", "_id": "87328"} -{"city": "FARMINGTON", "loc": [-108.199531, 36.706514], "pop": 36782, "state": "NM", "_id": "87401"} -{"city": "FARMINGTON", "loc": [-108.147766, 36.768503], "pop": 7238, "state": "NM", "_id": "87402"} -{"city": "AZTEC", "loc": [-108.010982, 36.820499], "pop": 10951, "state": "NM", "_id": "87410"} -{"city": "BLANCO", "loc": [-107.795276, 36.725612], "pop": 1007, "state": "NM", "_id": "87412"} -{"city": "BLOOMFIELD", "loc": [-107.978437, 36.695475], "pop": 9612, "state": "NM", "_id": "87413"} -{"city": "FLORA VISTA", "loc": [-108.082683, 36.802771], "pop": 2634, "state": "NM", "_id": "87415"} -{"city": "FRUITLAND", "loc": [-108.45339, 36.76323], "pop": 819, "state": "NM", "_id": "87416"} -{"city": "KIRTLAND", "loc": [-108.350956, 36.740953], "pop": 5087, "state": "NM", "_id": "87417"} -{"city": "LA PLATA", "loc": [-108.179243, 36.957632], "pop": 163, "state": "NM", "_id": "87418"} -{"city": "NAVAJO DAM", "loc": [-107.670991, 36.833838], "pop": 32, "state": "NM", "_id": "87419"} -{"city": "SHIPROCK", "loc": [-108.735479, 36.655981], "pop": 14805, "state": "NM", "_id": "87420"} -{"city": "WATERFLOW", "loc": [-108.431083, 36.780092], "pop": 378, "state": "NM", "_id": "87421"} -{"city": "POJOAQUE VALLEY", "loc": [-105.974818, 35.702472], "pop": 51715, "state": "NM", "_id": "87501"} -{"city": "SANTA FE", "loc": [-105.981994, 35.619623], "pop": 34054, "state": "NM", "_id": "87505"} -{"city": "ABIQUIU", "loc": [-106.244859, 36.176923], "pop": 1538, "state": "NM", "_id": "87510"} -{"city": "ARROYO HONDO", "loc": [-105.621998, 36.531848], "pop": 1333, "state": "NM", "_id": "87513"} -{"city": "ARROYO SECO", "loc": [-105.594609, 36.504568], "pop": 782, "state": "NM", "_id": "87514"} -{"city": "CHAMA", "loc": [-106.582928, 36.896214], "pop": 1103, "state": "NM", "_id": "87520"} -{"city": "CHAMISAL", "loc": [-105.68461, 36.160411], "pop": 2147, "state": "NM", "_id": "87521"} -{"city": "CUNDIYO", "loc": [-105.911441, 36.011906], "pop": 4380, "state": "NM", "_id": "87522"} -{"city": "COSTILLA", "loc": [-105.501586, 36.952556], "pop": 495, "state": "NM", "_id": "87524"} -{"city": "DIXON", "loc": [-105.86148, 36.179], "pop": 1320, "state": "NM", "_id": "87527"} -{"city": "DULCE", "loc": [-107.060179, 36.859808], "pop": 2936, "state": "NM", "_id": "87528"} -{"city": "EL RITO", "loc": [-106.21917, 36.364694], "pop": 928, "state": "NM", "_id": "87530"} -{"city": "EMBUDO", "loc": [-106.024187, 36.137028], "pop": 3377, "state": "NM", "_id": "87531"} -{"city": "ESPANOLA", "loc": [-106.071697, 35.987157], "pop": 9437, "state": "NM", "_id": "87532"} -{"city": "GLORIETA", "loc": [-105.790316, 35.566645], "pop": 1246, "state": "NM", "_id": "87535"} -{"city": "HERNANDEZ", "loc": [-106.139579, 36.073811], "pop": 832, "state": "NM", "_id": "87537"} -{"city": "LA MADERA", "loc": [-106.13443, 36.246387], "pop": 75, "state": "NM", "_id": "87539"} -{"city": "LAMY", "loc": [-105.940906, 35.431057], "pop": 516, "state": "NM", "_id": "87540"} -{"city": "LOS ALAMOS", "loc": [-106.267624, 35.866321], "pop": 18122, "state": "NM", "_id": "87544"} -{"city": "OJO CALIENTE", "loc": [-105.915507, 36.40991], "pop": 453, "state": "NM", "_id": "87549"} -{"city": "PECOS", "loc": [-105.670104, 35.571011], "pop": 2534, "state": "NM", "_id": "87552"} -{"city": "PENASCO", "loc": [-105.734245, 36.176023], "pop": 632, "state": "NM", "_id": "87553"} -{"city": "QUESTA", "loc": [-105.560984, 36.72572], "pop": 2881, "state": "NM", "_id": "87556"} -{"city": "RANCHOS DE TAOS", "loc": [-105.608576, 36.335692], "pop": 174, "state": "NM", "_id": "87557"} -{"city": "RIBERA", "loc": [-105.465228, 35.334448], "pop": 1619, "state": "NM", "_id": "87560"} -{"city": "RUTHERON", "loc": [-106.685267, 36.647146], "pop": 38, "state": "NM", "_id": "87563"} -{"city": "SAN CRISTOBAL", "loc": [-105.635968, 36.611638], "pop": 326, "state": "NM", "_id": "87564"} -{"city": "SAN JOSE", "loc": [-105.438341, 35.456949], "pop": 844, "state": "NM", "_id": "87565"} -{"city": "SAN JUAN PUEBLO", "loc": [-106.079267, 36.048886], "pop": 5209, "state": "NM", "_id": "87566"} -{"city": "SANTA CRUZ", "loc": [-106.031839, 35.986011], "pop": 5947, "state": "NM", "_id": "87567"} -{"city": "TAOS", "loc": [-105.584732, 36.395288], "pop": 13297, "state": "NM", "_id": "87571"} -{"city": "TERERRO", "loc": [-105.645725, 35.736881], "pop": 99, "state": "NM", "_id": "87573"} -{"city": "TIERRA AMARILLA", "loc": [-106.556972, 36.680481], "pop": 1865, "state": "NM", "_id": "87575"} -{"city": "VADITO", "loc": [-105.67818, 36.191305], "pop": 360, "state": "NM", "_id": "87579"} -{"city": "VALDEZ", "loc": [-105.506971, 36.569699], "pop": 238, "state": "NM", "_id": "87580"} -{"city": "VALLECITOS", "loc": [-106.085942, 36.516284], "pop": 576, "state": "NM", "_id": "87581"} -{"city": "LAS VEGAS", "loc": [-105.227162, 35.594862], "pop": 19567, "state": "NM", "_id": "87701"} -{"city": "ANTON CHICO", "loc": [-105.141041, 35.159262], "pop": 296, "state": "NM", "_id": "87711"} -{"city": "CHACON", "loc": [-105.385411, 36.138805], "pop": 180, "state": "NM", "_id": "87713"} -{"city": "CIMARRON", "loc": [-105.069694, 36.457424], "pop": 1582, "state": "NM", "_id": "87714"} -{"city": "CLEVELAND", "loc": [-105.43262, 35.989831], "pop": 228, "state": "NM", "_id": "87715"} -{"city": "EAGLE NEST", "loc": [-105.275561, 36.53255], "pop": 463, "state": "NM", "_id": "87718"} -{"city": "GUADALUPITA", "loc": [-105.127814, 36.113046], "pop": 52, "state": "NM", "_id": "87722"} -{"city": "LA LOMA", "loc": [-105.096864, 35.191562], "pop": 233, "state": "NM", "_id": "87724"} -{"city": "LEDOUX", "loc": [-105.421555, 35.919118], "pop": 179, "state": "NM", "_id": "87725"} -{"city": "MAXWELL", "loc": [-104.56395, 36.543393], "pop": 390, "state": "NM", "_id": "87728"} -{"city": "MIAMI", "loc": [-104.811769, 36.28955], "pop": 65, "state": "NM", "_id": "87729"} -{"city": "MILLS", "loc": [-104.227743, 36.128144], "pop": 21, "state": "NM", "_id": "87730"} -{"city": "MONTEZUMA", "loc": [-105.409729, 35.682959], "pop": 62, "state": "NM", "_id": "87731"} -{"city": "MORA", "loc": [-105.312837, 36.00405], "pop": 2636, "state": "NM", "_id": "87732"} -{"city": "ALBERT", "loc": [-103.746132, 35.715938], "pop": 373, "state": "NM", "_id": "87733"} -{"city": "OCATE", "loc": [-105.06595, 36.10366], "pop": 327, "state": "NM", "_id": "87734"} -{"city": "RATON", "loc": [-104.434881, 36.895187], "pop": 8449, "state": "NM", "_id": "87740"} -{"city": "ROCIADA", "loc": [-105.314716, 35.822648], "pop": 480, "state": "NM", "_id": "87742"} -{"city": "ROY", "loc": [-104.149698, 35.952248], "pop": 499, "state": "NM", "_id": "87743"} -{"city": "SAPELLO", "loc": [-105.107735, 35.762012], "pop": 2, "state": "NM", "_id": "87745"} -{"city": "SOLANO", "loc": [-104.1221, 35.837828], "pop": 87, "state": "NM", "_id": "87746"} -{"city": "SPRINGER", "loc": [-104.592705, 36.37672], "pop": 1976, "state": "NM", "_id": "87747"} -{"city": "VALMORA", "loc": [-104.911587, 35.794033], "pop": 138, "state": "NM", "_id": "87750"} -{"city": "WAGON MOUND", "loc": [-104.691, 35.981361], "pop": 524, "state": "NM", "_id": "87752"} -{"city": "SOCORRO", "loc": [-106.890659, 34.047853], "pop": 9663, "state": "NM", "_id": "87801"} -{"city": "BINGHAM", "loc": [-106.021163, 34.043864], "pop": 33, "state": "NM", "_id": "87815"} -{"city": "ARAGON", "loc": [-108.546589, 33.88592], "pop": 149, "state": "NM", "_id": "87820"} -{"city": "DATIL", "loc": [-108.018284, 34.04269], "pop": 340, "state": "NM", "_id": "87821"} -{"city": "LEMITAR", "loc": [-106.904381, 34.155999], "pop": 439, "state": "NM", "_id": "87823"} -{"city": "ALAMO", "loc": [-107.340647, 34.241178], "pop": 2715, "state": "NM", "_id": "87825"} -{"city": "PIE TOWN", "loc": [-108.368187, 34.324282], "pop": 396, "state": "NM", "_id": "87827"} -{"city": "POLVADERA", "loc": [-106.897505, 34.134672], "pop": 225, "state": "NM", "_id": "87828"} -{"city": "QUEMADO", "loc": [-108.757835, 34.265158], "pop": 188, "state": "NM", "_id": "87829"} -{"city": "RESERVE", "loc": [-108.800918, 33.688074], "pop": 1172, "state": "NM", "_id": "87830"} -{"city": "SAN ACACIA", "loc": [-106.904884, 34.228328], "pop": 522, "state": "NM", "_id": "87831"} -{"city": "TRUTH OR CONSEQU", "loc": [-107.248794, 33.139478], "pop": 7794, "state": "NM", "_id": "87901"} -{"city": "ARREY", "loc": [-107.354368, 32.802057], "pop": 555, "state": "NM", "_id": "87930"} -{"city": "CABALLO", "loc": [-107.347113, 33.013452], "pop": 860, "state": "NM", "_id": "87931"} -{"city": "CUCHILLO", "loc": [-107.456536, 33.370269], "pop": 125, "state": "NM", "_id": "87932"} -{"city": "DERRY", "loc": [-107.284271, 32.808561], "pop": 118, "state": "NM", "_id": "87933"} -{"city": "GARFIELD", "loc": [-107.270409, 32.755749], "pop": 432, "state": "NM", "_id": "87936"} -{"city": "HATCH", "loc": [-107.159028, 32.658335], "pop": 2314, "state": "NM", "_id": "87937"} -{"city": "RINCON", "loc": [-107.064343, 32.65969], "pop": 394, "state": "NM", "_id": "87940"} -{"city": "SALEM", "loc": [-107.215689, 32.709322], "pop": 709, "state": "NM", "_id": "87941"} -{"city": "WILLIAMSBURG", "loc": [-107.337583, 33.226498], "pop": 58, "state": "NM", "_id": "87942"} -{"city": "WINSTON", "loc": [-107.667456, 33.306089], "pop": 97, "state": "NM", "_id": "87943"} -{"city": "LAS CRUCES", "loc": [-106.746034, 32.321641], "pop": 57502, "state": "NM", "_id": "88001"} -{"city": "WHITE SANDS MISS", "loc": [-106.49236, 32.382669], "pop": 2616, "state": "NM", "_id": "88002"} -{"city": "LAS CRUCES", "loc": [-106.79908, 32.316076], "pop": 40042, "state": "NM", "_id": "88005"} -{"city": "ANIMAS", "loc": [-108.585695, 31.793696], "pop": 1291, "state": "NM", "_id": "88020"} -{"city": "CHAPARRAL", "loc": [-106.573452, 31.934505], "pop": 24480, "state": "NM", "_id": "88021"} -{"city": "VANADIUM", "loc": [-108.107667, 32.756334], "pop": 3694, "state": "NM", "_id": "88023"} -{"city": "BUCKHORN", "loc": [-108.477951, 32.813466], "pop": 1822, "state": "NM", "_id": "88025"} -{"city": "CENTRAL", "loc": [-108.162698, 32.779767], "pop": 3161, "state": "NM", "_id": "88026"} -{"city": "DEMING", "loc": [-107.746559, 32.231808], "pop": 18110, "state": "NM", "_id": "88030"} -{"city": "GLENWOOD", "loc": [-108.774282, 33.304789], "pop": 318, "state": "NM", "_id": "88039"} -{"city": "SAN LORENZO", "loc": [-108.082185, 32.853311], "pop": 271, "state": "NM", "_id": "88041"} -{"city": "HILLSBORO", "loc": [-107.6276, 32.923913], "pop": 305, "state": "NM", "_id": "88042"} -{"city": "HURLEY", "loc": [-108.15162, 32.64799], "pop": 1775, "state": "NM", "_id": "88043"} -{"city": "LA MESA", "loc": [-106.701103, 32.082312], "pop": 3370, "state": "NM", "_id": "88044"} -{"city": "ROAD FORKS", "loc": [-108.754657, 32.314032], "pop": 4667, "state": "NM", "_id": "88045"} -{"city": "MESILLA PARK", "loc": [-106.637016, 32.297725], "pop": 83, "state": "NM", "_id": "88047"} -{"city": "MESQUITE", "loc": [-106.667626, 32.145958], "pop": 3568, "state": "NM", "_id": "88048"} -{"city": "MIMBRES", "loc": [-107.942326, 32.860936], "pop": 818, "state": "NM", "_id": "88049"} -{"city": "SILVER CITY", "loc": [-108.274916, 32.789986], "pop": 16135, "state": "NM", "_id": "88061"} -{"city": "CLOVIS", "loc": [-103.221391, 34.412585], "pop": 39152, "state": "NM", "_id": "88101"} -{"city": "BROADVIEW", "loc": [-103.129235, 34.803956], "pop": 175, "state": "NM", "_id": "88112"} -{"city": "CAUSEY", "loc": [-103.150201, 33.892926], "pop": 229, "state": "NM", "_id": "88113"} -{"city": "CROSSROADS", "loc": [-103.356387, 33.527186], "pop": 17, "state": "NM", "_id": "88114"} -{"city": "ELIDA", "loc": [-103.632341, 33.940468], "pop": 503, "state": "NM", "_id": "88116"} -{"city": "FLOYD", "loc": [-103.582707, 34.251951], "pop": 681, "state": "NM", "_id": "88118"} -{"city": "FORT SUMNER", "loc": [-104.231689, 34.460018], "pop": 1934, "state": "NM", "_id": "88119"} -{"city": "GRADY", "loc": [-103.298043, 34.810697], "pop": 265, "state": "NM", "_id": "88120"} -{"city": "HOUSE", "loc": [-103.920256, 34.695156], "pop": 235, "state": "NM", "_id": "88121"} -{"city": "LINGO", "loc": [-103.208161, 33.696291], "pop": 52, "state": "NM", "_id": "88123"} -{"city": "MELROSE", "loc": [-103.632507, 34.44787], "pop": 976, "state": "NM", "_id": "88124"} -{"city": "MILNESAND", "loc": [-103.278189, 33.60501], "pop": 10, "state": "NM", "_id": "88125"} -{"city": "PEP", "loc": [-103.327208, 33.897032], "pop": 369, "state": "NM", "_id": "88126"} -{"city": "PORTALES", "loc": [-103.336311, 34.179915], "pop": 14828, "state": "NM", "_id": "88130"} -{"city": "ROGERS", "loc": [-103.127527, 34.024759], "pop": 30, "state": "NM", "_id": "88132"} -{"city": "SAINT VRAIN", "loc": [-103.453239, 34.465205], "pop": 188, "state": "NM", "_id": "88133"} -{"city": "TAIBAN", "loc": [-104.032899, 34.424829], "pop": 94, "state": "NM", "_id": "88134"} -{"city": "TEXICO", "loc": [-103.061544, 34.395795], "pop": 1451, "state": "NM", "_id": "88135"} -{"city": "YESO", "loc": [-104.527582, 34.474962], "pop": 224, "state": "NM", "_id": "88136"} -{"city": "ROSWELL", "loc": [-104.525857, 33.388504], "pop": 53644, "state": "NM", "_id": "88201"} -{"city": "ARTESIA", "loc": [-104.407401, 32.838355], "pop": 14689, "state": "NM", "_id": "88210"} -{"city": "CAPROCK", "loc": [-103.639009, 33.393855], "pop": 19, "state": "NM", "_id": "88213"} -{"city": "CARLSBAD", "loc": [-104.239539, 32.411867], "pop": 31888, "state": "NM", "_id": "88220"} -{"city": "DEXTER", "loc": [-104.383285, 33.191006], "pop": 2056, "state": "NM", "_id": "88230"} -{"city": "EUNICE", "loc": [-103.159407, 32.439225], "pop": 3014, "state": "NM", "_id": "88231"} -{"city": "HAGERMAN", "loc": [-104.329778, 33.107643], "pop": 1409, "state": "NM", "_id": "88232"} -{"city": "HOBBS", "loc": [-103.137246, 32.72217], "pop": 36880, "state": "NM", "_id": "88240"} -{"city": "HOPE", "loc": [-104.729921, 32.81561], "pop": 177, "state": "NM", "_id": "88250"} -{"city": "JAL", "loc": [-103.199724, 32.112816], "pop": 2335, "state": "NM", "_id": "88252"} -{"city": "LAKE ARTHUR", "loc": [-104.406412, 33.017106], "pop": 740, "state": "NM", "_id": "88253"} -{"city": "LOVING", "loc": [-104.091376, 32.274034], "pop": 1851, "state": "NM", "_id": "88256"} -{"city": "LOVINGTON", "loc": [-103.348849, 32.951166], "pop": 12046, "state": "NM", "_id": "88260"} -{"city": "MALJAMAR", "loc": [-103.723401, 32.842281], "pop": 61, "state": "NM", "_id": "88264"} -{"city": "MONUMENT", "loc": [-103.270256, 32.587405], "pop": 0, "state": "NM", "_id": "88265"} -{"city": "OIL CENTER", "loc": [-103.301915, 32.490383], "pop": 0, "state": "NM", "_id": "88266"} -{"city": "TATUM", "loc": [-103.314, 33.260018], "pop": 1393, "state": "NM", "_id": "88267"} -{"city": "CARRIZOZO", "loc": [-105.869617, 33.64962], "pop": 1288, "state": "NM", "_id": "88301"} -{"city": "ALAMOGORDO", "loc": [-105.948544, 32.893186], "pop": 31204, "state": "NM", "_id": "88310"} -{"city": "BENT", "loc": [-105.863472, 33.148014], "pop": 172, "state": "NM", "_id": "88314"} -{"city": "CAPITAN", "loc": [-105.526906, 33.560232], "pop": 1760, "state": "NM", "_id": "88316"} -{"city": "CLOUDCROFT", "loc": [-105.939028, 32.574067], "pop": 4482, "state": "NM", "_id": "88317"} -{"city": "CORONA", "loc": [-105.533873, 34.169259], "pop": 496, "state": "NM", "_id": "88318"} -{"city": "ENCINO", "loc": [-105.483914, 34.61496], "pop": 290, "state": "NM", "_id": "88321"} -{"city": "GLENCOE", "loc": [-105.502991, 33.386738], "pop": 68, "state": "NM", "_id": "88324"} -{"city": "HOLLOMAN AIR FOR", "loc": [-106.07845, 32.862093], "pop": 5896, "state": "NM", "_id": "88330"} -{"city": "HONDO", "loc": [-105.388854, 33.462518], "pop": 160, "state": "NM", "_id": "88336"} -{"city": "LA LUZ", "loc": [-105.953397, 32.977449], "pop": 2302, "state": "NM", "_id": "88337"} -{"city": "LINCOLN", "loc": [-105.528427, 33.492912], "pop": 150, "state": "NM", "_id": "88338"} -{"city": "MAYHILL", "loc": [-105.518056, 32.907048], "pop": 237, "state": "NM", "_id": "88339"} -{"city": "MESCALERO", "loc": [-105.774293, 33.216436], "pop": 2695, "state": "NM", "_id": "88340"} -{"city": "NOGAL", "loc": [-105.703631, 33.499552], "pop": 312, "state": "NM", "_id": "88341"} -{"city": "PICACHO", "loc": [-105.083382, 33.345642], "pop": 38, "state": "NM", "_id": "88343"} -{"city": "PINON", "loc": [-105.415708, 32.639584], "pop": 92, "state": "NM", "_id": "88344"} -{"city": "RUIDOSO", "loc": [-105.650994, 33.347406], "pop": 6883, "state": "NM", "_id": "88345"} -{"city": "RUIDOSO DOWNS", "loc": [-105.538306, 33.357718], "pop": 367, "state": "NM", "_id": "88346"} -{"city": "SACRAMENTO", "loc": [-105.621128, 32.805221], "pop": 69, "state": "NM", "_id": "88347"} -{"city": "SAN PATRICIO", "loc": [-105.349798, 33.38775], "pop": 532, "state": "NM", "_id": "88348"} -{"city": "TINNIE", "loc": [-105.202681, 33.337308], "pop": 165, "state": "NM", "_id": "88351"} -{"city": "TULAROSA", "loc": [-106.010789, 33.065049], "pop": 4583, "state": "NM", "_id": "88352"} -{"city": "VAUGHN", "loc": [-105.192558, 34.624074], "pop": 741, "state": "NM", "_id": "88353"} -{"city": "WEED", "loc": [-105.506296, 32.80519], "pop": 196, "state": "NM", "_id": "88354"} -{"city": "TUCUMCARI", "loc": [-103.717935, 35.168003], "pop": 8634, "state": "NM", "_id": "88401"} -{"city": "AMISTAD", "loc": [-103.212954, 35.898276], "pop": 170, "state": "NM", "_id": "88410"} -{"city": "BARD", "loc": [-103.317778, 35.115364], "pop": 358, "state": "NM", "_id": "88411"} -{"city": "BUEYEROS", "loc": [-103.666894, 36.013541], "pop": 7, "state": "NM", "_id": "88412"} -{"city": "CAPULIN", "loc": [-103.993599, 36.747453], "pop": 62, "state": "NM", "_id": "88414"} -{"city": "CLAYTON", "loc": [-103.188823, 36.441378], "pop": 2886, "state": "NM", "_id": "88415"} -{"city": "CONCHAS DAM", "loc": [-104.205231, 35.378561], "pop": 218, "state": "NM", "_id": "88416"} -{"city": "CUERVO", "loc": [-104.399867, 35.01013], "pop": 66, "state": "NM", "_id": "88417"} -{"city": "DES MOINES", "loc": [-103.873853, 36.729069], "pop": 278, "state": "NM", "_id": "88418"} -{"city": "FOLSOM", "loc": [-103.839925, 36.86896], "pop": 172, "state": "NM", "_id": "88419"} -{"city": "GARITA", "loc": [-104.441911, 35.350182], "pop": 113, "state": "NM", "_id": "88421"} -{"city": "GLADSTONE", "loc": [-103.893081, 36.307741], "pop": 45, "state": "NM", "_id": "88422"} -{"city": "GRENVILLE", "loc": [-103.415521, 36.725396], "pop": 185, "state": "NM", "_id": "88424"} -{"city": "LOGAN", "loc": [-103.438339, 35.368051], "pop": 962, "state": "NM", "_id": "88426"} -{"city": "MC ALISTER", "loc": [-103.657169, 34.749206], "pop": 171, "state": "NM", "_id": "88427"} -{"city": "MOUNT DORA", "loc": [-103.416667, 36.498795], "pop": 27, "state": "NM", "_id": "88429"} -{"city": "NARA VISA", "loc": [-103.131738, 35.617963], "pop": 170, "state": "NM", "_id": "88430"} -{"city": "NEWKIRK", "loc": [-104.243323, 35.084746], "pop": 40, "state": "NM", "_id": "88431"} -{"city": "PUERTO DE LUNA", "loc": [-104.619645, 34.847826], "pop": 66, "state": "NM", "_id": "88432"} -{"city": "QUAY", "loc": [-103.78176, 34.929916], "pop": 10, "state": "NM", "_id": "88433"} -{"city": "SAN JON", "loc": [-103.284599, 35.119848], "pop": 283, "state": "NM", "_id": "88434"} -{"city": "PASTURA", "loc": [-104.682155, 34.933276], "pop": 2714, "state": "NM", "_id": "88435"} -{"city": "SEDAN", "loc": [-103.14488, 36.226027], "pop": 105, "state": "NM", "_id": "88436"} -{"city": "SENECA", "loc": [-103.089394, 36.6279], "pop": 54, "state": "NM", "_id": "88437"} -{"city": "STEAD", "loc": [-103.121117, 36.12359], "pop": 140, "state": "NM", "_id": "88438"} -{"city": "TREMENTINA", "loc": [-104.616341, 35.584945], "pop": 125, "state": "NM", "_id": "88439"} -{"city": "BELL RANCH", "loc": [-104.111991, 35.638425], "pop": 80, "state": "NM", "_id": "88441"} -{"city": "FISHERS ISLAND", "loc": [-72.017834, 41.263934], "pop": 329, "state": "NY", "_id": "06390"} -{"city": "NEW YORK", "loc": [-73.996705, 40.74838], "pop": 18913, "state": "NY", "_id": "10001"} -{"city": "NEW YORK", "loc": [-73.987681, 40.715231], "pop": 84143, "state": "NY", "_id": "10002"} -{"city": "NEW YORK", "loc": [-73.989223, 40.731253], "pop": 51224, "state": "NY", "_id": "10003"} -{"city": "GOVERNORS ISLAND", "loc": [-74.019025, 40.693604], "pop": 3593, "state": "NY", "_id": "10004"} -{"city": "NEW YORK", "loc": [-74.008344, 40.705649], "pop": 202, "state": "NY", "_id": "10005"} -{"city": "NEW YORK", "loc": [-74.013474, 40.708451], "pop": 119, "state": "NY", "_id": "10006"} -{"city": "NEW YORK", "loc": [-74.007022, 40.713905], "pop": 3374, "state": "NY", "_id": "10007"} -{"city": "NEW YORK", "loc": [-73.979591, 40.726188], "pop": 57426, "state": "NY", "_id": "10009"} -{"city": "NEW YORK", "loc": [-73.981328, 40.737476], "pop": 24907, "state": "NY", "_id": "10010"} -{"city": "NEW YORK", "loc": [-73.99963, 40.740225], "pop": 46560, "state": "NY", "_id": "10011"} -{"city": "NEW YORK", "loc": [-73.998284, 40.72553], "pop": 26365, "state": "NY", "_id": "10012"} -{"city": "NEW YORK", "loc": [-74.002529, 40.718511], "pop": 21860, "state": "NY", "_id": "10013"} -{"city": "NEW YORK", "loc": [-74.005421, 40.73393], "pop": 31147, "state": "NY", "_id": "10014"} -{"city": "NEW YORK", "loc": [-73.978134, 40.744281], "pop": 51561, "state": "NY", "_id": "10016"} -{"city": "NEW YORK", "loc": [-73.970661, 40.75172], "pop": 12465, "state": "NY", "_id": "10017"} -{"city": "NEW YORK", "loc": [-73.992503, 40.754713], "pop": 4834, "state": "NY", "_id": "10018"} -{"city": "NEW YORK", "loc": [-73.985834, 40.765069], "pop": 36602, "state": "NY", "_id": "10019"} -{"city": "NEW YORK", "loc": [-73.982347, 40.759729], "pop": 393, "state": "NY", "_id": "10020"} -{"city": "NEW YORK", "loc": [-73.958805, 40.768476], "pop": 106564, "state": "NY", "_id": "10021"} -{"city": "NEW YORK", "loc": [-73.965703, 40.757091], "pop": 31870, "state": "NY", "_id": "10022"} -{"city": "NEW YORK", "loc": [-73.982652, 40.77638], "pop": 57385, "state": "NY", "_id": "10023"} -{"city": "NEW YORK", "loc": [-73.976385, 40.786446], "pop": 65141, "state": "NY", "_id": "10024"} -{"city": "NEW YORK", "loc": [-73.968312, 40.797466], "pop": 100027, "state": "NY", "_id": "10025"} -{"city": "NEW YORK", "loc": [-73.953069, 40.801942], "pop": 28453, "state": "NY", "_id": "10026"} -{"city": "NEW YORK", "loc": [-73.954978, 40.811556], "pop": 54631, "state": "NY", "_id": "10027"} -{"city": "NEW YORK", "loc": [-73.952866, 40.776267], "pop": 42757, "state": "NY", "_id": "10028"} -{"city": "NEW YORK", "loc": [-73.94475, 40.791817], "pop": 74643, "state": "NY", "_id": "10029"} -{"city": "NEW YORK", "loc": [-73.942597, 40.818333], "pop": 21132, "state": "NY", "_id": "10030"} -{"city": "NEW YORK", "loc": [-73.950712, 40.82455], "pop": 55989, "state": "NY", "_id": "10031"} -{"city": "NEW YORK", "loc": [-73.941978, 40.83819], "pop": 61332, "state": "NY", "_id": "10032"} -{"city": "NEW YORK", "loc": [-73.935649, 40.84955], "pop": 58648, "state": "NY", "_id": "10033"} -{"city": "NEW YORK", "loc": [-73.922077, 40.866222], "pop": 41131, "state": "NY", "_id": "10034"} -{"city": "NEW YORK", "loc": [-73.937098, 40.801116], "pop": 28099, "state": "NY", "_id": "10035"} -{"city": "NEW YORK", "loc": [-73.991826, 40.759724], "pop": 16748, "state": "NY", "_id": "10036"} -{"city": "NEW YORK", "loc": [-73.9381, 40.813491], "pop": 14982, "state": "NY", "_id": "10037"} -{"city": "NEW YORK", "loc": [-74.001298, 40.710092], "pop": 14015, "state": "NY", "_id": "10038"} -{"city": "NEW YORK", "loc": [-73.938266, 40.826458], "pop": 25293, "state": "NY", "_id": "10039"} -{"city": "NEW YORK", "loc": [-73.929601, 40.858308], "pop": 39780, "state": "NY", "_id": "10040"} -{"city": "NEW YORK", "loc": [-73.949136, 40.762998], "pop": 8190, "state": "NY", "_id": "10044"} -{"city": "NEW YORK", "loc": [-73.951112, 40.781618], "pop": 52311, "state": "NY", "_id": "10128"} -{"city": "NEW YORK", "loc": [-74.016323, 40.710537], "pop": 5574, "state": "NY", "_id": "10280"} -{"city": "STATEN ISLAND", "loc": [-74.092663, 40.631602], "pop": 35314, "state": "NY", "_id": "10301"} -{"city": "STATEN ISLAND", "loc": [-74.137918, 40.630597], "pop": 13369, "state": "NY", "_id": "10302"} -{"city": "STATEN ISLAND", "loc": [-74.160679, 40.630062], "pop": 17695, "state": "NY", "_id": "10303"} -{"city": "STATEN ISLAND", "loc": [-74.087836, 40.610249], "pop": 33028, "state": "NY", "_id": "10304"} -{"city": "STATEN ISLAND", "loc": [-74.076795, 40.597296], "pop": 30513, "state": "NY", "_id": "10305"} -{"city": "STATEN ISLAND", "loc": [-74.118386, 40.568183], "pop": 49849, "state": "NY", "_id": "10306"} -{"city": "STATEN ISLAND", "loc": [-74.244482, 40.508452], "pop": 7627, "state": "NY", "_id": "10307"} -{"city": "STATEN ISLAND", "loc": [-74.152649, 40.55181], "pop": 24947, "state": "NY", "_id": "10308"} -{"city": "STATEN ISLAND", "loc": [-74.211572, 40.535179], "pop": 18651, "state": "NY", "_id": "10309"} -{"city": "STATEN ISLAND", "loc": [-74.11715, 40.632427], "pop": 20282, "state": "NY", "_id": "10310"} -{"city": "STATEN ISLAND", "loc": [-74.179165, 40.545745], "pop": 49584, "state": "NY", "_id": "10312"} -{"city": "STATEN ISLAND", "loc": [-74.147218, 40.603915], "pop": 78118, "state": "NY", "_id": "10314"} -{"city": "BRONX", "loc": [-73.921735, 40.8222], "pop": 42854, "state": "NY", "_id": "10451"} -{"city": "BRONX", "loc": [-73.921555, 40.837594], "pop": 55890, "state": "NY", "_id": "10452"} -{"city": "BRONX", "loc": [-73.912937, 40.852047], "pop": 70544, "state": "NY", "_id": "10453"} -{"city": "BRONX", "loc": [-73.919821, 40.808549], "pop": 35994, "state": "NY", "_id": "10454"} -{"city": "BRONX", "loc": [-73.907172, 40.815309], "pop": 31882, "state": "NY", "_id": "10455"} -{"city": "BRONX", "loc": [-73.909893, 40.831557], "pop": 70157, "state": "NY", "_id": "10456"} -{"city": "BRONX", "loc": [-73.899907, 40.848635], "pop": 62133, "state": "NY", "_id": "10457"} -{"city": "BRONX", "loc": [-73.889464, 40.863307], "pop": 70612, "state": "NY", "_id": "10458"} -{"city": "BRONX", "loc": [-73.894047, 40.824699], "pop": 30983, "state": "NY", "_id": "10459"} -{"city": "BRONX", "loc": [-73.879409, 40.840949], "pop": 47250, "state": "NY", "_id": "10460"} -{"city": "BRONX", "loc": [-73.840953, 40.846506], "pop": 45273, "state": "NY", "_id": "10461"} -{"city": "BRONX", "loc": [-73.860185, 40.843369], "pop": 61478, "state": "NY", "_id": "10462"} -{"city": "BRONX", "loc": [-73.906737, 40.879812], "pop": 67435, "state": "NY", "_id": "10463"} -{"city": "BRONX", "loc": [-73.787436, 40.846941], "pop": 4113, "state": "NY", "_id": "10464"} -{"city": "BRONX", "loc": [-73.819581, 40.826065], "pop": 37457, "state": "NY", "_id": "10465"} -{"city": "BRONX", "loc": [-73.850333, 40.890375], "pop": 58394, "state": "NY", "_id": "10466"} -{"city": "BRONX", "loc": [-73.871242, 40.873671], "pop": 85710, "state": "NY", "_id": "10467"} -{"city": "BRONX", "loc": [-73.900259, 40.866231], "pop": 65854, "state": "NY", "_id": "10468"} -{"city": "BRONX", "loc": [-73.849465, 40.870193], "pop": 53962, "state": "NY", "_id": "10469"} -{"city": "BRONX", "loc": [-73.862194, 40.900029], "pop": 13254, "state": "NY", "_id": "10470"} -{"city": "BRONX", "loc": [-73.905283, 40.901084], "pop": 23348, "state": "NY", "_id": "10471"} -{"city": "BRONX", "loc": [-73.871557, 40.829464], "pop": 61122, "state": "NY", "_id": "10472"} -{"city": "BRONX", "loc": [-73.860626, 40.819364], "pop": 53981, "state": "NY", "_id": "10473"} -{"city": "BRONX", "loc": [-73.886376, 40.801518], "pop": 22823, "state": "NY", "_id": "10474"} -{"city": "BRONX", "loc": [-73.827817, 40.872903], "pop": 37045, "state": "NY", "_id": "10475"} -{"city": "AMAWALK", "loc": [-73.761079, 41.294618], "pop": 745, "state": "NY", "_id": "10501"} -{"city": "ARDSLEY", "loc": [-73.841311, 41.011332], "pop": 5529, "state": "NY", "_id": "10502"} -{"city": "ARMONK", "loc": [-73.700942, 41.136002], "pop": 7077, "state": "NY", "_id": "10504"} -{"city": "BEDFORD", "loc": [-73.635548, 41.190913], "pop": 4332, "state": "NY", "_id": "10506"} -{"city": "BEDFORD HILLS", "loc": [-73.691517, 41.234439], "pop": 6161, "state": "NY", "_id": "10507"} -{"city": "BREWSTER", "loc": [-73.599179, 41.409704], "pop": 15852, "state": "NY", "_id": "10509"} -{"city": "BRIARCLIFF MANOR", "loc": [-73.834956, 41.144364], "pop": 8509, "state": "NY", "_id": "10510"} -{"city": "BUCHANAN", "loc": [-73.941238, 41.258285], "pop": 2008, "state": "NY", "_id": "10511"} -{"city": "CARMEL", "loc": [-73.681526, 41.443216], "pop": 20716, "state": "NY", "_id": "10512"} -{"city": "CHAPPAQUA", "loc": [-73.771458, 41.170497], "pop": 11097, "state": "NY", "_id": "10514"} -{"city": "COLD SPRING", "loc": [-73.933485, 41.44142], "pop": 4904, "state": "NY", "_id": "10516"} -{"city": "CROSS RIVER", "loc": [-73.602027, 41.27218], "pop": 691, "state": "NY", "_id": "10518"} -{"city": "CROTON ON HUDSON", "loc": [-73.892408, 41.218037], "pop": 12527, "state": "NY", "_id": "10520"} -{"city": "DOBBS FERRY", "loc": [-73.866494, 41.011835], "pop": 9896, "state": "NY", "_id": "10522"} -{"city": "ELMSFORD", "loc": [-73.813597, 41.057171], "pop": 6266, "state": "NY", "_id": "10523"} -{"city": "GARRISON", "loc": [-73.920003, 41.362065], "pop": 3572, "state": "NY", "_id": "10524"} -{"city": "GRANITE SPRINGS", "loc": [-73.753008, 41.309791], "pop": 963, "state": "NY", "_id": "10527"} -{"city": "HARRISON", "loc": [-73.718068, 40.971876], "pop": 11601, "state": "NY", "_id": "10528"} -{"city": "HARTSDALE", "loc": [-73.807404, 41.019658], "pop": 13995, "state": "NY", "_id": "10530"} -{"city": "HAWTHORNE", "loc": [-73.801685, 41.095316], "pop": 7258, "state": "NY", "_id": "10532"} -{"city": "IRVINGTON", "loc": [-73.859666, 41.038113], "pop": 7498, "state": "NY", "_id": "10533"} -{"city": "JEFFERSON VALLEY", "loc": [-73.794697, 41.338522], "pop": 213, "state": "NY", "_id": "10535"} -{"city": "KATONAH", "loc": [-73.684115, 41.270884], "pop": 12263, "state": "NY", "_id": "10536"} -{"city": "LAKE PEEKSKILL", "loc": [-73.883787, 41.337437], "pop": 1604, "state": "NY", "_id": "10537"} -{"city": "LARCHMONT", "loc": [-73.75715, 40.935055], "pop": 16840, "state": "NY", "_id": "10538"} -{"city": "MAHOPAC", "loc": [-73.750755, 41.371708], "pop": 23687, "state": "NY", "_id": "10541"} -{"city": "MAMARONECK", "loc": [-73.735037, 40.952481], "pop": 18193, "state": "NY", "_id": "10543"} -{"city": "MILLWOOD", "loc": [-73.792626, 41.201519], "pop": 681, "state": "NY", "_id": "10546"} -{"city": "MOHEGAN LAKE", "loc": [-73.850836, 41.314348], "pop": 5753, "state": "NY", "_id": "10547"} -{"city": "MONTROSE", "loc": [-73.944593, 41.249585], "pop": 4350, "state": "NY", "_id": "10548"} -{"city": "MOUNT KISCO", "loc": [-73.729921, 41.204966], "pop": 14361, "state": "NY", "_id": "10549"} -{"city": "MOUNT VERNON", "loc": [-73.837961, 40.907863], "pop": 37889, "state": "NY", "_id": "10550"} -{"city": "MOUNT VERNON", "loc": [-73.829919, 40.923056], "pop": 18167, "state": "NY", "_id": "10552"} -{"city": "MOUNT VERNON", "loc": [-73.822111, 40.908645], "pop": 12452, "state": "NY", "_id": "10553"} -{"city": "NORTH SALEM", "loc": [-73.592947, 41.341388], "pop": 2601, "state": "NY", "_id": "10560"} -{"city": "OSSINING", "loc": [-73.853791, 41.167344], "pop": 29926, "state": "NY", "_id": "10562"} -{"city": "CORTLANDT MANOR", "loc": [-73.902562, 41.293722], "pop": 37431, "state": "NY", "_id": "10566"} -{"city": "PLEASANTVILLE", "loc": [-73.784484, 41.134977], "pop": 11812, "state": "NY", "_id": "10570"} -{"city": "RYE BROOK", "loc": [-73.672045, 41.007755], "pop": 32552, "state": "NY", "_id": "10573"} -{"city": "POUND RIDGE", "loc": [-73.573215, 41.204196], "pop": 4720, "state": "NY", "_id": "10576"} -{"city": "PURCHASE", "loc": [-73.715629, 41.038396], "pop": 4453, "state": "NY", "_id": "10577"} -{"city": "PURDYS", "loc": [-73.654313, 41.322427], "pop": 2534, "state": "NY", "_id": "10578"} -{"city": "PUTNAM VALLEY", "loc": [-73.85024, 41.372815], "pop": 7453, "state": "NY", "_id": "10579"} -{"city": "RYE", "loc": [-73.690721, 40.973403], "pop": 16479, "state": "NY", "_id": "10580"} -{"city": "HEATHCOTE", "loc": [-73.797566, 40.988365], "pop": 35302, "state": "NY", "_id": "10583"} -{"city": "SHRUB OAK", "loc": [-73.82732, 41.328608], "pop": 2103, "state": "NY", "_id": "10588"} -{"city": "SOMERS", "loc": [-73.695145, 41.334568], "pop": 6618, "state": "NY", "_id": "10589"} -{"city": "SOUTH SALEM", "loc": [-73.540224, 41.255261], "pop": 6936, "state": "NY", "_id": "10590"} -{"city": "NORTH TARRYTOWN", "loc": [-73.859335, 41.078108], "pop": 20080, "state": "NY", "_id": "10591"} -{"city": "THORNWOOD", "loc": [-73.773329, 41.118163], "pop": 5455, "state": "NY", "_id": "10594"} -{"city": "VALHALLA", "loc": [-73.777596, 41.085559], "pop": 5255, "state": "NY", "_id": "10595"} -{"city": "WACCABUC", "loc": [-73.603207, 41.303208], "pop": 415, "state": "NY", "_id": "10597"} -{"city": "YORKTOWN HEIGHTS", "loc": [-73.792398, 41.299947], "pop": 28501, "state": "NY", "_id": "10598"} -{"city": "WHITE PLAINS", "loc": [-73.765231, 41.032955], "pop": 8397, "state": "NY", "_id": "10601"} -{"city": "WHITE PLAINS", "loc": [-73.77758, 41.049913], "pop": 14342, "state": "NY", "_id": "10603"} -{"city": "EAST WHITE PLAIN", "loc": [-73.750727, 41.043295], "pop": 8899, "state": "NY", "_id": "10604"} -{"city": "WHITE PLAINS", "loc": [-73.755247, 41.014053], "pop": 17843, "state": "NY", "_id": "10605"} -{"city": "WHITE PLAINS", "loc": [-73.778097, 41.024714], "pop": 13436, "state": "NY", "_id": "10606"} -{"city": "WHITE PLAINS", "loc": [-73.811692, 41.039813], "pop": 7148, "state": "NY", "_id": "10607"} -{"city": "YONKERS", "loc": [-73.888317, 40.940716], "pop": 59476, "state": "NY", "_id": "10701"} -{"city": "YONKERS", "loc": [-73.885163, 40.951763], "pop": 19958, "state": "NY", "_id": "10703"} -{"city": "YONKERS", "loc": [-73.859347, 40.917633], "pop": 31604, "state": "NY", "_id": "10704"} -{"city": "YONKERS", "loc": [-73.895041, 40.917665], "pop": 36141, "state": "NY", "_id": "10705"} -{"city": "HASTINGS ON HUDS", "loc": [-73.875912, 40.99071], "pop": 8546, "state": "NY", "_id": "10706"} -{"city": "TUCKAHOE", "loc": [-73.819771, 40.95691], "pop": 15275, "state": "NY", "_id": "10707"} -{"city": "BRONXVILLE", "loc": [-73.835332, 40.939133], "pop": 20406, "state": "NY", "_id": "10708"} -{"city": "EASTCHESTER", "loc": [-73.80858, 40.954966], "pop": 2515, "state": "NY", "_id": "10709"} -{"city": "YONKERS", "loc": [-73.843435, 40.965574], "pop": 24952, "state": "NY", "_id": "10710"} -{"city": "NEW ROCHELLE", "loc": [-73.787729, 40.916628], "pop": 30929, "state": "NY", "_id": "10801"} -{"city": "PELHAM", "loc": [-73.807304, 40.904455], "pop": 11997, "state": "NY", "_id": "10803"} -{"city": "NEW ROCHELLE", "loc": [-73.786018, 40.948335], "pop": 14359, "state": "NY", "_id": "10804"} -{"city": "NEW ROCHELLE", "loc": [-73.781043, 40.900236], "pop": 18471, "state": "NY", "_id": "10805"} -{"city": "SUFFERN", "loc": [-74.124098, 41.117654], "pop": 21296, "state": "NY", "_id": "10901"} -{"city": "BEAR MOUNTAIN", "loc": [-73.996108, 41.306734], "pop": 14, "state": "NY", "_id": "10911"} -{"city": "BLAUVELT", "loc": [-73.962924, 41.062597], "pop": 4770, "state": "NY", "_id": "10913"} -{"city": "CAMPBELL HALL", "loc": [-74.250466, 41.442993], "pop": 3065, "state": "NY", "_id": "10916"} -{"city": "CENTRAL VALLEY", "loc": [-74.122036, 41.326836], "pop": 1407, "state": "NY", "_id": "10917"} -{"city": "CHESTER", "loc": [-74.265116, 41.355381], "pop": 9196, "state": "NY", "_id": "10918"} -{"city": "CIRCLEVILLE", "loc": [-74.385044, 41.524387], "pop": 79, "state": "NY", "_id": "10919"} -{"city": "CONGERS", "loc": [-73.94131, 41.148689], "pop": 8088, "state": "NY", "_id": "10920"} -{"city": "FLORIDA", "loc": [-74.352805, 41.329507], "pop": 2698, "state": "NY", "_id": "10921"} -{"city": "GARNERVILLE", "loc": [-74.000501, 41.202057], "pop": 9650, "state": "NY", "_id": "10923"} -{"city": "GOSHEN", "loc": [-74.330162, 41.394586], "pop": 11019, "state": "NY", "_id": "10924"} -{"city": "GREENWOOD LAKE", "loc": [-74.300007, 41.212718], "pop": 4723, "state": "NY", "_id": "10925"} -{"city": "HARRIMAN", "loc": [-74.155805, 41.299681], "pop": 3249, "state": "NY", "_id": "10926"} -{"city": "HAVERSTRAW", "loc": [-73.968959, 41.197095], "pop": 9438, "state": "NY", "_id": "10927"} -{"city": "HIGHLAND FALLS", "loc": [-73.974618, 41.358177], "pop": 5704, "state": "NY", "_id": "10928"} -{"city": "HIGHLAND MILLS", "loc": [-74.119678, 41.353592], "pop": 5629, "state": "NY", "_id": "10930"} -{"city": "HILLBURN", "loc": [-74.170195, 41.123981], "pop": 892, "state": "NY", "_id": "10931"} -{"city": "SCOTCHTOWN", "loc": [-74.412023, 41.457222], "pop": 51211, "state": "NY", "_id": "10940"} -{"city": "MONROE", "loc": [-74.188521, 41.328578], "pop": 30829, "state": "NY", "_id": "10950"} -{"city": "MONSEY", "loc": [-74.0736, 41.116255], "pop": 25251, "state": "NY", "_id": "10952"} -{"city": "BARDONIA", "loc": [-74.009589, 41.099176], "pop": 16390, "state": "NY", "_id": "10954"} -{"city": "NEW CITY", "loc": [-73.996206, 41.147247], "pop": 33340, "state": "NY", "_id": "10956"} -{"city": "NEW HAMPTON", "loc": [-74.443483, 41.362714], "pop": 3918, "state": "NY", "_id": "10958"} -{"city": "NYACK", "loc": [-73.925226, 41.091351], "pop": 14542, "state": "NY", "_id": "10960"} -{"city": "ORANGEBURG", "loc": [-73.960873, 41.044183], "pop": 6324, "state": "NY", "_id": "10962"} -{"city": "OTISVILLE", "loc": [-74.529413, 41.481806], "pop": 4748, "state": "NY", "_id": "10963"} -{"city": "PALISADES", "loc": [-73.924985, 41.010263], "pop": 996, "state": "NY", "_id": "10964"} -{"city": "PEARL RIVER", "loc": [-74.01587, 41.062939], "pop": 14791, "state": "NY", "_id": "10965"} -{"city": "PIERMONT", "loc": [-73.919187, 41.03955], "pop": 2104, "state": "NY", "_id": "10968"} -{"city": "PINE ISLAND", "loc": [-74.379457, 41.323796], "pop": 589, "state": "NY", "_id": "10969"} -{"city": "POMONA", "loc": [-74.043564, 41.190105], "pop": 7356, "state": "NY", "_id": "10970"} -{"city": "SLATE HILL", "loc": [-74.484658, 41.376004], "pop": 2127, "state": "NY", "_id": "10973"} -{"city": "STERLINGTON", "loc": [-74.188107, 41.16047], "pop": 3322, "state": "NY", "_id": "10974"} -{"city": "SOUTHFIELDS", "loc": [-74.176182, 41.247959], "pop": 5, "state": "NY", "_id": "10975"} -{"city": "SPARKILL", "loc": [-73.92288, 41.025573], "pop": 894, "state": "NY", "_id": "10976"} -{"city": "CHESTNUT RIDGE", "loc": [-74.046253, 41.117977], "pop": 43435, "state": "NY", "_id": "10977"} -{"city": "STONY POINT", "loc": [-73.996164, 41.229174], "pop": 11437, "state": "NY", "_id": "10980"} -{"city": "TAPPAN", "loc": [-73.949065, 41.027751], "pop": 6181, "state": "NY", "_id": "10983"} -{"city": "THIELLS", "loc": [-74.015441, 41.207826], "pop": 3971, "state": "NY", "_id": "10984"} -{"city": "THOMPSON RIDGE", "loc": [-74.377445, 41.580957], "pop": 82, "state": "NY", "_id": "10985"} -{"city": "TOMKINS COVE", "loc": [-73.98916, 41.259763], "pop": 1615, "state": "NY", "_id": "10986"} -{"city": "TUXEDO PARK", "loc": [-74.215912, 41.192486], "pop": 2481, "state": "NY", "_id": "10987"} -{"city": "VALLEY COTTAGE", "loc": [-73.943006, 41.118338], "pop": 9132, "state": "NY", "_id": "10989"} -{"city": "WARWICK", "loc": [-74.36037, 41.265563], "pop": 16960, "state": "NY", "_id": "10990"} -{"city": "WASHINGTONVILLE", "loc": [-74.1601, 41.423701], "pop": 8842, "state": "NY", "_id": "10992"} -{"city": "WEST HAVERSTRAW", "loc": [-73.982123, 41.209016], "pop": 3591, "state": "NY", "_id": "10993"} -{"city": "WEST NYACK", "loc": [-73.976846, 41.097324], "pop": 6655, "state": "NY", "_id": "10994"} -{"city": "WEST POINT", "loc": [-73.973666, 41.394545], "pop": 7979, "state": "NY", "_id": "10996"} -{"city": "WESTTOWN", "loc": [-74.552946, 41.321414], "pop": 2097, "state": "NY", "_id": "10998"} -{"city": "FLORAL PARK", "loc": [-73.70576, 40.723586], "pop": 26498, "state": "NY", "_id": "11001"} -{"city": "ALDEN MANOR", "loc": [-73.705751, 40.699615], "pop": 34741, "state": "NY", "_id": "11003"} -{"city": "GLEN OAKS", "loc": [-73.711436, 40.7481], "pop": 19058, "state": "NY", "_id": "11004"} -{"city": "FRANKLIN SQUARE", "loc": [-73.675807, 40.701049], "pop": 24595, "state": "NY", "_id": "11010"} -{"city": "GREAT NECK", "loc": [-73.718918, 40.774235], "pop": 6144, "state": "NY", "_id": "11020"} -{"city": "GREAT NECK", "loc": [-73.726984, 40.786674], "pop": 17198, "state": "NY", "_id": "11021"} -{"city": "GREAT NECK", "loc": [-73.734257, 40.799307], "pop": 7981, "state": "NY", "_id": "11023"} -{"city": "KINGS POINT CONT", "loc": [-73.741391, 40.813307], "pop": 7298, "state": "NY", "_id": "11024"} -{"city": "PLANDOME", "loc": [-73.688369, 40.798641], "pop": 15271, "state": "NY", "_id": "11030"} -{"city": "HILLSIDE MANOR", "loc": [-73.68042, 40.743926], "pop": 37695, "state": "NY", "_id": "11040"} -{"city": "NEW HYDE PARK", "loc": [-73.694978, 40.7602], "pop": 1, "state": "NY", "_id": "11042"} -{"city": "PORT WASHINGTON", "loc": [-73.696356, 40.834995], "pop": 28264, "state": "NY", "_id": "11050"} -{"city": "ASTORIA", "loc": [-73.939393, 40.750316], "pop": 23142, "state": "NY", "_id": "11101"} -{"city": "ASTORIA", "loc": [-73.926462, 40.77063], "pop": 30078, "state": "NY", "_id": "11102"} -{"city": "ASTORIA", "loc": [-73.914886, 40.762651], "pop": 38597, "state": "NY", "_id": "11103"} -{"city": "SUNNYSIDE", "loc": [-73.921556, 40.743641], "pop": 25898, "state": "NY", "_id": "11104"} -{"city": "ASTORIA", "loc": [-73.910965, 40.77627], "pop": 37297, "state": "NY", "_id": "11105"} -{"city": "ASTORIA", "loc": [-73.929527, 40.760813], "pop": 36515, "state": "NY", "_id": "11106"} -{"city": "BROOKLYN", "loc": [-73.99034, 40.694021], "pop": 46980, "state": "NY", "_id": "11201"} -{"city": "BROOKLYN", "loc": [-73.934888, 40.650496], "pop": 80566, "state": "NY", "_id": "11203"} -{"city": "BROOKLYN", "loc": [-73.985623, 40.617871], "pop": 64780, "state": "NY", "_id": "11204"} -{"city": "BROOKLYN", "loc": [-73.96662, 40.692433], "pop": 36852, "state": "NY", "_id": "11205"} -{"city": "BROOKLYN", "loc": [-73.943617, 40.701195], "pop": 74825, "state": "NY", "_id": "11206"} -{"city": "BROOKLYN", "loc": [-73.893957, 40.670486], "pop": 83158, "state": "NY", "_id": "11207"} -{"city": "BROOKLYN", "loc": [-73.873649, 40.676191], "pop": 78322, "state": "NY", "_id": "11208"} -{"city": "BROOKLYN", "loc": [-74.030304, 40.625106], "pop": 62346, "state": "NY", "_id": "11209"} -{"city": "BROOKLYN", "loc": [-73.946682, 40.628064], "pop": 62445, "state": "NY", "_id": "11210"} -{"city": "BROOKLYN", "loc": [-73.956283, 40.709476], "pop": 78338, "state": "NY", "_id": "11211"} -{"city": "BROOKLYN", "loc": [-73.914483, 40.662474], "pop": 87079, "state": "NY", "_id": "11212"} -{"city": "BROOKLYN", "loc": [-73.93665, 40.669961], "pop": 62607, "state": "NY", "_id": "11213"} -{"city": "BROOKLYN", "loc": [-73.99681, 40.601563], "pop": 73076, "state": "NY", "_id": "11214"} -{"city": "BROOKLYN", "loc": [-73.982783, 40.666863], "pop": 63338, "state": "NY", "_id": "11215"} -{"city": "BROOKLYN", "loc": [-73.949639, 40.67943], "pop": 58862, "state": "NY", "_id": "11216"} -{"city": "BROOKLYN", "loc": [-73.979797, 40.68165], "pop": 36232, "state": "NY", "_id": "11217"} -{"city": "BROOKLYN", "loc": [-73.975806, 40.642373], "pop": 66569, "state": "NY", "_id": "11218"} -{"city": "BROOKLYN", "loc": [-73.996011, 40.633568], "pop": 73527, "state": "NY", "_id": "11219"} -{"city": "BROOKLYN", "loc": [-74.013287, 40.641165], "pop": 76923, "state": "NY", "_id": "11220"} -{"city": "BROOKLYN", "loc": [-73.927373, 40.690695], "pop": 70583, "state": "NY", "_id": "11221"} -{"city": "BROOKLYN", "loc": [-73.949846, 40.727164], "pop": 38158, "state": "NY", "_id": "11222"} -{"city": "BROOKLYN", "loc": [-73.974291, 40.597874], "pop": 70904, "state": "NY", "_id": "11223"} -{"city": "BROOKLYN", "loc": [-73.988395, 40.576729], "pop": 52480, "state": "NY", "_id": "11224"} -{"city": "BROOKLYN", "loc": [-73.954588, 40.662776], "pop": 66752, "state": "NY", "_id": "11225"} -{"city": "BROOKLYN", "loc": [-73.956985, 40.646694], "pop": 111396, "state": "NY", "_id": "11226"} -{"city": "BROOKLYN", "loc": [-74.012067, 40.617441], "pop": 39220, "state": "NY", "_id": "11228"} -{"city": "BROOKLYN", "loc": [-73.94749, 40.601094], "pop": 72660, "state": "NY", "_id": "11229"} -{"city": "BROOKLYN", "loc": [-73.965007, 40.622493], "pop": 72733, "state": "NY", "_id": "11230"} -{"city": "BROOKLYN", "loc": [-74.00141, 40.679437], "pop": 32101, "state": "NY", "_id": "11231"} -{"city": "BROOKLYN", "loc": [-74.001797, 40.652113], "pop": 22777, "state": "NY", "_id": "11232"} -{"city": "BROOKLYN", "loc": [-73.921104, 40.678415], "pop": 58827, "state": "NY", "_id": "11233"} -{"city": "BROOKLYN", "loc": [-73.923915, 40.620475], "pop": 74953, "state": "NY", "_id": "11234"} -{"city": "BROOKLYN", "loc": [-73.953599, 40.583898], "pop": 67088, "state": "NY", "_id": "11235"} -{"city": "BROOKLYN", "loc": [-73.902764, 40.640685], "pop": 77253, "state": "NY", "_id": "11236"} -{"city": "BROOKLYN", "loc": [-73.917979, 40.700616], "pop": 48339, "state": "NY", "_id": "11237"} -{"city": "BROOKLYN", "loc": [-73.964387, 40.679015], "pop": 42507, "state": "NY", "_id": "11238"} -{"city": "BROOKLYN", "loc": [-73.882375, 40.649748], "pop": 14948, "state": "NY", "_id": "11239"} -{"city": "BROOKLYN NAVY YA", "loc": [-73.966511, 40.703578], "pop": 18, "state": "NY", "_id": "11251"} -{"city": "FLUSHING", "loc": [-73.824142, 40.766722], "pop": 51947, "state": "NY", "_id": "11354"} -{"city": "FLUSHING", "loc": [-73.822609, 40.753573], "pop": 69164, "state": "NY", "_id": "11355"} -{"city": "COLLEGE POINT", "loc": [-73.844955, 40.785511], "pop": 17723, "state": "NY", "_id": "11356"} -{"city": "WHITESTONE", "loc": [-73.809594, 40.785147], "pop": 39093, "state": "NY", "_id": "11357"} -{"city": "FLUSHING", "loc": [-73.796788, 40.760636], "pop": 34045, "state": "NY", "_id": "11358"} -{"city": "FORT TOTTEN", "loc": [-73.777244, 40.789967], "pop": 734, "state": "NY", "_id": "11359"} -{"city": "BAYSIDE", "loc": [-73.781216, 40.780684], "pop": 20337, "state": "NY", "_id": "11360"} -{"city": "BAYSIDE", "loc": [-73.774457, 40.76268], "pop": 25529, "state": "NY", "_id": "11361"} -{"city": "LITTLE NECK", "loc": [-73.732622, 40.759131], "pop": 16226, "state": "NY", "_id": "11362"} -{"city": "LITTLE NECK", "loc": [-73.745401, 40.772166], "pop": 6909, "state": "NY", "_id": "11363"} -{"city": "FLUSHING", "loc": [-73.758646, 40.745847], "pop": 32080, "state": "NY", "_id": "11364"} -{"city": "FRESH MEADOWS", "loc": [-73.79506, 40.737424], "pop": 34087, "state": "NY", "_id": "11365"} -{"city": "FRESH MEADOWS", "loc": [-73.794922, 40.727231], "pop": 12089, "state": "NY", "_id": "11366"} -{"city": "FLUSHING", "loc": [-73.81953, 40.727966], "pop": 36877, "state": "NY", "_id": "11367"} -{"city": "CORONA", "loc": [-73.861069, 40.745288], "pop": 75746, "state": "NY", "_id": "11368"} -{"city": "EAST ELMHURST", "loc": [-73.873902, 40.761254], "pop": 28106, "state": "NY", "_id": "11369"} -{"city": "EAST ELMHURST", "loc": [-73.891586, 40.761111], "pop": 23910, "state": "NY", "_id": "11370"} -{"city": "FLUSHING", "loc": [-73.873535, 40.772117], "pop": 49, "state": "NY", "_id": "11371"} -{"city": "JACKSON HEIGHTS", "loc": [-73.882975, 40.751329], "pop": 57726, "state": "NY", "_id": "11372"} -{"city": "JACKSON HEIGHTS", "loc": [-73.878551, 40.740388], "pop": 88241, "state": "NY", "_id": "11373"} -{"city": "REGO PARK", "loc": [-73.860191, 40.72775], "pop": 40024, "state": "NY", "_id": "11374"} -{"city": "FOREST HILLS", "loc": [-73.847306, 40.722854], "pop": 65180, "state": "NY", "_id": "11375"} -{"city": "WOODSIDE", "loc": [-73.906911, 40.744972], "pop": 75894, "state": "NY", "_id": "11377"} -{"city": "MASPETH", "loc": [-73.899682, 40.723865], "pop": 31292, "state": "NY", "_id": "11378"} -{"city": "MIDDLE VILLAGE", "loc": [-73.879228, 40.717286], "pop": 28981, "state": "NY", "_id": "11379"} -{"city": "RIDGEWOOD", "loc": [-73.896122, 40.703613], "pop": 85732, "state": "NY", "_id": "11385"} -{"city": "CAMBRIA HEIGHTS", "loc": [-73.737445, 40.694741], "pop": 20094, "state": "NY", "_id": "11411"} -{"city": "KEW GARDENS", "loc": [-73.758641, 40.698097], "pop": 32003, "state": "NY", "_id": "11412"} -{"city": "SPRINGFIELD GARD", "loc": [-73.75071, 40.67295], "pop": 36679, "state": "NY", "_id": "11413"} -{"city": "KEW GARDENS", "loc": [-73.845041, 40.660603], "pop": 27256, "state": "NY", "_id": "11414"} -{"city": "KEW GARDENS", "loc": [-73.829715, 40.706865], "pop": 16703, "state": "NY", "_id": "11415"} -{"city": "OZONE PARK", "loc": [-73.851397, 40.683753], "pop": 17876, "state": "NY", "_id": "11416"} -{"city": "OZONE PARK", "loc": [-73.844778, 40.676854], "pop": 22768, "state": "NY", "_id": "11417"} -{"city": "RICHMOND HILL", "loc": [-73.834484, 40.698171], "pop": 29382, "state": "NY", "_id": "11418"} -{"city": "S RICHMOND HILL", "loc": [-73.823871, 40.688113], "pop": 37530, "state": "NY", "_id": "11419"} -{"city": "S OZONE PARK", "loc": [-73.815773, 40.675379], "pop": 38096, "state": "NY", "_id": "11420"} -{"city": "WOODHAVEN", "loc": [-73.858514, 40.691345], "pop": 29442, "state": "NY", "_id": "11421"} -{"city": "ROSEDALE", "loc": [-73.735265, 40.662141], "pop": 26674, "state": "NY", "_id": "11422"} -{"city": "HOLLIS", "loc": [-73.767685, 40.714156], "pop": 29102, "state": "NY", "_id": "11423"} -{"city": "BELLEROSE", "loc": [-73.723018, 40.734735], "pop": 18145, "state": "NY", "_id": "11426"} -{"city": "QUEENS VILLAGE", "loc": [-73.748908, 40.727707], "pop": 21004, "state": "NY", "_id": "11427"} -{"city": "QUEENS VILLAGE", "loc": [-73.743312, 40.720756], "pop": 16793, "state": "NY", "_id": "11428"} -{"city": "QUEENS VILLAGE", "loc": [-73.740064, 40.70902], "pop": 24311, "state": "NY", "_id": "11429"} -{"city": "JAMAICA", "loc": [-73.782663, 40.647221], "pop": 398, "state": "NY", "_id": "11430"} -{"city": "JAMAICA", "loc": [-73.79442, 40.711867], "pop": 53011, "state": "NY", "_id": "11432"} -{"city": "JAMAICA", "loc": [-73.787669, 40.69691], "pop": 26325, "state": "NY", "_id": "11433"} -{"city": "JAMAICA", "loc": [-73.77584, 40.677483], "pop": 50464, "state": "NY", "_id": "11434"} -{"city": "JAMAICA", "loc": [-73.811121, 40.702934], "pop": 47987, "state": "NY", "_id": "11435"} -{"city": "JAMAICA", "loc": [-73.796596, 40.676347], "pop": 17020, "state": "NY", "_id": "11436"} -{"city": "MINEOLA", "loc": [-73.639761, 40.746927], "pop": 19087, "state": "NY", "_id": "11501"} -{"city": "ALBERTSON", "loc": [-73.651419, 40.77032], "pop": 7091, "state": "NY", "_id": "11507"} -{"city": "ATLANTIC BEACH", "loc": [-73.725545, 40.588652], "pop": 2831, "state": "NY", "_id": "11509"} -{"city": "BALDWIN", "loc": [-73.609688, 40.654755], "pop": 30959, "state": "NY", "_id": "11510"} -{"city": "CARLE PLACE", "loc": [-73.611941, 40.75115], "pop": 4863, "state": "NY", "_id": "11514"} -{"city": "CEDARHURST", "loc": [-73.726404, 40.623619], "pop": 7113, "state": "NY", "_id": "11516"} -{"city": "EAST ROCKAWAY", "loc": [-73.667376, 40.640445], "pop": 11027, "state": "NY", "_id": "11518"} -{"city": "FREEPORT", "loc": [-73.586615, 40.65359], "pop": 40662, "state": "NY", "_id": "11520"} -{"city": "GARDEN CITY", "loc": [-73.648718, 40.72452], "pop": 27951, "state": "NY", "_id": "11530"} -{"city": "GLEN COVE", "loc": [-73.62772, 40.864958], "pop": 24997, "state": "NY", "_id": "11542"} -{"city": "GLEN HEAD", "loc": [-73.60763, 40.828098], "pop": 13565, "state": "NY", "_id": "11545"} -{"city": "GREENVALE", "loc": [-73.626124, 40.812503], "pop": 1021, "state": "NY", "_id": "11548"} -{"city": "HEMPSTEAD", "loc": [-73.617641, 40.70492], "pop": 50933, "state": "NY", "_id": "11550"} -{"city": "WEST HEMPSTEAD", "loc": [-73.653859, 40.692915], "pop": 22972, "state": "NY", "_id": "11552"} -{"city": "UNIONDALE", "loc": [-73.591995, 40.702029], "pop": 20397, "state": "NY", "_id": "11553"} -{"city": "EAST MEADOW", "loc": [-73.556088, 40.714915], "pop": 36692, "state": "NY", "_id": "11554"} -{"city": "HEWLETT", "loc": [-73.695667, 40.640392], "pop": 8023, "state": "NY", "_id": "11557"} -{"city": "ISLAND PARK", "loc": [-73.655411, 40.604044], "pop": 8857, "state": "NY", "_id": "11558"} -{"city": "LAWRENCE", "loc": [-73.732969, 40.61396], "pop": 6703, "state": "NY", "_id": "11559"} -{"city": "LOCUST VALLEY", "loc": [-73.59271, 40.881728], "pop": 6771, "state": "NY", "_id": "11560"} -{"city": "LONG BEACH", "loc": [-73.659467, 40.587697], "pop": 39066, "state": "NY", "_id": "11561"} -{"city": "LYNBROOK", "loc": [-73.674143, 40.657107], "pop": 22659, "state": "NY", "_id": "11563"} -{"city": "MALVERNE", "loc": [-73.673073, 40.674982], "pop": 8660, "state": "NY", "_id": "11565"} -{"city": "NORTH MERRICK", "loc": [-73.555001, 40.668312], "pop": 36427, "state": "NY", "_id": "11566"} -{"city": "OLD WESTBURY", "loc": [-73.587515, 40.78821], "pop": 5524, "state": "NY", "_id": "11568"} -{"city": "ROCKVILLE CENTRE", "loc": [-73.638, 40.663745], "pop": 27127, "state": "NY", "_id": "11570"} -{"city": "OCEANSIDE", "loc": [-73.637533, 40.636199], "pop": 31406, "state": "NY", "_id": "11572"} -{"city": "ROOSEVELT", "loc": [-73.586697, 40.680171], "pop": 14595, "state": "NY", "_id": "11575"} -{"city": "ROSLYN", "loc": [-73.653362, 40.790668], "pop": 13114, "state": "NY", "_id": "11576"} -{"city": "ROSLYN HEIGHTS", "loc": [-73.640292, 40.784497], "pop": 10665, "state": "NY", "_id": "11577"} -{"city": "SEA CLIFF", "loc": [-73.643598, 40.845984], "pop": 5068, "state": "NY", "_id": "11579"} -{"city": "VALLEY STREAM", "loc": [-73.705738, 40.674184], "pop": 34327, "state": "NY", "_id": "11580"} -{"city": "NORTH WOODMERE", "loc": [-73.710705, 40.651838], "pop": 19582, "state": "NY", "_id": "11581"} -{"city": "WESTBURY", "loc": [-73.57226, 40.755749], "pop": 38026, "state": "NY", "_id": "11590"} -{"city": "WILLISTON PARK", "loc": [-73.644892, 40.759198], "pop": 11057, "state": "NY", "_id": "11596"} -{"city": "WOODMERE", "loc": [-73.714146, 40.63262], "pop": 13084, "state": "NY", "_id": "11598"} -{"city": "FAR ROCKAWAY", "loc": [-73.757971, 40.600645], "pop": 52162, "state": "NY", "_id": "11691"} -{"city": "FAR ROCKAWAY", "loc": [-73.797446, 40.59257], "pop": 21192, "state": "NY", "_id": "11692"} -{"city": "FAR ROCKAWAY", "loc": [-73.819804, 40.607644], "pop": 1630, "state": "NY", "_id": "11693"} -{"city": "FAR ROCKAWAY", "loc": [-73.839192, 40.579471], "pop": 21972, "state": "NY", "_id": "11694"} -{"city": "INWOOD", "loc": [-73.746988, 40.617784], "pop": 6660, "state": "NY", "_id": "11696"} -{"city": "FAR ROCKAWAY", "loc": [-73.914873, 40.560032], "pop": 3690, "state": "NY", "_id": "11697"} -{"city": "AMITYVILLE", "loc": [-73.417106, 40.684197], "pop": 29047, "state": "NY", "_id": "11701"} -{"city": "OAK BEACH", "loc": [-73.324583, 40.634183], "pop": 456, "state": "NY", "_id": "11702"} -{"city": "NORTH BABYLON", "loc": [-73.323581, 40.732102], "pop": 17916, "state": "NY", "_id": "11703"} -{"city": "WEST BABYLON", "loc": [-73.354591, 40.713476], "pop": 45519, "state": "NY", "_id": "11704"} -{"city": "BAYPORT", "loc": [-73.054207, 40.744408], "pop": 7616, "state": "NY", "_id": "11705"} -{"city": "KISMET", "loc": [-73.252708, 40.733744], "pop": 5507, "state": "NY", "_id": "11706"} -{"city": "BAYVILLE", "loc": [-73.560141, 40.907382], "pop": 7185, "state": "NY", "_id": "11709"} -{"city": "NORTH BELLMORE", "loc": [-73.534517, 40.677028], "pop": 34237, "state": "NY", "_id": "11710"} -{"city": "BELLPORT", "loc": [-72.946891, 40.77327], "pop": 7927, "state": "NY", "_id": "11713"} -{"city": "BETHPAGE", "loc": [-73.485727, 40.740014], "pop": 21957, "state": "NY", "_id": "11714"} -{"city": "BLUE POINT", "loc": [-73.035213, 40.750089], "pop": 4189, "state": "NY", "_id": "11715"} -{"city": "BOHEMIA", "loc": [-73.116339, 40.767788], "pop": 10448, "state": "NY", "_id": "11716"} -{"city": "WEST BRENTWOOD", "loc": [-73.248189, 40.777918], "pop": 60425, "state": "NY", "_id": "11717"} -{"city": "BRIGHTWATERS", "loc": [-73.264639, 40.727957], "pop": 20866, "state": "NY", "_id": "11718"} -{"city": "BROOKHAVEN", "loc": [-72.892125, 40.78428], "pop": 7493, "state": "NY", "_id": "11719"} -{"city": "CENTEREACH", "loc": [-73.082163, 40.870508], "pop": 27519, "state": "NY", "_id": "11720"} -{"city": "CENTERPORT", "loc": [-73.3754, 40.892899], "pop": 5770, "state": "NY", "_id": "11721"} -{"city": "CENTRAL ISLIP", "loc": [-73.196145, 40.786618], "pop": 29418, "state": "NY", "_id": "11722"} -{"city": "COLD SPRING HARB", "loc": [-73.450032, 40.863056], "pop": 2860, "state": "NY", "_id": "11724"} -{"city": "COMMACK", "loc": [-73.279924, 40.843032], "pop": 29847, "state": "NY", "_id": "11725"} -{"city": "COPIAGUE", "loc": [-73.396271, 40.677833], "pop": 15640, "state": "NY", "_id": "11726"} -{"city": "CORAM", "loc": [-73.006881, 40.884988], "pop": 25002, "state": "NY", "_id": "11727"} -{"city": "DEER PARK", "loc": [-73.32572, 40.759083], "pop": 32146, "state": "NY", "_id": "11729"} -{"city": "EAST ISLIP", "loc": [-73.180471, 40.72816], "pop": 15777, "state": "NY", "_id": "11730"} -{"city": "ELWOOD", "loc": [-73.319506, 40.867066], "pop": 30333, "state": "NY", "_id": "11731"} -{"city": "EAST NORWICH", "loc": [-73.534876, 40.847162], "pop": 3276, "state": "NY", "_id": "11732"} -{"city": "SETAUKET", "loc": [-73.101467, 40.930004], "pop": 16470, "state": "NY", "_id": "11733"} -{"city": "SOUTH FARMINGDAL", "loc": [-73.445055, 40.725061], "pop": 31358, "state": "NY", "_id": "11735"} -{"city": "FARMINGVILLE", "loc": [-73.04125, 40.836602], "pop": 14967, "state": "NY", "_id": "11738"} -{"city": "GREENLAWN", "loc": [-73.364641, 40.862121], "pop": 9967, "state": "NY", "_id": "11740"} -{"city": "HOLBROOK", "loc": [-73.071789, 40.796426], "pop": 25964, "state": "NY", "_id": "11741"} -{"city": "HOLTSVILLE", "loc": [-73.04161, 40.810478], "pop": 10141, "state": "NY", "_id": "11742"} -{"city": "HALESITE", "loc": [-73.410924, 40.866945], "pop": 41511, "state": "NY", "_id": "11743"} -{"city": "DIX HILLS", "loc": [-73.375472, 40.821754], "pop": 62162, "state": "NY", "_id": "11746"} -{"city": "MELVILLE", "loc": [-73.402963, 40.794606], "pop": 14534, "state": "NY", "_id": "11747"} -{"city": "ISLIP", "loc": [-73.22211, 40.734821], "pop": 21721, "state": "NY", "_id": "11751"} -{"city": "ISLIP TERRACE", "loc": [-73.182746, 40.75482], "pop": 9514, "state": "NY", "_id": "11752"} -{"city": "JERICHO", "loc": [-73.533067, 40.788118], "pop": 9837, "state": "NY", "_id": "11753"} -{"city": "KINGS PARK", "loc": [-73.243763, 40.886121], "pop": 19036, "state": "NY", "_id": "11754"} -{"city": "LAKE GROVE", "loc": [-73.116751, 40.856681], "pop": 8245, "state": "NY", "_id": "11755"} -{"city": "LEVITTOWN", "loc": [-73.516578, 40.725428], "pop": 44967, "state": "NY", "_id": "11756"} -{"city": "LINDENHURST", "loc": [-73.374493, 40.688373], "pop": 44726, "state": "NY", "_id": "11757"} -{"city": "NORTH MASSAPEQUA", "loc": [-73.469727, 40.685738], "pop": 42863, "state": "NY", "_id": "11758"} -{"city": "MASSAPEQUA PARK", "loc": [-73.444447, 40.680673], "pop": 31693, "state": "NY", "_id": "11762"} -{"city": "MEDFORD", "loc": [-72.985214, 40.817416], "pop": 20669, "state": "NY", "_id": "11763"} -{"city": "MILLER PLACE", "loc": [-72.991349, 40.943572], "pop": 9601, "state": "NY", "_id": "11764"} -{"city": "MILL NECK", "loc": [-73.552602, 40.88574], "pop": 877, "state": "NY", "_id": "11765"} -{"city": "MOUNT SINAI", "loc": [-73.012732, 40.927092], "pop": 8759, "state": "NY", "_id": "11766"} -{"city": "NESCONSET", "loc": [-73.1482, 40.846206], "pop": 9681, "state": "NY", "_id": "11767"} -{"city": "NORTHPORT", "loc": [-73.330889, 40.905062], "pop": 21714, "state": "NY", "_id": "11768"} -{"city": "OAKDALE", "loc": [-73.12969, 40.738224], "pop": 9366, "state": "NY", "_id": "11769"} -{"city": "OYSTER BAY", "loc": [-73.527212, 40.866012], "pop": 9806, "state": "NY", "_id": "11771"} -{"city": "DAVIS PARK", "loc": [-72.998108, 40.773501], "pop": 40962, "state": "NY", "_id": "11772"} -{"city": "PORT JEFFERSON S", "loc": [-73.052136, 40.911722], "pop": 19800, "state": "NY", "_id": "11776"} -{"city": "PORT JEFFERSON", "loc": [-73.061093, 40.945687], "pop": 8512, "state": "NY", "_id": "11777"} -{"city": "ROCKY POINT", "loc": [-72.935681, 40.949206], "pop": 11913, "state": "NY", "_id": "11778"} -{"city": "LAKE RONKONKOMA", "loc": [-73.1208, 40.820763], "pop": 43424, "state": "NY", "_id": "11779"} -{"city": "SAINT JAMES", "loc": [-73.159121, 40.881299], "pop": 14211, "state": "NY", "_id": "11780"} -{"city": "CHERRY GROVE", "loc": [-73.082297, 40.74606], "pop": 15718, "state": "NY", "_id": "11782"} -{"city": "SEAFORD", "loc": [-73.491015, 40.679513], "pop": 21644, "state": "NY", "_id": "11783"} -{"city": "SELDEN", "loc": [-73.044848, 40.869883], "pop": 23753, "state": "NY", "_id": "11784"} -{"city": "SHOREHAM", "loc": [-72.892685, 40.948493], "pop": 5114, "state": "NY", "_id": "11786"} -{"city": "SMITHTOWN", "loc": [-73.213816, 40.854186], "pop": 29932, "state": "NY", "_id": "11787"} -{"city": "HAUPPAUGE", "loc": [-73.195762, 40.823069], "pop": 19713, "state": "NY", "_id": "11788"} -{"city": "SOUND BEACH", "loc": [-72.974172, 40.956707], "pop": 6497, "state": "NY", "_id": "11789"} -{"city": "STONY BROOK", "loc": [-73.125085, 40.900257], "pop": 13767, "state": "NY", "_id": "11790"} -{"city": "SYOSSET", "loc": [-73.502397, 40.81462], "pop": 24949, "state": "NY", "_id": "11791"} -{"city": "WADING RIVER", "loc": [-72.834774, 40.952049], "pop": 6186, "state": "NY", "_id": "11792"} -{"city": "WANTAGH", "loc": [-73.51033, 40.684998], "pop": 31972, "state": "NY", "_id": "11793"} -{"city": "SUNY STONY BROOK", "loc": [-73.125456, 40.914127], "pop": 5504, "state": "NY", "_id": "11794"} -{"city": "WEST ISLIP", "loc": [-73.30072, 40.711734], "pop": 39344, "state": "NY", "_id": "11795"} -{"city": "WEST SAYVILLE", "loc": [-73.100019, 40.731971], "pop": 3575, "state": "NY", "_id": "11796"} -{"city": "WOODBURY", "loc": [-73.471612, 40.815441], "pop": 8104, "state": "NY", "_id": "11797"} -{"city": "WHEATLEY HEIGHTS", "loc": [-73.36596, 40.753258], "pop": 13312, "state": "NY", "_id": "11798"} -{"city": "HICKSVILLE", "loc": [-73.52297, 40.762305], "pop": 38307, "state": "NY", "_id": "11801"} -{"city": "PLAINVIEW", "loc": [-73.481638, 40.778099], "pop": 28207, "state": "NY", "_id": "11803"} -{"city": "OLD BETHPAGE", "loc": [-73.457481, 40.764991], "pop": 5233, "state": "NY", "_id": "11804"} -{"city": "RIVERHEAD", "loc": [-72.651966, 40.926202], "pop": 20705, "state": "NY", "_id": "11901"} -{"city": "CALVERTON", "loc": [-72.74229, 40.929662], "pop": 3954, "state": "NY", "_id": "11933"} -{"city": "CENTER MORICHES", "loc": [-72.797048, 40.799657], "pop": 6265, "state": "NY", "_id": "11934"} -{"city": "CUTCHOGUE", "loc": [-72.480255, 41.013918], "pop": 3678, "state": "NY", "_id": "11935"} -{"city": "EAST HAMPTON", "loc": [-72.178958, 40.992964], "pop": 12205, "state": "NY", "_id": "11937"} -{"city": "EAST MARION", "loc": [-72.34186, 41.126425], "pop": 717, "state": "NY", "_id": "11939"} -{"city": "EAST MORICHES", "loc": [-72.753778, 40.808975], "pop": 3822, "state": "NY", "_id": "11940"} -{"city": "EASTPORT", "loc": [-72.705388, 40.809947], "pop": 1331, "state": "NY", "_id": "11941"} -{"city": "EAST QUOGUE", "loc": [-72.581297, 40.84277], "pop": 4181, "state": "NY", "_id": "11942"} -{"city": "GREENPORT", "loc": [-72.367415, 41.103905], "pop": 3657, "state": "NY", "_id": "11944"} -{"city": "HAMPTON BAYS", "loc": [-72.520209, 40.872596], "pop": 9586, "state": "NY", "_id": "11946"} -{"city": "LAUREL", "loc": [-72.55404, 40.9674], "pop": 954, "state": "NY", "_id": "11948"} -{"city": "MANORVILLE", "loc": [-72.800208, 40.842102], "pop": 9660, "state": "NY", "_id": "11949"} -{"city": "MASTIC", "loc": [-72.856608, 40.80644], "pop": 20374, "state": "NY", "_id": "11950"} -{"city": "MASTIC BEACH", "loc": [-72.853701, 40.765653], "pop": 18812, "state": "NY", "_id": "11951"} -{"city": "MATTITUCK", "loc": [-72.536296, 40.994336], "pop": 3896, "state": "NY", "_id": "11952"} -{"city": "MIDDLE ISLAND", "loc": [-72.952539, 40.878212], "pop": 9411, "state": "NY", "_id": "11953"} -{"city": "MONTAUK", "loc": [-71.943963, 41.045853], "pop": 3008, "state": "NY", "_id": "11954"} -{"city": "MORICHES", "loc": [-72.822918, 40.809502], "pop": 2045, "state": "NY", "_id": "11955"} -{"city": "ORIENT", "loc": [-72.287894, 41.143741], "pop": 817, "state": "NY", "_id": "11957"} -{"city": "RIDGE", "loc": [-72.888135, 40.901846], "pop": 12817, "state": "NY", "_id": "11961"} -{"city": "SAG HARBOR", "loc": [-72.30674, 40.981996], "pop": 7384, "state": "NY", "_id": "11963"} -{"city": "SHELTER ISLAND", "loc": [-72.336616, 41.064046], "pop": 1144, "state": "NY", "_id": "11964"} -{"city": "SHELTER ISLAND H", "loc": [-72.348082, 41.074205], "pop": 1119, "state": "NY", "_id": "11965"} -{"city": "SHIRLEY", "loc": [-72.876043, 40.743932], "pop": 97, "state": "NY", "_id": "11967"} -{"city": "SOUTHAMPTON", "loc": [-72.410271, 40.904341], "pop": 10810, "state": "NY", "_id": "11968"} -{"city": "SOUTHOLD", "loc": [-72.429039, 41.05555], "pop": 5788, "state": "NY", "_id": "11971"} -{"city": "WATER MILL", "loc": [-72.349069, 40.920929], "pop": 1410, "state": "NY", "_id": "11976"} -{"city": "WESTHAMPTON", "loc": [-72.669931, 40.818031], "pop": 1306, "state": "NY", "_id": "11977"} -{"city": "WESTHAMPTON BEAC", "loc": [-72.644757, 40.822783], "pop": 2863, "state": "NY", "_id": "11978"} -{"city": "YAPHANK", "loc": [-72.917435, 40.837037], "pop": 5366, "state": "NY", "_id": "11980"} -{"city": "ALCOVE", "loc": [-74.034721, 42.453818], "pop": 233, "state": "NY", "_id": "12007"} -{"city": "ALPLAUS", "loc": [-73.900188, 42.857329], "pop": 340, "state": "NY", "_id": "12008"} -{"city": "ALTAMONT", "loc": [-74.019339, 42.70627], "pop": 5683, "state": "NY", "_id": "12009"} -{"city": "WEST CHARLTON", "loc": [-74.18393, 42.948822], "pop": 32028, "state": "NY", "_id": "12010"} -{"city": "ATHENS", "loc": [-73.815175, 42.2736], "pop": 2538, "state": "NY", "_id": "12015"} -{"city": "AUSTERLITZ", "loc": [-73.454965, 42.322272], "pop": 355, "state": "NY", "_id": "12017"} -{"city": "AVERILL PARK", "loc": [-73.550437, 42.636511], "pop": 6528, "state": "NY", "_id": "12018"} -{"city": "BALLSTON LAKE", "loc": [-73.855171, 42.919176], "pop": 16331, "state": "NY", "_id": "12019"} -{"city": "BALLSTON SPA", "loc": [-73.84858, 43.004956], "pop": 25126, "state": "NY", "_id": "12020"} -{"city": "BERLIN", "loc": [-73.370186, 42.691893], "pop": 275, "state": "NY", "_id": "12022"} -{"city": "BERNE", "loc": [-74.146577, 42.610848], "pop": 2293, "state": "NY", "_id": "12023"} -{"city": "BRAINARD", "loc": [-73.615874, 42.405101], "pop": 285, "state": "NY", "_id": "12024"} -{"city": "BROADALBIN", "loc": [-74.168367, 43.072687], "pop": 4055, "state": "NY", "_id": "12025"} -{"city": "BURNT HILLS", "loc": [-73.896043, 42.932902], "pop": 945, "state": "NY", "_id": "12027"} -{"city": "BUSKIRK", "loc": [-73.449677, 42.960134], "pop": 1272, "state": "NY", "_id": "12028"} -{"city": "CANAAN", "loc": [-73.415889, 42.413168], "pop": 1298, "state": "NY", "_id": "12029"} -{"city": "CARLISLE", "loc": [-74.456284, 42.749754], "pop": 420, "state": "NY", "_id": "12031"} -{"city": "CAROGA LAKE", "loc": [-74.516915, 43.192159], "pop": 545, "state": "NY", "_id": "12032"} -{"city": "CASTLETON ON HUD", "loc": [-73.709529, 42.538243], "pop": 7029, "state": "NY", "_id": "12033"} -{"city": "CENTRAL BRIDGE", "loc": [-74.345107, 42.73696], "pop": 315, "state": "NY", "_id": "12035"} -{"city": "CHARLOTTEVILLE", "loc": [-74.681863, 42.533034], "pop": 166, "state": "NY", "_id": "12036"} -{"city": "CHATHAM", "loc": [-73.587281, 42.349578], "pop": 4616, "state": "NY", "_id": "12037"} -{"city": "CLARKSVILLE", "loc": [-73.968775, 42.566974], "pop": 388, "state": "NY", "_id": "12041"} -{"city": "CLIMAX", "loc": [-73.862459, 42.370808], "pop": 316, "state": "NY", "_id": "12042"} -{"city": "COBLESKILL", "loc": [-74.493866, 42.684047], "pop": 7419, "state": "NY", "_id": "12043"} -{"city": "COEYMANS HOLLOW", "loc": [-73.920588, 42.486537], "pop": 979, "state": "NY", "_id": "12046"} -{"city": "COHOES", "loc": [-73.712356, 42.775362], "pop": 19033, "state": "NY", "_id": "12047"} -{"city": "COXSACKIE", "loc": [-73.819881, 42.350142], "pop": 5615, "state": "NY", "_id": "12051"} -{"city": "CROPSEYVILLE", "loc": [-73.471869, 42.766718], "pop": 1339, "state": "NY", "_id": "12052"} -{"city": "DELANSON", "loc": [-74.18681, 42.74802], "pop": 4476, "state": "NY", "_id": "12053"} -{"city": "DELMAR", "loc": [-73.837329, 42.61579], "pop": 15502, "state": "NY", "_id": "12054"} -{"city": "DORMANSVILLE", "loc": [-74.198966, 42.437859], "pop": 465, "state": "NY", "_id": "12055"} -{"city": "DUANESBURG", "loc": [-74.083911, 42.770839], "pop": 2675, "state": "NY", "_id": "12056"} -{"city": "WHITE CREEK", "loc": [-73.351974, 42.961527], "pop": 2252, "state": "NY", "_id": "12057"} -{"city": "EARLTON", "loc": [-73.906222, 42.352689], "pop": 1015, "state": "NY", "_id": "12058"} -{"city": "EAST BERNE", "loc": [-74.055488, 42.61913], "pop": 2224, "state": "NY", "_id": "12059"} -{"city": "EAST CHATHAM", "loc": [-73.49026, 42.433028], "pop": 1695, "state": "NY", "_id": "12060"} -{"city": "EAST GREENBUSH", "loc": [-73.682644, 42.595096], "pop": 7282, "state": "NY", "_id": "12061"} -{"city": "EAST NASSAU", "loc": [-73.498407, 42.535215], "pop": 2088, "state": "NY", "_id": "12062"} -{"city": "EAST WORCESTER", "loc": [-74.676348, 42.621423], "pop": 322, "state": "NY", "_id": "12064"} -{"city": "CLIFTON PARK", "loc": [-73.785094, 42.849865], "pop": 31352, "state": "NY", "_id": "12065"} -{"city": "ESPERANCE", "loc": [-74.288246, 42.771677], "pop": 1254, "state": "NY", "_id": "12066"} -{"city": "FEURA BUSH", "loc": [-73.923743, 42.554998], "pop": 1142, "state": "NY", "_id": "12067"} -{"city": "FONDA", "loc": [-74.402129, 42.957078], "pop": 2307, "state": "NY", "_id": "12068"} -{"city": "FORT JOHNSON", "loc": [-74.248436, 42.976535], "pop": 1811, "state": "NY", "_id": "12070"} -{"city": "FULTONHAM", "loc": [-74.38759, 42.585914], "pop": 379, "state": "NY", "_id": "12071"} -{"city": "FULTONVILLE", "loc": [-74.359765, 42.903601], "pop": 2356, "state": "NY", "_id": "12072"} -{"city": "GALWAY", "loc": [-74.029043, 43.021693], "pop": 3988, "state": "NY", "_id": "12074"} -{"city": "GHENT", "loc": [-73.64864, 42.303637], "pop": 2381, "state": "NY", "_id": "12075"} -{"city": "GILBOA", "loc": [-74.400275, 42.410835], "pop": 1400, "state": "NY", "_id": "12076"} -{"city": "GLENMONT", "loc": [-73.795884, 42.597147], "pop": 4298, "state": "NY", "_id": "12077"} -{"city": "GLOVERSVILLE", "loc": [-74.337526, 43.061603], "pop": 25292, "state": "NY", "_id": "12078"} -{"city": "GREENVILLE", "loc": [-74.022222, 42.411342], "pop": 3645, "state": "NY", "_id": "12083"} -{"city": "GUILDERLAND", "loc": [-73.897454, 42.697273], "pop": 4112, "state": "NY", "_id": "12084"} -{"city": "HAGAMAN", "loc": [-74.166764, 42.959715], "pop": 114, "state": "NY", "_id": "12086"} -{"city": "HANNACROIX", "loc": [-73.868029, 42.428533], "pop": 1665, "state": "NY", "_id": "12087"} -{"city": "HOOSICK FALLS", "loc": [-73.358105, 42.893712], "pop": 5910, "state": "NY", "_id": "12090"} -{"city": "HOWES CAVE", "loc": [-74.364825, 42.704526], "pop": 2351, "state": "NY", "_id": "12092"} -{"city": "JEFFERSON", "loc": [-74.611744, 42.499869], "pop": 1353, "state": "NY", "_id": "12093"} -{"city": "JOHNSONVILLE", "loc": [-73.498899, 42.876875], "pop": 2010, "state": "NY", "_id": "12094"} -{"city": "JOHNSTOWN", "loc": [-74.37149, 43.006923], "pop": 13170, "state": "NY", "_id": "12095"} -{"city": "KINDERHOOK", "loc": [-73.718259, 42.376675], "pop": 2795, "state": "NY", "_id": "12106"} -{"city": "LAKE PLEASANT", "loc": [-74.422563, 43.472543], "pop": 487, "state": "NY", "_id": "12108"} -{"city": "LATHAM", "loc": [-73.762985, 42.74616], "pop": 20091, "state": "NY", "_id": "12110"} -{"city": "LAWYERSVILLE", "loc": [-74.506969, 42.692682], "pop": 91, "state": "NY", "_id": "12113"} -{"city": "MALDEN BRIDGE", "loc": [-73.587958, 42.460201], "pop": 56, "state": "NY", "_id": "12115"} -{"city": "MARYLAND", "loc": [-74.903033, 42.537075], "pop": 871, "state": "NY", "_id": "12116"} -{"city": "MAYFIELD", "loc": [-74.244397, 43.141133], "pop": 3046, "state": "NY", "_id": "12117"} -{"city": "MECHANICVILLE", "loc": [-73.673173, 42.967613], "pop": 5890, "state": "NY", "_id": "12118"} -{"city": "MEDUSA", "loc": [-74.131524, 42.451504], "pop": 617, "state": "NY", "_id": "12120"} -{"city": "MELROSE", "loc": [-73.607667, 42.841163], "pop": 1968, "state": "NY", "_id": "12121"} -{"city": "MIDDLEBURGH", "loc": [-74.329168, 42.563734], "pop": 4005, "state": "NY", "_id": "12122"} -{"city": "NASSAU", "loc": [-73.611753, 42.527141], "pop": 5560, "state": "NY", "_id": "12123"} -{"city": "NEW LEBANON", "loc": [-73.377295, 42.475865], "pop": 557, "state": "NY", "_id": "12125"} -{"city": "NIVERVILLE", "loc": [-73.485614, 42.927575], "pop": 300, "state": "NY", "_id": "12130"} -{"city": "NORTH BLENHEIM", "loc": [-74.428174, 42.489938], "pop": 50, "state": "NY", "_id": "12131"} -{"city": "EDINBURG", "loc": [-74.168361, 43.237126], "pop": 3216, "state": "NY", "_id": "12134"} -{"city": "NORTON HILL", "loc": [-74.078945, 42.420014], "pop": 163, "state": "NY", "_id": "12135"} -{"city": "OLD CHATHAM", "loc": [-73.55447, 42.435692], "pop": 1037, "state": "NY", "_id": "12136"} -{"city": "PATTERSONVILLE", "loc": [-74.123126, 42.84995], "pop": 1423, "state": "NY", "_id": "12137"} -{"city": "TACONIC LAKE", "loc": [-73.371033, 42.736367], "pop": 3891, "state": "NY", "_id": "12138"} -{"city": "PISECO", "loc": [-74.526282, 43.448125], "pop": 223, "state": "NY", "_id": "12139"} -{"city": "POESTENKILL", "loc": [-73.588846, 42.679548], "pop": 1904, "state": "NY", "_id": "12140"} -{"city": "RAVENA", "loc": [-73.821991, 42.475371], "pop": 5976, "state": "NY", "_id": "12143"} -{"city": "RENSSELAER", "loc": [-73.721895, 42.635855], "pop": 19146, "state": "NY", "_id": "12144"} -{"city": "RENSSELAERVILLE", "loc": [-74.147431, 42.513288], "pop": 547, "state": "NY", "_id": "12147"} -{"city": "REXFORD", "loc": [-73.8701, 42.852411], "pop": 2306, "state": "NY", "_id": "12148"} -{"city": "RICHMONDVILLE", "loc": [-74.571001, 42.642445], "pop": 1502, "state": "NY", "_id": "12149"} -{"city": "ROTTERDAM JUNCTI", "loc": [-74.046857, 42.87296], "pop": 1128, "state": "NY", "_id": "12150"} -{"city": "ROUND LAKE", "loc": [-73.770127, 42.925994], "pop": 451, "state": "NY", "_id": "12151"} -{"city": "SAND LAKE", "loc": [-73.498949, 42.637912], "pop": 851, "state": "NY", "_id": "12153"} -{"city": "SCHAGHTICOKE", "loc": [-73.615428, 42.914356], "pop": 3035, "state": "NY", "_id": "12154"} -{"city": "SCHENEVUS", "loc": [-74.814863, 42.59004], "pop": 1895, "state": "NY", "_id": "12155"} -{"city": "SCHODACK LANDING", "loc": [-73.747996, 42.481644], "pop": 902, "state": "NY", "_id": "12156"} -{"city": "SCHOHARIE", "loc": [-74.30473, 42.661503], "pop": 4095, "state": "NY", "_id": "12157"} -{"city": "SELKIRK", "loc": [-73.812863, 42.54861], "pop": 6062, "state": "NY", "_id": "12158"} -{"city": "SLINGERLANDS", "loc": [-73.871065, 42.648461], "pop": 5431, "state": "NY", "_id": "12159"} -{"city": "SLOANSVILLE", "loc": [-74.364174, 42.759852], "pop": 1152, "state": "NY", "_id": "12160"} -{"city": "SPECULATOR", "loc": [-74.36667, 43.504159], "pop": 400, "state": "NY", "_id": "12164"} -{"city": "SPENCERTOWN", "loc": [-73.500754, 42.30908], "pop": 127, "state": "NY", "_id": "12165"} -{"city": "SPRAKERS", "loc": [-74.453558, 42.848446], "pop": 1608, "state": "NY", "_id": "12166"} -{"city": "STAMFORD", "loc": [-74.609831, 42.417409], "pop": 2734, "state": "NY", "_id": "12167"} -{"city": "STEPHENTOWN", "loc": [-73.422447, 42.523323], "pop": 1036, "state": "NY", "_id": "12168"} -{"city": "STEPHENTOWN", "loc": [-73.374964, 42.556224], "pop": 1566, "state": "NY", "_id": "12169"} -{"city": "STILLWATER", "loc": [-73.697163, 42.905652], "pop": 9226, "state": "NY", "_id": "12170"} -{"city": "STUYVESANT", "loc": [-73.761329, 42.359572], "pop": 1806, "state": "NY", "_id": "12173"} -{"city": "SUMMIT", "loc": [-74.574952, 42.587122], "pop": 1247, "state": "NY", "_id": "12175"} -{"city": "SURPRISE", "loc": [-73.951541, 42.361171], "pop": 162, "state": "NY", "_id": "12176"} -{"city": "TROY", "loc": [-73.668263, 42.728748], "pop": 56849, "state": "NY", "_id": "12180"} -{"city": "TROY", "loc": [-73.664806, 42.782921], "pop": 14688, "state": "NY", "_id": "12182"} -{"city": "GREEN ISLAND", "loc": [-73.693707, 42.743812], "pop": 2432, "state": "NY", "_id": "12183"} -{"city": "VALATIE", "loc": [-73.668322, 42.432051], "pop": 8072, "state": "NY", "_id": "12184"} -{"city": "VALLEY FALLS", "loc": [-73.543674, 42.885458], "pop": 1835, "state": "NY", "_id": "12185"} -{"city": "VOORHEESVILLE", "loc": [-73.944773, 42.643108], "pop": 7069, "state": "NY", "_id": "12186"} -{"city": "WARNERVILLE", "loc": [-74.487046, 42.638826], "pop": 782, "state": "NY", "_id": "12187"} -{"city": "WATERFORD", "loc": [-73.699481, 42.809957], "pop": 11576, "state": "NY", "_id": "12188"} -{"city": "WATERVLIET", "loc": [-73.712342, 42.729843], "pop": 16509, "state": "NY", "_id": "12189"} -{"city": "WELLS", "loc": [-74.288583, 43.401219], "pop": 706, "state": "NY", "_id": "12190"} -{"city": "WEST COXSACKIE", "loc": [-73.817033, 42.415055], "pop": 1982, "state": "NY", "_id": "12192"} -{"city": "WESTERLO", "loc": [-74.039383, 42.515621], "pop": 1880, "state": "NY", "_id": "12193"} -{"city": "WEST FULTON", "loc": [-74.463105, 42.550615], "pop": 72, "state": "NY", "_id": "12194"} -{"city": "WEST SAND LAKE", "loc": [-73.610896, 42.637969], "pop": 2511, "state": "NY", "_id": "12196"} -{"city": "WORCESTER", "loc": [-74.72992, 42.604889], "pop": 2239, "state": "NY", "_id": "12197"} -{"city": "WYNANTSKILL", "loc": [-73.63826, 42.687785], "pop": 6192, "state": "NY", "_id": "12198"} -{"city": "ALBANY", "loc": [-73.764071, 42.641314], "pop": 11097, "state": "NY", "_id": "12202"} -{"city": "MC KOWNVILLE", "loc": [-73.821988, 42.676757], "pop": 33356, "state": "NY", "_id": "12203"} -{"city": "ALBANY", "loc": [-73.735364, 42.684667], "pop": 6927, "state": "NY", "_id": "12204"} -{"city": "ROESSLEVILLE", "loc": [-73.820174, 42.713116], "pop": 26008, "state": "NY", "_id": "12205"} -{"city": "ALBANY", "loc": [-73.774406, 42.668326], "pop": 17230, "state": "NY", "_id": "12206"} -{"city": "ALBANY", "loc": [-73.752327, 42.658133], "pop": 2709, "state": "NY", "_id": "12207"} -{"city": "ALBANY", "loc": [-73.796357, 42.655989], "pop": 22041, "state": "NY", "_id": "12208"} -{"city": "ALBANY", "loc": [-73.785385, 42.641665], "pop": 10008, "state": "NY", "_id": "12209"} -{"city": "ALBANY", "loc": [-73.76052, 42.65677], "pop": 9374, "state": "NY", "_id": "12210"} -{"city": "LOUDONVILLE", "loc": [-73.769982, 42.704693], "pop": 12283, "state": "NY", "_id": "12211"} -{"city": "MAYFAIR", "loc": [-73.955051, 42.858839], "pop": 27516, "state": "NY", "_id": "12302"} -{"city": "ROTTERDAM", "loc": [-73.938776, 42.769645], "pop": 28448, "state": "NY", "_id": "12303"} -{"city": "SCHENECTADY", "loc": [-73.909432, 42.784083], "pop": 20431, "state": "NY", "_id": "12304"} -{"city": "SCHENECTADY", "loc": [-73.939786, 42.816131], "pop": 6253, "state": "NY", "_id": "12305"} -{"city": "SCHENECTADY", "loc": [-73.980876, 42.790384], "pop": 23182, "state": "NY", "_id": "12306"} -{"city": "SCHENECTADY", "loc": [-73.936349, 42.804653], "pop": 8534, "state": "NY", "_id": "12307"} -{"city": "SCHENECTADY", "loc": [-73.920591, 42.817928], "pop": 14055, "state": "NY", "_id": "12308"} -{"city": "NISKAYUNA", "loc": [-73.878268, 42.796168], "pop": 27928, "state": "NY", "_id": "12309"} -{"city": "EDDYVILLE", "loc": [-74.023575, 41.930126], "pop": 32883, "state": "NY", "_id": "12401"} -{"city": "ACCORD", "loc": [-74.235336, 41.808308], "pop": 2695, "state": "NY", "_id": "12404"} -{"city": "ACRA", "loc": [-74.085723, 42.330367], "pop": 525, "state": "NY", "_id": "12405"} -{"city": "ARKVILLE", "loc": [-74.554453, 42.082262], "pop": 141, "state": "NY", "_id": "12406"} -{"city": "ASHLAND", "loc": [-74.307925, 42.237174], "pop": 28, "state": "NY", "_id": "12407"} -{"city": "SHADY", "loc": [-74.171298, 42.041991], "pop": 960, "state": "NY", "_id": "12409"} -{"city": "OLIVEREA", "loc": [-74.446655, 42.143884], "pop": 69, "state": "NY", "_id": "12410"} -{"city": "BLOOMINGTON", "loc": [-74.066889, 41.856165], "pop": 148, "state": "NY", "_id": "12411"} -{"city": "BOICEVILLE", "loc": [-74.265808, 42.004761], "pop": 556, "state": "NY", "_id": "12412"} -{"city": "CAIRO", "loc": [-74.01154, 42.30965], "pop": 3057, "state": "NY", "_id": "12413"} -{"city": "CATSKILL", "loc": [-73.898536, 42.227598], "pop": 12128, "state": "NY", "_id": "12414"} -{"city": "CHICHESTER", "loc": [-74.281565, 42.087965], "pop": 642, "state": "NY", "_id": "12416"} -{"city": "CORNWALLVILLE", "loc": [-74.163092, 42.362874], "pop": 380, "state": "NY", "_id": "12418"} -{"city": "COTTEKILL", "loc": [-74.103774, 41.846706], "pop": 399, "state": "NY", "_id": "12419"} -{"city": "DENVER", "loc": [-74.540654, 42.252204], "pop": 62, "state": "NY", "_id": "12421"} -{"city": "DURHAM", "loc": [-74.184926, 42.402037], "pop": 216, "state": "NY", "_id": "12422"} -{"city": "EAST DURHAM", "loc": [-74.11169, 42.385978], "pop": 1097, "state": "NY", "_id": "12423"} -{"city": "EAST JEWETT", "loc": [-74.207981, 42.280567], "pop": 241, "state": "NY", "_id": "12424"} -{"city": "ELKA PARK", "loc": [-74.124539, 42.164309], "pop": 389, "state": "NY", "_id": "12427"} -{"city": "ELLENVILLE", "loc": [-74.414125, 41.721805], "pop": 6902, "state": "NY", "_id": "12428"} -{"city": "HALCOTT CENTER", "loc": [-74.531908, 42.178765], "pop": 1151, "state": "NY", "_id": "12430"} -{"city": "FREEHOLD", "loc": [-74.062266, 42.3815], "pop": 255, "state": "NY", "_id": "12431"} -{"city": "GLENFORD", "loc": [-74.153154, 42.005342], "pop": 354, "state": "NY", "_id": "12433"} -{"city": "GRAND GORGE", "loc": [-74.531173, 42.387358], "pop": 13, "state": "NY", "_id": "12434"} -{"city": "GREENFIELD PARK", "loc": [-74.520074, 41.728133], "pop": 241, "state": "NY", "_id": "12435"} -{"city": "EAST WINDHAM", "loc": [-74.213063, 42.259032], "pop": 57, "state": "NY", "_id": "12439"} -{"city": "HIGH FALLS", "loc": [-74.131122, 41.816749], "pop": 2168, "state": "NY", "_id": "12440"} -{"city": "HUNTER", "loc": [-74.203753, 42.237316], "pop": 616, "state": "NY", "_id": "12442"} -{"city": "HURLEY", "loc": [-74.06873, 41.932743], "pop": 1229, "state": "NY", "_id": "12443"} -{"city": "JEWETT", "loc": [-74.279274, 42.269383], "pop": 231, "state": "NY", "_id": "12444"} -{"city": "KERHONKSON", "loc": [-74.303457, 41.793866], "pop": 4220, "state": "NY", "_id": "12446"} -{"city": "LAKE HILL", "loc": [-74.212338, 42.073271], "pop": 73, "state": "NY", "_id": "12448"} -{"city": "LAKE KATRINE", "loc": [-73.992379, 41.991787], "pop": 3763, "state": "NY", "_id": "12449"} -{"city": "LANESVILLE", "loc": [-74.19715, 42.189149], "pop": 1131, "state": "NY", "_id": "12450"} -{"city": "LEEDS", "loc": [-73.945726, 42.304506], "pop": 779, "state": "NY", "_id": "12451"} -{"city": "MAPLECREST", "loc": [-74.165522, 42.299485], "pop": 342, "state": "NY", "_id": "12454"} -{"city": "KELLY CORNERS", "loc": [-74.648853, 42.163702], "pop": 2756, "state": "NY", "_id": "12455"} -{"city": "MOUNT MARION", "loc": [-74.000211, 42.035704], "pop": 753, "state": "NY", "_id": "12456"} -{"city": "MOUNT TREMPER", "loc": [-74.248481, 42.043545], "pop": 941, "state": "NY", "_id": "12457"} -{"city": "NAPANOCH", "loc": [-74.380354, 41.758965], "pop": 3502, "state": "NY", "_id": "12458"} -{"city": "OAK HILL", "loc": [-74.152832, 42.406902], "pop": 294, "state": "NY", "_id": "12460"} -{"city": "KRUMVILLE", "loc": [-74.246954, 41.895906], "pop": 1423, "state": "NY", "_id": "12461"} -{"city": "12462", "loc": [-74.444263, 42.026764], "pop": 53, "state": "NY", "_id": "12462"} -{"city": "PALENVILLE", "loc": [-74.016674, 42.17294], "pop": 1195, "state": "NY", "_id": "12463"} -{"city": "PHOENICIA", "loc": [-74.339328, 42.054426], "pop": 966, "state": "NY", "_id": "12464"} -{"city": "PINE HILL", "loc": [-74.487562, 42.133974], "pop": 392, "state": "NY", "_id": "12465"} -{"city": "PORT EWEN", "loc": [-73.987161, 41.913113], "pop": 7283, "state": "NY", "_id": "12466"} -{"city": "PRATTSVILLE", "loc": [-74.389502, 42.297904], "pop": 1669, "state": "NY", "_id": "12468"} -{"city": "PRESTON HOLLOW", "loc": [-74.24199, 42.456348], "pop": 421, "state": "NY", "_id": "12469"} -{"city": "PURLING", "loc": [-74.01138, 42.275497], "pop": 516, "state": "NY", "_id": "12470"} -{"city": "ROSENDALE", "loc": [-74.072999, 41.840248], "pop": 2939, "state": "NY", "_id": "12472"} -{"city": "ROUND TOP", "loc": [-74.052279, 42.267782], "pop": 454, "state": "NY", "_id": "12473"} -{"city": "ROXBURY", "loc": [-74.540519, 42.311688], "pop": 2178, "state": "NY", "_id": "12474"} -{"city": "SAUGERTIES", "loc": [-73.979684, 42.07376], "pop": 18932, "state": "NY", "_id": "12477"} -{"city": "SHANDAKEN", "loc": [-74.409084, 42.108353], "pop": 593, "state": "NY", "_id": "12480"} -{"city": "SHOKAN", "loc": [-74.211943, 41.976678], "pop": 1494, "state": "NY", "_id": "12481"} -{"city": "SOUTH CAIRO", "loc": [-73.963982, 42.27338], "pop": 360, "state": "NY", "_id": "12482"} -{"city": "STONE RIDGE", "loc": [-74.169748, 41.861562], "pop": 2389, "state": "NY", "_id": "12484"} -{"city": "TANNERSVILLE", "loc": [-74.101441, 42.203179], "pop": 492, "state": "NY", "_id": "12485"} -{"city": "TILLSON", "loc": [-74.072219, 41.816279], "pop": 377, "state": "NY", "_id": "12486"} -{"city": "ULSTER PARK", "loc": [-73.994843, 41.865109], "pop": 3625, "state": "NY", "_id": "12487"} -{"city": "WEST HURLEY", "loc": [-74.115232, 41.990816], "pop": 2365, "state": "NY", "_id": "12491"} -{"city": "WEST KILL", "loc": [-74.361994, 42.204584], "pop": 413, "state": "NY", "_id": "12492"} -{"city": "WEST SHOKAN", "loc": [-74.285117, 41.955478], "pop": 809, "state": "NY", "_id": "12494"} -{"city": "WILLOW", "loc": [-74.202537, 42.092318], "pop": 145, "state": "NY", "_id": "12495"} -{"city": "WINDHAM", "loc": [-74.262017, 42.317465], "pop": 1559, "state": "NY", "_id": "12496"} -{"city": "WOODSTOCK", "loc": [-74.111974, 42.034793], "pop": 4827, "state": "NY", "_id": "12498"} -{"city": "AMENIA", "loc": [-73.554158, 41.844695], "pop": 2325, "state": "NY", "_id": "12501"} -{"city": "ANCRAM", "loc": [-73.642368, 42.085093], "pop": 600, "state": "NY", "_id": "12502"} -{"city": "ANCRAMDALE", "loc": [-73.58187, 42.038103], "pop": 918, "state": "NY", "_id": "12503"} -{"city": "BARRYTOWN", "loc": [-73.921484, 42.0006], "pop": 388, "state": "NY", "_id": "12507"} -{"city": "BEACON", "loc": [-73.963384, 41.509681], "pop": 20022, "state": "NY", "_id": "12508"} -{"city": "CLAVERACK", "loc": [-73.722844, 42.2183], "pop": 447, "state": "NY", "_id": "12513"} -{"city": "CLINTON CORNERS", "loc": [-73.765867, 41.869262], "pop": 2667, "state": "NY", "_id": "12514"} -{"city": "CLINTONDALE", "loc": [-74.055713, 41.674939], "pop": 1171, "state": "NY", "_id": "12515"} -{"city": "COPAKE", "loc": [-73.552588, 42.111329], "pop": 1511, "state": "NY", "_id": "12516"} -{"city": "COPAKE FALLS", "loc": [-73.510773, 42.136737], "pop": 163, "state": "NY", "_id": "12517"} -{"city": "CORNWALL", "loc": [-74.053877, 41.430944], "pop": 8120, "state": "NY", "_id": "12518"} -{"city": "CORNWALL ON HUDS", "loc": [-74.016411, 41.443031], "pop": 2980, "state": "NY", "_id": "12520"} -{"city": "CRARYVILLE", "loc": [-73.657128, 42.175961], "pop": 1377, "state": "NY", "_id": "12521"} -{"city": "DOVER PLAINS", "loc": [-73.587024, 41.735054], "pop": 4775, "state": "NY", "_id": "12522"} -{"city": "ELIZAVILLE", "loc": [-73.781814, 42.090173], "pop": 2158, "state": "NY", "_id": "12523"} -{"city": "FISHKILL", "loc": [-73.89791, 41.540352], "pop": 11165, "state": "NY", "_id": "12524"} -{"city": "GARDINER", "loc": [-74.167155, 41.657615], "pop": 3900, "state": "NY", "_id": "12525"} -{"city": "GERMANTOWN", "loc": [-73.862451, 42.1219], "pop": 4061, "state": "NY", "_id": "12526"} -{"city": "HIGHLAND", "loc": [-73.992825, 41.716691], "pop": 11011, "state": "NY", "_id": "12528"} -{"city": "HILLSDALE", "loc": [-73.548306, 42.186816], "pop": 3084, "state": "NY", "_id": "12529"} -{"city": "HOLMES", "loc": [-73.662751, 41.532461], "pop": 3248, "state": "NY", "_id": "12531"} -{"city": "HOPEWELL JUNCTIO", "loc": [-73.797581, 41.576639], "pop": 18770, "state": "NY", "_id": "12533"} -{"city": "HUDSON", "loc": [-73.755248, 42.246978], "pop": 21205, "state": "NY", "_id": "12534"} -{"city": "HYDE PARK", "loc": [-73.906347, 41.788724], "pop": 15184, "state": "NY", "_id": "12538"} -{"city": "LAGRANGEVILLE", "loc": [-73.744955, 41.661471], "pop": 5539, "state": "NY", "_id": "12540"} -{"city": "MARLBORO", "loc": [-73.988017, 41.605612], "pop": 4489, "state": "NY", "_id": "12542"} -{"city": "MAYBROOK", "loc": [-74.216312, 41.48865], "pop": 2860, "state": "NY", "_id": "12543"} -{"city": "MILLBROOK", "loc": [-73.688491, 41.780334], "pop": 4503, "state": "NY", "_id": "12545"} -{"city": "MILLERTON", "loc": [-73.528709, 41.953623], "pop": 3131, "state": "NY", "_id": "12546"} -{"city": "MILTON", "loc": [-73.977194, 41.653487], "pop": 2834, "state": "NY", "_id": "12547"} -{"city": "MODENA", "loc": [-74.103578, 41.650347], "pop": 810, "state": "NY", "_id": "12548"} -{"city": "MONTGOMERY", "loc": [-74.253417, 41.53332], "pop": 7421, "state": "NY", "_id": "12549"} -{"city": "MIDDLE HOPE", "loc": [-74.03598, 41.517833], "pop": 47939, "state": "NY", "_id": "12550"} -{"city": "NEW WINDSOR", "loc": [-74.056596, 41.472374], "pop": 20576, "state": "NY", "_id": "12553"} -{"city": "MOHONK LAKE", "loc": [-74.083875, 41.743346], "pop": 16394, "state": "NY", "_id": "12561"} -{"city": "PATTERSON", "loc": [-73.58149, 41.488761], "pop": 6107, "state": "NY", "_id": "12563"} -{"city": "PAWLING", "loc": [-73.594847, 41.574893], "pop": 4511, "state": "NY", "_id": "12564"} -{"city": "PINE BUSH", "loc": [-74.326311, 41.617758], "pop": 7589, "state": "NY", "_id": "12566"} -{"city": "PINE PLAINS", "loc": [-73.660227, 41.989569], "pop": 2771, "state": "NY", "_id": "12567"} -{"city": "PLEASANT VALLEY", "loc": [-73.814276, 41.747032], "pop": 7978, "state": "NY", "_id": "12569"} -{"city": "POUGHQUAG", "loc": [-73.678328, 41.61936], "pop": 3867, "state": "NY", "_id": "12570"} -{"city": "RED HOOK", "loc": [-73.854577, 42.006439], "pop": 8890, "state": "NY", "_id": "12571"} -{"city": "RHINEBECK", "loc": [-73.888754, 41.927206], "pop": 9458, "state": "NY", "_id": "12572"} -{"city": "ROCK TAVERN", "loc": [-74.16588, 41.457523], "pop": 2202, "state": "NY", "_id": "12575"} -{"city": "SALISBURY MILLS", "loc": [-74.121378, 41.449714], "pop": 573, "state": "NY", "_id": "12577"} -{"city": "SALT POINT", "loc": [-73.801329, 41.805041], "pop": 1521, "state": "NY", "_id": "12578"} -{"city": "STAATSBURG", "loc": [-73.898838, 41.850193], "pop": 2957, "state": "NY", "_id": "12580"} -{"city": "STANFORDVILLE", "loc": [-73.694467, 41.887726], "pop": 2356, "state": "NY", "_id": "12581"} -{"city": "STORMVILLE", "loc": [-73.725548, 41.551193], "pop": 6735, "state": "NY", "_id": "12582"} -{"city": "TIVOLI", "loc": [-73.902514, 42.057945], "pop": 1614, "state": "NY", "_id": "12583"} -{"city": "VERBANK", "loc": [-73.71841, 41.722664], "pop": 989, "state": "NY", "_id": "12585"} -{"city": "WALDEN", "loc": [-74.176395, 41.559631], "pop": 10479, "state": "NY", "_id": "12586"} -{"city": "WALLKILL", "loc": [-74.143853, 41.615952], "pop": 11209, "state": "NY", "_id": "12589"} -{"city": "NEW HAMBURG", "loc": [-73.890588, 41.592199], "pop": 31950, "state": "NY", "_id": "12590"} -{"city": "WASSAIC", "loc": [-73.554382, 41.775884], "pop": 2064, "state": "NY", "_id": "12592"} -{"city": "WINGDALE", "loc": [-73.555621, 41.653824], "pop": 3716, "state": "NY", "_id": "12594"} -{"city": "SOUTH ROAD", "loc": [-73.9218, 41.702082], "pop": 38212, "state": "NY", "_id": "12601"} -{"city": "ARLINGTON", "loc": [-73.885217, 41.676775], "pop": 40724, "state": "NY", "_id": "12603"} -{"city": "MONTICELLO", "loc": [-74.700748, 41.65158], "pop": 12122, "state": "NY", "_id": "12701"} -{"city": "BARRYVILLE", "loc": [-74.915234, 41.491162], "pop": 772, "state": "NY", "_id": "12719"} -{"city": "BETHEL", "loc": [-74.893984, 41.669326], "pop": 139, "state": "NY", "_id": "12720"} -{"city": "BLOOMINGBURG", "loc": [-74.430351, 41.564427], "pop": 6139, "state": "NY", "_id": "12721"} -{"city": "CALLICOON", "loc": [-75.025688, 41.7754], "pop": 2492, "state": "NY", "_id": "12723"} -{"city": "CLARYVILLE", "loc": [-74.529287, 41.965666], "pop": 137, "state": "NY", "_id": "12725"} -{"city": "FOSTERDALE", "loc": [-74.980687, 41.698208], "pop": 1120, "state": "NY", "_id": "12726"} -{"city": "COCHECTON CENTER", "loc": [-74.977116, 41.645776], "pop": 131, "state": "NY", "_id": "12727"} -{"city": "CUDDEBACKVILLE", "loc": [-74.597564, 41.477601], "pop": 1191, "state": "NY", "_id": "12729"} -{"city": "ELDRED", "loc": [-74.896769, 41.532793], "pop": 899, "state": "NY", "_id": "12732"} -{"city": "FALLSBURG", "loc": [-74.615409, 41.7273], "pop": 866, "state": "NY", "_id": "12733"} -{"city": "GROSSINGER", "loc": [-74.754873, 41.730029], "pop": 851, "state": "NY", "_id": "12734"} -{"city": "FREMONT CENTER", "loc": [-75.029635, 41.847134], "pop": 349, "state": "NY", "_id": "12736"} -{"city": "GLEN SPEY", "loc": [-74.799493, 41.48576], "pop": 840, "state": "NY", "_id": "12737"} -{"city": "GLEN WILD", "loc": [-74.583289, 41.654536], "pop": 158, "state": "NY", "_id": "12738"} -{"city": "GODEFFROY", "loc": [-74.605716, 41.442347], "pop": 615, "state": "NY", "_id": "12739"} -{"city": "GRAHAMSVILLE", "loc": [-74.512697, 41.880659], "pop": 1305, "state": "NY", "_id": "12740"} -{"city": "MILESES", "loc": [-75.099809, 41.831288], "pop": 73, "state": "NY", "_id": "12741"} -{"city": "HARRIS", "loc": [-74.7218, 41.714055], "pop": 272, "state": "NY", "_id": "12742"} -{"city": "HIGHLAND LAKE", "loc": [-74.851615, 41.530925], "pop": 292, "state": "NY", "_id": "12743"} -{"city": "HORTONVILLE", "loc": [-75.026329, 41.78568], "pop": 59, "state": "NY", "_id": "12745"} -{"city": "HUGUENOT", "loc": [-74.64261, 41.437162], "pop": 885, "state": "NY", "_id": "12746"} -{"city": "HURLEYVILLE", "loc": [-74.65344, 41.760882], "pop": 3303, "state": "NY", "_id": "12747"} -{"city": "JEFFERSONVILLE", "loc": [-74.919574, 41.778394], "pop": 1698, "state": "NY", "_id": "12748"} -{"city": "KENOZA LAKE", "loc": [-74.935384, 41.777802], "pop": 443, "state": "NY", "_id": "12750"} -{"city": "KIAMESHA LAKE", "loc": [-74.672402, 41.683825], "pop": 320, "state": "NY", "_id": "12751"} -{"city": "LAKE HUNTINGTON", "loc": [-74.994933, 41.678158], "pop": 111, "state": "NY", "_id": "12752"} -{"city": "LEW BEACH", "loc": [-74.728835, 42.015762], "pop": 126, "state": "NY", "_id": "12753"} -{"city": "LIBERTY", "loc": [-74.748397, 41.79618], "pop": 7361, "state": "NY", "_id": "12754"} -{"city": "LIVINGSTON MANOR", "loc": [-74.827028, 41.87779], "pop": 4510, "state": "NY", "_id": "12758"} -{"city": "LOCH SHELDRAKE", "loc": [-74.661406, 41.778899], "pop": 466, "state": "NY", "_id": "12759"} -{"city": "LONG EDDY", "loc": [-75.094157, 41.864359], "pop": 279, "state": "NY", "_id": "12760"} -{"city": "MONGAUP VALLEY", "loc": [-74.802772, 41.681017], "pop": 228, "state": "NY", "_id": "12762"} -{"city": "MOUNTAIN DALE", "loc": [-74.535762, 41.691763], "pop": 841, "state": "NY", "_id": "12763"} -{"city": "NARROWSBURG", "loc": [-75.010687, 41.592108], "pop": 1576, "state": "NY", "_id": "12764"} -{"city": "NEVERSINK", "loc": [-74.612726, 41.849205], "pop": 868, "state": "NY", "_id": "12765"} -{"city": "NORTH BRANCH", "loc": [-74.982388, 41.814184], "pop": 409, "state": "NY", "_id": "12766"} -{"city": "PARKSVILLE", "loc": [-74.735933, 41.851686], "pop": 1199, "state": "NY", "_id": "12768"} -{"city": "POND EDDY", "loc": [-74.841029, 41.451068], "pop": 448, "state": "NY", "_id": "12770"} -{"city": "PORT JERVIS", "loc": [-74.669097, 41.378557], "pop": 14959, "state": "NY", "_id": "12771"} -{"city": "ROCK HILL", "loc": [-74.587223, 41.613351], "pop": 1027, "state": "NY", "_id": "12775"} -{"city": "COOK FALLS", "loc": [-74.923704, 41.945774], "pop": 2737, "state": "NY", "_id": "12776"} -{"city": "FORESTBURGH", "loc": [-74.724087, 41.569093], "pop": 581, "state": "NY", "_id": "12777"} -{"city": "SOUTH FALLSBURG", "loc": [-74.644401, 41.704192], "pop": 1838, "state": "NY", "_id": "12779"} -{"city": "SPARROWBUSH", "loc": [-74.723647, 41.435886], "pop": 1527, "state": "NY", "_id": "12780"} -{"city": "SUNDOWN", "loc": [-74.550838, 41.823822], "pop": 635, "state": "NY", "_id": "12782"} -{"city": "SWAN LAKE", "loc": [-74.834092, 41.728479], "pop": 1707, "state": "NY", "_id": "12783"} -{"city": "WHITE LAKE", "loc": [-74.865437, 41.648498], "pop": 178, "state": "NY", "_id": "12786"} -{"city": "WHITE SULPHUR SP", "loc": [-74.828065, 41.790042], "pop": 161, "state": "NY", "_id": "12787"} -{"city": "WOODBOURNE", "loc": [-74.592828, 41.770807], "pop": 2561, "state": "NY", "_id": "12788"} -{"city": "WOODRIDGE", "loc": [-74.581518, 41.716955], "pop": 2047, "state": "NY", "_id": "12789"} -{"city": "WURTSBORO", "loc": [-74.503891, 41.587667], "pop": 4589, "state": "NY", "_id": "12790"} -{"city": "YOUNGSVILLE", "loc": [-74.888776, 41.803238], "pop": 115, "state": "NY", "_id": "12791"} -{"city": "YULAN", "loc": [-74.926224, 41.538378], "pop": 107, "state": "NY", "_id": "12792"} -{"city": "QUEENSBURY", "loc": [-73.648816, 43.312539], "pop": 15023, "state": "NY", "_id": "12801"} -{"city": "SOUTH GLENS FALL", "loc": [-73.637947, 43.283911], "pop": 8758, "state": "NY", "_id": "12803"} -{"city": "QUEENSBURY", "loc": [-73.681846, 43.328983], "pop": 20993, "state": "NY", "_id": "12804"} -{"city": "ADIRONDACK", "loc": [-73.782486, 43.716479], "pop": 105, "state": "NY", "_id": "12808"} -{"city": "ARGYLE", "loc": [-73.464076, 43.238084], "pop": 2930, "state": "NY", "_id": "12809"} -{"city": "ATHOL", "loc": [-73.881695, 43.483872], "pop": 588, "state": "NY", "_id": "12810"} -{"city": "BLUE MOUNTAIN LA", "loc": [-74.429831, 43.837499], "pop": 234, "state": "NY", "_id": "12812"} -{"city": "BOLTON LANDING", "loc": [-73.671392, 43.576641], "pop": 1298, "state": "NY", "_id": "12814"} -{"city": "BRANT LAKE", "loc": [-73.720458, 43.698875], "pop": 807, "state": "NY", "_id": "12815"} -{"city": "CAMBRIDGE", "loc": [-73.381375, 43.046585], "pop": 4243, "state": "NY", "_id": "12816"} -{"city": "CHESTERTOWN", "loc": [-73.806641, 43.645053], "pop": 2141, "state": "NY", "_id": "12817"} -{"city": "CLEMONS", "loc": [-73.432613, 43.643544], "pop": 169, "state": "NY", "_id": "12819"} -{"city": "COMSTOCK", "loc": [-73.360607, 43.456706], "pop": 1226, "state": "NY", "_id": "12821"} -{"city": "CORINTH", "loc": [-73.836901, 43.242569], "pop": 6492, "state": "NY", "_id": "12822"} -{"city": "COSSAYUNA", "loc": [-73.41237, 43.175059], "pop": 241, "state": "NY", "_id": "12823"} -{"city": "DIAMOND POINT", "loc": [-73.700123, 43.515553], "pop": 770, "state": "NY", "_id": "12824"} -{"city": "EAST GREENWICH", "loc": [-73.392425, 43.157918], "pop": 116, "state": "NY", "_id": "12826"} -{"city": "FORT ANN", "loc": [-73.478381, 43.428457], "pop": 6818, "state": "NY", "_id": "12827"} -{"city": "FORT EDWARD", "loc": [-73.582169, 43.265321], "pop": 5507, "state": "NY", "_id": "12828"} -{"city": "GANSEVOORT", "loc": [-73.705267, 43.180343], "pop": 14485, "state": "NY", "_id": "12831"} -{"city": "GRANVILLE", "loc": [-73.297825, 43.377562], "pop": 6201, "state": "NY", "_id": "12832"} -{"city": "GREENFIELD CENTE", "loc": [-73.860193, 43.122488], "pop": 4753, "state": "NY", "_id": "12833"} -{"city": "THOMSON", "loc": [-73.506658, 43.096183], "pop": 6028, "state": "NY", "_id": "12834"} -{"city": "HADLEY", "loc": [-73.949905, 43.301268], "pop": 1840, "state": "NY", "_id": "12835"} -{"city": "HAGUE", "loc": [-73.528172, 43.74631], "pop": 692, "state": "NY", "_id": "12836"} -{"city": "HAMPTON", "loc": [-73.273072, 43.462135], "pop": 419, "state": "NY", "_id": "12837"} -{"city": "HARTFORD", "loc": [-73.404946, 43.349281], "pop": 679, "state": "NY", "_id": "12838"} -{"city": "HUDSON FALLS", "loc": [-73.574607, 43.314863], "pop": 12866, "state": "NY", "_id": "12839"} -{"city": "INDIAN LAKE", "loc": [-74.276638, 43.760594], "pop": 1247, "state": "NY", "_id": "12842"} -{"city": "JOHNSBURG", "loc": [-74.021254, 43.634081], "pop": 1437, "state": "NY", "_id": "12843"} -{"city": "PILOT KNOB", "loc": [-73.629883, 43.515561], "pop": 23, "state": "NY", "_id": "12844"} -{"city": "LAKE GEORGE", "loc": [-73.697547, 43.416725], "pop": 4677, "state": "NY", "_id": "12845"} -{"city": "LAKE LUZERNE", "loc": [-73.822821, 43.316487], "pop": 2816, "state": "NY", "_id": "12846"} -{"city": "LONG LAKE", "loc": [-74.466243, 43.947653], "pop": 930, "state": "NY", "_id": "12847"} -{"city": "MIDDLE GRANVILLE", "loc": [-73.303077, 43.450773], "pop": 249, "state": "NY", "_id": "12849"} -{"city": "MIDDLE GROVE", "loc": [-74.016687, 43.097548], "pop": 1760, "state": "NY", "_id": "12850"} -{"city": "MINERVA", "loc": [-73.983542, 43.781058], "pop": 308, "state": "NY", "_id": "12851"} -{"city": "NEWCOMB", "loc": [-74.129911, 43.945991], "pop": 544, "state": "NY", "_id": "12852"} -{"city": "NORTH CREEK", "loc": [-73.892802, 43.713802], "pop": 2447, "state": "NY", "_id": "12853"} -{"city": "NORTH GRANVILLE", "loc": [-73.330114, 43.506212], "pop": 185, "state": "NY", "_id": "12854"} -{"city": "NORTH HUDSON", "loc": [-73.712065, 43.986872], "pop": 266, "state": "NY", "_id": "12855"} -{"city": "OLMSTEDVILLE", "loc": [-73.933479, 43.779931], "pop": 450, "state": "NY", "_id": "12857"} -{"city": "PARADOX", "loc": [-73.644956, 43.891382], "pop": 29, "state": "NY", "_id": "12858"} -{"city": "PORTER CORNERS", "loc": [-73.883918, 43.172358], "pop": 1028, "state": "NY", "_id": "12859"} -{"city": "POTTERSVILLE", "loc": [-73.756438, 43.692956], "pop": 252, "state": "NY", "_id": "12860"} -{"city": "PUTNAM STATION", "loc": [-73.412299, 43.755976], "pop": 477, "state": "NY", "_id": "12861"} -{"city": "ROCK CITY FALLS", "loc": [-73.921523, 43.066248], "pop": 553, "state": "NY", "_id": "12863"} -{"city": "SALEM", "loc": [-73.332703, 43.182785], "pop": 1965, "state": "NY", "_id": "12865"} -{"city": "WILTON", "loc": [-73.780644, 43.080094], "pop": 30086, "state": "NY", "_id": "12866"} -{"city": "SCHROON LAKE", "loc": [-73.767382, 43.841159], "pop": 1656, "state": "NY", "_id": "12870"} -{"city": "SCHUYLERVILLE", "loc": [-73.60068, 43.087778], "pop": 3929, "state": "NY", "_id": "12871"} -{"city": "SEVERANCE", "loc": [-73.730127, 43.876903], "pop": 36, "state": "NY", "_id": "12872"} -{"city": "SHUSHAN", "loc": [-73.323148, 43.110575], "pop": 755, "state": "NY", "_id": "12873"} -{"city": "SILVER BAY", "loc": [-73.507062, 43.697804], "pop": 7, "state": "NY", "_id": "12874"} -{"city": "STONY CREEK", "loc": [-73.949467, 43.421389], "pop": 625, "state": "NY", "_id": "12878"} -{"city": "TICONDEROGA", "loc": [-73.442592, 43.846302], "pop": 5149, "state": "NY", "_id": "12883"} -{"city": "WARRENSBURG", "loc": [-73.792021, 43.500253], "pop": 4399, "state": "NY", "_id": "12885"} -{"city": "WEVERTOWN", "loc": [-73.930909, 43.64129], "pop": 137, "state": "NY", "_id": "12886"} -{"city": "WHITEHALL", "loc": [-73.386412, 43.5531], "pop": 5372, "state": "NY", "_id": "12887"} -{"city": "PLATTSBURGH", "loc": [-73.465969, 44.692715], "pop": 40905, "state": "NY", "_id": "12901"} -{"city": "ALTONA", "loc": [-73.640767, 44.881584], "pop": 2456, "state": "NY", "_id": "12910"} -{"city": "AU SABLE CHASM", "loc": [-73.508976, 44.521594], "pop": 514, "state": "NY", "_id": "12911"} -{"city": "AU SABLE FORKS", "loc": [-73.685672, 44.44994], "pop": 2143, "state": "NY", "_id": "12912"} -{"city": "BLOOMINGDALE", "loc": [-74.08293, 44.398477], "pop": 1016, "state": "NY", "_id": "12913"} -{"city": "BOMBAY", "loc": [-74.594737, 44.947861], "pop": 1042, "state": "NY", "_id": "12914"} -{"city": "BRUSHTON", "loc": [-74.522274, 44.828212], "pop": 1936, "state": "NY", "_id": "12916"} -{"city": "BURKE", "loc": [-74.173116, 44.917722], "pop": 1279, "state": "NY", "_id": "12917"} -{"city": "CADYVILLE", "loc": [-73.670242, 44.686473], "pop": 2122, "state": "NY", "_id": "12918"} -{"city": "CHAMPLAIN", "loc": [-73.446603, 44.977292], "pop": 2827, "state": "NY", "_id": "12919"} -{"city": "CHATEAUGAY", "loc": [-74.074098, 44.908768], "pop": 2088, "state": "NY", "_id": "12920"} -{"city": "CHAZY", "loc": [-73.450076, 44.888379], "pop": 2664, "state": "NY", "_id": "12921"} -{"city": "CHILDWOLD", "loc": [-74.675878, 44.286715], "pop": 0, "state": "NY", "_id": "12922"} -{"city": "CHURUBUSCO", "loc": [-73.935484, 44.943232], "pop": 663, "state": "NY", "_id": "12923"} -{"city": "KEESEVILLE", "loc": [-73.567971, 44.504814], "pop": 377, "state": "NY", "_id": "12924"} -{"city": "CONSTABLE", "loc": [-74.329713, 44.941688], "pop": 1949, "state": "NY", "_id": "12926"} -{"city": "CROWN POINT", "loc": [-73.466486, 43.952633], "pop": 1963, "state": "NY", "_id": "12928"} -{"city": "DICKINSON CENTER", "loc": [-74.552346, 44.723328], "pop": 549, "state": "NY", "_id": "12930"} -{"city": "ELIZABETHTOWN", "loc": [-73.601131, 44.224518], "pop": 1274, "state": "NY", "_id": "12932"} -{"city": "ELLENBURG CENTER", "loc": [-73.868546, 44.844353], "pop": 1494, "state": "NY", "_id": "12934"} -{"city": "ELLENBURG DEPOT", "loc": [-73.787572, 44.916266], "pop": 981, "state": "NY", "_id": "12935"} -{"city": "ESSEX", "loc": [-73.373147, 44.280695], "pop": 406, "state": "NY", "_id": "12936"} -{"city": "FORT COVINGTON", "loc": [-74.492879, 44.973096], "pop": 1568, "state": "NY", "_id": "12937"} -{"city": "NICHOLVILLE", "loc": [-74.536471, 44.638703], "pop": 1229, "state": "NY", "_id": "12938"} -{"city": "JAY", "loc": [-73.724702, 44.373351], "pop": 1245, "state": "NY", "_id": "12941"} -{"city": "KEENE", "loc": [-73.791457, 44.25548], "pop": 520, "state": "NY", "_id": "12942"} -{"city": "SAINT HUBERTS", "loc": [-73.795923, 44.177978], "pop": 392, "state": "NY", "_id": "12943"} -{"city": "KEESEVILLE", "loc": [-73.474538, 44.499933], "pop": 4129, "state": "NY", "_id": "12944"} -{"city": "UPPER SAINT REGI", "loc": [-74.243336, 44.359392], "pop": 412, "state": "NY", "_id": "12945"} -{"city": "NORTH POLE", "loc": [-73.986354, 44.274986], "pop": 4656, "state": "NY", "_id": "12946"} -{"city": "LAWRENCEVILLE", "loc": [-74.662927, 44.758988], "pop": 41, "state": "NY", "_id": "12949"} -{"city": "LEWIS", "loc": [-73.549129, 44.307507], "pop": 432, "state": "NY", "_id": "12950"} -{"city": "LYON MOUNTAIN", "loc": [-73.919452, 44.725491], "pop": 623, "state": "NY", "_id": "12952"} -{"city": "MALONE", "loc": [-74.292808, 44.848164], "pop": 13798, "state": "NY", "_id": "12953"} -{"city": "MERRILL", "loc": [-73.977832, 44.799364], "pop": 452, "state": "NY", "_id": "12955"} -{"city": "MINEVILLE", "loc": [-73.523588, 44.087631], "pop": 1868, "state": "NY", "_id": "12956"} -{"city": "MOIRA", "loc": [-74.560273, 44.850412], "pop": 1511, "state": "NY", "_id": "12957"} -{"city": "MOOERS", "loc": [-73.583413, 44.959244], "pop": 1569, "state": "NY", "_id": "12958"} -{"city": "MOOERS FORKS", "loc": [-73.672967, 44.960232], "pop": 963, "state": "NY", "_id": "12959"} -{"city": "MORIAH", "loc": [-73.507862, 44.043755], "pop": 889, "state": "NY", "_id": "12960"} -{"city": "MORIAH CENTER", "loc": [-73.511071, 44.066197], "pop": 209, "state": "NY", "_id": "12961"} -{"city": "MORRISONVILLE", "loc": [-73.577168, 44.68936], "pop": 4665, "state": "NY", "_id": "12962"} -{"city": "NEW RUSSIA", "loc": [-73.605881, 44.159532], "pop": 216, "state": "NY", "_id": "12964"} -{"city": "NICHOLVILLE", "loc": [-74.653379, 44.708182], "pop": 153, "state": "NY", "_id": "12965"} -{"city": "BANGOR", "loc": [-74.413369, 44.829997], "pop": 2867, "state": "NY", "_id": "12966"} -{"city": "NORTH LAWRENCE", "loc": [-74.665307, 44.774982], "pop": 943, "state": "NY", "_id": "12967"} -{"city": "ONCHIOTA", "loc": [-74.170991, 44.458163], "pop": 1407, "state": "NY", "_id": "12968"} -{"city": "OWLS HEAD", "loc": [-74.134173, 44.730791], "pop": 329, "state": "NY", "_id": "12969"} -{"city": "PAUL SMITHS", "loc": [-74.266432, 44.444967], "pop": 245, "state": "NY", "_id": "12970"} -{"city": "PERU", "loc": [-73.529322, 44.585109], "pop": 5640, "state": "NY", "_id": "12972"} -{"city": "PIERCEFIELD", "loc": [-74.573228, 44.234037], "pop": 112, "state": "NY", "_id": "12973"} -{"city": "PORT HENRY", "loc": [-73.470542, 44.04645], "pop": 1841, "state": "NY", "_id": "12974"} -{"city": "REDFORD", "loc": [-73.801948, 44.606926], "pop": 495, "state": "NY", "_id": "12978"} -{"city": "ROUSES POINT", "loc": [-73.369083, 44.988413], "pop": 2508, "state": "NY", "_id": "12979"} -{"city": "SAINT REGIS FALL", "loc": [-74.66032, 44.677298], "pop": 321, "state": "NY", "_id": "12980"} -{"city": "SARANAC", "loc": [-73.748135, 44.703168], "pop": 6545, "state": "NY", "_id": "12981"} -{"city": "SARANAC LAKE", "loc": [-74.132951, 44.324331], "pop": 9125, "state": "NY", "_id": "12983"} -{"city": "SCHUYLER FALLS", "loc": [-73.689481, 44.588224], "pop": 652, "state": "NY", "_id": "12985"} -{"city": "SUNMOUNT", "loc": [-74.463172, 44.228461], "pop": 6379, "state": "NY", "_id": "12986"} -{"city": "UPPER JAY", "loc": [-73.807864, 44.325586], "pop": 159, "state": "NY", "_id": "12987"} -{"city": "VERMONTVILLE", "loc": [-74.057278, 44.460134], "pop": 623, "state": "NY", "_id": "12989"} -{"city": "WEST CHAZY", "loc": [-73.511188, 44.796967], "pop": 3944, "state": "NY", "_id": "12992"} -{"city": "WESTPORT", "loc": [-73.470223, 44.204983], "pop": 2007, "state": "NY", "_id": "12993"} -{"city": "WHALLONSBURG", "loc": [-73.432205, 44.292637], "pop": 169, "state": "NY", "_id": "12994"} -{"city": "WILLSBORO", "loc": [-73.396292, 44.360396], "pop": 1729, "state": "NY", "_id": "12996"} -{"city": "WILMINGTON", "loc": [-73.816553, 44.387976], "pop": 958, "state": "NY", "_id": "12997"} -{"city": "AUBURN", "loc": [-76.562605, 42.929958], "pop": 43447, "state": "NY", "_id": "13021"} -{"city": "AURORA", "loc": [-76.67749, 42.747231], "pop": 1557, "state": "NY", "_id": "13026"} -{"city": "BALDWINSVILLE", "loc": [-76.323718, 43.162039], "pop": 28132, "state": "NY", "_id": "13027"} -{"city": "BERNHARDS BAY", "loc": [-75.937299, 43.271722], "pop": 1228, "state": "NY", "_id": "13028"} -{"city": "BREWERTON", "loc": [-76.135132, 43.225194], "pop": 4823, "state": "NY", "_id": "13029"} -{"city": "BRIDGEPORT", "loc": [-75.970009, 43.159015], "pop": 4507, "state": "NY", "_id": "13030"} -{"city": "CAMILLUS", "loc": [-76.280728, 43.041651], "pop": 15236, "state": "NY", "_id": "13031"} -{"city": "CANASTOTA", "loc": [-75.760197, 43.087764], "pop": 12909, "state": "NY", "_id": "13032"} -{"city": "CATO", "loc": [-76.564791, 43.179443], "pop": 3873, "state": "NY", "_id": "13033"} -{"city": "CAYUGA", "loc": [-76.702402, 42.914198], "pop": 2357, "state": "NY", "_id": "13034"} -{"city": "CAZENOVIA", "loc": [-75.839229, 42.937955], "pop": 7748, "state": "NY", "_id": "13035"} -{"city": "CENTRAL SQUARE", "loc": [-76.184852, 43.308986], "pop": 7720, "state": "NY", "_id": "13036"} -{"city": "CHITTENANGO", "loc": [-75.87684, 43.05524], "pop": 8756, "state": "NY", "_id": "13037"} -{"city": "CICERO", "loc": [-76.096185, 43.170693], "pop": 12600, "state": "NY", "_id": "13039"} -{"city": "CINCINNATUS", "loc": [-75.903029, 42.538539], "pop": 2416, "state": "NY", "_id": "13040"} -{"city": "CLAY", "loc": [-76.170748, 43.173734], "pop": 8609, "state": "NY", "_id": "13041"} -{"city": "CLEVELAND", "loc": [-75.853691, 43.243199], "pop": 2428, "state": "NY", "_id": "13042"} -{"city": "CONSTANTIA", "loc": [-76.004155, 43.272751], "pop": 2603, "state": "NY", "_id": "13044"} -{"city": "CORTLAND", "loc": [-76.185675, 42.595175], "pop": 29180, "state": "NY", "_id": "13045"} -{"city": "CUYLER", "loc": [-75.938367, 42.740571], "pop": 920, "state": "NY", "_id": "13050"} -{"city": "DE RUYTER", "loc": [-75.858226, 42.749444], "pop": 1914, "state": "NY", "_id": "13052"} -{"city": "DRYDEN", "loc": [-76.287224, 42.486118], "pop": 4659, "state": "NY", "_id": "13053"} -{"city": "DURHAMVILLE", "loc": [-75.671409, 43.157912], "pop": 1470, "state": "NY", "_id": "13054"} -{"city": "EAST FREETOWN", "loc": [-75.998924, 42.579845], "pop": 833, "state": "NY", "_id": "13055"} -{"city": "EAST SYRACUSE", "loc": [-76.05578, 43.073359], "pop": 14722, "state": "NY", "_id": "13057"} -{"city": "ELBRIDGE", "loc": [-76.435164, 43.025246], "pop": 1770, "state": "NY", "_id": "13060"} -{"city": "ERIEVILLE", "loc": [-75.754255, 42.856166], "pop": 1100, "state": "NY", "_id": "13061"} -{"city": "FABIUS", "loc": [-75.983645, 42.853117], "pop": 1785, "state": "NY", "_id": "13063"} -{"city": "FAYETTEVILLE", "loc": [-76.014503, 43.026774], "pop": 11793, "state": "NY", "_id": "13066"} -{"city": "FREEVILLE", "loc": [-76.363622, 42.499768], "pop": 5181, "state": "NY", "_id": "13068"} -{"city": "FULTON", "loc": [-76.40342, 43.321108], "pop": 23858, "state": "NY", "_id": "13069"} -{"city": "GENOA", "loc": [-76.541755, 42.674624], "pop": 1160, "state": "NY", "_id": "13071"} -{"city": "GEORGETOWN", "loc": [-75.744279, 42.763059], "pop": 611, "state": "NY", "_id": "13072"} -{"city": "GROTON", "loc": [-76.363286, 42.58549], "pop": 5940, "state": "NY", "_id": "13073"} -{"city": "HANNIBAL", "loc": [-76.546034, 43.311115], "pop": 5248, "state": "NY", "_id": "13074"} -{"city": "HASTINGS", "loc": [-76.147708, 43.35268], "pop": 2259, "state": "NY", "_id": "13076"} -{"city": "HOMER", "loc": [-76.18783, 42.672586], "pop": 7192, "state": "NY", "_id": "13077"} -{"city": "JAMESVILLE", "loc": [-76.076571, 42.982973], "pop": 8230, "state": "NY", "_id": "13078"} -{"city": "JORDAN", "loc": [-76.45978, 43.065141], "pop": 4766, "state": "NY", "_id": "13080"} -{"city": "KING FERRY", "loc": [-76.621603, 42.66351], "pop": 979, "state": "NY", "_id": "13081"} -{"city": "KIRKVILLE", "loc": [-75.955003, 43.098095], "pop": 4798, "state": "NY", "_id": "13082"} -{"city": "LACONA", "loc": [-76.050335, 43.642883], "pop": 2199, "state": "NY", "_id": "13083"} -{"city": "LA FAYETTE", "loc": [-76.106116, 42.890959], "pop": 4450, "state": "NY", "_id": "13084"} -{"city": "LEBANON", "loc": [-75.677585, 42.77451], "pop": 164, "state": "NY", "_id": "13085"} -{"city": "LIVERPOOL", "loc": [-76.186999, 43.109925], "pop": 23093, "state": "NY", "_id": "13088"} -{"city": "BAYBERRY", "loc": [-76.223269, 43.148048], "pop": 31387, "state": "NY", "_id": "13090"} -{"city": "LOCKE", "loc": [-76.415436, 42.655789], "pop": 2746, "state": "NY", "_id": "13092"} -{"city": "MC GRAW", "loc": [-76.081958, 42.594758], "pop": 2511, "state": "NY", "_id": "13101"} -{"city": "MALLORY", "loc": [-76.089208, 43.33782], "pop": 1267, "state": "NY", "_id": "13103"} -{"city": "MANLIUS", "loc": [-75.970345, 42.990441], "pop": 12754, "state": "NY", "_id": "13104"} -{"city": "MARCELLUS", "loc": [-76.33228, 42.982056], "pop": 6077, "state": "NY", "_id": "13108"} -{"city": "MARIETTA", "loc": [-76.28055, 42.897441], "pop": 1778, "state": "NY", "_id": "13110"} -{"city": "MARTVILLE", "loc": [-76.628936, 43.26608], "pop": 1342, "state": "NY", "_id": "13111"} -{"city": "MEMPHIS", "loc": [-76.40301, 43.093438], "pop": 1888, "state": "NY", "_id": "13112"} -{"city": "MEXICO", "loc": [-76.244588, 43.460533], "pop": 6641, "state": "NY", "_id": "13114"} -{"city": "MINOA", "loc": [-76.009812, 43.077212], "pop": 3790, "state": "NY", "_id": "13116"} -{"city": "MORAVIA", "loc": [-76.39898, 42.735456], "pop": 6267, "state": "NY", "_id": "13118"} -{"city": "NEDROW", "loc": [-76.152932, 42.955855], "pop": 2349, "state": "NY", "_id": "13120"} -{"city": "NEW WOODSTOCK", "loc": [-75.863526, 42.844135], "pop": 842, "state": "NY", "_id": "13122"} -{"city": "NORTH PITCHER", "loc": [-75.816359, 42.637243], "pop": 160, "state": "NY", "_id": "13124"} -{"city": "OSWEGO", "loc": [-76.497489, 43.443836], "pop": 38426, "state": "NY", "_id": "13126"} -{"city": "PARISH", "loc": [-76.100023, 43.415295], "pop": 3039, "state": "NY", "_id": "13131"} -{"city": "PENNELLVILLE", "loc": [-76.239466, 43.260946], "pop": 4566, "state": "NY", "_id": "13132"} -{"city": "PHOENIX", "loc": [-76.306449, 43.24679], "pop": 5342, "state": "NY", "_id": "13135"} -{"city": "PITCHER", "loc": [-75.846464, 42.596941], "pop": 678, "state": "NY", "_id": "13136"} -{"city": "PORT BYRON", "loc": [-76.644919, 43.042653], "pop": 4391, "state": "NY", "_id": "13140"} -{"city": "PREBLE", "loc": [-76.214105, 42.79501], "pop": 502, "state": "NY", "_id": "13141"} -{"city": "PULASKI", "loc": [-76.125231, 43.55617], "pop": 7355, "state": "NY", "_id": "13142"} -{"city": "RED CREEK", "loc": [-76.714556, 43.229068], "pop": 3289, "state": "NY", "_id": "13143"} -{"city": "RICHLAND", "loc": [-76.002918, 43.577578], "pop": 1267, "state": "NY", "_id": "13144"} -{"city": "SANDY CREEK", "loc": [-76.126439, 43.651681], "pop": 801, "state": "NY", "_id": "13145"} -{"city": "SAVANNAH", "loc": [-76.75647, 43.093439], "pop": 2141, "state": "NY", "_id": "13146"} -{"city": "VENICE CENTER", "loc": [-76.574175, 42.778472], "pop": 1520, "state": "NY", "_id": "13147"} -{"city": "SENECA FALLS", "loc": [-76.792538, 42.909377], "pop": 10987, "state": "NY", "_id": "13148"} -{"city": "SKANEATELES", "loc": [-76.405174, 42.925751], "pop": 9182, "state": "NY", "_id": "13152"} -{"city": "SOUTH OTSELIC", "loc": [-75.766919, 42.66256], "pop": 789, "state": "NY", "_id": "13155"} -{"city": "STERLING", "loc": [-76.674731, 43.329578], "pop": 2252, "state": "NY", "_id": "13156"} -{"city": "TRUXTON", "loc": [-76.018946, 42.708547], "pop": 593, "state": "NY", "_id": "13158"} -{"city": "TULLY", "loc": [-76.13936, 42.806977], "pop": 4777, "state": "NY", "_id": "13159"} -{"city": "UNION SPRINGS", "loc": [-76.673959, 42.833546], "pop": 1984, "state": "NY", "_id": "13160"} -{"city": "WARNERS", "loc": [-76.290413, 43.09317], "pop": 816, "state": "NY", "_id": "13164"} -{"city": "WATERLOO", "loc": [-76.875498, 42.904515], "pop": 10845, "state": "NY", "_id": "13165"} -{"city": "WEEDSPORT", "loc": [-76.542502, 43.048882], "pop": 5847, "state": "NY", "_id": "13166"} -{"city": "WEST MONROE", "loc": [-76.079747, 43.288235], "pop": 3369, "state": "NY", "_id": "13167"} -{"city": "SYRACUSE", "loc": [-76.148856, 43.040988], "pop": 5442, "state": "NY", "_id": "13202"} -{"city": "SYRACUSE", "loc": [-76.136931, 43.060703], "pop": 17585, "state": "NY", "_id": "13203"} -{"city": "SYRACUSE", "loc": [-76.175767, 43.044398], "pop": 25414, "state": "NY", "_id": "13204"} -{"city": "SYRACUSE", "loc": [-76.14518, 43.012314], "pop": 23048, "state": "NY", "_id": "13205"} -{"city": "SYRACUSE", "loc": [-76.110226, 43.06773], "pop": 17644, "state": "NY", "_id": "13206"} -{"city": "SYRACUSE", "loc": [-76.16501, 43.019482], "pop": 16380, "state": "NY", "_id": "13207"} -{"city": "SYRACUSE", "loc": [-76.148616, 43.073007], "pop": 22242, "state": "NY", "_id": "13208"} -{"city": "SOLVAY", "loc": [-76.238448, 43.078204], "pop": 13467, "state": "NY", "_id": "13209"} -{"city": "SYRACUSE", "loc": [-76.128166, 43.035414], "pop": 31197, "state": "NY", "_id": "13210"} -{"city": "MATTYDALE", "loc": [-76.142181, 43.09951], "pop": 6940, "state": "NY", "_id": "13211"} -{"city": "NORTH SYRACUSE", "loc": [-76.137295, 43.130623], "pop": 22829, "state": "NY", "_id": "13212"} -{"city": "DE WITT", "loc": [-76.07844, 43.042529], "pop": 9501, "state": "NY", "_id": "13214"} -{"city": "ONONDAGA", "loc": [-76.211851, 42.997544], "pop": 12557, "state": "NY", "_id": "13215"} -{"city": "SYRACUSE", "loc": [-76.226159, 43.040943], "pop": 16166, "state": "NY", "_id": "13219"} -{"city": "SYRACUSE", "loc": [-76.104609, 43.042134], "pop": 9845, "state": "NY", "_id": "13224"} -{"city": "ALDER CREEK", "loc": [-75.213748, 43.415659], "pop": 74, "state": "NY", "_id": "13301"} -{"city": "ALTMAR", "loc": [-75.971934, 43.497022], "pop": 1761, "state": "NY", "_id": "13302"} -{"city": "AVA", "loc": [-75.450919, 43.344519], "pop": 1708, "state": "NY", "_id": "13303"} -{"city": "BARNEVELD", "loc": [-75.161156, 43.223697], "pop": 1383, "state": "NY", "_id": "13304"} -{"city": "BLOSSVALE", "loc": [-75.687313, 43.230293], "pop": 4489, "state": "NY", "_id": "13308"} -{"city": "BOONVILLE", "loc": [-75.343973, 43.478615], "pop": 5768, "state": "NY", "_id": "13309"} -{"city": "BOUCKVILLE", "loc": [-75.567841, 42.894024], "pop": 650, "state": "NY", "_id": "13310"} -{"city": "BROOKFIELD", "loc": [-75.343859, 42.807538], "pop": 5, "state": "NY", "_id": "13314"} -{"city": "BURLINGTON FLATS", "loc": [-75.169044, 42.75162], "pop": 1145, "state": "NY", "_id": "13315"} -{"city": "CAMDEN", "loc": [-75.754258, 43.339197], "pop": 6987, "state": "NY", "_id": "13316"} -{"city": "AMES", "loc": [-74.583522, 42.88239], "pop": 3986, "state": "NY", "_id": "13317"} -{"city": "CASSVILLE", "loc": [-75.260704, 42.906931], "pop": 1566, "state": "NY", "_id": "13318"} -{"city": "CHADWICKS", "loc": [-75.265645, 43.022563], "pop": 274, "state": "NY", "_id": "13319"} -{"city": "CHERRY VALLEY", "loc": [-74.744439, 42.782315], "pop": 1642, "state": "NY", "_id": "13320"} -{"city": "CLAYVILLE", "loc": [-75.206126, 42.97119], "pop": 641, "state": "NY", "_id": "13322"} -{"city": "CLINTON", "loc": [-75.38079, 43.05856], "pop": 12483, "state": "NY", "_id": "13323"} -{"city": "COLD BROOK", "loc": [-74.997651, 43.302391], "pop": 1853, "state": "NY", "_id": "13324"} -{"city": "CONSTABLEVILLE", "loc": [-75.458406, 43.562982], "pop": 931, "state": "NY", "_id": "13325"} -{"city": "COOPERSTOWN", "loc": [-74.918148, 42.70319], "pop": 5402, "state": "NY", "_id": "13326"} -{"city": "CROGHAN", "loc": [-75.354192, 43.909461], "pop": 1841, "state": "NY", "_id": "13327"} -{"city": "DEANSBORO", "loc": [-75.438309, 42.981788], "pop": 639, "state": "NY", "_id": "13328"} -{"city": "DOLGEVILLE", "loc": [-74.764305, 43.104161], "pop": 4205, "state": "NY", "_id": "13329"} -{"city": "EAGLE BAY", "loc": [-74.965381, 43.723659], "pop": 1416, "state": "NY", "_id": "13331"} -{"city": "EARLVILLE", "loc": [-75.541653, 42.752218], "pop": 2413, "state": "NY", "_id": "13332"} -{"city": "EAST SPRINGFIELD", "loc": [-74.759741, 42.832947], "pop": 0, "state": "NY", "_id": "13333"} -{"city": "EATON", "loc": [-75.631359, 42.848417], "pop": 1583, "state": "NY", "_id": "13334"} -{"city": "EDMESTON", "loc": [-75.252522, 42.730281], "pop": 788, "state": "NY", "_id": "13335"} -{"city": "FLY CREEK", "loc": [-74.986921, 42.725177], "pop": 1033, "state": "NY", "_id": "13337"} -{"city": "FORESTPORT", "loc": [-75.178742, 43.473651], "pop": 1013, "state": "NY", "_id": "13338"} -{"city": "FORT PLAIN", "loc": [-74.643298, 42.937158], "pop": 6144, "state": "NY", "_id": "13339"} -{"city": "FRANKFORT", "loc": [-75.107155, 43.044041], "pop": 8546, "state": "NY", "_id": "13340"} -{"city": "GARRATTSVILLE", "loc": [-75.232701, 42.635634], "pop": 293, "state": "NY", "_id": "13342"} -{"city": "GLENFIELD", "loc": [-75.366902, 43.732306], "pop": 1909, "state": "NY", "_id": "13343"} -{"city": "GREIG", "loc": [-75.312393, 43.689184], "pop": 456, "state": "NY", "_id": "13345"} -{"city": "HAMILTON", "loc": [-75.543382, 42.82306], "pop": 5821, "state": "NY", "_id": "13346"} -{"city": "HARTWICK", "loc": [-75.055009, 42.695033], "pop": 1424, "state": "NY", "_id": "13348"} -{"city": "HERKIMER", "loc": [-74.98757, 43.030696], "pop": 10090, "state": "NY", "_id": "13350"} -{"city": "HOFFMEISTER", "loc": [-74.739974, 43.391545], "pop": 106, "state": "NY", "_id": "13353"} -{"city": "HOLLAND PATENT", "loc": [-75.253506, 43.248406], "pop": 3970, "state": "NY", "_id": "13354"} -{"city": "HUBBARDSVILLE", "loc": [-75.436707, 42.823663], "pop": 810, "state": "NY", "_id": "13355"} -{"city": "ILION", "loc": [-75.04836, 43.006391], "pop": 11603, "state": "NY", "_id": "13357"} -{"city": "INLET", "loc": [-74.784631, 43.748024], "pop": 343, "state": "NY", "_id": "13360"} -{"city": "JORDANVILLE", "loc": [-74.820919, 42.89478], "pop": 395, "state": "NY", "_id": "13361"} -{"city": "LEE CENTER", "loc": [-75.505532, 43.314804], "pop": 2076, "state": "NY", "_id": "13363"} -{"city": "LITTLE FALLS", "loc": [-74.860598, 43.047371], "pop": 10379, "state": "NY", "_id": "13365"} -{"city": "BEAVER RIVER", "loc": [-75.475378, 43.801055], "pop": 8238, "state": "NY", "_id": "13367"} -{"city": "LYONS FALLS", "loc": [-75.355312, 43.626165], "pop": 1210, "state": "NY", "_id": "13368"} -{"city": "MC CONNELLSVILLE", "loc": [-75.652477, 43.289552], "pop": 412, "state": "NY", "_id": "13401"} -{"city": "MADISON", "loc": [-75.507562, 42.896854], "pop": 1472, "state": "NY", "_id": "13402"} -{"city": "MARCY", "loc": [-75.278335, 43.163926], "pop": 7937, "state": "NY", "_id": "13403"} -{"city": "MIDDLEVILLE", "loc": [-74.923978, 43.136933], "pop": 180, "state": "NY", "_id": "13406"} -{"city": "MOHAWK", "loc": [-74.985298, 42.989986], "pop": 5907, "state": "NY", "_id": "13407"} -{"city": "MORRISVILLE", "loc": [-75.648656, 42.910805], "pop": 4491, "state": "NY", "_id": "13408"} -{"city": "MUNNSVILLE", "loc": [-75.594032, 42.986326], "pop": 2116, "state": "NY", "_id": "13409"} -{"city": "NEW BERLIN", "loc": [-75.347406, 42.622414], "pop": 2569, "state": "NY", "_id": "13411"} -{"city": "NEW HARTFORD", "loc": [-75.290551, 43.065412], "pop": 14035, "state": "NY", "_id": "13413"} -{"city": "NEW LISBON", "loc": [-75.327436, 42.578968], "pop": 19, "state": "NY", "_id": "13415"} -{"city": "NEWPORT", "loc": [-74.986133, 43.180002], "pop": 2554, "state": "NY", "_id": "13416"} -{"city": "NEW YORK MILLS", "loc": [-75.29369, 43.100038], "pop": 3646, "state": "NY", "_id": "13417"} -{"city": "NORTH BROOKFIELD", "loc": [-75.381489, 42.850088], "pop": 258, "state": "NY", "_id": "13418"} -{"city": "OLD FORGE", "loc": [-74.893511, 43.74347], "pop": 221, "state": "NY", "_id": "13420"} -{"city": "ONEIDA", "loc": [-75.650814, 43.086248], "pop": 11876, "state": "NY", "_id": "13421"} -{"city": "ORISKANY", "loc": [-75.343437, 43.152427], "pop": 3566, "state": "NY", "_id": "13424"} -{"city": "ORISKANY FALLS", "loc": [-75.483807, 42.957585], "pop": 2192, "state": "NY", "_id": "13425"} -{"city": "PALATINE BRIDGE", "loc": [-74.570825, 42.922119], "pop": 1738, "state": "NY", "_id": "13428"} -{"city": "POLAND", "loc": [-75.07313, 43.211458], "pop": 1789, "state": "NY", "_id": "13431"} -{"city": "PORT LEYDEN", "loc": [-75.326257, 43.580245], "pop": 1951, "state": "NY", "_id": "13433"} -{"city": "RAQUETTE LAKE", "loc": [-74.537959, 43.866224], "pop": 0, "state": "NY", "_id": "13436"} -{"city": "REDFIELD", "loc": [-75.82423, 43.565794], "pop": 482, "state": "NY", "_id": "13437"} -{"city": "REMSEN", "loc": [-75.161618, 43.338456], "pop": 4400, "state": "NY", "_id": "13438"} -{"city": "RICHFIELD SPRING", "loc": [-74.992214, 42.850298], "pop": 3825, "state": "NY", "_id": "13439"} -{"city": "ROME", "loc": [-75.449758, 43.219349], "pop": 56424, "state": "NY", "_id": "13440"} -{"city": "ROSEBOOM", "loc": [-74.74161, 42.788246], "pop": 8, "state": "NY", "_id": "13450"} -{"city": "SAINT JOHNSVILLE", "loc": [-74.64604, 43.016995], "pop": 4992, "state": "NY", "_id": "13452"} -{"city": "SALISBURY CENTER", "loc": [-74.780932, 43.162509], "pop": 896, "state": "NY", "_id": "13454"} -{"city": "SAUQUOIT", "loc": [-75.26259, 43.007291], "pop": 5652, "state": "NY", "_id": "13456"} -{"city": "SHARON SPRINGS", "loc": [-74.591911, 42.763357], "pop": 2919, "state": "NY", "_id": "13459"} -{"city": "SHERBURNE", "loc": [-75.483027, 42.685885], "pop": 4013, "state": "NY", "_id": "13460"} -{"city": "SHERRILL", "loc": [-75.598975, 43.070397], "pop": 2864, "state": "NY", "_id": "13461"} -{"city": "SMYRNA", "loc": [-75.612124, 42.689565], "pop": 1209, "state": "NY", "_id": "13464"} -{"city": "SOUTH EDMESTON", "loc": [-75.262919, 42.676754], "pop": 1224, "state": "NY", "_id": "13466"} -{"city": "SPRINGFIELD CENT", "loc": [-74.938051, 42.850106], "pop": 1402, "state": "NY", "_id": "13468"} -{"city": "STITTVILLE", "loc": [-75.289854, 43.222889], "pop": 567, "state": "NY", "_id": "13469"} -{"city": "STRATFORD", "loc": [-74.676786, 43.179101], "pop": 773, "state": "NY", "_id": "13470"} -{"city": "TABERG", "loc": [-75.602706, 43.336571], "pop": 2393, "state": "NY", "_id": "13471"} -{"city": "TURIN", "loc": [-75.413199, 43.644074], "pop": 1004, "state": "NY", "_id": "13473"} -{"city": "VAN HORNESVILLE", "loc": [-74.834662, 42.894812], "pop": 40, "state": "NY", "_id": "13475"} -{"city": "VERNON", "loc": [-75.56272, 43.094509], "pop": 4118, "state": "NY", "_id": "13476"} -{"city": "VERNON CENTER", "loc": [-75.521028, 43.044309], "pop": 1440, "state": "NY", "_id": "13477"} -{"city": "VERONA", "loc": [-75.572399, 43.147311], "pop": 2325, "state": "NY", "_id": "13478"} -{"city": "WATERVILLE", "loc": [-75.381466, 42.933244], "pop": 4059, "state": "NY", "_id": "13480"} -{"city": "WEST BURLINGTON", "loc": [-75.190603, 42.706229], "pop": 22, "state": "NY", "_id": "13482"} -{"city": "WESTDALE", "loc": [-75.822587, 43.411671], "pop": 161, "state": "NY", "_id": "13483"} -{"city": "WEST EDMESTON", "loc": [-75.303723, 42.794543], "pop": 1337, "state": "NY", "_id": "13485"} -{"city": "WESTERNVILLE", "loc": [-75.315118, 43.329413], "pop": 657, "state": "NY", "_id": "13486"} -{"city": "WESTFORD", "loc": [-75.100378, 42.503512], "pop": 253, "state": "NY", "_id": "13488"} -{"city": "WEST LEYDEN", "loc": [-75.512707, 43.459713], "pop": 500, "state": "NY", "_id": "13489"} -{"city": "WESTMORELAND", "loc": [-75.453259, 43.101686], "pop": 904, "state": "NY", "_id": "13490"} -{"city": "WEST WINFIELD", "loc": [-75.183491, 42.882566], "pop": 3825, "state": "NY", "_id": "13491"} -{"city": "WHITESBORO", "loc": [-75.309479, 43.115805], "pop": 10411, "state": "NY", "_id": "13492"} -{"city": "WILLIAMSTOWN", "loc": [-75.904411, 43.410559], "pop": 1798, "state": "NY", "_id": "13493"} -{"city": "WOODGATE", "loc": [-75.119111, 43.54881], "pop": 169, "state": "NY", "_id": "13494"} -{"city": "YORKVILLE", "loc": [-75.275565, 43.111582], "pop": 2482, "state": "NY", "_id": "13495"} -{"city": "UTICA", "loc": [-75.231463, 43.087112], "pop": 40223, "state": "NY", "_id": "13501"} -{"city": "UTICA", "loc": [-75.231383, 43.106723], "pop": 36632, "state": "NY", "_id": "13502"} -{"city": "WATERTOWN", "loc": [-75.912212, 43.974258], "pop": 39515, "state": "NY", "_id": "13601"} -{"city": "FORT DRUM", "loc": [-75.753972, 44.035434], "pop": 11895, "state": "NY", "_id": "13602"} -{"city": "FORT DRUM", "loc": [-75.791884, 44.072122], "pop": 30, "state": "NY", "_id": "13603"} -{"city": "SMITHVILLE", "loc": [-76.054302, 43.81614], "pop": 4489, "state": "NY", "_id": "13605"} -{"city": "ADAMS CENTER", "loc": [-76.00415, 43.863106], "pop": 2649, "state": "NY", "_id": "13606"} -{"city": "POINT VIVIAN", "loc": [-75.930619, 44.326982], "pop": 2024, "state": "NY", "_id": "13607"} -{"city": "ANTWERP", "loc": [-75.600484, 44.235775], "pop": 1830, "state": "NY", "_id": "13608"} -{"city": "BELLEVILLE", "loc": [-76.115053, 43.785373], "pop": 72, "state": "NY", "_id": "13611"} -{"city": "BLACK RIVER", "loc": [-75.795777, 44.004156], "pop": 3651, "state": "NY", "_id": "13612"} -{"city": "BRASHER FALLS", "loc": [-74.747303, 44.846718], "pop": 1950, "state": "NY", "_id": "13613"} -{"city": "BRIER HILL", "loc": [-75.672203, 44.552542], "pop": 481, "state": "NY", "_id": "13614"} -{"city": "CALCIUM", "loc": [-75.849884, 44.026484], "pop": 1839, "state": "NY", "_id": "13616"} -{"city": "CANTON", "loc": [-75.162792, 44.592442], "pop": 11781, "state": "NY", "_id": "13617"} -{"city": "CAPE VINCENT", "loc": [-76.316443, 44.124419], "pop": 1207, "state": "NY", "_id": "13618"} -{"city": "CARTHAGE", "loc": [-75.616008, 43.981039], "pop": 11741, "state": "NY", "_id": "13619"} -{"city": "CASTORLAND", "loc": [-75.460432, 43.88432], "pop": 2335, "state": "NY", "_id": "13620"} -{"city": "CHASE MILLS", "loc": [-75.073002, 44.867246], "pop": 431, "state": "NY", "_id": "13621"} -{"city": "CHAUMONT", "loc": [-76.123163, 44.08481], "pop": 2329, "state": "NY", "_id": "13622"} -{"city": "FRONTENAC", "loc": [-76.107056, 44.217016], "pop": 5480, "state": "NY", "_id": "13624"} -{"city": "COLTON", "loc": [-74.932672, 44.50156], "pop": 2382, "state": "NY", "_id": "13625"} -{"city": "COPENHAGEN", "loc": [-75.683913, 43.880136], "pop": 1891, "state": "NY", "_id": "13626"} -{"city": "DE KALB JUNCTION", "loc": [-75.287088, 44.489551], "pop": 1320, "state": "NY", "_id": "13630"} -{"city": "DE PEYSTER", "loc": [-75.432814, 44.541341], "pop": 95, "state": "NY", "_id": "13633"} -{"city": "DEXTER", "loc": [-76.06499, 44.006923], "pop": 3753, "state": "NY", "_id": "13634"} -{"city": "EDWARDS", "loc": [-75.252226, 44.311048], "pop": 1069, "state": "NY", "_id": "13635"} -{"city": "ELLISBURG", "loc": [-76.125871, 43.737202], "pop": 372, "state": "NY", "_id": "13636"} -{"city": "EVANS MILLS", "loc": [-75.830516, 44.081668], "pop": 3041, "state": "NY", "_id": "13637"} -{"city": "FELTS MILLS", "loc": [-75.752443, 44.020374], "pop": 384, "state": "NY", "_id": "13638"} -{"city": "GOUVERNEUR", "loc": [-75.465061, 44.328302], "pop": 8425, "state": "NY", "_id": "13642"} -{"city": "HAMMOND", "loc": [-75.672749, 44.450155], "pop": 2287, "state": "NY", "_id": "13646"} -{"city": "HARRISVILLE", "loc": [-75.325151, 44.161294], "pop": 2395, "state": "NY", "_id": "13648"} -{"city": "HENDERSON", "loc": [-76.235212, 43.846742], "pop": 300, "state": "NY", "_id": "13650"} -{"city": "HERMON", "loc": [-75.198664, 44.444845], "pop": 1636, "state": "NY", "_id": "13652"} -{"city": "HEUVELTON", "loc": [-75.420345, 44.593034], "pop": 2487, "state": "NY", "_id": "13654"} -{"city": "HOGANSBURG", "loc": [-74.662649, 44.982511], "pop": 1774, "state": "NY", "_id": "13655"} -{"city": "LA FARGEVILLE", "loc": [-75.956902, 44.198709], "pop": 2443, "state": "NY", "_id": "13656"} -{"city": "LISBON", "loc": [-75.269364, 44.718424], "pop": 1265, "state": "NY", "_id": "13658"} -{"city": "LORRAINE", "loc": [-75.905339, 43.756845], "pop": 462, "state": "NY", "_id": "13659"} -{"city": "MADRID", "loc": [-75.14134, 44.768978], "pop": 1840, "state": "NY", "_id": "13660"} -{"city": "MANNSVILLE", "loc": [-76.08203, 43.717905], "pop": 1750, "state": "NY", "_id": "13661"} -{"city": "MASSENA", "loc": [-74.886205, 44.932411], "pop": 16145, "state": "NY", "_id": "13662"} -{"city": "NATURAL BRIDGE", "loc": [-75.503883, 44.062982], "pop": 954, "state": "NY", "_id": "13665"} -{"city": "NEWTON FALLS", "loc": [-74.98456, 44.198584], "pop": 351, "state": "NY", "_id": "13666"} -{"city": "NORFOLK", "loc": [-74.957736, 44.84235], "pop": 4313, "state": "NY", "_id": "13667"} -{"city": "NORWOOD", "loc": [-74.999188, 44.747193], "pop": 4305, "state": "NY", "_id": "13668"} -{"city": "OGDENSBURG", "loc": [-75.477403, 44.690203], "pop": 19659, "state": "NY", "_id": "13669"} -{"city": "OSWEGATCHIE", "loc": [-75.065947, 44.193328], "pop": 287, "state": "NY", "_id": "13670"} -{"city": "PARISHVILLE", "loc": [-74.794062, 44.592655], "pop": 300, "state": "NY", "_id": "13672"} -{"city": "PHILADELPHIA", "loc": [-75.709917, 44.158896], "pop": 2393, "state": "NY", "_id": "13673"} -{"city": "PLESSIS", "loc": [-75.849577, 44.277364], "pop": 111, "state": "NY", "_id": "13675"} -{"city": "POTSDAM", "loc": [-74.968076, 44.659246], "pop": 16342, "state": "NY", "_id": "13676"} -{"city": "REDWOOD", "loc": [-75.814975, 44.321077], "pop": 1735, "state": "NY", "_id": "13679"} -{"city": "RENSSELAER FALLS", "loc": [-75.32261, 44.590635], "pop": 1027, "state": "NY", "_id": "13680"} -{"city": "RICHVILLE", "loc": [-75.402455, 44.40454], "pop": 1502, "state": "NY", "_id": "13681"} -{"city": "RODMAN", "loc": [-75.871879, 43.862217], "pop": 1097, "state": "NY", "_id": "13682"} -{"city": "DEGRASSE", "loc": [-75.113778, 44.380668], "pop": 717, "state": "NY", "_id": "13684"} -{"city": "SACKETS HARBOR", "loc": [-76.105039, 43.93983], "pop": 1943, "state": "NY", "_id": "13685"} -{"city": "SOUTH COLTON", "loc": [-74.860726, 44.504097], "pop": 169, "state": "NY", "_id": "13687"} -{"city": "STAR LAKE", "loc": [-75.033015, 44.157762], "pop": 1108, "state": "NY", "_id": "13690"} -{"city": "THERESA", "loc": [-75.801419, 44.211288], "pop": 2558, "state": "NY", "_id": "13691"} -{"city": "THREE MILE BAY", "loc": [-76.26893, 44.055102], "pop": 250, "state": "NY", "_id": "13693"} -{"city": "WADDINGTON", "loc": [-75.204887, 44.856373], "pop": 1380, "state": "NY", "_id": "13694"} -{"city": "WANAKENA", "loc": [-75.090765, 44.2184], "pop": 705, "state": "NY", "_id": "13695"} -{"city": "WEST STOCKHOLM", "loc": [-74.891932, 44.728951], "pop": 301, "state": "NY", "_id": "13696"} -{"city": "WINTHROP", "loc": [-74.806586, 44.758289], "pop": 2805, "state": "NY", "_id": "13697"} -{"city": "WOODVILLE", "loc": [-76.172002, 43.781245], "pop": 847, "state": "NY", "_id": "13698"} -{"city": "AFTON", "loc": [-75.536604, 42.241737], "pop": 2801, "state": "NY", "_id": "13730"} -{"city": "ANDES", "loc": [-74.788726, 42.156925], "pop": 985, "state": "NY", "_id": "13731"} -{"city": "APALACHIN", "loc": [-76.15194, 42.055579], "pop": 8712, "state": "NY", "_id": "13732"} -{"city": "BAINBRIDGE", "loc": [-75.489411, 42.311975], "pop": 5073, "state": "NY", "_id": "13733"} -{"city": "BARTON", "loc": [-76.398349, 42.069534], "pop": 2081, "state": "NY", "_id": "13734"} -{"city": "BERKSHIRE", "loc": [-76.192008, 42.307435], "pop": 2652, "state": "NY", "_id": "13736"} -{"city": "BLOOMVILLE", "loc": [-74.807118, 42.352236], "pop": 1022, "state": "NY", "_id": "13739"} -{"city": "BOVINA CENTER", "loc": [-74.766112, 42.27094], "pop": 492, "state": "NY", "_id": "13740"} -{"city": "CANDOR", "loc": [-76.332196, 42.206274], "pop": 4850, "state": "NY", "_id": "13743"} -{"city": "CASTLE CREEK", "loc": [-75.908746, 42.256805], "pop": 581, "state": "NY", "_id": "13744"} -{"city": "CHENANGO FORKS", "loc": [-75.824236, 42.263659], "pop": 3833, "state": "NY", "_id": "13746"} -{"city": "CONKLIN", "loc": [-75.807624, 42.045429], "pop": 4058, "state": "NY", "_id": "13748"} -{"city": "DAVENPORT", "loc": [-74.835709, 42.471081], "pop": 777, "state": "NY", "_id": "13750"} -{"city": "DAVENPORT CENTER", "loc": [-74.906975, 42.452303], "pop": 62, "state": "NY", "_id": "13751"} -{"city": "DE LANCEY", "loc": [-74.922899, 42.188984], "pop": 785, "state": "NY", "_id": "13752"} -{"city": "MEREDITH", "loc": [-74.922823, 42.287851], "pop": 5399, "state": "NY", "_id": "13753"} -{"city": "DEPOSIT", "loc": [-75.428745, 42.066581], "pop": 3047, "state": "NY", "_id": "13754"} -{"city": "DOWNSVILLE", "loc": [-75.015216, 42.071634], "pop": 615, "state": "NY", "_id": "13755"} -{"city": "EAST BRANCH", "loc": [-75.125189, 41.984366], "pop": 839, "state": "NY", "_id": "13756"} -{"city": "EAST MEREDITH", "loc": [-74.898699, 42.410098], "pop": 1478, "state": "NY", "_id": "13757"} -{"city": "ENDWELL", "loc": [-76.056927, 42.114089], "pop": 46236, "state": "NY", "_id": "13760"} -{"city": "FRANKLIN", "loc": [-75.148475, 42.342117], "pop": 1807, "state": "NY", "_id": "13775"} -{"city": "GILBERTSVILLE", "loc": [-75.360929, 42.432951], "pop": 10, "state": "NY", "_id": "13776"} -{"city": "GLEN AUBREY", "loc": [-75.980544, 42.257289], "pop": 1380, "state": "NY", "_id": "13777"} -{"city": "GREENE", "loc": [-75.75954, 42.337301], "pop": 5025, "state": "NY", "_id": "13778"} -{"city": "GUILFORD", "loc": [-75.482318, 42.426901], "pop": 738, "state": "NY", "_id": "13780"} -{"city": "HAMDEN", "loc": [-74.998391, 42.17873], "pop": 928, "state": "NY", "_id": "13782"} -{"city": "CADOSIA", "loc": [-75.265306, 41.959667], "pop": 2766, "state": "NY", "_id": "13783"} -{"city": "HARPERSFIELD", "loc": [-74.687059, 42.449901], "pop": 309, "state": "NY", "_id": "13786"} -{"city": "HARPURSVILLE", "loc": [-75.654538, 42.18232], "pop": 4349, "state": "NY", "_id": "13787"} -{"city": "HOBART", "loc": [-74.675889, 42.359409], "pop": 975, "state": "NY", "_id": "13788"} -{"city": "JOHNSON CITY", "loc": [-75.968492, 42.126683], "pop": 20788, "state": "NY", "_id": "13790"} -{"city": "KIRKWOOD", "loc": [-75.796711, 42.069463], "pop": 4133, "state": "NY", "_id": "13795"} -{"city": "LAURENS", "loc": [-75.127947, 42.538261], "pop": 1703, "state": "NY", "_id": "13796"} -{"city": "LISLE", "loc": [-76.030237, 42.340923], "pop": 2331, "state": "NY", "_id": "13797"} -{"city": "MC DONOUGH", "loc": [-75.762305, 42.506807], "pop": 881, "state": "NY", "_id": "13801"} -{"city": "MAINE", "loc": [-76.059526, 42.196204], "pop": 254, "state": "NY", "_id": "13802"} -{"city": "MARATHON", "loc": [-76.039526, 42.452725], "pop": 3586, "state": "NY", "_id": "13803"} -{"city": "MASONVILLE", "loc": [-75.215485, 42.262346], "pop": 15, "state": "NY", "_id": "13804"} -{"city": "MERIDALE", "loc": [-74.980258, 42.377987], "pop": 16, "state": "NY", "_id": "13806"} -{"city": "MILFORD", "loc": [-74.968498, 42.614801], "pop": 1409, "state": "NY", "_id": "13807"} -{"city": "MORRIS", "loc": [-75.244764, 42.547807], "pop": 1359, "state": "NY", "_id": "13808"} -{"city": "MOUNT UPTON", "loc": [-75.400268, 42.408064], "pop": 1462, "state": "NY", "_id": "13809"} -{"city": "MOUNT VISION", "loc": [-75.126366, 42.606763], "pop": 1261, "state": "NY", "_id": "13810"} -{"city": "NEWARK VALLEY", "loc": [-76.16248, 42.228136], "pop": 4968, "state": "NY", "_id": "13811"} -{"city": "NICHOLS", "loc": [-76.361039, 42.031961], "pop": 2888, "state": "NY", "_id": "13812"} -{"city": "NINEVEH", "loc": [-75.548409, 42.162481], "pop": 975, "state": "NY", "_id": "13813"} -{"city": "NORWICH", "loc": [-75.527431, 42.54145], "pop": 15380, "state": "NY", "_id": "13815"} -{"city": "ONEONTA", "loc": [-75.049072, 42.462453], "pop": 18196, "state": "NY", "_id": "13820"} -{"city": "OTEGO", "loc": [-75.207883, 42.41333], "pop": 5075, "state": "NY", "_id": "13825"} -{"city": "OUAQUAGA", "loc": [-75.647351, 42.119293], "pop": 75, "state": "NY", "_id": "13826"} -{"city": "OWEGO", "loc": [-76.252797, 42.113809], "pop": 11378, "state": "NY", "_id": "13827"} -{"city": "BRISBEN", "loc": [-75.606256, 42.443375], "pop": 5086, "state": "NY", "_id": "13830"} -{"city": "PLYMOUTH", "loc": [-75.617163, 42.633592], "pop": 445, "state": "NY", "_id": "13832"} -{"city": "SANITARIA SPRING", "loc": [-75.790978, 42.195735], "pop": 4777, "state": "NY", "_id": "13833"} -{"city": "PORTLANDVILLE", "loc": [-74.976059, 42.510303], "pop": 916, "state": "NY", "_id": "13834"} -{"city": "RICHFORD", "loc": [-76.186501, 42.394521], "pop": 1383, "state": "NY", "_id": "13835"} -{"city": "SIDNEY", "loc": [-75.3908, 42.307368], "pop": 4946, "state": "NY", "_id": "13838"} -{"city": "SIDNEY CENTER", "loc": [-75.287057, 42.244085], "pop": 2020, "state": "NY", "_id": "13839"} -{"city": "SMITHVILLE FLATS", "loc": [-75.823719, 42.398894], "pop": 484, "state": "NY", "_id": "13841"} -{"city": "SOUTH KORTRIGHT", "loc": [-74.725901, 42.376994], "pop": 253, "state": "NY", "_id": "13842"} -{"city": "SOUTH NEW BERLIN", "loc": [-75.35249, 42.530569], "pop": 2702, "state": "NY", "_id": "13843"} -{"city": "SOUTH PLYMOUTH", "loc": [-75.633, 42.605254], "pop": 510, "state": "NY", "_id": "13844"} -{"city": "TREADWELL", "loc": [-75.058754, 42.362963], "pop": 60, "state": "NY", "_id": "13846"} -{"city": "UNADILLA", "loc": [-75.336589, 42.325199], "pop": 4027, "state": "NY", "_id": "13849"} -{"city": "VESTAL", "loc": [-76.011757, 42.077106], "pop": 27686, "state": "NY", "_id": "13850"} -{"city": "WALTON", "loc": [-75.153177, 42.175647], "pop": 7282, "state": "NY", "_id": "13856"} -{"city": "WELLS BRIDGE", "loc": [-75.256565, 42.357973], "pop": 232, "state": "NY", "_id": "13859"} -{"city": "WEST ONEONTA", "loc": [-75.095689, 42.453854], "pop": 3471, "state": "NY", "_id": "13861"} -{"city": "WHITNEY POINT", "loc": [-75.952231, 42.338449], "pop": 4105, "state": "NY", "_id": "13862"} -{"city": "WILLET", "loc": [-75.901434, 42.452044], "pop": 188, "state": "NY", "_id": "13863"} -{"city": "WILLSEYVILLE", "loc": [-76.389721, 42.302915], "pop": 697, "state": "NY", "_id": "13864"} -{"city": "WINDSOR", "loc": [-75.672828, 42.074482], "pop": 6820, "state": "NY", "_id": "13865"} -{"city": "BINGHAMTON", "loc": [-75.886517, 42.146307], "pop": 21666, "state": "NY", "_id": "13901"} -{"city": "BINGHAMTON", "loc": [-75.897676, 42.081102], "pop": 20353, "state": "NY", "_id": "13903"} -{"city": "BINGHAMTON", "loc": [-75.865269, 42.11714], "pop": 10257, "state": "NY", "_id": "13904"} -{"city": "BINGHAMTON", "loc": [-75.930865, 42.115051], "pop": 30741, "state": "NY", "_id": "13905"} -{"city": "AKRON", "loc": [-78.508365, 43.024944], "pop": 7924, "state": "NY", "_id": "14001"} -{"city": "ALABAMA", "loc": [-78.385231, 43.071888], "pop": 68, "state": "NY", "_id": "14003"} -{"city": "ALDEN", "loc": [-78.525707, 42.89839], "pop": 12711, "state": "NY", "_id": "14004"} -{"city": "ALEXANDER", "loc": [-78.25889, 42.915851], "pop": 1692, "state": "NY", "_id": "14005"} -{"city": "ANGOLA", "loc": [-79.049651, 42.636581], "pop": 11509, "state": "NY", "_id": "14006"} -{"city": "APPLETON", "loc": [-78.637217, 43.310535], "pop": 940, "state": "NY", "_id": "14008"} -{"city": "ARCADE", "loc": [-78.413418, 42.562995], "pop": 4898, "state": "NY", "_id": "14009"} -{"city": "ATTICA", "loc": [-78.279826, 42.849918], "pop": 9372, "state": "NY", "_id": "14011"} -{"city": "BARKER", "loc": [-78.542004, 43.336779], "pop": 2441, "state": "NY", "_id": "14012"} -{"city": "BASOM", "loc": [-78.395143, 43.080724], "pop": 2062, "state": "NY", "_id": "14013"} -{"city": "BATAVIA", "loc": [-78.192869, 43.000316], "pop": 22835, "state": "NY", "_id": "14020"} -{"city": "BLISS", "loc": [-78.258084, 42.579936], "pop": 1498, "state": "NY", "_id": "14024"} -{"city": "BOSTON", "loc": [-78.73909, 42.631384], "pop": 2617, "state": "NY", "_id": "14025"} -{"city": "BOWMANSVILLE", "loc": [-78.682961, 42.947826], "pop": 285, "state": "NY", "_id": "14026"} -{"city": "BURT", "loc": [-78.714097, 43.322089], "pop": 2363, "state": "NY", "_id": "14028"} -{"city": "CHAFFEE", "loc": [-78.502543, 42.560492], "pop": 1481, "state": "NY", "_id": "14030"} -{"city": "CLARENCE", "loc": [-78.616228, 42.980965], "pop": 7712, "state": "NY", "_id": "14031"} -{"city": "CLARENCE CENTER", "loc": [-78.63903, 43.0362], "pop": 3996, "state": "NY", "_id": "14032"} -{"city": "COLDEN", "loc": [-78.692078, 42.655052], "pop": 2380, "state": "NY", "_id": "14033"} -{"city": "COLLINS", "loc": [-78.892974, 42.500082], "pop": 2194, "state": "NY", "_id": "14034"} -{"city": "CORFU", "loc": [-78.392906, 42.977734], "pop": 4841, "state": "NY", "_id": "14036"} -{"city": "COWLESVILLE", "loc": [-78.448136, 42.811245], "pop": 1017, "state": "NY", "_id": "14037"} -{"city": "DALE", "loc": [-78.174865, 42.826284], "pop": 72, "state": "NY", "_id": "14039"} -{"city": "DARIEN CENTER", "loc": [-78.387782, 42.894806], "pop": 1817, "state": "NY", "_id": "14040"} -{"city": "DAYTON", "loc": [-78.981842, 42.423075], "pop": 246, "state": "NY", "_id": "14041"} -{"city": "DELEVAN", "loc": [-78.479326, 42.492598], "pop": 4516, "state": "NY", "_id": "14042"} -{"city": "DEPEW", "loc": [-78.704052, 42.904973], "pop": 26460, "state": "NY", "_id": "14043"} -{"city": "DERBY", "loc": [-78.983365, 42.697445], "pop": 6436, "state": "NY", "_id": "14047"} -{"city": "VAN BUREN BAY", "loc": [-79.329366, 42.475907], "pop": 20303, "state": "NY", "_id": "14048"} -{"city": "SWORMVILLE", "loc": [-78.705035, 43.029168], "pop": 12714, "state": "NY", "_id": "14051"} -{"city": "EAST AURORA", "loc": [-78.601992, 42.770138], "pop": 17244, "state": "NY", "_id": "14052"} -{"city": "EAST BETHANY", "loc": [-78.134206, 42.916619], "pop": 1569, "state": "NY", "_id": "14054"} -{"city": "EAST CONCORD", "loc": [-78.610972, 42.546585], "pop": 1216, "state": "NY", "_id": "14055"} -{"city": "EDEN", "loc": [-78.878077, 42.650552], "pop": 7427, "state": "NY", "_id": "14057"} -{"city": "ELBA", "loc": [-78.170383, 43.089739], "pop": 2331, "state": "NY", "_id": "14058"} -{"city": "ELMA", "loc": [-78.634257, 42.834002], "pop": 8130, "state": "NY", "_id": "14059"} -{"city": "FARMERSVILLE STA", "loc": [-78.291533, 42.44827], "pop": 395, "state": "NY", "_id": "14060"} -{"city": "FORESTVILLE", "loc": [-79.160743, 42.448229], "pop": 2858, "state": "NY", "_id": "14062"} -{"city": "FREDONIA", "loc": [-79.333914, 42.433345], "pop": 10738, "state": "NY", "_id": "14063"} -{"city": "FREEDOM", "loc": [-78.35013, 42.489693], "pop": 2001, "state": "NY", "_id": "14065"} -{"city": "GAINESVILLE", "loc": [-78.179516, 42.61897], "pop": 1536, "state": "NY", "_id": "14066"} -{"city": "GASPORT", "loc": [-78.574536, 43.210587], "pop": 6010, "state": "NY", "_id": "14067"} -{"city": "GETZVILLE", "loc": [-78.753184, 43.023989], "pop": 5685, "state": "NY", "_id": "14068"} -{"city": "GLENWOOD", "loc": [-78.638634, 42.600094], "pop": 632, "state": "NY", "_id": "14069"} -{"city": "GOWANDA", "loc": [-78.933902, 42.471202], "pop": 6665, "state": "NY", "_id": "14070"} -{"city": "GRAND ISLAND", "loc": [-78.959059, 43.018266], "pop": 17561, "state": "NY", "_id": "14072"} -{"city": "HAMBURG", "loc": [-78.838853, 42.733404], "pop": 38851, "state": "NY", "_id": "14075"} -{"city": "HOLLAND", "loc": [-78.543889, 42.639582], "pop": 4004, "state": "NY", "_id": "14080"} -{"city": "IRVING", "loc": [-79.059634, 42.573866], "pop": 2993, "state": "NY", "_id": "14081"} -{"city": "JAVA CENTER", "loc": [-78.392527, 42.66342], "pop": 613, "state": "NY", "_id": "14082"} -{"city": "JAVA VILLAGE", "loc": [-78.44101, 42.676852], "pop": 35, "state": "NY", "_id": "14083"} -{"city": "LAKE VIEW", "loc": [-78.932693, 42.721535], "pop": 4936, "state": "NY", "_id": "14085"} -{"city": "LANCASTER", "loc": [-78.663085, 42.901681], "pop": 24540, "state": "NY", "_id": "14086"} -{"city": "LAWTONS", "loc": [-78.921222, 42.540364], "pop": 1510, "state": "NY", "_id": "14091"} -{"city": "LEWISTON", "loc": [-79.021547, 43.172165], "pop": 10540, "state": "NY", "_id": "14092"} -{"city": "LOCKPORT", "loc": [-78.692344, 43.159987], "pop": 48398, "state": "NY", "_id": "14094"} -{"city": "LYNDONVILLE", "loc": [-78.381057, 43.323312], "pop": 3109, "state": "NY", "_id": "14098"} -{"city": "MACHIAS", "loc": [-78.50586, 42.408271], "pop": 1860, "state": "NY", "_id": "14101"} -{"city": "MARILLA", "loc": [-78.558656, 42.833216], "pop": 1063, "state": "NY", "_id": "14102"} -{"city": "MEDINA", "loc": [-78.387422, 43.218428], "pop": 12042, "state": "NY", "_id": "14103"} -{"city": "MIDDLEPORT", "loc": [-78.484055, 43.218257], "pop": 3929, "state": "NY", "_id": "14105"} -{"city": "NEWFANE", "loc": [-78.706972, 43.272443], "pop": 5261, "state": "NY", "_id": "14108"} -{"city": "NORTH COLLINS", "loc": [-78.91073, 42.589648], "pop": 2429, "state": "NY", "_id": "14111"} -{"city": "NORTH JAVA", "loc": [-78.337958, 42.677631], "pop": 606, "state": "NY", "_id": "14113"} -{"city": "NORTH TONAWANDA", "loc": [-78.850997, 43.049828], "pop": 51399, "state": "NY", "_id": "14120"} -{"city": "OAKFIELD", "loc": [-78.270179, 43.071726], "pop": 3760, "state": "NY", "_id": "14125"} -{"city": "ORCHARD PARK", "loc": [-78.751834, 42.763891], "pop": 26554, "state": "NY", "_id": "14127"} -{"city": "PERRYSBURG", "loc": [-78.998108, 42.472289], "pop": 2198, "state": "NY", "_id": "14129"} -{"city": "RANSOMVILLE", "loc": [-78.898159, 43.228602], "pop": 5836, "state": "NY", "_id": "14131"} -{"city": "SANBORN", "loc": [-78.878522, 43.141879], "pop": 5167, "state": "NY", "_id": "14132"} -{"city": "SARDINIA", "loc": [-78.506415, 42.532976], "pop": 133, "state": "NY", "_id": "14134"} -{"city": "SILVER CREEK", "loc": [-79.162805, 42.535675], "pop": 5768, "state": "NY", "_id": "14136"} -{"city": "SOUTH DAYTON", "loc": [-79.050132, 42.371803], "pop": 1600, "state": "NY", "_id": "14138"} -{"city": "SOUTH WALES", "loc": [-78.545219, 42.706271], "pop": 2269, "state": "NY", "_id": "14139"} -{"city": "SPRINGVILLE", "loc": [-78.684717, 42.519982], "pop": 7146, "state": "NY", "_id": "14141"} -{"city": "STAFFORD", "loc": [-78.089783, 42.982894], "pop": 2076, "state": "NY", "_id": "14143"} -{"city": "STRYKERSVILLE", "loc": [-78.434714, 42.724892], "pop": 1374, "state": "NY", "_id": "14145"} -{"city": "TONAWANDA", "loc": [-78.85472, 43.002837], "pop": 53410, "state": "NY", "_id": "14150"} -{"city": "VARYSBURG", "loc": [-78.316749, 42.745935], "pop": 1782, "state": "NY", "_id": "14167"} -{"city": "WEST FALLS", "loc": [-78.677937, 42.70532], "pop": 2217, "state": "NY", "_id": "14170"} -{"city": "WEST VALLEY", "loc": [-78.627978, 42.43153], "pop": 2600, "state": "NY", "_id": "14171"} -{"city": "WILSON", "loc": [-78.824396, 43.29681], "pop": 3441, "state": "NY", "_id": "14172"} -{"city": "YOUNGSTOWN", "loc": [-79.024545, 43.246075], "pop": 5728, "state": "NY", "_id": "14174"} -{"city": "BUFFALO", "loc": [-78.884575, 42.896659], "pop": 16682, "state": "NY", "_id": "14201"} -{"city": "BUFFALO", "loc": [-78.877948, 42.887038], "pop": 2828, "state": "NY", "_id": "14202"} -{"city": "BUFFALO", "loc": [-78.868143, 42.893938], "pop": 1211, "state": "NY", "_id": "14203"} -{"city": "BUFFALO", "loc": [-78.859736, 42.883978], "pop": 10962, "state": "NY", "_id": "14204"} -{"city": "BUFFALO", "loc": [-78.810375, 42.881132], "pop": 25496, "state": "NY", "_id": "14206"} -{"city": "BUFFALO", "loc": [-78.897815, 42.949062], "pop": 23890, "state": "NY", "_id": "14207"} -{"city": "BUFFALO", "loc": [-78.850487, 42.915416], "pop": 14705, "state": "NY", "_id": "14208"} -{"city": "BUFFALO", "loc": [-78.865629, 42.913], "pop": 8865, "state": "NY", "_id": "14209"} -{"city": "BUFFALO", "loc": [-78.82055, 42.861432], "pop": 18506, "state": "NY", "_id": "14210"} -{"city": "BUFFALO", "loc": [-78.822477, 42.908153], "pop": 38411, "state": "NY", "_id": "14211"} -{"city": "BUFFALO", "loc": [-78.824458, 42.894553], "pop": 19935, "state": "NY", "_id": "14212"} -{"city": "BUFFALO", "loc": [-78.889461, 42.916675], "pop": 32876, "state": "NY", "_id": "14213"} -{"city": "BUFFALO", "loc": [-78.837403, 42.941429], "pop": 21770, "state": "NY", "_id": "14214"} -{"city": "BUFFALO", "loc": [-78.811504, 42.933536], "pop": 46789, "state": "NY", "_id": "14215"} -{"city": "BUFFALO", "loc": [-78.859865, 42.949914], "pop": 24852, "state": "NY", "_id": "14216"} -{"city": "KENMORE", "loc": [-78.872948, 42.968618], "pop": 25547, "state": "NY", "_id": "14217"} -{"city": "LACKAWANNA", "loc": [-78.817263, 42.818301], "pop": 22309, "state": "NY", "_id": "14218"} -{"city": "BLASDELL", "loc": [-78.822228, 42.790039], "pop": 13376, "state": "NY", "_id": "14219"} -{"city": "BUFFALO", "loc": [-78.818205, 42.844138], "pop": 28994, "state": "NY", "_id": "14220"} -{"city": "WILLIAMSVILLE", "loc": [-78.738044, 42.985621], "pop": 54703, "state": "NY", "_id": "14221"} -{"city": "BUFFALO", "loc": [-78.876333, 42.916401], "pop": 12712, "state": "NY", "_id": "14222"} -{"city": "BUFFALO", "loc": [-78.845, 42.973088], "pop": 25995, "state": "NY", "_id": "14223"} -{"city": "WEST SENECA", "loc": [-78.75109, 42.836162], "pop": 41539, "state": "NY", "_id": "14224"} -{"city": "CHEEKTOWAGA", "loc": [-78.760855, 42.928642], "pop": 37707, "state": "NY", "_id": "14225"} -{"city": "AMHERST", "loc": [-78.799849, 42.967232], "pop": 31912, "state": "NY", "_id": "14226"} -{"city": "CHEEKTOWAGA", "loc": [-78.741936, 42.877467], "pop": 25240, "state": "NY", "_id": "14227"} -{"city": "AMHERST", "loc": [-78.774604, 43.018414], "pop": 1813, "state": "NY", "_id": "14228"} -{"city": "NIAGARA FALLS", "loc": [-79.041443, 43.095467], "pop": 15756, "state": "NY", "_id": "14301"} -{"city": "NIAGARA FALLS", "loc": [-79.036958, 43.087777], "pop": 8616, "state": "NY", "_id": "14303"} -{"city": "NIAGARA FALLS", "loc": [-78.964375, 43.090844], "pop": 30527, "state": "NY", "_id": "14304"} -{"city": "NIAGARA FALLS", "loc": [-79.037804, 43.114648], "pop": 22726, "state": "NY", "_id": "14305"} -{"city": "ADAMS BASIN", "loc": [-77.853905, 43.190644], "pop": 989, "state": "NY", "_id": "14410"} -{"city": "ALBION", "loc": [-78.206846, 43.239827], "pop": 12743, "state": "NY", "_id": "14411"} -{"city": "AVON", "loc": [-77.727398, 42.903034], "pop": 6187, "state": "NY", "_id": "14414"} -{"city": "BELLONA", "loc": [-77.021737, 42.755442], "pop": 182, "state": "NY", "_id": "14415"} -{"city": "BERGEN", "loc": [-77.96033, 43.086937], "pop": 3092, "state": "NY", "_id": "14416"} -{"city": "BRANCHPORT", "loc": [-77.205165, 42.606537], "pop": 1126, "state": "NY", "_id": "14418"} -{"city": "BROCKPORT", "loc": [-77.936797, 43.21284], "pop": 17492, "state": "NY", "_id": "14420"} -{"city": "BYRON", "loc": [-78.062912, 43.073794], "pop": 2499, "state": "NY", "_id": "14422"} -{"city": "CALEDONIA", "loc": [-77.849295, 42.956661], "pop": 4942, "state": "NY", "_id": "14423"} -{"city": "CANANDAIGUA", "loc": [-77.284561, 42.868866], "pop": 20748, "state": "NY", "_id": "14424"} -{"city": "CANANDAIGUA", "loc": [-77.341139, 42.959591], "pop": 10082, "state": "NY", "_id": "14425"} -{"city": "CASTILE", "loc": [-78.054728, 42.635883], "pop": 2169, "state": "NY", "_id": "14427"} -{"city": "CLIFTON", "loc": [-77.860339, 43.089617], "pop": 6481, "state": "NY", "_id": "14428"} -{"city": "CLIFTON SPRINGS", "loc": [-77.143975, 42.963175], "pop": 5060, "state": "NY", "_id": "14432"} -{"city": "CLYDE", "loc": [-76.872464, 43.085549], "pop": 4565, "state": "NY", "_id": "14433"} -{"city": "CONESUS", "loc": [-77.67469, 42.721643], "pop": 2364, "state": "NY", "_id": "14435"} -{"city": "DANSVILLE", "loc": [-77.710907, 42.569975], "pop": 8804, "state": "NY", "_id": "14437"} -{"city": "DRESDEN", "loc": [-76.956389, 42.684565], "pop": 381, "state": "NY", "_id": "14441"} -{"city": "EAST ROCHESTER", "loc": [-77.490596, 43.112808], "pop": 8868, "state": "NY", "_id": "14445"} -{"city": "FAIRPORT", "loc": [-77.435956, 43.089198], "pop": 37337, "state": "NY", "_id": "14450"} -{"city": "GENESEO", "loc": [-77.799552, 42.793783], "pop": 9551, "state": "NY", "_id": "14454"} -{"city": "GENEVA", "loc": [-76.991349, 42.86372], "pop": 20112, "state": "NY", "_id": "14456"} -{"city": "GROVELAND", "loc": [-77.757334, 42.676046], "pop": 483, "state": "NY", "_id": "14462"} -{"city": "HAMLIN", "loc": [-77.926996, 43.307577], "pop": 7355, "state": "NY", "_id": "14464"} -{"city": "HEMLOCK", "loc": [-77.58199, 42.779957], "pop": 1160, "state": "NY", "_id": "14466"} -{"city": "HENRIETTA", "loc": [-77.612224, 43.048264], "pop": 8450, "state": "NY", "_id": "14467"} -{"city": "HILTON", "loc": [-77.790511, 43.292344], "pop": 15837, "state": "NY", "_id": "14468"} -{"city": "BLOOMFIELD", "loc": [-77.459983, 42.885101], "pop": 6129, "state": "NY", "_id": "14469"} -{"city": "HULBERTON", "loc": [-78.040392, 43.225494], "pop": 8375, "state": "NY", "_id": "14470"} -{"city": "HONEOYE", "loc": [-77.505434, 42.768637], "pop": 2704, "state": "NY", "_id": "14471"} -{"city": "HONEOYE FALLS", "loc": [-77.57812, 42.969499], "pop": 6768, "state": "NY", "_id": "14472"} -{"city": "IONIA", "loc": [-77.50086, 42.938002], "pop": 232, "state": "NY", "_id": "14475"} -{"city": "KENDALL", "loc": [-78.030359, 43.328415], "pop": 2186, "state": "NY", "_id": "14476"} -{"city": "KENT", "loc": [-78.135533, 43.334064], "pop": 2040, "state": "NY", "_id": "14477"} -{"city": "BLUFF POINT", "loc": [-77.105197, 42.601032], "pop": 1555, "state": "NY", "_id": "14478"} -{"city": "LAKEVILLE", "loc": [-77.714865, 42.82957], "pop": 724, "state": "NY", "_id": "14480"} -{"city": "LEICESTER", "loc": [-77.898976, 42.773903], "pop": 1881, "state": "NY", "_id": "14481"} -{"city": "LE ROY", "loc": [-77.985097, 42.977377], "pop": 9037, "state": "NY", "_id": "14482"} -{"city": "LIMA", "loc": [-77.608305, 42.901234], "pop": 4666, "state": "NY", "_id": "14485"} -{"city": "LINWOOD", "loc": [-77.910422, 42.884721], "pop": 860, "state": "NY", "_id": "14486"} -{"city": "LIVONIA", "loc": [-77.663472, 42.813514], "pop": 6108, "state": "NY", "_id": "14487"} -{"city": "LYONS", "loc": [-76.989575, 43.07768], "pop": 8168, "state": "NY", "_id": "14489"} -{"city": "MACEDON", "loc": [-77.337199, 43.078366], "pop": 8486, "state": "NY", "_id": "14502"} -{"city": "MANCHESTER", "loc": [-77.229733, 42.969874], "pop": 1522, "state": "NY", "_id": "14504"} -{"city": "MARION", "loc": [-77.186277, 43.154612], "pop": 4663, "state": "NY", "_id": "14505"} -{"city": "MENDON", "loc": [-77.50013, 42.995307], "pop": 864, "state": "NY", "_id": "14506"} -{"city": "MIDDLESEX", "loc": [-77.280509, 42.697624], "pop": 1256, "state": "NY", "_id": "14507"} -{"city": "TUSCARORA", "loc": [-77.861083, 42.705431], "pop": 7145, "state": "NY", "_id": "14510"} -{"city": "NAPLES", "loc": [-77.390106, 42.640425], "pop": 4616, "state": "NY", "_id": "14512"} -{"city": "NEWARK", "loc": [-77.094602, 43.051909], "pop": 14670, "state": "NY", "_id": "14513"} -{"city": "NORTH CHILI", "loc": [-77.800518, 43.118601], "pop": 4014, "state": "NY", "_id": "14514"} -{"city": "NORTH ROSE", "loc": [-76.915152, 43.196439], "pop": 2428, "state": "NY", "_id": "14516"} -{"city": "NUNDA", "loc": [-77.918006, 42.586708], "pop": 2439, "state": "NY", "_id": "14517"} -{"city": "ONTARIO", "loc": [-77.308781, 43.229092], "pop": 10016, "state": "NY", "_id": "14519"} -{"city": "HAYT CORNERS", "loc": [-76.821575, 42.676979], "pop": 3225, "state": "NY", "_id": "14521"} -{"city": "PALMYRA", "loc": [-77.221798, 43.062192], "pop": 9486, "state": "NY", "_id": "14522"} -{"city": "LINWOOD", "loc": [-78.014698, 42.880088], "pop": 2628, "state": "NY", "_id": "14525"} -{"city": "PENFIELD", "loc": [-77.456043, 43.139638], "pop": 16675, "state": "NY", "_id": "14526"} -{"city": "PENN YAN", "loc": [-77.05687, 42.664548], "pop": 11609, "state": "NY", "_id": "14527"} -{"city": "PERRY", "loc": [-78.005882, 42.722852], "pop": 6218, "state": "NY", "_id": "14530"} -{"city": "PHELPS", "loc": [-77.047264, 42.958178], "pop": 4366, "state": "NY", "_id": "14532"} -{"city": "WADSWORTH", "loc": [-77.882545, 42.84269], "pop": 2094, "state": "NY", "_id": "14533"} -{"city": "PITTSFORD", "loc": [-77.514067, 43.069511], "pop": 27413, "state": "NY", "_id": "14534"} -{"city": "PORTAGEVILLE", "loc": [-78.085635, 42.556957], "pop": 980, "state": "NY", "_id": "14536"} -{"city": "MAC DOUGALL", "loc": [-76.853582, 42.761378], "pop": 3150, "state": "NY", "_id": "14541"} -{"city": "WEST RUSH", "loc": [-77.665381, 42.998934], "pop": 2481, "state": "NY", "_id": "14543"} -{"city": "RUSHVILLE", "loc": [-77.239538, 42.75973], "pop": 1948, "state": "NY", "_id": "14544"} -{"city": "SCOTTSBURG", "loc": [-77.682633, 42.658907], "pop": 612, "state": "NY", "_id": "14545"} -{"city": "SCOTTSVILLE", "loc": [-77.774256, 43.02462], "pop": 5604, "state": "NY", "_id": "14546"} -{"city": "SHORTSVILLE", "loc": [-77.227312, 42.969034], "pop": 4524, "state": "NY", "_id": "14548"} -{"city": "ROCK GLEN", "loc": [-78.092994, 42.669446], "pop": 1824, "state": "NY", "_id": "14550"} -{"city": "SODUS", "loc": [-77.05141, 43.221681], "pop": 5831, "state": "NY", "_id": "14551"} -{"city": "SODUS POINT", "loc": [-76.98833, 43.265058], "pop": 1058, "state": "NY", "_id": "14555"} -{"city": "SPENCERPORT", "loc": [-77.804333, 43.189502], "pop": 14137, "state": "NY", "_id": "14559"} -{"city": "SPRINGWATER", "loc": [-77.577503, 42.677598], "pop": 2319, "state": "NY", "_id": "14560"} -{"city": "STANLEY", "loc": [-77.120702, 42.830277], "pop": 3102, "state": "NY", "_id": "14561"} -{"city": "VICTOR", "loc": [-77.417982, 42.986597], "pop": 7322, "state": "NY", "_id": "14564"} -{"city": "WALWORTH", "loc": [-77.28582, 43.140161], "pop": 5461, "state": "NY", "_id": "14568"} -{"city": "WARSAW", "loc": [-78.142899, 42.741035], "pop": 5811, "state": "NY", "_id": "14569"} -{"city": "WATERPORT", "loc": [-78.242958, 43.332563], "pop": 1669, "state": "NY", "_id": "14571"} -{"city": "WAYLAND", "loc": [-77.590613, 42.559274], "pop": 5626, "state": "NY", "_id": "14572"} -{"city": "WEBSTER", "loc": [-77.461587, 43.219563], "pop": 35072, "state": "NY", "_id": "14580"} -{"city": "WEST HENRIETTA", "loc": [-77.687131, 43.039667], "pop": 5397, "state": "NY", "_id": "14586"} -{"city": "WILLIAMSON", "loc": [-77.170011, 43.242071], "pop": 7947, "state": "NY", "_id": "14589"} -{"city": "WOLCOTT", "loc": [-76.821748, 43.234129], "pop": 5356, "state": "NY", "_id": "14590"} -{"city": "WYOMING", "loc": [-78.083292, 42.83175], "pop": 2074, "state": "NY", "_id": "14591"} -{"city": "ROCHESTER", "loc": [-77.607978, 43.157729], "pop": 1434, "state": "NY", "_id": "14604"} -{"city": "ROCHESTER", "loc": [-77.600711, 43.169758], "pop": 15196, "state": "NY", "_id": "14605"} -{"city": "ROCHESTER", "loc": [-77.684488, 43.168455], "pop": 29822, "state": "NY", "_id": "14606"} -{"city": "ROCHESTER", "loc": [-77.588976, 43.150086], "pop": 16479, "state": "NY", "_id": "14607"} -{"city": "ROCHESTER", "loc": [-77.625803, 43.152144], "pop": 14631, "state": "NY", "_id": "14608"} -{"city": "ROCHESTER", "loc": [-77.563701, 43.174001], "pop": 45805, "state": "NY", "_id": "14609"} -{"city": "ROCHESTER", "loc": [-77.549501, 43.14524], "pop": 15167, "state": "NY", "_id": "14610"} -{"city": "ROCHESTER", "loc": [-77.639353, 43.148375], "pop": 15951, "state": "NY", "_id": "14611"} -{"city": "ROCHESTER", "loc": [-77.665228, 43.256576], "pop": 35820, "state": "NY", "_id": "14612"} -{"city": "ROCHESTER", "loc": [-77.639276, 43.18308], "pop": 15525, "state": "NY", "_id": "14613"} -{"city": "ROCHESTER", "loc": [-77.61419, 43.155823], "pop": 1430, "state": "NY", "_id": "14614"} -{"city": "ROCHESTER", "loc": [-77.652118, 43.20575], "pop": 16000, "state": "NY", "_id": "14615"} -{"city": "GREECE", "loc": [-77.651238, 43.232359], "pop": 26500, "state": "NY", "_id": "14616"} -{"city": "ROCHESTER", "loc": [-77.599442, 43.220258], "pop": 24296, "state": "NY", "_id": "14617"} -{"city": "TWELVE CORNERS", "loc": [-77.558801, 43.115416], "pop": 21912, "state": "NY", "_id": "14618"} -{"city": "ROCHESTER", "loc": [-77.6481, 43.136685], "pop": 19393, "state": "NY", "_id": "14619"} -{"city": "ROCHESTER", "loc": [-77.606239, 43.131711], "pop": 29819, "state": "NY", "_id": "14620"} -{"city": "ROCHESTER", "loc": [-77.604284, 43.183362], "pop": 36055, "state": "NY", "_id": "14621"} -{"city": "ROCHESTER", "loc": [-77.55549, 43.213959], "pop": 12489, "state": "NY", "_id": "14622"} -{"city": "ROCHESTER", "loc": [-77.634412, 43.083371], "pop": 25127, "state": "NY", "_id": "14623"} -{"city": "WESTGATE", "loc": [-77.733552, 43.12589], "pop": 35748, "state": "NY", "_id": "14624"} -{"city": "PANORAMA", "loc": [-77.503188, 43.14949], "pop": 12009, "state": "NY", "_id": "14625"} -{"city": "ROCHESTER", "loc": [-77.703996, 43.21257], "pop": 25574, "state": "NY", "_id": "14626"} -{"city": "JAMESTOWN", "loc": [-79.243989, 42.092845], "pop": 45344, "state": "NY", "_id": "14701"} -{"city": "ALLEGANY", "loc": [-78.499883, 42.091827], "pop": 7885, "state": "NY", "_id": "14706"} -{"city": "ALMA", "loc": [-78.040012, 42.016859], "pop": 200, "state": "NY", "_id": "14708"} -{"city": "ANGELICA", "loc": [-77.994671, 42.326339], "pop": 1046, "state": "NY", "_id": "14709"} -{"city": "ASHVILLE", "loc": [-79.405624, 42.108376], "pop": 2450, "state": "NY", "_id": "14710"} -{"city": "BELFAST", "loc": [-78.094281, 42.320013], "pop": 2210, "state": "NY", "_id": "14711"} -{"city": "BEMUS POINT", "loc": [-79.35808, 42.151346], "pop": 3591, "state": "NY", "_id": "14712"} -{"city": "BLACK CREEK", "loc": [-78.231249, 42.285528], "pop": 757, "state": "NY", "_id": "14714"} -{"city": "BOLIVAR", "loc": [-78.144834, 42.070442], "pop": 3173, "state": "NY", "_id": "14715"} -{"city": "BROCTON", "loc": [-79.43443, 42.393973], "pop": 1863, "state": "NY", "_id": "14716"} -{"city": "CANEADEA", "loc": [-78.197321, 42.370304], "pop": 798, "state": "NY", "_id": "14717"} -{"city": "CASSADAGA", "loc": [-79.299282, 42.350356], "pop": 1977, "state": "NY", "_id": "14718"} -{"city": "CATTARAUGUS", "loc": [-78.888528, 42.333291], "pop": 3853, "state": "NY", "_id": "14719"} -{"city": "CERES", "loc": [-78.272686, 41.99939], "pop": 33, "state": "NY", "_id": "14721"} -{"city": "CHERRY CREEK", "loc": [-79.120275, 42.312725], "pop": 1408, "state": "NY", "_id": "14723"} -{"city": "CLYMER", "loc": [-79.668532, 42.055699], "pop": 2810, "state": "NY", "_id": "14724"} -{"city": "CONEWANGO VALLEY", "loc": [-79.021956, 42.262467], "pop": 1562, "state": "NY", "_id": "14726"} -{"city": "CUBA", "loc": [-78.275074, 42.18819], "pop": 5306, "state": "NY", "_id": "14727"} -{"city": "DEWITTVILLE", "loc": [-79.419303, 42.239413], "pop": 1159, "state": "NY", "_id": "14728"} -{"city": "EAST OTTO", "loc": [-78.743167, 42.3971], "pop": 1050, "state": "NY", "_id": "14729"} -{"city": "ELLICOTTVILLE", "loc": [-78.66064, 42.295873], "pop": 1767, "state": "NY", "_id": "14731"} -{"city": "FALCONER", "loc": [-79.189499, 42.123915], "pop": 3812, "state": "NY", "_id": "14733"} -{"city": "FILLMORE", "loc": [-78.106232, 42.463605], "pop": 2463, "state": "NY", "_id": "14735"} -{"city": "FINDLEY LAKE", "loc": [-79.734908, 42.120401], "pop": 77, "state": "NY", "_id": "14736"} -{"city": "FRANKLINVILLE", "loc": [-78.440043, 42.338823], "pop": 3965, "state": "NY", "_id": "14737"} -{"city": "FREWSBURG", "loc": [-79.131797, 42.052753], "pop": 3919, "state": "NY", "_id": "14738"} -{"city": "FRIENDSHIP", "loc": [-78.13588, 42.190666], "pop": 3214, "state": "NY", "_id": "14739"} -{"city": "GERRY", "loc": [-79.164865, 42.214728], "pop": 1836, "state": "NY", "_id": "14740"} -{"city": "GREAT VALLEY", "loc": [-78.620811, 42.208292], "pop": 2217, "state": "NY", "_id": "14741"} -{"city": "ISCHUA", "loc": [-78.400611, 42.179747], "pop": 2635, "state": "NY", "_id": "14743"} -{"city": "HOUGHTON", "loc": [-78.109506, 42.411567], "pop": 173, "state": "NY", "_id": "14744"} -{"city": "KENNEDY", "loc": [-79.096416, 42.150776], "pop": 2719, "state": "NY", "_id": "14747"} -{"city": "KILL BUCK", "loc": [-78.642604, 42.141005], "pop": 47, "state": "NY", "_id": "14748"} -{"city": "LAKEWOOD", "loc": [-79.329124, 42.097256], "pop": 5113, "state": "NY", "_id": "14750"} -{"city": "LIMESTONE", "loc": [-78.631979, 42.063945], "pop": 1981, "state": "NY", "_id": "14753"} -{"city": "LITTLE GENESEE", "loc": [-78.235056, 42.023964], "pop": 1240, "state": "NY", "_id": "14754"} -{"city": "LITTLE VALLEY", "loc": [-78.809286, 42.254121], "pop": 2311, "state": "NY", "_id": "14755"} -{"city": "MAYVILLE", "loc": [-79.496325, 42.240906], "pop": 3378, "state": "NY", "_id": "14757"} -{"city": "OLEAN", "loc": [-78.423265, 42.0787], "pop": 22055, "state": "NY", "_id": "14760"} -{"city": "PANAMA", "loc": [-79.481536, 42.056965], "pop": 2437, "state": "NY", "_id": "14767"} -{"city": "PORTLAND", "loc": [-79.458894, 42.385792], "pop": 2001, "state": "NY", "_id": "14769"} -{"city": "PORTVILLE", "loc": [-78.331353, 42.027253], "pop": 1743, "state": "NY", "_id": "14770"} -{"city": "RANDOLPH", "loc": [-78.960034, 42.163143], "pop": 4253, "state": "NY", "_id": "14772"} -{"city": "RIPLEY", "loc": [-79.712103, 42.248175], "pop": 2876, "state": "NY", "_id": "14775"} -{"city": "RUSHFORD", "loc": [-78.186704, 42.417216], "pop": 2701, "state": "NY", "_id": "14777"} -{"city": "SALAMANCA", "loc": [-78.73042, 42.16035], "pop": 8632, "state": "NY", "_id": "14779"} -{"city": "SHERMAN", "loc": [-79.585742, 42.163124], "pop": 2184, "state": "NY", "_id": "14781"} -{"city": "SINCLAIRVILLE", "loc": [-79.267297, 42.24548], "pop": 3065, "state": "NY", "_id": "14782"} -{"city": "STOCKTON", "loc": [-79.375792, 42.318195], "pop": 1894, "state": "NY", "_id": "14784"} -{"city": "WESTFIELD", "loc": [-79.572646, 42.321977], "pop": 5872, "state": "NY", "_id": "14787"} -{"city": "ADDISON", "loc": [-77.266027, 42.09825], "pop": 4967, "state": "NY", "_id": "14801"} -{"city": "ALFRED", "loc": [-77.79295, 42.255165], "pop": 4559, "state": "NY", "_id": "14802"} -{"city": "ALFRED STATION", "loc": [-77.778084, 42.255799], "pop": 1071, "state": "NY", "_id": "14803"} -{"city": "ALMOND", "loc": [-77.777965, 42.316036], "pop": 1642, "state": "NY", "_id": "14804"} -{"city": "ALPINE", "loc": [-76.734775, 42.351014], "pop": 1098, "state": "NY", "_id": "14805"} -{"city": "ANDOVER", "loc": [-77.792008, 42.157454], "pop": 2489, "state": "NY", "_id": "14806"} -{"city": "ARKPORT", "loc": [-77.691778, 42.422466], "pop": 2937, "state": "NY", "_id": "14807"} -{"city": "ATLANTA", "loc": [-77.480091, 42.556269], "pop": 472, "state": "NY", "_id": "14808"} -{"city": "WALLACE", "loc": [-77.434254, 42.41513], "pop": 2448, "state": "NY", "_id": "14809"} -{"city": "VETERANS ADMINIS", "loc": [-77.323255, 42.345451], "pop": 12982, "state": "NY", "_id": "14810"} -{"city": "BEAVER DAMS", "loc": [-76.971953, 42.279763], "pop": 3765, "state": "NY", "_id": "14812"} -{"city": "BELMONT", "loc": [-78.01103, 42.233409], "pop": 1773, "state": "NY", "_id": "14813"} -{"city": "BIG FLATS", "loc": [-76.952721, 42.145509], "pop": 1892, "state": "NY", "_id": "14814"} -{"city": "BRADFORD", "loc": [-77.09134, 42.382517], "pop": 944, "state": "NY", "_id": "14815"} -{"city": "BREESPORT", "loc": [-76.73612, 42.19392], "pop": 160, "state": "NY", "_id": "14816"} -{"city": "BROOKTONDALE", "loc": [-76.366844, 42.37653], "pop": 2665, "state": "NY", "_id": "14817"} -{"city": "BURDETT", "loc": [-76.829219, 42.439442], "pop": 1714, "state": "NY", "_id": "14818"} -{"city": "CAMERON", "loc": [-77.440341, 42.21284], "pop": 698, "state": "NY", "_id": "14819"} -{"city": "CAMERON MILLS", "loc": [-77.364976, 42.192547], "pop": 723, "state": "NY", "_id": "14820"} -{"city": "CAMPBELL", "loc": [-77.206619, 42.238569], "pop": 2966, "state": "NY", "_id": "14821"} -{"city": "CANASERAGA", "loc": [-77.795374, 42.458504], "pop": 1458, "state": "NY", "_id": "14822"} -{"city": "CANISTEO", "loc": [-77.589706, 42.263503], "pop": 3945, "state": "NY", "_id": "14823"} -{"city": "CAYUTA", "loc": [-76.697367, 42.277445], "pop": 599, "state": "NY", "_id": "14824"} -{"city": "CHEMUNG", "loc": [-76.620224, 42.039243], "pop": 615, "state": "NY", "_id": "14825"} -{"city": "COHOCTON", "loc": [-77.499763, 42.500315], "pop": 2286, "state": "NY", "_id": "14826"} -{"city": "CORNING", "loc": [-77.047546, 42.138331], "pop": 19957, "state": "NY", "_id": "14830"} -{"city": "DALTON", "loc": [-77.928889, 42.544895], "pop": 875, "state": "NY", "_id": "14836"} -{"city": "DUNDEE", "loc": [-77.002773, 42.505261], "pop": 5125, "state": "NY", "_id": "14837"} -{"city": "ERIN", "loc": [-76.681942, 42.185898], "pop": 2053, "state": "NY", "_id": "14838"} -{"city": "GREENWOOD", "loc": [-77.636041, 42.139809], "pop": 927, "state": "NY", "_id": "14839"} -{"city": "HAMMONDSPORT", "loc": [-77.197702, 42.431217], "pop": 3068, "state": "NY", "_id": "14840"} -{"city": "HECTOR", "loc": [-76.878597, 42.49658], "pop": 147, "state": "NY", "_id": "14841"} -{"city": "HIMROD", "loc": [-76.950774, 42.594455], "pop": 726, "state": "NY", "_id": "14842"} -{"city": "HORNELL", "loc": [-77.656907, 42.327393], "pop": 14311, "state": "NY", "_id": "14843"} -{"city": "HORSEHEADS", "loc": [-76.834539, 42.180493], "pop": 20799, "state": "NY", "_id": "14845"} -{"city": "HUNT", "loc": [-77.981839, 42.538821], "pop": 1236, "state": "NY", "_id": "14846"} -{"city": "INTERLAKEN", "loc": [-76.726798, 42.616524], "pop": 2148, "state": "NY", "_id": "14847"} -{"city": "ITHACA COLLEGE", "loc": [-76.492911, 42.448497], "pop": 40760, "state": "NY", "_id": "14850"} -{"city": "ITHACA", "loc": [-76.488707, 42.443087], "pop": 20379, "state": "NY", "_id": "14853"} -{"city": "JASPER", "loc": [-77.499909, 42.128962], "pop": 870, "state": "NY", "_id": "14855"} -{"city": "LINDLEY", "loc": [-77.154132, 42.042483], "pop": 1765, "state": "NY", "_id": "14858"} -{"city": "LOCKWOOD", "loc": [-76.536618, 42.114943], "pop": 951, "state": "NY", "_id": "14859"} -{"city": "LODI", "loc": [-76.833904, 42.596555], "pop": 1209, "state": "NY", "_id": "14860"} -{"city": "LOWMAN", "loc": [-76.693005, 42.06938], "pop": 2338, "state": "NY", "_id": "14861"} -{"city": "MILLPORT", "loc": [-76.839233, 42.258084], "pop": 1432, "state": "NY", "_id": "14864"} -{"city": "MONTOUR FALLS", "loc": [-76.839581, 42.343678], "pop": 2851, "state": "NY", "_id": "14865"} -{"city": "NEWFIELD", "loc": [-76.591978, 42.362137], "pop": 5311, "state": "NY", "_id": "14867"} -{"city": "ODESSA", "loc": [-76.771698, 42.360947], "pop": 1471, "state": "NY", "_id": "14869"} -{"city": "PAINTED POST", "loc": [-77.119375, 42.170967], "pop": 9269, "state": "NY", "_id": "14870"} -{"city": "PINE CITY", "loc": [-76.881519, 42.041938], "pop": 4911, "state": "NY", "_id": "14871"} -{"city": "PINE VALLEY", "loc": [-76.865206, 42.234795], "pop": 252, "state": "NY", "_id": "14872"} -{"city": "PRATTSBURG", "loc": [-77.282455, 42.526918], "pop": 2534, "state": "NY", "_id": "14873"} -{"city": "PULTENEY", "loc": [-77.169108, 42.523275], "pop": 504, "state": "NY", "_id": "14874"} -{"city": "REXVILLE", "loc": [-77.676663, 42.072645], "pop": 539, "state": "NY", "_id": "14877"} -{"city": "ROCK STREAM", "loc": [-76.936436, 42.448511], "pop": 703, "state": "NY", "_id": "14878"} -{"city": "SAVONA", "loc": [-77.208345, 42.30407], "pop": 1982, "state": "NY", "_id": "14879"} -{"city": "SCIO", "loc": [-77.990026, 42.169703], "pop": 2491, "state": "NY", "_id": "14880"} -{"city": "SLATERVILLE SPRI", "loc": [-76.315677, 42.401119], "pop": 107, "state": "NY", "_id": "14881"} -{"city": "LANSING", "loc": [-76.537455, 42.564494], "pop": 4043, "state": "NY", "_id": "14882"} -{"city": "SPENCER", "loc": [-76.489853, 42.246736], "pop": 3829, "state": "NY", "_id": "14883"} -{"city": "SWAIN", "loc": [-77.88899, 42.477314], "pop": 267, "state": "NY", "_id": "14884"} -{"city": "TROUPSBURG", "loc": [-77.550208, 42.050083], "pop": 831, "state": "NY", "_id": "14885"} -{"city": "TRUMANSBURG", "loc": [-76.668145, 42.520987], "pop": 6379, "state": "NY", "_id": "14886"} -{"city": "VALOIS", "loc": [-76.860879, 42.524304], "pop": 547, "state": "NY", "_id": "14888"} -{"city": "VAN ETTEN", "loc": [-76.571663, 42.208455], "pop": 1477, "state": "NY", "_id": "14889"} -{"city": "WATKINS GLEN", "loc": [-76.90215, 42.377121], "pop": 4584, "state": "NY", "_id": "14891"} -{"city": "WAVERLY", "loc": [-76.533308, 42.017228], "pop": 8262, "state": "NY", "_id": "14892"} -{"city": "WELLSBURG", "loc": [-76.772315, 42.027317], "pop": 2829, "state": "NY", "_id": "14894"} -{"city": "WELLSVILLE", "loc": [-77.94191, 42.110757], "pop": 9645, "state": "NY", "_id": "14895"} -{"city": "WHITESVILLE", "loc": [-77.810615, 42.045586], "pop": 1178, "state": "NY", "_id": "14897"} -{"city": "WOODHULL", "loc": [-77.420333, 42.073638], "pop": 1318, "state": "NY", "_id": "14898"} -{"city": "ELMIRA", "loc": [-76.811977, 42.100769], "pop": 19037, "state": "NY", "_id": "14901"} -{"city": "ELMIRA HEIGHTS", "loc": [-76.843572, 42.130203], "pop": 7918, "state": "NY", "_id": "14903"} -{"city": "ELMIRA", "loc": [-76.803735, 42.072866], "pop": 18103, "state": "NY", "_id": "14904"} -{"city": "ELMIRA", "loc": [-76.839686, 42.086919], "pop": 10018, "state": "NY", "_id": "14905"} -{"city": "ADVANCE", "loc": [-80.446301, 36.006496], "pop": 3325, "state": "NC", "_id": "27006"} -{"city": "ARARAT", "loc": [-80.608887, 36.386932], "pop": 3101, "state": "NC", "_id": "27007"} -{"city": "BELEWS CREEK", "loc": [-80.092988, 36.226874], "pop": 1556, "state": "NC", "_id": "27009"} -{"city": "BOONVILLE", "loc": [-80.63569, 36.234012], "pop": 262, "state": "NC", "_id": "27011"} -{"city": "CLEMMONS", "loc": [-80.39623, 36.034112], "pop": 12694, "state": "NC", "_id": "27012"} -{"city": "CLEVELAND", "loc": [-80.711254, 35.737397], "pop": 4070, "state": "NC", "_id": "27013"} -{"city": "DANBURY", "loc": [-80.219354, 36.455857], "pop": 1560, "state": "NC", "_id": "27016"} -{"city": "DOBSON", "loc": [-80.710065, 36.369826], "pop": 6092, "state": "NC", "_id": "27017"} -{"city": "EAST BEND", "loc": [-80.528405, 36.203152], "pop": 6140, "state": "NC", "_id": "27018"} -{"city": "GERMANTON", "loc": [-80.225432, 36.272513], "pop": 4428, "state": "NC", "_id": "27019"} -{"city": "HAMPTONVILLE", "loc": [-80.813687, 36.124248], "pop": 4691, "state": "NC", "_id": "27020"} -{"city": "KING", "loc": [-80.355957, 36.295033], "pop": 12631, "state": "NC", "_id": "27021"} -{"city": "LAWSONVILLE", "loc": [-80.210291, 36.513228], "pop": 1550, "state": "NC", "_id": "27022"} -{"city": "LEWISVILLE", "loc": [-80.420638, 36.0967], "pop": 7165, "state": "NC", "_id": "27023"} -{"city": "LOWGAP", "loc": [-80.788862, 36.503041], "pop": 5065, "state": "NC", "_id": "27024"} -{"city": "MADISON", "loc": [-79.965438, 36.369507], "pop": 10393, "state": "NC", "_id": "27025"} -{"city": "MAYODAN", "loc": [-79.956742, 36.427677], "pop": 6545, "state": "NC", "_id": "27027"} -{"city": "MOCKSVILLE", "loc": [-80.536965, 35.922007], "pop": 24999, "state": "NC", "_id": "27028"} -{"city": "MOUNT AIRY", "loc": [-80.611871, 36.500739], "pop": 29155, "state": "NC", "_id": "27030"} -{"city": "PFAFFTOWN", "loc": [-80.379826, 36.166914], "pop": 8160, "state": "NC", "_id": "27040"} -{"city": "PILOT MOUNTAIN", "loc": [-80.492065, 36.41094], "pop": 7058, "state": "NC", "_id": "27041"} -{"city": "PINE HALL", "loc": [-80.049533, 36.348178], "pop": 377, "state": "NC", "_id": "27042"} -{"city": "PINNACLE", "loc": [-80.439111, 36.333905], "pop": 4275, "state": "NC", "_id": "27043"} -{"city": "RURAL HALL", "loc": [-80.293643, 36.229251], "pop": 6635, "state": "NC", "_id": "27045"} -{"city": "SANDY RIDGE", "loc": [-80.085857, 36.489973], "pop": 1580, "state": "NC", "_id": "27046"} -{"city": "SILOAM", "loc": [-80.577162, 36.300208], "pop": 524, "state": "NC", "_id": "27047"} -{"city": "STONEVILLE", "loc": [-79.901297, 36.470483], "pop": 4166, "state": "NC", "_id": "27048"} -{"city": "TOBACCOVILLE", "loc": [-80.391549, 36.233627], "pop": 2697, "state": "NC", "_id": "27050"} -{"city": "WALKERTOWN", "loc": [-80.162865, 36.179999], "pop": 6199, "state": "NC", "_id": "27051"} -{"city": "WALNUT COVE", "loc": [-80.148414, 36.318872], "pop": 9048, "state": "NC", "_id": "27052"} -{"city": "WESTFIELD", "loc": [-80.367739, 36.467405], "pop": 3500, "state": "NC", "_id": "27053"} -{"city": "WOODLEAF", "loc": [-80.603941, 35.787564], "pop": 2735, "state": "NC", "_id": "27054"} -{"city": "YADKINVILLE", "loc": [-80.65302, 36.127694], "pop": 11978, "state": "NC", "_id": "27055"} -{"city": "WINSTON SALEM", "loc": [-80.222798, 36.10237], "pop": 19333, "state": "NC", "_id": "27101"} -{"city": "WINSTON SALEM", "loc": [-80.302509, 36.067127], "pop": 24299, "state": "NC", "_id": "27103"} -{"city": "WINSTON SALEM", "loc": [-80.322423, 36.091985], "pop": 23677, "state": "NC", "_id": "27104"} -{"city": "WINSTON SALEM", "loc": [-80.237646, 36.144039], "pop": 40194, "state": "NC", "_id": "27105"} -{"city": "WINSTON SALEM", "loc": [-80.306866, 36.142762], "pop": 35209, "state": "NC", "_id": "27106"} -{"city": "WINSTON SALEM", "loc": [-80.193265, 36.040324], "pop": 33085, "state": "NC", "_id": "27107"} -{"city": "WINSTON SALEM", "loc": [-80.260946, 36.042534], "pop": 20784, "state": "NC", "_id": "27127"} -{"city": "FARMER", "loc": [-79.819678, 35.693496], "pop": 41541, "state": "NC", "_id": "27203"} -{"city": "BEAR CREEK", "loc": [-79.372606, 35.612882], "pop": 3894, "state": "NC", "_id": "27207"} -{"city": "BENNETT", "loc": [-79.522524, 35.567269], "pop": 1218, "state": "NC", "_id": "27208"} -{"city": "BISCOE", "loc": [-79.759555, 35.325654], "pop": 3528, "state": "NC", "_id": "27209"} -{"city": "BLANCH", "loc": [-79.304185, 36.533839], "pop": 94, "state": "NC", "_id": "27212"} -{"city": "BROWNS SUMMIT", "loc": [-79.71008, 36.201868], "pop": 5688, "state": "NC", "_id": "27214"} -{"city": "GLEN RAVEN", "loc": [-79.462152, 36.072011], "pop": 29107, "state": "NC", "_id": "27215"} -{"city": "BURLINGTON", "loc": [-79.411447, 36.1288], "pop": 30651, "state": "NC", "_id": "27217"} -{"city": "CANDOR", "loc": [-79.787623, 35.22335], "pop": 967, "state": "NC", "_id": "27229"} -{"city": "CEDAR GROVE", "loc": [-79.166588, 36.201986], "pop": 1594, "state": "NC", "_id": "27231"} -{"city": "CLIMAX", "loc": [-79.701858, 35.934512], "pop": 1085, "state": "NC", "_id": "27233"} -{"city": "COLFAX", "loc": [-80.010276, 36.100272], "pop": 1983, "state": "NC", "_id": "27235"} -{"city": "DENTON", "loc": [-80.095926, 35.619567], "pop": 7102, "state": "NC", "_id": "27239"} -{"city": "EAGLE SPRINGS", "loc": [-79.630983, 35.335957], "pop": 1742, "state": "NC", "_id": "27242"} -{"city": "EFLAND", "loc": [-79.188415, 36.091166], "pop": 2661, "state": "NC", "_id": "27243"} -{"city": "ELON COLLEGE", "loc": [-79.502334, 36.146638], "pop": 10269, "state": "NC", "_id": "27244"} -{"city": "FRANKLINVILLE", "loc": [-79.713178, 35.791739], "pop": 4613, "state": "NC", "_id": "27248"} -{"city": "GIBSONVILLE", "loc": [-79.568454, 36.118304], "pop": 8409, "state": "NC", "_id": "27249"} -{"city": "GOLDSTON", "loc": [-79.33852, 35.56428], "pop": 1814, "state": "NC", "_id": "27252"} -{"city": "GRAHAM", "loc": [-79.381384, 36.030967], "pop": 19723, "state": "NC", "_id": "27253"} -{"city": "HAW RIVER", "loc": [-79.334557, 36.055085], "pop": 4964, "state": "NC", "_id": "27258"} -{"city": "HIGH POINT", "loc": [-80.011673, 35.959313], "pop": 36887, "state": "NC", "_id": "27260"} -{"city": "HIGH POINT", "loc": [-80.010677, 35.973406], "pop": 11401, "state": "NC", "_id": "27262"} -{"city": "ARCHDALE", "loc": [-79.961764, 35.910757], "pop": 17140, "state": "NC", "_id": "27263"} -{"city": "HIGH POINT", "loc": [-80.003571, 36.003584], "pop": 22310, "state": "NC", "_id": "27265"} -{"city": "HILLSBOROUGH", "loc": [-79.091416, 36.07558], "pop": 15811, "state": "NC", "_id": "27278"} -{"city": "JACKSON SPRINGS", "loc": [-79.668535, 35.225941], "pop": 2167, "state": "NC", "_id": "27281"} -{"city": "JAMESTOWN", "loc": [-79.929286, 35.998993], "pop": 9812, "state": "NC", "_id": "27282"} -{"city": "JULIAN", "loc": [-79.638629, 35.95429], "pop": 2894, "state": "NC", "_id": "27283"} -{"city": "KERNERSVILLE", "loc": [-80.083115, 36.116534], "pop": 34795, "state": "NC", "_id": "27284"} -{"city": "EDEN", "loc": [-79.758998, 36.500003], "pop": 26675, "state": "NC", "_id": "27288"} -{"city": "LEASBURG", "loc": [-79.194188, 36.415113], "pop": 2700, "state": "NC", "_id": "27291"} -{"city": "LEXINGTON", "loc": [-80.262049, 35.82306], "pop": 69179, "state": "NC", "_id": "27292"} -{"city": "LIBERTY", "loc": [-79.582144, 35.872878], "pop": 7303, "state": "NC", "_id": "27298"} -{"city": "LINWOOD", "loc": [-80.374895, 35.75623], "pop": 2823, "state": "NC", "_id": "27299"} -{"city": "MC LEANSVILLE", "loc": [-79.668388, 36.116291], "pop": 5200, "state": "NC", "_id": "27301"} -{"city": "MEBANE", "loc": [-79.271895, 36.097862], "pop": 14650, "state": "NC", "_id": "27302"} -{"city": "MILTON", "loc": [-79.208699, 36.515784], "pop": 1383, "state": "NC", "_id": "27305"} -{"city": "MOUNT GILEAD", "loc": [-79.98709, 35.227447], "pop": 5829, "state": "NC", "_id": "27306"} -{"city": "OAK RIDGE", "loc": [-79.980366, 36.167315], "pop": 2231, "state": "NC", "_id": "27310"} -{"city": "PELHAM", "loc": [-79.473605, 36.489912], "pop": 3964, "state": "NC", "_id": "27311"} -{"city": "PITTSBORO", "loc": [-79.175509, 35.769436], "pop": 10601, "state": "NC", "_id": "27312"} -{"city": "PLEASANT GARDEN", "loc": [-79.754943, 35.952243], "pop": 4064, "state": "NC", "_id": "27313"} -{"city": "PROSPECT HILL", "loc": [-79.215644, 36.293195], "pop": 1116, "state": "NC", "_id": "27314"} -{"city": "PROVIDENCE", "loc": [-79.36306, 36.48314], "pop": 3462, "state": "NC", "_id": "27315"} -{"city": "COLERIDGE", "loc": [-79.635101, 35.708349], "pop": 5238, "state": "NC", "_id": "27316"} -{"city": "RANDLEMAN", "loc": [-79.801794, 35.848805], "pop": 14220, "state": "NC", "_id": "27317"} -{"city": "REIDSVILLE", "loc": [-79.664212, 36.343209], "pop": 34283, "state": "NC", "_id": "27320"} -{"city": "ROBBINS", "loc": [-79.582034, 35.452839], "pop": 6383, "state": "NC", "_id": "27325"} -{"city": "RUFFIN", "loc": [-79.560551, 36.442763], "pop": 4138, "state": "NC", "_id": "27326"} -{"city": "COLON", "loc": [-79.176446, 35.464148], "pop": 39537, "state": "NC", "_id": "27330"} -{"city": "SEAGROVE", "loc": [-79.69787, 35.528309], "pop": 4656, "state": "NC", "_id": "27341"} -{"city": "SEMORA", "loc": [-79.094168, 36.511414], "pop": 1214, "state": "NC", "_id": "27343"} -{"city": "SILER CITY", "loc": [-79.456624, 35.735392], "pop": 12298, "state": "NC", "_id": "27344"} -{"city": "SNOW CAMP", "loc": [-79.427893, 35.906583], "pop": 3424, "state": "NC", "_id": "27349"} -{"city": "SOPHIA", "loc": [-79.898634, 35.829845], "pop": 4165, "state": "NC", "_id": "27350"} -{"city": "STALEY", "loc": [-79.584391, 35.801485], "pop": 1747, "state": "NC", "_id": "27355"} -{"city": "STAR", "loc": [-79.782607, 35.421113], "pop": 3077, "state": "NC", "_id": "27356"} -{"city": "STOKESDALE", "loc": [-79.970528, 36.255217], "pop": 3807, "state": "NC", "_id": "27357"} -{"city": "SUMMERFIELD", "loc": [-79.890136, 36.224454], "pop": 5937, "state": "NC", "_id": "27358"} -{"city": "THOMASVILLE", "loc": [-80.09128, 35.87128], "pop": 32777, "state": "NC", "_id": "27360"} -{"city": "TRINITY", "loc": [-79.990206, 35.842949], "pop": 14290, "state": "NC", "_id": "27370"} -{"city": "TROY", "loc": [-79.909252, 35.377702], "pop": 9536, "state": "NC", "_id": "27371"} -{"city": "WEST END", "loc": [-79.53598, 35.251239], "pop": 5480, "state": "NC", "_id": "27376"} -{"city": "WHITSETT", "loc": [-79.597151, 36.032981], "pop": 2345, "state": "NC", "_id": "27377"} -{"city": "YANCEYVILLE", "loc": [-79.346485, 36.390695], "pop": 3011, "state": "NC", "_id": "27379"} -{"city": "GREENSBORO", "loc": [-79.768151, 36.069741], "pop": 19389, "state": "NC", "_id": "27401"} -{"city": "GREENSBORO", "loc": [-79.820181, 36.064147], "pop": 21666, "state": "NC", "_id": "27403"} -{"city": "GREENSBORO", "loc": [-79.7733, 36.121408], "pop": 46387, "state": "NC", "_id": "27405"} -{"city": "GREENSBORO", "loc": [-79.782058, 36.021969], "pop": 45221, "state": "NC", "_id": "27406"} -{"city": "GREENSBORO", "loc": [-79.862647, 36.033442], "pop": 29591, "state": "NC", "_id": "27407"} -{"city": "GREENSBORO", "loc": [-79.816531, 36.1064], "pop": 19970, "state": "NC", "_id": "27408"} -{"city": "GREENSBORO", "loc": [-79.908602, 36.077683], "pop": 8734, "state": "NC", "_id": "27409"} -{"city": "GREENSBORO", "loc": [-79.879365, 36.103164], "pop": 33819, "state": "NC", "_id": "27410"} -{"city": "ANGIER", "loc": [-78.724931, 35.489725], "pop": 6349, "state": "NC", "_id": "27501"} -{"city": "APEX", "loc": [-78.840816, 35.722504], "pop": 19391, "state": "NC", "_id": "27502"} -{"city": "BAHAMA", "loc": [-78.890284, 36.156581], "pop": 3276, "state": "NC", "_id": "27503"} -{"city": "BENSON", "loc": [-78.54213, 35.403661], "pop": 9995, "state": "NC", "_id": "27504"} -{"city": "BROADWAY", "loc": [-79.043547, 35.418088], "pop": 5551, "state": "NC", "_id": "27505"} -{"city": "BULLOCK", "loc": [-78.56458, 36.507642], "pop": 1423, "state": "NC", "_id": "27507"} -{"city": "BUTNER", "loc": [-78.7636, 36.135939], "pop": 6310, "state": "NC", "_id": "27509"} -{"city": "CARRBORO", "loc": [-79.081832, 35.912409], "pop": 10049, "state": "NC", "_id": "27510"} -{"city": "CARY", "loc": [-78.778568, 35.764119], "pop": 31040, "state": "NC", "_id": "27511"} -{"city": "CARY", "loc": [-78.794061, 35.795642], "pop": 15082, "state": "NC", "_id": "27513"} -{"city": "CHAPEL HILL", "loc": [-79.037189, 35.920322], "pop": 39733, "state": "NC", "_id": "27514"} -{"city": "CHAPEL HILL", "loc": [-79.099867, 35.916175], "pop": 21377, "state": "NC", "_id": "27516"} -{"city": "CLAYTON", "loc": [-78.451013, 35.63484], "pop": 15067, "state": "NC", "_id": "27520"} -{"city": "COATS", "loc": [-78.662704, 35.40822], "pop": 4437, "state": "NC", "_id": "27521"} -{"city": "CREEDMOOR", "loc": [-78.647587, 36.112433], "pop": 7620, "state": "NC", "_id": "27522"} -{"city": "FOUR OAKS", "loc": [-78.415282, 35.404017], "pop": 9270, "state": "NC", "_id": "27524"} -{"city": "FRANKLINTON", "loc": [-78.448618, 36.095505], "pop": 8242, "state": "NC", "_id": "27525"} -{"city": "FUQUAY VARINA", "loc": [-78.790807, 35.579952], "pop": 16537, "state": "NC", "_id": "27526"} -{"city": "GARNER", "loc": [-78.597527, 35.681254], "pop": 21063, "state": "NC", "_id": "27529"} -{"city": "GRANTHAM", "loc": [-78.015813, 35.382585], "pop": 37791, "state": "NC", "_id": "27530"} -{"city": "SEYMOUR JOHNSON", "loc": [-77.964312, 35.352611], "pop": 4747, "state": "NC", "_id": "27531"} -{"city": "GOLDSBORO", "loc": [-77.922069, 35.36643], "pop": 31892, "state": "NC", "_id": "27534"} -{"city": "HENDERSON", "loc": [-78.398086, 36.330068], "pop": 36983, "state": "NC", "_id": "27536"} -{"city": "HOLLY SPRINGS", "loc": [-78.845816, 35.626263], "pop": 2282, "state": "NC", "_id": "27540"} -{"city": "HURDLE MILLS", "loc": [-79.08266, 36.251788], "pop": 2773, "state": "NC", "_id": "27541"} -{"city": "KENLY", "loc": [-78.138227, 35.607742], "pop": 6729, "state": "NC", "_id": "27542"} -{"city": "KITTRELL", "loc": [-78.424073, 36.204175], "pop": 2967, "state": "NC", "_id": "27544"} -{"city": "KNIGHTDALE", "loc": [-78.489814, 35.778926], "pop": 11525, "state": "NC", "_id": "27545"} -{"city": "LILLINGTON", "loc": [-78.921223, 35.331954], "pop": 23302, "state": "NC", "_id": "27546"} -{"city": "LOUISBURG", "loc": [-78.258625, 36.05781], "pop": 20285, "state": "NC", "_id": "27549"} -{"city": "MACON", "loc": [-77.998709, 36.444697], "pop": 4044, "state": "NC", "_id": "27551"} -{"city": "MANSON", "loc": [-78.29525, 36.460264], "pop": 1976, "state": "NC", "_id": "27553"} -{"city": "MIDDLESEX", "loc": [-78.206162, 35.766499], "pop": 4318, "state": "NC", "_id": "27557"} -{"city": "MONCURE", "loc": [-79.083915, 35.630615], "pop": 2695, "state": "NC", "_id": "27559"} -{"city": "MORRISVILLE", "loc": [-78.846594, 35.834371], "pop": 3922, "state": "NC", "_id": "27560"} -{"city": "NEW HILL", "loc": [-78.936534, 35.680877], "pop": 991, "state": "NC", "_id": "27562"} -{"city": "NORLINA", "loc": [-78.189455, 36.475364], "pop": 3605, "state": "NC", "_id": "27563"} -{"city": "OXFORD", "loc": [-78.613433, 36.331269], "pop": 20568, "state": "NC", "_id": "27565"} -{"city": "PRINCETON", "loc": [-78.167368, 35.455833], "pop": 5378, "state": "NC", "_id": "27569"} -{"city": "ROLESVILLE", "loc": [-78.465843, 35.915575], "pop": 3975, "state": "NC", "_id": "27571"} -{"city": "ROUGEMONT", "loc": [-78.901867, 36.239251], "pop": 4253, "state": "NC", "_id": "27572"} -{"city": "ROXBORO", "loc": [-78.973698, 36.405902], "pop": 22321, "state": "NC", "_id": "27573"} -{"city": "SELMA", "loc": [-78.264025, 35.556531], "pop": 13335, "state": "NC", "_id": "27576"} -{"city": "SMITHFIELD", "loc": [-78.347866, 35.50684], "pop": 15031, "state": "NC", "_id": "27577"} -{"city": "STEM", "loc": [-78.70012, 36.21004], "pop": 2421, "state": "NC", "_id": "27581"} -{"city": "TIMBERLAKE", "loc": [-78.935287, 36.291763], "pop": 3424, "state": "NC", "_id": "27583"} -{"city": "WAKE FOREST", "loc": [-78.539213, 35.981544], "pop": 11878, "state": "NC", "_id": "27587"} -{"city": "WARRENTON", "loc": [-78.159389, 36.353944], "pop": 8240, "state": "NC", "_id": "27589"} -{"city": "WENDELL", "loc": [-78.392601, 35.797964], "pop": 12418, "state": "NC", "_id": "27591"} -{"city": "WILLOW SPRING", "loc": [-78.671738, 35.547031], "pop": 4672, "state": "NC", "_id": "27592"} -{"city": "YOUNGSVILLE", "loc": [-78.441198, 36.007782], "pop": 4549, "state": "NC", "_id": "27596"} -{"city": "ZEBULON", "loc": [-78.317368, 35.832078], "pop": 10153, "state": "NC", "_id": "27597"} -{"city": "RALEIGH", "loc": [-78.632439, 35.772701], "pop": 9810, "state": "NC", "_id": "27601"} -{"city": "RALEIGH", "loc": [-78.656265, 35.707569], "pop": 25366, "state": "NC", "_id": "27603"} -{"city": "RALEIGH", "loc": [-78.579949, 35.833407], "pop": 35217, "state": "NC", "_id": "27604"} -{"city": "RALEIGH", "loc": [-78.653025, 35.790795], "pop": 4169, "state": "NC", "_id": "27605"} -{"city": "RALEIGH", "loc": [-78.711189, 35.764499], "pop": 29446, "state": "NC", "_id": "27606"} -{"city": "RALEIGH", "loc": [-78.687747, 35.801385], "pop": 19515, "state": "NC", "_id": "27607"} -{"city": "RALEIGH", "loc": [-78.646277, 35.807746], "pop": 10130, "state": "NC", "_id": "27608"} -{"city": "RALEIGH", "loc": [-78.631654, 35.847989], "pop": 29872, "state": "NC", "_id": "27609"} -{"city": "RALEIGH", "loc": [-78.60076, 35.766674], "pop": 32494, "state": "NC", "_id": "27610"} -{"city": "RALEIGH", "loc": [-78.684119, 35.851997], "pop": 20962, "state": "NC", "_id": "27612"} -{"city": "RALEIGH", "loc": [-78.705059, 35.894932], "pop": 19518, "state": "NC", "_id": "27613"} -{"city": "RALEIGH", "loc": [-78.643339, 35.945711], "pop": 7200, "state": "NC", "_id": "27614"} -{"city": "RALEIGH", "loc": [-78.639277, 35.888744], "pop": 29771, "state": "NC", "_id": "27615"} -{"city": "DURHAM", "loc": [-78.896613, 35.996725], "pop": 20284, "state": "NC", "_id": "27701"} -{"city": "DURHAM", "loc": [-78.843874, 35.978122], "pop": 24362, "state": "NC", "_id": "27703"} -{"city": "DURHAM", "loc": [-78.876437, 36.038297], "pop": 23460, "state": "NC", "_id": "27704"} -{"city": "DURHAM", "loc": [-78.947776, 36.021846], "pop": 32916, "state": "NC", "_id": "27705"} -{"city": "DURHAM", "loc": [-78.937524, 36.002427], "pop": 4790, "state": "NC", "_id": "27706"} -{"city": "DURHAM", "loc": [-78.931484, 35.963076], "pop": 36264, "state": "NC", "_id": "27707"} -{"city": "DURHAM", "loc": [-78.929919, 36.091779], "pop": 15138, "state": "NC", "_id": "27712"} -{"city": "RESEARCH TRIANGL", "loc": [-78.916641, 35.916105], "pop": 19493, "state": "NC", "_id": "27713"} -{"city": "ROCKY MOUNT", "loc": [-77.760816, 35.94265], "pop": 25072, "state": "NC", "_id": "27801"} -{"city": "ROCKY MOUNT", "loc": [-77.835022, 35.923784], "pop": 20416, "state": "NC", "_id": "27803"} -{"city": "WESLEYAN COLLEGE", "loc": [-77.827543, 35.979634], "pop": 24367, "state": "NC", "_id": "27804"} -{"city": "AULANDER", "loc": [-77.114086, 36.147452], "pop": 4880, "state": "NC", "_id": "27805"} -{"city": "AURORA", "loc": [-76.799448, 35.302282], "pop": 2957, "state": "NC", "_id": "27806"} -{"city": "BAILEY", "loc": [-78.089226, 35.807167], "pop": 4399, "state": "NC", "_id": "27807"} -{"city": "BATH", "loc": [-76.771531, 35.470161], "pop": 1915, "state": "NC", "_id": "27808"} -{"city": "BATTLEBORO", "loc": [-77.431107, 35.84387], "pop": 808, "state": "NC", "_id": "27809"} -{"city": "BELHAVEN", "loc": [-76.623574, 35.520493], "pop": 4130, "state": "NC", "_id": "27810"} -{"city": "BETHEL", "loc": [-77.374765, 35.790885], "pop": 3116, "state": "NC", "_id": "27812"} -{"city": "BLOUNTS CREEK", "loc": [-76.925004, 35.382029], "pop": 1272, "state": "NC", "_id": "27814"} -{"city": "CASTALIA", "loc": [-78.070452, 36.090124], "pop": 2623, "state": "NC", "_id": "27816"} -{"city": "CHOCOWINITY", "loc": [-77.086801, 35.481409], "pop": 5553, "state": "NC", "_id": "27817"} -{"city": "COMO", "loc": [-77.051526, 36.497818], "pop": 1484, "state": "NC", "_id": "27818"} -{"city": "CONWAY", "loc": [-77.250243, 36.416382], "pop": 3474, "state": "NC", "_id": "27820"} -{"city": "EDWARD", "loc": [-76.879388, 35.323588], "pop": 122, "state": "NC", "_id": "27821"} -{"city": "ELM CITY", "loc": [-77.856032, 35.810954], "pop": 8507, "state": "NC", "_id": "27822"} -{"city": "ENFIELD", "loc": [-77.712907, 36.197331], "pop": 8876, "state": "NC", "_id": "27823"} -{"city": "MIDDLETOWN", "loc": [-76.037434, 35.49806], "pop": 1822, "state": "NC", "_id": "27824"} -{"city": "FAIRFIELD", "loc": [-76.231768, 35.565873], "pop": 585, "state": "NC", "_id": "27826"} -{"city": "FARMVILLE", "loc": [-77.579303, 35.580606], "pop": 8260, "state": "NC", "_id": "27828"} -{"city": "FOUNTAIN", "loc": [-77.621448, 35.669436], "pop": 1419, "state": "NC", "_id": "27829"} -{"city": "EUREKA", "loc": [-77.996302, 35.518558], "pop": 6803, "state": "NC", "_id": "27830"} -{"city": "GARYSBURG", "loc": [-77.571905, 36.476147], "pop": 2824, "state": "NC", "_id": "27831"} -{"city": "GASTON", "loc": [-77.682379, 36.51153], "pop": 2560, "state": "NC", "_id": "27832"} -{"city": "GREENVILLE", "loc": [-77.397542, 35.619221], "pop": 35377, "state": "NC", "_id": "27834"} -{"city": "GRIMESLAND", "loc": [-77.256612, 35.558005], "pop": 6998, "state": "NC", "_id": "27837"} -{"city": "HALIFAX", "loc": [-77.560725, 36.30492], "pop": 4264, "state": "NC", "_id": "27839"} -{"city": "HAMILTON", "loc": [-77.21762, 35.944119], "pop": 1060, "state": "NC", "_id": "27840"} -{"city": "HENRICO", "loc": [-77.854625, 36.527409], "pop": 800, "state": "NC", "_id": "27842"} -{"city": "HOBGOOD", "loc": [-77.436959, 36.004281], "pop": 2408, "state": "NC", "_id": "27843"} -{"city": "HOLLISTER", "loc": [-77.931916, 36.25898], "pop": 2691, "state": "NC", "_id": "27844"} -{"city": "JACKSON", "loc": [-77.468144, 36.386984], "pop": 2703, "state": "NC", "_id": "27845"} -{"city": "JAMESVILLE", "loc": [-76.898332, 35.78385], "pop": 3141, "state": "NC", "_id": "27846"} -{"city": "KELFORD", "loc": [-77.259214, 36.199173], "pop": 150, "state": "NC", "_id": "27847"} -{"city": "LASKER", "loc": [-77.348266, 36.376664], "pop": 80, "state": "NC", "_id": "27848"} -{"city": "LEWISTON WOODVIL", "loc": [-77.206631, 36.134416], "pop": 634, "state": "NC", "_id": "27849"} -{"city": "LITTLETON", "loc": [-77.852815, 36.416877], "pop": 4388, "state": "NC", "_id": "27850"} -{"city": "LUCAMA", "loc": [-78.019687, 35.641504], "pop": 4537, "state": "NC", "_id": "27851"} -{"city": "CRISP", "loc": [-77.596442, 35.773741], "pop": 4145, "state": "NC", "_id": "27852"} -{"city": "MARGARETTSVILLE", "loc": [-77.325997, 36.524494], "pop": 289, "state": "NC", "_id": "27853"} -{"city": "MURFREESBORO", "loc": [-77.102696, 36.43192], "pop": 6186, "state": "NC", "_id": "27855"} -{"city": "NASHVILLE", "loc": [-77.959485, 35.98428], "pop": 12069, "state": "NC", "_id": "27856"} -{"city": "OAK CITY", "loc": [-77.300362, 35.959736], "pop": 2268, "state": "NC", "_id": "27857"} -{"city": "GREENVILLE", "loc": [-77.348505, 35.586567], "pop": 31246, "state": "NC", "_id": "27858"} -{"city": "PALMYRA", "loc": [-77.364288, 36.106268], "pop": 344, "state": "NC", "_id": "27859"} -{"city": "PANTEGO", "loc": [-76.698848, 35.620058], "pop": 1915, "state": "NC", "_id": "27860"} -{"city": "PENDLETON", "loc": [-77.191112, 36.493676], "pop": 1127, "state": "NC", "_id": "27862"} -{"city": "PIKEVILLE", "loc": [-77.957787, 35.49289], "pop": 1968, "state": "NC", "_id": "27863"} -{"city": "PINETOPS", "loc": [-77.691952, 35.819611], "pop": 5935, "state": "NC", "_id": "27864"} -{"city": "PINETOWN", "loc": [-76.807565, 35.57576], "pop": 1829, "state": "NC", "_id": "27865"} -{"city": "PLEASANT HILL", "loc": [-77.519217, 36.521246], "pop": 639, "state": "NC", "_id": "27866"} -{"city": "RICH SQUARE", "loc": [-77.296947, 36.277256], "pop": 2621, "state": "NC", "_id": "27869"} -{"city": "ROANOKE RAPIDS", "loc": [-77.67306, 36.446146], "pop": 25628, "state": "NC", "_id": "27870"} -{"city": "ROBERSONVILLE", "loc": [-77.26004, 35.821797], "pop": 4941, "state": "NC", "_id": "27871"} -{"city": "ROXOBEL", "loc": [-77.227061, 36.194464], "pop": 623, "state": "NC", "_id": "27872"} -{"city": "SARATOGA", "loc": [-77.767613, 35.662778], "pop": 1870, "state": "NC", "_id": "27873"} -{"city": "SCOTLAND NECK", "loc": [-77.427274, 36.130141], "pop": 5219, "state": "NC", "_id": "27874"} -{"city": "SCRANTON", "loc": [-76.491706, 35.525039], "pop": 1184, "state": "NC", "_id": "27875"} -{"city": "SEABOARD", "loc": [-77.411306, 36.488552], "pop": 1767, "state": "NC", "_id": "27876"} -{"city": "SIMS", "loc": [-78.085854, 35.74351], "pop": 2214, "state": "NC", "_id": "27880"} -{"city": "SPRING HOPE", "loc": [-78.1085, 35.930193], "pop": 6180, "state": "NC", "_id": "27882"} -{"city": "STANTONSBURG", "loc": [-77.837773, 35.593998], "pop": 2117, "state": "NC", "_id": "27883"} -{"city": "STOKES", "loc": [-77.272178, 35.710402], "pop": 922, "state": "NC", "_id": "27884"} -{"city": "SWANQUARTER", "loc": [-76.287521, 35.422207], "pop": 1203, "state": "NC", "_id": "27885"} -{"city": "TARBORO", "loc": [-77.542067, 35.898257], "pop": 16144, "state": "NC", "_id": "27886"} -{"city": "WALSTONBURG", "loc": [-77.698336, 35.588204], "pop": 2649, "state": "NC", "_id": "27888"} -{"city": "WASHINGTON", "loc": [-77.140356, 35.588392], "pop": 865, "state": "NC", "_id": "27889"} -{"city": "WELDON", "loc": [-77.60346, 36.420643], "pop": 2819, "state": "NC", "_id": "27890"} -{"city": "WHITAKERS", "loc": [-77.716743, 36.075797], "pop": 6281, "state": "NC", "_id": "27891"} -{"city": "WILLIAMSTON", "loc": [-77.048344, 35.660154], "pop": 36648, "state": "NC", "_id": "27892"} -{"city": "WILSON", "loc": [-77.922733, 35.727022], "pop": 47823, "state": "NC", "_id": "27893"} -{"city": "GEORGE", "loc": [-77.197782, 36.320465], "pop": 2164, "state": "NC", "_id": "27897"} -{"city": "ELIZABETH CITY", "loc": [-76.244548, 36.295051], "pop": 31298, "state": "NC", "_id": "27909"} -{"city": "AHOSKIE", "loc": [-76.996632, 36.295737], "pop": 11451, "state": "NC", "_id": "27910"} -{"city": "AYDLETT", "loc": [-75.902914, 36.304469], "pop": 356, "state": "NC", "_id": "27916"} -{"city": "BARCO", "loc": [-75.979284, 36.358778], "pop": 1031, "state": "NC", "_id": "27917"} -{"city": "BELVIDERE", "loc": [-76.543586, 36.309644], "pop": 1614, "state": "NC", "_id": "27919"} -{"city": "CAMDEN", "loc": [-76.150002, 36.324985], "pop": 2439, "state": "NC", "_id": "27921"} -{"city": "COFIELD", "loc": [-76.874568, 36.333316], "pop": 661, "state": "NC", "_id": "27922"} -{"city": "COINJOCK", "loc": [-75.934012, 36.375064], "pop": 434, "state": "NC", "_id": "27923"} -{"city": "COLERAIN", "loc": [-76.854799, 36.190083], "pop": 3709, "state": "NC", "_id": "27924"} -{"city": "COLUMBIA", "loc": [-76.234481, 35.905664], "pop": 3650, "state": "NC", "_id": "27925"} -{"city": "CORAPEAKE", "loc": [-76.597852, 36.517946], "pop": 1377, "state": "NC", "_id": "27926"} -{"city": "COROLLA", "loc": [-75.813217, 36.320646], "pop": 288, "state": "NC", "_id": "27927"} -{"city": "CRESWELL", "loc": [-76.419555, 35.865303], "pop": 2287, "state": "NC", "_id": "27928"} -{"city": "CURRITUCK", "loc": [-75.988399, 36.429691], "pop": 498, "state": "NC", "_id": "27929"} -{"city": "EDENTON", "loc": [-76.622384, 36.090777], "pop": 11572, "state": "NC", "_id": "27932"} -{"city": "EURE", "loc": [-76.846341, 36.434117], "pop": 1346, "state": "NC", "_id": "27935"} -{"city": "GATES", "loc": [-76.764563, 36.503618], "pop": 2598, "state": "NC", "_id": "27937"} -{"city": "GATESVILLE", "loc": [-76.732462, 36.407171], "pop": 1398, "state": "NC", "_id": "27938"} -{"city": "GRANDY", "loc": [-75.876808, 36.233889], "pop": 908, "state": "NC", "_id": "27939"} -{"city": "HARBINGER", "loc": [-75.812143, 36.086392], "pop": 189, "state": "NC", "_id": "27941"} -{"city": "HARRELLSVILLE", "loc": [-76.776677, 36.286586], "pop": 1046, "state": "NC", "_id": "27942"} -{"city": "DURANTS NECK", "loc": [-76.424299, 36.177058], "pop": 9357, "state": "NC", "_id": "27944"} -{"city": "HOBBSVILLE", "loc": [-76.617813, 36.354303], "pop": 722, "state": "NC", "_id": "27946"} -{"city": "JARVISBURG", "loc": [-75.859469, 36.173963], "pop": 653, "state": "NC", "_id": "27947"} -{"city": "KILL DEVIL HILLS", "loc": [-75.675654, 36.008793], "pop": 7870, "state": "NC", "_id": "27948"} -{"city": "SOUTHERN SHORES", "loc": [-75.725373, 36.100694], "pop": 4046, "state": "NC", "_id": "27949"} -{"city": "KNOTTS ISLAND", "loc": [-75.970247, 36.523167], "pop": 1493, "state": "NC", "_id": "27950"} -{"city": "EAST LAKE", "loc": [-75.944404, 35.887191], "pop": 139, "state": "NC", "_id": "27953"} -{"city": "MANTEO", "loc": [-75.671418, 35.894774], "pop": 5726, "state": "NC", "_id": "27954"} -{"city": "MAPLE", "loc": [-76.003874, 36.398686], "pop": 321, "state": "NC", "_id": "27956"} -{"city": "MERRY HILL", "loc": [-76.77756, 36.087121], "pop": 1187, "state": "NC", "_id": "27957"} -{"city": "MOYOCK", "loc": [-76.114575, 36.487094], "pop": 5475, "state": "NC", "_id": "27958"} -{"city": "NAGS HEAD", "loc": [-75.567592, 35.347209], "pop": 4085, "state": "NC", "_id": "27959"} -{"city": "PLYMOUTH", "loc": [-76.743148, 35.850826], "pop": 7951, "state": "NC", "_id": "27962"} -{"city": "POINT HARBOR", "loc": [-75.798285, 36.078114], "pop": 65, "state": "NC", "_id": "27964"} -{"city": "POPLAR BRANCH", "loc": [-75.883018, 36.257028], "pop": 733, "state": "NC", "_id": "27965"} -{"city": "POWELLS POINT", "loc": [-75.827314, 36.120674], "pop": 959, "state": "NC", "_id": "27966"} -{"city": "ROPER", "loc": [-76.580918, 35.899413], "pop": 3869, "state": "NC", "_id": "27970"} -{"city": "SHAWBORO", "loc": [-76.094451, 36.377875], "pop": 333, "state": "NC", "_id": "27973"} -{"city": "SHILOH", "loc": [-76.043168, 36.258449], "pop": 1378, "state": "NC", "_id": "27974"} -{"city": "SOUTH MILLS", "loc": [-76.303284, 36.453576], "pop": 2087, "state": "NC", "_id": "27976"} -{"city": "STUMPY POINT", "loc": [-75.77909, 35.863007], "pop": 880, "state": "NC", "_id": "27978"} -{"city": "SUNBURY", "loc": [-76.609568, 36.431605], "pop": 1711, "state": "NC", "_id": "27979"} -{"city": "TYNER", "loc": [-76.642796, 36.250239], "pop": 1563, "state": "NC", "_id": "27980"} -{"city": "WINDSOR", "loc": [-76.933612, 36.015881], "pop": 9205, "state": "NC", "_id": "27983"} -{"city": "WINTON", "loc": [-76.936007, 36.382616], "pop": 1443, "state": "NC", "_id": "27986"} -{"city": "ALBEMARLE", "loc": [-80.204363, 35.357276], "pop": 26329, "state": "NC", "_id": "28001"} -{"city": "ALEXIS", "loc": [-81.102204, 35.383613], "pop": 4724, "state": "NC", "_id": "28006"} -{"city": "BELMONT", "loc": [-81.044016, 35.244029], "pop": 19504, "state": "NC", "_id": "28012"} -{"city": "BESSEMER CITY", "loc": [-81.28635, 35.284904], "pop": 8858, "state": "NC", "_id": "28016"} -{"city": "BOSTIC", "loc": [-81.81183, 35.453259], "pop": 3094, "state": "NC", "_id": "28018"} -{"city": "CASAR", "loc": [-81.635742, 35.514496], "pop": 1646, "state": "NC", "_id": "28020"} -{"city": "CHERRYVILLE", "loc": [-81.350893, 35.374742], "pop": 16445, "state": "NC", "_id": "28021"} -{"city": "CHINA GROVE", "loc": [-80.59004, 35.5669], "pop": 13040, "state": "NC", "_id": "28023"} -{"city": "CONCORD", "loc": [-80.530027, 35.371614], "pop": 15094, "state": "NC", "_id": "28025"} -{"city": "CONCORD", "loc": [-80.616227, 35.414115], "pop": 39900, "state": "NC", "_id": "28027"} -{"city": "CRAMERTON", "loc": [-81.083061, 35.23965], "pop": 2835, "state": "NC", "_id": "28032"} -{"city": "CROUSE", "loc": [-81.341851, 35.483722], "pop": 5431, "state": "NC", "_id": "28033"} -{"city": "DALLAS", "loc": [-81.1862, 35.334853], "pop": 13328, "state": "NC", "_id": "28034"} -{"city": "CORNELIUS", "loc": [-80.857229, 35.495692], "pop": 7301, "state": "NC", "_id": "28036"} -{"city": "DENVER", "loc": [-80.989785, 35.483677], "pop": 6776, "state": "NC", "_id": "28037"} -{"city": "ELLENBORO", "loc": [-81.770652, 35.334426], "pop": 8287, "state": "NC", "_id": "28040"} -{"city": "ALEXANDER MILLS", "loc": [-81.864247, 35.301753], "pop": 25940, "state": "NC", "_id": "28043"} -{"city": "GASTONIA", "loc": [-81.219449, 35.244917], "pop": 37403, "state": "NC", "_id": "28052"} -{"city": "GASTONIA", "loc": [-81.145409, 35.200537], "pop": 15997, "state": "NC", "_id": "28054"} -{"city": "GASTONIA", "loc": [-81.149622, 35.258331], "pop": 33472, "state": "NC", "_id": "28056"} -{"city": "GOLD HILL", "loc": [-80.334558, 35.549784], "pop": 977, "state": "NC", "_id": "28071"} -{"city": "GROVER", "loc": [-81.455182, 35.183639], "pop": 2011, "state": "NC", "_id": "28073"} -{"city": "HARRISBURG", "loc": [-80.659401, 35.324731], "pop": 8810, "state": "NC", "_id": "28075"} -{"city": "CORNELIUS", "loc": [-80.864664, 35.421992], "pop": 15259, "state": "NC", "_id": "28078"} -{"city": "INDIAN TRAIL", "loc": [-80.659743, 35.08307], "pop": 13274, "state": "NC", "_id": "28079"} -{"city": "IRON STATION", "loc": [-81.107009, 35.465654], "pop": 9616, "state": "NC", "_id": "28080"} -{"city": "KANNAPOLIS", "loc": [-80.635875, 35.502016], "pop": 25583, "state": "NC", "_id": "28081"} -{"city": "KANNAPOLIS", "loc": [-80.601536, 35.484807], "pop": 14113, "state": "NC", "_id": "28083"} -{"city": "KINGS MOUNTAIN", "loc": [-81.380567, 35.251578], "pop": 21012, "state": "NC", "_id": "28086"} -{"city": "LANDIS", "loc": [-80.612926, 35.5435], "pop": 2651, "state": "NC", "_id": "28088"} -{"city": "LAWNDALE", "loc": [-81.533625, 35.444932], "pop": 6842, "state": "NC", "_id": "28090"} -{"city": "LILESVILLE", "loc": [-79.97208, 34.968914], "pop": 2923, "state": "NC", "_id": "28091"} -{"city": "BOGER CITY", "loc": [-81.228039, 35.482138], "pop": 24325, "state": "NC", "_id": "28092"} -{"city": "LOCUST", "loc": [-80.421061, 35.270416], "pop": 3077, "state": "NC", "_id": "28097"} -{"city": "LOWELL", "loc": [-81.096037, 35.265481], "pop": 3424, "state": "NC", "_id": "28098"} -{"city": "MARSHVILLE", "loc": [-80.378113, 35.016684], "pop": 7900, "state": "NC", "_id": "28103"} -{"city": "STALLINGS", "loc": [-80.713568, 35.121879], "pop": 18586, "state": "NC", "_id": "28105"} -{"city": "MIDLAND", "loc": [-80.531853, 35.247724], "pop": 4012, "state": "NC", "_id": "28107"} -{"city": "MONROE", "loc": [-80.53723, 35.017775], "pop": 34880, "state": "NC", "_id": "28110"} -{"city": "MONROE", "loc": [-80.553952, 34.894621], "pop": 9986, "state": "NC", "_id": "28112"} -{"city": "MOORESBORO", "loc": [-81.672171, 35.248389], "pop": 7554, "state": "NC", "_id": "28114"} -{"city": "MOORESVILLE", "loc": [-80.822629, 35.577358], "pop": 28805, "state": "NC", "_id": "28115"} -{"city": "MORVEN", "loc": [-80.002529, 34.85106], "pop": 1808, "state": "NC", "_id": "28119"} -{"city": "MOUNT HOLLY", "loc": [-81.030567, 35.311872], "pop": 14505, "state": "NC", "_id": "28120"} -{"city": "MOUNT PLEASANT", "loc": [-80.417081, 35.414576], "pop": 4640, "state": "NC", "_id": "28124"} -{"city": "MOUNT ULLA", "loc": [-80.72386, 35.638934], "pop": 683, "state": "NC", "_id": "28125"} -{"city": "NEW LONDON", "loc": [-80.205737, 35.428518], "pop": 5156, "state": "NC", "_id": "28127"} -{"city": "NORWOOD", "loc": [-80.14326, 35.227493], "pop": 7069, "state": "NC", "_id": "28128"} -{"city": "OAKBORO", "loc": [-80.341272, 35.245997], "pop": 6336, "state": "NC", "_id": "28129"} -{"city": "PEACHLAND", "loc": [-80.282943, 35.005434], "pop": 3310, "state": "NC", "_id": "28133"} -{"city": "PINEVILLE", "loc": [-80.885852, 35.070942], "pop": 4457, "state": "NC", "_id": "28134"} -{"city": "POLKTON", "loc": [-80.153812, 34.982288], "pop": 6679, "state": "NC", "_id": "28135"} -{"city": "RICHFIELD", "loc": [-80.267135, 35.511035], "pop": 2307, "state": "NC", "_id": "28137"} -{"city": "ROCKWELL", "loc": [-80.422568, 35.549437], "pop": 8739, "state": "NC", "_id": "28138"} -{"city": "RUTHERFORDTON", "loc": [-81.978107, 35.37058], "pop": 10091, "state": "NC", "_id": "28139"} -{"city": "SALISBURY", "loc": [-80.488945, 35.651498], "pop": 34563, "state": "NC", "_id": "28144"} -{"city": "SALISBURY", "loc": [-80.46569, 35.667564], "pop": 23261, "state": "NC", "_id": "28146"} -{"city": "KINGSTOWN", "loc": [-81.581573, 35.347075], "pop": 13326, "state": "NC", "_id": "28150"} -{"city": "SHELBY", "loc": [-81.533673, 35.268889], "pop": 31162, "state": "NC", "_id": "28152"} -{"city": "SPENCER", "loc": [-80.431049, 35.695312], "pop": 4083, "state": "NC", "_id": "28159"} -{"city": "SPINDALE", "loc": [-81.925142, 35.360131], "pop": 4017, "state": "NC", "_id": "28160"} -{"city": "STANFIELD", "loc": [-80.440672, 35.21061], "pop": 2436, "state": "NC", "_id": "28163"} -{"city": "STANLEY", "loc": [-81.095921, 35.351565], "pop": 6510, "state": "NC", "_id": "28164"} -{"city": "TROUTMAN", "loc": [-80.882229, 35.686271], "pop": 5013, "state": "NC", "_id": "28166"} -{"city": "UNION MILLS", "loc": [-81.96846, 35.47324], "pop": 3731, "state": "NC", "_id": "28167"} -{"city": "VALE", "loc": [-81.458887, 35.518796], "pop": 3422, "state": "NC", "_id": "28168"} -{"city": "WADESBORO", "loc": [-80.069586, 34.980938], "pop": 9765, "state": "NC", "_id": "28170"} -{"city": "WEDDINGTON", "loc": [-80.727901, 34.955334], "pop": 14423, "state": "NC", "_id": "28173"} -{"city": "WINGATE", "loc": [-80.447595, 34.984666], "pop": 2747, "state": "NC", "_id": "28174"} -{"city": "CHARLOTTE", "loc": [-80.841864, 35.229002], "pop": 5143, "state": "NC", "_id": "28202"} -{"city": "CHARLOTTE", "loc": [-80.858279, 35.208139], "pop": 10274, "state": "NC", "_id": "28203"} -{"city": "CHARLOTTE", "loc": [-80.823149, 35.213178], "pop": 6114, "state": "NC", "_id": "28204"} -{"city": "CHARLOTTE", "loc": [-80.788129, 35.219951], "pop": 44092, "state": "NC", "_id": "28205"} -{"city": "CHARLOTTE", "loc": [-80.826505, 35.252173], "pop": 13051, "state": "NC", "_id": "28206"} -{"city": "CHARLOTTE", "loc": [-80.827248, 35.193474], "pop": 7921, "state": "NC", "_id": "28207"} -{"city": "CHARLOTTE", "loc": [-80.896352, 35.235795], "pop": 38236, "state": "NC", "_id": "28208"} -{"city": "CHARLOTTE", "loc": [-80.855926, 35.179629], "pop": 18190, "state": "NC", "_id": "28209"} -{"city": "CHARLOTTE", "loc": [-80.857749, 35.131586], "pop": 35211, "state": "NC", "_id": "28210"} -{"city": "CHARLOTTE", "loc": [-80.793244, 35.167653], "pop": 25478, "state": "NC", "_id": "28211"} -{"city": "CHARLOTTE", "loc": [-80.744777, 35.190797], "pop": 30347, "state": "NC", "_id": "28212"} -{"city": "CHARLOTTE", "loc": [-80.750079, 35.317868], "pop": 20336, "state": "NC", "_id": "28213"} -{"city": "CHARLOTTE", "loc": [-80.95709, 35.273095], "pop": 16852, "state": "NC", "_id": "28214"} -{"city": "CHARLOTTE", "loc": [-80.738669, 35.243962], "pop": 27936, "state": "NC", "_id": "28215"} -{"city": "CHARLOTTE", "loc": [-80.870216, 35.283377], "pop": 22464, "state": "NC", "_id": "28216"} -{"city": "CHARLOTTE", "loc": [-81.007848, 35.0972], "pop": 2795, "state": "NC", "_id": "28217"} -{"city": "CHARLOTTE", "loc": [-80.816675, 35.086856], "pop": 41260, "state": "NC", "_id": "28226"} -{"city": "CHARLOTTE", "loc": [-80.684634, 35.193612], "pop": 33273, "state": "NC", "_id": "28227"} -{"city": "CHARLOTTE", "loc": [-80.775958, 35.272506], "pop": 15114, "state": "NC", "_id": "28262"} -{"city": "CHARLOTTE", "loc": [-80.820941, 35.288635], "pop": 6659, "state": "NC", "_id": "28269"} -{"city": "CHARLOTTE", "loc": [-80.766872, 35.135473], "pop": 13791, "state": "NC", "_id": "28270"} -{"city": "CHARLOTTE", "loc": [-80.896673, 35.159646], "pop": 19148, "state": "NC", "_id": "28273"} -{"city": "CHARLOTTE", "loc": [-80.800174, 35.134486], "pop": 6737, "state": "NC", "_id": "28277"} -{"city": "CHARLOTTE", "loc": [-80.960421, 35.146685], "pop": 5411, "state": "NC", "_id": "28278"} -{"city": "EAST FAYETTEVILL", "loc": [-78.842255, 35.05099], "pop": 35253, "state": "NC", "_id": "28301"} -{"city": "BONNIE DOONE", "loc": [-78.960135, 35.084046], "pop": 35745, "state": "NC", "_id": "28303"} -{"city": "FAYETTEVILLE", "loc": [-78.970494, 35.025683], "pop": 33868, "state": "NC", "_id": "28304"} -{"city": "FAYETTEVILLE", "loc": [-78.904658, 35.056022], "pop": 6670, "state": "NC", "_id": "28305"} -{"city": "FAYETTEVILLE", "loc": [-78.936408, 35.001874], "pop": 20122, "state": "NC", "_id": "28306"} -{"city": "FORT BRAGG", "loc": [-79.002457, 35.141649], "pop": 37688, "state": "NC", "_id": "28307"} -{"city": "FAYETTEVILLE", "loc": [-78.898217, 35.129416], "pop": 29497, "state": "NC", "_id": "28311"} -{"city": "FAYETTEVILLE", "loc": [-79.007985, 35.058322], "pop": 34856, "state": "NC", "_id": "28314"} -{"city": "ABERDEEN", "loc": [-79.445039, 35.121641], "pop": 7767, "state": "NC", "_id": "28315"} -{"city": "AUTRYVILLE", "loc": [-78.602111, 35.099673], "pop": 4129, "state": "NC", "_id": "28318"} -{"city": "BLADENBORO", "loc": [-78.779295, 34.565832], "pop": 7957, "state": "NC", "_id": "28320"} -{"city": "BUNNLEVEL", "loc": [-78.855189, 35.281057], "pop": 4749, "state": "NC", "_id": "28323"} -{"city": "JOHNSONVILLE", "loc": [-79.268826, 35.313581], "pop": 1851, "state": "NC", "_id": "28326"} -{"city": "CARTHAGE", "loc": [-79.396939, 35.306066], "pop": 11774, "state": "NC", "_id": "28327"} -{"city": "CLINTON", "loc": [-78.326007, 35.015143], "pop": 17737, "state": "NC", "_id": "28328"} -{"city": "DUDLEY", "loc": [-78.027274, 35.292564], "pop": 8450, "state": "NC", "_id": "28333"} -{"city": "DUNN", "loc": [-78.615079, 35.316511], "pop": 15795, "state": "NC", "_id": "28334"} -{"city": "ELIZABETHTOWN", "loc": [-78.574693, 34.64714], "pop": 10444, "state": "NC", "_id": "28337"} -{"city": "ELLERBE", "loc": [-79.752361, 35.091422], "pop": 4245, "state": "NC", "_id": "28338"} -{"city": "ERWIN", "loc": [-78.685935, 35.328651], "pop": 5574, "state": "NC", "_id": "28339"} -{"city": "MCDONALD", "loc": [-79.128596, 34.481402], "pop": 10978, "state": "NC", "_id": "28340"} -{"city": "FAISON", "loc": [-78.117983, 35.119884], "pop": 2658, "state": "NC", "_id": "28341"} -{"city": "GIBSON", "loc": [-79.583854, 34.754857], "pop": 1638, "state": "NC", "_id": "28343"} -{"city": "GODWIN", "loc": [-78.662472, 35.196919], "pop": 1886, "state": "NC", "_id": "28344"} -{"city": "HAMLET", "loc": [-79.702217, 34.889375], "pop": 13443, "state": "NC", "_id": "28345"} -{"city": "HOFFMAN", "loc": [-79.560027, 35.032607], "pop": 1073, "state": "NC", "_id": "28347"} -{"city": "HOPE MILLS", "loc": [-78.935364, 34.953564], "pop": 18396, "state": "NC", "_id": "28348"} -{"city": "KENANSVILLE", "loc": [-77.967743, 35.039621], "pop": 4998, "state": "NC", "_id": "28349"} -{"city": "LAUREL HILL", "loc": [-79.549135, 34.823831], "pop": 5726, "state": "NC", "_id": "28351"} -{"city": "LAURINBURG", "loc": [-79.467316, 34.759869], "pop": 23387, "state": "NC", "_id": "28352"} -{"city": "LINDEN", "loc": [-78.800361, 35.227645], "pop": 2903, "state": "NC", "_id": "28356"} -{"city": "LUMBER BRIDGE", "loc": [-79.066417, 34.876192], "pop": 1343, "state": "NC", "_id": "28357"} -{"city": "LUMBERTON", "loc": [-79.008309, 34.629301], "pop": 42871, "state": "NC", "_id": "28358"} -{"city": "MARSTON", "loc": [-79.659554, 34.989628], "pop": 963, "state": "NC", "_id": "28363"} -{"city": "MAXTON", "loc": [-79.309725, 34.733435], "pop": 11494, "state": "NC", "_id": "28364"} -{"city": "MOUNT OLIVE", "loc": [-78.09834, 35.210923], "pop": 8636, "state": "NC", "_id": "28365"} -{"city": "NEWTON GROVE", "loc": [-78.426037, 35.221258], "pop": 6775, "state": "NC", "_id": "28366"} -{"city": "ORRUM", "loc": [-79.031037, 34.447347], "pop": 1915, "state": "NC", "_id": "28369"} -{"city": "PARKTON", "loc": [-78.996943, 34.900569], "pop": 2195, "state": "NC", "_id": "28371"} -{"city": "PEMBROKE", "loc": [-79.18337, 34.690198], "pop": 10673, "state": "NC", "_id": "28372"} -{"city": "PINEHURST", "loc": [-79.473194, 35.188408], "pop": 5803, "state": "NC", "_id": "28374"} -{"city": "RAEFORD", "loc": [-79.22276, 34.989009], "pop": 20742, "state": "NC", "_id": "28376"} -{"city": "RED SPRINGS", "loc": [-79.163619, 34.808315], "pop": 8683, "state": "NC", "_id": "28377"} -{"city": "ROCKINGHAM", "loc": [-79.766566, 34.933613], "pop": 24282, "state": "NC", "_id": "28379"} -{"city": "ROSEBORO", "loc": [-78.504109, 34.994081], "pop": 6495, "state": "NC", "_id": "28382"} -{"city": "ROWLAND", "loc": [-79.261843, 34.588664], "pop": 7047, "state": "NC", "_id": "28383"} -{"city": "SAINT PAULS", "loc": [-78.973077, 34.800962], "pop": 7976, "state": "NC", "_id": "28384"} -{"city": "SALEMBURG", "loc": [-78.471385, 35.051459], "pop": 2821, "state": "NC", "_id": "28385"} -{"city": "SHANNON", "loc": [-79.180617, 34.898762], "pop": 2120, "state": "NC", "_id": "28386"} -{"city": "SOUTHERN PINES", "loc": [-79.395682, 35.169747], "pop": 11523, "state": "NC", "_id": "28387"} -{"city": "SPRING LAKE", "loc": [-78.978555, 35.182981], "pop": 11537, "state": "NC", "_id": "28390"} -{"city": "STEDMAN", "loc": [-78.694932, 35.034749], "pop": 4776, "state": "NC", "_id": "28391"} -{"city": "TAR HEEL", "loc": [-78.81341, 34.746541], "pop": 1989, "state": "NC", "_id": "28392"} -{"city": "TURKEY", "loc": [-78.212086, 34.985673], "pop": 3224, "state": "NC", "_id": "28393"} -{"city": "VASS", "loc": [-79.256189, 35.217133], "pop": 3932, "state": "NC", "_id": "28394"} -{"city": "WADE", "loc": [-78.724929, 35.160559], "pop": 1369, "state": "NC", "_id": "28395"} -{"city": "WAGRAM", "loc": [-79.39594, 34.904432], "pop": 3007, "state": "NC", "_id": "28396"} -{"city": "BOWDENS", "loc": [-78.086739, 34.99818], "pop": 5817, "state": "NC", "_id": "28398"} -{"city": "WHITE OAK", "loc": [-78.73014, 34.766206], "pop": 1292, "state": "NC", "_id": "28399"} -{"city": "CAPE FEAR", "loc": [-77.937856, 34.225304], "pop": 21522, "state": "NC", "_id": "28401"} -{"city": "WILMINGTON", "loc": [-77.886213, 34.223653], "pop": 25319, "state": "NC", "_id": "28403"} -{"city": "OGDEN", "loc": [-77.852937, 34.264065], "pop": 26744, "state": "NC", "_id": "28405"} -{"city": "WILMINGTON", "loc": [-77.87227, 34.166256], "pop": 17418, "state": "NC", "_id": "28409"} -{"city": "WILMINGTON", "loc": [-77.914137, 34.157173], "pop": 13932, "state": "NC", "_id": "28412"} -{"city": "ASH", "loc": [-78.505637, 34.065871], "pop": 2212, "state": "NC", "_id": "28420"} -{"city": "ATKINSON", "loc": [-78.167108, 34.530445], "pop": 1418, "state": "NC", "_id": "28421"} -{"city": "BOLIVIA", "loc": [-78.16814, 34.025962], "pop": 3392, "state": "NC", "_id": "28422"} -{"city": "BOLTON", "loc": [-78.337177, 34.309085], "pop": 3071, "state": "NC", "_id": "28423"} -{"city": "BURGAW", "loc": [-77.940317, 34.548679], "pop": 6405, "state": "NC", "_id": "28425"} -{"city": "CAROLINA BEACH", "loc": [-77.896289, 34.036599], "pop": 4524, "state": "NC", "_id": "28428"} -{"city": "CASTLE HAYNE", "loc": [-77.91085, 34.323596], "pop": 7329, "state": "NC", "_id": "28429"} -{"city": "CERRO GORDO", "loc": [-78.921571, 34.302483], "pop": 1742, "state": "NC", "_id": "28430"} -{"city": "CHADBOURN", "loc": [-78.826683, 34.322303], "pop": 2015, "state": "NC", "_id": "28431"} -{"city": "CLARENDON", "loc": [-78.788844, 34.199517], "pop": 3940, "state": "NC", "_id": "28432"} -{"city": "CLARKTON", "loc": [-78.631271, 34.503011], "pop": 3493, "state": "NC", "_id": "28433"} -{"city": "COUNCIL", "loc": [-78.411511, 34.429042], "pop": 2768, "state": "NC", "_id": "28434"} -{"city": "CURRIE", "loc": [-78.092516, 34.449668], "pop": 2094, "state": "NC", "_id": "28435"} -{"city": "DELCO", "loc": [-78.19169, 34.327419], "pop": 229, "state": "NC", "_id": "28436"} -{"city": "EVERGREEN", "loc": [-78.884638, 34.375234], "pop": 3906, "state": "NC", "_id": "28438"} -{"city": "FAIR BLUFF", "loc": [-79.017502, 34.302275], "pop": 1931, "state": "NC", "_id": "28439"} -{"city": "GARLAND", "loc": [-78.34142, 34.822906], "pop": 4133, "state": "NC", "_id": "28441"} -{"city": "HALLSBORO", "loc": [-78.604272, 34.318087], "pop": 2551, "state": "NC", "_id": "28442"} -{"city": "HAMPSTEAD", "loc": [-77.662808, 34.3879], "pop": 8159, "state": "NC", "_id": "28443"} -{"city": "HARRELLS", "loc": [-78.242973, 34.676918], "pop": 1635, "state": "NC", "_id": "28444"} -{"city": "SURF CITY", "loc": [-77.51005, 34.4644], "pop": 1279, "state": "NC", "_id": "28445"} -{"city": "IVANHOE", "loc": [-78.162333, 34.697169], "pop": 352, "state": "NC", "_id": "28447"} -{"city": "KELLY", "loc": [-78.294161, 34.459064], "pop": 723, "state": "NC", "_id": "28448"} -{"city": "KURE BEACH", "loc": [-77.909875, 33.992707], "pop": 568, "state": "NC", "_id": "28449"} -{"city": "LAKE WACCAMAW", "loc": [-78.510208, 34.339359], "pop": 1941, "state": "NC", "_id": "28450"} -{"city": "LELAND", "loc": [-78.057815, 34.267952], "pop": 7803, "state": "NC", "_id": "28451"} -{"city": "LONGWOOD", "loc": [-78.531531, 33.950059], "pop": 1913, "state": "NC", "_id": "28452"} -{"city": "MAGNOLIA", "loc": [-78.043213, 34.895702], "pop": 2056, "state": "NC", "_id": "28453"} -{"city": "MAPLE HILL", "loc": [-77.736588, 34.617753], "pop": 2095, "state": "NC", "_id": "28454"} -{"city": "NAKINA", "loc": [-78.657005, 34.115293], "pop": 1581, "state": "NC", "_id": "28455"} -{"city": "RIEGELWOOD", "loc": [-78.257473, 34.34706], "pop": 2038, "state": "NC", "_id": "28456"} -{"city": "ROCKY POINT", "loc": [-77.923448, 34.434418], "pop": 4657, "state": "NC", "_id": "28457"} -{"city": "ROSE HILL", "loc": [-78.016624, 34.823462], "pop": 4421, "state": "NC", "_id": "28458"} -{"city": "SHALLOTTE", "loc": [-78.41068, 33.943011], "pop": 6537, "state": "NC", "_id": "28459"} -{"city": "SNEADS FERRY", "loc": [-77.403801, 34.542589], "pop": 4586, "state": "NC", "_id": "28460"} -{"city": "BOILING SPRING L", "loc": [-78.045551, 34.012137], "pop": 8878, "state": "NC", "_id": "28461"} -{"city": "HOLDEN BEACH", "loc": [-78.296089, 33.962504], "pop": 7513, "state": "NC", "_id": "28462"} -{"city": "TABOR CITY", "loc": [-78.823178, 34.123314], "pop": 6573, "state": "NC", "_id": "28463"} -{"city": "TEACHEY", "loc": [-78.022091, 34.770036], "pop": 1461, "state": "NC", "_id": "28464"} -{"city": "OAK ISLAND", "loc": [-78.125455, 33.916122], "pop": 4752, "state": "NC", "_id": "28465"} -{"city": "WALLACE", "loc": [-77.942922, 34.754166], "pop": 7328, "state": "NC", "_id": "28466"} -{"city": "CALABASH", "loc": [-78.574406, 33.904668], "pop": 3061, "state": "NC", "_id": "28467"} -{"city": "SUNSET BEACH", "loc": [-78.519955, 33.883569], "pop": 1347, "state": "NC", "_id": "28468"} -{"city": "OCEAN ISLE BEACH", "loc": [-78.429849, 33.891271], "pop": 493, "state": "NC", "_id": "28469"} -{"city": "WATHA", "loc": [-78.007351, 34.620725], "pop": 1327, "state": "NC", "_id": "28471"} -{"city": "WHITEVILLE", "loc": [-78.716048, 34.324142], "pop": 18066, "state": "NC", "_id": "28472"} -{"city": "WILLARD", "loc": [-78.023445, 34.684451], "pop": 2456, "state": "NC", "_id": "28478"} -{"city": "WINNABOW", "loc": [-78.056211, 34.214511], "pop": 3084, "state": "NC", "_id": "28479"} -{"city": "WRIGHTSVILLE BEA", "loc": [-77.798166, 34.212228], "pop": 2928, "state": "NC", "_id": "28480"} -{"city": "KINSTON", "loc": [-77.585969, 35.278333], "pop": 44135, "state": "NC", "_id": "28501"} -{"city": "ALBERTSON", "loc": [-77.851517, 35.117647], "pop": 2644, "state": "NC", "_id": "28508"} -{"city": "ARAPAHOE", "loc": [-76.814909, 35.0055], "pop": 1378, "state": "NC", "_id": "28510"} -{"city": "ATLANTIC", "loc": [-76.352097, 34.888827], "pop": 808, "state": "NC", "_id": "28511"} -{"city": "PINE KNOLL SHORE", "loc": [-76.815163, 34.697295], "pop": 1441, "state": "NC", "_id": "28512"} -{"city": "AYDEN", "loc": [-77.405127, 35.456471], "pop": 8831, "state": "NC", "_id": "28513"} -{"city": "BAYBORO", "loc": [-76.751793, 35.152598], "pop": 1853, "state": "NC", "_id": "28515"} -{"city": "BEAUFORT", "loc": [-76.622834, 34.758037], "pop": 10606, "state": "NC", "_id": "28516"} -{"city": "BEULAVILLE", "loc": [-77.769655, 34.933962], "pop": 7278, "state": "NC", "_id": "28518"} -{"city": "CEDAR ISLAND", "loc": [-76.08049, 35.074881], "pop": 1029, "state": "NC", "_id": "28520"} -{"city": "CHINQUAPIN", "loc": [-77.763573, 34.827609], "pop": 1334, "state": "NC", "_id": "28521"} -{"city": "COVE CITY", "loc": [-77.296306, 35.202274], "pop": 2007, "state": "NC", "_id": "28523"} -{"city": "DEEP RUN", "loc": [-77.69275, 35.162991], "pop": 4252, "state": "NC", "_id": "28525"} -{"city": "DOVER", "loc": [-77.372688, 35.262652], "pop": 2198, "state": "NC", "_id": "28526"} -{"city": "ERNUL", "loc": [-77.050164, 35.254693], "pop": 303, "state": "NC", "_id": "28527"} -{"city": "GLOUCESTER", "loc": [-76.527627, 34.685645], "pop": 0, "state": "NC", "_id": "28528"} -{"city": "GRANTSBORO", "loc": [-76.884387, 35.122186], "pop": 3485, "state": "NC", "_id": "28529"} -{"city": "GRIFTON", "loc": [-77.4193, 35.375681], "pop": 3029, "state": "NC", "_id": "28530"} -{"city": "HARKERS ISLAND", "loc": [-76.558301, 34.69663], "pop": 1761, "state": "NC", "_id": "28531"} -{"city": "HAVELOCK", "loc": [-76.890042, 34.896753], "pop": 25957, "state": "NC", "_id": "28532"} -{"city": "HOBUCKEN", "loc": [-76.569602, 35.251838], "pop": 323, "state": "NC", "_id": "28537"} -{"city": "HOOKERTON", "loc": [-77.565555, 35.437976], "pop": 2112, "state": "NC", "_id": "28538"} -{"city": "HUBERT", "loc": [-77.207928, 34.69929], "pop": 8527, "state": "NC", "_id": "28539"} -{"city": "JACKSONVILLE", "loc": [-77.462815, 34.737456], "pop": 52792, "state": "NC", "_id": "28540"} -{"city": "CAMP LEJEUNE", "loc": [-77.3373, 34.665806], "pop": 23717, "state": "NC", "_id": "28542"} -{"city": "TARAWA TERRACE", "loc": [-77.383114, 34.73542], "pop": 11054, "state": "NC", "_id": "28543"} -{"city": "MIDWAY PARK", "loc": [-77.320001, 34.726994], "pop": 6799, "state": "NC", "_id": "28544"} -{"city": "JACKSONVILLE", "loc": [-77.378097, 34.77401], "pop": 27976, "state": "NC", "_id": "28546"} -{"city": "LA GRANGE", "loc": [-77.76862, 35.305381], "pop": 6686, "state": "NC", "_id": "28551"} -{"city": "LOWLAND", "loc": [-76.5777, 35.305955], "pop": 367, "state": "NC", "_id": "28552"} -{"city": "MARSHALLBERG", "loc": [-76.517323, 34.726472], "pop": 565, "state": "NC", "_id": "28553"} -{"city": "MAYSVILLE", "loc": [-77.231456, 34.869077], "pop": 3899, "state": "NC", "_id": "28555"} -{"city": "MERRITT", "loc": [-76.699401, 35.1228], "pop": 1146, "state": "NC", "_id": "28556"} -{"city": "MOREHEAD CITY", "loc": [-76.753069, 34.72532], "pop": 13985, "state": "NC", "_id": "28557"} -{"city": "NEW BERN", "loc": [-77.031945, 35.101941], "pop": 24585, "state": "NC", "_id": "28560"} -{"city": "NEW BERN", "loc": [-77.102874, 35.100434], "pop": 20936, "state": "NC", "_id": "28562"} -{"city": "NEWPORT", "loc": [-76.906945, 34.755076], "pop": 18841, "state": "NC", "_id": "28570"} -{"city": "ORIENTAL", "loc": [-76.701521, 35.036406], "pop": 1985, "state": "NC", "_id": "28571"} -{"city": "PINK HILL", "loc": [-77.712148, 35.066351], "pop": 2201, "state": "NC", "_id": "28572"} -{"city": "POLLOCKSVILLE", "loc": [-77.228727, 35.015105], "pop": 2406, "state": "NC", "_id": "28573"} -{"city": "RICHLANDS", "loc": [-77.586305, 34.862426], "pop": 8868, "state": "NC", "_id": "28574"} -{"city": "SEALEVEL", "loc": [-76.389778, 34.876949], "pop": 521, "state": "NC", "_id": "28577"} -{"city": "SEVEN SPRINGS", "loc": [-77.914621, 35.210466], "pop": 2228, "state": "NC", "_id": "28578"} -{"city": "SMYRNA", "loc": [-76.515313, 34.773384], "pop": 651, "state": "NC", "_id": "28579"} -{"city": "SNOW HILL", "loc": [-77.695565, 35.443848], "pop": 9637, "state": "NC", "_id": "28580"} -{"city": "STACY", "loc": [-76.428877, 34.84124], "pop": 264, "state": "NC", "_id": "28581"} -{"city": "STELLA", "loc": [-77.130807, 34.777672], "pop": 365, "state": "NC", "_id": "28582"} -{"city": "SWANSBORO", "loc": [-77.135013, 34.699066], "pop": 2535, "state": "NC", "_id": "28584"} -{"city": "TRENTON", "loc": [-77.459473, 35.074481], "pop": 5058, "state": "NC", "_id": "28585"} -{"city": "VANCEBORO", "loc": [-77.171618, 35.306255], "pop": 5627, "state": "NC", "_id": "28586"} -{"city": "VANDEMERE", "loc": [-76.657088, 35.195298], "pop": 835, "state": "NC", "_id": "28587"} -{"city": "WINTERVILLE", "loc": [-77.39097, 35.533582], "pop": 8382, "state": "NC", "_id": "28590"} -{"city": "EMERALD ISLE", "loc": [-77.025961, 34.666195], "pop": 2432, "state": "NC", "_id": "28594"} -{"city": "HICKORY", "loc": [-81.328858, 35.75757], "pop": 44977, "state": "NC", "_id": "28601"} -{"city": "HICKORY", "loc": [-81.361229, 35.68837], "pop": 21020, "state": "NC", "_id": "28602"} -{"city": "BANNER ELK", "loc": [-81.841194, 36.170461], "pop": 4570, "state": "NC", "_id": "28604"} -{"city": "BLOWING ROCK", "loc": [-81.750968, 36.094594], "pop": 2372, "state": "NC", "_id": "28605"} -{"city": "BOOMER", "loc": [-81.313704, 36.055192], "pop": 2146, "state": "NC", "_id": "28606"} -{"city": "BOONE", "loc": [-81.666025, 36.214237], "pop": 24897, "state": "NC", "_id": "28607"} -{"city": "CATAWBA", "loc": [-81.050307, 35.675708], "pop": 1767, "state": "NC", "_id": "28609"} -{"city": "CLAREMONT", "loc": [-81.129672, 35.721053], "pop": 8902, "state": "NC", "_id": "28610"} -{"city": "COLLETTSVILLE", "loc": [-81.674188, 35.951946], "pop": 2121, "state": "NC", "_id": "28611"} -{"city": "CONNELLYS SPRING", "loc": [-81.492958, 35.706972], "pop": 15391, "state": "NC", "_id": "28612"} -{"city": "CONOVER", "loc": [-81.216455, 35.731343], "pop": 15222, "state": "NC", "_id": "28613"} -{"city": "CRESTON", "loc": [-81.650624, 36.449959], "pop": 2527, "state": "NC", "_id": "28615"} -{"city": "CRUMPLER", "loc": [-81.403886, 36.464057], "pop": 2532, "state": "NC", "_id": "28617"} -{"city": "DEEP GAP", "loc": [-81.516265, 36.213573], "pop": 1176, "state": "NC", "_id": "28618"} -{"city": "ELKIN", "loc": [-80.855365, 36.28723], "pop": 10672, "state": "NC", "_id": "28621"} -{"city": "ELK PARK", "loc": [-81.963882, 36.164623], "pop": 3223, "state": "NC", "_id": "28622"} -{"city": "ENNICE", "loc": [-80.977141, 36.525278], "pop": 1509, "state": "NC", "_id": "28623"} -{"city": "FERGUSON", "loc": [-81.386407, 36.128316], "pop": 1551, "state": "NC", "_id": "28624"} -{"city": "FLEETWOOD", "loc": [-81.514008, 36.281382], "pop": 1693, "state": "NC", "_id": "28626"} -{"city": "GLADE VALLEY", "loc": [-81.016782, 36.442889], "pop": 1115, "state": "NC", "_id": "28627"} -{"city": "GRANITE FALLS", "loc": [-81.457145, 35.819663], "pop": 17749, "state": "NC", "_id": "28630"} -{"city": "GRASSY CREEK", "loc": [-81.446751, 36.541524], "pop": 837, "state": "NC", "_id": "28631"} -{"city": "HARMONY", "loc": [-80.758462, 35.957975], "pop": 3881, "state": "NC", "_id": "28634"} -{"city": "HAYS", "loc": [-81.116116, 36.310015], "pop": 1085, "state": "NC", "_id": "28635"} -{"city": "HIDDENITE", "loc": [-81.048663, 35.95045], "pop": 1703, "state": "NC", "_id": "28636"} -{"city": "HUDSON", "loc": [-81.489747, 35.840295], "pop": 9771, "state": "NC", "_id": "28638"} -{"city": "JEFFERSON", "loc": [-81.439626, 36.408987], "pop": 3080, "state": "NC", "_id": "28640"} -{"city": "JONESVILLE", "loc": [-80.787029, 36.228571], "pop": 7105, "state": "NC", "_id": "28642"} -{"city": "LANSING", "loc": [-81.526925, 36.517641], "pop": 3211, "state": "NC", "_id": "28643"} -{"city": "LAUREL SPRINGS", "loc": [-81.26061, 36.444897], "pop": 1837, "state": "NC", "_id": "28644"} -{"city": "LENOIR", "loc": [-81.539793, 35.914935], "pop": 39525, "state": "NC", "_id": "28645"} -{"city": "LONGISLAND", "loc": [-80.990403, 35.665814], "pop": 279, "state": "NC", "_id": "28648"} -{"city": "MC GRADY", "loc": [-81.191207, 36.310345], "pop": 1261, "state": "NC", "_id": "28649"} -{"city": "MAIDEN", "loc": [-81.174492, 35.575884], "pop": 7388, "state": "NC", "_id": "28650"} -{"city": "MILLERS CREEK", "loc": [-81.248533, 36.211949], "pop": 7018, "state": "NC", "_id": "28651"} -{"city": "MORAVIAN FALLS", "loc": [-81.178073, 36.078762], "pop": 3071, "state": "NC", "_id": "28654"} -{"city": "MORGANTON", "loc": [-81.704216, 35.73458], "pop": 50932, "state": "NC", "_id": "28655"} -{"city": "FRANK", "loc": [-81.952276, 36.040203], "pop": 8906, "state": "NC", "_id": "28657"} -{"city": "NEWTON", "loc": [-81.242546, 35.649766], "pop": 20759, "state": "NC", "_id": "28658"} -{"city": "NORTH WILKESBORO", "loc": [-81.128603, 36.20174], "pop": 20167, "state": "NC", "_id": "28659"} -{"city": "OLIN", "loc": [-80.851084, 35.959333], "pop": 723, "state": "NC", "_id": "28660"} -{"city": "PURLEAR", "loc": [-81.352773, 36.196391], "pop": 1102, "state": "NC", "_id": "28665"} -{"city": "ROARING GAP", "loc": [-81.018781, 36.383553], "pop": 21, "state": "NC", "_id": "28668"} -{"city": "ROARING RIVER", "loc": [-81.000373, 36.191561], "pop": 978, "state": "NC", "_id": "28669"} -{"city": "RONDA", "loc": [-80.926964, 36.20594], "pop": 2739, "state": "NC", "_id": "28670"} -{"city": "SHERRILLS FORD", "loc": [-81.033859, 35.596244], "pop": 5567, "state": "NC", "_id": "28673"} -{"city": "SPARTA", "loc": [-81.138442, 36.508851], "pop": 5746, "state": "NC", "_id": "28675"} -{"city": "STATE ROAD", "loc": [-80.865294, 36.34218], "pop": 3080, "state": "NC", "_id": "28676"} -{"city": "STATESVILLE", "loc": [-80.894009, 35.799022], "pop": 52895, "state": "NC", "_id": "28677"} -{"city": "STONY POINT", "loc": [-81.064135, 35.866109], "pop": 5212, "state": "NC", "_id": "28678"} -{"city": "SUGAR GROVE", "loc": [-81.844094, 36.262672], "pop": 1631, "state": "NC", "_id": "28679"} -{"city": "TAYLORSVILLE", "loc": [-81.212429, 35.901046], "pop": 19679, "state": "NC", "_id": "28681"} -{"city": "TERRELL", "loc": [-80.963064, 35.583587], "pop": 440, "state": "NC", "_id": "28682"} -{"city": "THURMOND", "loc": [-80.931674, 36.356188], "pop": 556, "state": "NC", "_id": "28683"} -{"city": "TODD", "loc": [-81.587403, 36.324527], "pop": 1039, "state": "NC", "_id": "28684"} -{"city": "TRAPHILL", "loc": [-81.015126, 36.330097], "pop": 1781, "state": "NC", "_id": "28685"} -{"city": "TRIPLETT", "loc": [-81.489649, 36.181685], "pop": 64, "state": "NC", "_id": "28686"} -{"city": "UNION GROVE", "loc": [-80.896695, 36.036947], "pop": 2264, "state": "NC", "_id": "28689"} -{"city": "VALDESE", "loc": [-81.56696, 35.744739], "pop": 4227, "state": "NC", "_id": "28690"} -{"city": "VALLE CRUCIS", "loc": [-81.880563, 36.207041], "pop": 238, "state": "NC", "_id": "28691"} -{"city": "VILAS", "loc": [-81.765203, 36.257375], "pop": 3022, "state": "NC", "_id": "28692"} -{"city": "WARRENSVILLE", "loc": [-81.546522, 36.45723], "pop": 994, "state": "NC", "_id": "28693"} -{"city": "WEST JEFFERSON", "loc": [-81.487218, 36.377648], "pop": 6348, "state": "NC", "_id": "28694"} -{"city": "WILKESBORO", "loc": [-81.157292, 36.135857], "pop": 11889, "state": "NC", "_id": "28697"} -{"city": "ZIONVILLE", "loc": [-81.747567, 36.319437], "pop": 1633, "state": "NC", "_id": "28698"} -{"city": "ALEXANDER", "loc": [-82.631134, 35.706394], "pop": 2960, "state": "NC", "_id": "28701"} -{"city": "ALMOND", "loc": [-83.578406, 35.3295], "pop": 678, "state": "NC", "_id": "28702"} -{"city": "AQUONE", "loc": [-83.566218, 35.240254], "pop": 1423, "state": "NC", "_id": "28703"} -{"city": "ARDEN", "loc": [-82.535372, 35.463666], "pop": 11386, "state": "NC", "_id": "28704"} -{"city": "BAKERSVILLE", "loc": [-82.171133, 36.028588], "pop": 6862, "state": "NC", "_id": "28705"} -{"city": "BALSAM GROVE", "loc": [-82.87795, 35.229751], "pop": 342, "state": "NC", "_id": "28708"} -{"city": "BARNARDSVILLE", "loc": [-82.456682, 35.77483], "pop": 2757, "state": "NC", "_id": "28709"} -{"city": "BLACK MOUNTAIN S", "loc": [-82.325087, 35.612494], "pop": 11914, "state": "NC", "_id": "28711"} -{"city": "BREVARD", "loc": [-82.740444, 35.22076], "pop": 14212, "state": "NC", "_id": "28712"} -{"city": "BRYSON CITY", "loc": [-83.439246, 35.424128], "pop": 7248, "state": "NC", "_id": "28713"} -{"city": "BURNSVILLE", "loc": [-82.287623, 35.902974], "pop": 13735, "state": "NC", "_id": "28714"} -{"city": "CANDLER", "loc": [-82.700081, 35.537626], "pop": 15823, "state": "NC", "_id": "28715"} -{"city": "CANTON", "loc": [-82.841291, 35.512651], "pop": 14331, "state": "NC", "_id": "28716"} -{"city": "CASHIERS", "loc": [-83.087074, 35.097117], "pop": 1099, "state": "NC", "_id": "28717"} -{"city": "CHEROKEE", "loc": [-83.31444, 35.50937], "pop": 3339, "state": "NC", "_id": "28719"} -{"city": "CLYDE", "loc": [-82.921582, 35.559654], "pop": 7400, "state": "NC", "_id": "28721"} -{"city": "COLUMBUS", "loc": [-82.120631, 35.241031], "pop": 5976, "state": "NC", "_id": "28722"} -{"city": "CULLOWHEE", "loc": [-83.147522, 35.240876], "pop": 2886, "state": "NC", "_id": "28723"} -{"city": "EAST FLAT ROCK", "loc": [-82.420423, 35.279868], "pop": 3770, "state": "NC", "_id": "28726"} -{"city": "ETOWAH", "loc": [-82.597705, 35.317192], "pop": 2461, "state": "NC", "_id": "28729"} -{"city": "FAIRVIEW", "loc": [-82.398534, 35.525759], "pop": 5156, "state": "NC", "_id": "28730"} -{"city": "FLAT ROCK", "loc": [-82.391568, 35.288993], "pop": 4240, "state": "NC", "_id": "28731"} -{"city": "FLETCHER", "loc": [-82.496559, 35.44989], "pop": 7201, "state": "NC", "_id": "28732"} -{"city": "FONTANA DAM", "loc": [-83.817631, 35.428187], "pop": 187, "state": "NC", "_id": "28733"} -{"city": "FRANKLIN", "loc": [-83.388479, 35.180984], "pop": 16689, "state": "NC", "_id": "28734"} -{"city": "GERTON", "loc": [-82.306204, 35.468723], "pop": 280, "state": "NC", "_id": "28735"} -{"city": "GLENVILLE", "loc": [-83.090033, 35.188164], "pop": 125, "state": "NC", "_id": "28736"} -{"city": "HAZELWOOD", "loc": [-83.004284, 35.476877], "pop": 1759, "state": "NC", "_id": "28738"} -{"city": "HENDERSONVILLE", "loc": [-82.499995, 35.319213], "pop": 26125, "state": "NC", "_id": "28739"} -{"city": "GREENMOUNTAIN", "loc": [-82.287867, 35.995619], "pop": 1471, "state": "NC", "_id": "28740"} -{"city": "HIGHLANDS", "loc": [-83.216044, 35.070546], "pop": 2685, "state": "NC", "_id": "28741"} -{"city": "HORSE SHOE", "loc": [-82.598128, 35.370267], "pop": 4069, "state": "NC", "_id": "28742"} -{"city": "HOT SPRINGS", "loc": [-82.812011, 35.816175], "pop": 3595, "state": "NC", "_id": "28743"} -{"city": "LAKE JUNALUSKA", "loc": [-82.970235, 35.525916], "pop": 539, "state": "NC", "_id": "28745"} -{"city": "LAKE LURE", "loc": [-82.175203, 35.446447], "pop": 1843, "state": "NC", "_id": "28746"} -{"city": "LAKE TOXAWAY", "loc": [-82.919081, 35.145052], "pop": 1849, "state": "NC", "_id": "28747"} -{"city": "LEICESTER", "loc": [-82.710622, 35.649781], "pop": 7709, "state": "NC", "_id": "28748"} -{"city": "MAGGIE VALLEY", "loc": [-83.092928, 35.52006], "pop": 1989, "state": "NC", "_id": "28751"} -{"city": "MARION", "loc": [-82.017993, 35.681916], "pop": 24988, "state": "NC", "_id": "28752"} -{"city": "WALNUT", "loc": [-82.656577, 35.856074], "pop": 7623, "state": "NC", "_id": "28753"} -{"city": "MARS HILL", "loc": [-82.525352, 35.852825], "pop": 5949, "state": "NC", "_id": "28754"} -{"city": "MILL SPRING", "loc": [-82.155733, 35.333792], "pop": 3075, "state": "NC", "_id": "28756"} -{"city": "NEBO", "loc": [-81.905581, 35.673158], "pop": 5018, "state": "NC", "_id": "28761"} -{"city": "OLD FORT", "loc": [-82.168621, 35.616948], "pop": 5594, "state": "NC", "_id": "28762"} -{"city": "OTTO", "loc": [-83.384755, 35.062668], "pop": 2297, "state": "NC", "_id": "28763"} -{"city": "PENROSE", "loc": [-82.622239, 35.252407], "pop": 653, "state": "NC", "_id": "28766"} -{"city": "PISGAH FOREST", "loc": [-82.669516, 35.259931], "pop": 5623, "state": "NC", "_id": "28768"} -{"city": "ROBBINSVILLE", "loc": [-83.788775, 35.325932], "pop": 6879, "state": "NC", "_id": "28771"} -{"city": "ROSMAN", "loc": [-82.818916, 35.119695], "pop": 2779, "state": "NC", "_id": "28772"} -{"city": "SALUDA", "loc": [-82.330595, 35.238341], "pop": 1451, "state": "NC", "_id": "28773"} -{"city": "SAPPHIRE", "loc": [-83.001924, 35.066578], "pop": 62, "state": "NC", "_id": "28774"} -{"city": "SCALY MOUNTAIN", "loc": [-83.311338, 35.024036], "pop": 405, "state": "NC", "_id": "28775"} -{"city": "SPRUCE PINE", "loc": [-82.070492, 35.905971], "pop": 7570, "state": "NC", "_id": "28777"} -{"city": "WARREN WILSON CO", "loc": [-82.40649, 35.604843], "pop": 5911, "state": "NC", "_id": "28778"} -{"city": "SYLVA", "loc": [-83.203054, 35.348055], "pop": 16275, "state": "NC", "_id": "28779"} -{"city": "TAPOCO", "loc": [-83.905415, 35.442023], "pop": 130, "state": "NC", "_id": "28780"} -{"city": "TOPTON", "loc": [-83.745117, 35.230604], "pop": 481, "state": "NC", "_id": "28781"} -{"city": "TRYON", "loc": [-82.23942, 35.215703], "pop": 3914, "state": "NC", "_id": "28782"} -{"city": "TUCKASEGEE", "loc": [-83.074864, 35.259934], "pop": 1096, "state": "NC", "_id": "28783"} -{"city": "WAYNESVILLE", "loc": [-82.991346, 35.501767], "pop": 20924, "state": "NC", "_id": "28786"} -{"city": "WEAVERVILLE", "loc": [-82.549109, 35.712642], "pop": 11884, "state": "NC", "_id": "28787"} -{"city": "WHITTIER", "loc": [-83.287239, 35.446934], "pop": 5368, "state": "NC", "_id": "28789"} -{"city": "ZIRCONIA", "loc": [-82.457368, 35.215291], "pop": 2350, "state": "NC", "_id": "28790"} -{"city": "HENDERSONVILLE", "loc": [-82.426443, 35.361342], "pop": 21037, "state": "NC", "_id": "28792"} -{"city": "ASHEVILLE", "loc": [-82.556533, 35.597075], "pop": 13316, "state": "NC", "_id": "28801"} -{"city": "ASHEVILLE", "loc": [-82.518021, 35.539291], "pop": 20904, "state": "NC", "_id": "28803"} -{"city": "ASHEVILLE", "loc": [-82.564625, 35.63743], "pop": 16709, "state": "NC", "_id": "28804"} -{"city": "ASHEVILLE", "loc": [-82.491781, 35.600363], "pop": 15335, "state": "NC", "_id": "28805"} -{"city": "ASHEVILLE", "loc": [-82.607787, 35.580814], "pop": 30809, "state": "NC", "_id": "28806"} -{"city": "ANDREWS", "loc": [-83.822836, 35.195948], "pop": 4469, "state": "NC", "_id": "28901"} -{"city": "BRASSTOWN", "loc": [-83.966773, 35.031392], "pop": 1390, "state": "NC", "_id": "28902"} -{"city": "HAYESVILLE", "loc": [-83.78668, 35.04172], "pop": 5965, "state": "NC", "_id": "28904"} -{"city": "MARBLE", "loc": [-83.938065, 35.14748], "pop": 3230, "state": "NC", "_id": "28905"} -{"city": "UNAKA", "loc": [-84.101454, 35.079228], "pop": 11333, "state": "NC", "_id": "28906"} -{"city": "WARNE", "loc": [-83.904541, 35.002437], "pop": 457, "state": "NC", "_id": "28909"} -{"city": "ABSARAKA", "loc": [-97.388769, 47.014359], "pop": 124, "state": "ND", "_id": "58002"} -{"city": "ALICE", "loc": [-97.531819, 46.768892], "pop": 255, "state": "ND", "_id": "58003"} -{"city": "AMENIA", "loc": [-97.204808, 47.019395], "pop": 321, "state": "ND", "_id": "58004"} -{"city": "ARGUSVILLE", "loc": [-96.905764, 47.10664], "pop": 353, "state": "ND", "_id": "58005"} -{"city": "ARTHUR", "loc": [-97.209737, 47.104774], "pop": 543, "state": "ND", "_id": "58006"} -{"city": "AYR", "loc": [-97.572841, 47.019576], "pop": 187, "state": "ND", "_id": "58007"} -{"city": "BARNEY", "loc": [-96.970772, 46.249621], "pop": 222, "state": "ND", "_id": "58008"} -{"city": "BLANCHARD", "loc": [-97.262079, 47.353664], "pop": 144, "state": "ND", "_id": "58009"} -{"city": "BUFFALO", "loc": [-97.535159, 46.926351], "pop": 281, "state": "ND", "_id": "58011"} -{"city": "CASSELTON", "loc": [-97.213821, 46.899195], "pop": 1838, "state": "ND", "_id": "58012"} -{"city": "CAYUGA", "loc": [-97.405941, 46.132589], "pop": 442, "state": "ND", "_id": "58013"} -{"city": "CHAFFEE", "loc": [-97.357753, 46.766477], "pop": 189, "state": "ND", "_id": "58014"} -{"city": "CHRISTINE", "loc": [-96.790882, 46.552157], "pop": 377, "state": "ND", "_id": "58015"} -{"city": "CLIFFORD", "loc": [-97.409961, 47.356946], "pop": 128, "state": "ND", "_id": "58016"} -{"city": "BRAMPTON", "loc": [-97.809543, 46.089106], "pop": 631, "state": "ND", "_id": "58017"} -{"city": "COLFAX", "loc": [-96.979001, 46.497153], "pop": 414, "state": "ND", "_id": "58018"} -{"city": "DAVENPORT", "loc": [-97.087073, 46.696811], "pop": 349, "state": "ND", "_id": "58021"} -{"city": "ENDERLIN", "loc": [-97.610554, 46.607898], "pop": 1732, "state": "ND", "_id": "58027"} -{"city": "ERIE", "loc": [-97.384851, 47.112803], "pop": 135, "state": "ND", "_id": "58029"} -{"city": "FAIRMOUNT", "loc": [-96.630811, 46.042658], "pop": 780, "state": "ND", "_id": "58030"} -{"city": "FINGAL", "loc": [-97.781198, 46.772754], "pop": 349, "state": "ND", "_id": "58031"} -{"city": "FORMAN", "loc": [-97.698643, 46.072075], "pop": 72, "state": "ND", "_id": "58032"} -{"city": "ENGLEVALE", "loc": [-97.905482, 46.478188], "pop": 764, "state": "ND", "_id": "58033"} -{"city": "GALESBURG", "loc": [-97.374599, 47.27779], "pop": 344, "state": "ND", "_id": "58035"} -{"city": "GARDNER", "loc": [-96.990116, 47.1252], "pop": 200, "state": "ND", "_id": "58036"} -{"city": "GRANDIN", "loc": [-97.020676, 47.215769], "pop": 365, "state": "ND", "_id": "58038"} -{"city": "GREAT BEND", "loc": [-96.817143, 46.154369], "pop": 235, "state": "ND", "_id": "58039"} -{"city": "CRETE", "loc": [-97.866853, 46.230455], "pop": 243, "state": "ND", "_id": "58040"} -{"city": "HANKINSON", "loc": [-96.899069, 46.055797], "pop": 1546, "state": "ND", "_id": "58041"} -{"city": "PROSPER", "loc": [-96.900973, 46.996497], "pop": 1045, "state": "ND", "_id": "58042"} -{"city": "HAVANA", "loc": [-97.60945, 45.964413], "pop": 272, "state": "ND", "_id": "58043"} -{"city": "KELSO", "loc": [-97.056024, 47.394168], "pop": 2398, "state": "ND", "_id": "58045"} -{"city": "COLGATE", "loc": [-97.784865, 47.19995], "pop": 90, "state": "ND", "_id": "58046"} -{"city": "HICKSON", "loc": [-96.860941, 46.739267], "pop": 2056, "state": "ND", "_id": "58047"} -{"city": "HUNTER", "loc": [-97.251648, 47.19132], "pop": 512, "state": "ND", "_id": "58048"} -{"city": "HASTINGS", "loc": [-97.999791, 46.705548], "pop": 355, "state": "ND", "_id": "58049"} -{"city": "KINDRED", "loc": [-97.004919, 46.654296], "pop": 907, "state": "ND", "_id": "58051"} -{"city": "LEONARD", "loc": [-97.272488, 46.659324], "pop": 554, "state": "ND", "_id": "58052"} -{"city": "GENESEO", "loc": [-97.144429, 46.073885], "pop": 1494, "state": "ND", "_id": "58053"} -{"city": "ELLIOTT", "loc": [-97.658895, 46.420133], "pop": 3078, "state": "ND", "_id": "58054"} -{"city": "LUVERNE", "loc": [-97.729622, 47.315052], "pop": 893, "state": "ND", "_id": "58056"} -{"city": "MCLEOD", "loc": [-97.313892, 46.41027], "pop": 70, "state": "ND", "_id": "58057"} -{"city": "MANTADOR", "loc": [-96.957004, 46.159935], "pop": 239, "state": "ND", "_id": "58058"} -{"city": "DURBIN", "loc": [-97.051366, 46.867546], "pop": 1665, "state": "ND", "_id": "58059"} -{"city": "DELAMERE", "loc": [-97.433724, 46.255074], "pop": 927, "state": "ND", "_id": "58060"} -{"city": "MOORETON", "loc": [-96.850726, 46.261537], "pop": 313, "state": "ND", "_id": "58061"} -{"city": "NOME", "loc": [-97.791099, 46.674834], "pop": 284, "state": "ND", "_id": "58062"} -{"city": "ORISKA", "loc": [-97.785331, 46.943251], "pop": 332, "state": "ND", "_id": "58063"} -{"city": "PAGE", "loc": [-97.596665, 47.151512], "pop": 586, "state": "ND", "_id": "58064"} -{"city": "RUTLAND", "loc": [-97.548284, 46.073813], "pop": 1166, "state": "ND", "_id": "58067"} -{"city": "SHELDON", "loc": [-97.454261, 46.554784], "pop": 546, "state": "ND", "_id": "58068"} -{"city": "STIRUM", "loc": [-97.654233, 46.229891], "pop": 776, "state": "ND", "_id": "58069"} -{"city": "TOWER CITY", "loc": [-97.659392, 46.911873], "pop": 417, "state": "ND", "_id": "58071"} -{"city": "VALLEY CITY", "loc": [-98.003316, 46.92681], "pop": 8633, "state": "ND", "_id": "58072"} -{"city": "DWIGHT", "loc": [-96.633934, 46.279842], "pop": 10683, "state": "ND", "_id": "58075"} -{"city": "WALCOTT", "loc": [-97.001442, 46.583529], "pop": 740, "state": "ND", "_id": "58077"} -{"city": "RIVERSIDE", "loc": [-96.895002, 46.869523], "pop": 12422, "state": "ND", "_id": "58078"} -{"city": "EMBDEN", "loc": [-97.391694, 46.875015], "pop": 367, "state": "ND", "_id": "58079"} -{"city": "WYNDMERE", "loc": [-97.128912, 46.289137], "pop": 1127, "state": "ND", "_id": "58081"} -{"city": "NORTH RIVER", "loc": [-96.793577, 46.900878], "pop": 33408, "state": "ND", "_id": "58102"} -{"city": "FARGO", "loc": [-96.812252, 46.856406], "pop": 38483, "state": "ND", "_id": "58103"} -{"city": "BRIARWOOD", "loc": [-96.823846, 46.81492], "pop": 5170, "state": "ND", "_id": "58104"} -{"city": "GRAND FORKS", "loc": [-97.04463, 47.901041], "pop": 31138, "state": "ND", "_id": "58201"} -{"city": "GRAND FORKS", "loc": [-97.067156, 47.927217], "pop": 19056, "state": "ND", "_id": "58203"} -{"city": "GRAND FORKS", "loc": [-97.370802, 47.959499], "pop": 9333, "state": "ND", "_id": "58205"} -{"city": "ADAMS", "loc": [-98.086741, 48.422299], "pop": 312, "state": "ND", "_id": "58210"} -{"city": "ANETA", "loc": [-97.981406, 47.699262], "pop": 426, "state": "ND", "_id": "58212"} -{"city": "ARDOCH", "loc": [-97.253534, 48.226911], "pop": 355, "state": "ND", "_id": "58213"} -{"city": "ARVILLA", "loc": [-97.487124, 47.909292], "pop": 529, "state": "ND", "_id": "58214"} -{"city": "BATHGATE", "loc": [-97.483215, 48.868632], "pop": 169, "state": "ND", "_id": "58216"} -{"city": "BUXTON", "loc": [-97.089259, 47.616314], "pop": 952, "state": "ND", "_id": "58218"} -{"city": "CALEDONIA", "loc": [-96.908476, 47.457645], "pop": 154, "state": "ND", "_id": "58219"} -{"city": "CONCRETE", "loc": [-97.657191, 48.794341], "pop": 2758, "state": "ND", "_id": "58220"} -{"city": "CRYSTAL", "loc": [-97.673846, 48.592387], "pop": 384, "state": "ND", "_id": "58222"} -{"city": "CUMMINGS", "loc": [-96.991287, 47.537023], "pop": 286, "state": "ND", "_id": "58223"} -{"city": "DAHLEN", "loc": [-97.957269, 48.159896], "pop": 103, "state": "ND", "_id": "58224"} -{"city": "BOWESMONT", "loc": [-97.199222, 48.578569], "pop": 1233, "state": "ND", "_id": "58225"} -{"city": "GARDAR", "loc": [-97.890188, 48.490164], "pop": 859, "state": "ND", "_id": "58227"} -{"city": "EMERADO", "loc": [-97.263012, 47.921376], "pop": 1690, "state": "ND", "_id": "58228"} -{"city": "FAIRDALE", "loc": [-98.206587, 48.481924], "pop": 274, "state": "ND", "_id": "58229"} -{"city": "FINLEY", "loc": [-97.744242, 47.530611], "pop": 1403, "state": "ND", "_id": "58230"} -{"city": "FORDVILLE", "loc": [-97.802238, 48.22119], "pop": 377, "state": "ND", "_id": "58231"} -{"city": "FOREST RIVER", "loc": [-97.460476, 48.225094], "pop": 284, "state": "ND", "_id": "58233"} -{"city": "HONEYFORD", "loc": [-97.463299, 48.077082], "pop": 395, "state": "ND", "_id": "58235"} -{"city": "NASH", "loc": [-97.415895, 48.410783], "pop": 6591, "state": "ND", "_id": "58237"} -{"city": "HAMILTON", "loc": [-97.469302, 48.794249], "pop": 135, "state": "ND", "_id": "58238"} -{"city": "HANNAH", "loc": [-98.723693, 48.959446], "pop": 141, "state": "ND", "_id": "58239"} -{"city": "HATTON", "loc": [-97.432548, 47.638012], "pop": 1081, "state": "ND", "_id": "58240"} -{"city": "HENSEL", "loc": [-97.596223, 48.673836], "pop": 121, "state": "ND", "_id": "58241"} -{"city": "HOOPLE", "loc": [-97.618333, 48.51981], "pop": 555, "state": "ND", "_id": "58243"} -{"city": "ORR", "loc": [-97.681302, 48.118654], "pop": 730, "state": "ND", "_id": "58244"} -{"city": "58245", "loc": [-97.454697, 48.143406], "pop": 109, "state": "ND", "_id": "58245"} -{"city": "58246", "loc": [-97.215983, 48.793789], "pop": 118, "state": "ND", "_id": "58246"} -{"city": "LANGDON", "loc": [-98.35662, 48.786562], "pop": 3701, "state": "ND", "_id": "58249"} -{"city": "LANKIN", "loc": [-98.006997, 48.295233], "pop": 796, "state": "ND", "_id": "58250"} -{"city": "MCCANNA", "loc": [-97.651792, 47.904048], "pop": 2023, "state": "ND", "_id": "58251"} -{"city": "KLOTEN", "loc": [-98.162999, 47.778981], "pop": 925, "state": "ND", "_id": "58254"} -{"city": "MAIDA", "loc": [-98.394826, 48.949043], "pop": 104, "state": "ND", "_id": "58255"} -{"city": "MANVEL", "loc": [-97.194332, 48.085314], "pop": 1033, "state": "ND", "_id": "58256"} -{"city": "MAYVILLE", "loc": [-97.317645, 47.50138], "pop": 2378, "state": "ND", "_id": "58257"} -{"city": "MEKINOCK", "loc": [-97.489593, 47.980909], "pop": 398, "state": "ND", "_id": "58258"} -{"city": "WHITMAN", "loc": [-98.124243, 48.04144], "pop": 624, "state": "ND", "_id": "58259"} -{"city": "MILTON", "loc": [-98.018285, 48.616963], "pop": 248, "state": "ND", "_id": "58260"} -{"city": "VOSS", "loc": [-97.333727, 48.298874], "pop": 851, "state": "ND", "_id": "58261"} -{"city": "MOUNTAIN", "loc": [-97.807569, 48.677341], "pop": 435, "state": "ND", "_id": "58262"} -{"city": "58264", "loc": [-97.471905, 48.497773], "pop": 204, "state": "ND", "_id": "58264"} -{"city": "NECHE", "loc": [-97.542687, 48.979567], "pop": 500, "state": "ND", "_id": "58265"} -{"city": "NIAGARA", "loc": [-97.833431, 47.984817], "pop": 164, "state": "ND", "_id": "58266"} -{"city": "KEMPTON", "loc": [-97.563054, 47.741756], "pop": 1910, "state": "ND", "_id": "58267"} -{"city": "OSNABROCK", "loc": [-98.23271, 48.641908], "pop": 620, "state": "ND", "_id": "58269"} -{"city": "PARK RIVER", "loc": [-97.743897, 48.403881], "pop": 2139, "state": "ND", "_id": "58270"} -{"city": "JOLIETTE", "loc": [-97.275611, 48.936124], "pop": 907, "state": "ND", "_id": "58271"} -{"city": "PETERSBURG", "loc": [-97.984033, 47.997973], "pop": 404, "state": "ND", "_id": "58272"} -{"city": "PISEK", "loc": [-97.702894, 48.297096], "pop": 350, "state": "ND", "_id": "58273"} -{"city": "PORTLAND", "loc": [-97.384303, 47.501542], "pop": 887, "state": "ND", "_id": "58274"} -{"city": "REYNOLDS", "loc": [-97.209153, 47.706776], "pop": 470, "state": "ND", "_id": "58275"} -{"city": "SAINT THOMAS", "loc": [-97.454508, 48.625223], "pop": 607, "state": "ND", "_id": "58276"} -{"city": "SHARON", "loc": [-97.905091, 47.606188], "pop": 163, "state": "ND", "_id": "58277"} -{"city": "THOMPSON", "loc": [-97.096201, 47.77658], "pop": 1705, "state": "ND", "_id": "58278"} -{"city": "WALES", "loc": [-98.559649, 48.921647], "pop": 122, "state": "ND", "_id": "58281"} -{"city": "BACKOO", "loc": [-97.881927, 48.91103], "pop": 1759, "state": "ND", "_id": "58282"} -{"city": "DEVILS LAKE", "loc": [-98.861588, 48.113162], "pop": 10324, "state": "ND", "_id": "58301"} -{"city": "LOMA", "loc": [-98.622545, 48.636847], "pop": 272, "state": "ND", "_id": "58311"} -{"city": "BARTON", "loc": [-100.204429, 48.461084], "pop": 140, "state": "ND", "_id": "58315"} -{"city": "BELCOURT", "loc": [-99.768754, 48.837862], "pop": 5415, "state": "ND", "_id": "58316"} -{"city": "BISBEE", "loc": [-99.363592, 48.555196], "pop": 507, "state": "ND", "_id": "58317"} -{"city": "BOTTINEAU", "loc": [-100.432894, 48.845137], "pop": 4356, "state": "ND", "_id": "58318"} -{"city": "BREMEN", "loc": [-99.371564, 47.727212], "pop": 108, "state": "ND", "_id": "58319"} -{"city": "BRINSMADE", "loc": [-99.312384, 48.156805], "pop": 168, "state": "ND", "_id": "58320"} -{"city": "BROCKET", "loc": [-98.355854, 48.225527], "pop": 131, "state": "ND", "_id": "58321"} -{"city": "CALVIN", "loc": [-98.924236, 48.80843], "pop": 117, "state": "ND", "_id": "58323"} -{"city": "MAZA", "loc": [-99.194553, 48.485479], "pop": 1800, "state": "ND", "_id": "58324"} -{"city": "CHURCHS FERRY", "loc": [-99.141187, 48.286206], "pop": 206, "state": "ND", "_id": "58325"} -{"city": "SOUTHAM", "loc": [-98.621236, 48.053028], "pop": 272, "state": "ND", "_id": "58327"} -{"city": "DOYON", "loc": [-98.506757, 48.081067], "pop": 121, "state": "ND", "_id": "58328"} -{"city": "SAN HAVEN", "loc": [-100.031105, 48.84801], "pop": 3092, "state": "ND", "_id": "58329"} -{"city": "EDMORE", "loc": [-98.445756, 48.43084], "pop": 572, "state": "ND", "_id": "58330"} -{"city": "EGELAND", "loc": [-99.111475, 48.635537], "pop": 297, "state": "ND", "_id": "58331"} -{"city": "FILLMORE", "loc": [-99.703388, 48.072022], "pop": 563, "state": "ND", "_id": "58332"} -{"city": "HAMBERG", "loc": [-99.468297, 47.779372], "pop": 213, "state": "ND", "_id": "58337"} -{"city": "HAMPDEN", "loc": [-98.654316, 48.522892], "pop": 148, "state": "ND", "_id": "58338"} -{"city": "HANSBORO", "loc": [-99.383259, 48.86648], "pop": 390, "state": "ND", "_id": "58339"} -{"city": "MANFRED", "loc": [-99.932941, 47.768121], "pop": 2769, "state": "ND", "_id": "58341"} -{"city": "HEIMDAL", "loc": [-99.689147, 47.799918], "pop": 180, "state": "ND", "_id": "58342"} -{"city": "KNOX", "loc": [-99.671274, 48.338812], "pop": 100, "state": "ND", "_id": "58343"} -{"city": "MAPES", "loc": [-98.341548, 48.035714], "pop": 1327, "state": "ND", "_id": "58344"} -{"city": "LAWTON", "loc": [-98.414199, 48.30326], "pop": 178, "state": "ND", "_id": "58345"} -{"city": "HARLOW", "loc": [-99.440123, 48.26681], "pop": 980, "state": "ND", "_id": "58346"} -{"city": "FLORA", "loc": [-99.543061, 47.95074], "pop": 1045, "state": "ND", "_id": "58348"} -{"city": "MINNEWAUKAN", "loc": [-99.274323, 48.069777], "pop": 554, "state": "ND", "_id": "58351"} -{"city": "CALIO", "loc": [-98.842646, 48.654605], "pop": 572, "state": "ND", "_id": "58352"} -{"city": "MYLO", "loc": [-99.632878, 48.635981], "pop": 292, "state": "ND", "_id": "58353"} -{"city": "BRANTFORD", "loc": [-99.078007, 47.679385], "pop": 2309, "state": "ND", "_id": "58356"} -{"city": "OBERON", "loc": [-99.140129, 47.948744], "pop": 556, "state": "ND", "_id": "58357"} -{"city": "OVERLY", "loc": [-100.175958, 48.679029], "pop": 61, "state": "ND", "_id": "58360"} -{"city": "PEKIN", "loc": [-98.326035, 47.769558], "pop": 229, "state": "ND", "_id": "58361"} -{"city": "PENN", "loc": [-99.065509, 48.227106], "pop": 165, "state": "ND", "_id": "58362"} -{"city": "PERTH", "loc": [-99.383076, 48.681601], "pop": 125, "state": "ND", "_id": "58363"} -{"city": "ROCKLAKE", "loc": [-99.179628, 48.821569], "pop": 508, "state": "ND", "_id": "58365"} -{"city": "NANSON", "loc": [-99.874375, 48.655224], "pop": 1125, "state": "ND", "_id": "58366"} -{"city": "ROLLA", "loc": [-99.613386, 48.859423], "pop": 1645, "state": "ND", "_id": "58367"} -{"city": "PLEASANT LAKE", "loc": [-99.998954, 48.317264], "pop": 4592, "state": "ND", "_id": "58368"} -{"city": "SAINT JOHN", "loc": [-99.76476, 48.936588], "pop": 1203, "state": "ND", "_id": "58369"} -{"city": "SAINT MICHAEL", "loc": [-98.918058, 47.977363], "pop": 2860, "state": "ND", "_id": "58370"} -{"city": "SARLES", "loc": [-98.960562, 48.945921], "pop": 172, "state": "ND", "_id": "58372"} -{"city": "58373", "loc": [-99.882826, 47.868454], "pop": 97, "state": "ND", "_id": "58373"} -{"city": "SHEYENNE", "loc": [-99.058326, 47.818386], "pop": 551, "state": "ND", "_id": "58374"} -{"city": "STARKWEATHER", "loc": [-98.853903, 48.448802], "pop": 456, "state": "ND", "_id": "58377"} -{"city": "HAMAR", "loc": [-98.475218, 47.802014], "pop": 463, "state": "ND", "_id": "58380"} -{"city": "WARWICK", "loc": [-98.680956, 47.891285], "pop": 195, "state": "ND", "_id": "58381"} -{"city": "WEBSTER", "loc": [-98.873944, 48.322852], "pop": 108, "state": "ND", "_id": "58382"} -{"city": "WILLOW CITY", "loc": [-100.296802, 48.606205], "pop": 453, "state": "ND", "_id": "58384"} -{"city": "WOLFORD", "loc": [-99.662756, 48.480937], "pop": 326, "state": "ND", "_id": "58385"} -{"city": "BAKER", "loc": [-99.550489, 48.321709], "pop": 74, "state": "ND", "_id": "58386"} -{"city": "ELDRIDGE", "loc": [-98.706127, 46.905899], "pop": 18347, "state": "ND", "_id": "58401"} -{"city": "ALFRED", "loc": [-98.914671, 46.58588], "pop": 121, "state": "ND", "_id": "58411"} -{"city": "ARENA", "loc": [-100.17651, 47.134934], "pop": 140, "state": "ND", "_id": "58412"} -{"city": "ASHLEY", "loc": [-99.3164, 46.053677], "pop": 1544, "state": "ND", "_id": "58413"} -{"city": "BERLIN", "loc": [-98.523342, 46.400834], "pop": 185, "state": "ND", "_id": "58415"} -{"city": "BINFORD", "loc": [-98.354625, 47.573899], "pop": 506, "state": "ND", "_id": "58416"} -{"city": "BOWDON", "loc": [-99.701532, 47.434318], "pop": 387, "state": "ND", "_id": "58418"} -{"city": "BUCHANAN", "loc": [-98.811158, 47.040917], "pop": 162, "state": "ND", "_id": "58420"} -{"city": "BORDULAC", "loc": [-99.108226, 47.453918], "pop": 3104, "state": "ND", "_id": "58421"} -{"city": "EMRICK", "loc": [-99.429677, 47.549857], "pop": 115, "state": "ND", "_id": "58422"} -{"city": "CHASELEY", "loc": [-99.824082, 47.449018], "pop": 68, "state": "ND", "_id": "58423"} -{"city": "WINDSOR", "loc": [-99.086596, 46.883242], "pop": 378, "state": "ND", "_id": "58424"} -{"city": "COOPERSTOWN", "loc": [-98.153332, 47.452732], "pop": 2313, "state": "ND", "_id": "58425"} -{"city": "COURTENAY", "loc": [-98.54885, 47.227195], "pop": 166, "state": "ND", "_id": "58426"} -{"city": "DAWSON", "loc": [-99.763544, 46.829612], "pop": 298, "state": "ND", "_id": "58428"} -{"city": "SIBLEY", "loc": [-98.143863, 47.195503], "pop": 376, "state": "ND", "_id": "58429"} -{"city": "DENHOFF", "loc": [-100.263037, 47.570905], "pop": 177, "state": "ND", "_id": "58430"} -{"city": "DICKEY", "loc": [-98.468212, 46.542565], "pop": 211, "state": "ND", "_id": "58431"} -{"city": "ECKELSON", "loc": [-98.366939, 46.916737], "pop": 138, "state": "ND", "_id": "58432"} -{"city": "MERRICOURT", "loc": [-98.708346, 46.324969], "pop": 1364, "state": "ND", "_id": "58433"} -{"city": "ELLENDALE", "loc": [-98.51383, 46.007317], "pop": 2446, "state": "ND", "_id": "58436"} -{"city": "FESSENDEN", "loc": [-99.643361, 47.628644], "pop": 1297, "state": "ND", "_id": "58438"} -{"city": "FORBES", "loc": [-98.812414, 46.010926], "pop": 287, "state": "ND", "_id": "58439"} -{"city": "FREDONIA", "loc": [-99.262818, 46.353456], "pop": 453, "state": "ND", "_id": "58440"} -{"city": "FULLERTON", "loc": [-98.388219, 46.195138], "pop": 375, "state": "ND", "_id": "58441"} -{"city": "GACKLE", "loc": [-99.219056, 46.591263], "pop": 866, "state": "ND", "_id": "58442"} -{"city": "JUANITA", "loc": [-98.663951, 47.443185], "pop": 539, "state": "ND", "_id": "58443"} -{"city": "GOODRICH", "loc": [-100.118979, 47.471048], "pop": 339, "state": "ND", "_id": "58444"} -{"city": "GRACE CITY", "loc": [-98.809465, 47.55358], "pop": 168, "state": "ND", "_id": "58445"} -{"city": "WALUM", "loc": [-98.15718, 47.299693], "pop": 374, "state": "ND", "_id": "58448"} -{"city": "HEATON", "loc": [-99.578482, 47.462249], "pop": 76, "state": "ND", "_id": "58450"} -{"city": "HURDSFIELD", "loc": [-99.94165, 47.438733], "pop": 194, "state": "ND", "_id": "58451"} -{"city": "NORTONVILLE", "loc": [-98.807661, 46.500996], "pop": 575, "state": "ND", "_id": "58454"} -{"city": "KENSAL", "loc": [-98.720579, 47.272609], "pop": 384, "state": "ND", "_id": "58455"} -{"city": "KULM", "loc": [-98.942724, 46.307274], "pop": 634, "state": "ND", "_id": "58456"} -{"city": "GRAND RAPIDS", "loc": [-98.30022, 46.361961], "pop": 1493, "state": "ND", "_id": "58458"} -{"city": "LEHR", "loc": [-99.349136, 46.258638], "pop": 227, "state": "ND", "_id": "58460"} -{"city": "LITCHVILLE", "loc": [-98.20327, 46.688788], "pop": 421, "state": "ND", "_id": "58461"} -{"city": "MCCLUSKY", "loc": [-100.451966, 47.489639], "pop": 1002, "state": "ND", "_id": "58463"} -{"city": "MCHENRY", "loc": [-98.572594, 47.560416], "pop": 172, "state": "ND", "_id": "58464"} -{"city": "MANFRED", "loc": [-99.764369, 47.711663], "pop": 73, "state": "ND", "_id": "58465"} -{"city": "MARION", "loc": [-98.253327, 46.571587], "pop": 723, "state": "ND", "_id": "58466"} -{"city": "MEDINA", "loc": [-99.310556, 46.891951], "pop": 622, "state": "ND", "_id": "58467"} -{"city": "MONANGO", "loc": [-98.57962, 46.193641], "pop": 172, "state": "ND", "_id": "58471"} -{"city": "ADRIAN", "loc": [-98.597647, 46.654218], "pop": 419, "state": "ND", "_id": "58472"} -{"city": "GUELPH", "loc": [-98.099339, 46.128796], "pop": 2569, "state": "ND", "_id": "58474"} -{"city": "PETTIBONE", "loc": [-99.527892, 47.119973], "pop": 163, "state": "ND", "_id": "58475"} -{"city": "EDMUNDS", "loc": [-99.002041, 47.165715], "pop": 624, "state": "ND", "_id": "58476"} -{"city": "REGAN", "loc": [-100.522448, 47.152703], "pop": 274, "state": "ND", "_id": "58477"} -{"city": "LAKE WILLIAMS", "loc": [-99.700302, 47.150765], "pop": 505, "state": "ND", "_id": "58478"} -{"city": "LEAL", "loc": [-98.220885, 47.071086], "pop": 395, "state": "ND", "_id": "58479"} -{"city": "SANBORN", "loc": [-98.233622, 46.927065], "pop": 304, "state": "ND", "_id": "58480"} -{"city": "SPIRITWOOD", "loc": [-98.648468, 47.101601], "pop": 263, "state": "ND", "_id": "58481"} -{"city": "STEELE", "loc": [-99.933559, 46.852357], "pop": 1277, "state": "ND", "_id": "58482"} -{"city": "STREETER", "loc": [-99.297108, 46.694999], "pop": 482, "state": "ND", "_id": "58483"} -{"city": "SUTTON", "loc": [-98.432044, 47.385355], "pop": 110, "state": "ND", "_id": "58484"} -{"city": "SYKESTON", "loc": [-99.39751, 47.436938], "pop": 384, "state": "ND", "_id": "58486"} -{"city": "TAPPEN", "loc": [-99.601892, 46.83362], "pop": 631, "state": "ND", "_id": "58487"} -{"city": "TUTTLE", "loc": [-99.987287, 47.162583], "pop": 458, "state": "ND", "_id": "58488"} -{"city": "VENTURIA", "loc": [-99.494253, 46.037075], "pop": 164, "state": "ND", "_id": "58489"} -{"city": "VERONA", "loc": [-98.089536, 46.371229], "pop": 278, "state": "ND", "_id": "58490"} -{"city": "WIMBLEDON", "loc": [-98.432916, 47.143837], "pop": 530, "state": "ND", "_id": "58492"} -{"city": "WING", "loc": [-100.307159, 47.151939], "pop": 376, "state": "ND", "_id": "58494"} -{"city": "BURNSTAD", "loc": [-99.586005, 46.251617], "pop": 1677, "state": "ND", "_id": "58495"} -{"city": "WOODWORTH", "loc": [-99.3406, 47.163639], "pop": 340, "state": "ND", "_id": "58496"} -{"city": "YPSILANTI", "loc": [-98.487636, 46.771949], "pop": 281, "state": "ND", "_id": "58497"} -{"city": "BISMARCK", "loc": [-100.774755, 46.823448], "pop": 36602, "state": "ND", "_id": "58501"} -{"city": "LINCOLN", "loc": [-100.774411, 46.782463], "pop": 19990, "state": "ND", "_id": "58504"} -{"city": "ALMONT", "loc": [-101.522126, 46.704605], "pop": 309, "state": "ND", "_id": "58520"} -{"city": "BALDWIN", "loc": [-100.761464, 46.954338], "pop": 1218, "state": "ND", "_id": "58521"} -{"city": "BEULAH", "loc": [-101.807468, 47.270664], "pop": 4363, "state": "ND", "_id": "58523"} -{"city": "BRADDOCK", "loc": [-100.241577, 46.549885], "pop": 401, "state": "ND", "_id": "58524"} -{"city": "CANNON BALL", "loc": [-100.59749, 46.387446], "pop": 608, "state": "ND", "_id": "58528"} -{"city": "CARSON", "loc": [-101.538725, 46.462389], "pop": 713, "state": "ND", "_id": "58529"} -{"city": "FORT CLARK", "loc": [-101.328643, 47.121912], "pop": 2381, "state": "ND", "_id": "58530"} -{"city": "COLEHARBOR", "loc": [-101.233198, 47.519559], "pop": 184, "state": "ND", "_id": "58531"} -{"city": "DRISCOLL", "loc": [-100.144063, 46.851139], "pop": 235, "state": "ND", "_id": "58532"} -{"city": "HEIL", "loc": [-101.835145, 46.411346], "pop": 1052, "state": "ND", "_id": "58533"} -{"city": "LARK", "loc": [-101.151681, 46.465133], "pop": 972, "state": "ND", "_id": "58535"} -{"city": "HUFF", "loc": [-100.693909, 46.563172], "pop": 266, "state": "ND", "_id": "58537"} -{"city": "FORT YATES", "loc": [-100.651611, 46.09054], "pop": 2096, "state": "ND", "_id": "58538"} -{"city": "EMMET", "loc": [-101.398325, 47.655219], "pop": 2215, "state": "ND", "_id": "58540"} -{"city": "GOLDEN VALLEY", "loc": [-102.061229, 47.293754], "pop": 315, "state": "ND", "_id": "58541"} -{"city": "HAGUE", "loc": [-99.974507, 46.058896], "pop": 252, "state": "ND", "_id": "58542"} -{"city": "HAZELTON", "loc": [-100.273325, 46.487477], "pop": 323, "state": "ND", "_id": "58544"} -{"city": "HAZEN", "loc": [-101.610695, 47.327138], "pop": 4072, "state": "ND", "_id": "58545"} -{"city": "KINTYRE", "loc": [-99.970462, 46.573573], "pop": 83, "state": "ND", "_id": "58549"} -{"city": "LEITH", "loc": [-101.455531, 46.234873], "pop": 456, "state": "ND", "_id": "58551"} -{"city": "TEMVIK", "loc": [-100.215832, 46.286924], "pop": 2362, "state": "ND", "_id": "58552"} -{"city": "MCKENZIE", "loc": [-100.3995, 46.831062], "pop": 163, "state": "ND", "_id": "58553"} -{"city": "MANDAN", "loc": [-100.909175, 46.830649], "pop": 18098, "state": "ND", "_id": "58554"} -{"city": "MENOKEN", "loc": [-100.527488, 46.861032], "pop": 176, "state": "ND", "_id": "58558"} -{"city": "MERCER", "loc": [-100.717079, 47.485792], "pop": 142, "state": "ND", "_id": "58559"} -{"city": "MOFFIT", "loc": [-100.297538, 46.675725], "pop": 176, "state": "ND", "_id": "58560"} -{"city": "NAPOLEON", "loc": [-99.77253, 46.486724], "pop": 1440, "state": "ND", "_id": "58561"} -{"city": "BENTLEY", "loc": [-101.89369, 46.374448], "pop": 1095, "state": "ND", "_id": "58562"} -{"city": "HANNOVER", "loc": [-101.424453, 46.851248], "pop": 1545, "state": "ND", "_id": "58563"} -{"city": "RALEIGH", "loc": [-101.282651, 46.335236], "pop": 138, "state": "ND", "_id": "58564"} -{"city": "RIVERDALE", "loc": [-101.115061, 47.640333], "pop": 86, "state": "ND", "_id": "58565"} -{"city": "SAINT ANTHONY", "loc": [-100.897237, 46.588831], "pop": 238, "state": "ND", "_id": "58566"} -{"city": "SELFRIDGE", "loc": [-101.150204, 46.035147], "pop": 647, "state": "ND", "_id": "58568"} -{"city": "SHIELDS", "loc": [-101.258894, 46.183772], "pop": 76, "state": "ND", "_id": "58569"} -{"city": "BREIEN", "loc": [-100.81363, 46.294106], "pop": 410, "state": "ND", "_id": "58570"} -{"city": "STANTON", "loc": [-101.38987, 47.312786], "pop": 720, "state": "ND", "_id": "58571"} -{"city": "STERLING", "loc": [-100.274392, 46.843583], "pop": 263, "state": "ND", "_id": "58572"} -{"city": "STRASBURG", "loc": [-100.211869, 46.097938], "pop": 1409, "state": "ND", "_id": "58573"} -{"city": "TURTLE LAKE", "loc": [-100.881364, 47.541416], "pop": 1059, "state": "ND", "_id": "58575"} -{"city": "UNDERWOOD", "loc": [-101.189807, 47.460163], "pop": 1629, "state": "ND", "_id": "58576"} -{"city": "WASHBURN", "loc": [-101.011568, 47.311388], "pop": 2006, "state": "ND", "_id": "58577"} -{"city": "WILTON", "loc": [-100.794385, 47.170853], "pop": 1335, "state": "ND", "_id": "58579"} -{"city": "ZAP", "loc": [-101.925649, 47.289711], "pop": 338, "state": "ND", "_id": "58580"} -{"city": "ZEELAND", "loc": [-99.772543, 46.005476], "pop": 497, "state": "ND", "_id": "58581"} -{"city": "NEW HRADEC", "loc": [-102.787595, 46.887289], "pop": 18751, "state": "ND", "_id": "58601"} -{"city": "AMIDON", "loc": [-103.264741, 46.455951], "pop": 673, "state": "ND", "_id": "58620"} -{"city": "BEACH", "loc": [-103.98429, 46.932381], "pop": 1577, "state": "ND", "_id": "58621"} -{"city": "FRYBURG", "loc": [-103.179515, 46.887728], "pop": 1290, "state": "ND", "_id": "58622"} -{"city": "BOWMAN", "loc": [-103.401965, 46.173316], "pop": 2432, "state": "ND", "_id": "58623"} -{"city": "DODGE", "loc": [-102.198505, 47.304893], "pop": 159, "state": "ND", "_id": "58625"} -{"city": "DUNN CENTER", "loc": [-102.589198, 47.345265], "pop": 276, "state": "ND", "_id": "58626"} -{"city": "GORHAM", "loc": [-103.222328, 47.118004], "pop": 729, "state": "ND", "_id": "58627"} -{"city": "GLADSTONE", "loc": [-102.527379, 46.815162], "pop": 397, "state": "ND", "_id": "58630"} -{"city": "GLEN ULLIN", "loc": [-101.822293, 46.823301], "pop": 1207, "state": "ND", "_id": "58631"} -{"city": "GOLVA", "loc": [-103.957545, 46.7075], "pop": 290, "state": "ND", "_id": "58632"} -{"city": "GRASSY BUTTE", "loc": [-103.294433, 47.443072], "pop": 282, "state": "ND", "_id": "58634"} -{"city": "WERNER", "loc": [-102.319286, 47.367752], "pop": 973, "state": "ND", "_id": "58636"} -{"city": "HEBRON", "loc": [-102.036678, 46.893613], "pop": 1133, "state": "ND", "_id": "58638"} -{"city": "BUCYRUS", "loc": [-102.584516, 46.027869], "pop": 2793, "state": "ND", "_id": "58639"} -{"city": "KILLDEER", "loc": [-102.776242, 47.410898], "pop": 1564, "state": "ND", "_id": "58640"} -{"city": "LEFOR", "loc": [-102.764292, 46.725264], "pop": 601, "state": "ND", "_id": "58641"} -{"city": "MANNING", "loc": [-102.680638, 47.125837], "pop": 733, "state": "ND", "_id": "58642"} -{"city": "MARMARTH", "loc": [-103.88, 46.325396], "pop": 234, "state": "ND", "_id": "58643"} -{"city": "MEDORA", "loc": [-103.421011, 46.845771], "pop": 379, "state": "ND", "_id": "58645"} -{"city": "BURT", "loc": [-102.312935, 46.401095], "pop": 1834, "state": "ND", "_id": "58646"} -{"city": "NEW ENGLAND", "loc": [-102.835816, 46.512841], "pop": 1183, "state": "ND", "_id": "58647"} -{"city": "REEDER", "loc": [-102.940519, 46.111577], "pop": 381, "state": "ND", "_id": "58649"} -{"city": "REGENT", "loc": [-102.568443, 46.423452], "pop": 379, "state": "ND", "_id": "58650"} -{"city": "RHAME", "loc": [-103.70791, 46.166449], "pop": 467, "state": "ND", "_id": "58651"} -{"city": "RICHARDTON", "loc": [-102.292004, 46.842614], "pop": 1091, "state": "ND", "_id": "58652"} -{"city": "GASCOYNE", "loc": [-103.144688, 46.139924], "pop": 697, "state": "ND", "_id": "58653"} -{"city": "SENTINEL BUTTE", "loc": [-103.800113, 46.830475], "pop": 229, "state": "ND", "_id": "58654"} -{"city": "SOUTH HEART", "loc": [-103.016241, 46.812701], "pop": 613, "state": "ND", "_id": "58655"} -{"city": "TAYLOR", "loc": [-102.375626, 46.928372], "pop": 389, "state": "ND", "_id": "58656"} -{"city": "TROTTERS", "loc": [-103.854704, 47.233008], "pop": 12, "state": "ND", "_id": "58657"} -{"city": "MINOT", "loc": [-101.298476, 48.22914], "pop": 42195, "state": "ND", "_id": "58701"} -{"city": "MINOT AFB", "loc": [-101.31678, 48.423217], "pop": 9095, "state": "ND", "_id": "58704"} -{"city": "ANAMOOSE", "loc": [-100.252941, 47.870307], "pop": 595, "state": "ND", "_id": "58710"} -{"city": "ANTLER", "loc": [-101.333758, 48.958525], "pop": 222, "state": "ND", "_id": "58711"} -{"city": "BALFOUR", "loc": [-100.520929, 47.968451], "pop": 82, "state": "ND", "_id": "58712"} -{"city": "BANTRY", "loc": [-100.789445, 48.511875], "pop": 504, "state": "ND", "_id": "58713"} -{"city": "BENEDICT", "loc": [-101.057884, 47.786493], "pop": 149, "state": "ND", "_id": "58716"} -{"city": "BLAISDELL", "loc": [-101.800562, 48.323399], "pop": 677, "state": "ND", "_id": "58718"} -{"city": "COTEAU", "loc": [-102.247271, 48.796803], "pop": 705, "state": "ND", "_id": "58721"} -{"city": "BURLINGTON", "loc": [-101.428205, 48.273534], "pop": 1291, "state": "ND", "_id": "58722"} -{"city": "BUTTE", "loc": [-100.660446, 47.811884], "pop": 241, "state": "ND", "_id": "58723"} -{"city": "CARPIO", "loc": [-101.711943, 48.432299], "pop": 298, "state": "ND", "_id": "58725"} -{"city": "LARSON", "loc": [-102.794848, 48.878875], "pop": 484, "state": "ND", "_id": "58727"} -{"city": "CROSBY", "loc": [-103.274023, 48.883551], "pop": 1881, "state": "ND", "_id": "58730"} -{"city": "DEERING", "loc": [-101.033685, 48.405693], "pop": 200, "state": "ND", "_id": "58731"} -{"city": "DES LACS", "loc": [-101.567167, 48.25569], "pop": 306, "state": "ND", "_id": "58733"} -{"city": "DONNYBROOK", "loc": [-101.896039, 48.490015], "pop": 208, "state": "ND", "_id": "58734"} -{"city": "DOUGLAS", "loc": [-101.511218, 47.865914], "pop": 135, "state": "ND", "_id": "58735"} -{"city": "DRAKE", "loc": [-100.378955, 47.902431], "pop": 577, "state": "ND", "_id": "58736"} -{"city": "NORTHGATE", "loc": [-102.341502, 48.917672], "pop": 240, "state": "ND", "_id": "58737"} -{"city": "FOXHOLM", "loc": [-101.59072, 48.339193], "pop": 88, "state": "ND", "_id": "58738"} -{"city": "GARDENA", "loc": [-100.48429, 48.683566], "pop": 82, "state": "ND", "_id": "58739"} -{"city": "WOLSETH", "loc": [-101.31955, 48.507978], "pop": 783, "state": "ND", "_id": "58740"} -{"city": "GRANVILLE", "loc": [-100.808193, 48.256575], "pop": 679, "state": "ND", "_id": "58741"} -{"city": "KARLSRUHE", "loc": [-100.574158, 48.10085], "pop": 682, "state": "ND", "_id": "58744"} -{"city": "COULEE", "loc": [-102.071745, 48.673149], "pop": 1756, "state": "ND", "_id": "58746"} -{"city": "KIEF", "loc": [-100.52146, 47.818615], "pop": 204, "state": "ND", "_id": "58747"} -{"city": "KRAMER", "loc": [-100.671638, 48.686666], "pop": 145, "state": "ND", "_id": "58748"} -{"city": "LANSFORD", "loc": [-101.385825, 48.625419], "pop": 562, "state": "ND", "_id": "58750"} -{"city": "LIGNITE", "loc": [-102.554177, 48.848113], "pop": 399, "state": "ND", "_id": "58752"} -{"city": "MCGREGOR", "loc": [-102.928795, 48.594983], "pop": 94, "state": "ND", "_id": "58755"} -{"city": "MAKOTI", "loc": [-101.814942, 47.985283], "pop": 247, "state": "ND", "_id": "58756"} -{"city": "MANDAREE", "loc": [-102.653473, 47.856744], "pop": 886, "state": "ND", "_id": "58757"} -{"city": "MARTIN", "loc": [-100.122517, 47.778144], "pop": 279, "state": "ND", "_id": "58758"} -{"city": "MAX", "loc": [-101.293166, 47.815577], "pop": 472, "state": "ND", "_id": "58759"} -{"city": "MAXBASS", "loc": [-101.256328, 48.772163], "pop": 404, "state": "ND", "_id": "58760"} -{"city": "LORAINE", "loc": [-101.554512, 48.765814], "pop": 1455, "state": "ND", "_id": "58761"} -{"city": "NEWBURG", "loc": [-100.968056, 48.698192], "pop": 516, "state": "ND", "_id": "58762"} -{"city": "CHARLSON", "loc": [-102.485793, 47.977154], "pop": 2033, "state": "ND", "_id": "58763"} -{"city": "NOONAN", "loc": [-103.009793, 48.885635], "pop": 326, "state": "ND", "_id": "58765"} -{"city": "NORWICH", "loc": [-100.971165, 48.249115], "pop": 167, "state": "ND", "_id": "58768"} -{"city": "PALERMO", "loc": [-102.239954, 48.3396], "pop": 133, "state": "ND", "_id": "58769"} -{"city": "PARSHALL", "loc": [-102.142732, 47.95597], "pop": 1425, "state": "ND", "_id": "58770"} -{"city": "PLAZA", "loc": [-101.964276, 48.02421], "pop": 309, "state": "ND", "_id": "58771"} -{"city": "PORTAL", "loc": [-102.548023, 48.975818], "pop": 286, "state": "ND", "_id": "58772"} -{"city": "BATTLEVIEW", "loc": [-102.644646, 48.589815], "pop": 888, "state": "ND", "_id": "58773"} -{"city": "ROSEGLEN", "loc": [-101.822041, 47.695967], "pop": 1178, "state": "ND", "_id": "58775"} -{"city": "ROSS", "loc": [-102.530721, 48.316159], "pop": 104, "state": "ND", "_id": "58776"} -{"city": "RUSO", "loc": [-100.875432, 47.762224], "pop": 187, "state": "ND", "_id": "58778"} -{"city": "RAUB", "loc": [-101.765095, 47.868017], "pop": 320, "state": "ND", "_id": "58779"} -{"city": "SAWYER", "loc": [-101.067396, 48.085837], "pop": 493, "state": "ND", "_id": "58781"} -{"city": "SHERWOOD", "loc": [-101.697024, 48.957551], "pop": 487, "state": "ND", "_id": "58782"} -{"city": "CARBURY", "loc": [-100.741324, 48.937378], "pop": 443, "state": "ND", "_id": "58783"} -{"city": "BELDEN", "loc": [-102.392802, 48.319189], "pop": 2711, "state": "ND", "_id": "58784"} -{"city": "SURREY", "loc": [-101.121617, 48.236472], "pop": 1109, "state": "ND", "_id": "58785"} -{"city": "TOLLEY", "loc": [-101.855891, 48.796903], "pop": 270, "state": "ND", "_id": "58787"} -{"city": "BERWICK", "loc": [-100.412322, 48.377657], "pop": 1251, "state": "ND", "_id": "58788"} -{"city": "UPHAM", "loc": [-100.732314, 48.581632], "pop": 285, "state": "ND", "_id": "58789"} -{"city": "VELVA", "loc": [-100.934623, 48.06748], "pop": 1241, "state": "ND", "_id": "58790"} -{"city": "BERGEN", "loc": [-100.803824, 47.951793], "pop": 418, "state": "ND", "_id": "58792"} -{"city": "WESTHOPE", "loc": [-101.033809, 48.905074], "pop": 855, "state": "ND", "_id": "58793"} -{"city": "WHITE EARTH", "loc": [-102.806672, 48.430748], "pop": 172, "state": "ND", "_id": "58794"} -{"city": "HAMLET", "loc": [-103.184878, 48.621929], "pop": 238, "state": "ND", "_id": "58795"} -{"city": "BONETRAILL", "loc": [-103.631699, 48.167924], "pop": 16473, "state": "ND", "_id": "58801"} -{"city": "APPAM", "loc": [-103.422154, 48.585803], "pop": 116, "state": "ND", "_id": "58830"} -{"city": "RAWSON", "loc": [-103.63961, 47.843517], "pop": 535, "state": "ND", "_id": "58831"} -{"city": "AMBROSE", "loc": [-103.700676, 48.817195], "pop": 569, "state": "ND", "_id": "58833"} -{"city": "ARNEGARD", "loc": [-103.453837, 47.808832], "pop": 190, "state": "ND", "_id": "58835"} -{"city": "CARTWRIGHT", "loc": [-103.948718, 47.79922], "pop": 944, "state": "ND", "_id": "58838"} -{"city": "SPRINGBROOK", "loc": [-103.372462, 48.260088], "pop": 195, "state": "ND", "_id": "58843"} -{"city": "COLGAN", "loc": [-103.828955, 48.924646], "pop": 123, "state": "ND", "_id": "58844"} -{"city": "ALKABO", "loc": [-103.929128, 48.613946], "pop": 313, "state": "ND", "_id": "58845"} -{"city": "KEENE", "loc": [-102.890567, 47.940516], "pop": 358, "state": "ND", "_id": "58847"} -{"city": "WHEELOCK", "loc": [-103.18365, 48.3323], "pop": 816, "state": "ND", "_id": "58849"} -{"city": "TEMPLE", "loc": [-102.961281, 48.392009], "pop": 1984, "state": "ND", "_id": "58852"} -{"city": "TRENTON", "loc": [-103.861276, 48.056303], "pop": 806, "state": "ND", "_id": "58853"} -{"city": "WATFORD CITY", "loc": [-103.258205, 47.804258], "pop": 3188, "state": "ND", "_id": "58854"} -{"city": "ZAHL", "loc": [-103.659926, 48.578747], "pop": 94, "state": "ND", "_id": "58856"} -{"city": "ALEXANDRIA", "loc": [-82.607683, 40.105965], "pop": 2485, "state": "OH", "_id": "43001"} -{"city": "AMLIN", "loc": [-83.179157, 40.07203], "pop": 1699, "state": "OH", "_id": "43002"} -{"city": "ASHLEY", "loc": [-82.954201, 40.416264], "pop": 3009, "state": "OH", "_id": "43003"} -{"city": "BLACKLICK", "loc": [-82.807857, 40.020952], "pop": 2288, "state": "OH", "_id": "43004"} -{"city": "BRINKHAVEN", "loc": [-82.155289, 40.458346], "pop": 378, "state": "OH", "_id": "43006"} -{"city": "CABLE", "loc": [-83.64696, 40.178411], "pop": 1660, "state": "OH", "_id": "43009"} -{"city": "CENTERBURG", "loc": [-82.680039, 40.286513], "pop": 4437, "state": "OH", "_id": "43011"} -{"city": "CROTON", "loc": [-82.698948, 40.237603], "pop": 1197, "state": "OH", "_id": "43013"} -{"city": "DANVILLE", "loc": [-82.263855, 40.455693], "pop": 2033, "state": "OH", "_id": "43014"} -{"city": "DELAWARE", "loc": [-83.072312, 40.293186], "pop": 29384, "state": "OH", "_id": "43015"} -{"city": "DUBLIN", "loc": [-83.114633, 40.109297], "pop": 34840, "state": "OH", "_id": "43017"} -{"city": "FREDERICKTOWN", "loc": [-82.585711, 40.497613], "pop": 10647, "state": "OH", "_id": "43019"} -{"city": "GALENA", "loc": [-82.895937, 40.191546], "pop": 4893, "state": "OH", "_id": "43021"} -{"city": "GAMBIER", "loc": [-82.382752, 40.378241], "pop": 3903, "state": "OH", "_id": "43022"} -{"city": "GRANVILLE", "loc": [-82.519399, 40.078791], "pop": 9523, "state": "OH", "_id": "43023"} -{"city": "HEBRON", "loc": [-82.491868, 39.953464], "pop": 7920, "state": "OH", "_id": "43025"} -{"city": "HILLIARD", "loc": [-83.138333, 40.032187], "pop": 25442, "state": "OH", "_id": "43026"} -{"city": "HOWARD", "loc": [-82.333351, 40.415818], "pop": 1972, "state": "OH", "_id": "43028"} -{"city": "IRWIN", "loc": [-83.458699, 40.128352], "pop": 498, "state": "OH", "_id": "43029"} -{"city": "JOHNSTOWN", "loc": [-82.697284, 40.14452], "pop": 7550, "state": "OH", "_id": "43031"} -{"city": "MAGNETIC SPRINGS", "loc": [-83.267142, 40.358023], "pop": 708, "state": "OH", "_id": "43036"} -{"city": "MARTINSBURG", "loc": [-82.356691, 40.267954], "pop": 370, "state": "OH", "_id": "43037"} -{"city": "MARYSVILLE", "loc": [-83.362213, 40.247723], "pop": 19644, "state": "OH", "_id": "43040"} -{"city": "MECHANICSBURG", "loc": [-83.572352, 40.064659], "pop": 5390, "state": "OH", "_id": "43044"} -{"city": "MILFORD CENTER", "loc": [-83.437333, 40.181666], "pop": 1260, "state": "OH", "_id": "43045"} -{"city": "MILLERSPORT", "loc": [-82.528348, 39.899307], "pop": 3171, "state": "OH", "_id": "43046"} -{"city": "MOUNT VERNON", "loc": [-82.487286, 40.384937], "pop": 24421, "state": "OH", "_id": "43050"} -{"city": "NEW ALBANY", "loc": [-82.798793, 40.084686], "pop": 3520, "state": "OH", "_id": "43054"} -{"city": "NEWARK", "loc": [-82.404565, 40.072429], "pop": 56412, "state": "OH", "_id": "43055"} -{"city": "HEATH", "loc": [-82.387527, 40.019659], "pop": 13336, "state": "OH", "_id": "43056"} -{"city": "NORTH LEWISBURG", "loc": [-83.561476, 40.222871], "pop": 1407, "state": "OH", "_id": "43060"} -{"city": "OSTRANDER", "loc": [-83.197813, 40.273971], "pop": 2622, "state": "OH", "_id": "43061"} -{"city": "PATASKALA", "loc": [-82.668656, 40.000925], "pop": 15470, "state": "OH", "_id": "43062"} -{"city": "PLAIN CITY", "loc": [-83.269049, 40.097356], "pop": 8323, "state": "OH", "_id": "43064"} -{"city": "SHAWNEE HILLS", "loc": [-83.074921, 40.152652], "pop": 14955, "state": "OH", "_id": "43065"} -{"city": "RADNOR", "loc": [-83.178074, 40.391779], "pop": 1150, "state": "OH", "_id": "43066"} -{"city": "RAYMOND", "loc": [-83.364927, 40.247321], "pop": 576, "state": "OH", "_id": "43067"} -{"city": "REYNOLDSBURG", "loc": [-82.803454, 39.955145], "pop": 35820, "state": "OH", "_id": "43068"} -{"city": "SAINT LOUISVILLE", "loc": [-82.356015, 40.181776], "pop": 2389, "state": "OH", "_id": "43071"} -{"city": "SAINT PARIS", "loc": [-83.963062, 40.105755], "pop": 5963, "state": "OH", "_id": "43072"} -{"city": "SUNBURY", "loc": [-82.851051, 40.265499], "pop": 7508, "state": "OH", "_id": "43074"} -{"city": "THORNVILLE", "loc": [-82.407059, 39.897364], "pop": 8187, "state": "OH", "_id": "43076"} -{"city": "URBANA", "loc": [-83.767142, 40.106639], "pop": 20175, "state": "OH", "_id": "43078"} -{"city": "UTICA", "loc": [-82.413459, 40.244137], "pop": 5502, "state": "OH", "_id": "43080"} -{"city": "WESTERVILLE", "loc": [-82.910504, 40.114569], "pop": 49023, "state": "OH", "_id": "43081"} -{"city": "WOODSTOCK", "loc": [-83.546149, 40.181644], "pop": 793, "state": "OH", "_id": "43084"} -{"city": "WORTHINGTON", "loc": [-83.010069, 40.105155], "pop": 27229, "state": "OH", "_id": "43085"} -{"city": "AMANDA", "loc": [-82.755236, 39.625104], "pop": 3730, "state": "OH", "_id": "43102"} -{"city": "ASHVILLE", "loc": [-82.944567, 39.731576], "pop": 9050, "state": "OH", "_id": "43103"} -{"city": "BALTIMORE", "loc": [-82.624023, 39.864452], "pop": 5765, "state": "OH", "_id": "43105"} -{"city": "BLOOMINGBURG", "loc": [-83.409521, 39.628617], "pop": 1643, "state": "OH", "_id": "43106"} -{"city": "HIDE A WAY HILLS", "loc": [-82.42548, 39.698807], "pop": 3087, "state": "OH", "_id": "43107"} -{"city": "CANAL WINCHESTER", "loc": [-82.804369, 39.83486], "pop": 8444, "state": "OH", "_id": "43110"} -{"city": "CARROLL", "loc": [-82.708358, 39.795743], "pop": 3914, "state": "OH", "_id": "43112"} -{"city": "CIRCLEVILLE", "loc": [-82.929966, 39.598836], "pop": 22337, "state": "OH", "_id": "43113"} -{"city": "CLARKSBURG", "loc": [-83.156282, 39.490432], "pop": 1523, "state": "OH", "_id": "43115"} -{"city": "GALLOWAY", "loc": [-83.183848, 39.936604], "pop": 9984, "state": "OH", "_id": "43119"} -{"city": "GROVE CITY", "loc": [-83.083944, 39.881382], "pop": 33730, "state": "OH", "_id": "43123"} -{"city": "GROVEPORT", "loc": [-82.887219, 39.858137], "pop": 7728, "state": "OH", "_id": "43125"} -{"city": "JEFFERSONVILLE", "loc": [-83.56873, 39.65896], "pop": 2359, "state": "OH", "_id": "43128"} -{"city": "LANCASTER", "loc": [-82.603074, 39.718697], "pop": 54451, "state": "OH", "_id": "43130"} -{"city": "LAURELVILLE", "loc": [-82.721219, 39.4757], "pop": 4552, "state": "OH", "_id": "43135"} -{"city": "LOCKBOURNE", "loc": [-82.976369, 39.814236], "pop": 1423, "state": "OH", "_id": "43137"} -{"city": "LOGAN", "loc": [-82.412594, 39.537175], "pop": 16011, "state": "OH", "_id": "43138"} -{"city": "LONDON", "loc": [-83.443899, 39.900074], "pop": 20432, "state": "OH", "_id": "43140"} -{"city": "MOUNT STERLING", "loc": [-83.280618, 39.717506], "pop": 4912, "state": "OH", "_id": "43143"} -{"city": "NEW HOLLAND", "loc": [-83.250429, 39.558897], "pop": 1768, "state": "OH", "_id": "43145"} -{"city": "ORIENT", "loc": [-83.154317, 39.795386], "pop": 13491, "state": "OH", "_id": "43146"} -{"city": "PICKERINGTON", "loc": [-82.756267, 39.906062], "pop": 18424, "state": "OH", "_id": "43147"} -{"city": "PLEASANTVILLE", "loc": [-82.504268, 39.822684], "pop": 1272, "state": "OH", "_id": "43148"} -{"city": "ROCKBRIDGE", "loc": [-82.562572, 39.550907], "pop": 2063, "state": "OH", "_id": "43149"} -{"city": "RUSHVILLE", "loc": [-82.427981, 39.767375], "pop": 1558, "state": "OH", "_id": "43150"} -{"city": "SOUTH BLOOMINGVI", "loc": [-82.639404, 39.374145], "pop": 1429, "state": "OH", "_id": "43152"} -{"city": "SOUTH SOLON", "loc": [-83.596984, 39.742295], "pop": 766, "state": "OH", "_id": "43153"} -{"city": "STOUTSVILLE", "loc": [-82.819279, 39.60672], "pop": 1666, "state": "OH", "_id": "43154"} -{"city": "SUGAR GROVE", "loc": [-82.532113, 39.627699], "pop": 1572, "state": "OH", "_id": "43155"} -{"city": "WASHINGTON COURT", "loc": [-83.438817, 39.534346], "pop": 21357, "state": "OH", "_id": "43160"} -{"city": "WEST JEFFERSON", "loc": [-83.285306, 39.942409], "pop": 7411, "state": "OH", "_id": "43162"} -{"city": "WILLIAMSPORT", "loc": [-83.125053, 39.611739], "pop": 2192, "state": "OH", "_id": "43164"} -{"city": "COLUMBUS", "loc": [-83.004732, 39.995157], "pop": 37110, "state": "OH", "_id": "43201"} -{"city": "COLUMBUS", "loc": [-83.011842, 40.020084], "pop": 21526, "state": "OH", "_id": "43202"} -{"city": "COLUMBUS", "loc": [-82.969131, 39.971925], "pop": 11047, "state": "OH", "_id": "43203"} -{"city": "COLUMBUS", "loc": [-83.077999, 39.952333], "pop": 38794, "state": "OH", "_id": "43204"} -{"city": "COLUMBUS", "loc": [-82.964352, 39.956905], "pop": 17567, "state": "OH", "_id": "43205"} -{"city": "COLUMBUS", "loc": [-82.974845, 39.942639], "pop": 26587, "state": "OH", "_id": "43206"} -{"city": "COLUMBUS", "loc": [-82.970334, 39.904565], "pop": 44404, "state": "OH", "_id": "43207"} -{"city": "BEXLEY", "loc": [-82.926595, 39.958999], "pop": 29574, "state": "OH", "_id": "43209"} -{"city": "COLUMBUS", "loc": [-83.016404, 40.002804], "pop": 10690, "state": "OH", "_id": "43210"} -{"city": "COLUMBUS", "loc": [-82.973196, 40.011792], "pop": 28031, "state": "OH", "_id": "43211"} -{"city": "COLUMBUS", "loc": [-83.045579, 39.987381], "pop": 18478, "state": "OH", "_id": "43212"} -{"city": "WHITEHALL", "loc": [-82.878275, 39.967146], "pop": 29375, "state": "OH", "_id": "43213"} -{"city": "COLUMBUS", "loc": [-83.01875, 40.053482], "pop": 26080, "state": "OH", "_id": "43214"} -{"city": "COLUMBUS", "loc": [-83.004383, 39.967106], "pop": 10145, "state": "OH", "_id": "43215"} -{"city": "COLUMBUS", "loc": [-82.947483, 39.806209], "pop": 2150, "state": "OH", "_id": "43217"} -{"city": "SHEPARD", "loc": [-82.936459, 40.004394], "pop": 24841, "state": "OH", "_id": "43219"} -{"city": "COLUMBUS", "loc": [-83.066911, 40.049484], "pop": 24378, "state": "OH", "_id": "43220"} -{"city": "UPPER ARLINGTON", "loc": [-83.064592, 40.015431], "pop": 21283, "state": "OH", "_id": "43221"} -{"city": "COLUMBUS", "loc": [-83.031109, 39.957628], "pop": 7019, "state": "OH", "_id": "43222"} -{"city": "COLUMBUS", "loc": [-83.046344, 39.938753], "pop": 30538, "state": "OH", "_id": "43223"} -{"city": "COLUMBUS", "loc": [-82.968947, 40.042493], "pop": 41442, "state": "OH", "_id": "43224"} -{"city": "COLUMBUS", "loc": [-82.890298, 39.944394], "pop": 24945, "state": "OH", "_id": "43227"} -{"city": "LINCOLN VILLAGE", "loc": [-83.123858, 39.947876], "pop": 37615, "state": "OH", "_id": "43228"} -{"city": "COLUMBUS", "loc": [-82.972568, 40.083886], "pop": 45798, "state": "OH", "_id": "43229"} -{"city": "GAHANNA", "loc": [-82.882429, 40.038458], "pop": 34342, "state": "OH", "_id": "43230"} -{"city": "COLUMBUS", "loc": [-82.938275, 40.080984], "pop": 14519, "state": "OH", "_id": "43231"} -{"city": "COLUMBUS", "loc": [-82.866432, 39.923024], "pop": 38848, "state": "OH", "_id": "43232"} -{"city": "WEST WORTHINGTON", "loc": [-83.059287, 40.101271], "pop": 34051, "state": "OH", "_id": "43235"} -{"city": "MARION", "loc": [-83.127056, 40.587648], "pop": 50967, "state": "OH", "_id": "43302"} -{"city": "BELLE CENTER", "loc": [-83.768773, 40.502371], "pop": 2057, "state": "OH", "_id": "43310"} -{"city": "BELLEFONTAINE", "loc": [-83.757076, 40.360472], "pop": 16796, "state": "OH", "_id": "43311"} -{"city": "CALEDONIA", "loc": [-82.992465, 40.627239], "pop": 3120, "state": "OH", "_id": "43314"} -{"city": "CARDINGTON", "loc": [-82.933728, 40.506583], "pop": 4739, "state": "OH", "_id": "43315"} -{"city": "CAREY", "loc": [-83.383578, 40.948599], "pop": 6177, "state": "OH", "_id": "43316"} -{"city": "DE GRAFF", "loc": [-83.9153, 40.305773], "pop": 3452, "state": "OH", "_id": "43318"} -{"city": "EAST LIBERTY", "loc": [-83.586221, 40.307682], "pop": 1351, "state": "OH", "_id": "43319"} -{"city": "EDISON", "loc": [-82.902301, 40.590475], "pop": 1319, "state": "OH", "_id": "43320"} -{"city": "FULTON", "loc": [-82.836244, 40.465013], "pop": 1181, "state": "OH", "_id": "43321"} -{"city": "HARPSTER", "loc": [-83.23428, 40.747541], "pop": 719, "state": "OH", "_id": "43323"} -{"city": "HUNTSVILLE", "loc": [-83.792748, 40.441295], "pop": 1953, "state": "OH", "_id": "43324"} -{"city": "KENTON", "loc": [-83.611087, 40.640433], "pop": 15332, "state": "OH", "_id": "43326"} -{"city": "LAKEVIEW", "loc": [-83.908139, 40.503033], "pop": 5722, "state": "OH", "_id": "43331"} -{"city": "LA RUE", "loc": [-83.373397, 40.578879], "pop": 2120, "state": "OH", "_id": "43332"} -{"city": "LEWISTOWN", "loc": [-83.920893, 40.42768], "pop": 958, "state": "OH", "_id": "43333"} -{"city": "MARENGO", "loc": [-82.812136, 40.389512], "pop": 3338, "state": "OH", "_id": "43334"} -{"city": "MARTEL", "loc": [-82.90586, 40.670775], "pop": 659, "state": "OH", "_id": "43335"} -{"city": "MORRAL", "loc": [-83.204606, 40.695366], "pop": 1035, "state": "OH", "_id": "43337"} -{"city": "MOUNT GILEAD", "loc": [-82.806235, 40.538371], "pop": 8271, "state": "OH", "_id": "43338"} -{"city": "MOUNT VICTORY", "loc": [-83.494165, 40.52321], "pop": 1015, "state": "OH", "_id": "43340"} -{"city": "NEW BLOOMINGTON", "loc": [-83.322351, 40.607301], "pop": 1428, "state": "OH", "_id": "43341"} -{"city": "PROSPECT", "loc": [-83.176295, 40.472659], "pop": 3365, "state": "OH", "_id": "43342"} -{"city": "QUINCY", "loc": [-83.974411, 40.287606], "pop": 1147, "state": "OH", "_id": "43343"} -{"city": "RICHWOOD", "loc": [-83.313639, 40.43698], "pop": 4055, "state": "OH", "_id": "43344"} -{"city": "RIDGEWAY", "loc": [-83.570161, 40.520923], "pop": 857, "state": "OH", "_id": "43345"} -{"city": "ROUNDHEAD", "loc": [-83.848502, 40.580337], "pop": 641, "state": "OH", "_id": "43346"} -{"city": "RUSHSYLVANIA", "loc": [-83.659789, 40.465808], "pop": 1544, "state": "OH", "_id": "43347"} -{"city": "RUSSELLS POINT", "loc": [-83.879825, 40.474956], "pop": 2416, "state": "OH", "_id": "43348"} -{"city": "SPARTA", "loc": [-82.697034, 40.373492], "pop": 966, "state": "OH", "_id": "43350"} -{"city": "UPPER SANDUSKY", "loc": [-83.297711, 40.824876], "pop": 9778, "state": "OH", "_id": "43351"} -{"city": "WALDO", "loc": [-83.070609, 40.460544], "pop": 1170, "state": "OH", "_id": "43356"} -{"city": "WEST LIBERTY", "loc": [-83.752763, 40.262541], "pop": 4148, "state": "OH", "_id": "43357"} -{"city": "WEST MANSFIELD", "loc": [-83.524274, 40.404284], "pop": 2580, "state": "OH", "_id": "43358"} -{"city": "WHARTON", "loc": [-83.463016, 40.861216], "pop": 378, "state": "OH", "_id": "43359"} -{"city": "ZANESFIELD", "loc": [-83.664832, 40.302396], "pop": 1171, "state": "OH", "_id": "43360"} -{"city": "BOWLING GREEN", "loc": [-83.650749, 41.381513], "pop": 34468, "state": "OH", "_id": "43402"} -{"city": "BRADNER", "loc": [-83.4456, 41.329789], "pop": 1934, "state": "OH", "_id": "43406"} -{"city": "BURGOON", "loc": [-83.247455, 41.267986], "pop": 572, "state": "OH", "_id": "43407"} -{"city": "CLYDE", "loc": [-82.991849, 41.302397], "pop": 9650, "state": "OH", "_id": "43410"} -{"city": "CURTICE", "loc": [-83.285825, 41.647736], "pop": 2426, "state": "OH", "_id": "43412"} -{"city": "CYGNET", "loc": [-83.614151, 41.247024], "pop": 1516, "state": "OH", "_id": "43413"} -{"city": "ELMORE", "loc": [-83.27673, 41.468144], "pop": 3230, "state": "OH", "_id": "43416"} -{"city": "FREMONT", "loc": [-83.118095, 41.349792], "pop": 31615, "state": "OH", "_id": "43420"} -{"city": "GENOA", "loc": [-83.358984, 41.530005], "pop": 5405, "state": "OH", "_id": "43430"} -{"city": "GIBSONBURG", "loc": [-83.335762, 41.380474], "pop": 3874, "state": "OH", "_id": "43431"} -{"city": "ELLISTON", "loc": [-83.261135, 41.527136], "pop": 115, "state": "OH", "_id": "43432"} -{"city": "MILLERSVILLE", "loc": [-83.323184, 41.318005], "pop": 1427, "state": "OH", "_id": "43435"} -{"city": "ISLE SAINT GEORG", "loc": [-82.819275, 41.713668], "pop": 38, "state": "OH", "_id": "43436"} -{"city": "KELLEYS ISLAND", "loc": [-82.706811, 41.600755], "pop": 172, "state": "OH", "_id": "43438"} -{"city": "LACARNE", "loc": [-83.024004, 41.527846], "pop": 1457, "state": "OH", "_id": "43439"} -{"city": "LAKESIDE", "loc": [-82.771392, 41.52913], "pop": 4410, "state": "OH", "_id": "43440"} -{"city": "LINDSEY", "loc": [-83.213474, 41.414747], "pop": 1646, "state": "OH", "_id": "43442"} -{"city": "LUCKEY", "loc": [-83.467396, 41.451669], "pop": 1771, "state": "OH", "_id": "43443"} -{"city": "BONO", "loc": [-83.345536, 41.596249], "pop": 3895, "state": "OH", "_id": "43445"} -{"city": "MILLBURY", "loc": [-83.438083, 41.56106], "pop": 3100, "state": "OH", "_id": "43447"} -{"city": "OAK HARBOR", "loc": [-83.127764, 41.523556], "pop": 9620, "state": "OH", "_id": "43449"} -{"city": "PEMBERVILLE", "loc": [-83.473642, 41.402258], "pop": 2569, "state": "OH", "_id": "43450"} -{"city": "PORTAGE", "loc": [-83.614299, 41.312702], "pop": 1304, "state": "OH", "_id": "43451"} -{"city": "PORT CLINTON", "loc": [-82.909368, 41.521535], "pop": 11851, "state": "OH", "_id": "43452"} -{"city": "PUT IN BAY", "loc": [-82.822593, 41.651356], "pop": 518, "state": "OH", "_id": "43456"} -{"city": "RISINGSUN", "loc": [-83.43259, 41.270639], "pop": 1756, "state": "OH", "_id": "43457"} -{"city": "ROSSFORD", "loc": [-83.563793, 41.604908], "pop": 5861, "state": "OH", "_id": "43460"} -{"city": "RUDOLPH", "loc": [-83.683212, 41.296734], "pop": 1737, "state": "OH", "_id": "43462"} -{"city": "VICKERY", "loc": [-82.898953, 41.39099], "pop": 1976, "state": "OH", "_id": "43464"} -{"city": "WALBRIDGE", "loc": [-83.493008, 41.586067], "pop": 4597, "state": "OH", "_id": "43465"} -{"city": "WAYNE", "loc": [-83.470065, 41.299312], "pop": 1572, "state": "OH", "_id": "43466"} -{"city": "WOODVILLE", "loc": [-83.364643, 41.451206], "pop": 2968, "state": "OH", "_id": "43469"} -{"city": "ALVORDTON", "loc": [-84.435533, 41.662489], "pop": 954, "state": "OH", "_id": "43501"} -{"city": "ARCHBOLD", "loc": [-84.304833, 41.533255], "pop": 6565, "state": "OH", "_id": "43502"} -{"city": "BERKEY", "loc": [-83.830972, 41.698892], "pop": 843, "state": "OH", "_id": "43504"} -{"city": "BRYAN", "loc": [-84.56287, 41.474839], "pop": 14693, "state": "OH", "_id": "43506"} -{"city": "CUSTAR", "loc": [-83.834919, 41.295318], "pop": 1049, "state": "OH", "_id": "43511"} -{"city": "DEFIANCE", "loc": [-84.362583, 41.279858], "pop": 29859, "state": "OH", "_id": "43512"} -{"city": "DELTA", "loc": [-83.9866, 41.557695], "pop": 11110, "state": "OH", "_id": "43515"} -{"city": "DESHLER", "loc": [-83.896434, 41.223945], "pop": 4043, "state": "OH", "_id": "43516"} -{"city": "EDGERTON", "loc": [-84.734937, 41.442496], "pop": 3663, "state": "OH", "_id": "43517"} -{"city": "EDON", "loc": [-84.757002, 41.584219], "pop": 2688, "state": "OH", "_id": "43518"} -{"city": "FAYETTE", "loc": [-84.325004, 41.671716], "pop": 2248, "state": "OH", "_id": "43521"} -{"city": "GRAND RAPIDS", "loc": [-83.855246, 41.437855], "pop": 3362, "state": "OH", "_id": "43522"} -{"city": "HAMLER", "loc": [-84.071989, 41.212874], "pop": 1949, "state": "OH", "_id": "43524"} -{"city": "HASKINS", "loc": [-83.705858, 41.465159], "pop": 549, "state": "OH", "_id": "43525"} -{"city": "HICKSVILLE", "loc": [-84.758852, 41.303378], "pop": 5541, "state": "OH", "_id": "43526"} -{"city": "HOLGATE", "loc": [-84.144735, 41.254859], "pop": 2032, "state": "OH", "_id": "43527"} -{"city": "HOLLAND", "loc": [-83.725712, 41.622629], "pop": 10773, "state": "OH", "_id": "43528"} -{"city": "LIBERTY CENTER", "loc": [-83.985889, 41.451371], "pop": 3933, "state": "OH", "_id": "43532"} -{"city": "LYONS", "loc": [-84.062351, 41.690546], "pop": 1395, "state": "OH", "_id": "43533"} -{"city": "MC CLURE", "loc": [-83.942482, 41.377309], "pop": 1876, "state": "OH", "_id": "43534"} -{"city": "MALINTA", "loc": [-84.045731, 41.308425], "pop": 1121, "state": "OH", "_id": "43535"} -{"city": "MARK CENTER", "loc": [-84.62778, 41.291669], "pop": 963, "state": "OH", "_id": "43536"} -{"city": "MAUMEE", "loc": [-83.66283, 41.581682], "pop": 22993, "state": "OH", "_id": "43537"} -{"city": "METAMORA", "loc": [-83.925972, 41.695233], "pop": 1531, "state": "OH", "_id": "43540"} -{"city": "MONCLOVA", "loc": [-83.775718, 41.568416], "pop": 2286, "state": "OH", "_id": "43542"} -{"city": "MONTPELIER", "loc": [-84.614703, 41.59821], "pop": 7569, "state": "OH", "_id": "43543"} -{"city": "NAPOLEON", "loc": [-84.143271, 41.390969], "pop": 14196, "state": "OH", "_id": "43545"} -{"city": "NEW BAVARIA", "loc": [-84.191336, 41.208649], "pop": 523, "state": "OH", "_id": "43548"} -{"city": "NEY", "loc": [-84.526921, 41.378083], "pop": 1801, "state": "OH", "_id": "43549"} -{"city": "PERRYSBURG", "loc": [-83.592727, 41.542926], "pop": 29621, "state": "OH", "_id": "43551"} -{"city": "PIONEER", "loc": [-84.536324, 41.665619], "pop": 2350, "state": "OH", "_id": "43554"} -{"city": "SHERWOOD", "loc": [-84.541674, 41.294593], "pop": 1686, "state": "OH", "_id": "43556"} -{"city": "STRYKER", "loc": [-84.408914, 41.486123], "pop": 2607, "state": "OH", "_id": "43557"} -{"city": "SWANTON", "loc": [-83.871821, 41.594497], "pop": 10729, "state": "OH", "_id": "43558"} -{"city": "SYLVANIA", "loc": [-83.706825, 41.707985], "pop": 24564, "state": "OH", "_id": "43560"} -{"city": "WATERVILLE", "loc": [-83.733142, 41.502248], "pop": 5918, "state": "OH", "_id": "43566"} -{"city": "WAUSEON", "loc": [-84.153745, 41.566796], "pop": 10493, "state": "OH", "_id": "43567"} -{"city": "WESTON", "loc": [-83.797336, 41.351593], "pop": 2560, "state": "OH", "_id": "43569"} -{"city": "WEST UNITY", "loc": [-84.442066, 41.575645], "pop": 3305, "state": "OH", "_id": "43570"} -{"city": "WHITEHOUSE", "loc": [-83.81151, 41.519432], "pop": 5199, "state": "OH", "_id": "43571"} -{"city": "TOLEDO", "loc": [-83.554747, 41.647984], "pop": 4084, "state": "OH", "_id": "43602"} -{"city": "TOLEDO", "loc": [-83.524949, 41.661415], "pop": 5506, "state": "OH", "_id": "43604"} -{"city": "OREGON", "loc": [-83.512341, 41.640701], "pop": 33168, "state": "OH", "_id": "43605"} -{"city": "TOLEDO", "loc": [-83.605992, 41.671213], "pop": 29111, "state": "OH", "_id": "43606"} -{"city": "TOLEDO", "loc": [-83.597419, 41.650417], "pop": 30240, "state": "OH", "_id": "43607"} -{"city": "TOLEDO", "loc": [-83.534359, 41.677908], "pop": 21427, "state": "OH", "_id": "43608"} -{"city": "TOLEDO", "loc": [-83.577282, 41.629761], "pop": 29228, "state": "OH", "_id": "43609"} -{"city": "TOLEDO", "loc": [-83.557303, 41.676693], "pop": 8374, "state": "OH", "_id": "43610"} -{"city": "TOLEDO", "loc": [-83.489203, 41.704507], "pop": 23349, "state": "OH", "_id": "43611"} -{"city": "TOLEDO", "loc": [-83.565622, 41.704567], "pop": 33408, "state": "OH", "_id": "43612"} -{"city": "TOLEDO", "loc": [-83.603397, 41.703913], "pop": 35327, "state": "OH", "_id": "43613"} -{"city": "TOLEDO", "loc": [-83.62917, 41.60279], "pop": 31020, "state": "OH", "_id": "43614"} -{"city": "TOLEDO", "loc": [-83.670583, 41.649197], "pop": 38173, "state": "OH", "_id": "43615"} -{"city": "OREGON", "loc": [-83.471366, 41.641842], "pop": 15713, "state": "OH", "_id": "43616"} -{"city": "TOLEDO", "loc": [-83.716967, 41.666765], "pop": 7449, "state": "OH", "_id": "43617"} -{"city": "OREGON", "loc": [-83.392302, 41.665636], "pop": 3379, "state": "OH", "_id": "43618"} -{"city": "NORTHWOOD", "loc": [-83.48056, 41.607986], "pop": 7526, "state": "OH", "_id": "43619"} -{"city": "TOLEDO", "loc": [-83.553602, 41.66536], "pop": 8471, "state": "OH", "_id": "43620"} -{"city": "TOLEDO", "loc": [-83.643408, 41.707968], "pop": 21315, "state": "OH", "_id": "43623"} -{"city": "TOLEDO", "loc": [-83.545005, 41.65627], "pop": 1596, "state": "OH", "_id": "43624"} -{"city": "SONORA", "loc": [-82.008898, 39.944265], "pop": 56655, "state": "OH", "_id": "43701"} -{"city": "SOMERTON", "loc": [-81.171295, 39.9812], "pop": 7140, "state": "OH", "_id": "43713"} -{"city": "BEALLSVILLE", "loc": [-81.014358, 39.872601], "pop": 716, "state": "OH", "_id": "43716"} -{"city": "BELMONT", "loc": [-81.006564, 40.03202], "pop": 3229, "state": "OH", "_id": "43718"} -{"city": "BETHESDA", "loc": [-81.076742, 40.019221], "pop": 2464, "state": "OH", "_id": "43719"} -{"city": "BLUE ROCK", "loc": [-81.891023, 39.799996], "pop": 1001, "state": "OH", "_id": "43720"} -{"city": "BYESVILLE", "loc": [-81.548485, 39.962336], "pop": 5164, "state": "OH", "_id": "43723"} -{"city": "CALDWELL", "loc": [-81.51528, 39.746651], "pop": 7043, "state": "OH", "_id": "43724"} -{"city": "CLAYSVILLE", "loc": [-81.591277, 40.030501], "pop": 21261, "state": "OH", "_id": "43725"} -{"city": "CHANDLERSVILLE", "loc": [-81.830084, 39.889659], "pop": 1368, "state": "OH", "_id": "43727"} -{"city": "CHESTERHILL", "loc": [-81.877286, 39.495277], "pop": 1323, "state": "OH", "_id": "43728"} -{"city": "HEMLOCK", "loc": [-82.097737, 39.634902], "pop": 3374, "state": "OH", "_id": "43730"} -{"city": "CROOKSVILLE", "loc": [-82.084018, 39.762321], "pop": 4609, "state": "OH", "_id": "43731"} -{"city": "CUMBERLAND", "loc": [-81.625925, 39.874092], "pop": 1831, "state": "OH", "_id": "43732"} -{"city": "DUNCAN FALLS", "loc": [-81.911665, 39.877777], "pop": 957, "state": "OH", "_id": "43734"} -{"city": "GLENFORD", "loc": [-82.302592, 39.869935], "pop": 1632, "state": "OH", "_id": "43739"} -{"city": "HOPEWELL", "loc": [-82.175619, 39.96008], "pop": 1160, "state": "OH", "_id": "43746"} -{"city": "JERUSALEM", "loc": [-81.092088, 39.848831], "pop": 2037, "state": "OH", "_id": "43747"} -{"city": "JUNCTION CITY", "loc": [-82.315525, 39.696531], "pop": 2577, "state": "OH", "_id": "43748"} -{"city": "GUERNSEY", "loc": [-81.53036, 40.166544], "pop": 2109, "state": "OH", "_id": "43749"} -{"city": "LEWISVILLE", "loc": [-81.231583, 39.768379], "pop": 1366, "state": "OH", "_id": "43754"} -{"city": "LORE CITY", "loc": [-81.447853, 40.045854], "pop": 1487, "state": "OH", "_id": "43755"} -{"city": "MC CONNELSVILLE", "loc": [-81.837625, 39.670014], "pop": 4784, "state": "OH", "_id": "43756"} -{"city": "MALTA", "loc": [-81.912746, 39.648212], "pop": 3127, "state": "OH", "_id": "43758"} -{"city": "MOUNT PERRY", "loc": [-82.188039, 39.878798], "pop": 1550, "state": "OH", "_id": "43760"} -{"city": "NEW CONCORD", "loc": [-81.738727, 40.008798], "pop": 4770, "state": "OH", "_id": "43762"} -{"city": "NEW LEXINGTON", "loc": [-82.201884, 39.717438], "pop": 8536, "state": "OH", "_id": "43764"} -{"city": "NEW STRAITSVILLE", "loc": [-82.248804, 39.586872], "pop": 1669, "state": "OH", "_id": "43766"} -{"city": "NORWICH", "loc": [-81.802425, 39.993438], "pop": 1142, "state": "OH", "_id": "43767"} -{"city": "PHILO", "loc": [-81.917479, 39.845773], "pop": 1374, "state": "OH", "_id": "43771"} -{"city": "PLEASANT CITY", "loc": [-81.55797, 39.909514], "pop": 1231, "state": "OH", "_id": "43772"} -{"city": "QUAKER CITY", "loc": [-81.289883, 39.986576], "pop": 2139, "state": "OH", "_id": "43773"} -{"city": "ROSEVILLE", "loc": [-82.079212, 39.818656], "pop": 4293, "state": "OH", "_id": "43777"} -{"city": "SALESVILLE", "loc": [-81.372793, 40.008146], "pop": 1009, "state": "OH", "_id": "43778"} -{"city": "SARAHSVILLE", "loc": [-81.467825, 39.793846], "pop": 947, "state": "OH", "_id": "43779"} -{"city": "SENECAVILLE", "loc": [-81.458043, 39.933719], "pop": 3014, "state": "OH", "_id": "43780"} -{"city": "SHAWNEE", "loc": [-82.208459, 39.610984], "pop": 1019, "state": "OH", "_id": "43782"} -{"city": "SOMERSET", "loc": [-82.29911, 39.793615], "pop": 2541, "state": "OH", "_id": "43783"} -{"city": "PENNSVILLE", "loc": [-81.825251, 39.561434], "pop": 1865, "state": "OH", "_id": "43787"} -{"city": "SUMMERFIELD", "loc": [-81.331993, 39.803597], "pop": 1151, "state": "OH", "_id": "43788"} -{"city": "ANTIOCH", "loc": [-81.10336, 39.753063], "pop": 5231, "state": "OH", "_id": "43793"} -{"city": "ADAMSVILLE", "loc": [-81.871847, 40.07929], "pop": 943, "state": "OH", "_id": "43802"} -{"city": "BALTIC", "loc": [-81.679171, 40.447568], "pop": 2225, "state": "OH", "_id": "43804"} -{"city": "CONESVILLE", "loc": [-81.895074, 40.180441], "pop": 696, "state": "OH", "_id": "43811"} -{"city": "COSHOCTON", "loc": [-81.866033, 40.275412], "pop": 21561, "state": "OH", "_id": "43812"} -{"city": "ADAMS MILLS", "loc": [-82.018807, 40.112941], "pop": 4204, "state": "OH", "_id": "43821"} -{"city": "FRAZEYSBURG", "loc": [-82.129279, 40.131616], "pop": 2746, "state": "OH", "_id": "43822"} -{"city": "FRESNO", "loc": [-81.762297, 40.371126], "pop": 2777, "state": "OH", "_id": "43824"} -{"city": "NASHPORT", "loc": [-82.09976, 40.038588], "pop": 3579, "state": "OH", "_id": "43830"} -{"city": "NEWCOMERSTOWN", "loc": [-81.593969, 40.273895], "pop": 7574, "state": "OH", "_id": "43832"} -{"city": "PORT WASHINGTON", "loc": [-81.521518, 40.340359], "pop": 1190, "state": "OH", "_id": "43837"} -{"city": "STONE CREEK", "loc": [-81.589018, 40.405188], "pop": 1208, "state": "OH", "_id": "43840"} -{"city": "WALHONDING", "loc": [-82.209359, 40.362037], "pop": 937, "state": "OH", "_id": "43843"} -{"city": "WARSAW", "loc": [-82.055989, 40.317243], "pop": 3177, "state": "OH", "_id": "43844"} -{"city": "WEST LAFAYETTE", "loc": [-81.736102, 40.271829], "pop": 4667, "state": "OH", "_id": "43845"} -{"city": "ADENA", "loc": [-80.881538, 40.212581], "pop": 1690, "state": "OH", "_id": "43901"} -{"city": "ALLEDONIA", "loc": [-80.957753, 39.905271], "pop": 499, "state": "OH", "_id": "43902"} -{"city": "AMSTERDAM", "loc": [-80.959554, 40.473131], "pop": 819, "state": "OH", "_id": "43903"} -{"city": "BELLAIRE", "loc": [-80.763813, 40.020399], "pop": 10480, "state": "OH", "_id": "43906"} -{"city": "MOOREFIELD", "loc": [-80.996244, 40.264537], "pop": 5925, "state": "OH", "_id": "43907"} -{"city": "BERGHOLZ", "loc": [-80.886153, 40.47771], "pop": 3392, "state": "OH", "_id": "43908"} -{"city": "BLOOMINGDALE", "loc": [-80.807214, 40.3742], "pop": 3925, "state": "OH", "_id": "43910"} -{"city": "BRIDGEPORT", "loc": [-80.774682, 40.075184], "pop": 7706, "state": "OH", "_id": "43912"} -{"city": "BRILLIANT", "loc": [-80.631949, 40.268283], "pop": 2057, "state": "OH", "_id": "43913"} -{"city": "CLARINGTON", "loc": [-80.911311, 39.781908], "pop": 1890, "state": "OH", "_id": "43915"} -{"city": "DILLONVALE", "loc": [-80.802843, 40.212132], "pop": 5972, "state": "OH", "_id": "43917"} -{"city": "CALCUTTA", "loc": [-80.578669, 40.645918], "pop": 25993, "state": "OH", "_id": "43920"} -{"city": "HAMMONDSVILLE", "loc": [-80.729918, 40.579909], "pop": 1060, "state": "OH", "_id": "43930"} -{"city": "IRONDALE", "loc": [-80.791546, 40.511052], "pop": 496, "state": "OH", "_id": "43932"} -{"city": "ARMSTRONG MILLS", "loc": [-80.882181, 39.956304], "pop": 1795, "state": "OH", "_id": "43933"} -{"city": "MARTINS FERRY", "loc": [-80.736125, 40.103597], "pop": 10605, "state": "OH", "_id": "43935"} -{"city": "MINGO JUNCTION", "loc": [-80.625021, 40.320256], "pop": 6388, "state": "OH", "_id": "43938"} -{"city": "POWHATAN POINT", "loc": [-80.8168, 39.867935], "pop": 2694, "state": "OH", "_id": "43942"} -{"city": "RAYLAND", "loc": [-80.712534, 40.208274], "pop": 4623, "state": "OH", "_id": "43943"} -{"city": "RICHMOND", "loc": [-80.76128, 40.426061], "pop": 2326, "state": "OH", "_id": "43944"} -{"city": "SALINEVILLE", "loc": [-80.834977, 40.619525], "pop": 3617, "state": "OH", "_id": "43945"} -{"city": "SARDIS", "loc": [-80.924308, 39.652639], "pop": 2785, "state": "OH", "_id": "43946"} -{"city": "SHADYSIDE", "loc": [-80.764309, 39.967497], "pop": 5918, "state": "OH", "_id": "43947"} -{"city": "SAINT CLAIRSVILL", "loc": [-80.902285, 40.083439], "pop": 14292, "state": "OH", "_id": "43950"} -{"city": "WINTERSVILLE", "loc": [-80.66115, 40.370638], "pop": 35873, "state": "OH", "_id": "43952"} -{"city": "TILTONSVILLE", "loc": [-80.699647, 40.168075], "pop": 872, "state": "OH", "_id": "43963"} -{"city": "TORONTO", "loc": [-80.632504, 40.473298], "pop": 11981, "state": "OH", "_id": "43964"} -{"city": "WELLSVILLE", "loc": [-80.662095, 40.617099], "pop": 8315, "state": "OH", "_id": "43968"} -{"city": "YORKVILLE", "loc": [-80.707737, 40.158051], "pop": 1213, "state": "OH", "_id": "43971"} -{"city": "FREEPORT", "loc": [-81.276955, 40.192502], "pop": 1905, "state": "OH", "_id": "43973"} -{"city": "HOPEDALE", "loc": [-80.902136, 40.349647], "pop": 1501, "state": "OH", "_id": "43976"} -{"city": "FLUSHING", "loc": [-81.07573, 40.145144], "pop": 2719, "state": "OH", "_id": "43977"} -{"city": "PIEDMONT", "loc": [-81.214494, 40.150716], "pop": 420, "state": "OH", "_id": "43983"} -{"city": "JEWETT", "loc": [-81.000379, 40.37446], "pop": 1998, "state": "OH", "_id": "43986"} -{"city": "SCIO", "loc": [-81.101577, 40.40116], "pop": 2640, "state": "OH", "_id": "43988"} -{"city": "SOUTH AMHERST", "loc": [-82.228165, 41.390506], "pop": 18839, "state": "OH", "_id": "44001"} -{"city": "ANDOVER", "loc": [-80.575374, 41.622488], "pop": 3491, "state": "OH", "_id": "44003"} -{"city": "ASHTABULA", "loc": [-80.794681, 41.867877], "pop": 37480, "state": "OH", "_id": "44004"} -{"city": "AUSTINBURG", "loc": [-80.858422, 41.75536], "pop": 1902, "state": "OH", "_id": "44010"} -{"city": "AVON", "loc": [-82.020359, 41.446713], "pop": 7332, "state": "OH", "_id": "44011"} -{"city": "AVON LAKE", "loc": [-82.011094, 41.501904], "pop": 15061, "state": "OH", "_id": "44012"} -{"city": "BEREA", "loc": [-81.861756, 41.367557], "pop": 18561, "state": "OH", "_id": "44017"} -{"city": "BURTON", "loc": [-81.15262, 41.452702], "pop": 6677, "state": "OH", "_id": "44021"} -{"city": "CHAGRIN FALLS", "loc": [-81.361437, 41.418577], "pop": 29184, "state": "OH", "_id": "44022"} -{"city": "CHARDON", "loc": [-81.205629, 41.571862], "pop": 20339, "state": "OH", "_id": "44024"} -{"city": "CHESTERLAND", "loc": [-81.342102, 41.534378], "pop": 12510, "state": "OH", "_id": "44026"} -{"city": "COLUMBIA STATION", "loc": [-81.934398, 41.318708], "pop": 8436, "state": "OH", "_id": "44028"} -{"city": "CONNEAUT", "loc": [-80.580257, 41.934479], "pop": 16612, "state": "OH", "_id": "44030"} -{"city": "DORSET", "loc": [-80.668334, 41.658972], "pop": 1536, "state": "OH", "_id": "44032"} -{"city": "ELYRIA", "loc": [-82.105088, 41.372353], "pop": 66674, "state": "OH", "_id": "44035"} -{"city": "NORTH RIDGEVILLE", "loc": [-82.003323, 41.396432], "pop": 21574, "state": "OH", "_id": "44039"} -{"city": "GATES MILLS", "loc": [-81.415021, 41.532368], "pop": 2879, "state": "OH", "_id": "44040"} -{"city": "GENEVA", "loc": [-80.947363, 41.802864], "pop": 14694, "state": "OH", "_id": "44041"} -{"city": "GRAFTON", "loc": [-82.043098, 41.28537], "pop": 12127, "state": "OH", "_id": "44044"} -{"city": "HUNTSBURG", "loc": [-81.057183, 41.530559], "pop": 1804, "state": "OH", "_id": "44046"} -{"city": "JEFFERSON", "loc": [-80.756166, 41.733513], "pop": 8242, "state": "OH", "_id": "44047"} -{"city": "KINGSVILLE", "loc": [-80.660092, 41.872311], "pop": 2192, "state": "OH", "_id": "44048"} -{"city": "LAGRANGE", "loc": [-82.127918, 41.242278], "pop": 5092, "state": "OH", "_id": "44050"} -{"city": "LORAIN", "loc": [-82.171039, 41.457796], "pop": 35989, "state": "OH", "_id": "44052"} -{"city": "LORAIN", "loc": [-82.203833, 41.432002], "pop": 16141, "state": "OH", "_id": "44053"} -{"city": "SHEFFIELD LAKE", "loc": [-82.096497, 41.482276], "pop": 11685, "state": "OH", "_id": "44054"} -{"city": "LORAIN", "loc": [-82.134992, 41.436131], "pop": 22919, "state": "OH", "_id": "44055"} -{"city": "MACEDONIA", "loc": [-81.499578, 41.322236], "pop": 7470, "state": "OH", "_id": "44056"} -{"city": "MADISON", "loc": [-81.058819, 41.805367], "pop": 18357, "state": "OH", "_id": "44057"} -{"city": "MENTOR", "loc": [-81.342133, 41.689468], "pop": 60109, "state": "OH", "_id": "44060"} -{"city": "MIDDLEFIELD", "loc": [-81.037338, 41.445547], "pop": 11700, "state": "OH", "_id": "44062"} -{"city": "MONTVILLE", "loc": [-81.057036, 41.603446], "pop": 1867, "state": "OH", "_id": "44064"} -{"city": "NEWBURY", "loc": [-81.23452, 41.475022], "pop": 2907, "state": "OH", "_id": "44065"} -{"city": "NORTHFIELD", "loc": [-81.542943, 41.320821], "pop": 14014, "state": "OH", "_id": "44067"} -{"city": "NORTH OLMSTED", "loc": [-81.913056, 41.420129], "pop": 34123, "state": "OH", "_id": "44070"} -{"city": "NOVELTY", "loc": [-81.334228, 41.476288], "pop": 4736, "state": "OH", "_id": "44072"} -{"city": "OBERLIN", "loc": [-82.222863, 41.28987], "pop": 11631, "state": "OH", "_id": "44074"} -{"city": "EAST ORWELL", "loc": [-80.839671, 41.533456], "pop": 3814, "state": "OH", "_id": "44076"} -{"city": "FAIRPORT HARBOR", "loc": [-81.243665, 41.714014], "pop": 43783, "state": "OH", "_id": "44077"} -{"city": "PERRY", "loc": [-81.143312, 41.767916], "pop": 5571, "state": "OH", "_id": "44081"} -{"city": "PIERPONT", "loc": [-80.574114, 41.767661], "pop": 1460, "state": "OH", "_id": "44082"} -{"city": "ROAMING SHORES", "loc": [-80.885134, 41.6651], "pop": 2979, "state": "OH", "_id": "44084"} -{"city": "ROAMING SHORES", "loc": [-80.832075, 41.602629], "pop": 1810, "state": "OH", "_id": "44085"} -{"city": "THOMPSON", "loc": [-81.057318, 41.676189], "pop": 2668, "state": "OH", "_id": "44086"} -{"city": "TWINSBURG", "loc": [-81.455912, 41.328851], "pop": 11274, "state": "OH", "_id": "44087"} -{"city": "VERMILION", "loc": [-82.355417, 41.409989], "pop": 15058, "state": "OH", "_id": "44089"} -{"city": "WELLINGTON", "loc": [-82.226915, 41.171178], "pop": 9870, "state": "OH", "_id": "44090"} -{"city": "WICKLIFFE", "loc": [-81.469175, 41.604565], "pop": 18334, "state": "OH", "_id": "44092"} -{"city": "WILLIAMSFIELD", "loc": [-80.596378, 41.538296], "pop": 1817, "state": "OH", "_id": "44093"} -{"city": "WILLOUGHBY", "loc": [-81.407619, 41.630229], "pop": 30153, "state": "OH", "_id": "44094"} -{"city": "WILLOWICK", "loc": [-81.447887, 41.649822], "pop": 37473, "state": "OH", "_id": "44095"} -{"city": "WINDSOR", "loc": [-80.966745, 41.56233], "pop": 2085, "state": "OH", "_id": "44099"} -{"city": "CLEVELAND", "loc": [-81.739791, 41.473508], "pop": 53416, "state": "OH", "_id": "44102"} -{"city": "CLEVELAND", "loc": [-81.640475, 41.515726], "pop": 28288, "state": "OH", "_id": "44103"} -{"city": "CLEVELAND", "loc": [-81.624502, 41.480924], "pop": 34766, "state": "OH", "_id": "44104"} -{"city": "CLEVELAND", "loc": [-81.619002, 41.450912], "pop": 56341, "state": "OH", "_id": "44105"} -{"city": "CLEVELAND", "loc": [-81.60757, 41.508359], "pop": 35787, "state": "OH", "_id": "44106"} -{"city": "EDGEWATER", "loc": [-81.797143, 41.482654], "pop": 59702, "state": "OH", "_id": "44107"} -{"city": "CLEVELAND", "loc": [-81.608974, 41.53492], "pop": 40401, "state": "OH", "_id": "44108"} -{"city": "CLEVELAND", "loc": [-81.703315, 41.445768], "pop": 47515, "state": "OH", "_id": "44109"} -{"city": "CLEVELAND", "loc": [-81.573276, 41.563557], "pop": 26613, "state": "OH", "_id": "44110"} -{"city": "CLEVELAND", "loc": [-81.78435, 41.457066], "pop": 43366, "state": "OH", "_id": "44111"} -{"city": "EAST CLEVELAND", "loc": [-81.576262, 41.535517], "pop": 41340, "state": "OH", "_id": "44112"} -{"city": "CLEVELAND", "loc": [-81.701848, 41.481648], "pop": 20211, "state": "OH", "_id": "44113"} -{"city": "CLEVELAND", "loc": [-81.67425, 41.506351], "pop": 4673, "state": "OH", "_id": "44114"} -{"city": "CLEVELAND", "loc": [-81.667009, 41.494574], "pop": 7035, "state": "OH", "_id": "44115"} -{"city": "ROCKY RIVER", "loc": [-81.851246, 41.469401], "pop": 20410, "state": "OH", "_id": "44116"} -{"city": "EUCLID", "loc": [-81.525686, 41.569615], "pop": 12371, "state": "OH", "_id": "44117"} -{"city": "CLEVELAND HEIGHT", "loc": [-81.553945, 41.501213], "pop": 45619, "state": "OH", "_id": "44118"} -{"city": "CLEVELAND", "loc": [-81.546759, 41.588238], "pop": 13654, "state": "OH", "_id": "44119"} -{"city": "CLEVELAND", "loc": [-81.583911, 41.471433], "pop": 49357, "state": "OH", "_id": "44120"} -{"city": "SOUTH EUCLID", "loc": [-81.533758, 41.526019], "pop": 37107, "state": "OH", "_id": "44121"} -{"city": "BEACHWOOD", "loc": [-81.523172, 41.470109], "pop": 35115, "state": "OH", "_id": "44122"} -{"city": "SHORE", "loc": [-81.524325, 41.604416], "pop": 18724, "state": "OH", "_id": "44123"} -{"city": "LYNDHURST MAYFIE", "loc": [-81.46801, 41.514349], "pop": 41699, "state": "OH", "_id": "44124"} -{"city": "GARFIELD HEIGHTS", "loc": [-81.605385, 41.415792], "pop": 31020, "state": "OH", "_id": "44125"} -{"city": "FAIRVIEW PARK", "loc": [-81.856381, 41.4433], "pop": 18145, "state": "OH", "_id": "44126"} -{"city": "CLEVELAND", "loc": [-81.648999, 41.470125], "pop": 9046, "state": "OH", "_id": "44127"} -{"city": "CLEVELAND", "loc": [-81.548574, 41.441565], "pop": 35258, "state": "OH", "_id": "44128"} -{"city": "PARMA", "loc": [-81.734604, 41.396474], "pop": 30370, "state": "OH", "_id": "44129"} -{"city": "MIDPARK", "loc": [-81.774858, 41.377178], "pop": 52198, "state": "OH", "_id": "44130"} -{"city": "INDEPENDENCE", "loc": [-81.66421, 41.380905], "pop": 20594, "state": "OH", "_id": "44131"} -{"city": "NOBLE", "loc": [-81.499056, 41.608516], "pop": 16398, "state": "OH", "_id": "44132"} -{"city": "NORTH ROYALTON", "loc": [-81.745657, 41.323164], "pop": 23197, "state": "OH", "_id": "44133"} -{"city": "PARMA", "loc": [-81.705726, 41.390764], "pop": 41397, "state": "OH", "_id": "44134"} -{"city": "CLEVELAND", "loc": [-81.804433, 41.434177], "pop": 31032, "state": "OH", "_id": "44135"} -{"city": "STRONGSVILLE", "loc": [-81.828457, 41.313218], "pop": 35308, "state": "OH", "_id": "44136"} -{"city": "MAPLE HEIGHTS", "loc": [-81.560339, 41.410513], "pop": 26945, "state": "OH", "_id": "44137"} -{"city": "OLMSTED FALLS", "loc": [-81.915769, 41.373351], "pop": 15722, "state": "OH", "_id": "44138"} -{"city": "SOLON", "loc": [-81.442082, 41.386597], "pop": 18830, "state": "OH", "_id": "44139"} -{"city": "BAY VILLAGE", "loc": [-81.928868, 41.484137], "pop": 17000, "state": "OH", "_id": "44140"} -{"city": "BRECKSVILLE", "loc": [-81.626083, 41.316554], "pop": 12172, "state": "OH", "_id": "44141"} -{"city": "BROOKPARK", "loc": [-81.811811, 41.397944], "pop": 22865, "state": "OH", "_id": "44142"} -{"city": "RICHMOND HEIGHTS", "loc": [-81.484715, 41.552195], "pop": 21114, "state": "OH", "_id": "44143"} -{"city": "BROOKLYN", "loc": [-81.735222, 41.434419], "pop": 22288, "state": "OH", "_id": "44144"} -{"city": "WESTLAKE", "loc": [-81.921771, 41.453459], "pop": 27099, "state": "OH", "_id": "44145"} -{"city": "BEDFORD", "loc": [-81.52315, 41.392067], "pop": 33014, "state": "OH", "_id": "44146"} -{"city": "BROADVIEW HEIGHT", "loc": [-81.680879, 41.32907], "pop": 11951, "state": "OH", "_id": "44147"} -{"city": "ATWATER", "loc": [-81.198456, 41.033487], "pop": 7081, "state": "OH", "_id": "44201"} -{"city": "REMINDERVILLE", "loc": [-81.360424, 41.320935], "pop": 11547, "state": "OH", "_id": "44202"} -{"city": "NORTON", "loc": [-81.615314, 41.018589], "pop": 42160, "state": "OH", "_id": "44203"} -{"city": "BRUNSWICK", "loc": [-81.827968, 41.247053], "pop": 32435, "state": "OH", "_id": "44212"} -{"city": "BURBANK", "loc": [-81.995753, 40.963725], "pop": 1298, "state": "OH", "_id": "44214"} -{"city": "CHIPPEWA LAKE", "loc": [-81.909173, 41.06719], "pop": 2714, "state": "OH", "_id": "44215"} -{"city": "CLINTON", "loc": [-81.587079, 40.939062], "pop": 8830, "state": "OH", "_id": "44216"} -{"city": "CRESTON", "loc": [-81.921083, 40.978846], "pop": 6602, "state": "OH", "_id": "44217"} -{"city": "CUYAHOGA FALLS", "loc": [-81.478961, 41.140082], "pop": 32009, "state": "OH", "_id": "44221"} -{"city": "CUYAHOGA FALLS", "loc": [-81.510719, 41.146448], "pop": 14757, "state": "OH", "_id": "44223"} -{"city": "STOW", "loc": [-81.438017, 41.174808], "pop": 31912, "state": "OH", "_id": "44224"} -{"city": "DOYLESTOWN", "loc": [-81.684845, 40.965042], "pop": 7451, "state": "OH", "_id": "44230"} -{"city": "GARRETTSVILLE", "loc": [-81.070365, 41.298803], "pop": 7352, "state": "OH", "_id": "44231"} -{"city": "HINCKLEY", "loc": [-81.74527, 41.241872], "pop": 5845, "state": "OH", "_id": "44233"} -{"city": "HIRAM", "loc": [-81.14644, 41.332253], "pop": 3775, "state": "OH", "_id": "44234"} -{"city": "HOMERVILLE", "loc": [-82.124957, 41.02669], "pop": 1196, "state": "OH", "_id": "44235"} -{"city": "HUDSON", "loc": [-81.436659, 41.245836], "pop": 18695, "state": "OH", "_id": "44236"} -{"city": "KENT", "loc": [-81.34976, 41.14492], "pop": 43071, "state": "OH", "_id": "44240"} -{"city": "STREETSBORO", "loc": [-81.338298, 41.249099], "pop": 10735, "state": "OH", "_id": "44241"} -{"city": "LITCHFIELD", "loc": [-82.015674, 41.166847], "pop": 2506, "state": "OH", "_id": "44253"} -{"city": "LODI", "loc": [-82.014661, 41.032713], "pop": 4776, "state": "OH", "_id": "44254"} -{"city": "MANTUA", "loc": [-81.228259, 41.294141], "pop": 7324, "state": "OH", "_id": "44255"} -{"city": "MEDINA", "loc": [-81.858351, 41.140415], "pop": 35686, "state": "OH", "_id": "44256"} -{"city": "MOGADORE", "loc": [-81.358963, 41.038196], "pop": 12947, "state": "OH", "_id": "44260"} -{"city": "MUNROE FALLS", "loc": [-81.437565, 41.14202], "pop": 5375, "state": "OH", "_id": "44262"} -{"city": "PENINSULA", "loc": [-81.540013, 41.225579], "pop": 1886, "state": "OH", "_id": "44264"} -{"city": "RAVENNA", "loc": [-81.233656, 41.164886], "pop": 31700, "state": "OH", "_id": "44266"} -{"city": "RITTMAN", "loc": [-81.782599, 40.968381], "pop": 8270, "state": "OH", "_id": "44270"} -{"city": "ROOTSTOWN", "loc": [-81.202642, 41.099534], "pop": 3556, "state": "OH", "_id": "44272"} -{"city": "SEVILLE", "loc": [-81.856199, 41.022731], "pop": 3611, "state": "OH", "_id": "44273"} -{"city": "SPENCER", "loc": [-82.099907, 41.098287], "pop": 2595, "state": "OH", "_id": "44275"} -{"city": "STERLING", "loc": [-81.851914, 40.953263], "pop": 1838, "state": "OH", "_id": "44276"} -{"city": "TALLMADGE", "loc": [-81.425966, 41.097492], "pop": 15402, "state": "OH", "_id": "44278"} -{"city": "VALLEY CITY", "loc": [-81.924467, 41.236806], "pop": 3711, "state": "OH", "_id": "44280"} -{"city": "WADSWORTH", "loc": [-81.737373, 41.038375], "pop": 24707, "state": "OH", "_id": "44281"} -{"city": "RICHFIELD", "loc": [-81.646668, 41.23712], "pop": 4724, "state": "OH", "_id": "44286"} -{"city": "WEST SALEM", "loc": [-82.106638, 40.948514], "pop": 5351, "state": "OH", "_id": "44287"} -{"city": "WINDHAM", "loc": [-81.053451, 41.239244], "pop": 4561, "state": "OH", "_id": "44288"} -{"city": "AKRON", "loc": [-81.520048, 41.044852], "pop": 18356, "state": "OH", "_id": "44301"} -{"city": "AKRON", "loc": [-81.542015, 41.091988], "pop": 6835, "state": "OH", "_id": "44302"} -{"city": "AKRON", "loc": [-81.538609, 41.102508], "pop": 8658, "state": "OH", "_id": "44303"} -{"city": "AKRON", "loc": [-81.508526, 41.080808], "pop": 9521, "state": "OH", "_id": "44304"} -{"city": "AKRON", "loc": [-81.464409, 41.076029], "pop": 25788, "state": "OH", "_id": "44305"} -{"city": "AKRON", "loc": [-81.491554, 41.04791], "pop": 25758, "state": "OH", "_id": "44306"} -{"city": "AKRON", "loc": [-81.548786, 41.069465], "pop": 9945, "state": "OH", "_id": "44307"} -{"city": "AKRON", "loc": [-81.519363, 41.079576], "pop": 1113, "state": "OH", "_id": "44308"} -{"city": "AKRON", "loc": [-81.500586, 41.107547], "pop": 25482, "state": "OH", "_id": "44310"} -{"city": "AKRON", "loc": [-81.520005, 41.063784], "pop": 9040, "state": "OH", "_id": "44311"} -{"city": "AKRON", "loc": [-81.438528, 41.033442], "pop": 32097, "state": "OH", "_id": "44312"} -{"city": "AKRON", "loc": [-81.568487, 41.121995], "pop": 23501, "state": "OH", "_id": "44313"} -{"city": "AKRON", "loc": [-81.559825, 41.040774], "pop": 21289, "state": "OH", "_id": "44314"} -{"city": "AKRON", "loc": [-81.53468, 40.97912], "pop": 21902, "state": "OH", "_id": "44319"} -{"city": "AKRON", "loc": [-81.56744, 41.083496], "pop": 25910, "state": "OH", "_id": "44320"} -{"city": "COPLEY", "loc": [-81.648045, 41.103139], "pop": 8756, "state": "OH", "_id": "44321"} -{"city": "FAIRLAWN", "loc": [-81.62385, 41.146734], "pop": 15383, "state": "OH", "_id": "44333"} -{"city": "BERLIN CENTER", "loc": [-80.934104, 41.024319], "pop": 2771, "state": "OH", "_id": "44401"} -{"city": "BRISTOLVILLE", "loc": [-80.856839, 41.379749], "pop": 3220, "state": "OH", "_id": "44402"} -{"city": "BROOKFIELD", "loc": [-80.578767, 41.247957], "pop": 5819, "state": "OH", "_id": "44403"} -{"city": "BURGHILL", "loc": [-80.54406, 41.334688], "pop": 802, "state": "OH", "_id": "44404"} -{"city": "CAMPBELL", "loc": [-80.589721, 41.077829], "pop": 10027, "state": "OH", "_id": "44405"} -{"city": "CANFIELD", "loc": [-80.756436, 41.029328], "pop": 16683, "state": "OH", "_id": "44406"} -{"city": "COLUMBIANA", "loc": [-80.697473, 40.885279], "pop": 9868, "state": "OH", "_id": "44408"} -{"city": "CORTLAND", "loc": [-80.732745, 41.325125], "pop": 15687, "state": "OH", "_id": "44410"} -{"city": "DEERFIELD", "loc": [-81.052827, 41.03586], "pop": 2323, "state": "OH", "_id": "44411"} -{"city": "DIAMOND", "loc": [-81.0425, 41.093473], "pop": 1854, "state": "OH", "_id": "44412"} -{"city": "EAST PALESTINE", "loc": [-80.546513, 40.840554], "pop": 7493, "state": "OH", "_id": "44413"} -{"city": "FARMDALE", "loc": [-80.662818, 41.392301], "pop": 1781, "state": "OH", "_id": "44417"} -{"city": "FOWLER", "loc": [-80.605894, 41.334851], "pop": 1692, "state": "OH", "_id": "44418"} -{"city": "GIRARD", "loc": [-80.693305, 41.161136], "pop": 16480, "state": "OH", "_id": "44420"} -{"city": "HANOVERTON", "loc": [-80.914445, 40.773116], "pop": 2461, "state": "OH", "_id": "44423"} -{"city": "HUBBARD", "loc": [-80.576192, 41.162401], "pop": 15616, "state": "OH", "_id": "44425"} -{"city": "KENSINGTON", "loc": [-80.938079, 40.71418], "pop": 1463, "state": "OH", "_id": "44427"} -{"city": "KINSMAN", "loc": [-80.576506, 41.435442], "pop": 3397, "state": "OH", "_id": "44428"} -{"city": "LAKE MILTON", "loc": [-80.97235, 41.101354], "pop": 3984, "state": "OH", "_id": "44429"} -{"city": "LEAVITTSBURG", "loc": [-80.886922, 41.244498], "pop": 4368, "state": "OH", "_id": "44430"} -{"city": "LEETONIA", "loc": [-80.758453, 40.863077], "pop": 4281, "state": "OH", "_id": "44431"} -{"city": "LISBON", "loc": [-80.758674, 40.759154], "pop": 11236, "state": "OH", "_id": "44432"} -{"city": "LOWELLVILLE", "loc": [-80.541551, 41.050256], "pop": 3882, "state": "OH", "_id": "44436"} -{"city": "MC DONALD", "loc": [-80.731169, 41.158026], "pop": 4868, "state": "OH", "_id": "44437"} -{"city": "MASURY", "loc": [-80.532565, 41.22552], "pop": 5864, "state": "OH", "_id": "44438"} -{"city": "MINERAL RIDGE", "loc": [-80.755293, 41.131843], "pop": 921, "state": "OH", "_id": "44440"} -{"city": "NEGLEY", "loc": [-80.564497, 40.774601], "pop": 2222, "state": "OH", "_id": "44441"} -{"city": "NEW MIDDLETOWN", "loc": [-80.553415, 40.964607], "pop": 3396, "state": "OH", "_id": "44442"} -{"city": "NEW SPRINGFIELD", "loc": [-80.585585, 40.926517], "pop": 2547, "state": "OH", "_id": "44443"} -{"city": "NEWTON FALLS", "loc": [-80.970135, 41.191047], "pop": 11623, "state": "OH", "_id": "44444"} -{"city": "NEW WATERFORD", "loc": [-80.620855, 40.848941], "pop": 3141, "state": "OH", "_id": "44445"} -{"city": "NILES", "loc": [-80.755775, 41.182414], "pop": 27450, "state": "OH", "_id": "44446"} -{"city": "NORTH BENTON", "loc": [-81.016168, 40.987579], "pop": 892, "state": "OH", "_id": "44449"} -{"city": "NORTH BLOOMFIELD", "loc": [-80.806835, 41.456887], "pop": 3393, "state": "OH", "_id": "44450"} -{"city": "NORTH JACKSON", "loc": [-80.86225, 41.088044], "pop": 2448, "state": "OH", "_id": "44451"} -{"city": "NORTH LIMA", "loc": [-80.654911, 40.964866], "pop": 2657, "state": "OH", "_id": "44452"} -{"city": "PETERSBURG", "loc": [-80.540031, 40.904861], "pop": 650, "state": "OH", "_id": "44454"} -{"city": "ROGERS", "loc": [-80.620237, 40.778943], "pop": 1418, "state": "OH", "_id": "44455"} -{"city": "SALEM", "loc": [-80.861883, 40.900024], "pop": 26756, "state": "OH", "_id": "44460"} -{"city": "SOUTHINGTON", "loc": [-80.948474, 41.298312], "pop": 3019, "state": "OH", "_id": "44470"} -{"city": "STRUTHERS", "loc": [-80.598487, 41.050847], "pop": 12318, "state": "OH", "_id": "44471"} -{"city": "VIENNA", "loc": [-80.654998, 41.217478], "pop": 5638, "state": "OH", "_id": "44473"} -{"city": "WARREN", "loc": [-80.871806, 41.172426], "pop": 5407, "state": "OH", "_id": "44481"} -{"city": "WARREN", "loc": [-80.816448, 41.263878], "pop": 30257, "state": "OH", "_id": "44483"} -{"city": "WARREN", "loc": [-80.764243, 41.231819], "pop": 25898, "state": "OH", "_id": "44484"} -{"city": "WARREN", "loc": [-80.844136, 41.240511], "pop": 24847, "state": "OH", "_id": "44485"} -{"city": "WASHINGTONVILLE", "loc": [-80.763137, 40.897331], "pop": 482, "state": "OH", "_id": "44490"} -{"city": "WEST FARMINGTON", "loc": [-80.967245, 41.350849], "pop": 768, "state": "OH", "_id": "44491"} -{"city": "YOUNGSTOWN", "loc": [-80.640905, 41.077366], "pop": 13671, "state": "OH", "_id": "44502"} -{"city": "YOUNGSTOWN", "loc": [-80.650007, 41.102016], "pop": 1315, "state": "OH", "_id": "44503"} -{"city": "YOUNGSTOWN", "loc": [-80.653887, 41.123686], "pop": 5845, "state": "OH", "_id": "44504"} -{"city": "YOUNGSTOWN", "loc": [-80.627748, 41.125748], "pop": 25106, "state": "OH", "_id": "44505"} -{"city": "YOUNGSTOWN", "loc": [-80.625916, 41.096045], "pop": 5619, "state": "OH", "_id": "44506"} -{"city": "YOUNGSTOWN", "loc": [-80.655336, 41.073236], "pop": 10501, "state": "OH", "_id": "44507"} -{"city": "YOUNGSTOWN", "loc": [-80.694463, 41.10498], "pop": 14399, "state": "OH", "_id": "44509"} -{"city": "YOUNGSTOWN", "loc": [-80.667204, 41.119714], "pop": 4513, "state": "OH", "_id": "44510"} -{"city": "YOUNGSTOWN", "loc": [-80.693098, 41.070402], "pop": 27278, "state": "OH", "_id": "44511"} -{"city": "BOARDMAN", "loc": [-80.666629, 41.031985], "pop": 35628, "state": "OH", "_id": "44512"} -{"city": "POLAND", "loc": [-80.610254, 41.023258], "pop": 21694, "state": "OH", "_id": "44514"} -{"city": "AUSTINTOWN", "loc": [-80.743966, 41.093903], "pop": 27130, "state": "OH", "_id": "44515"} -{"city": "ALLIANCE", "loc": [-81.118193, 40.915842], "pop": 35230, "state": "OH", "_id": "44601"} -{"city": "APPLE CREEK", "loc": [-81.809256, 40.755118], "pop": 7286, "state": "OH", "_id": "44606"} -{"city": "BEACH CITY", "loc": [-81.585129, 40.656199], "pop": 2321, "state": "OH", "_id": "44608"} -{"city": "BELOIT", "loc": [-80.989669, 40.895678], "pop": 3205, "state": "OH", "_id": "44609"} -{"city": "BIG PRAIRIE", "loc": [-82.072048, 40.618777], "pop": 1956, "state": "OH", "_id": "44611"} -{"city": "BOLIVAR", "loc": [-81.446356, 40.634692], "pop": 4225, "state": "OH", "_id": "44612"} -{"city": "BREWSTER", "loc": [-81.598752, 40.714387], "pop": 2129, "state": "OH", "_id": "44613"} -{"city": "CANAL FULTON", "loc": [-81.577269, 40.88871], "pop": 10310, "state": "OH", "_id": "44614"} -{"city": "CARROLLTON", "loc": [-81.081789, 40.578663], "pop": 10052, "state": "OH", "_id": "44615"} -{"city": "DALTON", "loc": [-81.70078, 40.779326], "pop": 6069, "state": "OH", "_id": "44618"} -{"city": "DELLROY", "loc": [-81.19856, 40.586143], "pop": 1844, "state": "OH", "_id": "44620"} -{"city": "DENNISON", "loc": [-81.320301, 40.408885], "pop": 4573, "state": "OH", "_id": "44621"} -{"city": "DOVER", "loc": [-81.476321, 40.534338], "pop": 17042, "state": "OH", "_id": "44622"} -{"city": "DUNDEE", "loc": [-81.676519, 40.639441], "pop": 3871, "state": "OH", "_id": "44624"} -{"city": "EAST ROCHESTER", "loc": [-81.01749, 40.756288], "pop": 1531, "state": "OH", "_id": "44625"} -{"city": "EAST SPARTA", "loc": [-81.368728, 40.697065], "pop": 2908, "state": "OH", "_id": "44626"} -{"city": "FREDERICKSBURG", "loc": [-81.851812, 40.685953], "pop": 3336, "state": "OH", "_id": "44627"} -{"city": "GLENMONT", "loc": [-82.150538, 40.521682], "pop": 1908, "state": "OH", "_id": "44628"} -{"city": "GNADENHUTTEN", "loc": [-81.405858, 40.372123], "pop": 5892, "state": "OH", "_id": "44629"} -{"city": "HARTVILLE", "loc": [-81.323873, 40.961798], "pop": 8318, "state": "OH", "_id": "44632"} -{"city": "HOLMESVILLE", "loc": [-81.927476, 40.633042], "pop": 2145, "state": "OH", "_id": "44633"} -{"city": "HOMEWORTH", "loc": [-81.065473, 40.859192], "pop": 3400, "state": "OH", "_id": "44634"} -{"city": "KILLBUCK", "loc": [-81.983721, 40.4933], "pop": 1868, "state": "OH", "_id": "44637"} -{"city": "LAKEVILLE", "loc": [-82.145477, 40.651965], "pop": 1516, "state": "OH", "_id": "44638"} -{"city": "LOUISVILLE", "loc": [-81.259464, 40.847729], "pop": 18994, "state": "OH", "_id": "44641"} -{"city": "MAGNOLIA", "loc": [-81.307607, 40.651414], "pop": 2166, "state": "OH", "_id": "44643"} -{"city": "MALVERN", "loc": [-81.18378, 40.684532], "pop": 4761, "state": "OH", "_id": "44644"} -{"city": "MARSHALLVILLE", "loc": [-81.722527, 40.906677], "pop": 1838, "state": "OH", "_id": "44645"} -{"city": "MASSILLON", "loc": [-81.497263, 40.811605], "pop": 45092, "state": "OH", "_id": "44646"} -{"city": "MASSILLON", "loc": [-81.553252, 40.795918], "pop": 17715, "state": "OH", "_id": "44647"} -{"city": "MECHANICSTOWN", "loc": [-80.956025, 40.626279], "pop": 709, "state": "OH", "_id": "44651"} -{"city": "MILLERSBURG", "loc": [-81.832383, 40.556683], "pop": 20350, "state": "OH", "_id": "44654"} -{"city": "ZOARVILLE", "loc": [-81.354561, 40.625686], "pop": 2969, "state": "OH", "_id": "44656"} -{"city": "MINERVA", "loc": [-81.103076, 40.742049], "pop": 10547, "state": "OH", "_id": "44657"} -{"city": "NAVARRE", "loc": [-81.533824, 40.720405], "pop": 10006, "state": "OH", "_id": "44662"} -{"city": "NEW PHILADELPHIA", "loc": [-81.435827, 40.484539], "pop": 24640, "state": "OH", "_id": "44663"} -{"city": "NORTH LAWRENCE", "loc": [-81.629946, 40.838652], "pop": 2528, "state": "OH", "_id": "44666"} -{"city": "ORRVILLE", "loc": [-81.774109, 40.845836], "pop": 11983, "state": "OH", "_id": "44667"} -{"city": "PARIS", "loc": [-81.15399, 40.801413], "pop": 1338, "state": "OH", "_id": "44669"} -{"city": "SEBRING", "loc": [-81.023191, 40.922694], "pop": 6276, "state": "OH", "_id": "44672"} -{"city": "SHERRODSVILLE", "loc": [-81.233945, 40.518418], "pop": 1330, "state": "OH", "_id": "44675"} -{"city": "SHREVE", "loc": [-82.032451, 40.692584], "pop": 4312, "state": "OH", "_id": "44676"} -{"city": "SMITHVILLE", "loc": [-81.863328, 40.859228], "pop": 2695, "state": "OH", "_id": "44677"} -{"city": "STRASBURG", "loc": [-81.536646, 40.60028], "pop": 3201, "state": "OH", "_id": "44680"} -{"city": "SUGARCREEK", "loc": [-81.660356, 40.514785], "pop": 4772, "state": "OH", "_id": "44681"} -{"city": "UHRICHSVILLE", "loc": [-81.337365, 40.390502], "pop": 6925, "state": "OH", "_id": "44683"} -{"city": "UNIONTOWN", "loc": [-81.421108, 40.963694], "pop": 21009, "state": "OH", "_id": "44685"} -{"city": "WAYNESBURG", "loc": [-81.265891, 40.682881], "pop": 3149, "state": "OH", "_id": "44688"} -{"city": "WILMOT", "loc": [-81.635247, 40.656723], "pop": 286, "state": "OH", "_id": "44689"} -{"city": "WOOSTER", "loc": [-81.948272, 40.809354], "pop": 35504, "state": "OH", "_id": "44691"} -{"city": "BOWERSTON", "loc": [-81.18624, 40.437056], "pop": 1484, "state": "OH", "_id": "44695"} -{"city": "TIPPECANOE", "loc": [-81.291937, 40.279748], "pop": 1184, "state": "OH", "_id": "44699"} -{"city": "CANTON", "loc": [-81.373946, 40.80267], "pop": 1208, "state": "OH", "_id": "44702"} -{"city": "CANTON", "loc": [-81.381439, 40.809791], "pop": 11520, "state": "OH", "_id": "44703"} -{"city": "CANTON", "loc": [-81.353701, 40.799076], "pop": 5408, "state": "OH", "_id": "44704"} -{"city": "CANTON", "loc": [-81.339903, 40.825866], "pop": 21271, "state": "OH", "_id": "44705"} -{"city": "CANTON", "loc": [-81.411903, 40.767959], "pop": 19443, "state": "OH", "_id": "44706"} -{"city": "NORTH INDUSTRY", "loc": [-81.360407, 40.776885], "pop": 11010, "state": "OH", "_id": "44707"} -{"city": "CANTON", "loc": [-81.424116, 40.81196], "pop": 25891, "state": "OH", "_id": "44708"} -{"city": "NORTH CANTON", "loc": [-81.385947, 40.837227], "pop": 19371, "state": "OH", "_id": "44709"} -{"city": "CANTON", "loc": [-81.416946, 40.791107], "pop": 9928, "state": "OH", "_id": "44710"} -{"city": "CANTON", "loc": [-81.360963, 40.827174], "pop": 8988, "state": "OH", "_id": "44714"} -{"city": "JACKSON BELDEN", "loc": [-81.448514, 40.85479], "pop": 10407, "state": "OH", "_id": "44718"} -{"city": "NORTH CANTON", "loc": [-81.413464, 40.889919], "pop": 33193, "state": "OH", "_id": "44720"} -{"city": "CANTON", "loc": [-81.33279, 40.883446], "pop": 10254, "state": "OH", "_id": "44721"} -{"city": "EAST CANTON", "loc": [-81.278295, 40.784983], "pop": 6154, "state": "OH", "_id": "44730"} -{"city": "ALVADA", "loc": [-83.376982, 41.046284], "pop": 1955, "state": "OH", "_id": "44802"} -{"city": "ARCADIA", "loc": [-83.50195, 41.111562], "pop": 1142, "state": "OH", "_id": "44804"} -{"city": "ASHLAND", "loc": [-82.318931, 40.855892], "pop": 27072, "state": "OH", "_id": "44805"} -{"city": "CARROTHERS", "loc": [-82.889721, 41.077755], "pop": 2792, "state": "OH", "_id": "44807"} -{"city": "BELLEVUE", "loc": [-82.85765, 41.268432], "pop": 14361, "state": "OH", "_id": "44811"} -{"city": "BELLVILLE", "loc": [-82.517516, 40.613604], "pop": 6432, "state": "OH", "_id": "44813"} -{"city": "BERLIN HEIGHTS", "loc": [-82.477713, 41.320519], "pop": 2485, "state": "OH", "_id": "44814"} -{"city": "BLOOMDALE", "loc": [-83.572356, 41.181489], "pop": 1331, "state": "OH", "_id": "44817"} -{"city": "BLOOMVILLE", "loc": [-82.989874, 41.018206], "pop": 2572, "state": "OH", "_id": "44818"} -{"city": "BUCYRUS", "loc": [-82.969829, 40.810306], "pop": 19018, "state": "OH", "_id": "44820"} -{"city": "BUTLER", "loc": [-82.398984, 40.543754], "pop": 3870, "state": "OH", "_id": "44822"} -{"city": "CASTALIA", "loc": [-82.799426, 41.387229], "pop": 4027, "state": "OH", "_id": "44824"} -{"city": "CHATFIELD", "loc": [-83.021432, 40.956637], "pop": 514, "state": "OH", "_id": "44825"} -{"city": "COLLINS", "loc": [-82.490402, 41.245023], "pop": 1934, "state": "OH", "_id": "44826"} -{"city": "CRESTLINE", "loc": [-82.736723, 40.792714], "pop": 6723, "state": "OH", "_id": "44827"} -{"city": "FOSTORIA", "loc": [-83.413938, 41.162346], "pop": 20686, "state": "OH", "_id": "44830"} -{"city": "GALION", "loc": [-82.793943, 40.730316], "pop": 19069, "state": "OH", "_id": "44833"} -{"city": "GREEN SPRINGS", "loc": [-83.088549, 41.228111], "pop": 3752, "state": "OH", "_id": "44836"} -{"city": "GREENWICH", "loc": [-82.51334, 41.04068], "pop": 3054, "state": "OH", "_id": "44837"} -{"city": "SHINROCK", "loc": [-82.555491, 41.390662], "pop": 10386, "state": "OH", "_id": "44839"} -{"city": "JEROMESVILLE", "loc": [-82.186134, 40.813366], "pop": 4052, "state": "OH", "_id": "44840"} -{"city": "KANSAS", "loc": [-83.329829, 41.212543], "pop": 893, "state": "OH", "_id": "44841"} -{"city": "LOUDONVILLE", "loc": [-82.23559, 40.636059], "pop": 4767, "state": "OH", "_id": "44842"} -{"city": "LUCAS", "loc": [-82.408671, 40.70389], "pop": 2127, "state": "OH", "_id": "44843"} -{"city": "MC CUTCHENVILLE", "loc": [-83.263669, 40.975225], "pop": 515, "state": "OH", "_id": "44844"} -{"city": "MELMORE", "loc": [-83.143238, 41.038954], "pop": 1611, "state": "OH", "_id": "44845"} -{"city": "MILAN", "loc": [-82.612565, 41.311065], "pop": 3568, "state": "OH", "_id": "44846"} -{"city": "MONROEVILLE", "loc": [-82.70233, 41.218074], "pop": 2721, "state": "OH", "_id": "44847"} -{"city": "NEVADA", "loc": [-83.126567, 40.825946], "pop": 2464, "state": "OH", "_id": "44849"} -{"city": "NEW LONDON", "loc": [-82.396588, 41.090575], "pop": 4243, "state": "OH", "_id": "44851"} -{"city": "NEW RIEGEL", "loc": [-83.241807, 41.036058], "pop": 1483, "state": "OH", "_id": "44853"} -{"city": "NEW WASHINGTON", "loc": [-82.850359, 40.957076], "pop": 1679, "state": "OH", "_id": "44854"} -{"city": "NORTH FAIRFIELD", "loc": [-82.599801, 41.102987], "pop": 1229, "state": "OH", "_id": "44855"} -{"city": "NORWALK", "loc": [-82.60785, 41.240314], "pop": 21687, "state": "OH", "_id": "44857"} -{"city": "NOVA", "loc": [-82.338439, 41.028215], "pop": 1565, "state": "OH", "_id": "44859"} -{"city": "PERRYSVILLE", "loc": [-82.321307, 40.660626], "pop": 2818, "state": "OH", "_id": "44864"} -{"city": "PLYMOUTH", "loc": [-82.663492, 41.00031], "pop": 4122, "state": "OH", "_id": "44865"} -{"city": "POLK", "loc": [-82.212587, 40.934293], "pop": 2293, "state": "OH", "_id": "44866"} -{"city": "REPUBLIC", "loc": [-83.019407, 41.125876], "pop": 1735, "state": "OH", "_id": "44867"} -{"city": "SANDUSKY", "loc": [-82.70633, 41.434878], "pop": 44129, "state": "OH", "_id": "44870"} -{"city": "SAVANNAH", "loc": [-82.344272, 40.945382], "pop": 2983, "state": "OH", "_id": "44874"} -{"city": "SHELBY", "loc": [-82.654949, 40.878432], "pop": 14736, "state": "OH", "_id": "44875"} -{"city": "SHILOH", "loc": [-82.522155, 40.934015], "pop": 4297, "state": "OH", "_id": "44878"} -{"city": "SULLIVAN", "loc": [-82.217217, 41.036818], "pop": 1772, "state": "OH", "_id": "44880"} -{"city": "SYCAMORE", "loc": [-83.149176, 40.941299], "pop": 2610, "state": "OH", "_id": "44882"} -{"city": "TIFFIN", "loc": [-83.184356, 41.123822], "pop": 28310, "state": "OH", "_id": "44883"} -{"city": "TIRO", "loc": [-82.797032, 40.880992], "pop": 1471, "state": "OH", "_id": "44887"} -{"city": "WAKEMAN", "loc": [-82.378168, 41.263748], "pop": 6464, "state": "OH", "_id": "44889"} -{"city": "WILLARD", "loc": [-82.728805, 41.062787], "pop": 11156, "state": "OH", "_id": "44890"} -{"city": "MANSFIELD", "loc": [-82.512269, 40.755937], "pop": 8504, "state": "OH", "_id": "44902"} -{"city": "MANSFIELD", "loc": [-82.52538, 40.762258], "pop": 26575, "state": "OH", "_id": "44903"} -{"city": "LEXINGTON", "loc": [-82.590605, 40.682568], "pop": 12466, "state": "OH", "_id": "44904"} -{"city": "LINCOLN", "loc": [-82.474609, 40.777173], "pop": 16221, "state": "OH", "_id": "44905"} -{"city": "MANSFIELD", "loc": [-82.559295, 40.762679], "pop": 16809, "state": "OH", "_id": "44906"} -{"city": "MANSFIELD", "loc": [-82.519833, 40.734483], "pop": 14891, "state": "OH", "_id": "44907"} -{"city": "ADDYSTON", "loc": [-84.709602, 39.137364], "pop": 1198, "state": "OH", "_id": "45001"} -{"city": "CLEVES", "loc": [-84.733969, 39.193707], "pop": 10249, "state": "OH", "_id": "45002"} -{"city": "COLLEGE CORNER", "loc": [-84.804951, 39.575453], "pop": 708, "state": "OH", "_id": "45003"} -{"city": "CARLISLE", "loc": [-84.305881, 39.547839], "pop": 29720, "state": "OH", "_id": "45005"} -{"city": "HAMILTON", "loc": [-84.522117, 39.405906], "pop": 41229, "state": "OH", "_id": "45011"} -{"city": "ROSSVILLE", "loc": [-84.606655, 39.40619], "pop": 48553, "state": "OH", "_id": "45013"} -{"city": "FAIRFIELD", "loc": [-84.547881, 39.326602], "pop": 41880, "state": "OH", "_id": "45014"} -{"city": "LINDENWALD", "loc": [-84.551187, 39.367152], "pop": 13310, "state": "OH", "_id": "45015"} -{"city": "HARRISON", "loc": [-84.78368, 39.259208], "pop": 15218, "state": "OH", "_id": "45030"} -{"city": "OTTERBIEN HOME", "loc": [-84.218754, 39.442047], "pop": 24354, "state": "OH", "_id": "45036"} -{"city": "MAINEVILLE", "loc": [-84.252567, 39.313878], "pop": 12215, "state": "OH", "_id": "45039"} -{"city": "MASON", "loc": [-84.314935, 39.335741], "pop": 18803, "state": "OH", "_id": "45040"} -{"city": "MIDDLETOWN", "loc": [-84.389601, 39.532121], "pop": 27251, "state": "OH", "_id": "45042"} -{"city": "EXCELLO", "loc": [-84.383461, 39.485259], "pop": 38868, "state": "OH", "_id": "45044"} -{"city": "MONROE", "loc": [-84.365196, 39.441285], "pop": 4352, "state": "OH", "_id": "45050"} -{"city": "NORTH BEND", "loc": [-84.727261, 39.153605], "pop": 4013, "state": "OH", "_id": "45052"} -{"city": "OKEANA", "loc": [-84.776149, 39.353732], "pop": 2894, "state": "OH", "_id": "45053"} -{"city": "OREGONIA", "loc": [-84.051136, 39.414479], "pop": 1405, "state": "OH", "_id": "45054"} -{"city": "MIAMI UNIVERSITY", "loc": [-84.738518, 39.503838], "pop": 26075, "state": "OH", "_id": "45056"} -{"city": "SOMERVILLE", "loc": [-84.621911, 39.555366], "pop": 902, "state": "OH", "_id": "45064"} -{"city": "SOUTH LEBANON", "loc": [-84.210783, 39.371451], "pop": 2774, "state": "OH", "_id": "45065"} -{"city": "SPRINGBORO", "loc": [-84.228774, 39.562975], "pop": 9106, "state": "OH", "_id": "45066"} -{"city": "TRENTON", "loc": [-84.459769, 39.479937], "pop": 7072, "state": "OH", "_id": "45067"} -{"city": "WAYNESVILLE", "loc": [-84.081518, 39.528489], "pop": 6880, "state": "OH", "_id": "45068"} -{"city": "WEST CHESTER", "loc": [-84.399786, 39.340243], "pop": 32613, "state": "OH", "_id": "45069"} -{"city": "ABERDEEN", "loc": [-83.763723, 38.670864], "pop": 2176, "state": "OH", "_id": "45101"} -{"city": "AMELIA", "loc": [-84.211174, 39.021138], "pop": 15931, "state": "OH", "_id": "45102"} -{"city": "BATAVIA", "loc": [-84.145125, 39.095661], "pop": 14416, "state": "OH", "_id": "45103"} -{"city": "BETHEL", "loc": [-84.09195, 38.94236], "pop": 10408, "state": "OH", "_id": "45106"} -{"city": "BLANCHESTER", "loc": [-83.973977, 39.303442], "pop": 6158, "state": "OH", "_id": "45107"} -{"city": "CAMP DENNISON", "loc": [-84.289659, 39.196212], "pop": 482, "state": "OH", "_id": "45111"} -{"city": "CLARKSVILLE", "loc": [-83.959407, 39.404233], "pop": 1434, "state": "OH", "_id": "45113"} -{"city": "FAYETTEVILLE", "loc": [-83.950087, 39.186214], "pop": 3177, "state": "OH", "_id": "45118"} -{"city": "FELICITY", "loc": [-84.098581, 38.826248], "pop": 2662, "state": "OH", "_id": "45120"} -{"city": "GEORGETOWN", "loc": [-83.909153, 38.871708], "pop": 7474, "state": "OH", "_id": "45121"} -{"city": "GOSHEN", "loc": [-84.118764, 39.220931], "pop": 11742, "state": "OH", "_id": "45122"} -{"city": "GREENFIELD", "loc": [-83.389805, 39.347763], "pop": 8106, "state": "OH", "_id": "45123"} -{"city": "HAMERSVILLE", "loc": [-83.993062, 38.919962], "pop": 3415, "state": "OH", "_id": "45130"} -{"city": "HILLSBORO", "loc": [-83.606406, 39.167877], "pop": 19825, "state": "OH", "_id": "45133"} -{"city": "LEESBURG", "loc": [-83.548146, 39.345767], "pop": 3684, "state": "OH", "_id": "45135"} -{"city": "LOVELAND", "loc": [-84.258802, 39.244484], "pop": 29488, "state": "OH", "_id": "45140"} -{"city": "LYNCHBURG", "loc": [-83.802124, 39.211931], "pop": 4539, "state": "OH", "_id": "45142"} -{"city": "MANCHESTER", "loc": [-83.618064, 38.698167], "pop": 4303, "state": "OH", "_id": "45144"} -{"city": "MARTINSVILLE", "loc": [-83.800545, 39.312705], "pop": 1413, "state": "OH", "_id": "45146"} -{"city": "MIDLAND", "loc": [-83.893131, 39.29169], "pop": 1488, "state": "OH", "_id": "45148"} -{"city": "DAY HEIGHTS", "loc": [-84.243814, 39.179987], "pop": 24709, "state": "OH", "_id": "45150"} -{"city": "MORROW", "loc": [-84.118085, 39.347619], "pop": 5498, "state": "OH", "_id": "45152"} -{"city": "MOSCOW", "loc": [-84.195824, 38.858255], "pop": 1957, "state": "OH", "_id": "45153"} -{"city": "MOUNT ORAB", "loc": [-83.948027, 39.045368], "pop": 8247, "state": "OH", "_id": "45154"} -{"city": "NEW RICHMOND", "loc": [-84.237903, 38.953663], "pop": 8660, "state": "OH", "_id": "45157"} -{"city": "NEW VIENNA", "loc": [-83.688171, 39.332058], "pop": 2213, "state": "OH", "_id": "45159"} -{"city": "PLEASANT PLAIN", "loc": [-84.096736, 39.288382], "pop": 2911, "state": "OH", "_id": "45162"} -{"city": "RIPLEY", "loc": [-83.822677, 38.755095], "pop": 3742, "state": "OH", "_id": "45167"} -{"city": "RUSSELLVILLE", "loc": [-83.762459, 38.851128], "pop": 1600, "state": "OH", "_id": "45168"} -{"city": "SABINA", "loc": [-83.650252, 39.490022], "pop": 4866, "state": "OH", "_id": "45169"} -{"city": "SARDINIA", "loc": [-83.796649, 38.983232], "pop": 3377, "state": "OH", "_id": "45171"} -{"city": "TERRACE PARK", "loc": [-84.309762, 39.160155], "pop": 2133, "state": "OH", "_id": "45174"} -{"city": "WILLIAMSBURG", "loc": [-84.043167, 39.075345], "pop": 5038, "state": "OH", "_id": "45176"} -{"city": "WILMINGTON", "loc": [-83.841653, 39.448788], "pop": 18361, "state": "OH", "_id": "45177"} -{"city": "CINCINNATI", "loc": [-84.501956, 39.107225], "pop": 6428, "state": "OH", "_id": "45202"} -{"city": "CINCINNATI", "loc": [-84.525684, 39.10754], "pop": 5327, "state": "OH", "_id": "45203"} -{"city": "CINCINNATI", "loc": [-84.566794, 39.102498], "pop": 11444, "state": "OH", "_id": "45204"} -{"city": "CINCINNATI", "loc": [-84.575672, 39.110439], "pop": 23052, "state": "OH", "_id": "45205"} -{"city": "CINCINNATI", "loc": [-84.485258, 39.126916], "pop": 14017, "state": "OH", "_id": "45206"} -{"city": "CINCINNATI", "loc": [-84.470621, 39.139747], "pop": 8156, "state": "OH", "_id": "45207"} -{"city": "CINCINNATI", "loc": [-84.435474, 39.136082], "pop": 19557, "state": "OH", "_id": "45208"} -{"city": "CINCINNATI", "loc": [-84.427833, 39.151578], "pop": 12082, "state": "OH", "_id": "45209"} -{"city": "CINCINNATI", "loc": [-84.513535, 39.112579], "pop": 11349, "state": "OH", "_id": "45210"} -{"city": "CINCINNATI", "loc": [-84.596714, 39.152401], "pop": 44072, "state": "OH", "_id": "45211"} -{"city": "NORWOOD", "loc": [-84.452765, 39.162505], "pop": 26737, "state": "OH", "_id": "45212"} -{"city": "TAFT", "loc": [-84.418701, 39.182905], "pop": 14333, "state": "OH", "_id": "45213"} -{"city": "CINCINNATI", "loc": [-84.541442, 39.120642], "pop": 12013, "state": "OH", "_id": "45214"} -{"city": "LOCKLAND", "loc": [-84.457168, 39.230063], "pop": 34166, "state": "OH", "_id": "45215"} -{"city": "ELMWOOD PLACE", "loc": [-84.479232, 39.199183], "pop": 10261, "state": "OH", "_id": "45216"} -{"city": "SAINT BERNARD", "loc": [-84.497424, 39.161715], "pop": 8838, "state": "OH", "_id": "45217"} -{"city": "GREENHILLS", "loc": [-84.519608, 39.266573], "pop": 4680, "state": "OH", "_id": "45218"} -{"city": "CINCINNATI", "loc": [-84.513127, 39.127027], "pop": 21619, "state": "OH", "_id": "45219"} -{"city": "CINCINNATI", "loc": [-84.521738, 39.143183], "pop": 15449, "state": "OH", "_id": "45220"} -{"city": "CINCINNATI", "loc": [-84.547807, 39.169619], "pop": 15639, "state": "OH", "_id": "45223"} -{"city": "COLLEGE HILL", "loc": [-84.53883, 39.203079], "pop": 23394, "state": "OH", "_id": "45224"} -{"city": "CINCINNATI", "loc": [-84.553267, 39.144654], "pop": 13324, "state": "OH", "_id": "45225"} -{"city": "CINCINNATI", "loc": [-84.431194, 39.117356], "pop": 5711, "state": "OH", "_id": "45226"} -{"city": "MADISONVILLE", "loc": [-84.387211, 39.15431], "pop": 21393, "state": "OH", "_id": "45227"} -{"city": "CINCINNATI", "loc": [-84.423539, 39.066448], "pop": 538, "state": "OH", "_id": "45228"} -{"city": "CINCINNATI", "loc": [-84.489184, 39.149016], "pop": 19811, "state": "OH", "_id": "45229"} -{"city": "ANDERSON", "loc": [-84.378727, 39.080861], "pop": 26856, "state": "OH", "_id": "45230"} -{"city": "CINCINNATI", "loc": [-84.543702, 39.241827], "pop": 44838, "state": "OH", "_id": "45231"} -{"city": "CINCINNATI", "loc": [-84.514101, 39.185926], "pop": 9364, "state": "OH", "_id": "45232"} -{"city": "SAYLOR PARK", "loc": [-84.669411, 39.11928], "pop": 15452, "state": "OH", "_id": "45233"} -{"city": "TAFT", "loc": [-84.394746, 39.207302], "pop": 26113, "state": "OH", "_id": "45236"} -{"city": "CINCINNATI", "loc": [-84.457997, 39.18797], "pop": 25445, "state": "OH", "_id": "45237"} -{"city": "WESTERN HILLS", "loc": [-84.608805, 39.111667], "pop": 48302, "state": "OH", "_id": "45238"} -{"city": "GROESBECK", "loc": [-84.579225, 39.207995], "pop": 27024, "state": "OH", "_id": "45239"} -{"city": "PARKDALE", "loc": [-84.526299, 39.286424], "pop": 27517, "state": "OH", "_id": "45240"} -{"city": "SHARONVILLE", "loc": [-84.391161, 39.276745], "pop": 21992, "state": "OH", "_id": "45241"} -{"city": "SYCAMORE", "loc": [-84.359919, 39.239881], "pop": 21183, "state": "OH", "_id": "45242"} -{"city": "MADEIRA", "loc": [-84.359349, 39.187847], "pop": 14999, "state": "OH", "_id": "45243"} -{"city": "NEWTOWN", "loc": [-84.347765, 39.107091], "pop": 12310, "state": "OH", "_id": "45244"} -{"city": "NEWTOWN", "loc": [-84.277383, 39.091293], "pop": 36134, "state": "OH", "_id": "45245"} -{"city": "GLENDALE", "loc": [-84.472353, 39.28751], "pop": 13861, "state": "OH", "_id": "45246"} -{"city": "GROESBECK", "loc": [-84.631608, 39.207604], "pop": 16834, "state": "OH", "_id": "45247"} -{"city": "WESTWOOD", "loc": [-84.651535, 39.159056], "pop": 20735, "state": "OH", "_id": "45248"} -{"city": "SYCAMORE", "loc": [-84.326673, 39.275946], "pop": 10312, "state": "OH", "_id": "45249"} -{"city": "GROESBECK", "loc": [-84.587987, 39.253005], "pop": 22487, "state": "OH", "_id": "45251"} -{"city": "CINCINNATI", "loc": [-84.62832, 39.266803], "pop": 3302, "state": "OH", "_id": "45252"} -{"city": "ANDERSON", "loc": [-84.330774, 39.070642], "pop": 15600, "state": "OH", "_id": "45255"} -{"city": "ANNA", "loc": [-84.210344, 40.405105], "pop": 3483, "state": "OH", "_id": "45302"} -{"city": "ANSONIA", "loc": [-84.640642, 40.215064], "pop": 2230, "state": "OH", "_id": "45303"} -{"city": "CASTINE", "loc": [-84.53702, 39.988367], "pop": 8201, "state": "OH", "_id": "45304"} -{"city": "BELLBROOK", "loc": [-84.08245, 39.640187], "pop": 8162, "state": "OH", "_id": "45305"} -{"city": "BOTKINS", "loc": [-84.177994, 40.465897], "pop": 2168, "state": "OH", "_id": "45306"} -{"city": "BRADFORD", "loc": [-84.429288, 40.128558], "pop": 6966, "state": "OH", "_id": "45308"} -{"city": "BROOKVILLE", "loc": [-84.416464, 39.841393], "pop": 11613, "state": "OH", "_id": "45309"} -{"city": "CAMDEN", "loc": [-84.610008, 39.613391], "pop": 8394, "state": "OH", "_id": "45311"} -{"city": "CASSTOWN", "loc": [-84.108799, 40.071578], "pop": 1283, "state": "OH", "_id": "45312"} -{"city": "CEDARVILLE", "loc": [-83.801253, 39.748424], "pop": 4685, "state": "OH", "_id": "45314"} -{"city": "CLAYTON", "loc": [-84.33989, 39.855124], "pop": 2269, "state": "OH", "_id": "45315"} -{"city": "CONOVER", "loc": [-84.028297, 40.145694], "pop": 1053, "state": "OH", "_id": "45317"} -{"city": "COVINGTON", "loc": [-84.349646, 40.11756], "pop": 3154, "state": "OH", "_id": "45318"} -{"city": "EATON", "loc": [-84.650849, 39.742572], "pop": 13779, "state": "OH", "_id": "45320"} -{"city": "ELDORADO", "loc": [-84.678592, 39.888209], "pop": 1168, "state": "OH", "_id": "45321"} -{"city": "UNION", "loc": [-84.309515, 39.873976], "pop": 20297, "state": "OH", "_id": "45322"} -{"city": "ENON", "loc": [-83.938464, 39.866282], "pop": 5820, "state": "OH", "_id": "45323"} -{"city": "FAIRBORN", "loc": [-84.019789, 39.805311], "pop": 35733, "state": "OH", "_id": "45324"} -{"city": "FARMERSVILLE", "loc": [-84.420507, 39.686684], "pop": 2292, "state": "OH", "_id": "45325"} -{"city": "FLETCHER", "loc": [-84.13304, 40.152622], "pop": 1752, "state": "OH", "_id": "45326"} -{"city": "GERMANTOWN", "loc": [-84.376384, 39.6244], "pop": 8187, "state": "OH", "_id": "45327"} -{"city": "GREENVILLE", "loc": [-84.634189, 40.098726], "pop": 23035, "state": "OH", "_id": "45331"} -{"city": "HOLLANSBURG", "loc": [-84.79008, 39.989862], "pop": 789, "state": "OH", "_id": "45332"} -{"city": "HOUSTON", "loc": [-84.352109, 40.253478], "pop": 1613, "state": "OH", "_id": "45333"} -{"city": "JACKSON CENTER", "loc": [-84.045677, 40.435802], "pop": 2318, "state": "OH", "_id": "45334"} -{"city": "JAMESTOWN", "loc": [-83.750417, 39.642848], "pop": 6956, "state": "OH", "_id": "45335"} -{"city": "LAURA", "loc": [-84.3994, 39.978492], "pop": 2602, "state": "OH", "_id": "45337"} -{"city": "LEWISBURG", "loc": [-84.53685, 39.853404], "pop": 5582, "state": "OH", "_id": "45338"} -{"city": "LUDLOW FALLS", "loc": [-84.333391, 39.987091], "pop": 1888, "state": "OH", "_id": "45339"} -{"city": "MAPLEWOOD", "loc": [-84.076657, 40.343743], "pop": 2071, "state": "OH", "_id": "45340"} -{"city": "MEDWAY", "loc": [-84.026808, 39.879774], "pop": 4385, "state": "OH", "_id": "45341"} -{"city": "MIAMISBURG", "loc": [-84.267477, 39.632095], "pop": 27356, "state": "OH", "_id": "45342"} -{"city": "NEW CARLISLE", "loc": [-84.021704, 39.930025], "pop": 17912, "state": "OH", "_id": "45344"} -{"city": "NEW LEBANON", "loc": [-84.395591, 39.739798], "pop": 7359, "state": "OH", "_id": "45345"} -{"city": "NEW MADISON", "loc": [-84.722211, 39.968711], "pop": 2519, "state": "OH", "_id": "45346"} -{"city": "NEW PARIS", "loc": [-84.779337, 39.861718], "pop": 3783, "state": "OH", "_id": "45347"} -{"city": "NEW WESTON", "loc": [-84.630792, 40.334923], "pop": 1266, "state": "OH", "_id": "45348"} -{"city": "PIQUA", "loc": [-84.253053, 40.148621], "pop": 24508, "state": "OH", "_id": "45356"} -{"city": "PLEASANT HILL", "loc": [-84.343634, 40.053136], "pop": 2553, "state": "OH", "_id": "45359"} -{"city": "ROSSBURG", "loc": [-84.626419, 40.29465], "pop": 1450, "state": "OH", "_id": "45362"} -{"city": "RUSSIA", "loc": [-84.412255, 40.234065], "pop": 911, "state": "OH", "_id": "45363"} -{"city": "SIDNEY", "loc": [-84.162223, 40.287357], "pop": 27517, "state": "OH", "_id": "45365"} -{"city": "SELMA", "loc": [-83.660787, 39.846939], "pop": 4143, "state": "OH", "_id": "45368"} -{"city": "SOUTH VIENNA", "loc": [-83.615704, 39.94732], "pop": 3814, "state": "OH", "_id": "45369"} -{"city": "SPRING VALLEY", "loc": [-84.101583, 39.602761], "pop": 5950, "state": "OH", "_id": "45370"} -{"city": "PHONETON", "loc": [-84.171501, 39.943577], "pop": 14967, "state": "OH", "_id": "45371"} -{"city": "TROY", "loc": [-84.203151, 40.037394], "pop": 29422, "state": "OH", "_id": "45373"} -{"city": "VANDALIA", "loc": [-84.202266, 39.888273], "pop": 14355, "state": "OH", "_id": "45377"} -{"city": "VERSAILLES", "loc": [-84.495697, 40.227284], "pop": 4921, "state": "OH", "_id": "45380"} -{"city": "WEST ALEXANDRIA", "loc": [-84.535214, 39.725898], "pop": 5707, "state": "OH", "_id": "45381"} -{"city": "WEST MANCHESTER", "loc": [-84.619383, 39.902564], "pop": 1599, "state": "OH", "_id": "45382"} -{"city": "WEST MILTON", "loc": [-84.324237, 39.953077], "pop": 6095, "state": "OH", "_id": "45383"} -{"city": "XENIA", "loc": [-83.936878, 39.684204], "pop": 36683, "state": "OH", "_id": "45385"} -{"city": "YELLOW SPRINGS", "loc": [-83.889066, 39.799569], "pop": 5743, "state": "OH", "_id": "45387"} -{"city": "YORKSHIRE", "loc": [-84.483587, 40.328328], "pop": 1054, "state": "OH", "_id": "45388"} -{"city": "UNION CITY", "loc": [-84.783209, 40.201773], "pop": 4123, "state": "OH", "_id": "45390"} -{"city": "DAYTON", "loc": [-84.189508, 39.756305], "pop": 2811, "state": "OH", "_id": "45402"} -{"city": "DAYTON", "loc": [-84.149802, 39.761728], "pop": 19567, "state": "OH", "_id": "45403"} -{"city": "DAYTON", "loc": [-84.162157, 39.78619], "pop": 14396, "state": "OH", "_id": "45404"} -{"city": "DAYTON", "loc": [-84.213546, 39.78993], "pop": 25151, "state": "OH", "_id": "45405"} -{"city": "DAYTON", "loc": [-84.237297, 39.782148], "pop": 31024, "state": "OH", "_id": "45406"} -{"city": "DAYTON", "loc": [-84.224232, 39.762699], "pop": 13708, "state": "OH", "_id": "45407"} -{"city": "DAYTON", "loc": [-84.228963, 39.739526], "pop": 13177, "state": "OH", "_id": "45408"} -{"city": "DAYTON", "loc": [-84.182495, 39.728496], "pop": 12633, "state": "OH", "_id": "45409"} -{"city": "DAYTON", "loc": [-84.16001, 39.74743], "pop": 19743, "state": "OH", "_id": "45410"} -{"city": "DAYTON", "loc": [-84.202444, 39.828528], "pop": 23033, "state": "OH", "_id": "45414"} -{"city": "DAYTON", "loc": [-84.261328, 39.835488], "pop": 12885, "state": "OH", "_id": "45415"} -{"city": "TROTWOOD", "loc": [-84.259824, 39.805541], "pop": 6905, "state": "OH", "_id": "45416"} -{"city": "DAYTON", "loc": [-84.246961, 39.752812], "pop": 14617, "state": "OH", "_id": "45417"} -{"city": "DAYTON", "loc": [-84.267696, 39.716251], "pop": 6913, "state": "OH", "_id": "45418"} -{"city": "DAYTON", "loc": [-84.163656, 39.715486], "pop": 17079, "state": "OH", "_id": "45419"} -{"city": "KETTERING", "loc": [-84.133892, 39.721286], "pop": 26955, "state": "OH", "_id": "45420"} -{"city": "HUBER HEIGHTS", "loc": [-84.123287, 39.845339], "pop": 48120, "state": "OH", "_id": "45424"} -{"city": "TROTWOOD", "loc": [-84.298283, 39.810548], "pop": 17853, "state": "OH", "_id": "45426"} -{"city": "DAYTON", "loc": [-84.281884, 39.754527], "pop": 13469, "state": "OH", "_id": "45427"} -{"city": "KETTERING", "loc": [-84.156077, 39.686392], "pop": 27574, "state": "OH", "_id": "45429"} -{"city": "BEAVERCREEK", "loc": [-84.083596, 39.709381], "pop": 6511, "state": "OH", "_id": "45430"} -{"city": "BEAVERCREEK", "loc": [-84.099802, 39.765396], "pop": 25772, "state": "OH", "_id": "45431"} -{"city": "BEAVERCREEK", "loc": [-84.094157, 39.740774], "pop": 14917, "state": "OH", "_id": "45432"} -{"city": "DAYTON", "loc": [-84.059048, 39.813758], "pop": 2298, "state": "OH", "_id": "45433"} -{"city": "BEAVERCREEK", "loc": [-84.040385, 39.716552], "pop": 8855, "state": "OH", "_id": "45434"} -{"city": "WEST CARROLLTON", "loc": [-84.21626, 39.689617], "pop": 9866, "state": "OH", "_id": "45439"} -{"city": "DAYTON", "loc": [-84.113573, 39.674854], "pop": 17129, "state": "OH", "_id": "45440"} -{"city": "WEST CARROLLTON", "loc": [-84.237887, 39.662098], "pop": 20391, "state": "OH", "_id": "45449"} -{"city": "CENTERVILLE", "loc": [-84.162697, 39.615755], "pop": 16364, "state": "OH", "_id": "45458"} -{"city": "CENTERVILLE", "loc": [-84.166422, 39.645957], "pop": 25767, "state": "OH", "_id": "45459"} -{"city": "SPRINGFIELD", "loc": [-83.841338, 39.930486], "pop": 16510, "state": "OH", "_id": "45502"} -{"city": "SPRINGFIELD", "loc": [-83.78043, 39.9528], "pop": 30547, "state": "OH", "_id": "45503"} -{"city": "SPRINGFIELD", "loc": [-83.834302, 39.940793], "pop": 19854, "state": "OH", "_id": "45504"} -{"city": "SPRINGFIELD", "loc": [-83.785593, 39.910588], "pop": 23956, "state": "OH", "_id": "45505"} -{"city": "SPRINGFIELD", "loc": [-83.827512, 39.910418], "pop": 19027, "state": "OH", "_id": "45506"} -{"city": "CHILLICOTHE", "loc": [-82.98949, 39.337997], "pop": 54615, "state": "OH", "_id": "45601"} -{"city": "BAINBRIDGE", "loc": [-83.276268, 39.213116], "pop": 4356, "state": "OH", "_id": "45612"} -{"city": "BEAVER", "loc": [-82.847469, 39.023847], "pop": 2953, "state": "OH", "_id": "45613"} -{"city": "BIDWELL", "loc": [-82.270092, 38.927647], "pop": 3572, "state": "OH", "_id": "45614"} -{"city": "BLUE CREEK", "loc": [-83.35268, 38.756451], "pop": 1436, "state": "OH", "_id": "45616"} -{"city": "CHESAPEAKE", "loc": [-82.450448, 38.455089], "pop": 8076, "state": "OH", "_id": "45619"} -{"city": "CHESHIRE", "loc": [-82.123537, 38.958678], "pop": 1007, "state": "OH", "_id": "45620"} -{"city": "CREOLA", "loc": [-82.493835, 39.3497], "pop": 535, "state": "OH", "_id": "45622"} -{"city": "CROWN CITY", "loc": [-82.265717, 38.613548], "pop": 2677, "state": "OH", "_id": "45623"} -{"city": "FRANKFORT", "loc": [-83.203356, 39.391015], "pop": 3985, "state": "OH", "_id": "45628"} -{"city": "FRANKLIN FURNACE", "loc": [-82.814477, 38.628216], "pop": 3956, "state": "OH", "_id": "45629"} -{"city": "GALLIPOLIS", "loc": [-82.22902, 38.814781], "pop": 18163, "state": "OH", "_id": "45631"} -{"city": "HAMDEN", "loc": [-82.509976, 39.168497], "pop": 1554, "state": "OH", "_id": "45634"} -{"city": "IRONTON", "loc": [-82.665351, 38.529429], "pop": 21093, "state": "OH", "_id": "45638"} -{"city": "JACKSON", "loc": [-82.647209, 39.042821], "pop": 13373, "state": "OH", "_id": "45640"} -{"city": "KINGSTON", "loc": [-82.848808, 39.441432], "pop": 1347, "state": "OH", "_id": "45644"} -{"city": "KITTS HILL", "loc": [-82.548897, 38.564945], "pop": 2837, "state": "OH", "_id": "45645"} -{"city": "LATHAM", "loc": [-83.328294, 39.080436], "pop": 351, "state": "OH", "_id": "45646"} -{"city": "LONDONDERRY", "loc": [-82.783288, 39.272283], "pop": 1481, "state": "OH", "_id": "45647"} -{"city": "LUCASVILLE", "loc": [-82.994009, 38.893832], "pop": 12974, "state": "OH", "_id": "45648"} -{"city": "ALLENSVILLE", "loc": [-82.490699, 39.255326], "pop": 5028, "state": "OH", "_id": "45651"} -{"city": "MC DERMOTT", "loc": [-83.068892, 38.836203], "pop": 2932, "state": "OH", "_id": "45652"} -{"city": "MINFORD", "loc": [-82.855538, 38.875087], "pop": 3969, "state": "OH", "_id": "45653"} -{"city": "NEW PLYMOUTH", "loc": [-82.389172, 39.388421], "pop": 804, "state": "OH", "_id": "45654"} -{"city": "OAK HILL", "loc": [-82.588343, 38.891559], "pop": 6036, "state": "OH", "_id": "45656"} -{"city": "OTWAY", "loc": [-83.222172, 38.85203], "pop": 2287, "state": "OH", "_id": "45657"} -{"city": "PATRIOT", "loc": [-82.427491, 38.77702], "pop": 2202, "state": "OH", "_id": "45658"} -{"city": "PEDRO", "loc": [-82.647722, 38.650323], "pop": 3870, "state": "OH", "_id": "45659"} -{"city": "PEEBLES", "loc": [-83.368698, 38.986885], "pop": 7298, "state": "OH", "_id": "45660"} -{"city": "IDAHO", "loc": [-83.076742, 39.040196], "pop": 4342, "state": "OH", "_id": "45661"} -{"city": "NEW BOSTON", "loc": [-82.94864, 38.757252], "pop": 33275, "state": "OH", "_id": "45662"} -{"city": "PORTSMOUTH", "loc": [-83.047657, 38.749222], "pop": 8485, "state": "OH", "_id": "45663"} -{"city": "PROCTORVILLE", "loc": [-82.352294, 38.463461], "pop": 8976, "state": "OH", "_id": "45669"} -{"city": "RADCLIFF", "loc": [-82.354243, 39.105205], "pop": 1413, "state": "OH", "_id": "45670"} -{"city": "RARDEN", "loc": [-83.237337, 38.943388], "pop": 524, "state": "OH", "_id": "45671"} -{"city": "RAY", "loc": [-82.690801, 39.207678], "pop": 1472, "state": "OH", "_id": "45672"} -{"city": "RICHMOND DALE", "loc": [-82.814913, 39.203922], "pop": 454, "state": "OH", "_id": "45673"} -{"city": "ROCK CAMP", "loc": [-82.563832, 38.522145], "pop": 435, "state": "OH", "_id": "45675"} -{"city": "SCOTTOWN", "loc": [-82.396685, 38.594034], "pop": 1433, "state": "OH", "_id": "45678"} -{"city": "SEAMAN", "loc": [-83.593625, 38.962072], "pop": 3482, "state": "OH", "_id": "45679"} -{"city": "SOUTH POINT", "loc": [-82.552883, 38.433856], "pop": 13272, "state": "OH", "_id": "45680"} -{"city": "SOUTH SALEM", "loc": [-83.271953, 39.302137], "pop": 1001, "state": "OH", "_id": "45681"} -{"city": "SOUTH WEBSTER", "loc": [-82.720151, 38.819967], "pop": 2094, "state": "OH", "_id": "45682"} -{"city": "STOUT", "loc": [-83.208984, 38.654588], "pop": 1342, "state": "OH", "_id": "45684"} -{"city": "THURMAN", "loc": [-82.404996, 38.898786], "pop": 2291, "state": "OH", "_id": "45685"} -{"city": "VINTON", "loc": [-82.357014, 38.978338], "pop": 1898, "state": "OH", "_id": "45686"} -{"city": "WATERLOO", "loc": [-82.517399, 38.718152], "pop": 385, "state": "OH", "_id": "45688"} -{"city": "WAVERLY", "loc": [-83.004874, 39.126445], "pop": 14422, "state": "OH", "_id": "45690"} -{"city": "WELLSTON", "loc": [-82.548474, 39.118897], "pop": 9486, "state": "OH", "_id": "45692"} -{"city": "WEST UNION", "loc": [-83.533349, 38.801702], "pop": 8656, "state": "OH", "_id": "45693"} -{"city": "WHEELERSBURG", "loc": [-82.820434, 38.741793], "pop": 10537, "state": "OH", "_id": "45694"} -{"city": "WILLOW WOOD", "loc": [-82.453, 38.593976], "pop": 665, "state": "OH", "_id": "45696"} -{"city": "WINCHESTER", "loc": [-83.666137, 38.935283], "pop": 3146, "state": "OH", "_id": "45697"} -{"city": "ATHENS", "loc": [-82.102011, 39.317824], "pop": 28523, "state": "OH", "_id": "45701"} -{"city": "ALBANY", "loc": [-82.217727, 39.209664], "pop": 4090, "state": "OH", "_id": "45710"} -{"city": "AMESVILLE", "loc": [-81.964976, 39.408641], "pop": 1661, "state": "OH", "_id": "45711"} -{"city": "BELPRE", "loc": [-81.596795, 39.286772], "pop": 9237, "state": "OH", "_id": "45714"} -{"city": "BEVERLY", "loc": [-81.634588, 39.571435], "pop": 2715, "state": "OH", "_id": "45715"} -{"city": "COOLVILLE", "loc": [-81.832867, 39.214131], "pop": 5023, "state": "OH", "_id": "45723"} -{"city": "CUTLER", "loc": [-81.76573, 39.404172], "pop": 2624, "state": "OH", "_id": "45724"} -{"city": "DEXTER CITY", "loc": [-81.467151, 39.652585], "pop": 277, "state": "OH", "_id": "45727"} -{"city": "FLEMING", "loc": [-81.656444, 39.392806], "pop": 1864, "state": "OH", "_id": "45729"} -{"city": "GLOUSTER", "loc": [-82.087128, 39.497837], "pop": 6226, "state": "OH", "_id": "45732"} -{"city": "RINARD MILLS", "loc": [-81.189152, 39.659384], "pop": 535, "state": "OH", "_id": "45734"} -{"city": "GUYSVILLE", "loc": [-81.926841, 39.294368], "pop": 359, "state": "OH", "_id": "45735"} -{"city": "DEXTER", "loc": [-82.231186, 39.056325], "pop": 1613, "state": "OH", "_id": "45741"} -{"city": "LITTLE HOCKING", "loc": [-81.707107, 39.280046], "pop": 3346, "state": "OH", "_id": "45742"} -{"city": "LONG BOTTOM", "loc": [-81.888748, 39.08015], "pop": 1692, "state": "OH", "_id": "45743"} -{"city": "LOWELL", "loc": [-81.519679, 39.53847], "pop": 1741, "state": "OH", "_id": "45744"} -{"city": "WARNER", "loc": [-81.35452, 39.593895], "pop": 1900, "state": "OH", "_id": "45745"} -{"city": "MACKSBURG", "loc": [-81.447088, 39.620356], "pop": 445, "state": "OH", "_id": "45746"} -{"city": "MARIETTA", "loc": [-81.464438, 39.428141], "pop": 25904, "state": "OH", "_id": "45750"} -{"city": "MIDDLEPORT", "loc": [-82.060012, 38.999312], "pop": 3433, "state": "OH", "_id": "45760"} -{"city": "MILLFIELD", "loc": [-82.111328, 39.407711], "pop": 2656, "state": "OH", "_id": "45761"} -{"city": "NELSONVILLE", "loc": [-82.230883, 39.455635], "pop": 8924, "state": "OH", "_id": "45764"} -{"city": "NEW MARSHFIELD", "loc": [-82.222513, 39.338295], "pop": 2321, "state": "OH", "_id": "45766"} -{"city": "NEW MATAMORAS", "loc": [-81.093998, 39.528819], "pop": 3032, "state": "OH", "_id": "45767"} -{"city": "NEWPORT", "loc": [-81.240175, 39.39711], "pop": 1645, "state": "OH", "_id": "45768"} -{"city": "POMEROY", "loc": [-82.033145, 39.060729], "pop": 5509, "state": "OH", "_id": "45769"} -{"city": "PORTLAND", "loc": [-81.813548, 38.999919], "pop": 905, "state": "OH", "_id": "45770"} -{"city": "RACINE", "loc": [-81.925759, 38.978551], "pop": 3774, "state": "OH", "_id": "45771"} -{"city": "REEDSVILLE", "loc": [-81.792433, 39.149002], "pop": 1305, "state": "OH", "_id": "45772"} -{"city": "RENO", "loc": [-81.38043, 39.378408], "pop": 2673, "state": "OH", "_id": "45773"} -{"city": "45774", "loc": [-81.182005, 39.559845], "pop": 598, "state": "OH", "_id": "45774"} -{"city": "RUTLAND", "loc": [-82.121914, 39.045653], "pop": 1598, "state": "OH", "_id": "45775"} -{"city": "SHADE", "loc": [-82.021755, 39.212862], "pop": 1890, "state": "OH", "_id": "45776"} -{"city": "STEWART", "loc": [-81.892884, 39.32133], "pop": 975, "state": "OH", "_id": "45778"} -{"city": "THE PLAINS", "loc": [-82.134148, 39.366177], "pop": 3567, "state": "OH", "_id": "45780"} -{"city": "VINCENT", "loc": [-81.674265, 39.337446], "pop": 1524, "state": "OH", "_id": "45784"} -{"city": "WATERFORD", "loc": [-81.655917, 39.515904], "pop": 3932, "state": "OH", "_id": "45786"} -{"city": "WHIPPLE", "loc": [-81.363639, 39.480975], "pop": 1835, "state": "OH", "_id": "45788"} -{"city": "WINGETT RUN", "loc": [-81.283999, 39.542813], "pop": 365, "state": "OH", "_id": "45789"} -{"city": "LIMA", "loc": [-84.097296, 40.764066], "pop": 27344, "state": "OH", "_id": "45801"} -{"city": "LIMA", "loc": [-84.089023, 40.727476], "pop": 19289, "state": "OH", "_id": "45804"} -{"city": "LIMA", "loc": [-84.14591, 40.739911], "pop": 23080, "state": "OH", "_id": "45805"} -{"city": "CRIDERSVILLE", "loc": [-84.144049, 40.675926], "pop": 12250, "state": "OH", "_id": "45806"} -{"city": "ELIDA", "loc": [-84.163966, 40.791599], "pop": 9531, "state": "OH", "_id": "45807"} -{"city": "ADA", "loc": [-83.815402, 40.770931], "pop": 7299, "state": "OH", "_id": "45810"} -{"city": "ALGER", "loc": [-83.825011, 40.705967], "pop": 2394, "state": "OH", "_id": "45812"} -{"city": "ANTWERP", "loc": [-84.744796, 41.188736], "pop": 3039, "state": "OH", "_id": "45813"} -{"city": "ARLINGTON", "loc": [-83.668536, 40.876109], "pop": 2603, "state": "OH", "_id": "45814"} -{"city": "BLUFFTON", "loc": [-83.891397, 40.878978], "pop": 6055, "state": "OH", "_id": "45817"} -{"city": "CECIL", "loc": [-84.629646, 41.217355], "pop": 1527, "state": "OH", "_id": "45821"} -{"city": "CARTHAGENA", "loc": [-84.570067, 40.546074], "pop": 18799, "state": "OH", "_id": "45822"} -{"city": "CLOVERDALE", "loc": [-84.29382, 41.037934], "pop": 1225, "state": "OH", "_id": "45827"} -{"city": "COLDWATER", "loc": [-84.651653, 40.484557], "pop": 6677, "state": "OH", "_id": "45828"} -{"city": "COLUMBUS GROVE", "loc": [-84.070467, 40.91372], "pop": 6030, "state": "OH", "_id": "45830"} -{"city": "CONTINENTAL", "loc": [-84.235778, 41.114769], "pop": 3518, "state": "OH", "_id": "45831"} -{"city": "CONVOY", "loc": [-84.723772, 40.926998], "pop": 2134, "state": "OH", "_id": "45832"} -{"city": "DELPHOS", "loc": [-84.324678, 40.833619], "pop": 10062, "state": "OH", "_id": "45833"} -{"city": "DOLA", "loc": [-83.689371, 40.787645], "pop": 256, "state": "OH", "_id": "45835"} -{"city": "DUNKIRK", "loc": [-83.633912, 40.782375], "pop": 1522, "state": "OH", "_id": "45836"} -{"city": "FINDLAY", "loc": [-83.645656, 41.044903], "pop": 48109, "state": "OH", "_id": "45840"} -{"city": "JENERA", "loc": [-83.725628, 40.900388], "pop": 335, "state": "OH", "_id": "45841"} -{"city": "PATTERSON", "loc": [-83.509771, 40.79955], "pop": 3333, "state": "OH", "_id": "45843"} -{"city": "FORT JENNINGS", "loc": [-84.237381, 40.948393], "pop": 5287, "state": "OH", "_id": "45844"} -{"city": "FORT LORAMIE", "loc": [-84.374131, 40.330632], "pop": 2639, "state": "OH", "_id": "45845"} -{"city": "FORT RECOVERY", "loc": [-84.76125, 40.401829], "pop": 3555, "state": "OH", "_id": "45846"} -{"city": "GROVER HILL", "loc": [-84.495601, 41.024497], "pop": 1113, "state": "OH", "_id": "45849"} -{"city": "HARROD", "loc": [-83.943649, 40.717657], "pop": 4897, "state": "OH", "_id": "45850"} -{"city": "HAVILAND", "loc": [-84.6139, 41.032911], "pop": 659, "state": "OH", "_id": "45851"} -{"city": "LEIPSIC", "loc": [-83.995723, 41.109197], "pop": 4657, "state": "OH", "_id": "45856"} -{"city": "MC COMB", "loc": [-83.801423, 41.112498], "pop": 2298, "state": "OH", "_id": "45858"} -{"city": "MARIA STEIN", "loc": [-84.507579, 40.406225], "pop": 2284, "state": "OH", "_id": "45860"} -{"city": "MENDON", "loc": [-84.51515, 40.677737], "pop": 1527, "state": "OH", "_id": "45862"} -{"city": "MIDDLE POINT", "loc": [-84.417495, 40.873147], "pop": 2691, "state": "OH", "_id": "45863"} -{"city": "MINSTER", "loc": [-84.372895, 40.390983], "pop": 4436, "state": "OH", "_id": "45865"} -{"city": "MOUNT BLANCHARD", "loc": [-83.555286, 40.892929], "pop": 957, "state": "OH", "_id": "45867"} -{"city": "MOUNT CORY", "loc": [-83.809285, 40.943717], "pop": 1553, "state": "OH", "_id": "45868"} -{"city": "NEW BREMEN", "loc": [-84.3821, 40.438917], "pop": 3400, "state": "OH", "_id": "45869"} -{"city": "NEW KNOXVILLE", "loc": [-84.311797, 40.50386], "pop": 2003, "state": "OH", "_id": "45871"} -{"city": "NORTH BALTIMORE", "loc": [-83.680581, 41.186703], "pop": 3820, "state": "OH", "_id": "45872"} -{"city": "OAKWOOD", "loc": [-84.396923, 41.090788], "pop": 3129, "state": "OH", "_id": "45873"} -{"city": "OHIO CITY", "loc": [-84.673063, 40.785428], "pop": 3117, "state": "OH", "_id": "45874"} -{"city": "GILBOA", "loc": [-84.047293, 41.029747], "pop": 10177, "state": "OH", "_id": "45875"} -{"city": "OTTOVILLE", "loc": [-84.347392, 40.937218], "pop": 1972, "state": "OH", "_id": "45876"} -{"city": "PANDORA", "loc": [-83.952073, 40.95087], "pop": 2026, "state": "OH", "_id": "45877"} -{"city": "PAULDING", "loc": [-84.572227, 41.141016], "pop": 6565, "state": "OH", "_id": "45879"} -{"city": "PAYNE", "loc": [-84.734127, 41.08069], "pop": 2766, "state": "OH", "_id": "45880"} -{"city": "RAWSON", "loc": [-83.806951, 41.000135], "pop": 955, "state": "OH", "_id": "45881"} -{"city": "ROCKFORD", "loc": [-84.664174, 40.677077], "pop": 3038, "state": "OH", "_id": "45882"} -{"city": "SAINT HENRY", "loc": [-84.633338, 40.409138], "pop": 3615, "state": "OH", "_id": "45883"} -{"city": "SAINT MARYS", "loc": [-84.394399, 40.543988], "pop": 13066, "state": "OH", "_id": "45885"} -{"city": "SCOTT", "loc": [-84.584455, 40.989238], "pop": 397, "state": "OH", "_id": "45886"} -{"city": "SPENCERVILLE", "loc": [-84.34125, 40.703767], "pop": 5745, "state": "OH", "_id": "45887"} -{"city": "VAN BUREN", "loc": [-83.647302, 41.132691], "pop": 1232, "state": "OH", "_id": "45889"} -{"city": "VANLUE", "loc": [-83.497062, 40.958314], "pop": 901, "state": "OH", "_id": "45890"} -{"city": "VAN WERT", "loc": [-84.590364, 40.868927], "pop": 16625, "state": "OH", "_id": "45891"} -{"city": "VENEDOCIA", "loc": [-84.46204, 40.768454], "pop": 966, "state": "OH", "_id": "45894"} -{"city": "WAPAKONETA", "loc": [-84.177378, 40.568978], "pop": 16364, "state": "OH", "_id": "45895"} -{"city": "WAYNESFIELD", "loc": [-83.958475, 40.607185], "pop": 1648, "state": "OH", "_id": "45896"} -{"city": "WILLSHIRE", "loc": [-84.77773, 40.734618], "pop": 1265, "state": "OH", "_id": "45898"} -{"city": "ALEX", "loc": [-97.75709, 34.961202], "pop": 1976, "state": "OK", "_id": "73002"} -{"city": "AMBER", "loc": [-97.764566, 35.137691], "pop": 2575, "state": "OK", "_id": "73004"} -{"city": "ANADARKO", "loc": [-98.24291, 35.072808], "pop": 10332, "state": "OK", "_id": "73005"} -{"city": "APACHE", "loc": [-98.369483, 34.903376], "pop": 2969, "state": "OK", "_id": "73006"} -{"city": "ARCADIA", "loc": [-97.324289, 35.6543], "pop": 697, "state": "OK", "_id": "73007"} -{"city": "BETHANY", "loc": [-97.639856, 35.504315], "pop": 21064, "state": "OK", "_id": "73008"} -{"city": "BINGER", "loc": [-98.314797, 35.310593], "pop": 1799, "state": "OK", "_id": "73009"} -{"city": "BLANCHARD", "loc": [-97.640131, 35.119215], "pop": 4511, "state": "OK", "_id": "73010"} -{"city": "BRADLEY", "loc": [-97.711841, 34.874124], "pop": 416, "state": "OK", "_id": "73011"} -{"city": "EDMOND", "loc": [-97.473268, 35.621534], "pop": 22802, "state": "OK", "_id": "73013"} -{"city": "CALUMET", "loc": [-98.189991, 35.594819], "pop": 1507, "state": "OK", "_id": "73014"} -{"city": "CARNEGIE", "loc": [-98.575454, 35.123484], "pop": 3907, "state": "OK", "_id": "73015"} -{"city": "CASHION", "loc": [-97.679523, 35.799996], "pop": 408, "state": "OK", "_id": "73016"} -{"city": "CEMENT", "loc": [-98.14656, 34.932092], "pop": 1641, "state": "OK", "_id": "73017"} -{"city": "CHICKASHA", "loc": [-97.951847, 35.026751], "pop": 19634, "state": "OK", "_id": "73018"} -{"city": "CHOCTAW", "loc": [-97.272564, 35.471758], "pop": 13832, "state": "OK", "_id": "73020"} -{"city": "COLONY", "loc": [-98.670674, 35.344844], "pop": 312, "state": "OK", "_id": "73021"} -{"city": "CORN", "loc": [-98.806226, 35.399842], "pop": 1156, "state": "OK", "_id": "73024"} -{"city": "COYLE", "loc": [-97.260683, 35.898496], "pop": 3054, "state": "OK", "_id": "73027"} -{"city": "CRESCENT", "loc": [-97.596922, 35.942], "pop": 2832, "state": "OK", "_id": "73028"} -{"city": "CYRIL", "loc": [-98.208269, 34.895854], "pop": 1433, "state": "OK", "_id": "73029"} -{"city": "DAVIS", "loc": [-97.10843, 34.495301], "pop": 4823, "state": "OK", "_id": "73030"} -{"city": "EDMOND", "loc": [-97.479835, 35.666483], "pop": 43814, "state": "OK", "_id": "73034"} -{"city": "ELMORE CITY", "loc": [-97.39003, 34.608516], "pop": 2520, "state": "OK", "_id": "73035"} -{"city": "EL RENO", "loc": [-97.959091, 35.533468], "pop": 18480, "state": "OK", "_id": "73036"} -{"city": "FORT COBB", "loc": [-98.430296, 35.116097], "pop": 1847, "state": "OK", "_id": "73038"} -{"city": "FOSTER", "loc": [-97.533395, 34.627789], "pop": 578, "state": "OK", "_id": "73039"} -{"city": "GEARY", "loc": [-98.390529, 35.621709], "pop": 2124, "state": "OK", "_id": "73040"} -{"city": "GOTEBO", "loc": [-98.875977, 35.075891], "pop": 534, "state": "OK", "_id": "73041"} -{"city": "GRACEMONT", "loc": [-98.283513, 35.187498], "pop": 968, "state": "OK", "_id": "73042"} -{"city": "GREENFIELD", "loc": [-98.384073, 35.733269], "pop": 270, "state": "OK", "_id": "73043"} -{"city": "GUTHRIE", "loc": [-97.435995, 35.832955], "pop": 19769, "state": "OK", "_id": "73044"} -{"city": "HARRAH", "loc": [-97.17343, 35.483258], "pop": 6766, "state": "OK", "_id": "73045"} -{"city": "HENNEPIN", "loc": [-97.421467, 34.485893], "pop": 656, "state": "OK", "_id": "73046"} -{"city": "HINTON", "loc": [-98.331348, 35.4675], "pop": 2433, "state": "OK", "_id": "73047"} -{"city": "HYDRO", "loc": [-98.560448, 35.45201], "pop": 2009, "state": "OK", "_id": "73048"} -{"city": "JONES", "loc": [-97.28914, 35.575316], "pop": 3059, "state": "OK", "_id": "73049"} -{"city": "LEXINGTON", "loc": [-97.260945, 35.037661], "pop": 7904, "state": "OK", "_id": "73051"} -{"city": "LINDSAY", "loc": [-97.599788, 34.821116], "pop": 5597, "state": "OK", "_id": "73052"} -{"city": "LOOKEBA", "loc": [-98.389833, 35.367946], "pop": 802, "state": "OK", "_id": "73053"} -{"city": "LUTHER", "loc": [-97.182292, 35.631491], "pop": 2111, "state": "OK", "_id": "73054"} -{"city": "MARLOW", "loc": [-97.940955, 34.638681], "pop": 7881, "state": "OK", "_id": "73055"} -{"city": "MARSHALL", "loc": [-97.617052, 36.148455], "pop": 479, "state": "OK", "_id": "73056"} -{"city": "MAYSVILLE", "loc": [-97.413143, 34.811316], "pop": 2456, "state": "OK", "_id": "73057"} -{"city": "MERIDIAN", "loc": [-97.24623, 35.8451], "pop": 61, "state": "OK", "_id": "73058"} -{"city": "MINCO", "loc": [-97.96638, 35.306723], "pop": 2119, "state": "OK", "_id": "73059"} -{"city": "MORRISON", "loc": [-97.022777, 36.290214], "pop": 1425, "state": "OK", "_id": "73061"} -{"city": "MOUNTAIN VIEW", "loc": [-98.730694, 35.06535], "pop": 1808, "state": "OK", "_id": "73062"} -{"city": "MULHALL", "loc": [-97.409809, 36.053678], "pop": 463, "state": "OK", "_id": "73063"} -{"city": "MUSTANG", "loc": [-97.730888, 35.388498], "pop": 12156, "state": "OK", "_id": "73064"} -{"city": "NEWCASTLE", "loc": [-97.621573, 35.245269], "pop": 4653, "state": "OK", "_id": "73065"} -{"city": "NINNEKAH", "loc": [-97.933277, 34.91435], "pop": 1075, "state": "OK", "_id": "73067"} -{"city": "NOBLE", "loc": [-97.340929, 35.141742], "pop": 8098, "state": "OK", "_id": "73068"} -{"city": "NORMAN", "loc": [-97.457743, 35.220389], "pop": 21299, "state": "OK", "_id": "73069"} -{"city": "NORMAN", "loc": [-97.379159, 35.224254], "pop": 32228, "state": "OK", "_id": "73071"} -{"city": "NORMAN", "loc": [-97.472984, 35.210733], "pop": 27969, "state": "OK", "_id": "73072"} -{"city": "ORLANDO", "loc": [-97.395992, 36.141973], "pop": 332, "state": "OK", "_id": "73073"} -{"city": "PAOLI", "loc": [-97.260807, 34.828492], "pop": 989, "state": "OK", "_id": "73074"} -{"city": "PAULS VALLEY", "loc": [-97.219501, 34.738506], "pop": 8663, "state": "OK", "_id": "73075"} -{"city": "PERRY", "loc": [-97.284175, 36.287468], "pop": 7231, "state": "OK", "_id": "73077"} -{"city": "PIEDMONT", "loc": [-97.743109, 35.66946], "pop": 2267, "state": "OK", "_id": "73078"} -{"city": "POCASSET", "loc": [-97.97904, 35.154437], "pop": 895, "state": "OK", "_id": "73079"} -{"city": "PURCELL", "loc": [-97.425493, 35.010293], "pop": 8700, "state": "OK", "_id": "73080"} -{"city": "RATLIFF CITY", "loc": [-97.514424, 34.420719], "pop": 894, "state": "OK", "_id": "73081"} -{"city": "RUSH SPRINGS", "loc": [-97.943101, 34.770804], "pop": 3537, "state": "OK", "_id": "73082"} -{"city": "SPENCER", "loc": [-97.348775, 35.518276], "pop": 7675, "state": "OK", "_id": "73084"} -{"city": "SULPHUR", "loc": [-96.979695, 34.511585], "pop": 7018, "state": "OK", "_id": "73086"} -{"city": "TUSSY", "loc": [-97.536379, 34.492159], "pop": 90, "state": "OK", "_id": "73088"} -{"city": "TUTTLE", "loc": [-97.744621, 35.267406], "pop": 8753, "state": "OK", "_id": "73089"} -{"city": "UNION CITY", "loc": [-97.93979, 35.391333], "pop": 551, "state": "OK", "_id": "73090"} -{"city": "VERDEN", "loc": [-98.079206, 35.08356], "pop": 734, "state": "OK", "_id": "73092"} -{"city": "WASHINGTON", "loc": [-97.486969, 35.13235], "pop": 1670, "state": "OK", "_id": "73093"} -{"city": "WAYNE", "loc": [-97.329014, 34.915353], "pop": 1430, "state": "OK", "_id": "73095"} -{"city": "WEATHERFORD", "loc": [-98.699603, 35.535046], "pop": 11963, "state": "OK", "_id": "73096"} -{"city": "WYNNEWOOD", "loc": [-97.176952, 34.63847], "pop": 3762, "state": "OK", "_id": "73098"} -{"city": "YUKON", "loc": [-97.732307, 35.49772], "pop": 38891, "state": "OK", "_id": "73099"} -{"city": "OKLAHOMA CITY", "loc": [-97.519926, 35.472601], "pop": 2227, "state": "OK", "_id": "73102"} -{"city": "OKLAHOMA CITY", "loc": [-97.519591, 35.490957], "pop": 4253, "state": "OK", "_id": "73103"} -{"city": "OKLAHOMA CITY", "loc": [-97.501714, 35.479388], "pop": 2534, "state": "OK", "_id": "73104"} -{"city": "OKLAHOMA CITY", "loc": [-97.500291, 35.510811], "pop": 5379, "state": "OK", "_id": "73105"} -{"city": "OKLAHOMA CITY", "loc": [-97.537228, 35.485328], "pop": 12706, "state": "OK", "_id": "73106"} -{"city": "OKLAHOMA CITY", "loc": [-97.573974, 35.48736], "pop": 23130, "state": "OK", "_id": "73107"} -{"city": "OKLAHOMA CITY", "loc": [-97.561928, 35.444485], "pop": 13259, "state": "OK", "_id": "73108"} -{"city": "OKLAHOMA CITY", "loc": [-97.526131, 35.425944], "pop": 16821, "state": "OK", "_id": "73109"} -{"city": "MIDWEST CITY", "loc": [-97.397661, 35.461978], "pop": 35101, "state": "OK", "_id": "73110"} -{"city": "OKLAHOMA CITY", "loc": [-97.480607, 35.504238], "pop": 15332, "state": "OK", "_id": "73111"} -{"city": "OKLAHOMA CITY", "loc": [-97.574639, 35.518435], "pop": 29057, "state": "OK", "_id": "73112"} -{"city": "OKLAHOMA CITY", "loc": [-97.525736, 35.570357], "pop": 15126, "state": "OK", "_id": "73114"} -{"city": "DEL CITY", "loc": [-97.441645, 35.440093], "pop": 23589, "state": "OK", "_id": "73115"} -{"city": "NICHOLS HILLS", "loc": [-97.56394, 35.542484], "pop": 9559, "state": "OK", "_id": "73116"} -{"city": "OKLAHOMA CITY", "loc": [-97.472195, 35.479667], "pop": 6489, "state": "OK", "_id": "73117"} -{"city": "OKLAHOMA CITY", "loc": [-97.531908, 35.513645], "pop": 13826, "state": "OK", "_id": "73118"} -{"city": "OKLAHOMA CITY", "loc": [-97.561584, 35.421033], "pop": 25150, "state": "OK", "_id": "73119"} -{"city": "OKLAHOMA CITY", "loc": [-97.563756, 35.583478], "pop": 35879, "state": "OK", "_id": "73120"} -{"city": "OKLAHOMA CITY", "loc": [-97.445183, 35.506235], "pop": 3134, "state": "OK", "_id": "73121"} -{"city": "WARR ACRES", "loc": [-97.613305, 35.520239], "pop": 13337, "state": "OK", "_id": "73122"} -{"city": "OKLAHOMA CITY", "loc": [-97.629927, 35.483371], "pop": 20789, "state": "OK", "_id": "73127"} -{"city": "OKLAHOMA CITY", "loc": [-97.616362, 35.444358], "pop": 1349, "state": "OK", "_id": "73128"} -{"city": "OKLAHOMA CITY", "loc": [-97.491309, 35.43119], "pop": 18830, "state": "OK", "_id": "73129"} -{"city": "MIDWEST CITY", "loc": [-97.351489, 35.460863], "pop": 16223, "state": "OK", "_id": "73130"} -{"city": "OKLAHOMA CITY", "loc": [-97.469127, 35.579693], "pop": 1460, "state": "OK", "_id": "73131"} -{"city": "WARR ACRES", "loc": [-97.636333, 35.552783], "pop": 22038, "state": "OK", "_id": "73132"} -{"city": "OKLAHOMA CITY", "loc": [-97.558342, 35.617397], "pop": 1317, "state": "OK", "_id": "73134"} -{"city": "OKLAHOMA CITY", "loc": [-97.438762, 35.411037], "pop": 13933, "state": "OK", "_id": "73135"} -{"city": "OKLAHOMA CITY", "loc": [-97.536205, 35.379193], "pop": 22429, "state": "OK", "_id": "73139"} -{"city": "OKLAHOMA CITY", "loc": [-97.366606, 35.491848], "pop": 2667, "state": "OK", "_id": "73141"} -{"city": "OKLAHOMA CITY", "loc": [-97.625067, 35.598994], "pop": 4366, "state": "OK", "_id": "73142"} -{"city": "TINKER AFB", "loc": [-97.403707, 35.415706], "pop": 3714, "state": "OK", "_id": "73145"} -{"city": "OKLAHOMA CITY", "loc": [-97.497175, 35.394998], "pop": 5335, "state": "OK", "_id": "73149"} -{"city": "OKLAHOMA CITY", "loc": [-97.33308, 35.41231], "pop": 4512, "state": "OK", "_id": "73150"} -{"city": "OKLAHOMA CITY", "loc": [-97.39057, 35.568508], "pop": 1315, "state": "OK", "_id": "73151"} -{"city": "OKLAHOMA CITY", "loc": [-97.55674, 35.39224], "pop": 21343, "state": "OK", "_id": "73159"} -{"city": "MOORE", "loc": [-97.487352, 35.342465], "pop": 39935, "state": "OK", "_id": "73160"} -{"city": "OKLAHOMA CITY", "loc": [-97.641934, 35.580647], "pop": 21084, "state": "OK", "_id": "73162"} -{"city": "MOORE", "loc": [-97.349792, 35.337086], "pop": 4202, "state": "OK", "_id": "73165"} -{"city": "OKLAHOMA CITY", "loc": [-97.658683, 35.388233], "pop": 1266, "state": "OK", "_id": "73169"} -{"city": "MOORE", "loc": [-97.536, 35.341554], "pop": 13250, "state": "OK", "_id": "73170"} -{"city": "OKLAHOMA CITY", "loc": [-97.63171, 35.342455], "pop": 667, "state": "OK", "_id": "73173"} -{"city": "OKLAHOMA CITY", "loc": [-97.654729, 35.424157], "pop": 1177, "state": "OK", "_id": "73179"} -{"city": "MILO", "loc": [-97.134157, 34.176681], "pop": 30328, "state": "OK", "_id": "73401"} -{"city": "BURNEYVILLE", "loc": [-97.324929, 33.951516], "pop": 740, "state": "OK", "_id": "73430"} -{"city": "COLEMAN", "loc": [-96.458818, 34.262498], "pop": 1073, "state": "OK", "_id": "73432"} -{"city": "GRAHAM", "loc": [-97.495435, 34.337379], "pop": 674, "state": "OK", "_id": "73437"} -{"city": "HEALDTON", "loc": [-97.488904, 34.229017], "pop": 3114, "state": "OK", "_id": "73438"} -{"city": "KINGSTON", "loc": [-96.711977, 33.951664], "pop": 3808, "state": "OK", "_id": "73439"} -{"city": "LEBANON", "loc": [-96.864495, 33.961036], "pop": 144, "state": "OK", "_id": "73440"} -{"city": "LEON", "loc": [-97.447058, 33.924278], "pop": 919, "state": "OK", "_id": "73441"} -{"city": "LOCO", "loc": [-97.665803, 34.321416], "pop": 338, "state": "OK", "_id": "73442"} -{"city": "LONE GROVE", "loc": [-97.268523, 34.177373], "pop": 3002, "state": "OK", "_id": "73443"} -{"city": "MC MILLAN", "loc": [-96.787258, 34.07126], "pop": 6877, "state": "OK", "_id": "73446"} -{"city": "MANNSVILLE", "loc": [-96.877801, 34.189902], "pop": 888, "state": "OK", "_id": "73447"} -{"city": "MARIETTA", "loc": [-97.114801, 33.943099], "pop": 5242, "state": "OK", "_id": "73448"} -{"city": "MEAD", "loc": [-96.529886, 33.994442], "pop": 1520, "state": "OK", "_id": "73449"} -{"city": "MILBURN", "loc": [-96.54286, 34.195172], "pop": 884, "state": "OK", "_id": "73450"} -{"city": "OVERBROOK", "loc": [-97.132353, 34.053906], "pop": 283, "state": "OK", "_id": "73453"} -{"city": "RINGLING", "loc": [-97.602867, 34.167865], "pop": 2215, "state": "OK", "_id": "73456"} -{"city": "SPRINGER", "loc": [-97.122266, 34.303832], "pop": 1248, "state": "OK", "_id": "73458"} -{"city": "THACKERVILLE", "loc": [-97.136349, 33.788167], "pop": 973, "state": "OK", "_id": "73459"} -{"city": "TISHOMINGO", "loc": [-96.667502, 34.264286], "pop": 4958, "state": "OK", "_id": "73460"} -{"city": "WAPANUCKA", "loc": [-96.453223, 34.386634], "pop": 719, "state": "OK", "_id": "73461"} -{"city": "RUBOTTOM", "loc": [-97.42487, 34.164206], "pop": 3095, "state": "OK", "_id": "73463"} -{"city": "LAWTON", "loc": [-98.369783, 34.591467], "pop": 18175, "state": "OK", "_id": "73501"} -{"city": "FORT SILL", "loc": [-98.40041, 34.659525], "pop": 12228, "state": "OK", "_id": "73503"} -{"city": "LAWTON", "loc": [-98.455234, 34.617939], "pop": 45542, "state": "OK", "_id": "73505"} -{"city": "LAWTON", "loc": [-98.389453, 34.624595], "pop": 19244, "state": "OK", "_id": "73507"} -{"city": "ALTUS", "loc": [-99.320483, 34.648406], "pop": 22957, "state": "OK", "_id": "73521"} -{"city": "BLAIR", "loc": [-99.333404, 34.778813], "pop": 1147, "state": "OK", "_id": "73526"} -{"city": "CACHE", "loc": [-98.615351, 34.613072], "pop": 3857, "state": "OK", "_id": "73527"} -{"city": "CHATTANOOGA", "loc": [-98.651365, 34.426193], "pop": 483, "state": "OK", "_id": "73528"} -{"city": "COMANCHE", "loc": [-97.979286, 34.376523], "pop": 4998, "state": "OK", "_id": "73529"} -{"city": "DAVIDSON", "loc": [-99.064031, 34.251423], "pop": 800, "state": "OK", "_id": "73530"} -{"city": "DEVOL", "loc": [-98.576991, 34.195589], "pop": 311, "state": "OK", "_id": "73531"} -{"city": "DUKE", "loc": [-99.548172, 34.666769], "pop": 625, "state": "OK", "_id": "73532"} -{"city": "DUNCAN", "loc": [-97.940322, 34.507277], "pop": 28871, "state": "OK", "_id": "73533"} -{"city": "ELDORADO", "loc": [-99.645956, 34.472744], "pop": 742, "state": "OK", "_id": "73537"} -{"city": "ELGIN", "loc": [-98.407322, 34.772018], "pop": 5476, "state": "OK", "_id": "73538"} -{"city": "ELMER", "loc": [-99.316744, 34.513668], "pop": 569, "state": "OK", "_id": "73539"} -{"city": "FAXON", "loc": [-98.55771, 34.464521], "pop": 352, "state": "OK", "_id": "73540"} -{"city": "FLETCHER", "loc": [-98.200246, 34.784657], "pop": 3382, "state": "OK", "_id": "73541"} -{"city": "FREDERICK", "loc": [-99.011877, 34.401199], "pop": 6196, "state": "OK", "_id": "73542"} -{"city": "GERONIMO", "loc": [-98.387525, 34.480499], "pop": 1305, "state": "OK", "_id": "73543"} -{"city": "GOULD", "loc": [-99.784337, 34.664964], "pop": 553, "state": "OK", "_id": "73544"} -{"city": "GRANDFIELD", "loc": [-98.686654, 34.228247], "pop": 1436, "state": "OK", "_id": "73546"} -{"city": "GRANITE", "loc": [-99.388139, 34.971184], "pop": 2216, "state": "OK", "_id": "73547"} -{"city": "HASTINGS", "loc": [-98.107539, 34.225064], "pop": 210, "state": "OK", "_id": "73548"} -{"city": "HEADRICK", "loc": [-99.23904, 34.72189], "pop": 1903, "state": "OK", "_id": "73549"} -{"city": "HOLLIS", "loc": [-99.917711, 34.695281], "pop": 3136, "state": "OK", "_id": "73550"} -{"city": "HOLLISTER", "loc": [-98.881404, 34.352473], "pop": 127, "state": "OK", "_id": "73551"} -{"city": "INDIAHOMA", "loc": [-98.734864, 34.624226], "pop": 1385, "state": "OK", "_id": "73552"} -{"city": "LOVELAND", "loc": [-98.72353, 34.391018], "pop": 312, "state": "OK", "_id": "73553"} -{"city": "REED", "loc": [-99.505769, 34.875473], "pop": 4057, "state": "OK", "_id": "73554"} -{"city": "MOUNTAIN PARK", "loc": [-98.959136, 34.703158], "pop": 631, "state": "OK", "_id": "73559"} -{"city": "OLUSTEE", "loc": [-99.428684, 34.549634], "pop": 821, "state": "OK", "_id": "73560"} -{"city": "OSCAR", "loc": [-97.761359, 33.973237], "pop": 35, "state": "OK", "_id": "73561"} -{"city": "RANDLETT", "loc": [-98.459974, 34.174045], "pop": 891, "state": "OK", "_id": "73562"} -{"city": "ROOSEVELT", "loc": [-98.983599, 34.846983], "pop": 666, "state": "OK", "_id": "73564"} -{"city": "RYAN", "loc": [-97.946141, 34.023787], "pop": 1331, "state": "OK", "_id": "73565"} -{"city": "SNYDER", "loc": [-98.950752, 34.654779], "pop": 1858, "state": "OK", "_id": "73566"} -{"city": "TEMPLE", "loc": [-98.237074, 34.260669], "pop": 1588, "state": "OK", "_id": "73568"} -{"city": "GRADY", "loc": [-97.935045, 33.897162], "pop": 563, "state": "OK", "_id": "73569"} -{"city": "TIPTON", "loc": [-99.131482, 34.509793], "pop": 1513, "state": "OK", "_id": "73570"} -{"city": "VINSON", "loc": [-99.833929, 34.906179], "pop": 104, "state": "OK", "_id": "73571"} -{"city": "WALTERS", "loc": [-98.313983, 34.360526], "pop": 3861, "state": "OK", "_id": "73572"} -{"city": "WAURIKA", "loc": [-97.997346, 34.174466], "pop": 2656, "state": "OK", "_id": "73573"} -{"city": "CLINTON", "loc": [-98.979533, 35.511543], "pop": 10287, "state": "OK", "_id": "73601"} -{"city": "ARAPAHO", "loc": [-98.959508, 35.578864], "pop": 1005, "state": "OK", "_id": "73620"} -{"city": "BESSIE", "loc": [-98.989638, 35.38545], "pop": 271, "state": "OK", "_id": "73622"} -{"city": "BUTLER", "loc": [-99.242749, 35.633638], "pop": 1110, "state": "OK", "_id": "73625"} -{"city": "CANUTE", "loc": [-99.281575, 35.403678], "pop": 1245, "state": "OK", "_id": "73626"} -{"city": "CARTER", "loc": [-99.482239, 35.220779], "pop": 664, "state": "OK", "_id": "73627"} -{"city": "STRONG CITY", "loc": [-99.676688, 35.623181], "pop": 1586, "state": "OK", "_id": "73628"} -{"city": "CORDELL", "loc": [-98.951056, 35.278803], "pop": 4210, "state": "OK", "_id": "73632"} -{"city": "CRAWFORD", "loc": [-99.806446, 35.836766], "pop": 178, "state": "OK", "_id": "73638"} -{"city": "CUSTER CITY", "loc": [-98.912013, 35.689371], "pop": 758, "state": "OK", "_id": "73639"} -{"city": "DILL CITY", "loc": [-99.153738, 35.278704], "pop": 966, "state": "OK", "_id": "73641"} -{"city": "DURHAM", "loc": [-99.908774, 35.836391], "pop": 173, "state": "OK", "_id": "73642"} -{"city": "ELK CITY", "loc": [-99.421086, 35.410359], "pop": 12461, "state": "OK", "_id": "73644"} -{"city": "ERICK", "loc": [-99.863463, 35.228586], "pop": 1638, "state": "OK", "_id": "73645"} -{"city": "FAY", "loc": [-98.658678, 35.820417], "pop": 136, "state": "OK", "_id": "73646"} -{"city": "FOSS", "loc": [-99.15259, 35.373645], "pop": 1609, "state": "OK", "_id": "73647"} -{"city": "HAMMON", "loc": [-99.402683, 35.646131], "pop": 873, "state": "OK", "_id": "73650"} -{"city": "HOBART", "loc": [-99.094433, 35.025521], "pop": 4844, "state": "OK", "_id": "73651"} -{"city": "LEEDEY", "loc": [-99.349146, 35.869772], "pop": 906, "state": "OK", "_id": "73654"} -{"city": "LONE WOLF", "loc": [-99.250161, 34.980584], "pop": 1135, "state": "OK", "_id": "73655"} -{"city": "EAGLE CITY", "loc": [-98.708058, 35.921429], "pop": 332, "state": "OK", "_id": "73658"} -{"city": "PUTNAM", "loc": [-98.963765, 35.853851], "pop": 149, "state": "OK", "_id": "73659"} -{"city": "REYDON", "loc": [-99.916567, 35.657587], "pop": 537, "state": "OK", "_id": "73660"} -{"city": "ROCKY", "loc": [-99.048037, 35.152636], "pop": 242, "state": "OK", "_id": "73661"} -{"city": "SAYRE", "loc": [-99.642928, 35.304722], "pop": 4072, "state": "OK", "_id": "73662"} -{"city": "SEILING", "loc": [-98.88746, 36.127823], "pop": 1847, "state": "OK", "_id": "73663"} -{"city": "SENTINEL", "loc": [-99.170611, 35.161741], "pop": 1358, "state": "OK", "_id": "73664"} -{"city": "SWEETWATER", "loc": [-99.900619, 35.448835], "pop": 428, "state": "OK", "_id": "73666"} -{"city": "TALOGA", "loc": [-98.982473, 35.997242], "pop": 784, "state": "OK", "_id": "73667"} -{"city": "TEXOLA", "loc": [-99.98218, 35.224977], "pop": 89, "state": "OK", "_id": "73668"} -{"city": "THOMAS", "loc": [-98.738842, 35.738165], "pop": 1774, "state": "OK", "_id": "73669"} -{"city": "WILLOW", "loc": [-99.542091, 35.070245], "pop": 376, "state": "OK", "_id": "73673"} -{"city": "ENID", "loc": [-97.862257, 36.402842], "pop": 22487, "state": "OK", "_id": "73701"} -{"city": "ENID", "loc": [-97.915697, 36.397509], "pop": 24700, "state": "OK", "_id": "73703"} -{"city": "ALINE", "loc": [-98.457387, 36.505621], "pop": 526, "state": "OK", "_id": "73716"} -{"city": "ALVA", "loc": [-98.672162, 36.801564], "pop": 6376, "state": "OK", "_id": "73717"} -{"city": "AMES", "loc": [-98.181989, 36.242293], "pop": 618, "state": "OK", "_id": "73718"} -{"city": "AMORITA", "loc": [-98.245821, 36.941221], "pop": 178, "state": "OK", "_id": "73719"} -{"city": "BISON", "loc": [-97.880484, 36.196222], "pop": 212, "state": "OK", "_id": "73720"} -{"city": "BURLINGTON", "loc": [-98.421455, 36.903613], "pop": 359, "state": "OK", "_id": "73722"} -{"city": "BYRON", "loc": [-98.244502, 36.879857], "pop": 172, "state": "OK", "_id": "73723"} -{"city": "CANTON", "loc": [-98.577818, 36.037194], "pop": 1293, "state": "OK", "_id": "73724"} -{"city": "CAPRON", "loc": [-98.629891, 36.896812], "pop": 268, "state": "OK", "_id": "73725"} -{"city": "CARMEN", "loc": [-98.457793, 36.584206], "pop": 642, "state": "OK", "_id": "73726"} -{"city": "CARRIER", "loc": [-97.999688, 36.518892], "pop": 619, "state": "OK", "_id": "73727"} -{"city": "CHEROKEE", "loc": [-98.359375, 36.756376], "pop": 2256, "state": "OK", "_id": "73728"} -{"city": "CLEO SPRINGS", "loc": [-98.442275, 36.408989], "pop": 630, "state": "OK", "_id": "73729"} -{"city": "COVINGTON", "loc": [-97.575163, 36.309944], "pop": 1017, "state": "OK", "_id": "73730"} -{"city": "DACOMA", "loc": [-98.594202, 36.660604], "pop": 351, "state": "OK", "_id": "73731"} -{"city": "DOUGLAS", "loc": [-97.689626, 36.248138], "pop": 314, "state": "OK", "_id": "73733"} -{"city": "DOVER", "loc": [-97.906677, 35.984761], "pop": 580, "state": "OK", "_id": "73734"} -{"city": "DRUMMOND", "loc": [-98.035846, 36.284142], "pop": 896, "state": "OK", "_id": "73735"} -{"city": "FAIRMONT", "loc": [-97.711493, 36.391614], "pop": 701, "state": "OK", "_id": "73736"} -{"city": "ORIENTA", "loc": [-98.506271, 36.265981], "pop": 3964, "state": "OK", "_id": "73737"} -{"city": "GARBER", "loc": [-97.578899, 36.439184], "pop": 1296, "state": "OK", "_id": "73738"} -{"city": "GOLTRY", "loc": [-98.153978, 36.531364], "pop": 436, "state": "OK", "_id": "73739"} -{"city": "HELENA", "loc": [-98.277819, 36.54375], "pop": 1365, "state": "OK", "_id": "73741"} -{"city": "HENNESSEY", "loc": [-97.892595, 36.086848], "pop": 4006, "state": "OK", "_id": "73742"} -{"city": "HITCHCOCK", "loc": [-98.331535, 35.971209], "pop": 231, "state": "OK", "_id": "73744"} -{"city": "ISABELLA", "loc": [-98.337446, 36.23407], "pop": 416, "state": "OK", "_id": "73747"} -{"city": "JET", "loc": [-98.172121, 36.692917], "pop": 526, "state": "OK", "_id": "73749"} -{"city": "KINGFISHER", "loc": [-97.947293, 35.863613], "pop": 6323, "state": "OK", "_id": "73750"} -{"city": "KREMLIN", "loc": [-97.854186, 36.520735], "pop": 971, "state": "OK", "_id": "73753"} -{"city": "LAHOMA", "loc": [-98.072738, 36.385005], "pop": 1275, "state": "OK", "_id": "73754"} -{"city": "LONGDALE", "loc": [-98.549966, 36.121233], "pop": 668, "state": "OK", "_id": "73755"} -{"city": "LOYAL", "loc": [-98.115516, 35.970529], "pop": 132, "state": "OK", "_id": "73756"} -{"city": "LUCIEN", "loc": [-97.452574, 36.275327], "pop": 120, "state": "OK", "_id": "73757"} -{"city": "MANCHESTER", "loc": [-98.03826, 36.9744], "pop": 206, "state": "OK", "_id": "73758"} -{"city": "MEDFORD", "loc": [-97.720215, 36.814195], "pop": 2027, "state": "OK", "_id": "73759"} -{"city": "MENO", "loc": [-98.163501, 36.381497], "pop": 455, "state": "OK", "_id": "73760"} -{"city": "NASH", "loc": [-98.025764, 36.696146], "pop": 647, "state": "OK", "_id": "73761"} -{"city": "OKARCHE", "loc": [-97.92999, 35.750158], "pop": 1703, "state": "OK", "_id": "73762"} -{"city": "OKEENE", "loc": [-98.325422, 36.11646], "pop": 1899, "state": "OK", "_id": "73763"} -{"city": "OMEGA", "loc": [-98.186344, 35.850653], "pop": 60, "state": "OK", "_id": "73764"} -{"city": "POND CREEK", "loc": [-97.801903, 36.664281], "pop": 1173, "state": "OK", "_id": "73766"} -{"city": "RINGWOOD", "loc": [-98.270639, 36.375293], "pop": 1362, "state": "OK", "_id": "73768"} -{"city": "SOUTHARD", "loc": [-98.446456, 36.074706], "pop": 8, "state": "OK", "_id": "73770"} -{"city": "WAKITA", "loc": [-97.942687, 36.875333], "pop": 684, "state": "OK", "_id": "73771"} -{"city": "WATONGA", "loc": [-98.417487, 35.853762], "pop": 5042, "state": "OK", "_id": "73772"} -{"city": "WAUKOMIS", "loc": [-97.899578, 36.278057], "pop": 1704, "state": "OK", "_id": "73773"} -{"city": "WOODWARD", "loc": [-99.402016, 36.426784], "pop": 14279, "state": "OK", "_id": "73801"} -{"city": "HARMON", "loc": [-99.73266, 36.120524], "pop": 1353, "state": "OK", "_id": "73832"} -{"city": "SELMAN", "loc": [-99.604816, 36.835901], "pop": 1957, "state": "OK", "_id": "73834"} -{"city": "CAMARGO", "loc": [-99.278101, 36.021235], "pop": 233, "state": "OK", "_id": "73835"} -{"city": "CHESTER", "loc": [-98.884078, 36.252841], "pop": 561, "state": "OK", "_id": "73838"} -{"city": "FARGO", "loc": [-99.650602, 36.40525], "pop": 692, "state": "OK", "_id": "73840"} -{"city": "FORT SUPPLY", "loc": [-99.526797, 36.564556], "pop": 1222, "state": "OK", "_id": "73841"} -{"city": "FREEDOM", "loc": [-99.131922, 36.809036], "pop": 532, "state": "OK", "_id": "73842"} -{"city": "GAGE", "loc": [-99.760003, 36.317996], "pop": 567, "state": "OK", "_id": "73843"} -{"city": "GATE", "loc": [-100.073386, 36.875679], "pop": 323, "state": "OK", "_id": "73844"} -{"city": "KNOWLES", "loc": [-100.217867, 36.839316], "pop": 49, "state": "OK", "_id": "73847"} -{"city": "LAVERNE", "loc": [-99.891766, 36.70625], "pop": 1747, "state": "OK", "_id": "73848"} -{"city": "LOGAN", "loc": [-100.167934, 36.635554], "pop": 467, "state": "OK", "_id": "73849"} -{"city": "MAY", "loc": [-99.72469, 36.62673], "pop": 88, "state": "OK", "_id": "73851"} -{"city": "MOORELAND", "loc": [-99.18321, 36.442828], "pop": 2047, "state": "OK", "_id": "73852"} -{"city": "MUTUAL", "loc": [-99.114492, 36.213954], "pop": 262, "state": "OK", "_id": "73853"} -{"city": "ROSSTON", "loc": [-99.900253, 36.878915], "pop": 271, "state": "OK", "_id": "73855"} -{"city": "SHARON", "loc": [-99.358736, 36.269872], "pop": 1166, "state": "OK", "_id": "73857"} -{"city": "SHATTUCK", "loc": [-99.879303, 36.288922], "pop": 1886, "state": "OK", "_id": "73858"} -{"city": "VICI", "loc": [-99.267004, 36.137168], "pop": 1333, "state": "OK", "_id": "73859"} -{"city": "WAYNOKA", "loc": [-98.848746, 36.585788], "pop": 1516, "state": "OK", "_id": "73860"} -{"city": "BALKO", "loc": [-100.710329, 36.599607], "pop": 826, "state": "OK", "_id": "73931"} -{"city": "ELMWOOD", "loc": [-100.532938, 36.795562], "pop": 2128, "state": "OK", "_id": "73932"} -{"city": "BOISE CITY", "loc": [-102.535519, 36.728328], "pop": 2064, "state": "OK", "_id": "73933"} -{"city": "FELT", "loc": [-102.797422, 36.566569], "pop": 210, "state": "OK", "_id": "73937"} -{"city": "FORGAN", "loc": [-100.540875, 36.908588], "pop": 688, "state": "OK", "_id": "73938"} -{"city": "GOODWELL", "loc": [-101.71369, 36.674382], "pop": 1857, "state": "OK", "_id": "73939"} -{"city": "GUYMON", "loc": [-101.47778, 36.696052], "pop": 9387, "state": "OK", "_id": "73942"} -{"city": "HARDESTY", "loc": [-101.153944, 36.601754], "pop": 450, "state": "OK", "_id": "73944"} -{"city": "OPTIMA", "loc": [-101.190683, 36.847981], "pop": 2585, "state": "OK", "_id": "73945"} -{"city": "KENTON", "loc": [-102.912421, 36.855639], "pop": 108, "state": "OK", "_id": "73946"} -{"city": "KEYES", "loc": [-102.236063, 36.800316], "pop": 781, "state": "OK", "_id": "73947"} -{"city": "TEXHOMA", "loc": [-101.839351, 36.52529], "pop": 1057, "state": "OK", "_id": "73949"} -{"city": "BAKER", "loc": [-100.869028, 36.909012], "pop": 1542, "state": "OK", "_id": "73950"} -{"city": "TYRONE", "loc": [-101.059408, 36.95577], "pop": 1221, "state": "OK", "_id": "73951"} -{"city": "BARNSDALL", "loc": [-96.131789, 36.542866], "pop": 2936, "state": "OK", "_id": "74002"} -{"city": "BARTLESVILLE", "loc": [-95.992091, 36.743956], "pop": 14990, "state": "OK", "_id": "74003"} -{"city": "BARTLESVILLE", "loc": [-95.92513, 36.736646], "pop": 23075, "state": "OK", "_id": "74006"} -{"city": "BIXBY", "loc": [-95.872895, 35.917291], "pop": 7505, "state": "OK", "_id": "74008"} -{"city": "BRISTOW", "loc": [-96.375838, 35.820904], "pop": 7361, "state": "OK", "_id": "74010"} -{"city": "BROKEN ARROW", "loc": [-95.814332, 35.990812], "pop": 23493, "state": "OK", "_id": "74011"} -{"city": "BROKEN ARROW", "loc": [-95.807863, 36.04466], "pop": 35399, "state": "OK", "_id": "74012"} -{"city": "BROKEN ARROW", "loc": [-95.722269, 36.054435], "pop": 17246, "state": "OK", "_id": "74014"} -{"city": "CATOOSA", "loc": [-95.727321, 36.17208], "pop": 7103, "state": "OK", "_id": "74015"} -{"city": "CHELSEA", "loc": [-95.448871, 36.535586], "pop": 3789, "state": "OK", "_id": "74016"} -{"city": "CLAREMORE", "loc": [-95.598539, 36.324208], "pop": 29964, "state": "OK", "_id": "74017"} -{"city": "CLEVELAND", "loc": [-96.423009, 36.255334], "pop": 8636, "state": "OK", "_id": "74020"} -{"city": "COLLINSVILLE", "loc": [-95.84688, 36.370069], "pop": 8944, "state": "OK", "_id": "74021"} -{"city": "COPAN", "loc": [-95.912987, 36.906181], "pop": 1965, "state": "OK", "_id": "74022"} -{"city": "CUSHING", "loc": [-96.752628, 35.982174], "pop": 9961, "state": "OK", "_id": "74023"} -{"city": "DELAWARE", "loc": [-95.618096, 36.780382], "pop": 1275, "state": "OK", "_id": "74027"} -{"city": "DEPEW", "loc": [-96.489665, 35.756141], "pop": 2224, "state": "OK", "_id": "74028"} -{"city": "DEWEY", "loc": [-95.93454, 36.80125], "pop": 5217, "state": "OK", "_id": "74029"} -{"city": "DRUMRIGHT", "loc": [-96.519789, 35.993136], "pop": 7886, "state": "OK", "_id": "74030"} -{"city": "GLENCOE", "loc": [-96.913845, 36.215815], "pop": 951, "state": "OK", "_id": "74032"} -{"city": "GLENPOOL", "loc": [-95.999709, 35.959106], "pop": 6575, "state": "OK", "_id": "74033"} -{"city": "HOMINY", "loc": [-96.387848, 36.411068], "pop": 3410, "state": "OK", "_id": "74035"} -{"city": "INOLA", "loc": [-95.520456, 36.150315], "pop": 4268, "state": "OK", "_id": "74036"} -{"city": "JENKS", "loc": [-95.979711, 36.014834], "pop": 8086, "state": "OK", "_id": "74037"} -{"city": "JENNINGS", "loc": [-96.573227, 36.186302], "pop": 766, "state": "OK", "_id": "74038"} -{"city": "KELLYVILLE", "loc": [-96.218009, 35.917075], "pop": 2648, "state": "OK", "_id": "74039"} -{"city": "LENAPAH", "loc": [-95.623316, 36.880055], "pop": 692, "state": "OK", "_id": "74042"} -{"city": "MANNFORD", "loc": [-96.357552, 36.092688], "pop": 6963, "state": "OK", "_id": "74044"} -{"city": "MARAMEC", "loc": [-96.68441, 36.217465], "pop": 290, "state": "OK", "_id": "74045"} -{"city": "MOUNDS", "loc": [-96.068513, 35.912919], "pop": 5412, "state": "OK", "_id": "74047"} -{"city": "NOWATA", "loc": [-95.640421, 36.694565], "pop": 5688, "state": "OK", "_id": "74048"} -{"city": "OCHELATA", "loc": [-95.969143, 36.594655], "pop": 1381, "state": "OK", "_id": "74051"} -{"city": "OOLOGAH", "loc": [-95.72901, 36.443723], "pop": 3369, "state": "OK", "_id": "74053"} -{"city": "OSAGE", "loc": [-96.377411, 36.283156], "pop": 633, "state": "OK", "_id": "74054"} -{"city": "OWASSO", "loc": [-95.822151, 36.286258], "pop": 20281, "state": "OK", "_id": "74055"} -{"city": "PAWHUSKA", "loc": [-96.31209, 36.690424], "pop": 6039, "state": "OK", "_id": "74056"} -{"city": "PAWNEE", "loc": [-96.79227, 36.336197], "pop": 4209, "state": "OK", "_id": "74058"} -{"city": "PERKINS", "loc": [-97.044059, 35.97684], "pop": 3246, "state": "OK", "_id": "74059"} -{"city": "PRUE", "loc": [-96.270074, 36.250125], "pop": 712, "state": "OK", "_id": "74060"} -{"city": "RAMONA", "loc": [-95.89622, 36.575245], "pop": 1628, "state": "OK", "_id": "74061"} -{"city": "RIPLEY", "loc": [-96.89667, 35.998491], "pop": 839, "state": "OK", "_id": "74062"} -{"city": "SAND SPRINGS", "loc": [-96.142601, 36.13414], "pop": 25745, "state": "OK", "_id": "74063"} -{"city": "SAPULPA", "loc": [-96.11061, 36.00297], "pop": 24409, "state": "OK", "_id": "74066"} -{"city": "SKIATOOK", "loc": [-96.012325, 36.372495], "pop": 8619, "state": "OK", "_id": "74070"} -{"city": "S COFFEYVILLE", "loc": [-95.606509, 36.983684], "pop": 1397, "state": "OK", "_id": "74072"} -{"city": "SPERRY", "loc": [-95.980368, 36.29547], "pop": 1979, "state": "OK", "_id": "74073"} -{"city": "STILLWATER", "loc": [-97.060868, 36.104349], "pop": 25259, "state": "OK", "_id": "74074"} -{"city": "STILLWATER", "loc": [-97.063035, 36.139584], "pop": 18955, "state": "OK", "_id": "74075"} -{"city": "KENDRICK", "loc": [-96.684377, 35.782389], "pop": 4898, "state": "OK", "_id": "74079"} -{"city": "TALALA", "loc": [-95.714203, 36.542915], "pop": 883, "state": "OK", "_id": "74080"} -{"city": "TERLTON", "loc": [-96.487569, 36.188856], "pop": 915, "state": "OK", "_id": "74081"} -{"city": "WANN", "loc": [-95.776752, 36.940211], "pop": 977, "state": "OK", "_id": "74083"} -{"city": "WYNONA", "loc": [-96.368891, 36.50847], "pop": 1815, "state": "OK", "_id": "74084"} -{"city": "YALE", "loc": [-96.702209, 36.110065], "pop": 2296, "state": "OK", "_id": "74085"} -{"city": "TULSA", "loc": [-95.995426, 36.153858], "pop": 1105, "state": "OK", "_id": "74103"} -{"city": "TULSA", "loc": [-95.952566, 36.146446], "pop": 13247, "state": "OK", "_id": "74104"} -{"city": "TULSA", "loc": [-95.965544, 36.094808], "pop": 29466, "state": "OK", "_id": "74105"} -{"city": "TULSA", "loc": [-95.985956, 36.188296], "pop": 18108, "state": "OK", "_id": "74106"} -{"city": "TULSA", "loc": [-96.024448, 36.104199], "pop": 18899, "state": "OK", "_id": "74107"} -{"city": "TULSA", "loc": [-95.792311, 36.149893], "pop": 8018, "state": "OK", "_id": "74108"} -{"city": "TULSA", "loc": [-95.952492, 36.180296], "pop": 14224, "state": "OK", "_id": "74110"} -{"city": "TULSA", "loc": [-95.907036, 36.147039], "pop": 21754, "state": "OK", "_id": "74112"} -{"city": "TULSA", "loc": [-95.940796, 36.126152], "pop": 17190, "state": "OK", "_id": "74114"} -{"city": "TULSA", "loc": [-95.911183, 36.175408], "pop": 22100, "state": "OK", "_id": "74115"} -{"city": "TULSA", "loc": [-95.847695, 36.174994], "pop": 2067, "state": "OK", "_id": "74116"} -{"city": "TULSA", "loc": [-95.910768, 36.27949], "pop": 824, "state": "OK", "_id": "74117"} -{"city": "TULSA", "loc": [-95.990194, 36.140688], "pop": 4059, "state": "OK", "_id": "74119"} -{"city": "TULSA", "loc": [-95.973373, 36.144228], "pop": 5612, "state": "OK", "_id": "74120"} -{"city": "TULSA", "loc": [-95.993113, 36.238288], "pop": 13636, "state": "OK", "_id": "74126"} -{"city": "TULSA", "loc": [-96.03107, 36.157636], "pop": 17336, "state": "OK", "_id": "74127"} -{"city": "TULSA", "loc": [-95.851377, 36.145927], "pop": 12260, "state": "OK", "_id": "74128"} -{"city": "TULSA", "loc": [-95.865354, 36.125928], "pop": 17278, "state": "OK", "_id": "74129"} -{"city": "TULSA", "loc": [-95.959649, 36.239481], "pop": 2553, "state": "OK", "_id": "74130"} -{"city": "TULSA", "loc": [-96.060229, 36.05566], "pop": 4057, "state": "OK", "_id": "74131"} -{"city": "TULSA", "loc": [-96.025104, 36.063971], "pop": 5290, "state": "OK", "_id": "74132"} -{"city": "TULSA", "loc": [-95.884062, 36.046717], "pop": 30499, "state": "OK", "_id": "74133"} -{"city": "TULSA", "loc": [-95.822472, 36.116223], "pop": 12607, "state": "OK", "_id": "74134"} -{"city": "TULSA", "loc": [-95.922805, 36.097603], "pop": 21783, "state": "OK", "_id": "74135"} -{"city": "TULSA", "loc": [-95.945178, 36.060548], "pop": 29192, "state": "OK", "_id": "74136"} -{"city": "TULSA", "loc": [-95.930597, 36.028426], "pop": 15434, "state": "OK", "_id": "74137"} -{"city": "TULSA", "loc": [-95.885576, 36.093433], "pop": 17509, "state": "OK", "_id": "74145"} -{"city": "TULSA", "loc": [-95.85061, 36.109293], "pop": 12965, "state": "OK", "_id": "74146"} -{"city": "VINITA", "loc": [-95.138164, 36.633353], "pop": 8987, "state": "OK", "_id": "74301"} -{"city": "ADAIR", "loc": [-95.273199, 36.411453], "pop": 4938, "state": "OK", "_id": "74330"} -{"city": "BERNICE", "loc": [-94.907491, 36.630072], "pop": 5352, "state": "OK", "_id": "74331"} -{"city": "BIG CABIN", "loc": [-95.274736, 36.607931], "pop": 2634, "state": "OK", "_id": "74332"} -{"city": "BLUEJACKET", "loc": [-95.101844, 36.797453], "pop": 1005, "state": "OK", "_id": "74333"} -{"city": "CHOUTEAU", "loc": [-95.341574, 36.166924], "pop": 3763, "state": "OK", "_id": "74337"} -{"city": "COLCORD", "loc": [-94.654675, 36.233349], "pop": 4202, "state": "OK", "_id": "74338"} -{"city": "COMMERCE", "loc": [-94.872983, 36.933063], "pop": 2624, "state": "OK", "_id": "74339"} -{"city": "EUCHA", "loc": [-94.923003, 36.398709], "pop": 2935, "state": "OK", "_id": "74342"} -{"city": "FAIRLAND", "loc": [-94.827934, 36.74178], "pop": 2003, "state": "OK", "_id": "74343"} -{"city": "GROVE", "loc": [-94.756536, 36.592869], "pop": 8408, "state": "OK", "_id": "74344"} -{"city": "JAY", "loc": [-94.776309, 36.436343], "pop": 5073, "state": "OK", "_id": "74346"} -{"city": "KANSAS", "loc": [-94.811416, 36.216114], "pop": 1594, "state": "OK", "_id": "74347"} -{"city": "LOCUST GROVE", "loc": [-95.168854, 36.181902], "pop": 5856, "state": "OK", "_id": "74352"} -{"city": "MIAMI", "loc": [-94.87186, 36.876377], "pop": 16679, "state": "OK", "_id": "74354"} -{"city": "OAKS", "loc": [-94.850206, 36.168663], "pop": 327, "state": "OK", "_id": "74359"} -{"city": "PICHER", "loc": [-94.817301, 36.979961], "pop": 3019, "state": "OK", "_id": "74360"} -{"city": "PRYOR", "loc": [-95.312942, 36.292112], "pop": 11669, "state": "OK", "_id": "74361"} -{"city": "QUAPAW", "loc": [-94.742983, 36.928248], "pop": 2465, "state": "OK", "_id": "74363"} -{"city": "LEACH", "loc": [-94.993362, 36.211263], "pop": 1222, "state": "OK", "_id": "74364"} -{"city": "SALINA", "loc": [-95.115772, 36.311597], "pop": 3432, "state": "OK", "_id": "74365"} -{"city": "SPAVINAW", "loc": [-95.028465, 36.415548], "pop": 1252, "state": "OK", "_id": "74366"} -{"city": "STRANG", "loc": [-95.070768, 36.463861], "pop": 2240, "state": "OK", "_id": "74367"} -{"city": "TWIN OAKS", "loc": [-94.854444, 36.191616], "pop": 435, "state": "OK", "_id": "74368"} -{"city": "WELCH", "loc": [-95.129455, 36.902022], "pop": 1746, "state": "OK", "_id": "74369"} -{"city": "WYANDOTTE", "loc": [-94.700239, 36.779616], "pop": 2500, "state": "OK", "_id": "74370"} -{"city": "MUSKOGEE", "loc": [-95.375491, 35.730661], "pop": 21813, "state": "OK", "_id": "74401"} -{"city": "MUSKOGEE", "loc": [-95.344907, 35.741057], "pop": 24787, "state": "OK", "_id": "74403"} -{"city": "BEGGS", "loc": [-96.026398, 35.789595], "pop": 4590, "state": "OK", "_id": "74421"} -{"city": "BOYNTON", "loc": [-95.660036, 35.657652], "pop": 762, "state": "OK", "_id": "74422"} -{"city": "BRAGGS", "loc": [-95.203326, 35.657395], "pop": 671, "state": "OK", "_id": "74423"} -{"city": "CANADIAN", "loc": [-95.653042, 35.159258], "pop": 1339, "state": "OK", "_id": "74425"} -{"city": "CHECOTAH", "loc": [-95.535038, 35.435786], "pop": 9683, "state": "OK", "_id": "74426"} -{"city": "COOKSON", "loc": [-94.913205, 35.711622], "pop": 1312, "state": "OK", "_id": "74427"} -{"city": "COUNCIL HILL", "loc": [-95.711326, 35.532175], "pop": 992, "state": "OK", "_id": "74428"} -{"city": "COWETA", "loc": [-95.652597, 35.957835], "pop": 9873, "state": "OK", "_id": "74429"} -{"city": "EUFAULA", "loc": [-95.647227, 35.29114], "pop": 5069, "state": "OK", "_id": "74432"} -{"city": "FORT GIBSON", "loc": [-95.229734, 35.794285], "pop": 5233, "state": "OK", "_id": "74434"} -{"city": "GORE", "loc": [-95.109474, 35.541772], "pop": 1167, "state": "OK", "_id": "74435"} -{"city": "HASKELL", "loc": [-95.683981, 35.810752], "pop": 3467, "state": "OK", "_id": "74436"} -{"city": "HOFFMAN", "loc": [-95.976187, 35.454476], "pop": 10729, "state": "OK", "_id": "74437"} -{"city": "HOYT", "loc": [-95.299382, 35.268466], "pop": 65, "state": "OK", "_id": "74440"} -{"city": "HULBERT", "loc": [-95.165095, 35.925415], "pop": 4597, "state": "OK", "_id": "74441"} -{"city": "INDIANOLA", "loc": [-95.784479, 35.093724], "pop": 2014, "state": "OK", "_id": "74442"} -{"city": "MORRIS", "loc": [-95.83186, 35.661877], "pop": 3028, "state": "OK", "_id": "74445"} -{"city": "OKMULGEE", "loc": [-95.96967, 35.628805], "pop": 18109, "state": "OK", "_id": "74447"} -{"city": "OKTAHA", "loc": [-95.485591, 35.625014], "pop": 2624, "state": "OK", "_id": "74450"} -{"city": "PARK HILL", "loc": [-94.982173, 35.797686], "pop": 4003, "state": "OK", "_id": "74451"} -{"city": "PEGGS", "loc": [-94.991928, 36.059061], "pop": 3896, "state": "OK", "_id": "74452"} -{"city": "PORTER", "loc": [-95.508193, 35.85674], "pop": 2705, "state": "OK", "_id": "74454"} -{"city": "PORUM", "loc": [-95.260661, 35.363059], "pop": 2976, "state": "OK", "_id": "74455"} -{"city": "PROCTOR", "loc": [-94.744159, 35.967111], "pop": 443, "state": "OK", "_id": "74457"} -{"city": "STIDHAM", "loc": [-95.705575, 35.381389], "pop": 286, "state": "OK", "_id": "74461"} -{"city": "STIGLER", "loc": [-95.10707, 35.268561], "pop": 4932, "state": "OK", "_id": "74462"} -{"city": "TAFT", "loc": [-95.54936, 35.758059], "pop": 1822, "state": "OK", "_id": "74463"} -{"city": "TAHLEQUAH", "loc": [-94.97873, 35.909385], "pop": 17092, "state": "OK", "_id": "74464"} -{"city": "WAGONER", "loc": [-95.353956, 35.954864], "pop": 13813, "state": "OK", "_id": "74467"} -{"city": "WARNER", "loc": [-95.306434, 35.494546], "pop": 2501, "state": "OK", "_id": "74469"} -{"city": "WEBBERS FALLS", "loc": [-95.165881, 35.513787], "pop": 1530, "state": "OK", "_id": "74470"} -{"city": "WELLING", "loc": [-94.865326, 35.881917], "pop": 3149, "state": "OK", "_id": "74471"} -{"city": "WHITEFIELD", "loc": [-95.237493, 35.25114], "pop": 283, "state": "OK", "_id": "74472"} -{"city": "MCALESTER", "loc": [-95.759168, 34.926233], "pop": 23786, "state": "OK", "_id": "74501"} -{"city": "ANTLERS", "loc": [-95.625412, 34.234923], "pop": 5432, "state": "OK", "_id": "74523"} -{"city": "ATOKA", "loc": [-96.141827, 34.34451], "pop": 7450, "state": "OK", "_id": "74525"} -{"city": "BLANCO", "loc": [-95.792069, 34.762151], "pop": 289, "state": "OK", "_id": "74528"} -{"city": "CALVIN", "loc": [-96.270972, 34.877987], "pop": 779, "state": "OK", "_id": "74531"} -{"city": "CANEY", "loc": [-96.258364, 34.222112], "pop": 1443, "state": "OK", "_id": "74533"} -{"city": "CENTRAHOMA", "loc": [-96.338585, 34.606642], "pop": 435, "state": "OK", "_id": "74534"} -{"city": "CLAYTON", "loc": [-95.379981, 34.590591], "pop": 1635, "state": "OK", "_id": "74536"} -{"city": "COALGATE", "loc": [-96.216726, 34.534408], "pop": 3908, "state": "OK", "_id": "74538"} -{"city": "DAISY", "loc": [-95.708773, 34.538357], "pop": 147, "state": "OK", "_id": "74540"} -{"city": "FARRIS", "loc": [-95.841368, 34.255833], "pop": 1226, "state": "OK", "_id": "74542"} -{"city": "FINLEY", "loc": [-95.538519, 34.340654], "pop": 149, "state": "OK", "_id": "74543"} -{"city": "HARTSHORNE", "loc": [-95.573972, 34.84517], "pop": 5133, "state": "OK", "_id": "74547"} -{"city": "HAYWOOD", "loc": [-95.967154, 34.951102], "pop": 2185, "state": "OK", "_id": "74548"} -{"city": "HONOBIA", "loc": [-94.991557, 34.592604], "pop": 125, "state": "OK", "_id": "74549"} -{"city": "KINTA", "loc": [-95.317545, 35.197065], "pop": 2676, "state": "OK", "_id": "74552"} -{"city": "KIOWA", "loc": [-95.932836, 34.727755], "pop": 1390, "state": "OK", "_id": "74553"} -{"city": "LANE", "loc": [-95.968515, 34.269122], "pop": 1001, "state": "OK", "_id": "74555"} -{"city": "MOYERS", "loc": [-95.663881, 34.338648], "pop": 280, "state": "OK", "_id": "74557"} -{"city": "NASHOBA", "loc": [-95.207467, 34.507204], "pop": 534, "state": "OK", "_id": "74558"} -{"city": "PITTSBURG", "loc": [-95.843905, 34.69748], "pop": 536, "state": "OK", "_id": "74560"} -{"city": "QUINTON", "loc": [-95.467085, 35.154666], "pop": 3909, "state": "OK", "_id": "74561"} -{"city": "RATTAN", "loc": [-95.344235, 34.245417], "pop": 1648, "state": "OK", "_id": "74562"} -{"city": "RED OAK", "loc": [-95.090432, 34.942174], "pop": 2167, "state": "OK", "_id": "74563"} -{"city": "SNOW", "loc": [-95.427833, 34.377963], "pop": 317, "state": "OK", "_id": "74567"} -{"city": "STRINGTOWN", "loc": [-96.000245, 34.467597], "pop": 755, "state": "OK", "_id": "74569"} -{"city": "STUART", "loc": [-96.138105, 34.882581], "pop": 751, "state": "OK", "_id": "74570"} -{"city": "TALIHINA", "loc": [-94.997813, 34.73812], "pop": 2930, "state": "OK", "_id": "74571"} -{"city": "TUPELO", "loc": [-96.427365, 34.559809], "pop": 1388, "state": "OK", "_id": "74572"} -{"city": "TUSKAHOMA", "loc": [-95.220798, 34.734778], "pop": 2491, "state": "OK", "_id": "74574"} -{"city": "WARDVILLE", "loc": [-96.020958, 34.568791], "pop": 756, "state": "OK", "_id": "74576"} -{"city": "WHITESBORO", "loc": [-94.869977, 34.684311], "pop": 219, "state": "OK", "_id": "74577"} -{"city": "WILBURTON", "loc": [-95.338896, 34.912805], "pop": 5866, "state": "OK", "_id": "74578"} -{"city": "PONCA CITY", "loc": [-97.078409, 36.703104], "pop": 24347, "state": "OK", "_id": "74601"} -{"city": "PONCA CITY", "loc": [-97.045443, 36.729916], "pop": 9327, "state": "OK", "_id": "74604"} -{"city": "BILLINGS", "loc": [-97.418889, 36.524609], "pop": 855, "state": "OK", "_id": "74630"} -{"city": "BLACKWELL", "loc": [-97.286695, 36.800574], "pop": 8480, "state": "OK", "_id": "74631"} -{"city": "BRAMAN", "loc": [-97.308231, 36.933054], "pop": 746, "state": "OK", "_id": "74632"} -{"city": "BURBANK", "loc": [-96.786873, 36.696583], "pop": 641, "state": "OK", "_id": "74633"} -{"city": "DEER CREEK", "loc": [-97.513581, 36.80482], "pop": 260, "state": "OK", "_id": "74636"} -{"city": "FAIRFAX", "loc": [-96.699691, 36.557687], "pop": 2307, "state": "OK", "_id": "74637"} -{"city": "HUNTER", "loc": [-97.642507, 36.560342], "pop": 533, "state": "OK", "_id": "74640"} -{"city": "KAW CITY", "loc": [-96.898862, 36.786663], "pop": 876, "state": "OK", "_id": "74641"} -{"city": "LAMONT", "loc": [-97.560093, 36.693884], "pop": 650, "state": "OK", "_id": "74643"} -{"city": "MARLAND", "loc": [-97.097592, 36.559067], "pop": 639, "state": "OK", "_id": "74644"} -{"city": "NARDIN", "loc": [-97.432458, 36.815517], "pop": 262, "state": "OK", "_id": "74646"} -{"city": "PECKHAM", "loc": [-97.05133, 36.887425], "pop": 3274, "state": "OK", "_id": "74647"} -{"city": "RALSTON", "loc": [-96.775481, 36.499228], "pop": 756, "state": "OK", "_id": "74650"} -{"city": "RED ROCK", "loc": [-97.164021, 36.474754], "pop": 775, "state": "OK", "_id": "74651"} -{"city": "FORAKER", "loc": [-96.674962, 36.814915], "pop": 1143, "state": "OK", "_id": "74652"} -{"city": "TONKAWA", "loc": [-97.30634, 36.68062], "pop": 4002, "state": "OK", "_id": "74653"} -{"city": "DURANT", "loc": [-96.384705, 34.00609], "pop": 17179, "state": "OK", "_id": "74701"} -{"city": "BENNINGTON", "loc": [-96.018774, 33.977085], "pop": 1411, "state": "OK", "_id": "74723"} -{"city": "BETHEL", "loc": [-94.878126, 34.358443], "pop": 1468, "state": "OK", "_id": "74724"} -{"city": "BOKCHITO", "loc": [-96.162097, 33.985913], "pop": 1812, "state": "OK", "_id": "74726"} -{"city": "BOSWELL", "loc": [-95.840313, 34.024475], "pop": 2162, "state": "OK", "_id": "74727"} -{"city": "BROKEN BOW", "loc": [-94.762296, 34.026991], "pop": 10436, "state": "OK", "_id": "74728"} -{"city": "CADDO", "loc": [-96.260017, 34.115688], "pop": 1474, "state": "OK", "_id": "74729"} -{"city": "CALERA", "loc": [-96.410205, 33.928872], "pop": 2448, "state": "OK", "_id": "74730"} -{"city": "CARTWRIGHT", "loc": [-96.551909, 33.884552], "pop": 1796, "state": "OK", "_id": "74731"} -{"city": "COLBERT", "loc": [-96.495345, 33.857516], "pop": 2267, "state": "OK", "_id": "74733"} -{"city": "EAGLETOWN", "loc": [-94.559557, 34.036358], "pop": 1237, "state": "OK", "_id": "74734"} -{"city": "FORT TOWSON", "loc": [-95.253015, 34.051968], "pop": 2249, "state": "OK", "_id": "74735"} -{"city": "GARVIN", "loc": [-94.932308, 33.920512], "pop": 804, "state": "OK", "_id": "74736"} -{"city": "GRANT", "loc": [-95.489324, 33.930239], "pop": 740, "state": "OK", "_id": "74738"} -{"city": "TOM", "loc": [-94.581775, 33.787507], "pop": 1861, "state": "OK", "_id": "74740"} -{"city": "HENDRIX", "loc": [-96.358134, 33.801582], "pop": 1812, "state": "OK", "_id": "74741"} -{"city": "HUGO", "loc": [-95.513876, 34.011339], "pop": 8804, "state": "OK", "_id": "74743"} -{"city": "IDABEL", "loc": [-94.802012, 33.88512], "pop": 10294, "state": "OK", "_id": "74745"} -{"city": "KENEFIC", "loc": [-96.388663, 34.131377], "pop": 370, "state": "OK", "_id": "74748"} -{"city": "RINGOLD", "loc": [-95.070387, 34.179892], "pop": 97, "state": "OK", "_id": "74754"} -{"city": "RUFE", "loc": [-95.136011, 34.161684], "pop": 244, "state": "OK", "_id": "74755"} -{"city": "SAWYER", "loc": [-95.355833, 34.027726], "pop": 421, "state": "OK", "_id": "74756"} -{"city": "SOPER", "loc": [-95.691566, 34.03659], "pop": 935, "state": "OK", "_id": "74759"} -{"city": "SPENCERVILLE", "loc": [-95.383468, 34.13263], "pop": 246, "state": "OK", "_id": "74760"} -{"city": "VALLIANT", "loc": [-95.068625, 34.009056], "pop": 3875, "state": "OK", "_id": "74764"} -{"city": "WRIGHT CITY", "loc": [-94.992889, 34.079922], "pop": 1879, "state": "OK", "_id": "74766"} -{"city": "SHAWNEE", "loc": [-96.931321, 35.34907], "pop": 40076, "state": "OK", "_id": "74801"} -{"city": "ADA", "loc": [-96.69236, 34.780044], "pop": 24967, "state": "OK", "_id": "74820"} -{"city": "AGRA", "loc": [-96.87794, 35.882548], "pop": 1327, "state": "OK", "_id": "74824"} -{"city": "ALLEN", "loc": [-96.558462, 34.850176], "pop": 4803, "state": "OK", "_id": "74825"} -{"city": "ASHER", "loc": [-96.876326, 34.984862], "pop": 970, "state": "OK", "_id": "74826"} -{"city": "ATWOOD", "loc": [-96.357703, 34.919194], "pop": 723, "state": "OK", "_id": "74827"} -{"city": "BOLEY", "loc": [-96.470386, 35.491328], "pop": 1437, "state": "OK", "_id": "74829"} -{"city": "BYARS", "loc": [-97.099723, 34.890397], "pop": 1133, "state": "OK", "_id": "74831"} -{"city": "CARNEY", "loc": [-97.015942, 35.805381], "pop": 663, "state": "OK", "_id": "74832"} -{"city": "CASTLE", "loc": [-96.379133, 35.473235], "pop": 168, "state": "OK", "_id": "74833"} -{"city": "CHANDLER", "loc": [-96.858266, 35.704253], "pop": 6506, "state": "OK", "_id": "74834"} -{"city": "CLEARVIEW", "loc": [-96.171404, 35.392585], "pop": 227, "state": "OK", "_id": "74835"} -{"city": "DUSTIN", "loc": [-96.057271, 35.251937], "pop": 893, "state": "OK", "_id": "74839"} -{"city": "EARLSBORO", "loc": [-96.804093, 35.262792], "pop": 572, "state": "OK", "_id": "74840"} -{"city": "FITTSTOWN", "loc": [-96.649458, 34.630134], "pop": 759, "state": "OK", "_id": "74842"} -{"city": "FITZHUGH", "loc": [-96.774487, 34.661545], "pop": 201, "state": "OK", "_id": "74843"} -{"city": "VERNON", "loc": [-95.895795, 35.208526], "pop": 638, "state": "OK", "_id": "74845"} -{"city": "HOLDENVILLE", "loc": [-96.376968, 35.083935], "pop": 7471, "state": "OK", "_id": "74848"} -{"city": "KONAWA", "loc": [-96.734279, 34.970824], "pop": 2800, "state": "OK", "_id": "74849"} -{"city": "LAMAR", "loc": [-96.114226, 35.083557], "pop": 225, "state": "OK", "_id": "74850"} -{"city": "MC LOUD", "loc": [-97.105178, 35.41907], "pop": 3334, "state": "OK", "_id": "74851"} -{"city": "MACOMB", "loc": [-97.033964, 35.12052], "pop": 1005, "state": "OK", "_id": "74852"} -{"city": "MAUD", "loc": [-96.762686, 35.129224], "pop": 4144, "state": "OK", "_id": "74854"} -{"city": "MEEKER", "loc": [-96.998052, 35.521111], "pop": 6180, "state": "OK", "_id": "74855"} -{"city": "MILL CREEK", "loc": [-96.788419, 34.309754], "pop": 1510, "state": "OK", "_id": "74856"} -{"city": "NEWALLA", "loc": [-97.197123, 35.373415], "pop": 7768, "state": "OK", "_id": "74857"} -{"city": "BEARDEN", "loc": [-96.306788, 35.447516], "pop": 5792, "state": "OK", "_id": "74859"} -{"city": "PADEN", "loc": [-96.571921, 35.518171], "pop": 1508, "state": "OK", "_id": "74860"} -{"city": "PRAGUE", "loc": [-96.700879, 35.510482], "pop": 4504, "state": "OK", "_id": "74864"} -{"city": "ROFF", "loc": [-96.842313, 34.615294], "pop": 1316, "state": "OK", "_id": "74865"} -{"city": "SASAKWA", "loc": [-96.538706, 34.950982], "pop": 854, "state": "OK", "_id": "74867"} -{"city": "SEMINOLE", "loc": [-96.668307, 35.252095], "pop": 11673, "state": "OK", "_id": "74868"} -{"city": "SPARKS", "loc": [-96.816269, 35.61378], "pop": 330, "state": "OK", "_id": "74869"} -{"city": "HARDEN CITY", "loc": [-96.54289, 34.666194], "pop": 2317, "state": "OK", "_id": "74871"} -{"city": "STRATFORD", "loc": [-96.976277, 34.771412], "pop": 2773, "state": "OK", "_id": "74872"} -{"city": "TECUMSEH", "loc": [-96.966713, 35.25023], "pop": 9203, "state": "OK", "_id": "74873"} -{"city": "TRYON", "loc": [-96.9984, 35.863151], "pop": 1356, "state": "OK", "_id": "74875"} -{"city": "WANETTE", "loc": [-97.0419, 34.998313], "pop": 1288, "state": "OK", "_id": "74878"} -{"city": "WELEETKA", "loc": [-96.113609, 35.341582], "pop": 2133, "state": "OK", "_id": "74880"} -{"city": "WELLSTON", "loc": [-97.059685, 35.675738], "pop": 3452, "state": "OK", "_id": "74881"} -{"city": "WELTY", "loc": [-96.42762, 35.610727], "pop": 286, "state": "OK", "_id": "74882"} -{"city": "WETUMKA", "loc": [-96.242082, 35.239552], "pop": 2181, "state": "OK", "_id": "74883"} -{"city": "NEW LIMA", "loc": [-96.503793, 35.182893], "pop": 8015, "state": "OK", "_id": "74884"} -{"city": "ARKOMA", "loc": [-94.440311, 35.343352], "pop": 2764, "state": "OK", "_id": "74901"} -{"city": "POCOLA", "loc": [-94.476029, 35.243603], "pop": 3575, "state": "OK", "_id": "74902"} -{"city": "BOKOSHE", "loc": [-94.722158, 35.160925], "pop": 3931, "state": "OK", "_id": "74930"} -{"city": "BUNCH", "loc": [-94.734154, 35.735691], "pop": 1656, "state": "OK", "_id": "74931"} -{"city": "CAMERON", "loc": [-94.506015, 35.149355], "pop": 935, "state": "OK", "_id": "74932"} -{"city": "HEAVENER", "loc": [-94.61849, 34.835267], "pop": 5628, "state": "OK", "_id": "74937"} -{"city": "HODGEN", "loc": [-94.63908, 34.753757], "pop": 756, "state": "OK", "_id": "74939"} -{"city": "HOWE", "loc": [-94.657072, 34.929936], "pop": 1352, "state": "OK", "_id": "74940"} -{"city": "KEOTA", "loc": [-94.902822, 35.264541], "pop": 1878, "state": "OK", "_id": "74941"} -{"city": "MCCURTAIN", "loc": [-95.012737, 35.140403], "pop": 1106, "state": "OK", "_id": "74944"} -{"city": "MULDROW", "loc": [-94.633216, 35.401985], "pop": 8185, "state": "OK", "_id": "74948"} -{"city": "MUSE", "loc": [-94.719007, 34.664124], "pop": 183, "state": "OK", "_id": "74949"} -{"city": "POTEAU", "loc": [-94.609603, 35.060561], "pop": 9466, "state": "OK", "_id": "74953"} -{"city": "ROLAND", "loc": [-94.529085, 35.453811], "pop": 7047, "state": "OK", "_id": "74954"} -{"city": "SALLISAW", "loc": [-94.778998, 35.485191], "pop": 11399, "state": "OK", "_id": "74955"} -{"city": "SHADY POINT", "loc": [-94.666534, 35.129333], "pop": 937, "state": "OK", "_id": "74956"} -{"city": "OCTAVIA", "loc": [-94.619117, 34.479056], "pop": 442, "state": "OK", "_id": "74957"} -{"city": "SPIRO", "loc": [-94.626546, 35.249219], "pop": 7368, "state": "OK", "_id": "74959"} -{"city": "STILWELL", "loc": [-94.631322, 35.810703], "pop": 10267, "state": "OK", "_id": "74960"} -{"city": "VIAN", "loc": [-94.988756, 35.540383], "pop": 6030, "state": "OK", "_id": "74962"} -{"city": "WATSON", "loc": [-94.556217, 34.419252], "pop": 797, "state": "OK", "_id": "74963"} -{"city": "WATTS", "loc": [-94.634472, 36.115216], "pop": 2156, "state": "OK", "_id": "74964"} -{"city": "WESTVILLE", "loc": [-94.592627, 35.991226], "pop": 3899, "state": "OK", "_id": "74965"} -{"city": "WISTER", "loc": [-94.783205, 34.955593], "pop": 3656, "state": "OK", "_id": "74966"} -{"city": "ANTELOPE", "loc": [-120.791384, 44.889196], "pop": 129, "state": "OR", "_id": "97001"} -{"city": "AURORA", "loc": [-122.803881, 45.228432], "pop": 4638, "state": "OR", "_id": "97002"} -{"city": "BEAVERCREEK", "loc": [-122.475122, 45.259723], "pop": 4253, "state": "OR", "_id": "97004"} -{"city": "BEAVERTON", "loc": [-122.805395, 45.475035], "pop": 46660, "state": "OR", "_id": "97005"} -{"city": "ALOHA", "loc": [-122.859209, 45.517675], "pop": 31650, "state": "OR", "_id": "97006"} -{"city": "ALOHA", "loc": [-122.859473, 45.472985], "pop": 35583, "state": "OR", "_id": "97007"} -{"city": "BORING", "loc": [-122.380713, 45.429704], "pop": 11406, "state": "OR", "_id": "97009"} -{"city": "BRIDAL VEIL", "loc": [-122.176587, 45.557904], "pop": 8, "state": "OR", "_id": "97010"} -{"city": "BRIGHTWOOD", "loc": [-122.003621, 45.365218], "pop": 788, "state": "OR", "_id": "97011"} -{"city": "CANBY", "loc": [-122.68322, 45.251425], "pop": 15801, "state": "OR", "_id": "97013"} -{"city": "BONNEVILLE", "loc": [-121.882411, 45.671447], "pop": 951, "state": "OR", "_id": "97014"} -{"city": "CLACKAMAS", "loc": [-122.52005, 45.414992], "pop": 12352, "state": "OR", "_id": "97015"} -{"city": "WESTPORT", "loc": [-123.2124, 46.09978], "pop": 6361, "state": "OR", "_id": "97016"} -{"city": "COLTON", "loc": [-122.424753, 45.157291], "pop": 4223, "state": "OR", "_id": "97017"} -{"city": "COLUMBIA CITY", "loc": [-122.812174, 45.892474], "pop": 1003, "state": "OR", "_id": "97018"} -{"city": "CORBETT", "loc": [-122.241746, 45.522116], "pop": 2355, "state": "OR", "_id": "97019"} -{"city": "FRIEND", "loc": [-121.146797, 45.429099], "pop": 1111, "state": "OR", "_id": "97021"} -{"city": "EAGLE CREEK", "loc": [-122.338053, 45.358205], "pop": 3285, "state": "OR", "_id": "97022"} -{"city": "ESTACADA", "loc": [-122.325858, 45.287177], "pop": 8703, "state": "OR", "_id": "97023"} -{"city": "GERVAIS", "loc": [-122.896185, 45.108645], "pop": 992, "state": "OR", "_id": "97026"} -{"city": "GLADSTONE", "loc": [-122.590197, 45.389882], "pop": 10148, "state": "OR", "_id": "97027"} -{"city": "TIMBERLINE LODGE", "loc": [-121.785426, 45.318366], "pop": 268, "state": "OR", "_id": "97028"} -{"city": "GRASS VALLEY", "loc": [-120.747795, 45.301333], "pop": 415, "state": "OR", "_id": "97029"} -{"city": "GRESHAM", "loc": [-122.420258, 45.515397], "pop": 35728, "state": "OR", "_id": "97030"} -{"city": "HOOD RIVER", "loc": [-121.539104, 45.671058], "pop": 13718, "state": "OR", "_id": "97031"} -{"city": "HUBBARD", "loc": [-122.754115, 45.160422], "pop": 6393, "state": "OR", "_id": "97032"} -{"city": "KENT", "loc": [-120.664895, 45.083789], "pop": 0, "state": "OR", "_id": "97033"} -{"city": "LAKE OSWEGO", "loc": [-122.684721, 45.409263], "pop": 18063, "state": "OR", "_id": "97034"} -{"city": "LAKE OSWEGO", "loc": [-122.722709, 45.414666], "pop": 19305, "state": "OR", "_id": "97035"} -{"city": "MAUPIN", "loc": [-121.228164, 45.074247], "pop": 1758, "state": "OR", "_id": "97037"} -{"city": "MOLALLA", "loc": [-122.575574, 45.122256], "pop": 7829, "state": "OR", "_id": "97038"} -{"city": "MORO", "loc": [-120.695666, 45.485332], "pop": 550, "state": "OR", "_id": "97039"} -{"city": "MOSIER", "loc": [-121.324532, 45.66167], "pop": 1573, "state": "OR", "_id": "97040"} -{"city": "MOUNT HOOD PARKD", "loc": [-121.588485, 45.521584], "pop": 2236, "state": "OR", "_id": "97041"} -{"city": "MULINO", "loc": [-122.535068, 45.212973], "pop": 2935, "state": "OR", "_id": "97042"} -{"city": "ODELL", "loc": [-121.440131, 45.623245], "pop": 0, "state": "OR", "_id": "97044"} -{"city": "OREGON CITY", "loc": [-122.569991, 45.337718], "pop": 36753, "state": "OR", "_id": "97045"} -{"city": "RAINIER", "loc": [-122.967067, 46.064552], "pop": 6357, "state": "OR", "_id": "97048"} -{"city": "ZIGZAG", "loc": [-121.953691, 45.355201], "pop": 1325, "state": "OR", "_id": "97049"} -{"city": "RUFUS", "loc": [-120.726777, 45.68515], "pop": 389, "state": "OR", "_id": "97050"} -{"city": "SAINT HELENS", "loc": [-122.828177, 45.860825], "pop": 10355, "state": "OR", "_id": "97051"} -{"city": "WARREN", "loc": [-122.863445, 45.826043], "pop": 2378, "state": "OR", "_id": "97053"} -{"city": "DEER ISLAND", "loc": [-122.898458, 45.935553], "pop": 1317, "state": "OR", "_id": "97054"} -{"city": "SANDY", "loc": [-122.223049, 45.378954], "pop": 12936, "state": "OR", "_id": "97055"} -{"city": "SCAPPOOSE", "loc": [-122.892771, 45.765451], "pop": 7812, "state": "OR", "_id": "97056"} -{"city": "SHANIKO", "loc": [-120.806953, 45.047231], "pop": 81, "state": "OR", "_id": "97057"} -{"city": "THE DALLES", "loc": [-121.190493, 45.599504], "pop": 16319, "state": "OR", "_id": "97058"} -{"city": "TROUTDALE", "loc": [-122.373866, 45.525398], "pop": 4849, "state": "OR", "_id": "97060"} -{"city": "TUALATIN", "loc": [-122.763132, 45.372688], "pop": 16371, "state": "OR", "_id": "97062"} -{"city": "WAMIC", "loc": [-121.296517, 45.231789], "pop": 712, "state": "OR", "_id": "97063"} -{"city": "VERNONIA", "loc": [-123.196662, 45.857298], "pop": 2931, "state": "OR", "_id": "97064"} -{"city": "WASCO", "loc": [-120.730356, 45.597447], "pop": 564, "state": "OR", "_id": "97065"} -{"city": "WELCHES", "loc": [-121.959826, 45.339862], "pop": 802, "state": "OR", "_id": "97067"} -{"city": "WEST LINN", "loc": [-122.647952, 45.366874], "pop": 19962, "state": "OR", "_id": "97068"} -{"city": "WILSONVILLE", "loc": [-122.769886, 45.298646], "pop": 9397, "state": "OR", "_id": "97070"} -{"city": "WOODBURN", "loc": [-122.858342, 45.144617], "pop": 17482, "state": "OR", "_id": "97071"} -{"city": "GRESHAM", "loc": [-122.415645, 45.481699], "pop": 25232, "state": "OR", "_id": "97080"} -{"city": "AMITY", "loc": [-123.174402, 45.115704], "pop": 2810, "state": "OR", "_id": "97101"} -{"city": "ASTORIA", "loc": [-123.79798, 46.155802], "pop": 19140, "state": "OR", "_id": "97103"} -{"city": "BANKS", "loc": [-123.120982, 45.653476], "pop": 3316, "state": "OR", "_id": "97106"} -{"city": "BAY CITY", "loc": [-123.876075, 45.519658], "pop": 1483, "state": "OR", "_id": "97107"} -{"city": "BEAVER", "loc": [-123.823417, 45.276746], "pop": 133, "state": "OR", "_id": "97108"} -{"city": "BUXTON", "loc": [-123.214555, 45.736983], "pop": 420, "state": "OR", "_id": "97109"} -{"city": "CARLTON", "loc": [-123.152346, 45.28593], "pop": 1555, "state": "OR", "_id": "97111"} -{"city": "CLOVERDALE", "loc": [-123.835628, 45.285821], "pop": 1269, "state": "OR", "_id": "97112"} -{"city": "CORNELIUS", "loc": [-123.041536, 45.529034], "pop": 13173, "state": "OR", "_id": "97113"} -{"city": "DAYTON", "loc": [-123.075332, 45.197722], "pop": 3640, "state": "OR", "_id": "97114"} -{"city": "DUNDEE", "loc": [-123.01523, 45.27761], "pop": 2382, "state": "OR", "_id": "97115"} -{"city": "GLENWOOD", "loc": [-123.115152, 45.532835], "pop": 17626, "state": "OR", "_id": "97116"} -{"city": "GALES CREEK", "loc": [-123.233967, 45.595747], "pop": 374, "state": "OR", "_id": "97117"} -{"city": "GASTON", "loc": [-123.16657, 45.442738], "pop": 3844, "state": "OR", "_id": "97119"} -{"city": "HAMMOND", "loc": [-123.952726, 46.198028], "pop": 627, "state": "OR", "_id": "97121"} -{"city": "HEBO", "loc": [-123.871433, 45.212016], "pop": 366, "state": "OR", "_id": "97122"} -{"city": "HILLSBORO", "loc": [-122.956998, 45.498401], "pop": 24201, "state": "OR", "_id": "97123"} -{"city": "HILLSBORO", "loc": [-122.963608, 45.53868], "pop": 20503, "state": "OR", "_id": "97124"} -{"city": "MANNING", "loc": [-123.186113, 45.652468], "pop": 549, "state": "OR", "_id": "97125"} -{"city": "LAFAYETTE", "loc": [-123.111362, 45.246638], "pop": 1315, "state": "OR", "_id": "97127"} -{"city": "MCMINNVILLE", "loc": [-123.204342, 45.209677], "pop": 21848, "state": "OR", "_id": "97128"} -{"city": "NEHALEM", "loc": [-123.904943, 45.72159], "pop": 2118, "state": "OR", "_id": "97131"} -{"city": "NEWBERG", "loc": [-122.968503, 45.309901], "pop": 18911, "state": "OR", "_id": "97132"} -{"city": "ROCKAWAY", "loc": [-123.907834, 45.608511], "pop": 3164, "state": "OR", "_id": "97136"} -{"city": "SAINT PAUL", "loc": [-122.96737, 45.195996], "pop": 1623, "state": "OR", "_id": "97137"} -{"city": "GEARHART", "loc": [-123.878837, 45.969506], "pop": 7239, "state": "OR", "_id": "97138"} -{"city": "SHERWOOD", "loc": [-122.856724, 45.351419], "pop": 7623, "state": "OR", "_id": "97140"} -{"city": "TILLAMOOK", "loc": [-123.818851, 45.449185], "pop": 11010, "state": "OR", "_id": "97141"} -{"city": "TIMBER", "loc": [-123.311852, 45.727033], "pop": 124, "state": "OR", "_id": "97144"} -{"city": "TOLOVANA PARK", "loc": [-123.95887, 45.886172], "pop": 1114, "state": "OR", "_id": "97145"} -{"city": "WARRENTON", "loc": [-123.925366, 46.145017], "pop": 4224, "state": "OR", "_id": "97146"} -{"city": "YAMHILL", "loc": [-123.203639, 45.335049], "pop": 4276, "state": "OR", "_id": "97148"} -{"city": "NESKOWIN", "loc": [-123.926344, 45.178165], "pop": 2027, "state": "OR", "_id": "97149"} -{"city": "PORTLAND", "loc": [-122.690258, 45.498819], "pop": 22763, "state": "OR", "_id": "97201"} -{"city": "PORTLAND", "loc": [-122.636534, 45.484007], "pop": 37147, "state": "OR", "_id": "97202"} -{"city": "PORTLAND", "loc": [-122.734699, 45.588872], "pop": 24789, "state": "OR", "_id": "97203"} -{"city": "PORTLAND", "loc": [-122.674498, 45.51807], "pop": 1094, "state": "OR", "_id": "97204"} -{"city": "PORTLAND", "loc": [-122.688846, 45.52072], "pop": 5804, "state": "OR", "_id": "97205"} -{"city": "PORTLAND", "loc": [-122.59727, 45.483995], "pop": 43134, "state": "OR", "_id": "97206"} -{"city": "PORTLAND", "loc": [-122.685447, 45.526962], "pop": 5810, "state": "OR", "_id": "97209"} -{"city": "PORTLAND", "loc": [-122.703348, 45.530318], "pop": 9284, "state": "OR", "_id": "97210"} -{"city": "PORTLAND", "loc": [-122.644815, 45.565259], "pop": 28736, "state": "OR", "_id": "97211"} -{"city": "PORTLAND", "loc": [-122.642319, 45.544127], "pop": 23898, "state": "OR", "_id": "97212"} -{"city": "PORTLAND", "loc": [-122.59867, 45.537292], "pop": 29400, "state": "OR", "_id": "97213"} -{"city": "PORTLAND", "loc": [-122.636397, 45.514207], "pop": 23413, "state": "OR", "_id": "97214"} -{"city": "PORTLAND", "loc": [-122.599001, 45.514282], "pop": 16563, "state": "OR", "_id": "97215"} -{"city": "PORTLAND", "loc": [-122.55688, 45.513746], "pop": 11436, "state": "OR", "_id": "97216"} -{"city": "PORTLAND", "loc": [-122.684196, 45.57424], "pop": 29086, "state": "OR", "_id": "97217"} -{"city": "PORTLAND", "loc": [-122.600131, 45.560032], "pop": 12305, "state": "OR", "_id": "97218"} -{"city": "PORTLAND", "loc": [-122.70738, 45.457956], "pop": 34992, "state": "OR", "_id": "97219"} -{"city": "PORTLAND", "loc": [-122.556586, 45.541109], "pop": 25679, "state": "OR", "_id": "97220"} -{"city": "PORTLAND", "loc": [-122.726723, 45.491829], "pop": 10834, "state": "OR", "_id": "97221"} -{"city": "MILWAUKIE", "loc": [-122.615092, 45.442919], "pop": 30905, "state": "OR", "_id": "97222"} -{"city": "GARDEN HOME", "loc": [-122.775974, 45.443343], "pop": 33529, "state": "OR", "_id": "97223"} -{"city": "TIGARD", "loc": [-122.788379, 45.407292], "pop": 17149, "state": "OR", "_id": "97224"} -{"city": "CEDAR HILLS", "loc": [-122.768344, 45.500449], "pop": 20934, "state": "OR", "_id": "97225"} -{"city": "PORTLAND", "loc": [-122.674257, 45.549564], "pop": 3171, "state": "OR", "_id": "97227"} -{"city": "PORTLAND", "loc": [-122.829924, 45.541087], "pop": 23490, "state": "OR", "_id": "97229"} -{"city": "ROCKWOOD CORNERS", "loc": [-122.500343, 45.535753], "pop": 30643, "state": "OR", "_id": "97230"} -{"city": "PORTLAND", "loc": [-122.838032, 45.640124], "pop": 3760, "state": "OR", "_id": "97231"} -{"city": "PORTLAND", "loc": [-122.63631, 45.528712], "pop": 10323, "state": "OR", "_id": "97232"} -{"city": "PORTLAND", "loc": [-122.498493, 45.514206], "pop": 27274, "state": "OR", "_id": "97233"} -{"city": "PORTLAND", "loc": [-122.509091, 45.488748], "pop": 24710, "state": "OR", "_id": "97236"} -{"city": "PORTLAND", "loc": [-122.559607, 45.476207], "pop": 29648, "state": "OR", "_id": "97266"} -{"city": "OAK GROVE", "loc": [-122.610631, 45.407494], "pop": 29597, "state": "OR", "_id": "97267"} -{"city": "SALEM", "loc": [-122.979692, 44.926039], "pop": 48007, "state": "OR", "_id": "97301"} -{"city": "SALEM", "loc": [-123.044514, 44.903899], "pop": 34814, "state": "OR", "_id": "97302"} -{"city": "KEIZER", "loc": [-123.019015, 44.985794], "pop": 35826, "state": "OR", "_id": "97303"} -{"city": "SALEM", "loc": [-123.075323, 44.958846], "pop": 16986, "state": "OR", "_id": "97304"} -{"city": "BROOKS", "loc": [-122.966892, 44.982502], "pop": 28239, "state": "OR", "_id": "97305"} -{"city": "SALEM", "loc": [-123.043789, 44.8685], "pop": 14770, "state": "OR", "_id": "97306"} -{"city": "ALBANY", "loc": [-123.094409, 44.627722], "pop": 42908, "state": "OR", "_id": "97321"} -{"city": "ALSEA", "loc": [-123.60892, 44.369068], "pop": 1008, "state": "OR", "_id": "97324"} -{"city": "WEST STAYTON", "loc": [-122.878575, 44.817817], "pop": 4185, "state": "OR", "_id": "97325"} -{"city": "BLODGETT", "loc": [-123.606715, 44.628141], "pop": 657, "state": "OR", "_id": "97326"} -{"city": "BROWNSVILLE", "loc": [-122.948491, 44.376974], "pop": 3249, "state": "OR", "_id": "97327"} -{"city": "CASCADIA", "loc": [-122.464214, 44.392239], "pop": 163, "state": "OR", "_id": "97329"} -{"city": "CORVALLIS", "loc": [-123.272171, 44.590411], "pop": 38801, "state": "OR", "_id": "97330"} -{"city": "CORVALLIS", "loc": [-123.277889, 44.563783], "pop": 2528, "state": "OR", "_id": "97331"} -{"city": "CORVALLIS", "loc": [-123.279908, 44.539281], "pop": 13009, "state": "OR", "_id": "97333"} -{"city": "DALLAS", "loc": [-123.319991, 44.922534], "pop": 14447, "state": "OR", "_id": "97338"} -{"city": "DEPOE BAY", "loc": [-124.03234, 44.851445], "pop": 3022, "state": "OR", "_id": "97341"} -{"city": "DETROIT", "loc": [-122.18447, 44.776619], "pop": 589, "state": "OR", "_id": "97342"} -{"city": "EDDYVILLE", "loc": [-123.753096, 44.637139], "pop": 599, "state": "OR", "_id": "97343"} -{"city": "FALLS CITY", "loc": [-123.446149, 44.870597], "pop": 957, "state": "OR", "_id": "97344"} -{"city": "FOSTER", "loc": [-122.544898, 44.383556], "pop": 192, "state": "OR", "_id": "97345"} -{"city": "GATES", "loc": [-122.399498, 44.752716], "pop": 697, "state": "OR", "_id": "97346"} -{"city": "GRAND RONDE", "loc": [-123.633518, 45.074973], "pop": 1151, "state": "OR", "_id": "97347"} -{"city": "HALSEY", "loc": [-123.125103, 44.386151], "pop": 1159, "state": "OR", "_id": "97348"} -{"city": "IDANHA", "loc": [-122.047574, 44.701484], "pop": 340, "state": "OR", "_id": "97350"} -{"city": "INDEPENDENCE", "loc": [-123.187913, 44.848098], "pop": 5955, "state": "OR", "_id": "97351"} -{"city": "JEFFERSON", "loc": [-123.00596, 44.749452], "pop": 5829, "state": "OR", "_id": "97352"} -{"city": "LEBANON", "loc": [-122.882064, 44.531558], "pop": 23147, "state": "OR", "_id": "97355"} -{"city": "LOGSDEN", "loc": [-123.773645, 44.747514], "pop": 234, "state": "OR", "_id": "97357"} -{"city": "LYONS", "loc": [-122.820083, 44.776792], "pop": 0, "state": "OR", "_id": "97358"} -{"city": "MILL CITY", "loc": [-122.476825, 44.751566], "pop": 2340, "state": "OR", "_id": "97360"} -{"city": "MONMOUTH", "loc": [-123.251233, 44.837706], "pop": 8071, "state": "OR", "_id": "97361"} -{"city": "MOUNT ANGEL", "loc": [-122.785611, 45.073727], "pop": 4747, "state": "OR", "_id": "97362"} -{"city": "NEOTSU", "loc": [-123.984337, 44.998801], "pop": 347, "state": "OR", "_id": "97364"} -{"city": "NEWPORT", "loc": [-124.050903, 44.648653], "pop": 9239, "state": "OR", "_id": "97365"} -{"city": "SOUTH BEACH", "loc": [-124.059968, 44.57122], "pop": 1423, "state": "OR", "_id": "97366"} -{"city": "LINCOLN CITY", "loc": [-123.99556, 44.968139], "pop": 6786, "state": "OR", "_id": "97367"} -{"city": "OTIS", "loc": [-123.933244, 45.013755], "pop": 2506, "state": "OR", "_id": "97368"} -{"city": "PHILOMATH", "loc": [-123.392271, 44.548817], "pop": 6879, "state": "OR", "_id": "97370"} -{"city": "RICKREALL", "loc": [-123.206424, 45.020032], "pop": 266, "state": "OR", "_id": "97371"} -{"city": "SCIO", "loc": [-122.768356, 44.716792], "pop": 7170, "state": "OR", "_id": "97374"} -{"city": "SCOTTS MILLS", "loc": [-122.665418, 45.022], "pop": 1326, "state": "OR", "_id": "97375"} -{"city": "SEAL ROCK", "loc": [-124.060708, 44.477749], "pop": 1845, "state": "OR", "_id": "97376"} -{"city": "SHEDD", "loc": [-123.106462, 44.452951], "pop": 1067, "state": "OR", "_id": "97377"} -{"city": "SHERIDAN", "loc": [-123.400335, 45.089703], "pop": 6591, "state": "OR", "_id": "97378"} -{"city": "SILETZ", "loc": [-123.906239, 44.731333], "pop": 1869, "state": "OR", "_id": "97380"} -{"city": "SILVERTON", "loc": [-122.762724, 44.991041], "pop": 10399, "state": "OR", "_id": "97381"} -{"city": "STAYTON", "loc": [-122.76241, 44.80211], "pop": 6808, "state": "OR", "_id": "97383"} -{"city": "SUBLIMITY", "loc": [-122.800718, 44.842523], "pop": 2881, "state": "OR", "_id": "97385"} -{"city": "SWEET HOME", "loc": [-122.728561, 44.398111], "pop": 11237, "state": "OR", "_id": "97386"} -{"city": "TANGENT", "loc": [-123.110815, 44.54973], "pop": 1257, "state": "OR", "_id": "97389"} -{"city": "TIDEWATER", "loc": [-123.914861, 44.405538], "pop": 523, "state": "OR", "_id": "97390"} -{"city": "TOLEDO", "loc": [-123.930119, 44.627082], "pop": 5609, "state": "OR", "_id": "97391"} -{"city": "TURNER", "loc": [-122.950117, 44.847607], "pop": 1243, "state": "OR", "_id": "97392"} -{"city": "WALDPORT", "loc": [-124.035053, 44.408497], "pop": 3302, "state": "OR", "_id": "97394"} -{"city": "WILLAMINA", "loc": [-123.504708, 45.082605], "pop": 2697, "state": "OR", "_id": "97396"} -{"city": "COBURG", "loc": [-123.078757, 44.073677], "pop": 36277, "state": "OR", "_id": "97401"} -{"city": "EUGENE", "loc": [-123.155525, 44.061243], "pop": 37830, "state": "OR", "_id": "97402"} -{"city": "EUGENE", "loc": [-123.061422, 44.038534], "pop": 11073, "state": "OR", "_id": "97403"} -{"city": "EUGENE", "loc": [-123.13336, 44.100536], "pop": 23778, "state": "OR", "_id": "97404"} -{"city": "EUGENE", "loc": [-123.099769, 44.018497], "pop": 40921, "state": "OR", "_id": "97405"} -{"city": "AGNESS", "loc": [-124.064769, 42.574788], "pop": 126, "state": "OR", "_id": "97406"} -{"city": "AZALEA", "loc": [-123.155017, 42.844992], "pop": 1111, "state": "OR", "_id": "97410"} -{"city": "BANDON", "loc": [-124.40367, 43.096806], "pop": 4852, "state": "OR", "_id": "97411"} -{"city": "BLACHLY", "loc": [-123.534816, 44.196597], "pop": 495, "state": "OR", "_id": "97412"} -{"city": "MC KENZIE BRIDGE", "loc": [-122.222951, 44.177809], "pop": 1139, "state": "OR", "_id": "97413"} -{"city": "BROADBENT", "loc": [-124.118924, 42.985048], "pop": 121, "state": "OR", "_id": "97414"} -{"city": "HARBOR", "loc": [-124.267811, 42.064004], "pop": 11673, "state": "OR", "_id": "97415"} -{"city": "CAMAS VALLEY", "loc": [-123.665465, 43.05566], "pop": 972, "state": "OR", "_id": "97416"} -{"city": "CANYONVILLE", "loc": [-123.278015, 42.930683], "pop": 1670, "state": "OR", "_id": "97417"} -{"city": "CHESHIRE", "loc": [-123.371516, 44.178206], "pop": 1050, "state": "OR", "_id": "97419"} -{"city": "CHARLESTON", "loc": [-124.233101, 43.362812], "pop": 24343, "state": "OR", "_id": "97420"} -{"city": "COQUILLE", "loc": [-124.201386, 43.188413], "pop": 8374, "state": "OR", "_id": "97423"} -{"city": "COTTAGE GROVE", "loc": [-123.05291, 43.783934], "pop": 15067, "state": "OR", "_id": "97424"} -{"city": "CRESWELL", "loc": [-123.02838, 43.90583], "pop": 6332, "state": "OR", "_id": "97426"} -{"city": "CULP CREEK", "loc": [-122.752417, 43.684534], "pop": 317, "state": "OR", "_id": "97427"} -{"city": "DAYS CREEK", "loc": [-123.14387, 42.981946], "pop": 392, "state": "OR", "_id": "97429"} -{"city": "GREENLEAF", "loc": [-123.688328, 44.145131], "pop": 492, "state": "OR", "_id": "97430"} -{"city": "DEXTER", "loc": [-122.842351, 43.921691], "pop": 2980, "state": "OR", "_id": "97431"} -{"city": "DORENA", "loc": [-122.885796, 43.758655], "pop": 225, "state": "OR", "_id": "97434"} -{"city": "DRAIN", "loc": [-123.292922, 43.687659], "pop": 2331, "state": "OR", "_id": "97435"} -{"city": "ELKTON", "loc": [-123.590014, 43.637761], "pop": 878, "state": "OR", "_id": "97436"} -{"city": "ELMIRA", "loc": [-123.367051, 44.08726], "pop": 2424, "state": "OR", "_id": "97437"} -{"city": "FALL CREEK", "loc": [-122.785904, 43.95616], "pop": 1581, "state": "OR", "_id": "97438"} -{"city": "FLORENCE", "loc": [-124.099303, 43.988099], "pop": 10063, "state": "OR", "_id": "97439"} -{"city": "GARDINER", "loc": [-124.143695, 43.785736], "pop": 14, "state": "OR", "_id": "97441"} -{"city": "GLENDALE", "loc": [-123.394302, 42.751751], "pop": 2050, "state": "OR", "_id": "97442"} -{"city": "GLIDE", "loc": [-122.963846, 43.277395], "pop": 2905, "state": "OR", "_id": "97443"} -{"city": "PISTOL RIVER", "loc": [-124.396072, 42.434818], "pop": 4760, "state": "OR", "_id": "97444"} -{"city": "HARRISBURG", "loc": [-123.143165, 44.271656], "pop": 3272, "state": "OR", "_id": "97446"} -{"city": "IDLEYLD PARK", "loc": [-122.901789, 43.371571], "pop": 222, "state": "OR", "_id": "97447"} -{"city": "JUNCTION CITY", "loc": [-123.230014, 44.198792], "pop": 10456, "state": "OR", "_id": "97448"} -{"city": "LAKESIDE", "loc": [-124.162364, 43.583306], "pop": 994, "state": "OR", "_id": "97449"} -{"city": "LANGLOIS", "loc": [-124.441322, 42.915386], "pop": 557, "state": "OR", "_id": "97450"} -{"city": "LORANE", "loc": [-123.247679, 43.829044], "pop": 499, "state": "OR", "_id": "97451"} -{"city": "LOWELL", "loc": [-122.780627, 43.920993], "pop": 792, "state": "OR", "_id": "97452"} -{"city": "MAPLETON", "loc": [-123.865735, 44.031189], "pop": 1104, "state": "OR", "_id": "97453"} -{"city": "MARCOLA", "loc": [-122.82464, 44.206439], "pop": 1442, "state": "OR", "_id": "97454"} -{"city": "PLEASANT HILL", "loc": [-122.928487, 43.945816], "pop": 2218, "state": "OR", "_id": "97455"} -{"city": "MONROE", "loc": [-123.32033, 44.32446], "pop": 2478, "state": "OR", "_id": "97456"} -{"city": "MYRTLE CREEK", "loc": [-123.285054, 43.016161], "pop": 9453, "state": "OR", "_id": "97457"} -{"city": "MYRTLE POINT", "loc": [-124.121327, 43.066694], "pop": 5246, "state": "OR", "_id": "97458"} -{"city": "NORTH BEND", "loc": [-124.213103, 43.432665], "pop": 15269, "state": "OR", "_id": "97459"} -{"city": "NOTI", "loc": [-123.456962, 44.119509], "pop": 1189, "state": "OR", "_id": "97461"} -{"city": "OAKLAND", "loc": [-123.355774, 43.452968], "pop": 3315, "state": "OR", "_id": "97462"} -{"city": "OAKRIDGE", "loc": [-122.457711, 43.749767], "pop": 4058, "state": "OR", "_id": "97463"} -{"city": "PORT ORFORD", "loc": [-124.491283, 42.757194], "pop": 1799, "state": "OR", "_id": "97465"} -{"city": "POWERS", "loc": [-124.066441, 42.891006], "pop": 953, "state": "OR", "_id": "97466"} -{"city": "WINCHESTER BAY", "loc": [-124.105476, 43.695701], "pop": 6723, "state": "OR", "_id": "97467"} -{"city": "REMOTE", "loc": [-123.89149, 43.007909], "pop": 121, "state": "OR", "_id": "97468"} -{"city": "RIDDLE", "loc": [-123.361247, 42.938867], "pop": 2698, "state": "OR", "_id": "97469"} -{"city": "ROSEBURG", "loc": [-123.366437, 43.222726], "pop": 41697, "state": "OR", "_id": "97470"} -{"city": "SCOTTSBURG", "loc": [-123.804065, 43.676481], "pop": 329, "state": "OR", "_id": "97473"} -{"city": "SIXES", "loc": [-124.44093, 42.824984], "pop": 412, "state": "OR", "_id": "97476"} -{"city": "SPRINGFIELD", "loc": [-123.015259, 44.06106], "pop": 32384, "state": "OR", "_id": "97477"} -{"city": "SPRINGFIELD", "loc": [-122.917108, 44.056056], "pop": 27521, "state": "OR", "_id": "97478"} -{"city": "SUTHERLIN", "loc": [-123.297425, 43.390404], "pop": 7304, "state": "OR", "_id": "97479"} -{"city": "SWISSHOME", "loc": [-123.827899, 44.089903], "pop": 641, "state": "OR", "_id": "97480"} -{"city": "TENMILE", "loc": [-123.530104, 43.137116], "pop": 1231, "state": "OR", "_id": "97481"} -{"city": "TILLER", "loc": [-122.908088, 42.985896], "pop": 534, "state": "OR", "_id": "97484"} -{"city": "UMPQUA", "loc": [-123.535771, 43.374537], "pop": 587, "state": "OR", "_id": "97486"} -{"city": "VENETA", "loc": [-123.35159, 44.038235], "pop": 6004, "state": "OR", "_id": "97487"} -{"city": "VIDA", "loc": [-122.504429, 44.130041], "pop": 1040, "state": "OR", "_id": "97488"} -{"city": "LEABURG", "loc": [-122.629064, 44.135163], "pop": 432, "state": "OR", "_id": "97489"} -{"city": "WALTON", "loc": [-123.589304, 44.028194], "pop": 298, "state": "OR", "_id": "97490"} -{"city": "WESTFIR", "loc": [-122.514095, 43.756636], "pop": 514, "state": "OR", "_id": "97492"} -{"city": "WESTLAKE", "loc": [-124.033364, 43.914017], "pop": 272, "state": "OR", "_id": "97493"} -{"city": "WINSTON", "loc": [-123.432481, 43.104855], "pop": 5971, "state": "OR", "_id": "97496"} -{"city": "SUNNY VALLEY", "loc": [-123.351538, 42.655128], "pop": 1253, "state": "OR", "_id": "97497"} -{"city": "YACHATS", "loc": [-124.086262, 44.325563], "pop": 1235, "state": "OR", "_id": "97498"} -{"city": "YONCALLA", "loc": [-123.292562, 43.60434], "pop": 2261, "state": "OR", "_id": "97499"} -{"city": "WEST MAIN", "loc": [-122.887011, 42.319293], "pop": 28708, "state": "OR", "_id": "97501"} -{"city": "CENTRAL POINT", "loc": [-122.922235, 42.389914], "pop": 17293, "state": "OR", "_id": "97502"} -{"city": "WHITE CITY", "loc": [-122.82962, 42.431919], "pop": 7024, "state": "OR", "_id": "97503"} -{"city": "MEDFORD", "loc": [-122.839801, 42.336251], "pop": 30933, "state": "OR", "_id": "97504"} -{"city": "ASHLAND", "loc": [-122.693033, 42.188509], "pop": 19579, "state": "OR", "_id": "97520"} -{"city": "BUTTE FALLS", "loc": [-122.563801, 42.549243], "pop": 1115, "state": "OR", "_id": "97522"} -{"city": "CAVE JUNCTION", "loc": [-123.627199, 42.134789], "pop": 5500, "state": "OR", "_id": "97523"} -{"city": "EAGLE POINT", "loc": [-122.808802, 42.493467], "pop": 7468, "state": "OR", "_id": "97524"} -{"city": "GOLD HILL", "loc": [-123.08543, 42.424436], "pop": 6650, "state": "OR", "_id": "97525"} -{"city": "GRANTS PASS", "loc": [-123.345727, 42.463758], "pop": 27145, "state": "OR", "_id": "97526"} -{"city": "GRANTS PASS", "loc": [-123.353799, 42.398913], "pop": 21774, "state": "OR", "_id": "97527"} -{"city": "APPLEGATE", "loc": [-123.028098, 42.254894], "pop": 5723, "state": "OR", "_id": "97530"} -{"city": "KERBY", "loc": [-123.657302, 42.209343], "pop": 78, "state": "OR", "_id": "97531"} -{"city": "MERLIN", "loc": [-123.439256, 42.529654], "pop": 1806, "state": "OR", "_id": "97532"} -{"city": "O BRIEN", "loc": [-123.720898, 42.068816], "pop": 247, "state": "OR", "_id": "97534"} -{"city": "PHOENIX", "loc": [-122.822694, 42.276555], "pop": 6054, "state": "OR", "_id": "97535"} -{"city": "PROSPECT", "loc": [-122.50898, 42.754394], "pop": 869, "state": "OR", "_id": "97536"} -{"city": "ROGUE RIVER", "loc": [-123.158726, 42.488889], "pop": 6131, "state": "OR", "_id": "97537"} -{"city": "SELMA", "loc": [-123.568394, 42.296358], "pop": 2065, "state": "OR", "_id": "97538"} -{"city": "SHADY COVE", "loc": [-122.812191, 42.607575], "pop": 2364, "state": "OR", "_id": "97539"} -{"city": "TALENT", "loc": [-122.78605, 42.236252], "pop": 5288, "state": "OR", "_id": "97540"} -{"city": "TRAIL", "loc": [-122.816029, 42.686358], "pop": 1028, "state": "OR", "_id": "97541"} -{"city": "WILDERVILLE", "loc": [-123.513861, 42.392483], "pop": 927, "state": "OR", "_id": "97543"} -{"city": "WILLIAMS", "loc": [-123.282877, 42.223049], "pop": 1854, "state": "OR", "_id": "97544"} -{"city": "ORETECH", "loc": [-121.786969, 42.229601], "pop": 18626, "state": "OR", "_id": "97601"} -{"city": "KLAMATH FALLS", "loc": [-121.724124, 42.191915], "pop": 24894, "state": "OR", "_id": "97603"} -{"city": "ADEL", "loc": [-119.883291, 42.148697], "pop": 190, "state": "OR", "_id": "97620"} -{"city": "BEATTY", "loc": [-121.219962, 42.436942], "pop": 95, "state": "OR", "_id": "97621"} -{"city": "BONANZA", "loc": [-121.333558, 42.246268], "pop": 2753, "state": "OR", "_id": "97623"} -{"city": "CHILOQUIN", "loc": [-121.744959, 42.546293], "pop": 3608, "state": "OR", "_id": "97624"} -{"city": "DAIRY", "loc": [-121.642453, 42.258141], "pop": 8, "state": "OR", "_id": "97625"} -{"city": "KENO", "loc": [-121.972427, 42.175472], "pop": 2696, "state": "OR", "_id": "97627"} -{"city": "LAKEVIEW", "loc": [-120.377533, 42.185443], "pop": 4890, "state": "OR", "_id": "97630"} -{"city": "MALIN", "loc": [-121.422121, 42.019502], "pop": 1456, "state": "OR", "_id": "97632"} -{"city": "MERRILL", "loc": [-121.598545, 42.029516], "pop": 1344, "state": "OR", "_id": "97633"} -{"city": "NEW PINE CREEK", "loc": [-120.28939, 42.027759], "pop": 198, "state": "OR", "_id": "97635"} -{"city": "PAISLEY", "loc": [-120.553208, 42.703089], "pop": 619, "state": "OR", "_id": "97636"} -{"city": "PLUSH", "loc": [-119.894722, 42.503493], "pop": 126, "state": "OR", "_id": "97637"} -{"city": "SILVER LAKE", "loc": [-120.780109, 43.258164], "pop": 1127, "state": "OR", "_id": "97638"} -{"city": "SUMMER LAKE", "loc": [-121.063254, 42.831302], "pop": 1, "state": "OR", "_id": "97640"} -{"city": "BEND", "loc": [-121.293632, 44.092788], "pop": 29432, "state": "OR", "_id": "97701"} -{"city": "BEND", "loc": [-121.298543, 44.022332], "pop": 20214, "state": "OR", "_id": "97702"} -{"city": "SUNRIVER", "loc": [-121.457871, 43.850336], "pop": 2881, "state": "OR", "_id": "97707"} -{"city": "ASHWOOD", "loc": [-120.719161, 44.719479], "pop": 114, "state": "OR", "_id": "97711"} -{"city": "BROTHERS", "loc": [-120.477035, 43.778877], "pop": 41, "state": "OR", "_id": "97712"} -{"city": "BURNS", "loc": [-119.050398, 43.51451], "pop": 5844, "state": "OR", "_id": "97720"} -{"city": "CAMP SHERMAN", "loc": [-121.639363, 44.454997], "pop": 378, "state": "OR", "_id": "97730"} -{"city": "DIAMOND LAKE", "loc": [-121.8447, 43.124669], "pop": 388, "state": "OR", "_id": "97731"} -{"city": "CRANE", "loc": [-118.4642, 43.426219], "pop": 280, "state": "OR", "_id": "97732"} -{"city": "CRESCENT", "loc": [-121.664813, 43.497226], "pop": 1848, "state": "OR", "_id": "97733"} -{"city": "CULVER", "loc": [-121.234353, 44.481796], "pop": 2517, "state": "OR", "_id": "97734"} -{"city": "FORT ROCK", "loc": [-121.08243, 43.44615], "pop": 35, "state": "OR", "_id": "97735"} -{"city": "GILCHRIST", "loc": [-121.886596, 43.30759], "pop": 148, "state": "OR", "_id": "97737"} -{"city": "LA PINE", "loc": [-121.519445, 43.709125], "pop": 4815, "state": "OR", "_id": "97739"} -{"city": "LAWEN", "loc": [-118.8418, 43.561816], "pop": 708, "state": "OR", "_id": "97740"} -{"city": "MADRAS", "loc": [-121.134924, 44.637579], "pop": 8294, "state": "OR", "_id": "97741"} -{"city": "MITCHELL", "loc": [-120.145579, 44.565734], "pop": 394, "state": "OR", "_id": "97750"} -{"city": "PAULINA", "loc": [-119.782768, 44.210243], "pop": 98, "state": "OR", "_id": "97751"} -{"city": "POST", "loc": [-120.299781, 44.079273], "pop": 252, "state": "OR", "_id": "97752"} -{"city": "POWELL BUTTE", "loc": [-121.011326, 44.241512], "pop": 1056, "state": "OR", "_id": "97753"} -{"city": "PRINEVILLE", "loc": [-120.833616, 44.30445], "pop": 12484, "state": "OR", "_id": "97754"} -{"city": "REDMOND", "loc": [-121.189604, 44.27669], "pop": 12161, "state": "OR", "_id": "97756"} -{"city": "RILEY", "loc": [-116.900621, 44.930529], "pop": 157, "state": "OR", "_id": "97758"} -{"city": "BLACK BUTTE RANC", "loc": [-121.524133, 44.307329], "pop": 3534, "state": "OR", "_id": "97759"} -{"city": "CROOKED RIVER RA", "loc": [-121.167565, 44.355505], "pop": 2101, "state": "OR", "_id": "97760"} -{"city": "WARM SPRINGS", "loc": [-121.290941, 44.746826], "pop": 2400, "state": "OR", "_id": "97761"} -{"city": "PENDLETON", "loc": [-118.783104, 45.660535], "pop": 20225, "state": "OR", "_id": "97801"} -{"city": "ADAMS", "loc": [-118.617582, 45.749678], "pop": 609, "state": "OR", "_id": "97810"} -{"city": "ARLINGTON", "loc": [-120.197148, 45.666417], "pop": 708, "state": "OR", "_id": "97812"} -{"city": "ATHENA", "loc": [-118.497147, 45.828893], "pop": 1334, "state": "OR", "_id": "97813"} -{"city": "MEDICAL SPRINGS", "loc": [-117.828631, 44.780102], "pop": 10024, "state": "OR", "_id": "97814"} -{"city": "BOARDMAN", "loc": [-119.72057, 45.827165], "pop": 2143, "state": "OR", "_id": "97818"} -{"city": "CANYON CITY", "loc": [-118.950155, 44.410005], "pop": 2806, "state": "OR", "_id": "97820"} -{"city": "CONDON", "loc": [-120.189834, 45.230587], "pop": 1009, "state": "OR", "_id": "97823"} -{"city": "COVE", "loc": [-117.814741, 45.319902], "pop": 1280, "state": "OR", "_id": "97824"} -{"city": "DAYVILLE", "loc": [-119.531178, 44.466307], "pop": 144, "state": "OR", "_id": "97825"} -{"city": "ECHO", "loc": [-119.194904, 45.74109], "pop": 648, "state": "OR", "_id": "97826"} -{"city": "ELGIN", "loc": [-117.91119, 45.594101], "pop": 2549, "state": "OR", "_id": "97827"} -{"city": "ENTERPRISE", "loc": [-117.288808, 45.437037], "pop": 3201, "state": "OR", "_id": "97828"} -{"city": "KINZUA", "loc": [-120.206699, 44.985289], "pop": 564, "state": "OR", "_id": "97830"} -{"city": "FOX", "loc": [-119.291646, 44.660681], "pop": 47, "state": "OR", "_id": "97831"} -{"city": "HAINES", "loc": [-117.97556, 44.877622], "pop": 1642, "state": "OR", "_id": "97833"} -{"city": "HALFWAY", "loc": [-117.113152, 44.895272], "pop": 1020, "state": "OR", "_id": "97834"} -{"city": "HELIX", "loc": [-118.722219, 45.866541], "pop": 383, "state": "OR", "_id": "97835"} -{"city": "HEPPNER", "loc": [-119.536897, 45.348577], "pop": 2127, "state": "OR", "_id": "97836"} -{"city": "HEREFORD", "loc": [-117.98896, 44.641048], "pop": 287, "state": "OR", "_id": "97837"} -{"city": "HERMISTON", "loc": [-119.284876, 45.844992], "pop": 16982, "state": "OR", "_id": "97838"} -{"city": "LEXINGTON", "loc": [-119.74636, 45.426362], "pop": 519, "state": "OR", "_id": "97839"} -{"city": "IMBLER", "loc": [-117.954377, 45.459908], "pop": 561, "state": "OR", "_id": "97841"} -{"city": "IMNAHA", "loc": [-116.825741, 45.513733], "pop": 255, "state": "OR", "_id": "97842"} -{"city": "IONE", "loc": [-119.769034, 45.54033], "pop": 535, "state": "OR", "_id": "97843"} -{"city": "IRRIGON", "loc": [-119.507016, 45.88768], "pop": 2301, "state": "OR", "_id": "97844"} -{"city": "JOHN DAY", "loc": [-119.105157, 44.409977], "pop": 1367, "state": "OR", "_id": "97845"} -{"city": "JOSEPH", "loc": [-117.212805, 45.349432], "pop": 1749, "state": "OR", "_id": "97846"} -{"city": "KIMBERLY", "loc": [-119.596497, 44.72263], "pop": 62, "state": "OR", "_id": "97848"} -{"city": "LA GRANDE", "loc": [-118.085228, 45.330435], "pop": 15401, "state": "OR", "_id": "97850"} -{"city": "LONG CREEK", "loc": [-119.09702, 44.755416], "pop": 442, "state": "OR", "_id": "97856"} -{"city": "LOSTINE", "loc": [-117.435909, 45.493861], "pop": 456, "state": "OR", "_id": "97857"} -{"city": "MILTON FREEWATER", "loc": [-118.391172, 45.948581], "pop": 9723, "state": "OR", "_id": "97862"} -{"city": "MONUMENT", "loc": [-119.430164, 44.818451], "pop": 410, "state": "OR", "_id": "97864"} -{"city": "MOUNT VERNON", "loc": [-119.112142, 44.417095], "pop": 538, "state": "OR", "_id": "97865"} -{"city": "NORTH POWDER", "loc": [-117.933666, 45.031667], "pop": 571, "state": "OR", "_id": "97867"} -{"city": "PILOT ROCK", "loc": [-118.848331, 45.422724], "pop": 2298, "state": "OR", "_id": "97868"} -{"city": "PRAIRIE CITY", "loc": [-118.695199, 44.456977], "pop": 1614, "state": "OR", "_id": "97869"} -{"city": "RICHLAND", "loc": [-117.301825, 44.797013], "pop": 927, "state": "OR", "_id": "97870"} -{"city": "RITTER", "loc": [-118.941818, 44.95404], "pop": 71, "state": "OR", "_id": "97872"} -{"city": "SENECA", "loc": [-119.057797, 44.127704], "pop": 352, "state": "OR", "_id": "97873"} -{"city": "SPRAY", "loc": [-119.830318, 44.824186], "pop": 438, "state": "OR", "_id": "97874"} -{"city": "STANFIELD", "loc": [-119.211602, 45.785988], "pop": 2031, "state": "OR", "_id": "97875"} -{"city": "SUMMERVILLE", "loc": [-118.026978, 45.507687], "pop": 773, "state": "OR", "_id": "97876"} -{"city": "SUMPTER", "loc": [-118.190604, 44.73164], "pop": 251, "state": "OR", "_id": "97877"} -{"city": "MCNARY", "loc": [-119.313715, 45.915725], "pop": 4009, "state": "OR", "_id": "97882"} -{"city": "UNION", "loc": [-117.853554, 45.201939], "pop": 2463, "state": "OR", "_id": "97883"} -{"city": "UNITY", "loc": [-118.16273, 44.449063], "pop": 315, "state": "OR", "_id": "97884"} -{"city": "WALLOWA", "loc": [-117.535727, 45.571722], "pop": 1250, "state": "OR", "_id": "97885"} -{"city": "WESTON", "loc": [-118.373279, 45.807365], "pop": 1007, "state": "OR", "_id": "97886"} -{"city": "ADRIAN", "loc": [-117.060193, 43.653712], "pop": 568, "state": "OR", "_id": "97901"} -{"city": "AROCK", "loc": [-117.034189, 43.970896], "pop": 846, "state": "OR", "_id": "97902"} -{"city": "BROGAN", "loc": [-117.590371, 44.199396], "pop": 205, "state": "OR", "_id": "97903"} -{"city": "DREWSEY", "loc": [-118.47066, 43.864277], "pop": 228, "state": "OR", "_id": "97904"} -{"city": "HARPER", "loc": [-117.528433, 43.873363], "pop": 326, "state": "OR", "_id": "97906"} -{"city": "HUNTINGTON", "loc": [-117.309691, 44.381113], "pop": 694, "state": "OR", "_id": "97907"} -{"city": "IRONSIDE", "loc": [-117.944435, 44.300913], "pop": 79, "state": "OR", "_id": "97908"} -{"city": "JAMIESON", "loc": [-117.437269, 44.181873], "pop": 53, "state": "OR", "_id": "97909"} -{"city": "JORDAN VALLEY", "loc": [-117.280984, 42.880139], "pop": 879, "state": "OR", "_id": "97910"} -{"city": "JUNTURA", "loc": [-118.092118, 43.825277], "pop": 83, "state": "OR", "_id": "97911"} -{"city": "NYSSA", "loc": [-117.025113, 43.860386], "pop": 5288, "state": "OR", "_id": "97913"} -{"city": "ONTARIO", "loc": [-116.978268, 44.04156], "pop": 13665, "state": "OR", "_id": "97914"} -{"city": "RIVERSIDE", "loc": [-118.095769, 43.467415], "pop": 65, "state": "OR", "_id": "97917"} -{"city": "VALE", "loc": [-117.267412, 44.003902], "pop": 3940, "state": "OR", "_id": "97918"} -{"city": "WESTFALL", "loc": [-117.687673, 43.992246], "pop": 41, "state": "OR", "_id": "97920"} -{"city": "MACARTHUR", "loc": [-80.281567, 40.604424], "pop": 36849, "state": "PA", "_id": "15001"} -{"city": "FAIROAKS", "loc": [-80.219778, 40.595368], "pop": 13449, "state": "PA", "_id": "15003"} -{"city": "BADEN", "loc": [-80.198471, 40.641595], "pop": 10068, "state": "PA", "_id": "15005"} -{"city": "BAKERSTOWN", "loc": [-79.930956, 40.647826], "pop": 782, "state": "PA", "_id": "15007"} -{"city": "BEAVER", "loc": [-80.336528, 40.697184], "pop": 14968, "state": "PA", "_id": "15009"} -{"city": "RACINE", "loc": [-80.33988, 40.766234], "pop": 30623, "state": "PA", "_id": "15010"} -{"city": "ROSTRAVER", "loc": [-79.83454, 40.14368], "pop": 18967, "state": "PA", "_id": "15012"} -{"city": "BRACKENRIDGE", "loc": [-79.741375, 40.608223], "pop": 3674, "state": "PA", "_id": "15014"} -{"city": "BRADFORDWOODS", "loc": [-80.082305, 40.634175], "pop": 2319, "state": "PA", "_id": "15015"} -{"city": "BRIDGEVILLE", "loc": [-80.115293, 40.347195], "pop": 12705, "state": "PA", "_id": "15017"} -{"city": "BUENA VISTA", "loc": [-79.791874, 40.278635], "pop": 1186, "state": "PA", "_id": "15018"} -{"city": "BULGER", "loc": [-80.362192, 40.405119], "pop": 1118, "state": "PA", "_id": "15019"} -{"city": "PARIS", "loc": [-80.444485, 40.390114], "pop": 5527, "state": "PA", "_id": "15021"} -{"city": "CHARLEROI", "loc": [-79.92002, 40.140402], "pop": 11800, "state": "PA", "_id": "15022"} -{"city": "CHESWICK", "loc": [-79.841002, 40.568117], "pop": 9823, "state": "PA", "_id": "15024"} -{"city": "LARGE", "loc": [-79.910096, 40.298917], "pop": 17031, "state": "PA", "_id": "15025"} -{"city": "CLINTON", "loc": [-80.342282, 40.513148], "pop": 2543, "state": "PA", "_id": "15026"} -{"city": "CONWAY", "loc": [-80.234899, 40.664917], "pop": 2438, "state": "PA", "_id": "15027"} -{"city": "CREIGHTON", "loc": [-79.78177, 40.582051], "pop": 1761, "state": "PA", "_id": "15030"} -{"city": "CUDDY", "loc": [-80.164432, 40.360069], "pop": 1037, "state": "PA", "_id": "15031"} -{"city": "DONORA", "loc": [-79.861858, 40.176821], "pop": 5928, "state": "PA", "_id": "15033"} -{"city": "DRAVOSBURG", "loc": [-79.889461, 40.352676], "pop": 2377, "state": "PA", "_id": "15034"} -{"city": "EAST MC KEESPORT", "loc": [-79.807881, 40.383944], "pop": 2698, "state": "PA", "_id": "15035"} -{"city": "ELIZABETH", "loc": [-79.856842, 40.265575], "pop": 12937, "state": "PA", "_id": "15037"} -{"city": "FREEDOM", "loc": [-80.214663, 40.683023], "pop": 9416, "state": "PA", "_id": "15042"} -{"city": "GEORGETOWN", "loc": [-80.490023, 40.574275], "pop": 2810, "state": "PA", "_id": "15043"} -{"city": "GIBSONIA", "loc": [-79.944307, 40.625233], "pop": 18318, "state": "PA", "_id": "15044"} -{"city": "GLASSPORT", "loc": [-79.888324, 40.325952], "pop": 5582, "state": "PA", "_id": "15045"} -{"city": "HARWICK", "loc": [-79.805115, 40.557433], "pop": 1111, "state": "PA", "_id": "15049"} -{"city": "HOOKSTOWN", "loc": [-80.436268, 40.542929], "pop": 2933, "state": "PA", "_id": "15050"} -{"city": "INDIANOLA", "loc": [-79.881765, 40.576605], "pop": 1539, "state": "PA", "_id": "15051"} -{"city": "INDUSTRY", "loc": [-80.415085, 40.667101], "pop": 2908, "state": "PA", "_id": "15052"} -{"city": "JOFFRE", "loc": [-80.391542, 40.381027], "pop": 1634, "state": "PA", "_id": "15053"} -{"city": "LAWRENCE", "loc": [-80.144029, 40.311615], "pop": 1933, "state": "PA", "_id": "15055"} -{"city": "LEETSDALE", "loc": [-80.209874, 40.566165], "pop": 1387, "state": "PA", "_id": "15056"} -{"city": "MC DONALD", "loc": [-80.256204, 40.369664], "pop": 9077, "state": "PA", "_id": "15057"} -{"city": "MIDLAND", "loc": [-80.468952, 40.658962], "pop": 6476, "state": "PA", "_id": "15059"} -{"city": "MIDWAY", "loc": [-80.29209, 40.367974], "pop": 1043, "state": "PA", "_id": "15060"} -{"city": "MONACA", "loc": [-80.291743, 40.671847], "pop": 13344, "state": "PA", "_id": "15061"} -{"city": "MONESSEN", "loc": [-79.883526, 40.152379], "pop": 9906, "state": "PA", "_id": "15062"} -{"city": "MONONGAHELA", "loc": [-79.924127, 40.193728], "pop": 13183, "state": "PA", "_id": "15063"} -{"city": "MORGAN", "loc": [-80.154064, 40.360433], "pop": 1751, "state": "PA", "_id": "15064"} -{"city": "NATRONA", "loc": [-79.725621, 40.628862], "pop": 13305, "state": "PA", "_id": "15065"} -{"city": "NEW BRIGHTON", "loc": [-80.29722, 40.739333], "pop": 12222, "state": "PA", "_id": "15066"} -{"city": "NEW EAGLE", "loc": [-79.953408, 40.206573], "pop": 2222, "state": "PA", "_id": "15067"} -{"city": "ARNOLD", "loc": [-79.741547, 40.571179], "pop": 41922, "state": "PA", "_id": "15068"} -{"city": "NOBLESTOWN", "loc": [-80.202615, 40.428734], "pop": 11012, "state": "PA", "_id": "15071"} -{"city": "ROCHESTER", "loc": [-80.260315, 40.715745], "pop": 10784, "state": "PA", "_id": "15074"} -{"city": "RUSSELLTON", "loc": [-79.837102, 40.614344], "pop": 1437, "state": "PA", "_id": "15076"} -{"city": "SHIPPINGPORT", "loc": [-80.419866, 40.626012], "pop": 227, "state": "PA", "_id": "15077"} -{"city": "SLOVAN", "loc": [-80.399239, 40.363086], "pop": 1620, "state": "PA", "_id": "15078"} -{"city": "SUTERSVILLE", "loc": [-79.792223, 40.238207], "pop": 1467, "state": "PA", "_id": "15083"} -{"city": "TARENTUM", "loc": [-79.785204, 40.618651], "pop": 10752, "state": "PA", "_id": "15084"} -{"city": "LEVEL GREEN", "loc": [-79.755157, 40.385155], "pop": 3345, "state": "PA", "_id": "15085"} -{"city": "WARRENDALE", "loc": [-80.080705, 40.664149], "pop": 379, "state": "PA", "_id": "15086"} -{"city": "WEST NEWTON", "loc": [-79.757976, 40.207549], "pop": 7641, "state": "PA", "_id": "15089"} -{"city": "WEXFORD", "loc": [-80.064879, 40.612044], "pop": 14960, "state": "PA", "_id": "15090"} -{"city": "ALLISON PARK", "loc": [-79.966512, 40.569975], "pop": 22474, "state": "PA", "_id": "15101"} -{"city": "BETHEL PARK", "loc": [-80.039793, 40.320984], "pop": 31160, "state": "PA", "_id": "15102"} -{"city": "RANKIN", "loc": [-79.864352, 40.406304], "pop": 13915, "state": "PA", "_id": "15104"} -{"city": "CARNEGIE", "loc": [-80.091532, 40.402941], "pop": 19539, "state": "PA", "_id": "15106"} -{"city": "MOON TWP", "loc": [-80.18969, 40.513245], "pop": 35285, "state": "PA", "_id": "15108"} -{"city": "DUQUESNE", "loc": [-79.852244, 40.370449], "pop": 8525, "state": "PA", "_id": "15110"} -{"city": "EAST PITTSBURGH", "loc": [-79.83889, 40.403577], "pop": 4145, "state": "PA", "_id": "15112"} -{"city": "GLENSHAW", "loc": [-79.964351, 40.537492], "pop": 16588, "state": "PA", "_id": "15116"} -{"city": "MUNHALL", "loc": [-79.904199, 40.394171], "pop": 21866, "state": "PA", "_id": "15120"} -{"city": "W MIFFLIN FIN", "loc": [-79.89429, 40.366535], "pop": 22805, "state": "PA", "_id": "15122"} -{"city": "IMPERIAL", "loc": [-80.264861, 40.458384], "pop": 3626, "state": "PA", "_id": "15126"} -{"city": "LIBRARY", "loc": [-80.001144, 40.292199], "pop": 11353, "state": "PA", "_id": "15129"} -{"city": "WHITE OAK", "loc": [-79.810519, 40.341147], "pop": 9410, "state": "PA", "_id": "15131"} -{"city": "MC KEESPORT", "loc": [-79.8452, 40.341713], "pop": 27694, "state": "PA", "_id": "15132"} -{"city": "MC KEESPORT", "loc": [-79.866759, 40.332835], "pop": 7385, "state": "PA", "_id": "15133"} -{"city": "BOSTON", "loc": [-79.812844, 40.304412], "pop": 5385, "state": "PA", "_id": "15135"} -{"city": "MC KEES ROCKS", "loc": [-80.087567, 40.471742], "pop": 25092, "state": "PA", "_id": "15136"} -{"city": "NORTH VERSAILLES", "loc": [-79.812427, 40.376248], "pop": 12237, "state": "PA", "_id": "15137"} -{"city": "OAKMONT", "loc": [-79.836865, 40.519647], "pop": 6961, "state": "PA", "_id": "15139"} -{"city": "PITCAIRN", "loc": [-79.776951, 40.404787], "pop": 4083, "state": "PA", "_id": "15140"} -{"city": "PRESTO", "loc": [-80.120922, 40.384664], "pop": 864, "state": "PA", "_id": "15142"} -{"city": "SEWICKLEY", "loc": [-80.157848, 40.557031], "pop": 15849, "state": "PA", "_id": "15143"} -{"city": "SPRINGDALE", "loc": [-79.784447, 40.543999], "pop": 4858, "state": "PA", "_id": "15144"} -{"city": "TURTLE CREEK", "loc": [-79.822046, 40.41135], "pop": 8157, "state": "PA", "_id": "15145"} -{"city": "MONROEVILLE", "loc": [-79.762279, 40.429026], "pop": 29228, "state": "PA", "_id": "15146"} -{"city": "VERONA", "loc": [-79.834535, 40.492727], "pop": 20843, "state": "PA", "_id": "15147"} -{"city": "WALL", "loc": [-79.803033, 40.393416], "pop": 3074, "state": "PA", "_id": "15148"} -{"city": "ARSENAL", "loc": [-79.952524, 40.474536], "pop": 15278, "state": "PA", "_id": "15201"} -{"city": "BELLEVUE", "loc": [-80.066966, 40.501321], "pop": 22168, "state": "PA", "_id": "15202"} -{"city": "CARSON", "loc": [-79.977556, 40.425439], "pop": 11012, "state": "PA", "_id": "15203"} -{"city": "CORLISS", "loc": [-80.061056, 40.455569], "pop": 10266, "state": "PA", "_id": "15204"} -{"city": "CRAFTON", "loc": [-80.073393, 40.438045], "pop": 24562, "state": "PA", "_id": "15205"} -{"city": "EAST LIBERTY", "loc": [-79.919267, 40.468885], "pop": 37220, "state": "PA", "_id": "15206"} -{"city": "HAZELWOOD", "loc": [-79.933935, 40.401206], "pop": 15095, "state": "PA", "_id": "15207"} -{"city": "HOMEWOOD", "loc": [-79.898474, 40.454955], "pop": 16136, "state": "PA", "_id": "15208"} -{"city": "MILLVALE", "loc": [-79.97401, 40.49718], "pop": 15034, "state": "PA", "_id": "15209"} -{"city": "MOUNT OLIVER", "loc": [-79.987405, 40.408541], "pop": 35727, "state": "PA", "_id": "15210"} -{"city": "MOUNT WASHINGTON", "loc": [-80.012156, 40.42908], "pop": 13333, "state": "PA", "_id": "15211"} -{"city": "ALLEGHENY", "loc": [-80.013128, 40.468873], "pop": 35280, "state": "PA", "_id": "15212"} -{"city": "OAKLAND", "loc": [-79.954428, 40.44372], "pop": 31142, "state": "PA", "_id": "15213"} -{"city": "OBSERVATORY", "loc": [-80.01393, 40.481309], "pop": 19981, "state": "PA", "_id": "15214"} -{"city": "ASPINWALL", "loc": [-79.917513, 40.499225], "pop": 12801, "state": "PA", "_id": "15215"} -{"city": "SOUTH HILLS", "loc": [-80.035727, 40.399584], "pop": 26700, "state": "PA", "_id": "15216"} -{"city": "SQUIRREL HILL", "loc": [-79.924973, 40.431852], "pop": 27450, "state": "PA", "_id": "15217"} -{"city": "SWISSVALE", "loc": [-79.887591, 40.424468], "pop": 16501, "state": "PA", "_id": "15218"} -{"city": "UPTOWN", "loc": [-79.977229, 40.44539], "pop": 18924, "state": "PA", "_id": "15219"} -{"city": "PARKWAY CENTER", "loc": [-80.051202, 40.417405], "pop": 19228, "state": "PA", "_id": "15220"} -{"city": "WILKINSBURG", "loc": [-79.870243, 40.438352], "pop": 39410, "state": "PA", "_id": "15221"} -{"city": "DOWNTOWN", "loc": [-80.000556, 40.442111], "pop": 1763, "state": "PA", "_id": "15222"} -{"city": "ETNA", "loc": [-79.95145, 40.50428], "pop": 7261, "state": "PA", "_id": "15223"} -{"city": "BLOOMFIELD", "loc": [-79.945445, 40.464215], "pop": 14742, "state": "PA", "_id": "15224"} -{"city": "NEVILLE ISLAND", "loc": [-80.137027, 40.513819], "pop": 1273, "state": "PA", "_id": "15225"} -{"city": "BROOKLINE", "loc": [-80.015759, 40.394628], "pop": 15607, "state": "PA", "_id": "15226"} -{"city": "BRENTWOOD", "loc": [-79.975816, 40.37619], "pop": 27563, "state": "PA", "_id": "15227"} -{"city": "MOUNT LEBANON", "loc": [-80.043186, 40.371326], "pop": 18660, "state": "PA", "_id": "15228"} -{"city": "WEST VIEW", "loc": [-80.035685, 40.519321], "pop": 15127, "state": "PA", "_id": "15229"} -{"city": "SHADYSIDE", "loc": [-79.932557, 40.453598], "pop": 10936, "state": "PA", "_id": "15232"} -{"city": "KILBUCK", "loc": [-80.029965, 40.460425], "pop": 5918, "state": "PA", "_id": "15233"} -{"city": "CASTLE SHANNON", "loc": [-80.017907, 40.369424], "pop": 14952, "state": "PA", "_id": "15234"} -{"city": "PENN HILLS", "loc": [-79.826892, 40.4605], "pop": 41997, "state": "PA", "_id": "15235"} -{"city": "CASTE VILLAGE", "loc": [-79.976894, 40.345244], "pop": 36047, "state": "PA", "_id": "15236"} -{"city": "MC KNIGHT", "loc": [-80.034939, 40.552238], "pop": 39733, "state": "PA", "_id": "15237"} -{"city": "BLAWNOX", "loc": [-79.877423, 40.515077], "pop": 10776, "state": "PA", "_id": "15238"} -{"city": "PLUM", "loc": [-79.734505, 40.477693], "pop": 20793, "state": "PA", "_id": "15239"} -{"city": "UPPER SAINT CLAI", "loc": [-80.07921, 40.332174], "pop": 20276, "state": "PA", "_id": "15241"} -{"city": "CEDARHURST", "loc": [-80.072425, 40.373797], "pop": 14242, "state": "PA", "_id": "15243"} -{"city": "WASHINGTON", "loc": [-80.255957, 40.171687], "pop": 48612, "state": "PA", "_id": "15301"} -{"city": "ALEPPO", "loc": [-80.457918, 39.824647], "pop": 656, "state": "PA", "_id": "15310"} -{"city": "AMITY", "loc": [-80.194865, 40.061786], "pop": 1458, "state": "PA", "_id": "15311"} -{"city": "AVELLA", "loc": [-80.456503, 40.273386], "pop": 3681, "state": "PA", "_id": "15312"} -{"city": "BEALLSVILLE", "loc": [-80.025056, 40.064503], "pop": 530, "state": "PA", "_id": "15313"} -{"city": "BENTLEYVILLE", "loc": [-80.006987, 40.118702], "pop": 2673, "state": "PA", "_id": "15314"} -{"city": "MC MURRAY", "loc": [-80.153153, 40.270743], "pop": 29555, "state": "PA", "_id": "15317"} -{"city": "CARMICHAELS", "loc": [-79.971007, 39.882548], "pop": 6702, "state": "PA", "_id": "15320"} -{"city": "CECIL", "loc": [-80.20437, 40.322813], "pop": 3257, "state": "PA", "_id": "15321"} -{"city": "CLARKSVILLE", "loc": [-80.012386, 39.994064], "pop": 3186, "state": "PA", "_id": "15322"} -{"city": "CLAYSVILLE", "loc": [-80.385904, 40.137557], "pop": 6836, "state": "PA", "_id": "15323"} -{"city": "COKEBURG", "loc": [-80.065152, 40.100801], "pop": 854, "state": "PA", "_id": "15324"} -{"city": "DILLINER", "loc": [-79.977135, 39.755536], "pop": 2407, "state": "PA", "_id": "15327"} -{"city": "PROSPERITY", "loc": [-80.261257, 40.0224], "pop": 1633, "state": "PA", "_id": "15329"} -{"city": "EIGHTY FOUR", "loc": [-80.062695, 40.186821], "pop": 5021, "state": "PA", "_id": "15330"} -{"city": "ELLSWORTH", "loc": [-80.020364, 40.107321], "pop": 1040, "state": "PA", "_id": "15331"} -{"city": "FINLEYVILLE", "loc": [-79.97531, 40.259301], "pop": 8838, "state": "PA", "_id": "15332"} -{"city": "FREDERICKTOWN", "loc": [-80.030937, 40.023749], "pop": 770, "state": "PA", "_id": "15333"} -{"city": "GRAYSVILLE", "loc": [-80.395223, 39.909165], "pop": 468, "state": "PA", "_id": "15337"} -{"city": "GREENSBORO", "loc": [-79.93992, 39.804482], "pop": 2164, "state": "PA", "_id": "15338"} -{"city": "HICKORY", "loc": [-80.302508, 40.292511], "pop": 2064, "state": "PA", "_id": "15340"} -{"city": "HOLBROOK", "loc": [-80.338455, 39.848878], "pop": 1247, "state": "PA", "_id": "15341"} -{"city": "HOUSTON", "loc": [-80.220937, 40.242492], "pop": 6060, "state": "PA", "_id": "15342"} -{"city": "JEFFERSON", "loc": [-80.050286, 39.933391], "pop": 2154, "state": "PA", "_id": "15344"} -{"city": "MARIANNA", "loc": [-80.114506, 40.033067], "pop": 2695, "state": "PA", "_id": "15345"} -{"city": "MATHER", "loc": [-80.085353, 39.945226], "pop": 2856, "state": "PA", "_id": "15346"} -{"city": "DAVISTOWN", "loc": [-80.096763, 39.744289], "pop": 1719, "state": "PA", "_id": "15349"} -{"city": "NEW FREEPORT", "loc": [-80.454228, 39.750728], "pop": 833, "state": "PA", "_id": "15352"} -{"city": "NINEVEH", "loc": [-80.312809, 39.974328], "pop": 884, "state": "PA", "_id": "15353"} -{"city": "RICES LANDING", "loc": [-79.985701, 39.943993], "pop": 2095, "state": "PA", "_id": "15357"} -{"city": "ROGERSVILLE", "loc": [-80.283665, 39.900515], "pop": 313, "state": "PA", "_id": "15359"} -{"city": "SCENERY HILL", "loc": [-80.091414, 40.099378], "pop": 2027, "state": "PA", "_id": "15360"} -{"city": "SPRAGGS", "loc": [-80.262186, 39.761038], "pop": 1682, "state": "PA", "_id": "15362"} -{"city": "STRABANE", "loc": [-80.146156, 40.216914], "pop": 2386, "state": "PA", "_id": "15363"} -{"city": "SYCAMORE", "loc": [-80.228199, 39.95273], "pop": 395, "state": "PA", "_id": "15364"} -{"city": "VENETIA", "loc": [-80.059849, 40.275451], "pop": 3568, "state": "PA", "_id": "15367"} -{"city": "WAYNESBURG", "loc": [-80.179524, 39.891691], "pop": 11246, "state": "PA", "_id": "15370"} -{"city": "WEST ALEXANDER", "loc": [-80.497769, 40.106514], "pop": 1642, "state": "PA", "_id": "15376"} -{"city": "WEST FINLEY", "loc": [-80.440806, 39.991397], "pop": 1313, "state": "PA", "_id": "15377"} -{"city": "WIND RIDGE", "loc": [-80.464665, 39.897106], "pop": 554, "state": "PA", "_id": "15380"} -{"city": "UNIONTOWN", "loc": [-79.728216, 39.889733], "pop": 34582, "state": "PA", "_id": "15401"} -{"city": "ADAH", "loc": [-79.890827, 39.883101], "pop": 596, "state": "PA", "_id": "15410"} -{"city": "ADDISON", "loc": [-79.319334, 39.761195], "pop": 1158, "state": "PA", "_id": "15411"} -{"city": "ALLENPORT", "loc": [-79.854194, 40.089845], "pop": 665, "state": "PA", "_id": "15412"} -{"city": "ALLISON", "loc": [-79.875675, 39.982566], "pop": 326, "state": "PA", "_id": "15413"} -{"city": "WEST BROWNSVILLE", "loc": [-79.920609, 40.02671], "pop": 9133, "state": "PA", "_id": "15417"} -{"city": "CALIFORNIA", "loc": [-79.895319, 40.062529], "pop": 5778, "state": "PA", "_id": "15419"} -{"city": "COAL CENTER", "loc": [-79.8839, 40.097272], "pop": 482, "state": "PA", "_id": "15423"} -{"city": "LISTONBURG", "loc": [-79.351304, 39.821247], "pop": 1998, "state": "PA", "_id": "15424"} -{"city": "SOUTH CONNELLSVI", "loc": [-79.587838, 40.025037], "pop": 25516, "state": "PA", "_id": "15425"} -{"city": "DAISYTOWN", "loc": [-79.967036, 40.074349], "pop": 1858, "state": "PA", "_id": "15427"} -{"city": "DAWSON", "loc": [-79.660224, 40.063767], "pop": 1673, "state": "PA", "_id": "15428"} -{"city": "DUNBAR", "loc": [-79.64305, 39.972171], "pop": 2947, "state": "PA", "_id": "15431"} -{"city": "DUNLEVY", "loc": [-79.862703, 40.115077], "pop": 417, "state": "PA", "_id": "15432"} -{"city": "EAST MILLSBORO", "loc": [-79.966442, 39.982214], "pop": 104, "state": "PA", "_id": "15433"} -{"city": "ELCO", "loc": [-79.875836, 40.079671], "pop": 373, "state": "PA", "_id": "15434"} -{"city": "FAIRCHANCE", "loc": [-79.755051, 39.822876], "pop": 1918, "state": "PA", "_id": "15436"} -{"city": "FARMINGTON", "loc": [-79.583185, 39.806995], "pop": 3296, "state": "PA", "_id": "15437"} -{"city": "FAYETTE CITY", "loc": [-79.836584, 40.098799], "pop": 921, "state": "PA", "_id": "15438"} -{"city": "GIBBON GLADE", "loc": [-79.573603, 39.736357], "pop": 94, "state": "PA", "_id": "15440"} -{"city": "GRINDSTONE", "loc": [-79.840447, 40.008415], "pop": 6843, "state": "PA", "_id": "15442"} -{"city": "HILLER", "loc": [-79.899936, 40.004496], "pop": 2028, "state": "PA", "_id": "15444"} -{"city": "HOPWOOD", "loc": [-79.705945, 39.868868], "pop": 3367, "state": "PA", "_id": "15445"} -{"city": "INDIAN HEAD", "loc": [-79.393387, 40.020195], "pop": 181, "state": "PA", "_id": "15446"} -{"city": "LA BELLE", "loc": [-79.937024, 39.973829], "pop": 2169, "state": "PA", "_id": "15450"} -{"city": "LAKE LYNN", "loc": [-79.86184, 39.750065], "pop": 2217, "state": "PA", "_id": "15451"} -{"city": "LEMONT FURNACE", "loc": [-79.647684, 39.931057], "pop": 1638, "state": "PA", "_id": "15456"} -{"city": "LAMBERTON", "loc": [-79.859022, 39.890185], "pop": 4703, "state": "PA", "_id": "15458"} -{"city": "MARKLEYSBURG", "loc": [-79.46003, 39.759723], "pop": 2052, "state": "PA", "_id": "15459"} -{"city": "GRAYS LANDING", "loc": [-79.893813, 39.83495], "pop": 6033, "state": "PA", "_id": "15461"} -{"city": "MELCROFT", "loc": [-79.3587, 40.064789], "pop": 77, "state": "PA", "_id": "15462"} -{"city": "MERRITTSTOWN", "loc": [-79.874447, 39.964854], "pop": 1830, "state": "PA", "_id": "15463"} -{"city": "MILL RUN", "loc": [-79.462219, 39.967985], "pop": 1972, "state": "PA", "_id": "15464"} -{"city": "NEW SALEM", "loc": [-79.80407, 39.940747], "pop": 4509, "state": "PA", "_id": "15468"} -{"city": "NORMALVILLE", "loc": [-79.415061, 40.041793], "pop": 3814, "state": "PA", "_id": "15469"} -{"city": "OHIOPYLE", "loc": [-79.48846, 39.873202], "pop": 815, "state": "PA", "_id": "15470"} -{"city": "OLIVER", "loc": [-79.715601, 39.911659], "pop": 2395, "state": "PA", "_id": "15472"} -{"city": "LAYTON", "loc": [-79.754384, 40.085724], "pop": 1833, "state": "PA", "_id": "15473"} -{"city": "POINT MARION", "loc": [-79.898986, 39.735124], "pop": 1474, "state": "PA", "_id": "15474"} -{"city": "REPUBLIC", "loc": [-79.902914, 39.961427], "pop": 641, "state": "PA", "_id": "15475"} -{"city": "ROSCOE", "loc": [-79.865617, 40.078686], "pop": 802, "state": "PA", "_id": "15477"} -{"city": "SMITHFIELD", "loc": [-79.81268, 39.792306], "pop": 2163, "state": "PA", "_id": "15478"} -{"city": "VAN METER", "loc": [-79.731277, 40.144267], "pop": 1009, "state": "PA", "_id": "15479"} -{"city": "SMOCK", "loc": [-79.767828, 39.996096], "pop": 986, "state": "PA", "_id": "15480"} -{"city": "STAR JUNCTION", "loc": [-79.755461, 40.083198], "pop": 2803, "state": "PA", "_id": "15482"} -{"city": "STOCKDALE", "loc": [-79.850103, 40.082567], "pop": 630, "state": "PA", "_id": "15483"} -{"city": "VANDERBILT", "loc": [-79.695518, 40.024581], "pop": 2706, "state": "PA", "_id": "15486"} -{"city": "WALTERSBURG", "loc": [-79.780336, 39.990465], "pop": 237, "state": "PA", "_id": "15488"} -{"city": "WHITE", "loc": [-79.425117, 40.072596], "pop": 138, "state": "PA", "_id": "15490"} -{"city": "SOMERSET", "loc": [-79.080814, 40.024813], "pop": 18303, "state": "PA", "_id": "15501"} -{"city": "ALUM BANK", "loc": [-78.620591, 40.185833], "pop": 2175, "state": "PA", "_id": "15521"} -{"city": "BEDFORD", "loc": [-78.526071, 39.990838], "pop": 10788, "state": "PA", "_id": "15522"} -{"city": "BERLIN", "loc": [-78.963692, 39.918847], "pop": 4459, "state": "PA", "_id": "15530"} -{"city": "BOSWELL", "loc": [-79.036179, 40.191807], "pop": 1681, "state": "PA", "_id": "15531"} -{"city": "BREEZEWOOD", "loc": [-78.245271, 39.990521], "pop": 999, "state": "PA", "_id": "15533"} -{"city": "BUFFALO MILLS", "loc": [-78.69962, 39.922025], "pop": 445, "state": "PA", "_id": "15534"} -{"city": "CLEARVILLE", "loc": [-78.43918, 39.853284], "pop": 2304, "state": "PA", "_id": "15535"} -{"city": "CRYSTAL SPRING", "loc": [-78.225836, 39.919973], "pop": 643, "state": "PA", "_id": "15536"} -{"city": "EVERETT", "loc": [-78.371315, 40.009808], "pop": 7289, "state": "PA", "_id": "15537"} -{"city": "GLENCOE", "loc": [-78.827522, 39.909046], "pop": 1350, "state": "PA", "_id": "15538"} -{"city": "FORT HILL", "loc": [-79.23535, 39.877282], "pop": 305, "state": "PA", "_id": "15540"} -{"city": "FRIEDENS", "loc": [-78.903298, 40.010997], "pop": 2065, "state": "PA", "_id": "15541"} -{"city": "GARRETT", "loc": [-79.061561, 39.864581], "pop": 520, "state": "PA", "_id": "15542"} -{"city": "HYNDMAN", "loc": [-78.733512, 39.804857], "pop": 2732, "state": "PA", "_id": "15545"} -{"city": "JENNERS", "loc": [-79.054571, 40.147321], "pop": 4586, "state": "PA", "_id": "15546"} -{"city": "MANNS CHOICE", "loc": [-78.642259, 39.980787], "pop": 1579, "state": "PA", "_id": "15550"} -{"city": "MARKLETON", "loc": [-79.287851, 39.869096], "pop": 288, "state": "PA", "_id": "15551"} -{"city": "MEYERSDALE", "loc": [-79.026141, 39.790489], "pop": 9276, "state": "PA", "_id": "15552"} -{"city": "NEW PARIS", "loc": [-78.588569, 40.118876], "pop": 3982, "state": "PA", "_id": "15554"} -{"city": "ROCKWOOD", "loc": [-79.186546, 39.937296], "pop": 5125, "state": "PA", "_id": "15557"} -{"city": "SALISBURY", "loc": [-79.083516, 39.753052], "pop": 716, "state": "PA", "_id": "15558"} -{"city": "SCHELLSBURG", "loc": [-78.648194, 40.04869], "pop": 1523, "state": "PA", "_id": "15559"} -{"city": "SPRINGS", "loc": [-79.08895, 39.748895], "pop": 82, "state": "PA", "_id": "15562"} -{"city": "STOYSTOWN", "loc": [-78.955978, 40.109886], "pop": 2690, "state": "PA", "_id": "15563"} -{"city": "GREENSBURG", "loc": [-79.542439, 40.307359], "pop": 52186, "state": "PA", "_id": "15601"} -{"city": "ACME", "loc": [-79.482747, 40.104891], "pop": 3862, "state": "PA", "_id": "15610"} -{"city": "ADAMSBURG", "loc": [-79.656469, 40.312577], "pop": 255, "state": "PA", "_id": "15611"} -{"city": "ALVERTON", "loc": [-79.558608, 40.127866], "pop": 2166, "state": "PA", "_id": "15612"} -{"city": "APOLLO", "loc": [-79.577158, 40.556481], "pop": 18588, "state": "PA", "_id": "15613"} -{"city": "ARDARA", "loc": [-79.733533, 40.362993], "pop": 450, "state": "PA", "_id": "15615"} -{"city": "ARMBRUST", "loc": [-79.553884, 40.236097], "pop": 500, "state": "PA", "_id": "15616"} -{"city": "ARONA", "loc": [-79.659104, 40.26799], "pop": 397, "state": "PA", "_id": "15617"} -{"city": "AVONMORE", "loc": [-79.485264, 40.522125], "pop": 3306, "state": "PA", "_id": "15618"} -{"city": "BRADENVILLE", "loc": [-79.309679, 40.296094], "pop": 139, "state": "PA", "_id": "15620"} -{"city": "CHAMPION", "loc": [-79.324719, 40.043976], "pop": 109, "state": "PA", "_id": "15622"} -{"city": "DARRAGH", "loc": [-79.678036, 40.260377], "pop": 159, "state": "PA", "_id": "15625"} -{"city": "DELMONT", "loc": [-79.57638, 40.413901], "pop": 2852, "state": "PA", "_id": "15626"} -{"city": "DERRY", "loc": [-79.332585, 40.334931], "pop": 10610, "state": "PA", "_id": "15627"} -{"city": "DONEGAL", "loc": [-79.381043, 40.099613], "pop": 584, "state": "PA", "_id": "15628"} -{"city": "EVERSON", "loc": [-79.587277, 40.091144], "pop": 868, "state": "PA", "_id": "15631"} -{"city": "EXPORT", "loc": [-79.611021, 40.425185], "pop": 8364, "state": "PA", "_id": "15632"} -{"city": "GRAPEVILLE", "loc": [-79.603189, 40.303804], "pop": 683, "state": "PA", "_id": "15634"} -{"city": "HARRISON CITY", "loc": [-79.679754, 40.372455], "pop": 12018, "state": "PA", "_id": "15636"} -{"city": "HERMINIE", "loc": [-79.717164, 40.245485], "pop": 3820, "state": "PA", "_id": "15637"} -{"city": "HUNKER", "loc": [-79.582364, 40.214947], "pop": 1592, "state": "PA", "_id": "15639"} -{"city": "HYDE PARK", "loc": [-79.589884, 40.631102], "pop": 542, "state": "PA", "_id": "15641"} -{"city": "NORTH HUNTINGDON", "loc": [-79.723855, 40.319227], "pop": 36415, "state": "PA", "_id": "15642"} -{"city": "JEANNETTE", "loc": [-79.614412, 40.32947], "pop": 20989, "state": "PA", "_id": "15644"} -{"city": "JONES MILLS", "loc": [-79.339933, 40.07977], "pop": 226, "state": "PA", "_id": "15646"} -{"city": "LARIMER", "loc": [-79.736627, 40.351842], "pop": 1731, "state": "PA", "_id": "15647"} -{"city": "LATROBE", "loc": [-79.410278, 40.292625], "pop": 29529, "state": "PA", "_id": "15650"} -{"city": "LAUGHLINTOWN", "loc": [-79.18058, 40.208025], "pop": 436, "state": "PA", "_id": "15655"} -{"city": "LEECHBURG", "loc": [-79.620101, 40.634398], "pop": 11552, "state": "PA", "_id": "15656"} -{"city": "WILPEN", "loc": [-79.236666, 40.245133], "pop": 9229, "state": "PA", "_id": "15658"} -{"city": "LOYALHANNA", "loc": [-79.344157, 40.30199], "pop": 564, "state": "PA", "_id": "15661"} -{"city": "MADISON", "loc": [-79.675978, 40.246669], "pop": 539, "state": "PA", "_id": "15663"} -{"city": "MANOR", "loc": [-79.671221, 40.33822], "pop": 3848, "state": "PA", "_id": "15665"} -{"city": "MOUNT PLEASANT", "loc": [-79.513383, 40.174179], "pop": 15336, "state": "PA", "_id": "15666"} -{"city": "MURRYSVILLE", "loc": [-79.684154, 40.446674], "pop": 10965, "state": "PA", "_id": "15668"} -{"city": "NEW ALEXANDRIA", "loc": [-79.396594, 40.398112], "pop": 2658, "state": "PA", "_id": "15670"} -{"city": "NEW DERRY", "loc": [-79.300863, 40.333334], "pop": 2950, "state": "PA", "_id": "15671"} -{"city": "NEW STANTON", "loc": [-79.618187, 40.223345], "pop": 3237, "state": "PA", "_id": "15672"} -{"city": "PENN", "loc": [-79.641336, 40.330081], "pop": 619, "state": "PA", "_id": "15675"} -{"city": "RECTOR", "loc": [-79.247331, 40.186444], "pop": 376, "state": "PA", "_id": "15677"} -{"city": "RILLTON", "loc": [-79.728211, 40.282454], "pop": 1050, "state": "PA", "_id": "15678"} -{"city": "RUFFS DALE", "loc": [-79.627743, 40.158458], "pop": 6385, "state": "PA", "_id": "15679"} -{"city": "SALTSBURG", "loc": [-79.442844, 40.479239], "pop": 3922, "state": "PA", "_id": "15681"} -{"city": "SCOTTDALE", "loc": [-79.593017, 40.102948], "pop": 6132, "state": "PA", "_id": "15683"} -{"city": "SLICKVILLE", "loc": [-79.506654, 40.465347], "pop": 318, "state": "PA", "_id": "15684"} -{"city": "SPRING CHURCH", "loc": [-79.454487, 40.615329], "pop": 1291, "state": "PA", "_id": "15686"} -{"city": "STAHLSTOWN", "loc": [-79.344473, 40.138593], "pop": 2555, "state": "PA", "_id": "15687"} -{"city": "TARRS", "loc": [-79.566101, 40.161915], "pop": 385, "state": "PA", "_id": "15688"} -{"city": "PARK", "loc": [-79.565531, 40.605883], "pop": 9409, "state": "PA", "_id": "15690"} -{"city": "WESTMORELAND CIT", "loc": [-79.547696, 40.276802], "pop": 2293, "state": "PA", "_id": "15692"} -{"city": "YOUNGWOOD", "loc": [-79.582291, 40.239482], "pop": 3319, "state": "PA", "_id": "15697"} -{"city": "YUKON", "loc": [-79.684941, 40.215529], "pop": 726, "state": "PA", "_id": "15698"} -{"city": "INDIANA", "loc": [-79.159596, 40.619628], "pop": 28962, "state": "PA", "_id": "15701"} -{"city": "ANITA", "loc": [-78.966637, 41.001806], "pop": 586, "state": "PA", "_id": "15711"} -{"city": "AULTMAN", "loc": [-79.219791, 40.540083], "pop": 162, "state": "PA", "_id": "15713"} -{"city": "BARNESBORO", "loc": [-78.777058, 40.673307], "pop": 3878, "state": "PA", "_id": "15714"} -{"city": "BLACK LICK", "loc": [-79.193231, 40.466905], "pop": 1100, "state": "PA", "_id": "15716"} -{"city": "BLAIRSVILLE", "loc": [-79.253329, 40.441262], "pop": 9738, "state": "PA", "_id": "15717"} -{"city": "BRUSH VALLEY", "loc": [-79.058765, 40.528565], "pop": 1811, "state": "PA", "_id": "15720"} -{"city": "BURNSIDE", "loc": [-78.786488, 40.813431], "pop": 350, "state": "PA", "_id": "15721"} -{"city": "CARROLLTOWN", "loc": [-78.703676, 40.589054], "pop": 2644, "state": "PA", "_id": "15722"} -{"city": "CHERRY TREE", "loc": [-78.847357, 40.755387], "pop": 2220, "state": "PA", "_id": "15724"} -{"city": "CLARKSBURG", "loc": [-79.367676, 40.503933], "pop": 1369, "state": "PA", "_id": "15725"} -{"city": "CLYMER", "loc": [-79.011863, 40.668811], "pop": 1499, "state": "PA", "_id": "15728"} -{"city": "COMMODORE", "loc": [-78.913371, 40.701588], "pop": 3259, "state": "PA", "_id": "15729"} -{"city": "COOLSPRING", "loc": [-78.922015, 40.951816], "pop": 44, "state": "PA", "_id": "15730"} -{"city": "CREEKSIDE", "loc": [-79.201396, 40.719892], "pop": 2198, "state": "PA", "_id": "15732"} -{"city": "ERNEST", "loc": [-79.096937, 40.704255], "pop": 3841, "state": "PA", "_id": "15739"} -{"city": "GLEN CAMPBELL", "loc": [-78.855538, 40.850766], "pop": 1308, "state": "PA", "_id": "15742"} -{"city": "HAMILTON", "loc": [-79.093987, 40.921432], "pop": 0, "state": "PA", "_id": "15744"} -{"city": "HOME", "loc": [-79.164082, 40.783441], "pop": 926, "state": "PA", "_id": "15747"} -{"city": "GRACETON", "loc": [-79.183942, 40.538375], "pop": 3677, "state": "PA", "_id": "15748"} -{"city": "LA JOSE", "loc": [-78.62337, 40.830118], "pop": 1119, "state": "PA", "_id": "15753"} -{"city": "LUCERNEMINES", "loc": [-79.157349, 40.545313], "pop": 3227, "state": "PA", "_id": "15754"} -{"city": "MC GEES MILLS", "loc": [-78.72039, 40.889251], "pop": 1792, "state": "PA", "_id": "15757"} -{"city": "MARCHAND", "loc": [-79.045151, 40.875411], "pop": 1302, "state": "PA", "_id": "15758"} -{"city": "MARION CENTER", "loc": [-79.02524, 40.78135], "pop": 2085, "state": "PA", "_id": "15759"} -{"city": "MARSTELLER", "loc": [-78.813356, 40.65004], "pop": 959, "state": "PA", "_id": "15760"} -{"city": "NICKTOWN", "loc": [-78.811394, 40.601623], "pop": 1395, "state": "PA", "_id": "15762"} -{"city": "NORTHPOINT", "loc": [-79.125657, 40.903703], "pop": 49, "state": "PA", "_id": "15763"} -{"city": "OLIVEBURG", "loc": [-79.038197, 40.996379], "pop": 8, "state": "PA", "_id": "15764"} -{"city": "PENN RUN", "loc": [-78.971597, 40.626825], "pop": 4258, "state": "PA", "_id": "15765"} -{"city": "PUNXSUTAWNEY", "loc": [-78.968056, 40.947937], "pop": 12377, "state": "PA", "_id": "15767"} -{"city": "RINGGOLD", "loc": [-79.176581, 40.999731], "pop": 692, "state": "PA", "_id": "15770"} -{"city": "ROCHESTER MILLS", "loc": [-78.997985, 40.819482], "pop": 253, "state": "PA", "_id": "15771"} -{"city": "ROSSITER", "loc": [-78.941353, 40.884553], "pop": 1858, "state": "PA", "_id": "15772"} -{"city": "SAINT BENEDICT", "loc": [-78.738332, 40.611845], "pop": 1544, "state": "PA", "_id": "15773"} -{"city": "SHELOCTA", "loc": [-79.31907, 40.638787], "pop": 7854, "state": "PA", "_id": "15774"} -{"city": "SPANGLER", "loc": [-78.769061, 40.648082], "pop": 2851, "state": "PA", "_id": "15775"} -{"city": "SPRANKLE MILLS", "loc": [-79.077809, 41.01846], "pop": 980, "state": "PA", "_id": "15776"} -{"city": "STARFORD", "loc": [-78.980317, 40.700971], "pop": 850, "state": "PA", "_id": "15777"} -{"city": "TIMBLIN", "loc": [-79.20135, 40.968387], "pop": 178, "state": "PA", "_id": "15778"} -{"city": "VALIER", "loc": [-79.083263, 40.922851], "pop": 44, "state": "PA", "_id": "15780"} -{"city": "WORTHVILLE", "loc": [-79.138685, 41.023767], "pop": 65, "state": "PA", "_id": "15784"} -{"city": "DU BOIS", "loc": [-78.752698, 41.126039], "pop": 17345, "state": "PA", "_id": "15801"} -{"city": "BENEZETT", "loc": [-78.357621, 41.325318], "pop": 243, "state": "PA", "_id": "15821"} -{"city": "BROCKPORT", "loc": [-78.712829, 41.28217], "pop": 1646, "state": "PA", "_id": "15823"} -{"city": "BROCKWAY", "loc": [-78.811568, 41.240564], "pop": 6361, "state": "PA", "_id": "15824"} -{"city": "HAZEN", "loc": [-79.064101, 41.159986], "pop": 9303, "state": "PA", "_id": "15825"} -{"city": "BYRNEDALE", "loc": [-78.505126, 41.286459], "pop": 1892, "state": "PA", "_id": "15827"} -{"city": "CLARINGTON", "loc": [-79.146557, 41.323272], "pop": 311, "state": "PA", "_id": "15828"} -{"city": "CORSICA", "loc": [-79.175567, 41.182983], "pop": 1070, "state": "PA", "_id": "15829"} -{"city": "DRIFTWOOD", "loc": [-78.163194, 41.376396], "pop": 526, "state": "PA", "_id": "15832"} -{"city": "EMPORIUM", "loc": [-78.25361, 41.517689], "pop": 5219, "state": "PA", "_id": "15834"} -{"city": "FALLS CREEK", "loc": [-78.812791, 41.145505], "pop": 1422, "state": "PA", "_id": "15840"} -{"city": "JOHNSONBURG", "loc": [-78.67826, 41.492823], "pop": 3350, "state": "PA", "_id": "15845"} -{"city": "KERSEY", "loc": [-78.60152, 41.356271], "pop": 3392, "state": "PA", "_id": "15846"} -{"city": "LUTHERSBURG", "loc": [-78.742758, 41.05321], "pop": 2249, "state": "PA", "_id": "15848"} -{"city": "PENFIELD", "loc": [-78.579111, 41.208519], "pop": 1395, "state": "PA", "_id": "15849"} -{"city": "REYNOLDSVILLE", "loc": [-78.896147, 41.062935], "pop": 8758, "state": "PA", "_id": "15851"} -{"city": "PORTLAND MILLS", "loc": [-78.729715, 41.431566], "pop": 7419, "state": "PA", "_id": "15853"} -{"city": "ROCKTON", "loc": [-78.657662, 41.080571], "pop": 833, "state": "PA", "_id": "15856"} -{"city": "SAINT MARYS", "loc": [-78.550533, 41.428949], "pop": 14020, "state": "PA", "_id": "15857"} -{"city": "SIGEL", "loc": [-79.053957, 41.309921], "pop": 1103, "state": "PA", "_id": "15860"} -{"city": "SINNAMAHONING", "loc": [-78.06607, 41.376197], "pop": 168, "state": "PA", "_id": "15861"} -{"city": "SUMMERVILLE", "loc": [-79.172583, 41.105822], "pop": 1749, "state": "PA", "_id": "15864"} -{"city": "SYKESVILLE", "loc": [-78.819508, 41.05137], "pop": 1387, "state": "PA", "_id": "15865"} -{"city": "WEEDVILLE", "loc": [-78.495165, 41.268502], "pop": 195, "state": "PA", "_id": "15868"} -{"city": "WILCOX", "loc": [-78.682295, 41.573471], "pop": 1870, "state": "PA", "_id": "15870"} -{"city": "JOHNSTOWN", "loc": [-78.91408, 40.325957], "pop": 6649, "state": "PA", "_id": "15901"} -{"city": "JOHNSTOWN", "loc": [-78.896905, 40.307787], "pop": 16012, "state": "PA", "_id": "15902"} -{"city": "JOHNSTOWN", "loc": [-78.865383, 40.285026], "pop": 16858, "state": "PA", "_id": "15904"} -{"city": "JOHNSTOWN", "loc": [-78.943006, 40.307188], "pop": 22509, "state": "PA", "_id": "15905"} -{"city": "JOHNSTOWN", "loc": [-78.938317, 40.352193], "pop": 13249, "state": "PA", "_id": "15906"} -{"city": "JOHNSTOWN", "loc": [-78.862284, 40.387965], "pop": 7944, "state": "PA", "_id": "15909"} -{"city": "ARMAGH", "loc": [-79.013055, 40.442452], "pop": 2839, "state": "PA", "_id": "15920"} -{"city": "BOLIVAR", "loc": [-79.160546, 40.367289], "pop": 2043, "state": "PA", "_id": "15923"} -{"city": "CAIRNBROOK", "loc": [-78.810082, 40.114472], "pop": 2178, "state": "PA", "_id": "15924"} -{"city": "CENTRAL CITY", "loc": [-78.844753, 40.091257], "pop": 2886, "state": "PA", "_id": "15926"} -{"city": "COLVER", "loc": [-78.778432, 40.541086], "pop": 1531, "state": "PA", "_id": "15927"} -{"city": "DAVIDSVILLE", "loc": [-78.936333, 40.224451], "pop": 244, "state": "PA", "_id": "15928"} -{"city": "EBENSBURG", "loc": [-78.726294, 40.480105], "pop": 9386, "state": "PA", "_id": "15931"} -{"city": "HOLLSOPPLE", "loc": [-78.951471, 40.234301], "pop": 7012, "state": "PA", "_id": "15935"} -{"city": "HOOVERSVILLE", "loc": [-78.914071, 40.148776], "pop": 731, "state": "PA", "_id": "15936"} -{"city": "LILLY", "loc": [-78.62306, 40.423844], "pop": 2252, "state": "PA", "_id": "15938"} -{"city": "LORETTO", "loc": [-78.629357, 40.510484], "pop": 2771, "state": "PA", "_id": "15940"} -{"city": "MINERAL POINT", "loc": [-78.835201, 40.379253], "pop": 420, "state": "PA", "_id": "15942"} -{"city": "NANTY GLO", "loc": [-78.837504, 40.470435], "pop": 4680, "state": "PA", "_id": "15943"} -{"city": "NEW FLORENCE", "loc": [-79.096803, 40.382266], "pop": 3949, "state": "PA", "_id": "15944"} -{"city": "PARKHILL", "loc": [-78.869432, 40.359385], "pop": 120, "state": "PA", "_id": "15945"} -{"city": "PURITAN", "loc": [-78.671753, 40.384201], "pop": 8160, "state": "PA", "_id": "15946"} -{"city": "ROBINSON", "loc": [-79.137928, 40.407655], "pop": 829, "state": "PA", "_id": "15949"} -{"city": "SAINT MICHAEL", "loc": [-78.782999, 40.336248], "pop": 1425, "state": "PA", "_id": "15951"} -{"city": "SALIX", "loc": [-78.743704, 40.300627], "pop": 6215, "state": "PA", "_id": "15952"} -{"city": "SEANOR", "loc": [-78.890897, 40.231191], "pop": 18, "state": "PA", "_id": "15953"} -{"city": "SEWARD", "loc": [-79.023224, 40.409907], "pop": 1348, "state": "PA", "_id": "15954"} -{"city": "SIDMAN", "loc": [-78.745966, 40.329873], "pop": 1235, "state": "PA", "_id": "15955"} -{"city": "SOUTH FORK", "loc": [-78.788671, 40.362877], "pop": 2497, "state": "PA", "_id": "15956"} -{"city": "STRONGSTOWN", "loc": [-78.912961, 40.56279], "pop": 678, "state": "PA", "_id": "15957"} -{"city": "SUMMERHILL", "loc": [-78.755979, 40.388959], "pop": 2215, "state": "PA", "_id": "15958"} -{"city": "TWIN ROCKS", "loc": [-78.860488, 40.518254], "pop": 1535, "state": "PA", "_id": "15960"} -{"city": "VINTONDALE", "loc": [-78.94267, 40.493949], "pop": 2141, "state": "PA", "_id": "15961"} -{"city": "WINDBER", "loc": [-78.830289, 40.228695], "pop": 11421, "state": "PA", "_id": "15963"} -{"city": "BON AIRE", "loc": [-79.902717, 40.862096], "pop": 55158, "state": "PA", "_id": "16001"} -{"city": "BOYERS", "loc": [-79.904692, 41.109205], "pop": 974, "state": "PA", "_id": "16020"} -{"city": "BRUIN", "loc": [-79.729051, 41.057078], "pop": 637, "state": "PA", "_id": "16022"} -{"city": "MARWOOD", "loc": [-79.770851, 40.779723], "pop": 3483, "state": "PA", "_id": "16023"} -{"city": "CHICORA", "loc": [-79.746237, 40.945768], "pop": 4673, "state": "PA", "_id": "16025"} -{"city": "EAST BRADY", "loc": [-79.630187, 40.974373], "pop": 2069, "state": "PA", "_id": "16028"} -{"city": "EAU CLAIRE", "loc": [-79.798093, 41.13478], "pop": 371, "state": "PA", "_id": "16030"} -{"city": "EVANS CITY", "loc": [-80.059195, 40.780795], "pop": 6859, "state": "PA", "_id": "16033"} -{"city": "FENELTON", "loc": [-79.737152, 40.855464], "pop": 1753, "state": "PA", "_id": "16034"} -{"city": "FOXBURG", "loc": [-79.653443, 41.160239], "pop": 988, "state": "PA", "_id": "16036"} -{"city": "HARMONY", "loc": [-80.138117, 40.849646], "pop": 2049, "state": "PA", "_id": "16037"} -{"city": "HARRISVILLE", "loc": [-79.979639, 41.163087], "pop": 3617, "state": "PA", "_id": "16038"} -{"city": "HILLIARDS", "loc": [-79.821456, 41.100894], "pop": 1092, "state": "PA", "_id": "16040"} -{"city": "KARNS CITY", "loc": [-79.716017, 41.002193], "pop": 3032, "state": "PA", "_id": "16041"} -{"city": "LYNDORA", "loc": [-79.921401, 40.855071], "pop": 2232, "state": "PA", "_id": "16045"} -{"city": "MARS", "loc": [-80.035769, 40.700514], "pop": 10192, "state": "PA", "_id": "16046"} -{"city": "PARKER", "loc": [-79.688888, 41.100891], "pop": 3131, "state": "PA", "_id": "16049"} -{"city": "PETROLIA", "loc": [-79.771069, 41.044208], "pop": 491, "state": "PA", "_id": "16050"} -{"city": "PORTERSVILLE", "loc": [-80.172965, 40.948651], "pop": 3642, "state": "PA", "_id": "16051"} -{"city": "PROSPECT", "loc": [-80.067903, 40.904941], "pop": 2224, "state": "PA", "_id": "16052"} -{"city": "RENFREW", "loc": [-79.977004, 40.810003], "pop": 3180, "state": "PA", "_id": "16053"} -{"city": "SARVER", "loc": [-79.74243, 40.714285], "pop": 8370, "state": "PA", "_id": "16055"} -{"city": "SAXONBURG", "loc": [-79.835222, 40.736099], "pop": 4693, "state": "PA", "_id": "16056"} -{"city": "SLIPPERY ROCK", "loc": [-80.046847, 41.045412], "pop": 12443, "state": "PA", "_id": "16057"} -{"city": "VALENCIA", "loc": [-79.923527, 40.701831], "pop": 6437, "state": "PA", "_id": "16059"} -{"city": "WEST SUNBURY", "loc": [-79.875134, 41.002601], "pop": 1999, "state": "PA", "_id": "16061"} -{"city": "ZELIENOPLE", "loc": [-80.128564, 40.73136], "pop": 18300, "state": "PA", "_id": "16063"} -{"city": "NEW CASTLE", "loc": [-80.328449, 40.99222], "pop": 36694, "state": "PA", "_id": "16101"} -{"city": "NEW CASTLE", "loc": [-80.390704, 40.967745], "pop": 7186, "state": "PA", "_id": "16102"} -{"city": "NESHANNOCK", "loc": [-80.342191, 41.033502], "pop": 14292, "state": "PA", "_id": "16105"} -{"city": "ADAMSVILLE", "loc": [-80.376544, 41.506677], "pop": 287, "state": "PA", "_id": "16110"} -{"city": "ATLANTIC", "loc": [-80.286251, 41.533313], "pop": 2390, "state": "PA", "_id": "16111"} -{"city": "BESSEMER", "loc": [-80.493689, 40.975493], "pop": 1611, "state": "PA", "_id": "16112"} -{"city": "CLARKS MILLS", "loc": [-80.175223, 41.385141], "pop": 1351, "state": "PA", "_id": "16114"} -{"city": "DARLINGTON", "loc": [-80.455611, 40.796839], "pop": 3397, "state": "PA", "_id": "16115"} -{"city": "EDINBURG", "loc": [-80.463178, 41.027614], "pop": 3240, "state": "PA", "_id": "16116"} -{"city": "ELLPORT", "loc": [-80.274606, 40.859024], "pop": 19193, "state": "PA", "_id": "16117"} -{"city": "ENON VALLEY", "loc": [-80.461182, 40.8721], "pop": 2421, "state": "PA", "_id": "16120"} -{"city": "FARRELL", "loc": [-80.494442, 41.210995], "pop": 7619, "state": "PA", "_id": "16121"} -{"city": "FOMBELL", "loc": [-80.207312, 40.812527], "pop": 2611, "state": "PA", "_id": "16123"} -{"city": "FREDONIA", "loc": [-80.269871, 41.324141], "pop": 1772, "state": "PA", "_id": "16124"} -{"city": "SHENANGO", "loc": [-80.380344, 41.399403], "pop": 19982, "state": "PA", "_id": "16125"} -{"city": "GROVE CITY", "loc": [-80.084138, 41.160704], "pop": 14568, "state": "PA", "_id": "16127"} -{"city": "HADLEY", "loc": [-80.153544, 41.43302], "pop": 3623, "state": "PA", "_id": "16130"} -{"city": "HARTSTOWN", "loc": [-80.381321, 41.550875], "pop": 406, "state": "PA", "_id": "16131"} -{"city": "JACKSON CENTER", "loc": [-80.103726, 41.280134], "pop": 1959, "state": "PA", "_id": "16133"} -{"city": "WESTFORD", "loc": [-80.456459, 41.506031], "pop": 3328, "state": "PA", "_id": "16134"} -{"city": "MERCER", "loc": [-80.234018, 41.23254], "pop": 11036, "state": "PA", "_id": "16137"} -{"city": "NEW GALILEE", "loc": [-80.393904, 40.856891], "pop": 1852, "state": "PA", "_id": "16141"} -{"city": "NEW WILMINGTON", "loc": [-80.324541, 41.138155], "pop": 9268, "state": "PA", "_id": "16142"} -{"city": "PULASKI", "loc": [-80.468515, 41.094215], "pop": 3898, "state": "PA", "_id": "16143"} -{"city": "SANDY LAKE", "loc": [-80.04974, 41.338337], "pop": 843, "state": "PA", "_id": "16145"} -{"city": "SHARON", "loc": [-80.499342, 41.231552], "pop": 17457, "state": "PA", "_id": "16146"} -{"city": "HERMITAGE", "loc": [-80.45303, 41.232601], "pop": 14573, "state": "PA", "_id": "16148"} -{"city": "SHARPSVILLE", "loc": [-80.465642, 41.267648], "pop": 8962, "state": "PA", "_id": "16150"} -{"city": "STONEBORO", "loc": [-80.097613, 41.34385], "pop": 2249, "state": "PA", "_id": "16153"} -{"city": "TRANSFER", "loc": [-80.419742, 41.324401], "pop": 2662, "state": "PA", "_id": "16154"} -{"city": "VOLANT", "loc": [-80.244129, 41.093767], "pop": 2114, "state": "PA", "_id": "16156"} -{"city": "WAMPUM", "loc": [-80.339184, 40.881879], "pop": 5466, "state": "PA", "_id": "16157"} -{"city": "WEST MIDDLESEX", "loc": [-80.452759, 41.174054], "pop": 5604, "state": "PA", "_id": "16159"} -{"city": "KITTANNING", "loc": [-79.510675, 40.815516], "pop": 19140, "state": "PA", "_id": "16201"} -{"city": "ADRIAN", "loc": [-79.507444, 40.904902], "pop": 726, "state": "PA", "_id": "16210"} -{"city": "CADOGAN", "loc": [-79.579827, 40.75392], "pop": 427, "state": "PA", "_id": "16212"} -{"city": "CALLENSBURG", "loc": [-79.55695, 41.132379], "pop": 688, "state": "PA", "_id": "16213"} -{"city": "CLARION", "loc": [-79.377268, 41.212272], "pop": 8740, "state": "PA", "_id": "16214"} -{"city": "COOKSBURG", "loc": [-79.19708, 41.338366], "pop": 36, "state": "PA", "_id": "16217"} -{"city": "COWANSVILLE", "loc": [-79.594607, 40.922985], "pop": 1089, "state": "PA", "_id": "16218"} -{"city": "DAYTON", "loc": [-79.268551, 40.874101], "pop": 1974, "state": "PA", "_id": "16222"} -{"city": "FAIRMOUNT CITY", "loc": [-79.278405, 41.042861], "pop": 2104, "state": "PA", "_id": "16224"} -{"city": "FISHER", "loc": [-79.247033, 41.266737], "pop": 407, "state": "PA", "_id": "16225"} -{"city": "FORD CITY", "loc": [-79.51222, 40.738407], "pop": 6576, "state": "PA", "_id": "16226"} -{"city": "FREEPORT", "loc": [-79.662991, 40.703277], "pop": 4517, "state": "PA", "_id": "16229"} -{"city": "KNOX", "loc": [-79.519404, 41.224518], "pop": 7489, "state": "PA", "_id": "16232"} -{"city": "LEEPER", "loc": [-79.302179, 41.367074], "pop": 487, "state": "PA", "_id": "16233"} -{"city": "LIMESTONE", "loc": [-79.299292, 41.133396], "pop": 1686, "state": "PA", "_id": "16234"} -{"city": "LUCINDA", "loc": [-79.375372, 41.312377], "pop": 1281, "state": "PA", "_id": "16235"} -{"city": "MC GRANN", "loc": [-79.53086, 40.76929], "pop": 3844, "state": "PA", "_id": "16236"} -{"city": "MANORVILLE", "loc": [-79.521333, 40.786256], "pop": 418, "state": "PA", "_id": "16238"} -{"city": "MARIENVILLE", "loc": [-79.130581, 41.462237], "pop": 1374, "state": "PA", "_id": "16239"} -{"city": "MAYPORT", "loc": [-79.261701, 40.99059], "pop": 1058, "state": "PA", "_id": "16240"} -{"city": "NEW BETHLEHEM", "loc": [-79.352654, 40.9999], "pop": 4698, "state": "PA", "_id": "16242"} -{"city": "HUEY", "loc": [-79.510677, 41.039434], "pop": 3629, "state": "PA", "_id": "16248"} -{"city": "RURAL VALLEY", "loc": [-79.299102, 40.788488], "pop": 3499, "state": "PA", "_id": "16249"} -{"city": "SHIPPENVILLE", "loc": [-79.433199, 41.247491], "pop": 2448, "state": "PA", "_id": "16254"} -{"city": "SLIGO", "loc": [-79.480485, 41.11394], "pop": 1213, "state": "PA", "_id": "16255"} -{"city": "SMICKSBURG", "loc": [-79.161432, 40.837193], "pop": 2236, "state": "PA", "_id": "16256"} -{"city": "STRATTANVILLE", "loc": [-79.308188, 41.195498], "pop": 2086, "state": "PA", "_id": "16258"} -{"city": "TEMPLETON", "loc": [-79.449884, 40.941884], "pop": 1654, "state": "PA", "_id": "16259"} -{"city": "VOWINCKEL", "loc": [-79.274553, 41.378642], "pop": 1615, "state": "PA", "_id": "16260"} -{"city": "CRAIGSVILLE", "loc": [-79.638525, 40.834442], "pop": 3065, "state": "PA", "_id": "16262"} -{"city": "OIL CITY", "loc": [-79.691648, 41.431936], "pop": 19792, "state": "PA", "_id": "16301"} -{"city": "CARLTON", "loc": [-80.020302, 41.481541], "pop": 282, "state": "PA", "_id": "16311"} -{"city": "CLARENDON", "loc": [-79.171949, 41.730224], "pop": 422, "state": "PA", "_id": "16313"} -{"city": "COCHRANTON", "loc": [-80.057269, 41.520487], "pop": 3010, "state": "PA", "_id": "16314"} -{"city": "CONNEAUT LAKE", "loc": [-80.308567, 41.6189], "pop": 3849, "state": "PA", "_id": "16316"} -{"city": "COOPERSTOWN", "loc": [-79.875676, 41.497998], "pop": 506, "state": "PA", "_id": "16317"} -{"city": "CRANBERRY", "loc": [-79.719121, 41.337184], "pop": 353, "state": "PA", "_id": "16319"} -{"city": "EAST HICKORY", "loc": [-79.385483, 41.5691], "pop": 282, "state": "PA", "_id": "16321"} -{"city": "FRANKLIN", "loc": [-79.83089, 41.404775], "pop": 18357, "state": "PA", "_id": "16323"} -{"city": "FRYBURG", "loc": [-79.413401, 41.371736], "pop": 1880, "state": "PA", "_id": "16326"} -{"city": "GUYS MILLS", "loc": [-79.971437, 41.633265], "pop": 2825, "state": "PA", "_id": "16327"} -{"city": "IRVINE", "loc": [-79.286303, 41.843283], "pop": 479, "state": "PA", "_id": "16329"} -{"city": "KOSSUTH", "loc": [-79.588249, 41.290215], "pop": 43, "state": "PA", "_id": "16331"} -{"city": "LICKINGVILLE", "loc": [-79.371516, 41.3789], "pop": 22, "state": "PA", "_id": "16332"} -{"city": "LUDLOW", "loc": [-78.924345, 41.728409], "pop": 612, "state": "PA", "_id": "16333"} -{"city": "MARBLE", "loc": [-79.445929, 41.326077], "pop": 27, "state": "PA", "_id": "16334"} -{"city": "MEADVILLE", "loc": [-80.148787, 41.633847], "pop": 31290, "state": "PA", "_id": "16335"} -{"city": "PITTSFIELD", "loc": [-79.419619, 41.836629], "pop": 3172, "state": "PA", "_id": "16340"} -{"city": "PLEASANTVILLE", "loc": [-79.5685, 41.586696], "pop": 2211, "state": "PA", "_id": "16341"} -{"city": "POLK", "loc": [-79.93461, 41.358315], "pop": 3159, "state": "PA", "_id": "16342"} -{"city": "RUSSELL", "loc": [-79.127079, 41.946106], "pop": 3833, "state": "PA", "_id": "16345"} -{"city": "SENECA", "loc": [-79.675902, 41.374436], "pop": 3723, "state": "PA", "_id": "16346"} -{"city": "SHEFFIELD", "loc": [-79.034814, 41.70053], "pop": 2382, "state": "PA", "_id": "16347"} -{"city": "SUGAR GROVE", "loc": [-79.318609, 41.947542], "pop": 3592, "state": "PA", "_id": "16350"} -{"city": "TIDIOUTE", "loc": [-79.375224, 41.703008], "pop": 2458, "state": "PA", "_id": "16351"} -{"city": "TIONESTA", "loc": [-79.366332, 41.511616], "pop": 2293, "state": "PA", "_id": "16353"} -{"city": "TITUSVILLE", "loc": [-79.685494, 41.638163], "pop": 14509, "state": "PA", "_id": "16354"} -{"city": "TOWNVILLE", "loc": [-79.876679, 41.685581], "pop": 1047, "state": "PA", "_id": "16360"} -{"city": "UTICA", "loc": [-79.940292, 41.479848], "pop": 1863, "state": "PA", "_id": "16362"} -{"city": "VENUS", "loc": [-79.504765, 41.376113], "pop": 534, "state": "PA", "_id": "16364"} -{"city": "NORTH WARREN", "loc": [-79.14286, 41.845265], "pop": 22507, "state": "PA", "_id": "16365"} -{"city": "YOUNGSVILLE", "loc": [-79.318708, 41.853654], "pop": 1853, "state": "PA", "_id": "16371"} -{"city": "CLINTONVILLE", "loc": [-79.87338, 41.20022], "pop": 525, "state": "PA", "_id": "16372"} -{"city": "EMLENTON", "loc": [-79.746996, 41.202769], "pop": 3119, "state": "PA", "_id": "16373"} -{"city": "KENNERDELL", "loc": [-79.739313, 41.284762], "pop": 1318, "state": "PA", "_id": "16374"} -{"city": "LUNDYS LANE", "loc": [-80.375273, 41.885882], "pop": 4286, "state": "PA", "_id": "16401"} -{"city": "BEAR LAKE", "loc": [-79.461365, 41.97006], "pop": 1042, "state": "PA", "_id": "16402"} -{"city": "CAMBRIDGE SPRING", "loc": [-80.028003, 41.794611], "pop": 5770, "state": "PA", "_id": "16403"} -{"city": "CENTERVILLE", "loc": [-79.79004, 41.724316], "pop": 2119, "state": "PA", "_id": "16404"} -{"city": "COLUMBUS", "loc": [-79.573073, 41.938152], "pop": 1751, "state": "PA", "_id": "16405"} -{"city": "CONNEAUTVILLE", "loc": [-80.344516, 41.745455], "pop": 2542, "state": "PA", "_id": "16406"} -{"city": "CORRY", "loc": [-79.656742, 41.922593], "pop": 10654, "state": "PA", "_id": "16407"} -{"city": "CRANESVILLE", "loc": [-80.308528, 41.916222], "pop": 1783, "state": "PA", "_id": "16410"} -{"city": "EAST SPRINGFIELD", "loc": [-80.430336, 41.979363], "pop": 1318, "state": "PA", "_id": "16411"} -{"city": "EDINBORO", "loc": [-80.135604, 41.875629], "pop": 12914, "state": "PA", "_id": "16412"} -{"city": "FAIRVIEW", "loc": [-80.239508, 42.040741], "pop": 7876, "state": "PA", "_id": "16415"} -{"city": "GIRARD", "loc": [-80.317756, 41.989573], "pop": 8190, "state": "PA", "_id": "16417"} -{"city": "GRAND VALLEY", "loc": [-79.546944, 41.77315], "pop": 59, "state": "PA", "_id": "16420"} -{"city": "HARBORCREEK", "loc": [-79.941648, 42.176719], "pop": 3533, "state": "PA", "_id": "16421"} -{"city": "LAKE CITY", "loc": [-80.338834, 42.020361], "pop": 3674, "state": "PA", "_id": "16423"} -{"city": "ESPYVILLE", "loc": [-80.426911, 41.663535], "pop": 5520, "state": "PA", "_id": "16424"} -{"city": "MC KEAN", "loc": [-80.147336, 41.999035], "pop": 4099, "state": "PA", "_id": "16426"} -{"city": "NORTH EAST", "loc": [-79.833179, 42.200793], "pop": 12531, "state": "PA", "_id": "16428"} -{"city": "SAEGERTOWN", "loc": [-80.147857, 41.726753], "pop": 5094, "state": "PA", "_id": "16433"} -{"city": "SPARTANSBURG", "loc": [-79.684916, 41.793648], "pop": 2785, "state": "PA", "_id": "16434"} -{"city": "SPRINGBORO", "loc": [-80.375276, 41.811348], "pop": 2131, "state": "PA", "_id": "16435"} -{"city": "SPRING CREEK", "loc": [-79.56554, 41.846832], "pop": 191, "state": "PA", "_id": "16436"} -{"city": "UNION CITY", "loc": [-79.845464, 41.893851], "pop": 8890, "state": "PA", "_id": "16438"} -{"city": "VENANGO", "loc": [-80.125353, 41.791968], "pop": 918, "state": "PA", "_id": "16440"} -{"city": "WATERFORD", "loc": [-79.99963, 41.960266], "pop": 9247, "state": "PA", "_id": "16441"} -{"city": "WATTSBURG", "loc": [-79.836282, 42.039114], "pop": 2463, "state": "PA", "_id": "16442"} -{"city": "WEST SPRINGFIELD", "loc": [-80.46501, 41.94646], "pop": 1375, "state": "PA", "_id": "16443"} -{"city": "ERIE", "loc": [-80.08601, 42.125962], "pop": 1956, "state": "PA", "_id": "16501"} -{"city": "ERIE", "loc": [-80.097607, 42.113332], "pop": 18640, "state": "PA", "_id": "16502"} -{"city": "ERIE", "loc": [-80.063976, 42.126506], "pop": 19019, "state": "PA", "_id": "16503"} -{"city": "ERIE", "loc": [-80.05208, 42.1108], "pop": 17680, "state": "PA", "_id": "16504"} -{"city": "PRESQUE ISLE", "loc": [-80.161902, 42.097526], "pop": 17753, "state": "PA", "_id": "16505"} -{"city": "ERIE", "loc": [-80.14844, 42.073801], "pop": 19269, "state": "PA", "_id": "16506"} -{"city": "ERIE", "loc": [-80.086424, 42.131579], "pop": 10936, "state": "PA", "_id": "16507"} -{"city": "ERIE", "loc": [-80.093544, 42.097577], "pop": 17645, "state": "PA", "_id": "16508"} -{"city": "ERIE", "loc": [-80.066827, 42.076326], "pop": 24232, "state": "PA", "_id": "16509"} -{"city": "WESLEYVILLE", "loc": [-80.003752, 42.123673], "pop": 26455, "state": "PA", "_id": "16510"} -{"city": "ERIE", "loc": [-80.017665, 42.15529], "pop": 11355, "state": "PA", "_id": "16511"} -{"city": "ERIE", "loc": [-80.10011, 42.0687], "pop": 97, "state": "PA", "_id": "16565"} -{"city": "ALTOONA", "loc": [-78.408901, 40.520945], "pop": 31560, "state": "PA", "_id": "16601"} -{"city": "ALTOONA", "loc": [-78.390533, 40.50524], "pop": 32649, "state": "PA", "_id": "16602"} -{"city": "BARREE", "loc": [-78.107066, 40.549901], "pop": 2765, "state": "PA", "_id": "16611"} -{"city": "ASHVILLE", "loc": [-78.534639, 40.551266], "pop": 1275, "state": "PA", "_id": "16613"} -{"city": "BECCARIA", "loc": [-78.508036, 40.757938], "pop": 1571, "state": "PA", "_id": "16616"} -{"city": "BELLWOOD", "loc": [-78.337234, 40.60394], "pop": 8152, "state": "PA", "_id": "16617"} -{"city": "BRISBIN", "loc": [-78.352634, 40.838711], "pop": 369, "state": "PA", "_id": "16620"} -{"city": "BROAD TOP", "loc": [-78.1406, 40.201891], "pop": 331, "state": "PA", "_id": "16621"} -{"city": "CALVIN", "loc": [-78.023697, 40.298667], "pop": 1190, "state": "PA", "_id": "16622"} -{"city": "CASSVILLE", "loc": [-78.027178, 40.293977], "pop": 183, "state": "PA", "_id": "16623"} -{"city": "CLAYSBURG", "loc": [-78.479658, 40.329243], "pop": 3890, "state": "PA", "_id": "16625"} -{"city": "COALPORT", "loc": [-78.535238, 40.750323], "pop": 940, "state": "PA", "_id": "16627"} -{"city": "CRESSON", "loc": [-78.586068, 40.460779], "pop": 5829, "state": "PA", "_id": "16630"} -{"city": "DUDLEY", "loc": [-78.183823, 40.219453], "pop": 616, "state": "PA", "_id": "16634"} -{"city": "DUNCANSVILLE", "loc": [-78.43833, 40.426228], "pop": 13889, "state": "PA", "_id": "16635"} -{"city": "DYSART", "loc": [-78.527072, 40.60885], "pop": 769, "state": "PA", "_id": "16636"} -{"city": "EAST FREEDOM", "loc": [-78.447519, 40.328197], "pop": 2871, "state": "PA", "_id": "16637"} -{"city": "FALLENTIMBER", "loc": [-78.465912, 40.671924], "pop": 684, "state": "PA", "_id": "16639"} -{"city": "FLINTON", "loc": [-78.48149, 40.701615], "pop": 1589, "state": "PA", "_id": "16640"} -{"city": "GALLITZIN", "loc": [-78.555435, 40.48772], "pop": 2852, "state": "PA", "_id": "16641"} -{"city": "GLEN HOPE", "loc": [-78.499869, 40.798405], "pop": 187, "state": "PA", "_id": "16645"} -{"city": "HASTINGS", "loc": [-78.702924, 40.665874], "pop": 2616, "state": "PA", "_id": "16646"} -{"city": "HESSTON", "loc": [-78.128109, 40.412139], "pop": 956, "state": "PA", "_id": "16647"} -{"city": "HOLLIDAYSBURG", "loc": [-78.368627, 40.438727], "pop": 12867, "state": "PA", "_id": "16648"} -{"city": "HOPEWELL", "loc": [-78.312897, 40.119225], "pop": 2440, "state": "PA", "_id": "16650"} -{"city": "HOUTZDALE", "loc": [-78.3618, 40.830538], "pop": 2825, "state": "PA", "_id": "16651"} -{"city": "HUNTINGDON", "loc": [-78.005028, 40.502274], "pop": 16288, "state": "PA", "_id": "16652"} -{"city": "IMLER", "loc": [-78.516719, 40.23186], "pop": 3037, "state": "PA", "_id": "16655"} -{"city": "IRVONA", "loc": [-78.560243, 40.801744], "pop": 1183, "state": "PA", "_id": "16656"} -{"city": "JAMES CREEK", "loc": [-78.188678, 40.356672], "pop": 485, "state": "PA", "_id": "16657"} -{"city": "LOYSBURG", "loc": [-78.386435, 40.174577], "pop": 656, "state": "PA", "_id": "16659"} -{"city": "MADERA", "loc": [-78.427475, 40.827086], "pop": 1389, "state": "PA", "_id": "16661"} -{"city": "MARTINSBURG", "loc": [-78.324367, 40.295082], "pop": 5016, "state": "PA", "_id": "16662"} -{"city": "NEW ENTERPRISE", "loc": [-78.425916, 40.200013], "pop": 1898, "state": "PA", "_id": "16664"} -{"city": "OSCEOLA MILLS", "loc": [-78.275705, 40.872208], "pop": 4346, "state": "PA", "_id": "16666"} -{"city": "ST CLAIRSVILLE", "loc": [-78.509928, 40.158583], "pop": 174, "state": "PA", "_id": "16667"} -{"city": "PATTON", "loc": [-78.635028, 40.623045], "pop": 4737, "state": "PA", "_id": "16668"} -{"city": "PETERSBURG", "loc": [-77.998402, 40.602968], "pop": 2175, "state": "PA", "_id": "16669"} -{"city": "RAMEY", "loc": [-78.399821, 40.801511], "pop": 538, "state": "PA", "_id": "16671"} -{"city": "ROARING SPRING", "loc": [-78.39284, 40.327747], "pop": 4942, "state": "PA", "_id": "16673"} -{"city": "ROBERTSDALE", "loc": [-78.111696, 40.178674], "pop": 727, "state": "PA", "_id": "16674"} -{"city": "SAXTON", "loc": [-78.247137, 40.223301], "pop": 3031, "state": "PA", "_id": "16678"} -{"city": "SIX MILE RUN", "loc": [-78.210814, 40.157583], "pop": 1670, "state": "PA", "_id": "16679"} -{"city": "SMITHMILL", "loc": [-78.399442, 40.767808], "pop": 1192, "state": "PA", "_id": "16680"} -{"city": "SPRUCE CREEK", "loc": [-78.136083, 40.621767], "pop": 281, "state": "PA", "_id": "16683"} -{"city": "TODD", "loc": [-78.100354, 40.25775], "pop": 889, "state": "PA", "_id": "16685"} -{"city": "TYRONE", "loc": [-78.241905, 40.661905], "pop": 11290, "state": "PA", "_id": "16686"} -{"city": "WATERFALL", "loc": [-78.047708, 40.073871], "pop": 1168, "state": "PA", "_id": "16689"} -{"city": "WELLS TANNERY", "loc": [-78.140269, 40.100996], "pop": 563, "state": "PA", "_id": "16691"} -{"city": "WESTOVER", "loc": [-78.735481, 40.76152], "pop": 1550, "state": "PA", "_id": "16692"} -{"city": "GANISTER", "loc": [-78.22555, 40.437356], "pop": 4799, "state": "PA", "_id": "16693"} -{"city": "WOODBURY", "loc": [-78.366573, 40.21847], "pop": 741, "state": "PA", "_id": "16695"} -{"city": "BRADFORD", "loc": [-78.653967, 41.954678], "pop": 18738, "state": "PA", "_id": "16701"} -{"city": "AUSTIN", "loc": [-78.090812, 41.629649], "pop": 1123, "state": "PA", "_id": "16720"} -{"city": "CROSBY", "loc": [-78.374637, 41.713356], "pop": 593, "state": "PA", "_id": "16724"} -{"city": "ORMSBY", "loc": [-78.566743, 41.826327], "pop": 369, "state": "PA", "_id": "16726"} -{"city": "DERRICK CITY", "loc": [-78.562564, 41.972577], "pop": 623, "state": "PA", "_id": "16727"} -{"city": "DUKE CENTER", "loc": [-78.492269, 41.954017], "pop": 1122, "state": "PA", "_id": "16729"} -{"city": "ELDRED", "loc": [-78.388439, 41.948925], "pop": 2616, "state": "PA", "_id": "16731"} -{"city": "GIFFORD", "loc": [-78.584604, 41.860715], "pop": 586, "state": "PA", "_id": "16732"} -{"city": "JAMES CITY", "loc": [-78.85052, 41.593116], "pop": 551, "state": "PA", "_id": "16734"} -{"city": "KANE", "loc": [-78.797778, 41.661861], "pop": 6600, "state": "PA", "_id": "16735"} -{"city": "LEWIS RUN", "loc": [-78.680498, 41.821123], "pop": 2617, "state": "PA", "_id": "16738"} -{"city": "MOUNT JEWETT", "loc": [-78.644613, 41.724737], "pop": 1039, "state": "PA", "_id": "16740"} -{"city": "PORT ALLEGANY", "loc": [-78.279909, 41.816919], "pop": 4468, "state": "PA", "_id": "16743"} -{"city": "REW", "loc": [-78.535406, 41.912222], "pop": 614, "state": "PA", "_id": "16744"} -{"city": "RIXFORD", "loc": [-78.458647, 41.934606], "pop": 514, "state": "PA", "_id": "16745"} -{"city": "ROULETTE", "loc": [-78.153843, 41.773795], "pop": 1354, "state": "PA", "_id": "16746"} -{"city": "SHINGLEHOUSE", "loc": [-78.19062, 41.957176], "pop": 3390, "state": "PA", "_id": "16748"} -{"city": "SMETHPORT", "loc": [-78.470229, 41.802063], "pop": 4375, "state": "PA", "_id": "16749"} -{"city": "TURTLEPOINT", "loc": [-78.330793, 41.884665], "pop": 720, "state": "PA", "_id": "16750"} -{"city": "STATE COLLEGE", "loc": [-77.852279, 40.792522], "pop": 42278, "state": "PA", "_id": "16801"} -{"city": "STATE COLLEGE", "loc": [-77.892578, 40.808162], "pop": 20669, "state": "PA", "_id": "16803"} -{"city": "AARONSBURG", "loc": [-77.387977, 40.876944], "pop": 100, "state": "PA", "_id": "16820"} -{"city": "ALLPORT", "loc": [-78.21038, 40.975039], "pop": 275, "state": "PA", "_id": "16821"} -{"city": "BEECH CREEK", "loc": [-77.585118, 41.084507], "pop": 1723, "state": "PA", "_id": "16822"} -{"city": "PLEASANT GAP", "loc": [-77.7642, 40.909377], "pop": 23418, "state": "PA", "_id": "16823"} -{"city": "BOALSBURG", "loc": [-77.782236, 40.779344], "pop": 3787, "state": "PA", "_id": "16827"} -{"city": "CENTRE HALL", "loc": [-77.674225, 40.825429], "pop": 4223, "state": "PA", "_id": "16828"} -{"city": "CLARENCE", "loc": [-77.931213, 41.058482], "pop": 123, "state": "PA", "_id": "16829"} -{"city": "CLEARFIELD", "loc": [-78.443488, 41.02103], "pop": 14648, "state": "PA", "_id": "16830"} -{"city": "COBURN", "loc": [-77.492173, 40.867818], "pop": 836, "state": "PA", "_id": "16832"} -{"city": "CURWENSVILLE", "loc": [-78.527247, 40.965972], "pop": 4085, "state": "PA", "_id": "16833"} -{"city": "FRENCHVILLE", "loc": [-78.234465, 41.103794], "pop": 1278, "state": "PA", "_id": "16836"} -{"city": "GLEN RICHEY", "loc": [-78.475215, 40.938209], "pop": 331, "state": "PA", "_id": "16837"} -{"city": "GRAMPIAN", "loc": [-78.594913, 40.981768], "pop": 3187, "state": "PA", "_id": "16838"} -{"city": "GRASSFLAT", "loc": [-78.128354, 40.995881], "pop": 2208, "state": "PA", "_id": "16839"} -{"city": "HAWK RUN", "loc": [-78.213787, 40.941215], "pop": 2224, "state": "PA", "_id": "16840"} -{"city": "HOWARD", "loc": [-77.670178, 41.020315], "pop": 3723, "state": "PA", "_id": "16841"} -{"city": "JULIAN", "loc": [-77.933243, 40.891709], "pop": 2461, "state": "PA", "_id": "16844"} -{"city": "KARTHAUS", "loc": [-78.087509, 41.113635], "pop": 932, "state": "PA", "_id": "16845"} -{"city": "MADISONBURG", "loc": [-77.494959, 40.933407], "pop": 678, "state": "PA", "_id": "16852"} -{"city": "MILLHEIM", "loc": [-77.450531, 40.896314], "pop": 1978, "state": "PA", "_id": "16854"} -{"city": "MORRISDALE", "loc": [-78.235717, 41.000128], "pop": 1235, "state": "PA", "_id": "16858"} -{"city": "MOSHANNON", "loc": [-78.009469, 41.03419], "pop": 516, "state": "PA", "_id": "16859"} -{"city": "MUNSON", "loc": [-78.18621, 40.966704], "pop": 110, "state": "PA", "_id": "16860"} -{"city": "NEW MILLPORT", "loc": [-78.494543, 40.885302], "pop": 657, "state": "PA", "_id": "16861"} -{"city": "OLANTA", "loc": [-78.500079, 40.905621], "pop": 47, "state": "PA", "_id": "16863"} -{"city": "ORVISTON", "loc": [-77.620306, 41.075386], "pop": 1023, "state": "PA", "_id": "16864"} -{"city": "PENNSYLVANIA FUR", "loc": [-77.954068, 40.728194], "pop": 2558, "state": "PA", "_id": "16865"} -{"city": "PHILIPSBURG", "loc": [-78.219008, 40.886252], "pop": 7810, "state": "PA", "_id": "16866"} -{"city": "PORT MATILDA", "loc": [-78.078795, 40.801819], "pop": 1890, "state": "PA", "_id": "16870"} -{"city": "POTTERSDALE", "loc": [-78.034056, 41.186798], "pop": 34, "state": "PA", "_id": "16871"} -{"city": "REBERSBURG", "loc": [-77.405322, 40.954906], "pop": 816, "state": "PA", "_id": "16872"} -{"city": "SNOW SHOE", "loc": [-77.95228, 41.037581], "pop": 1917, "state": "PA", "_id": "16874"} -{"city": "SPRING MILLS", "loc": [-77.574031, 40.857753], "pop": 1805, "state": "PA", "_id": "16875"} -{"city": "WARRIORS MARK", "loc": [-78.077478, 40.741414], "pop": 2760, "state": "PA", "_id": "16877"} -{"city": "WEST DECATUR", "loc": [-78.312936, 40.949305], "pop": 2226, "state": "PA", "_id": "16878"} -{"city": "WINBURNE", "loc": [-78.156235, 40.967779], "pop": 387, "state": "PA", "_id": "16879"} -{"city": "WOODLAND", "loc": [-78.321445, 41.009833], "pop": 2571, "state": "PA", "_id": "16881"} -{"city": "WOODWARD", "loc": [-77.348269, 40.911574], "pop": 183, "state": "PA", "_id": "16882"} -{"city": "WELLSBORO", "loc": [-77.30802, 41.737343], "pop": 9906, "state": "PA", "_id": "16901"} -{"city": "BLOSSBURG", "loc": [-77.079711, 41.669771], "pop": 2123, "state": "PA", "_id": "16912"} -{"city": "COLUMBIA CROSS R", "loc": [-76.793242, 41.846282], "pop": 2398, "state": "PA", "_id": "16914"} -{"city": "OSWAYO", "loc": [-78.003861, 41.781529], "pop": 5171, "state": "PA", "_id": "16915"} -{"city": "COVINGTON", "loc": [-77.108795, 41.739297], "pop": 1417, "state": "PA", "_id": "16917"} -{"city": "ELKLAND", "loc": [-77.313392, 41.988165], "pop": 1910, "state": "PA", "_id": "16920"} -{"city": "GAINES", "loc": [-77.568001, 41.747134], "pop": 544, "state": "PA", "_id": "16921"} -{"city": "GALETON", "loc": [-77.654756, 41.723006], "pop": 2050, "state": "PA", "_id": "16922"} -{"city": "NORTH BINGHAM", "loc": [-77.873585, 41.935312], "pop": 1585, "state": "PA", "_id": "16923"} -{"city": "GILLETT", "loc": [-76.771329, 41.956826], "pop": 4273, "state": "PA", "_id": "16925"} -{"city": "GRANVILLE SUMMIT", "loc": [-76.721829, 41.697299], "pop": 1447, "state": "PA", "_id": "16926"} -{"city": "HARRISON VALLEY", "loc": [-77.687665, 41.949824], "pop": 1476, "state": "PA", "_id": "16927"} -{"city": "KNOXVILLE", "loc": [-77.435678, 41.959557], "pop": 893, "state": "PA", "_id": "16928"} -{"city": "LAWRENCEVILLE", "loc": [-77.11355, 41.978266], "pop": 1915, "state": "PA", "_id": "16929"} -{"city": "LIBERTY", "loc": [-77.119505, 41.565571], "pop": 893, "state": "PA", "_id": "16930"} -{"city": "MAINESBURG", "loc": [-76.968156, 41.790029], "pop": 1140, "state": "PA", "_id": "16932"} -{"city": "MANSFIELD", "loc": [-77.07163, 41.812288], "pop": 7111, "state": "PA", "_id": "16933"} -{"city": "MIDDLEBURY CENTE", "loc": [-77.314764, 41.891706], "pop": 2549, "state": "PA", "_id": "16935"} -{"city": "MILLERTON", "loc": [-76.974766, 41.962467], "pop": 2072, "state": "PA", "_id": "16936"} -{"city": "MILLS", "loc": [-77.762051, 41.902482], "pop": 653, "state": "PA", "_id": "16937"} -{"city": "MORRIS", "loc": [-77.291975, 41.54752], "pop": 959, "state": "PA", "_id": "16938"} -{"city": "MORRIS RUN", "loc": [-77.027769, 41.672943], "pop": 496, "state": "PA", "_id": "16939"} -{"city": "NELSON", "loc": [-77.241909, 41.978719], "pop": 599, "state": "PA", "_id": "16940"} -{"city": "GENESEE", "loc": [-77.773995, 41.981963], "pop": 47, "state": "PA", "_id": "16941"} -{"city": "OSCEOLA", "loc": [-77.353983, 41.984765], "pop": 609, "state": "PA", "_id": "16942"} -{"city": "SABINSVILLE", "loc": [-77.537825, 41.856414], "pop": 576, "state": "PA", "_id": "16943"} -{"city": "TIOGA", "loc": [-77.139294, 41.912454], "pop": 1796, "state": "PA", "_id": "16946"} -{"city": "TROY", "loc": [-76.771143, 41.77815], "pop": 3476, "state": "PA", "_id": "16947"} -{"city": "ULYSSES", "loc": [-77.712557, 41.845903], "pop": 727, "state": "PA", "_id": "16948"} -{"city": "LITTLE MARSH", "loc": [-77.530975, 41.919063], "pop": 2844, "state": "PA", "_id": "16950"} -{"city": "ALLENSVILLE", "loc": [-77.829396, 40.524921], "pop": 1135, "state": "PA", "_id": "17002"} -{"city": "ANNVILLE", "loc": [-76.544676, 40.345608], "pop": 12173, "state": "PA", "_id": "17003"} -{"city": "BELLEVILLE", "loc": [-77.735823, 40.601571], "pop": 4203, "state": "PA", "_id": "17004"} -{"city": "BERRYSBURG", "loc": [-76.811207, 40.60199], "pop": 376, "state": "PA", "_id": "17005"} -{"city": "BLAIN", "loc": [-77.511736, 40.329314], "pop": 755, "state": "PA", "_id": "17006"} -{"city": "BOILING SPRINGS", "loc": [-77.119489, 40.144873], "pop": 4331, "state": "PA", "_id": "17007"} -{"city": "BURNHAM", "loc": [-77.562459, 40.636119], "pop": 2005, "state": "PA", "_id": "17009"} -{"city": "SHIREMANSTOWN", "loc": [-76.929111, 40.238071], "pop": 33023, "state": "PA", "_id": "17011"} -{"city": "CARLISLE BARRACK", "loc": [-77.199526, 40.203877], "pop": 50939, "state": "PA", "_id": "17013"} -{"city": "COCOLAMUS", "loc": [-77.106749, 40.656706], "pop": 887, "state": "PA", "_id": "17014"} -{"city": "DALMATIA", "loc": [-76.879713, 40.648315], "pop": 1694, "state": "PA", "_id": "17017"} -{"city": "DAUPHIN", "loc": [-76.928304, 40.384581], "pop": 4523, "state": "PA", "_id": "17018"} -{"city": "DILLSBURG", "loc": [-77.03387, 40.096373], "pop": 11814, "state": "PA", "_id": "17019"} -{"city": "DUNCANNON", "loc": [-77.047254, 40.408678], "pop": 10021, "state": "PA", "_id": "17020"} -{"city": "EAST WATERFORD", "loc": [-77.652789, 40.354191], "pop": 929, "state": "PA", "_id": "17021"} -{"city": "ELIZABETHTOWN", "loc": [-76.602545, 40.155331], "pop": 21808, "state": "PA", "_id": "17022"} -{"city": "ELIZABETHVILLE", "loc": [-76.835484, 40.55497], "pop": 4564, "state": "PA", "_id": "17023"} -{"city": "ELLIOTTSBURG", "loc": [-77.270348, 40.362428], "pop": 1125, "state": "PA", "_id": "17024"} -{"city": "ENOLA", "loc": [-76.943208, 40.292178], "pop": 13103, "state": "PA", "_id": "17025"} -{"city": "FREDERICKSBURG", "loc": [-76.42674, 40.452392], "pop": 2382, "state": "PA", "_id": "17026"} -{"city": "GRANTVILLE", "loc": [-76.671331, 40.360629], "pop": 4569, "state": "PA", "_id": "17028"} -{"city": "GRANVILLE", "loc": [-77.613358, 40.547868], "pop": 1293, "state": "PA", "_id": "17029"} -{"city": "GRATZ", "loc": [-76.718851, 40.610424], "pop": 696, "state": "PA", "_id": "17030"} -{"city": "GREEN PARK", "loc": [-77.320096, 40.375143], "pop": 347, "state": "PA", "_id": "17031"} -{"city": "HALIFAX", "loc": [-76.89404, 40.47603], "pop": 7648, "state": "PA", "_id": "17032"} -{"city": "HERSHEY", "loc": [-76.654518, 40.263767], "pop": 20514, "state": "PA", "_id": "17033"} -{"city": "HIGHSPIRE", "loc": [-76.785303, 40.208348], "pop": 2670, "state": "PA", "_id": "17034"} -{"city": "HONEY GROVE", "loc": [-77.57607, 40.430903], "pop": 884, "state": "PA", "_id": "17035"} -{"city": "HUMMELSTOWN", "loc": [-76.709375, 40.278199], "pop": 8178, "state": "PA", "_id": "17036"} -{"city": "ICKESBURG", "loc": [-77.34291, 40.434154], "pop": 1828, "state": "PA", "_id": "17037"} -{"city": "JONESTOWN", "loc": [-76.503842, 40.43607], "pop": 5098, "state": "PA", "_id": "17038"} -{"city": "LANDISBURG", "loc": [-77.319146, 40.332644], "pop": 1140, "state": "PA", "_id": "17040"} -{"city": "CLEONA", "loc": [-76.425895, 40.335912], "pop": 61993, "state": "PA", "_id": "17042"} -{"city": "WORMLEYSBURG", "loc": [-76.89757, 40.247158], "pop": 5340, "state": "PA", "_id": "17043"} -{"city": "LEWISTOWN", "loc": [-77.57558, 40.599439], "pop": 19311, "state": "PA", "_id": "17044"} -{"city": "LIVERPOOL", "loc": [-77.008327, 40.575272], "pop": 3951, "state": "PA", "_id": "17045"} -{"city": "LOYSVILLE", "loc": [-77.413823, 40.36576], "pop": 1850, "state": "PA", "_id": "17047"} -{"city": "LYKENS", "loc": [-76.70736, 40.590919], "pop": 2904, "state": "PA", "_id": "17048"} -{"city": "MC ALISTERVILLE", "loc": [-77.2602, 40.646916], "pop": 3073, "state": "PA", "_id": "17049"} -{"city": "MC VEYTOWN", "loc": [-77.718625, 40.504593], "pop": 3685, "state": "PA", "_id": "17051"} -{"city": "MAPLETON DEPOT", "loc": [-77.960444, 40.386414], "pop": 1329, "state": "PA", "_id": "17052"} -{"city": "MARYSVILLE", "loc": [-76.972204, 40.335062], "pop": 4561, "state": "PA", "_id": "17053"} -{"city": "HAMPDEN", "loc": [-76.99493, 40.212669], "pop": 51902, "state": "PA", "_id": "17055"} -{"city": "MIDDLETOWN", "loc": [-76.733127, 40.204086], "pop": 21545, "state": "PA", "_id": "17057"} -{"city": "MIFFLIN", "loc": [-77.41314, 40.570842], "pop": 1189, "state": "PA", "_id": "17058"} -{"city": "MIFFLINTOWN", "loc": [-77.376119, 40.572666], "pop": 5646, "state": "PA", "_id": "17059"} -{"city": "MILL CREEK", "loc": [-77.917689, 40.447102], "pop": 1002, "state": "PA", "_id": "17060"} -{"city": "MILLERSBURG", "loc": [-76.930483, 40.558743], "pop": 6135, "state": "PA", "_id": "17061"} -{"city": "MILLERSTOWN", "loc": [-77.129776, 40.550548], "pop": 1589, "state": "PA", "_id": "17062"} -{"city": "MILROY", "loc": [-77.556739, 40.72033], "pop": 3627, "state": "PA", "_id": "17063"} -{"city": "MOUNT HOLLY SPRI", "loc": [-77.190807, 40.118356], "pop": 2857, "state": "PA", "_id": "17065"} -{"city": "MOUNT UNION", "loc": [-77.863704, 40.390106], "pop": 7218, "state": "PA", "_id": "17066"} -{"city": "MYERSTOWN", "loc": [-76.314328, 40.378949], "pop": 12843, "state": "PA", "_id": "17067"} -{"city": "NEW BLOOMFIELD", "loc": [-77.193836, 40.419325], "pop": 3066, "state": "PA", "_id": "17068"} -{"city": "NEW CUMBERLAND", "loc": [-76.868909, 40.215105], "pop": 15037, "state": "PA", "_id": "17070"} -{"city": "NEW GERMANTOWN", "loc": [-77.579701, 40.305749], "pop": 455, "state": "PA", "_id": "17071"} -{"city": "NEWMANSTOWN", "loc": [-76.2426, 40.317938], "pop": 3560, "state": "PA", "_id": "17073"} -{"city": "NEWPORT", "loc": [-77.165866, 40.482662], "pop": 6378, "state": "PA", "_id": "17074"} -{"city": "OAKLAND MILLS", "loc": [-77.319244, 40.614748], "pop": 457, "state": "PA", "_id": "17076"} -{"city": "PALMYRA", "loc": [-76.58861, 40.301055], "pop": 14239, "state": "PA", "_id": "17078"} -{"city": "PORT ROYAL", "loc": [-77.430958, 40.51068], "pop": 3319, "state": "PA", "_id": "17082"} -{"city": "REEDSVILLE", "loc": [-77.611589, 40.672189], "pop": 3320, "state": "PA", "_id": "17084"} -{"city": "RICHFIELD", "loc": [-77.122296, 40.688424], "pop": 1673, "state": "PA", "_id": "17086"} -{"city": "RICHLAND", "loc": [-76.265447, 40.380595], "pop": 3367, "state": "PA", "_id": "17087"} -{"city": "SHERMANS DALE", "loc": [-77.180856, 40.329898], "pop": 5128, "state": "PA", "_id": "17090"} -{"city": "THOMPSONTOWN", "loc": [-77.207551, 40.590782], "pop": 2515, "state": "PA", "_id": "17094"} -{"city": "WICONISCO", "loc": [-76.709084, 40.567511], "pop": 1702, "state": "PA", "_id": "17097"} -{"city": "WILLIAMSTOWN", "loc": [-76.622259, 40.580761], "pop": 2655, "state": "PA", "_id": "17098"} -{"city": "YEAGERTOWN", "loc": [-77.568823, 40.643558], "pop": 2197, "state": "PA", "_id": "17099"} -{"city": "HARRISBURG", "loc": [-76.883079, 40.261767], "pop": 2151, "state": "PA", "_id": "17101"} -{"city": "HARRISBURG", "loc": [-76.891044, 40.27278], "pop": 8862, "state": "PA", "_id": "17102"} -{"city": "PENBROOK", "loc": [-76.863812, 40.273852], "pop": 12335, "state": "PA", "_id": "17103"} -{"city": "HARRISBURG", "loc": [-76.859397, 40.259683], "pop": 21882, "state": "PA", "_id": "17104"} -{"city": "COLONIAL PARK", "loc": [-76.822612, 40.29122], "pop": 22952, "state": "PA", "_id": "17109"} -{"city": "HARRISBURG", "loc": [-76.886246, 40.302957], "pop": 19314, "state": "PA", "_id": "17110"} -{"city": "SWATARA", "loc": [-76.793918, 40.266058], "pop": 22558, "state": "PA", "_id": "17111"} -{"city": "HARRISBURG", "loc": [-76.791438, 40.335208], "pop": 27559, "state": "PA", "_id": "17112"} -{"city": "STEELTON", "loc": [-76.827568, 40.234007], "pop": 9841, "state": "PA", "_id": "17113"} -{"city": "CHAMBERSBURG", "loc": [-77.657928, 39.931318], "pop": 41893, "state": "PA", "_id": "17201"} -{"city": "ARTEMAS", "loc": [-78.40314, 39.757465], "pop": 481, "state": "PA", "_id": "17211"} -{"city": "BIG COVE TANNERY", "loc": [-78.012366, 39.889704], "pop": 2178, "state": "PA", "_id": "17212"} -{"city": "BLAIRS MILLS", "loc": [-77.769473, 40.254804], "pop": 551, "state": "PA", "_id": "17213"} -{"city": "BLUE RIDGE SUMMI", "loc": [-77.469836, 39.726951], "pop": 1090, "state": "PA", "_id": "17214"} -{"city": "BURNT CABINS", "loc": [-77.901718, 40.075278], "pop": 155, "state": "PA", "_id": "17215"} -{"city": "CONCORD", "loc": [-77.703133, 40.245842], "pop": 109, "state": "PA", "_id": "17217"} -{"city": "DOYLESBURG", "loc": [-77.686203, 40.217195], "pop": 905, "state": "PA", "_id": "17219"} -{"city": "DRY RUN", "loc": [-77.76457, 40.174744], "pop": 499, "state": "PA", "_id": "17220"} -{"city": "FANNETTSBURG", "loc": [-77.82101, 40.071692], "pop": 628, "state": "PA", "_id": "17221"} -{"city": "FAYETTEVILLE", "loc": [-77.53096, 39.906543], "pop": 9459, "state": "PA", "_id": "17222"} -{"city": "FORT LITTLETON", "loc": [-77.975678, 40.054372], "pop": 750, "state": "PA", "_id": "17223"} -{"city": "FORT LOUDON", "loc": [-77.898365, 39.954692], "pop": 1412, "state": "PA", "_id": "17224"} -{"city": "GREENCASTLE", "loc": [-77.746956, 39.781827], "pop": 14553, "state": "PA", "_id": "17225"} -{"city": "HARRISONVILLE", "loc": [-78.084077, 39.976137], "pop": 1410, "state": "PA", "_id": "17228"} -{"city": "HUSTONTOWN", "loc": [-78.014835, 40.044111], "pop": 245, "state": "PA", "_id": "17229"} -{"city": "LURGAN", "loc": [-77.635063, 40.127422], "pop": 845, "state": "PA", "_id": "17232"} -{"city": "MC CONNELLSBURG", "loc": [-77.990117, 39.944251], "pop": 2529, "state": "PA", "_id": "17233"} -{"city": "MERCERSBURG", "loc": [-77.907259, 39.819519], "pop": 7735, "state": "PA", "_id": "17236"} -{"city": "MONT ALTO", "loc": [-77.553676, 39.841689], "pop": 1601, "state": "PA", "_id": "17237"} -{"city": "NEEDMORE", "loc": [-78.143935, 39.871279], "pop": 1208, "state": "PA", "_id": "17238"} -{"city": "NEELYTON", "loc": [-77.858015, 40.137051], "pop": 816, "state": "PA", "_id": "17239"} -{"city": "NEWBURG", "loc": [-77.566915, 40.13333], "pop": 2350, "state": "PA", "_id": "17240"} -{"city": "NEWVILLE", "loc": [-77.411401, 40.185468], "pop": 9740, "state": "PA", "_id": "17241"} -{"city": "ORBISONIA", "loc": [-77.906924, 40.238872], "pop": 2368, "state": "PA", "_id": "17243"} -{"city": "ORRSTOWN", "loc": [-77.639762, 40.07305], "pop": 2281, "state": "PA", "_id": "17244"} -{"city": "PLEASANT HALL", "loc": [-77.703148, 40.04135], "pop": 366, "state": "PA", "_id": "17246"} -{"city": "SAINT THOMAS", "loc": [-77.7908, 39.924052], "pop": 4109, "state": "PA", "_id": "17252"} -{"city": "SHADE GAP", "loc": [-77.868045, 40.172976], "pop": 416, "state": "PA", "_id": "17255"} -{"city": "SHIPPENSBURG", "loc": [-77.519477, 40.051359], "pop": 19302, "state": "PA", "_id": "17257"} -{"city": "SHIRLEYSBURG", "loc": [-77.870062, 40.316768], "pop": 1416, "state": "PA", "_id": "17260"} -{"city": "SPRING RUN", "loc": [-77.740525, 40.14663], "pop": 796, "state": "PA", "_id": "17262"} -{"city": "THREE SPRINGS", "loc": [-77.99412, 40.183437], "pop": 2197, "state": "PA", "_id": "17264"} -{"city": "UPPERSTRASBURG", "loc": [-77.736791, 40.05799], "pop": 499, "state": "PA", "_id": "17265"} -{"city": "WALNUT BOTTOM", "loc": [-77.408984, 40.086042], "pop": 1157, "state": "PA", "_id": "17266"} -{"city": "WARFORDSBURG", "loc": [-78.198627, 39.769765], "pop": 2988, "state": "PA", "_id": "17267"} -{"city": "WAYNESBORO", "loc": [-77.567363, 39.763504], "pop": 25878, "state": "PA", "_id": "17268"} -{"city": "WILLOW HILL", "loc": [-77.796947, 40.113694], "pop": 409, "state": "PA", "_id": "17271"} -{"city": "ABBOTTSTOWN", "loc": [-76.993077, 39.888099], "pop": 1777, "state": "PA", "_id": "17301"} -{"city": "AIRVILLE", "loc": [-76.401179, 39.821012], "pop": 1685, "state": "PA", "_id": "17302"} -{"city": "ASPERS", "loc": [-77.228657, 39.976533], "pop": 2894, "state": "PA", "_id": "17304"} -{"city": "BIGLERVILLE", "loc": [-77.288549, 39.928119], "pop": 5280, "state": "PA", "_id": "17307"} -{"city": "BROGUE", "loc": [-76.488236, 39.883044], "pop": 5123, "state": "PA", "_id": "17309"} -{"city": "YOE", "loc": [-76.644794, 39.900127], "pop": 8691, "state": "PA", "_id": "17313"} -{"city": "DELTA", "loc": [-76.344101, 39.751754], "pop": 4910, "state": "PA", "_id": "17314"} -{"city": "DOVER", "loc": [-76.855485, 40.006158], "pop": 20094, "state": "PA", "_id": "17315"} -{"city": "EAST BERLIN", "loc": [-77.007252, 39.964546], "pop": 5538, "state": "PA", "_id": "17316"} -{"city": "ETTERS", "loc": [-76.801861, 40.154506], "pop": 6135, "state": "PA", "_id": "17319"} -{"city": "GREENSTONE", "loc": [-77.376824, 39.762694], "pop": 4709, "state": "PA", "_id": "17320"} -{"city": "FAWN GROVE", "loc": [-76.439237, 39.751024], "pop": 1938, "state": "PA", "_id": "17321"} -{"city": "FELTON", "loc": [-76.593721, 39.836006], "pop": 3128, "state": "PA", "_id": "17322"} -{"city": "GARDNERS", "loc": [-77.187725, 40.042759], "pop": 4246, "state": "PA", "_id": "17324"} -{"city": "GETTYSBURG", "loc": [-77.222313, 39.832044], "pop": 23574, "state": "PA", "_id": "17325"} -{"city": "GLEN ROCK", "loc": [-76.747713, 39.781326], "pop": 7217, "state": "PA", "_id": "17327"} -{"city": "BRODBECKS", "loc": [-76.862046, 39.759907], "pop": 2230, "state": "PA", "_id": "17329"} -{"city": "HANOVER", "loc": [-76.981196, 39.794286], "pop": 37367, "state": "PA", "_id": "17331"} -{"city": "LEWISBERRY", "loc": [-76.870004, 40.146295], "pop": 5338, "state": "PA", "_id": "17339"} -{"city": "LITTLESTOWN", "loc": [-77.100326, 39.749549], "pop": 7758, "state": "PA", "_id": "17340"} -{"city": "MC SHERRYSTOWN", "loc": [-77.01496, 39.804832], "pop": 3838, "state": "PA", "_id": "17344"} -{"city": "MANCHESTER", "loc": [-76.733245, 40.069461], "pop": 8095, "state": "PA", "_id": "17345"} -{"city": "MOUNT WOLF", "loc": [-76.696576, 40.071126], "pop": 3083, "state": "PA", "_id": "17347"} -{"city": "NEW FREEDOM", "loc": [-76.684064, 39.742266], "pop": 6346, "state": "PA", "_id": "17349"} -{"city": "NEW OXFORD", "loc": [-77.06433, 39.877459], "pop": 9674, "state": "PA", "_id": "17350"} -{"city": "NEW PARK", "loc": [-76.504167, 39.760027], "pop": 1190, "state": "PA", "_id": "17352"} -{"city": "ORRTANNA", "loc": [-77.380592, 39.881032], "pop": 2066, "state": "PA", "_id": "17353"} -{"city": "RED LION", "loc": [-76.608075, 39.902572], "pop": 12737, "state": "PA", "_id": "17356"} -{"city": "SEVEN VALLEYS", "loc": [-76.738336, 39.855613], "pop": 5219, "state": "PA", "_id": "17360"} -{"city": "SHREWSBURY", "loc": [-76.674827, 39.760133], "pop": 3749, "state": "PA", "_id": "17361"} -{"city": "SPRING GROVE", "loc": [-76.877356, 39.857208], "pop": 13901, "state": "PA", "_id": "17362"} -{"city": "STEWARTSTOWN", "loc": [-76.597037, 39.771962], "pop": 5865, "state": "PA", "_id": "17363"} -{"city": "THOMASVILLE", "loc": [-76.882159, 39.934619], "pop": 3435, "state": "PA", "_id": "17364"} -{"city": "WELLSVILLE", "loc": [-76.944315, 40.055721], "pop": 2456, "state": "PA", "_id": "17365"} -{"city": "WINDSOR", "loc": [-76.559126, 39.923271], "pop": 5489, "state": "PA", "_id": "17366"} -{"city": "WRIGHTSVILLE", "loc": [-76.526971, 39.996559], "pop": 7677, "state": "PA", "_id": "17368"} -{"city": "YORK HAVEN", "loc": [-76.773725, 40.122154], "pop": 4948, "state": "PA", "_id": "17370"} -{"city": "YORK SPRINGS", "loc": [-77.106136, 40.00839], "pop": 3042, "state": "PA", "_id": "17372"} -{"city": "YORK", "loc": [-76.726887, 39.963539], "pop": 2439, "state": "PA", "_id": "17401"} -{"city": "EAST YORK", "loc": [-76.674578, 39.971508], "pop": 35648, "state": "PA", "_id": "17402"} -{"city": "YORK", "loc": [-76.712998, 39.94943], "pop": 40210, "state": "PA", "_id": "17403"} -{"city": "WEST YORK", "loc": [-76.768987, 39.961988], "pop": 49524, "state": "PA", "_id": "17404"} -{"city": "HELLAM", "loc": [-76.592646, 39.998249], "pop": 6095, "state": "PA", "_id": "17406"} -{"city": "JACOBUS", "loc": [-76.714634, 39.880203], "pop": 1872, "state": "PA", "_id": "17407"} -{"city": "AKRON", "loc": [-76.205295, 40.157086], "pop": 4286, "state": "PA", "_id": "17501"} -{"city": "BAINBRIDGE", "loc": [-76.672589, 40.1086], "pop": 2688, "state": "PA", "_id": "17502"} -{"city": "BIRD IN HAND", "loc": [-76.183036, 40.056109], "pop": 862, "state": "PA", "_id": "17505"} -{"city": "NINEPOINTS", "loc": [-76.025983, 39.935632], "pop": 4517, "state": "PA", "_id": "17509"} -{"city": "COLUMBIA", "loc": [-76.48622, 40.039079], "pop": 17454, "state": "PA", "_id": "17512"} -{"city": "CONESTOGA", "loc": [-76.357475, 39.940303], "pop": 4493, "state": "PA", "_id": "17516"} -{"city": "DENVER", "loc": [-76.115688, 40.229671], "pop": 10737, "state": "PA", "_id": "17517"} -{"city": "DRUMORE", "loc": [-76.245684, 39.838399], "pop": 1191, "state": "PA", "_id": "17518"} -{"city": "EAST EARL", "loc": [-76.027634, 40.139475], "pop": 4249, "state": "PA", "_id": "17519"} -{"city": "EAST PETERSBURG", "loc": [-76.351169, 40.100781], "pop": 4387, "state": "PA", "_id": "17520"} -{"city": "EPHRATA", "loc": [-76.182093, 40.175641], "pop": 25859, "state": "PA", "_id": "17522"} -{"city": "GAP", "loc": [-75.997801, 40.002018], "pop": 4318, "state": "PA", "_id": "17527"} -{"city": "GORDONVILLE", "loc": [-76.11063, 40.035304], "pop": 4429, "state": "PA", "_id": "17529"} -{"city": "HOLTWOOD", "loc": [-76.300822, 39.863146], "pop": 2526, "state": "PA", "_id": "17532"} -{"city": "KINZERS", "loc": [-76.049326, 40.005326], "pop": 2524, "state": "PA", "_id": "17535"} -{"city": "KIRKWOOD", "loc": [-76.093315, 39.82571], "pop": 2384, "state": "PA", "_id": "17536"} -{"city": "SALUNGA", "loc": [-76.414975, 40.08825], "pop": 5489, "state": "PA", "_id": "17538"} -{"city": "LEOLA", "loc": [-76.192109, 40.096448], "pop": 9624, "state": "PA", "_id": "17540"} -{"city": "BRUNNERVILLE", "loc": [-76.29926, 40.162573], "pop": 29376, "state": "PA", "_id": "17543"} -{"city": "MANHEIM", "loc": [-76.416794, 40.170229], "pop": 16992, "state": "PA", "_id": "17545"} -{"city": "MARIETTA", "loc": [-76.564527, 40.066442], "pop": 5751, "state": "PA", "_id": "17547"} -{"city": "MILLERSVILLE", "loc": [-76.356568, 39.998213], "pop": 8021, "state": "PA", "_id": "17551"} -{"city": "FLORIN", "loc": [-76.507551, 40.106828], "pop": 12282, "state": "PA", "_id": "17552"} -{"city": "MOUNTVILLE", "loc": [-76.427694, 40.042742], "pop": 4192, "state": "PA", "_id": "17554"} -{"city": "NARVON", "loc": [-75.975584, 40.125165], "pop": 7239, "state": "PA", "_id": "17555"} -{"city": "NEW HOLLAND", "loc": [-76.080136, 40.100511], "pop": 11604, "state": "PA", "_id": "17557"} -{"city": "NEW PROVIDENCE", "loc": [-76.224319, 39.909776], "pop": 5330, "state": "PA", "_id": "17560"} -{"city": "PARADISE", "loc": [-76.108074, 39.985249], "pop": 2757, "state": "PA", "_id": "17562"} -{"city": "PEACH BOTTOM", "loc": [-76.179083, 39.770511], "pop": 4352, "state": "PA", "_id": "17563"} -{"city": "PEQUEA", "loc": [-76.320866, 39.905765], "pop": 1800, "state": "PA", "_id": "17565"} -{"city": "QUARRYVILLE", "loc": [-76.146462, 39.894932], "pop": 9361, "state": "PA", "_id": "17566"} -{"city": "REINHOLDS", "loc": [-76.101332, 40.268758], "pop": 4665, "state": "PA", "_id": "17569"} -{"city": "RONKS", "loc": [-76.166132, 40.020754], "pop": 2964, "state": "PA", "_id": "17572"} -{"city": "SMOKETOWN", "loc": [-76.22007, 40.040651], "pop": 2141, "state": "PA", "_id": "17576"} -{"city": "STEVENS", "loc": [-76.162604, 40.219397], "pop": 6511, "state": "PA", "_id": "17578"} -{"city": "STRASBURG", "loc": [-76.184824, 39.970075], "pop": 5694, "state": "PA", "_id": "17579"} -{"city": "TERRE HILL", "loc": [-76.051083, 40.158539], "pop": 1282, "state": "PA", "_id": "17581"} -{"city": "WASHINGTON BORO", "loc": [-76.4402, 39.988118], "pop": 2214, "state": "PA", "_id": "17582"} -{"city": "WILLOW STREET", "loc": [-76.27524, 39.967003], "pop": 7176, "state": "PA", "_id": "17584"} -{"city": "NEFFSVILLE", "loc": [-76.319888, 40.075381], "pop": 41062, "state": "PA", "_id": "17601"} -{"city": "LANCASTER", "loc": [-76.284364, 40.033514], "pop": 40850, "state": "PA", "_id": "17602"} -{"city": "ROHRERSTOWN", "loc": [-76.331583, 40.030475], "pop": 55173, "state": "PA", "_id": "17603"} -{"city": "SOUTH WILLIAMSPO", "loc": [-77.020571, 41.247217], "pop": 58844, "state": "PA", "_id": "17701"} -{"city": "CAMMAL", "loc": [-77.462021, 41.380901], "pop": 246, "state": "PA", "_id": "17723"} -{"city": "CANTON", "loc": [-76.858188, 41.653784], "pop": 5189, "state": "PA", "_id": "17724"} -{"city": "CEDAR RUN", "loc": [-77.48891, 41.498972], "pop": 102, "state": "PA", "_id": "17727"} -{"city": "COGAN STATION", "loc": [-77.068996, 41.31517], "pop": 4582, "state": "PA", "_id": "17728"} -{"city": "CROSS FORK", "loc": [-77.80953, 41.473672], "pop": 184, "state": "PA", "_id": "17729"} -{"city": "HUGHESVILLE", "loc": [-76.71411, 41.255952], "pop": 6489, "state": "PA", "_id": "17737"} -{"city": "SALLADASBURG", "loc": [-77.242704, 41.200733], "pop": 11484, "state": "PA", "_id": "17740"} -{"city": "LAIRDSVILLE", "loc": [-76.58893, 41.233549], "pop": 914, "state": "PA", "_id": "17742"} -{"city": "LINDEN", "loc": [-77.152652, 41.247216], "pop": 2994, "state": "PA", "_id": "17744"} -{"city": "LOCK HAVEN", "loc": [-77.443588, 41.142497], "pop": 16448, "state": "PA", "_id": "17745"} -{"city": "LOGANTON", "loc": [-77.320397, 41.028317], "pop": 2326, "state": "PA", "_id": "17747"} -{"city": "MILL HALL", "loc": [-77.483609, 41.086688], "pop": 7510, "state": "PA", "_id": "17751"} -{"city": "MONTGOMERY", "loc": [-76.883933, 41.178778], "pop": 5539, "state": "PA", "_id": "17752"} -{"city": "MONTOURSVILLE", "loc": [-76.903035, 41.266252], "pop": 11266, "state": "PA", "_id": "17754"} -{"city": "MUNCY", "loc": [-76.763258, 41.213715], "pop": 8627, "state": "PA", "_id": "17756"} -{"city": "MUNCY VALLEY", "loc": [-76.541518, 41.381206], "pop": 1548, "state": "PA", "_id": "17758"} -{"city": "RALSTON", "loc": [-76.95835, 41.503817], "pop": 588, "state": "PA", "_id": "17763"} -{"city": "RENOVO", "loc": [-77.74479, 41.333376], "pop": 3639, "state": "PA", "_id": "17764"} -{"city": "ROARING BRANCH", "loc": [-76.942053, 41.569234], "pop": 448, "state": "PA", "_id": "17765"} -{"city": "SHUNK", "loc": [-76.745521, 41.553574], "pop": 300, "state": "PA", "_id": "17768"} -{"city": "TROUT RUN", "loc": [-77.009776, 41.412481], "pop": 4449, "state": "PA", "_id": "17771"} -{"city": "TURBOTVILLE", "loc": [-76.742493, 41.111867], "pop": 3902, "state": "PA", "_id": "17772"} -{"city": "UNITYVILLE", "loc": [-76.518318, 41.243552], "pop": 871, "state": "PA", "_id": "17774"} -{"city": "WATERVILLE", "loc": [-77.360368, 41.31136], "pop": 334, "state": "PA", "_id": "17776"} -{"city": "WATSONTOWN", "loc": [-76.853192, 41.102006], "pop": 6601, "state": "PA", "_id": "17777"} -{"city": "WESTPORT", "loc": [-77.931496, 41.27445], "pop": 25, "state": "PA", "_id": "17778"} -{"city": "WOOLRICH", "loc": [-77.331307, 41.188734], "pop": 4694, "state": "PA", "_id": "17779"} -{"city": "SUNBURY", "loc": [-76.777611, 40.855122], "pop": 17335, "state": "PA", "_id": "17801"} -{"city": "ALLENWOOD", "loc": [-76.972362, 41.126424], "pop": 2666, "state": "PA", "_id": "17810"} -{"city": "BEAVER SPRINGS", "loc": [-77.231801, 40.752766], "pop": 1575, "state": "PA", "_id": "17812"} -{"city": "BEAVERTOWN", "loc": [-77.169112, 40.777378], "pop": 2226, "state": "PA", "_id": "17813"} -{"city": "BENTON", "loc": [-76.340632, 41.223142], "pop": 5292, "state": "PA", "_id": "17814"} -{"city": "BLOOMSBURG", "loc": [-76.438379, 41.011528], "pop": 25338, "state": "PA", "_id": "17815"} -{"city": "CATAWISSA", "loc": [-76.441586, 40.918031], "pop": 6334, "state": "PA", "_id": "17820"} -{"city": "DANVILLE", "loc": [-76.622897, 40.979895], "pop": 17269, "state": "PA", "_id": "17821"} -{"city": "DORNSIFE", "loc": [-76.7626, 40.757092], "pop": 432, "state": "PA", "_id": "17823"} -{"city": "ELYSBURG", "loc": [-76.556924, 40.863871], "pop": 2165, "state": "PA", "_id": "17824"} -{"city": "FREEBURG", "loc": [-76.942963, 40.767498], "pop": 1080, "state": "PA", "_id": "17827"} -{"city": "GOWEN CITY", "loc": [-76.528149, 40.751141], "pop": 646, "state": "PA", "_id": "17828"} -{"city": "HERNDON", "loc": [-76.800761, 40.691789], "pop": 2089, "state": "PA", "_id": "17830"} -{"city": "MARION HEIGHTS", "loc": [-76.465137, 40.804576], "pop": 837, "state": "PA", "_id": "17832"} -{"city": "KULPMONT", "loc": [-76.474384, 40.793278], "pop": 3183, "state": "PA", "_id": "17834"} -{"city": "LAURELTON", "loc": [-77.205211, 40.91268], "pop": 448, "state": "PA", "_id": "17835"} -{"city": "LECK KILL", "loc": [-76.627108, 40.710135], "pop": 626, "state": "PA", "_id": "17836"} -{"city": "LEWISBURG", "loc": [-76.909878, 40.970205], "pop": 18821, "state": "PA", "_id": "17837"} -{"city": "MC CLURE", "loc": [-77.375791, 40.699084], "pop": 4901, "state": "PA", "_id": "17841"} -{"city": "MIDDLEBURG", "loc": [-77.046798, 40.797656], "pop": 6546, "state": "PA", "_id": "17842"} -{"city": "BEAVER SPRINGS", "loc": [-76.970366, 40.810917], "pop": 1791, "state": "PA", "_id": "17843"} -{"city": "MIFFLINBURG", "loc": [-77.050515, 40.921826], "pop": 8379, "state": "PA", "_id": "17844"} -{"city": "MILLMONT", "loc": [-77.194142, 40.880324], "pop": 2111, "state": "PA", "_id": "17845"} -{"city": "MILLVILLE", "loc": [-76.520767, 41.126051], "pop": 5496, "state": "PA", "_id": "17846"} -{"city": "MILTON", "loc": [-76.839817, 41.01681], "pop": 9223, "state": "PA", "_id": "17847"} -{"city": "MONTANDON", "loc": [-76.851024, 40.975936], "pop": 3167, "state": "PA", "_id": "17850"} -{"city": "MOUNT CARMEL", "loc": [-76.419466, 40.795535], "pop": 9925, "state": "PA", "_id": "17851"} -{"city": "MOUNT PLEASANT M", "loc": [-77.005241, 40.700185], "pop": 1876, "state": "PA", "_id": "17853"} -{"city": "NEW BERLIN", "loc": [-76.986124, 40.880212], "pop": 904, "state": "PA", "_id": "17855"} -{"city": "NEW COLUMBIA", "loc": [-76.901851, 41.054108], "pop": 3072, "state": "PA", "_id": "17856"} -{"city": "NORTHUMBERLAND", "loc": [-76.790794, 40.904355], "pop": 7326, "state": "PA", "_id": "17857"} -{"city": "ORANGEVILLE", "loc": [-76.38097, 41.10156], "pop": 2625, "state": "PA", "_id": "17859"} -{"city": "PAXINOS", "loc": [-76.635041, 40.844809], "pop": 1684, "state": "PA", "_id": "17860"} -{"city": "PORT TREVORTON", "loc": [-76.908215, 40.699593], "pop": 2911, "state": "PA", "_id": "17864"} -{"city": "RANSHAW", "loc": [-76.498242, 40.803039], "pop": 124, "state": "PA", "_id": "17866"} -{"city": "REBUCK", "loc": [-76.740605, 40.712544], "pop": 620, "state": "PA", "_id": "17867"} -{"city": "RIVERSIDE", "loc": [-76.637458, 40.951269], "pop": 1991, "state": "PA", "_id": "17868"} -{"city": "SELINSGROVE", "loc": [-76.86825, 40.822372], "pop": 14796, "state": "PA", "_id": "17870"} -{"city": "EXCELSIOR", "loc": [-76.561118, 40.790336], "pop": 20982, "state": "PA", "_id": "17872"} -{"city": "SNYDERTOWN", "loc": [-76.674945, 40.874097], "pop": 416, "state": "PA", "_id": "17877"} -{"city": "STILLWATER", "loc": [-76.369624, 41.151517], "pop": 523, "state": "PA", "_id": "17878"} -{"city": "TREVORTON", "loc": [-76.670234, 40.781867], "pop": 2067, "state": "PA", "_id": "17881"} -{"city": "WILBURTON", "loc": [-76.392922, 40.812087], "pop": 98, "state": "PA", "_id": "17888"} -{"city": "WINFIELD", "loc": [-76.871833, 40.890805], "pop": 2071, "state": "PA", "_id": "17889"} -{"city": "POTTSVILLE", "loc": [-76.212318, 40.683978], "pop": 27703, "state": "PA", "_id": "17901"} -{"city": "ASHLAND", "loc": [-76.342972, 40.773231], "pop": 8328, "state": "PA", "_id": "17921"} -{"city": "AUBURN", "loc": [-76.13439, 40.596157], "pop": 2444, "state": "PA", "_id": "17922"} -{"city": "BRANCHDALE", "loc": [-76.332788, 40.664328], "pop": 835, "state": "PA", "_id": "17923"} -{"city": "BROCKTON", "loc": [-76.049874, 40.763162], "pop": 1230, "state": "PA", "_id": "17925"} -{"city": "CENTRALIA", "loc": [-76.357325, 40.81375], "pop": 1006, "state": "PA", "_id": "17927"} -{"city": "CRESSONA", "loc": [-76.195028, 40.628361], "pop": 2240, "state": "PA", "_id": "17929"} -{"city": "FRACKVILLE", "loc": [-76.231137, 40.782537], "pop": 7039, "state": "PA", "_id": "17931"} -{"city": "GIRARDVILLE", "loc": [-76.28581, 40.792162], "pop": 1892, "state": "PA", "_id": "17935"} -{"city": "HEGINS", "loc": [-76.473168, 40.666898], "pop": 2499, "state": "PA", "_id": "17938"} -{"city": "KLINGERSTOWN", "loc": [-76.650709, 40.667571], "pop": 508, "state": "PA", "_id": "17941"} -{"city": "MAHANOY CITY", "loc": [-76.139601, 40.812302], "pop": 5209, "state": "PA", "_id": "17948"} -{"city": "MINERSVILLE", "loc": [-76.261533, 40.690637], "pop": 4998, "state": "PA", "_id": "17954"} -{"city": "MUIR", "loc": [-76.468205, 40.613614], "pop": 374, "state": "PA", "_id": "17957"} -{"city": "KASKA", "loc": [-76.11348, 40.722728], "pop": 2826, "state": "PA", "_id": "17959"} -{"city": "NEW RINGGOLD", "loc": [-75.948409, 40.714851], "pop": 4284, "state": "PA", "_id": "17960"} -{"city": "ORWIGSBURG", "loc": [-76.083999, 40.643071], "pop": 6928, "state": "PA", "_id": "17961"} -{"city": "PINE GROVE", "loc": [-76.326913, 40.567093], "pop": 12239, "state": "PA", "_id": "17963"} -{"city": "PITMAN", "loc": [-76.523297, 40.704868], "pop": 992, "state": "PA", "_id": "17964"} -{"city": "PORT CARBON", "loc": [-76.166046, 40.697731], "pop": 2260, "state": "PA", "_id": "17965"} -{"city": "RINGTOWN", "loc": [-76.23493, 40.855854], "pop": 2311, "state": "PA", "_id": "17967"} -{"city": "SACRAMENTO", "loc": [-76.612833, 40.635238], "pop": 347, "state": "PA", "_id": "17968"} -{"city": "SAINT CLAIR", "loc": [-76.192381, 40.719273], "pop": 4091, "state": "PA", "_id": "17970"} -{"city": "SCHUYLKILL HAVEN", "loc": [-76.169973, 40.630571], "pop": 6199, "state": "PA", "_id": "17972"} -{"city": "SHENANDOAH", "loc": [-76.203502, 40.816744], "pop": 11159, "state": "PA", "_id": "17976"} -{"city": "SPRING GLEN", "loc": [-76.635018, 40.633447], "pop": 288, "state": "PA", "_id": "17978"} -{"city": "TOWER CITY", "loc": [-76.550022, 40.584475], "pop": 3905, "state": "PA", "_id": "17980"} -{"city": "DONALDSON", "loc": [-76.398796, 40.626842], "pop": 2629, "state": "PA", "_id": "17981"} -{"city": "VALLEY VIEW", "loc": [-76.544843, 40.644467], "pop": 2007, "state": "PA", "_id": "17983"} -{"city": "ZION GROVE", "loc": [-76.231032, 40.937152], "pop": 2070, "state": "PA", "_id": "17985"} -{"city": "ALBURTIS", "loc": [-75.62113, 40.486201], "pop": 4844, "state": "PA", "_id": "18011"} -{"city": "ROSETO", "loc": [-75.195644, 40.854907], "pop": 16916, "state": "PA", "_id": "18013"} -{"city": "BATH", "loc": [-75.40856, 40.755144], "pop": 11601, "state": "PA", "_id": "18014"} -{"city": "BETHLEHEM", "loc": [-75.380507, 40.600167], "pop": 29778, "state": "PA", "_id": "18015"} -{"city": "BUTZTOWN", "loc": [-75.35823, 40.65168], "pop": 46369, "state": "PA", "_id": "18017"} -{"city": "BETHLEHEM", "loc": [-75.392827, 40.627849], "pop": 32072, "state": "PA", "_id": "18018"} -{"city": "BREINIGSVILLE", "loc": [-75.655269, 40.552621], "pop": 3437, "state": "PA", "_id": "18031"} -{"city": "CATASAUQUA", "loc": [-75.46927, 40.655696], "pop": 9663, "state": "PA", "_id": "18032"} -{"city": "CENTER VALLEY", "loc": [-75.424208, 40.539594], "pop": 5250, "state": "PA", "_id": "18034"} -{"city": "CHERRYVILLE", "loc": [-75.552133, 40.738476], "pop": 905, "state": "PA", "_id": "18035"} -{"city": "COOPERSBURG", "loc": [-75.388778, 40.507553], "pop": 10656, "state": "PA", "_id": "18036"} -{"city": "COPLAY", "loc": [-75.518825, 40.684865], "pop": 6015, "state": "PA", "_id": "18037"} -{"city": "DANIELSVILLE", "loc": [-75.518604, 40.786636], "pop": 2004, "state": "PA", "_id": "18038"} -{"city": "EAST GREENVILLE", "loc": [-75.505618, 40.411876], "pop": 6266, "state": "PA", "_id": "18041"} -{"city": "FORKS TOWNSHIP", "loc": [-75.23582, 40.6867], "pop": 65784, "state": "PA", "_id": "18042"} -{"city": "EMMAUS", "loc": [-75.500991, 40.529295], "pop": 16204, "state": "PA", "_id": "18049"} -{"city": "FOGELSVILLE", "loc": [-75.656794, 40.59304], "pop": 2175, "state": "PA", "_id": "18051"} -{"city": "HOKENDAUQUA", "loc": [-75.495383, 40.647479], "pop": 23666, "state": "PA", "_id": "18052"} -{"city": "GERMANSVILLE", "loc": [-75.714687, 40.711826], "pop": 975, "state": "PA", "_id": "18053"} -{"city": "GREEN LANE", "loc": [-75.435148, 40.353377], "pop": 6020, "state": "PA", "_id": "18054"} -{"city": "HELLERTOWN", "loc": [-75.325513, 40.581715], "pop": 11759, "state": "PA", "_id": "18055"} -{"city": "HEREFORD", "loc": [-75.579983, 40.448659], "pop": 3026, "state": "PA", "_id": "18056"} -{"city": "KUNKLETOWN", "loc": [-75.475974, 40.899891], "pop": 4924, "state": "PA", "_id": "18058"} -{"city": "MACUNGIE", "loc": [-75.566618, 40.528543], "pop": 10376, "state": "PA", "_id": "18062"} -{"city": "NAZARETH", "loc": [-75.319932, 40.744962], "pop": 19236, "state": "PA", "_id": "18064"} -{"city": "NEW TRIPOLI", "loc": [-75.741739, 40.654544], "pop": 4684, "state": "PA", "_id": "18066"} -{"city": "NORTHAMPTON", "loc": [-75.48742, 40.699765], "pop": 13792, "state": "PA", "_id": "18067"} -{"city": "OREFIELD", "loc": [-75.597395, 40.624826], "pop": 6397, "state": "PA", "_id": "18069"} -{"city": "PALM", "loc": [-75.533124, 40.43167], "pop": 512, "state": "PA", "_id": "18070"} -{"city": "PALMERTON", "loc": [-75.601119, 40.816976], "pop": 11535, "state": "PA", "_id": "18071"} -{"city": "PEN ARGYL", "loc": [-75.270115, 40.85182], "pop": 9736, "state": "PA", "_id": "18072"} -{"city": "PENNSBURG", "loc": [-75.486608, 40.391138], "pop": 5126, "state": "PA", "_id": "18073"} -{"city": "PERKIOMENVILLE", "loc": [-75.50218, 40.31566], "pop": 4053, "state": "PA", "_id": "18074"} -{"city": "RED HILL", "loc": [-75.484613, 40.375807], "pop": 1807, "state": "PA", "_id": "18076"} -{"city": "RIEGELSVILLE", "loc": [-75.219064, 40.57824], "pop": 1924, "state": "PA", "_id": "18077"} -{"city": "SCHNECKSVILLE", "loc": [-75.623924, 40.681949], "pop": 6843, "state": "PA", "_id": "18078"} -{"city": "EMERALD", "loc": [-75.621612, 40.740695], "pop": 11316, "state": "PA", "_id": "18080"} -{"city": "TREXLERTOWN", "loc": [-75.602293, 40.554418], "pop": 690, "state": "PA", "_id": "18087"} -{"city": "WALNUTPORT", "loc": [-75.565749, 40.76147], "pop": 7180, "state": "PA", "_id": "18088"} -{"city": "WIND GAP", "loc": [-75.326378, 40.816922], "pop": 1740, "state": "PA", "_id": "18091"} -{"city": "ZIONSVILLE", "loc": [-75.526146, 40.473425], "pop": 2312, "state": "PA", "_id": "18092"} -{"city": "ALLENTOWN", "loc": [-75.470955, 40.602729], "pop": 3638, "state": "PA", "_id": "18101"} -{"city": "ALLENTOWN", "loc": [-75.478139, 40.606818], "pop": 41873, "state": "PA", "_id": "18102"} -{"city": "ALLENTOWN", "loc": [-75.464521, 40.589145], "pop": 59074, "state": "PA", "_id": "18103"} -{"city": "ALLENTOWN", "loc": [-75.522499, 40.601849], "pop": 33989, "state": "PA", "_id": "18104"} -{"city": "WESCOSVILLE", "loc": [-75.566424, 40.561451], "pop": 6260, "state": "PA", "_id": "18106"} -{"city": "WEST HAZLETON", "loc": [-75.978193, 40.962107], "pop": 38788, "state": "PA", "_id": "18201"} -{"city": "ALBRIGHTSVILLE", "loc": [-75.584206, 40.974786], "pop": 3862, "state": "PA", "_id": "18210"} -{"city": "ANDREAS", "loc": [-75.834247, 40.746457], "pop": 1235, "state": "PA", "_id": "18211"} -{"city": "BARNESVILLE", "loc": [-76.06109, 40.813811], "pop": 1949, "state": "PA", "_id": "18214"} -{"city": "BEAVER MEADOWS", "loc": [-75.940648, 40.922672], "pop": 2470, "state": "PA", "_id": "18216"} -{"city": "COALDALE", "loc": [-75.910385, 40.821942], "pop": 2531, "state": "PA", "_id": "18218"} -{"city": "DELANO", "loc": [-76.069462, 40.841048], "pop": 605, "state": "PA", "_id": "18220"} -{"city": "DRUMS", "loc": [-75.97676, 41.025525], "pop": 6008, "state": "PA", "_id": "18222"} -{"city": "FREELAND", "loc": [-75.888001, 41.019557], "pop": 6311, "state": "PA", "_id": "18224"} -{"city": "JIM THORPE", "loc": [-75.739665, 40.870002], "pop": 5048, "state": "PA", "_id": "18229"} -{"city": "LANSFORD", "loc": [-75.882834, 40.831444], "pop": 4583, "state": "PA", "_id": "18232"} -{"city": "WEISSPORT", "loc": [-75.706088, 40.830024], "pop": 17675, "state": "PA", "_id": "18235"} -{"city": "MCADOO", "loc": [-75.997117, 40.89791], "pop": 4184, "state": "PA", "_id": "18237"} -{"city": "NESQUEHONING", "loc": [-75.82389, 40.862608], "pop": 3364, "state": "PA", "_id": "18240"} -{"city": "QUAKAKE", "loc": [-75.982477, 40.849337], "pop": 1292, "state": "PA", "_id": "18245"} -{"city": "ROCK GLEN", "loc": [-76.163761, 40.964628], "pop": 1938, "state": "PA", "_id": "18246"} -{"city": "SHEPPTON", "loc": [-76.137952, 40.888073], "pop": 1374, "state": "PA", "_id": "18248"} -{"city": "SUGARLOAF", "loc": [-76.071655, 40.997126], "pop": 5007, "state": "PA", "_id": "18249"} -{"city": "SUMMIT HILL", "loc": [-75.869275, 40.825524], "pop": 3332, "state": "PA", "_id": "18250"} -{"city": "TAMAQUA", "loc": [-75.97353, 40.798319], "pop": 10446, "state": "PA", "_id": "18252"} -{"city": "WEATHERLY", "loc": [-75.830635, 40.941085], "pop": 4295, "state": "PA", "_id": "18255"} -{"city": "EAST STROUDSBURG", "loc": [-75.173463, 41.036714], "pop": 26726, "state": "PA", "_id": "18301"} -{"city": "BARTONSVILLE", "loc": [-75.296726, 41.008007], "pop": 553, "state": "PA", "_id": "18321"} -{"city": "BRODHEADSVILLE", "loc": [-75.410415, 40.930862], "pop": 1834, "state": "PA", "_id": "18322"} -{"city": "BUSHKILL", "loc": [-75.013207, 41.128476], "pop": 2483, "state": "PA", "_id": "18324"} -{"city": "CANADENSIS", "loc": [-75.257288, 41.233791], "pop": 2194, "state": "PA", "_id": "18325"} -{"city": "CRESCO", "loc": [-75.268228, 41.160605], "pop": 3074, "state": "PA", "_id": "18326"} -{"city": "DELAWARE WATER G", "loc": [-75.149987, 40.982863], "pop": 1052, "state": "PA", "_id": "18327"} -{"city": "DINGMANS FERRY", "loc": [-74.938018, 41.239966], "pop": 3705, "state": "PA", "_id": "18328"} -{"city": "EFFORT", "loc": [-75.452286, 40.966946], "pop": 3817, "state": "PA", "_id": "18330"} -{"city": "GILBERT", "loc": [-75.431373, 40.908866], "pop": 405, "state": "PA", "_id": "18331"} -{"city": "HENRYVILLE", "loc": [-75.279753, 41.088912], "pop": 1927, "state": "PA", "_id": "18332"} -{"city": "KRESGEVILLE", "loc": [-75.507437, 40.898156], "pop": 1125, "state": "PA", "_id": "18333"} -{"city": "LONG POND", "loc": [-75.448245, 41.067732], "pop": 667, "state": "PA", "_id": "18334"} -{"city": "MATAMORAS", "loc": [-74.715358, 41.367437], "pop": 3391, "state": "PA", "_id": "18336"} -{"city": "MILFORD", "loc": [-74.88236, 41.322816], "pop": 6668, "state": "PA", "_id": "18337"} -{"city": "MILLRIFT", "loc": [-74.773876, 41.358265], "pop": 649, "state": "PA", "_id": "18340"} -{"city": "MOUNT BETHEL", "loc": [-75.111545, 40.900839], "pop": 3966, "state": "PA", "_id": "18343"} -{"city": "MOUNT POCONO", "loc": [-75.352868, 41.121558], "pop": 2465, "state": "PA", "_id": "18344"} -{"city": "POCONO SUMMIT", "loc": [-75.413554, 41.103989], "pop": 1509, "state": "PA", "_id": "18346"} -{"city": "POCONO LAKE", "loc": [-75.555863, 41.118661], "pop": 1943, "state": "PA", "_id": "18347"} -{"city": "POCONO PINES", "loc": [-75.476038, 41.105387], "pop": 866, "state": "PA", "_id": "18350"} -{"city": "REEDERS", "loc": [-75.278113, 40.989533], "pop": 481, "state": "PA", "_id": "18352"} -{"city": "SAYLORSBURG", "loc": [-75.374761, 40.917179], "pop": 6970, "state": "PA", "_id": "18353"} -{"city": "SCIOTA", "loc": [-75.293779, 40.928282], "pop": 4155, "state": "PA", "_id": "18354"} -{"city": "SCOTRUN", "loc": [-75.32646, 41.075147], "pop": 966, "state": "PA", "_id": "18355"} -{"city": "STROUDSBURG", "loc": [-75.24852, 40.987697], "pop": 17668, "state": "PA", "_id": "18360"} -{"city": "SWIFTWATER", "loc": [-75.348278, 41.087936], "pop": 838, "state": "PA", "_id": "18370"} -{"city": "TAMIMENT", "loc": [-74.952614, 41.168112], "pop": 557, "state": "PA", "_id": "18371"} -{"city": "TANNERSVILLE", "loc": [-75.309984, 41.048202], "pop": 2353, "state": "PA", "_id": "18372"} -{"city": "ALDENVILLE", "loc": [-75.36749, 41.642679], "pop": 422, "state": "PA", "_id": "18401"} -{"city": "EYNON", "loc": [-75.555232, 41.495633], "pop": 6489, "state": "PA", "_id": "18403"} -{"city": "BEACH LAKE", "loc": [-75.11649, 41.603403], "pop": 918, "state": "PA", "_id": "18405"} -{"city": "SIMPSON", "loc": [-75.507363, 41.583481], "pop": 16409, "state": "PA", "_id": "18407"} -{"city": "CLARKS SUMMIT", "loc": [-75.705713, 41.487795], "pop": 23088, "state": "PA", "_id": "18411"} -{"city": "DALTON", "loc": [-75.703737, 41.539496], "pop": 7621, "state": "PA", "_id": "18414"} -{"city": "DAMASCUS", "loc": [-75.131151, 41.736623], "pop": 1206, "state": "PA", "_id": "18415"} -{"city": "EQUINUNK", "loc": [-75.189081, 41.811712], "pop": 663, "state": "PA", "_id": "18417"} -{"city": "FACTORYVILLE", "loc": [-75.765182, 41.576168], "pop": 4402, "state": "PA", "_id": "18419"} -{"city": "BROWNDALE", "loc": [-75.48725, 41.654587], "pop": 3728, "state": "PA", "_id": "18421"} -{"city": "GOULDSBORO", "loc": [-75.503653, 41.2448], "pop": 2805, "state": "PA", "_id": "18424"} -{"city": "GREELEY", "loc": [-75.012491, 41.437238], "pop": 1121, "state": "PA", "_id": "18425"} -{"city": "GREENTOWN", "loc": [-75.281911, 41.332145], "pop": 1592, "state": "PA", "_id": "18426"} -{"city": "HAMLIN", "loc": [-75.354232, 41.401477], "pop": 1575, "state": "PA", "_id": "18427"} -{"city": "HAWLEY", "loc": [-75.197822, 41.478685], "pop": 2817, "state": "PA", "_id": "18428"} -{"city": "HERRICK CENTER", "loc": [-75.504387, 41.76434], "pop": 218, "state": "PA", "_id": "18430"} -{"city": "HONESDALE", "loc": [-75.25279, 41.579227], "pop": 12655, "state": "PA", "_id": "18431"} -{"city": "MAYFIELD", "loc": [-75.542948, 41.532723], "pop": 4274, "state": "PA", "_id": "18433"} -{"city": "JESSUP", "loc": [-75.568891, 41.472443], "pop": 4349, "state": "PA", "_id": "18434"} -{"city": "LACKAWAXEN", "loc": [-75.074859, 41.504272], "pop": 1711, "state": "PA", "_id": "18435"} -{"city": "LAKE ARIEL", "loc": [-75.431257, 41.439476], "pop": 7939, "state": "PA", "_id": "18436"} -{"city": "LAKE COMO", "loc": [-75.32308, 41.866553], "pop": 415, "state": "PA", "_id": "18437"} -{"city": "LAKEVILLE", "loc": [-75.260717, 41.422278], "pop": 1028, "state": "PA", "_id": "18438"} -{"city": "LAKEWOOD", "loc": [-75.383824, 41.817138], "pop": 653, "state": "PA", "_id": "18439"} -{"city": "LENOXVILLE", "loc": [-75.631934, 41.667713], "pop": 587, "state": "PA", "_id": "18441"} -{"city": "MILANVILLE", "loc": [-75.08818, 41.649208], "pop": 922, "state": "PA", "_id": "18443"} -{"city": "MOSCOW", "loc": [-75.530137, 41.343194], "pop": 10881, "state": "PA", "_id": "18444"} -{"city": "NEWFOUNDLAND", "loc": [-75.338405, 41.304125], "pop": 659, "state": "PA", "_id": "18445"} -{"city": "NICHOLSON", "loc": [-75.764073, 41.641198], "pop": 3513, "state": "PA", "_id": "18446"} -{"city": "OLYPHANT", "loc": [-75.601502, 41.467709], "pop": 7421, "state": "PA", "_id": "18447"} -{"city": "PAUPACK", "loc": [-75.23032, 41.381197], "pop": 887, "state": "PA", "_id": "18451"} -{"city": "PECKVILLE", "loc": [-75.589884, 41.482124], "pop": 6252, "state": "PA", "_id": "18452"} -{"city": "PLEASANT MOUNT", "loc": [-75.398944, 41.732204], "pop": 1232, "state": "PA", "_id": "18453"} -{"city": "PRESTON PARK", "loc": [-75.383147, 41.867264], "pop": 224, "state": "PA", "_id": "18455"} -{"city": "PROMPTON", "loc": [-75.320749, 41.582031], "pop": 552, "state": "PA", "_id": "18456"} -{"city": "SHOHOLA", "loc": [-74.917962, 41.418193], "pop": 1586, "state": "PA", "_id": "18458"} -{"city": "SOUTH STERLING", "loc": [-75.381443, 41.271222], "pop": 922, "state": "PA", "_id": "18460"} -{"city": "STARLIGHT", "loc": [-75.321238, 41.925087], "pop": 431, "state": "PA", "_id": "18461"} -{"city": "STARRUCCA", "loc": [-75.449001, 41.890739], "pop": 333, "state": "PA", "_id": "18462"} -{"city": "STERLING", "loc": [-75.394467, 41.341912], "pop": 689, "state": "PA", "_id": "18463"} -{"city": "TAFTON", "loc": [-75.101598, 41.369445], "pop": 2752, "state": "PA", "_id": "18464"} -{"city": "THOMPSON", "loc": [-75.534215, 41.83395], "pop": 1277, "state": "PA", "_id": "18465"} -{"city": "TOBYHANNA", "loc": [-75.391781, 41.183638], "pop": 6668, "state": "PA", "_id": "18466"} -{"city": "TYLER HILL", "loc": [-75.154246, 41.670873], "pop": 592, "state": "PA", "_id": "18469"} -{"city": "UNION DALE", "loc": [-75.546476, 41.707941], "pop": 1325, "state": "PA", "_id": "18470"} -{"city": "WAYMART", "loc": [-75.406478, 41.570276], "pop": 3922, "state": "PA", "_id": "18472"} -{"city": "SCRANTON", "loc": [-75.664205, 41.409517], "pop": 720, "state": "PA", "_id": "18503"} -{"city": "SCRANTON", "loc": [-75.686081, 41.412777], "pop": 22279, "state": "PA", "_id": "18504"} -{"city": "SCRANTON", "loc": [-75.665738, 41.39145], "pop": 21733, "state": "PA", "_id": "18505"} -{"city": "MOOSIC", "loc": [-75.717093, 41.361492], "pop": 5339, "state": "PA", "_id": "18507"} -{"city": "SCRANTON", "loc": [-75.662529, 41.438917], "pop": 13555, "state": "PA", "_id": "18508"} -{"city": "SCRANTON", "loc": [-75.646454, 41.427353], "pop": 15432, "state": "PA", "_id": "18509"} -{"city": "SCRANTON", "loc": [-75.648397, 41.408039], "pop": 14306, "state": "PA", "_id": "18510"} -{"city": "DUNMORE", "loc": [-75.62294, 41.426184], "pop": 14428, "state": "PA", "_id": "18512"} -{"city": "TAYLOR", "loc": [-75.715848, 41.390442], "pop": 5738, "state": "PA", "_id": "18517"} -{"city": "OLD FORGE", "loc": [-75.739075, 41.370076], "pop": 8834, "state": "PA", "_id": "18518"} -{"city": "DICKSON CITY", "loc": [-75.624343, 41.462306], "pop": 5012, "state": "PA", "_id": "18519"} -{"city": "BERWICK", "loc": [-76.244269, 41.066477], "pop": 19977, "state": "PA", "_id": "18603"} -{"city": "BLAKESLEE", "loc": [-75.534309, 41.048502], "pop": 426, "state": "PA", "_id": "18610"} -{"city": "COLLEGE MISERICO", "loc": [-75.958911, 41.363762], "pop": 13726, "state": "PA", "_id": "18612"} -{"city": "DUSHORE", "loc": [-76.402145, 41.523213], "pop": 1739, "state": "PA", "_id": "18614"} -{"city": "FALLS", "loc": [-75.856004, 41.466677], "pop": 2349, "state": "PA", "_id": "18615"} -{"city": "FORKSVILLE", "loc": [-76.60079, 41.526925], "pop": 1080, "state": "PA", "_id": "18616"} -{"city": "GLEN LYON", "loc": [-76.074578, 41.174635], "pop": 2082, "state": "PA", "_id": "18617"} -{"city": "HARVEYS LAKE", "loc": [-76.04506, 41.359181], "pop": 3404, "state": "PA", "_id": "18618"} -{"city": "HILLSGROVE", "loc": [-76.697914, 41.448159], "pop": 337, "state": "PA", "_id": "18619"} -{"city": "HUNLOCK CREEK", "loc": [-76.087915, 41.245923], "pop": 4207, "state": "PA", "_id": "18621"} -{"city": "HUNTINGTON MILLS", "loc": [-76.197342, 41.200905], "pop": 345, "state": "PA", "_id": "18622"} -{"city": "LACEYVILLE", "loc": [-76.142566, 41.66621], "pop": 3195, "state": "PA", "_id": "18623"} -{"city": "LAKE HARMONY", "loc": [-75.633129, 41.05424], "pop": 1203, "state": "PA", "_id": "18624"} -{"city": "LOPEZ", "loc": [-76.300206, 41.418002], "pop": 600, "state": "PA", "_id": "18628"} -{"city": "MEHOOPANY", "loc": [-76.103462, 41.558695], "pop": 1630, "state": "PA", "_id": "18629"} -{"city": "MESHOPPEN", "loc": [-76.015464, 41.639163], "pop": 3307, "state": "PA", "_id": "18630"} -{"city": "MIFFLINVILLE", "loc": [-76.292396, 41.023473], "pop": 2297, "state": "PA", "_id": "18631"} -{"city": "MILDRED", "loc": [-76.38313, 41.479236], "pop": 500, "state": "PA", "_id": "18632"} -{"city": "NANTICOKE", "loc": [-76.004419, 41.19634], "pop": 14778, "state": "PA", "_id": "18634"} -{"city": "NESCOPECK", "loc": [-76.19809, 41.046887], "pop": 2723, "state": "PA", "_id": "18635"} -{"city": "NOXEN", "loc": [-76.045952, 41.418131], "pop": 1653, "state": "PA", "_id": "18636"} -{"city": "PITTSTON", "loc": [-75.788492, 41.317501], "pop": 18888, "state": "PA", "_id": "18640"} -{"city": "AVOCA", "loc": [-75.744655, 41.330857], "pop": 6485, "state": "PA", "_id": "18641"} -{"city": "DURYEA", "loc": [-75.761104, 41.348557], "pop": 4262, "state": "PA", "_id": "18642"} -{"city": "WEST PITTSTON", "loc": [-75.816651, 41.337964], "pop": 13853, "state": "PA", "_id": "18643"} -{"city": "WYOMING", "loc": [-75.854071, 41.319713], "pop": 8126, "state": "PA", "_id": "18644"} -{"city": "PLYMOUTH", "loc": [-75.948064, 41.245798], "pop": 11895, "state": "PA", "_id": "18651"} -{"city": "MOCANAQUA", "loc": [-76.167096, 41.176674], "pop": 6324, "state": "PA", "_id": "18655"} -{"city": "SWEET VALLEY", "loc": [-76.133907, 41.30663], "pop": 3237, "state": "PA", "_id": "18656"} -{"city": "CENTER MORELAND", "loc": [-75.941043, 41.550687], "pop": 9921, "state": "PA", "_id": "18657"} -{"city": "WAPWALLOPEN", "loc": [-76.085729, 41.06797], "pop": 1198, "state": "PA", "_id": "18660"} -{"city": "WHITE HAVEN", "loc": [-75.771492, 41.079049], "pop": 4435, "state": "PA", "_id": "18661"} -{"city": "WILKES BARRE", "loc": [-75.884063, 41.244892], "pop": 4320, "state": "PA", "_id": "18701"} -{"city": "HANOVER TOWNSHIP", "loc": [-75.882557, 41.236512], "pop": 54119, "state": "PA", "_id": "18702"} -{"city": "KINGSTON", "loc": [-75.890338, 41.274223], "pop": 33661, "state": "PA", "_id": "18704"} -{"city": "WILKES BARRE", "loc": [-75.845309, 41.268921], "pop": 17504, "state": "PA", "_id": "18705"} -{"city": "ASHLEY", "loc": [-75.918157, 41.206709], "pop": 7980, "state": "PA", "_id": "18706"} -{"city": "MOUNTAIN TOP", "loc": [-75.937642, 41.134975], "pop": 13244, "state": "PA", "_id": "18707"} -{"city": "SHAVERTOWN", "loc": [-75.97108, 41.299802], "pop": 12656, "state": "PA", "_id": "18708"} -{"city": "LUZERNE", "loc": [-75.893475, 41.284257], "pop": 3157, "state": "PA", "_id": "18709"} -{"city": "MONTROSE", "loc": [-75.882055, 41.839584], "pop": 6624, "state": "PA", "_id": "18801"} -{"city": "ATHENS", "loc": [-76.488855, 41.949002], "pop": 7602, "state": "PA", "_id": "18810"} -{"city": "BRACKNEY", "loc": [-75.937527, 41.966614], "pop": 1645, "state": "PA", "_id": "18812"} -{"city": "EAST SMITHFIELD", "loc": [-76.617207, 41.863115], "pop": 1520, "state": "PA", "_id": "18817"} -{"city": "FRIENDSVILLE", "loc": [-76.02569, 41.916445], "pop": 1844, "state": "PA", "_id": "18818"} -{"city": "GREAT BEND", "loc": [-75.732742, 41.977513], "pop": 1654, "state": "PA", "_id": "18821"} -{"city": "HALLSTEAD", "loc": [-75.782595, 41.959798], "pop": 2970, "state": "PA", "_id": "18822"} -{"city": "HARFORD", "loc": [-75.678632, 41.779891], "pop": 1155, "state": "PA", "_id": "18823"} -{"city": "HOP BOTTOM", "loc": [-75.789656, 41.693196], "pop": 1088, "state": "PA", "_id": "18824"} -{"city": "JACKSON", "loc": [-75.609136, 41.864881], "pop": 668, "state": "PA", "_id": "18825"} -{"city": "KINGSLEY", "loc": [-75.783101, 41.765856], "pop": 1159, "state": "PA", "_id": "18826"} -{"city": "LAWTON", "loc": [-76.091214, 41.757294], "pop": 15, "state": "PA", "_id": "18828"} -{"city": "LE RAYSVILLE", "loc": [-76.179604, 41.843415], "pop": 1020, "state": "PA", "_id": "18829"} -{"city": "LITTLE MEADOWS", "loc": [-76.118472, 41.976593], "pop": 750, "state": "PA", "_id": "18830"} -{"city": "MILAN", "loc": [-76.532777, 41.896555], "pop": 328, "state": "PA", "_id": "18831"} -{"city": "MONROETON", "loc": [-76.500995, 41.699387], "pop": 2332, "state": "PA", "_id": "18832"} -{"city": "NEW ALBANY", "loc": [-76.43983, 41.59867], "pop": 1390, "state": "PA", "_id": "18833"} -{"city": "NEW MILFORD", "loc": [-75.717113, 41.866409], "pop": 2684, "state": "PA", "_id": "18834"} -{"city": "ROME", "loc": [-76.301498, 41.863403], "pop": 2053, "state": "PA", "_id": "18837"} -{"city": "RUSHVILLE", "loc": [-76.050955, 41.776893], "pop": 1228, "state": "PA", "_id": "18839"} -{"city": "SAYRE", "loc": [-76.521757, 41.984222], "pop": 10320, "state": "PA", "_id": "18840"} -{"city": "SOUTH GIBSON", "loc": [-75.606723, 41.754373], "pop": 402, "state": "PA", "_id": "18842"} -{"city": "SPRINGVILLE", "loc": [-75.902472, 41.714684], "pop": 2003, "state": "PA", "_id": "18844"} -{"city": "STEVENSVILLE", "loc": [-76.171696, 41.771782], "pop": 401, "state": "PA", "_id": "18845"} -{"city": "SUGAR RUN", "loc": [-76.259785, 41.604057], "pop": 1057, "state": "PA", "_id": "18846"} -{"city": "SUSQUEHANNA", "loc": [-75.586249, 41.948669], "pop": 5137, "state": "PA", "_id": "18847"} -{"city": "TOWANDA", "loc": [-76.464527, 41.763758], "pop": 7449, "state": "PA", "_id": "18848"} -{"city": "ULSTER", "loc": [-76.487574, 41.840809], "pop": 2049, "state": "PA", "_id": "18850"} -{"city": "WARREN CENTER", "loc": [-76.196445, 41.939389], "pop": 927, "state": "PA", "_id": "18851"} -{"city": "WYALUSING", "loc": [-76.275433, 41.701499], "pop": 3439, "state": "PA", "_id": "18853"} -{"city": "WYSOX", "loc": [-76.383397, 41.782621], "pop": 2121, "state": "PA", "_id": "18854"} -{"city": "NEW BRITAIN", "loc": [-75.129987, 40.320391], "pop": 33133, "state": "PA", "_id": "18901"} -{"city": "CARVERSVILLE", "loc": [-75.063127, 40.39079], "pop": 579, "state": "PA", "_id": "18913"} -{"city": "CHALFONT", "loc": [-75.214938, 40.289175], "pop": 12838, "state": "PA", "_id": "18914"} -{"city": "COLMAR", "loc": [-75.266861, 40.271814], "pop": 2168, "state": "PA", "_id": "18915"} -{"city": "DUBLIN", "loc": [-75.204453, 40.371996], "pop": 1985, "state": "PA", "_id": "18917"} -{"city": "ERWINNA", "loc": [-75.080372, 40.508689], "pop": 573, "state": "PA", "_id": "18920"} -{"city": "FOUNTAINVILLE", "loc": [-75.153627, 40.336815], "pop": 261, "state": "PA", "_id": "18923"} -{"city": "FURLONG", "loc": [-75.064946, 40.294518], "pop": 3789, "state": "PA", "_id": "18925"} -{"city": "HILLTOWN", "loc": [-75.27118, 40.34762], "pop": 881, "state": "PA", "_id": "18927"} -{"city": "JAMISON", "loc": [-75.096093, 40.256599], "pop": 3797, "state": "PA", "_id": "18929"} -{"city": "KINTNERSVILLE", "loc": [-75.211708, 40.531009], "pop": 2997, "state": "PA", "_id": "18930"} -{"city": "LINE LEXINGTON", "loc": [-75.255535, 40.288781], "pop": 301, "state": "PA", "_id": "18932"} -{"city": "LUMBERVILLE", "loc": [-75.055166, 40.407103], "pop": 382, "state": "PA", "_id": "18933"} -{"city": "MECHANICSVILLE", "loc": [-75.062962, 40.343821], "pop": 429, "state": "PA", "_id": "18934"} -{"city": "MONTGOMERYVILLE", "loc": [-75.234643, 40.251353], "pop": 5520, "state": "PA", "_id": "18936"} -{"city": "NEW HOPE", "loc": [-74.983889, 40.355613], "pop": 7111, "state": "PA", "_id": "18938"} -{"city": "GEORGE SCHOOL", "loc": [-74.94313, 40.245817], "pop": 21946, "state": "PA", "_id": "18940"} -{"city": "OTTSVILLE", "loc": [-75.157009, 40.459239], "pop": 2976, "state": "PA", "_id": "18942"} -{"city": "PERKASIE", "loc": [-75.264803, 40.376526], "pop": 18235, "state": "PA", "_id": "18944"} -{"city": "PIPERSVILLE", "loc": [-75.107398, 40.4262], "pop": 3856, "state": "PA", "_id": "18947"} -{"city": "QUAKERTOWN", "loc": [-75.350667, 40.4411], "pop": 28546, "state": "PA", "_id": "18951"} -{"city": "RICHBORO", "loc": [-75.002936, 40.216672], "pop": 8006, "state": "PA", "_id": "18954"} -{"city": "RICHLANDTOWN", "loc": [-75.32193, 40.472166], "pop": 1199, "state": "PA", "_id": "18955"} -{"city": "SELLERSVILLE", "loc": [-75.318953, 40.362024], "pop": 8387, "state": "PA", "_id": "18960"} -{"city": "BETHTON", "loc": [-75.321339, 40.312796], "pop": 9350, "state": "PA", "_id": "18964"} -{"city": "HOLLAND", "loc": [-75.005994, 40.190212], "pop": 38866, "state": "PA", "_id": "18966"} -{"city": "TELFORD", "loc": [-75.352001, 40.320478], "pop": 10929, "state": "PA", "_id": "18969"} -{"city": "UPPER BLACK EDDY", "loc": [-75.125858, 40.541093], "pop": 3443, "state": "PA", "_id": "18972"} -{"city": "WARMINSTER", "loc": [-75.090513, 40.206676], "pop": 37759, "state": "PA", "_id": "18974"} -{"city": "WARRINGTON", "loc": [-75.135392, 40.246438], "pop": 13862, "state": "PA", "_id": "18976"} -{"city": "WASHINGTON CROSS", "loc": [-74.882859, 40.291906], "pop": 2824, "state": "PA", "_id": "18977"} -{"city": "OGONTZ CAMPUS", "loc": [-75.128918, 40.128141], "pop": 17302, "state": "PA", "_id": "19001"} -{"city": "MAPLE GLEN", "loc": [-75.207234, 40.166318], "pop": 24133, "state": "PA", "_id": "19002"} -{"city": "ARDMORE", "loc": [-75.29665, 40.001971], "pop": 12443, "state": "PA", "_id": "19003"} -{"city": "BALA CYNWYD", "loc": [-75.23421, 40.01179], "pop": 9239, "state": "PA", "_id": "19004"} -{"city": "HUNTINGDON VALLE", "loc": [-75.058979, 40.129686], "pop": 19866, "state": "PA", "_id": "19006"} -{"city": "TULLYTOWN", "loc": [-74.860718, 40.109174], "pop": 22334, "state": "PA", "_id": "19007"} -{"city": "BROOMALL", "loc": [-75.360214, 39.974666], "pop": 20432, "state": "PA", "_id": "19008"} -{"city": "BRYN MAWR", "loc": [-75.329487, 40.023618], "pop": 21826, "state": "PA", "_id": "19010"} -{"city": "CHELTENHAM", "loc": [-75.104774, 40.060327], "pop": 6912, "state": "PA", "_id": "19012"} -{"city": "CHESTER", "loc": [-75.374687, 39.849817], "pop": 49144, "state": "PA", "_id": "19013"} -{"city": "ASTON", "loc": [-75.43321, 39.864282], "pop": 18171, "state": "PA", "_id": "19014"} -{"city": "BROOKHAVEN", "loc": [-75.388483, 39.865355], "pop": 17321, "state": "PA", "_id": "19015"} -{"city": "PRIMOS SECANE", "loc": [-75.299592, 39.923579], "pop": 23885, "state": "PA", "_id": "19018"} -{"city": "BENSALEM", "loc": [-74.937753, 40.110881], "pop": 51884, "state": "PA", "_id": "19020"} -{"city": "CROYDON", "loc": [-74.899077, 40.093322], "pop": 10198, "state": "PA", "_id": "19021"} -{"city": "CRUM LYNNE", "loc": [-75.337397, 39.868457], "pop": 769, "state": "PA", "_id": "19022"} -{"city": "COLLINGDALE", "loc": [-75.266226, 39.916732], "pop": 23274, "state": "PA", "_id": "19023"} -{"city": "DRESHER", "loc": [-75.162379, 40.143141], "pop": 4089, "state": "PA", "_id": "19025"} -{"city": "PILGRIM GARDENS", "loc": [-75.303479, 39.949197], "pop": 32559, "state": "PA", "_id": "19026"} -{"city": "LESTER", "loc": [-75.293521, 39.866864], "pop": 4440, "state": "PA", "_id": "19029"} -{"city": "FAIRLESS HILLS", "loc": [-74.851923, 40.174822], "pop": 13433, "state": "PA", "_id": "19030"} -{"city": "FLOURTOWN", "loc": [-75.21148, 40.106774], "pop": 4284, "state": "PA", "_id": "19031"} -{"city": "FOLCROFT", "loc": [-75.282117, 39.890508], "pop": 7399, "state": "PA", "_id": "19032"} -{"city": "FOLSOM", "loc": [-75.329567, 39.890129], "pop": 7677, "state": "PA", "_id": "19033"} -{"city": "FORT WASHINGTON", "loc": [-75.202175, 40.138592], "pop": 6787, "state": "PA", "_id": "19034"} -{"city": "GLADWYNE", "loc": [-75.282082, 40.045118], "pop": 3742, "state": "PA", "_id": "19035"} -{"city": "GLENOLDEN", "loc": [-75.294559, 39.904848], "pop": 14390, "state": "PA", "_id": "19036"} -{"city": "GLENSIDE", "loc": [-75.154964, 40.10959], "pop": 23067, "state": "PA", "_id": "19038"} -{"city": "HATBORO", "loc": [-75.107182, 40.178547], "pop": 21765, "state": "PA", "_id": "19040"} -{"city": "HAVERFORD", "loc": [-75.312116, 40.009739], "pop": 6560, "state": "PA", "_id": "19041"} -{"city": "HOLMES", "loc": [-75.308674, 39.900284], "pop": 2747, "state": "PA", "_id": "19043"} -{"city": "HORSHAM", "loc": [-75.147932, 40.182057], "pop": 15131, "state": "PA", "_id": "19044"} -{"city": "MEADOWBROOK", "loc": [-75.117273, 40.100477], "pop": 14402, "state": "PA", "_id": "19046"} -{"city": "PENNDEL", "loc": [-74.915101, 40.175055], "pop": 34108, "state": "PA", "_id": "19047"} -{"city": "YEADON", "loc": [-75.264872, 39.93779], "pop": 30411, "state": "PA", "_id": "19050"} -{"city": "FEASTERVILLE TRE", "loc": [-74.983758, 40.151188], "pop": 24877, "state": "PA", "_id": "19053"} -{"city": "LEVITTOWN", "loc": [-74.823138, 40.168142], "pop": 16390, "state": "PA", "_id": "19054"} -{"city": "LEVITTOWN", "loc": [-74.83714, 40.148329], "pop": 14924, "state": "PA", "_id": "19055"} -{"city": "LEVITTOWN", "loc": [-74.882632, 40.151861], "pop": 15227, "state": "PA", "_id": "19056"} -{"city": "LEVITTOWN", "loc": [-74.861366, 40.143359], "pop": 17014, "state": "PA", "_id": "19057"} -{"city": "BOOTHWYN", "loc": [-75.448309, 39.833934], "pop": 21328, "state": "PA", "_id": "19061"} -{"city": "GLEN RIDDLE LIMA", "loc": [-75.407226, 39.915562], "pop": 36385, "state": "PA", "_id": "19063"} -{"city": "SPRINGFIELD", "loc": [-75.333786, 39.929599], "pop": 25179, "state": "PA", "_id": "19064"} -{"city": "MERION STATION", "loc": [-75.250302, 40.003043], "pop": 5633, "state": "PA", "_id": "19066"} -{"city": "YARDLEY", "loc": [-74.822153, 40.212064], "pop": 47492, "state": "PA", "_id": "19067"} -{"city": "MORTON", "loc": [-75.323785, 39.906292], "pop": 6517, "state": "PA", "_id": "19070"} -{"city": "NARBERTH", "loc": [-75.2594, 40.01768], "pop": 9846, "state": "PA", "_id": "19072"} -{"city": "NEWTOWN SQUARE", "loc": [-75.406997, 39.986292], "pop": 15024, "state": "PA", "_id": "19073"} -{"city": "NORWOOD", "loc": [-75.297247, 39.887026], "pop": 6189, "state": "PA", "_id": "19074"} -{"city": "ORELAND", "loc": [-75.18685, 40.113197], "pop": 7397, "state": "PA", "_id": "19075"} -{"city": "PROSPECT PARK", "loc": [-75.308165, 39.885737], "pop": 6769, "state": "PA", "_id": "19076"} -{"city": "RIDLEY PARK", "loc": [-75.321517, 39.878411], "pop": 12624, "state": "PA", "_id": "19078"} -{"city": "SHARON HILL", "loc": [-75.269524, 39.903511], "pop": 9893, "state": "PA", "_id": "19079"} -{"city": "SWARTHMORE", "loc": [-75.347428, 39.896724], "pop": 9885, "state": "PA", "_id": "19081"} -{"city": "UPPER DARBY", "loc": [-75.268128, 39.95785], "pop": 36400, "state": "PA", "_id": "19082"} -{"city": "HAVERTOWN", "loc": [-75.310613, 39.97736], "pop": 36702, "state": "PA", "_id": "19083"} -{"city": "VILLANOVA", "loc": [-75.345866, 40.039875], "pop": 7034, "state": "PA", "_id": "19085"} -{"city": "WALLINGFORD", "loc": [-75.372131, 39.887054], "pop": 11005, "state": "PA", "_id": "19086"} -{"city": "RADNOR", "loc": [-75.40416, 40.059554], "pop": 30671, "state": "PA", "_id": "19087"} -{"city": "WILLOW GROVE NAS", "loc": [-75.121297, 40.146725], "pop": 19558, "state": "PA", "_id": "19090"} -{"city": "WOODLYN", "loc": [-75.346309, 39.875993], "pop": 5186, "state": "PA", "_id": "19094"} -{"city": "WYNCOTE", "loc": [-75.152417, 40.086673], "pop": 6164, "state": "PA", "_id": "19095"} -{"city": "WYNNEWOOD", "loc": [-75.275984, 40.0], "pop": 8285, "state": "PA", "_id": "19096"} -{"city": "PHILADELPHIA", "loc": [-75.166109, 39.948908], "pop": 3623, "state": "PA", "_id": "19102"} -{"city": "PHILADELPHIA", "loc": [-75.174136, 39.951285], "pop": 17665, "state": "PA", "_id": "19103"} -{"city": "PHILADELPHIA", "loc": [-75.202445, 39.959732], "pop": 51295, "state": "PA", "_id": "19104"} -{"city": "PHILADELPHIA", "loc": [-75.147271, 39.94742], "pop": 7043, "state": "PA", "_id": "19106"} -{"city": "PHILADELPHIA", "loc": [-75.159339, 39.94867], "pop": 9634, "state": "PA", "_id": "19107"} -{"city": "PHILADELPHIA", "loc": [-75.081792, 40.059635], "pop": 61416, "state": "PA", "_id": "19111"} -{"city": "PHILADELPHIA", "loc": [-75.178207, 39.889252], "pop": 4516, "state": "PA", "_id": "19112"} -{"city": "PHILADELPHIA", "loc": [-75.275196, 39.864998], "pop": 0, "state": "PA", "_id": "19113"} -{"city": "PHILADELPHIA", "loc": [-74.999032, 40.063356], "pop": 31199, "state": "PA", "_id": "19114"} -{"city": "PHILADELPHIA", "loc": [-75.041036, 40.090286], "pop": 31339, "state": "PA", "_id": "19115"} -{"city": "PHILADELPHIA", "loc": [-75.019803, 40.116599], "pop": 32898, "state": "PA", "_id": "19116"} -{"city": "ELKINS PARK", "loc": [-75.127669, 40.075798], "pop": 12813, "state": "PA", "_id": "19117"} -{"city": "PHILADELPHIA", "loc": [-75.2006, 40.081247], "pop": 19549, "state": "PA", "_id": "19118"} -{"city": "PHILADELPHIA", "loc": [-75.186564, 40.054681], "pop": 29935, "state": "PA", "_id": "19119"} -{"city": "PHILADELPHIA", "loc": [-75.121256, 40.034254], "pop": 63223, "state": "PA", "_id": "19120"} -{"city": "PHILADELPHIA", "loc": [-75.174005, 39.981085], "pop": 46705, "state": "PA", "_id": "19121"} -{"city": "PHILADELPHIA", "loc": [-75.145882, 39.978014], "pop": 21177, "state": "PA", "_id": "19122"} -{"city": "PHILADELPHIA", "loc": [-75.150968, 39.965975], "pop": 12270, "state": "PA", "_id": "19123"} -{"city": "PHILADELPHIA", "loc": [-75.089526, 40.017798], "pop": 60009, "state": "PA", "_id": "19124"} -{"city": "PHILADELPHIA", "loc": [-75.126156, 39.978751], "pop": 24521, "state": "PA", "_id": "19125"} -{"city": "PHILADELPHIA", "loc": [-75.137854, 40.056839], "pop": 22000, "state": "PA", "_id": "19126"} -{"city": "PHILADELPHIA", "loc": [-75.224167, 40.027512], "pop": 6028, "state": "PA", "_id": "19127"} -{"city": "PHILADELPHIA", "loc": [-75.223084, 40.040247], "pop": 36845, "state": "PA", "_id": "19128"} -{"city": "PHILADELPHIA", "loc": [-75.186149, 40.011816], "pop": 13430, "state": "PA", "_id": "19129"} -{"city": "PHILADELPHIA", "loc": [-75.173467, 39.967677], "pop": 21544, "state": "PA", "_id": "19130"} -{"city": "PHILADELPHIA", "loc": [-75.228226, 39.98447], "pop": 48270, "state": "PA", "_id": "19131"} -{"city": "PHILADELPHIA", "loc": [-75.16982, 39.995393], "pop": 49011, "state": "PA", "_id": "19132"} -{"city": "PHILADELPHIA", "loc": [-75.141505, 39.992467], "pop": 32608, "state": "PA", "_id": "19133"} -{"city": "PHILADELPHIA", "loc": [-75.113284, 39.99252], "pop": 58607, "state": "PA", "_id": "19134"} -{"city": "PHILADELPHIA", "loc": [-75.051827, 40.024694], "pop": 32188, "state": "PA", "_id": "19135"} -{"city": "PHILADELPHIA", "loc": [-75.024388, 40.042159], "pop": 40682, "state": "PA", "_id": "19136"} -{"city": "PHILADELPHIA", "loc": [-75.072654, 40.000849], "pop": 8395, "state": "PA", "_id": "19137"} -{"city": "PHILADELPHIA", "loc": [-75.156898, 40.05683], "pop": 37458, "state": "PA", "_id": "19138"} -{"city": "PHILADELPHIA", "loc": [-75.230301, 39.961166], "pop": 48467, "state": "PA", "_id": "19139"} -{"city": "PHILADELPHIA", "loc": [-75.145626, 40.011771], "pop": 62864, "state": "PA", "_id": "19140"} -{"city": "PHILADELPHIA", "loc": [-75.145109, 40.036473], "pop": 38546, "state": "PA", "_id": "19141"} -{"city": "PHILADELPHIA", "loc": [-75.233796, 39.922332], "pop": 29171, "state": "PA", "_id": "19142"} -{"city": "PHILADELPHIA", "loc": [-75.228819, 39.944815], "pop": 80454, "state": "PA", "_id": "19143"} -{"city": "PHILADELPHIA", "loc": [-75.173099, 40.033773], "pop": 46612, "state": "PA", "_id": "19144"} -{"city": "PHILADELPHIA", "loc": [-75.181194, 39.922724], "pop": 52538, "state": "PA", "_id": "19145"} -{"city": "PHILADELPHIA", "loc": [-75.179364, 39.937949], "pop": 38870, "state": "PA", "_id": "19146"} -{"city": "PHILADELPHIA", "loc": [-75.156324, 39.936175], "pop": 34634, "state": "PA", "_id": "19147"} -{"city": "PHILADELPHIA", "loc": [-75.159538, 39.92068], "pop": 49685, "state": "PA", "_id": "19148"} -{"city": "PHILADELPHIA", "loc": [-75.066374, 40.036915], "pop": 47535, "state": "PA", "_id": "19149"} -{"city": "PHILADELPHIA", "loc": [-75.170621, 40.07262], "pop": 27609, "state": "PA", "_id": "19150"} -{"city": "PHILADELPHIA", "loc": [-75.254492, 39.977199], "pop": 36265, "state": "PA", "_id": "19151"} -{"city": "PHILADELPHIA", "loc": [-75.047079, 40.060571], "pop": 31225, "state": "PA", "_id": "19152"} -{"city": "PHILADELPHIA", "loc": [-75.244431, 39.905512], "pop": 13375, "state": "PA", "_id": "19153"} -{"city": "PHILADELPHIA", "loc": [-74.978052, 40.089738], "pop": 38023, "state": "PA", "_id": "19154"} -{"city": "PAOLI", "loc": [-75.482702, 40.04259], "pop": 6969, "state": "PA", "_id": "19301"} -{"city": "ATGLEN", "loc": [-75.970343, 39.945782], "pop": 2318, "state": "PA", "_id": "19310"} -{"city": "AVONDALE", "loc": [-75.768694, 39.821904], "pop": 4161, "state": "PA", "_id": "19311"} -{"city": "BERWYN", "loc": [-75.447457, 40.041184], "pop": 9482, "state": "PA", "_id": "19312"} -{"city": "CHADDS FORD", "loc": [-75.588515, 39.864769], "pop": 4522, "state": "PA", "_id": "19317"} -{"city": "CHEYNEY", "loc": [-75.548738, 39.917496], "pop": 198, "state": "PA", "_id": "19319"} -{"city": "COATESVILLE", "loc": [-75.825299, 39.984313], "pop": 39595, "state": "PA", "_id": "19320"} -{"city": "COCHRANVILLE", "loc": [-75.921381, 39.875686], "pop": 3425, "state": "PA", "_id": "19330"} -{"city": "DEVON", "loc": [-75.422691, 40.045181], "pop": 6290, "state": "PA", "_id": "19333"} -{"city": "DOWNINGTOWN", "loc": [-75.718261, 40.016078], "pop": 35056, "state": "PA", "_id": "19335"} -{"city": "EXTON", "loc": [-75.643196, 40.046817], "pop": 12495, "state": "PA", "_id": "19341"} -{"city": "GLEN MILLS", "loc": [-75.504872, 39.901515], "pop": 11632, "state": "PA", "_id": "19342"} -{"city": "GLENMOORE", "loc": [-75.771103, 40.084602], "pop": 6959, "state": "PA", "_id": "19343"} -{"city": "HONEY BROOK", "loc": [-75.88432, 40.083227], "pop": 8615, "state": "PA", "_id": "19344"} -{"city": "KELTON", "loc": [-75.875827, 39.795501], "pop": 816, "state": "PA", "_id": "19346"} -{"city": "KENNETT SQUARE", "loc": [-75.70002, 39.855033], "pop": 16663, "state": "PA", "_id": "19348"} -{"city": "LANDENBERG", "loc": [-75.780707, 39.769558], "pop": 4787, "state": "PA", "_id": "19350"} -{"city": "LINCOLN UNIVERSI", "loc": [-75.881784, 39.780905], "pop": 6834, "state": "PA", "_id": "19352"} -{"city": "FRAZER", "loc": [-75.533021, 40.037123], "pop": 21709, "state": "PA", "_id": "19355"} -{"city": "NOTTINGHAM", "loc": [-76.035551, 39.74411], "pop": 2814, "state": "PA", "_id": "19362"} -{"city": "OXFORD", "loc": [-75.981522, 39.782704], "pop": 11545, "state": "PA", "_id": "19363"} -{"city": "PARKESBURG", "loc": [-75.926041, 39.965388], "pop": 5420, "state": "PA", "_id": "19365"} -{"city": "THORNDALE", "loc": [-75.762859, 40.000127], "pop": 1712, "state": "PA", "_id": "19372"} -{"city": "THORNTON", "loc": [-75.531344, 39.904127], "pop": 2305, "state": "PA", "_id": "19373"} -{"city": "TOUGHKENAMON", "loc": [-75.782533, 39.825117], "pop": 1045, "state": "PA", "_id": "19374"} -{"city": "WEST CHESTER", "loc": [-75.596231, 39.984458], "pop": 40656, "state": "PA", "_id": "19380"} -{"city": "WEST CHESTER", "loc": [-75.588197, 39.944081], "pop": 44164, "state": "PA", "_id": "19382"} -{"city": "WEST GROVE", "loc": [-75.837374, 39.825314], "pop": 7248, "state": "PA", "_id": "19390"} -{"city": "NORRISTOWN", "loc": [-75.330446, 40.124464], "pop": 46735, "state": "PA", "_id": "19401"} -{"city": "EAGLEVILLE", "loc": [-75.384672, 40.14335], "pop": 39184, "state": "PA", "_id": "19403"} -{"city": "BRIDGEPORT", "loc": [-75.340234, 40.103042], "pop": 5163, "state": "PA", "_id": "19405"} -{"city": "KING OF PRUSSIA", "loc": [-75.373706, 40.095581], "pop": 21049, "state": "PA", "_id": "19406"} -{"city": "PENLLYN", "loc": [-75.279656, 40.15939], "pop": 16645, "state": "PA", "_id": "19422"} -{"city": "CHESTER SPRINGS", "loc": [-75.639769, 40.097781], "pop": 4413, "state": "PA", "_id": "19425"} -{"city": "COLLEGEVILLE", "loc": [-75.448762, 40.189277], "pop": 16453, "state": "PA", "_id": "19426"} -{"city": "WEST CONSHOHOCKE", "loc": [-75.301332, 40.079848], "pop": 15924, "state": "PA", "_id": "19428"} -{"city": "FREDERICK", "loc": [-75.531975, 40.299924], "pop": 728, "state": "PA", "_id": "19435"} -{"city": "GWYNEDD", "loc": [-75.250746, 40.202089], "pop": 540, "state": "PA", "_id": "19436"} -{"city": "HARLEYSVILLE", "loc": [-75.388335, 40.265922], "pop": 16027, "state": "PA", "_id": "19438"} -{"city": "HATFIELD", "loc": [-75.297507, 40.277826], "pop": 16686, "state": "PA", "_id": "19440"} -{"city": "LAFAYETTE HILL", "loc": [-75.260052, 40.089597], "pop": 8312, "state": "PA", "_id": "19444"} -{"city": "LANSDALE", "loc": [-75.295512, 40.237776], "pop": 41034, "state": "PA", "_id": "19446"} -{"city": "MONT CLARE", "loc": [-75.499931, 40.13642], "pop": 1749, "state": "PA", "_id": "19453"} -{"city": "NORTH WALES", "loc": [-75.256483, 40.216593], "pop": 14886, "state": "PA", "_id": "19454"} -{"city": "PHOENIXVILLE", "loc": [-75.527192, 40.126704], "pop": 30460, "state": "PA", "_id": "19460"} -{"city": "PLYMOUTH MEETING", "loc": [-75.279559, 40.107735], "pop": 5206, "state": "PA", "_id": "19462"} -{"city": "SANATOGA", "loc": [-75.639256, 40.242989], "pop": 53017, "state": "PA", "_id": "19464"} -{"city": "LIMERICK", "loc": [-75.530548, 40.19286], "pop": 13825, "state": "PA", "_id": "19468"} -{"city": "SCHWENKSVILLE", "loc": [-75.460155, 40.247087], "pop": 13139, "state": "PA", "_id": "19473"} -{"city": "SPRING CITY", "loc": [-75.56969, 40.176477], "pop": 7988, "state": "PA", "_id": "19475"} -{"city": "SPRING HOUSE", "loc": [-75.237501, 40.186954], "pop": 2129, "state": "PA", "_id": "19477"} -{"city": "ZIEGLERSVILLE", "loc": [-75.485462, 40.281673], "pop": 290, "state": "PA", "_id": "19492"} -{"city": "ADAMSTOWN", "loc": [-76.056542, 40.242992], "pop": 1108, "state": "PA", "_id": "19501"} -{"city": "BALLY", "loc": [-75.587483, 40.400557], "pop": 973, "state": "PA", "_id": "19503"} -{"city": "BARTO", "loc": [-75.574889, 40.381501], "pop": 2509, "state": "PA", "_id": "19504"} -{"city": "BECHTELSVILLE", "loc": [-75.625701, 40.379454], "pop": 3640, "state": "PA", "_id": "19505"} -{"city": "BERNVILLE", "loc": [-76.124732, 40.455061], "pop": 6245, "state": "PA", "_id": "19506"} -{"city": "BETHEL", "loc": [-76.274209, 40.480834], "pop": 3523, "state": "PA", "_id": "19507"} -{"city": "BIRDSBORO", "loc": [-75.834373, 40.256304], "pop": 15266, "state": "PA", "_id": "19508"} -{"city": "BLANDON", "loc": [-75.883681, 40.443492], "pop": 2663, "state": "PA", "_id": "19510"} -{"city": "BOYERTOWN", "loc": [-75.660368, 40.333905], "pop": 17139, "state": "PA", "_id": "19512"} -{"city": "DOUGLASSVILLE", "loc": [-75.739673, 40.270876], "pop": 7852, "state": "PA", "_id": "19518"} -{"city": "ELVERSON", "loc": [-75.786563, 40.156781], "pop": 4592, "state": "PA", "_id": "19520"} -{"city": "EVANSVILLE", "loc": [-75.8144, 40.446766], "pop": 11505, "state": "PA", "_id": "19522"} -{"city": "GILBERTSVILLE", "loc": [-75.595296, 40.305941], "pop": 7250, "state": "PA", "_id": "19525"} -{"city": "HAMBURG", "loc": [-75.987361, 40.548799], "pop": 9959, "state": "PA", "_id": "19526"} -{"city": "KEMPTON", "loc": [-75.85127, 40.632794], "pop": 2387, "state": "PA", "_id": "19529"} -{"city": "KUTZTOWN", "loc": [-75.777395, 40.521354], "pop": 13345, "state": "PA", "_id": "19530"} -{"city": "LEESPORT", "loc": [-75.994421, 40.415216], "pop": 6199, "state": "PA", "_id": "19533"} -{"city": "LENHARTSVILLE", "loc": [-75.850002, 40.575284], "pop": 1840, "state": "PA", "_id": "19534"} -{"city": "MERTZTOWN", "loc": [-75.687202, 40.499183], "pop": 4058, "state": "PA", "_id": "19539"} -{"city": "MOHNTON", "loc": [-75.98332, 40.258442], "pop": 8402, "state": "PA", "_id": "19540"} -{"city": "MOHRSVILLE", "loc": [-76.012491, 40.478307], "pop": 3473, "state": "PA", "_id": "19541"} -{"city": "MORGANTOWN", "loc": [-75.899802, 40.155248], "pop": 2121, "state": "PA", "_id": "19543"} -{"city": "OLEY", "loc": [-75.770575, 40.383312], "pop": 3949, "state": "PA", "_id": "19547"} -{"city": "PORT CLINTON", "loc": [-76.026652, 40.581787], "pop": 328, "state": "PA", "_id": "19549"} -{"city": "ROBESONIA", "loc": [-76.13659, 40.355281], "pop": 5310, "state": "PA", "_id": "19551"} -{"city": "SHOEMAKERSVILLE", "loc": [-75.960313, 40.495495], "pop": 3947, "state": "PA", "_id": "19555"} -{"city": "TEMPLE", "loc": [-75.904582, 40.402504], "pop": 7503, "state": "PA", "_id": "19560"} -{"city": "TOPTON", "loc": [-75.701528, 40.502941], "pop": 1987, "state": "PA", "_id": "19562"} -{"city": "WERNERSVILLE", "loc": [-76.09014, 40.329289], "pop": 5056, "state": "PA", "_id": "19565"} -{"city": "WOMELSDORF", "loc": [-76.198511, 40.374333], "pop": 4034, "state": "PA", "_id": "19567"} -{"city": "READING", "loc": [-75.935132, 40.346621], "pop": 29968, "state": "PA", "_id": "19601"} -{"city": "READING", "loc": [-75.919229, 40.330604], "pop": 16303, "state": "PA", "_id": "19602"} -{"city": "READING", "loc": [-75.914262, 40.350721], "pop": 22987, "state": "PA", "_id": "19604"} -{"city": "READING", "loc": [-75.932769, 40.38859], "pop": 13878, "state": "PA", "_id": "19605"} -{"city": "MOUNT PENN", "loc": [-75.868178, 40.325109], "pop": 27732, "state": "PA", "_id": "19606"} -{"city": "SHILLINGTON", "loc": [-75.953103, 40.299696], "pop": 19281, "state": "PA", "_id": "19607"} -{"city": "SINKING SPRING", "loc": [-76.024086, 40.31449], "pop": 13037, "state": "PA", "_id": "19608"} -{"city": "WEST LAWN", "loc": [-75.995347, 40.325778], "pop": 9845, "state": "PA", "_id": "19609"} -{"city": "WYOMISSING", "loc": [-75.976382, 40.333478], "pop": 12394, "state": "PA", "_id": "19610"} -{"city": "READING", "loc": [-75.944188, 40.324989], "pop": 10763, "state": "PA", "_id": "19611"} -{"city": "ASHAWAY", "loc": [-71.783745, 41.423054], "pop": 2472, "state": "RI", "_id": "02804"} -{"city": "BARRINGTON", "loc": [-71.317497, 41.744334], "pop": 15849, "state": "RI", "_id": "02806"} -{"city": "BLOCK ISLAND", "loc": [-71.574825, 41.171546], "pop": 836, "state": "RI", "_id": "02807"} -{"city": "BRADFORD", "loc": [-71.746453, 41.411448], "pop": 2184, "state": "RI", "_id": "02808"} -{"city": "BRISTOL", "loc": [-71.267558, 41.68247], "pop": 21625, "state": "RI", "_id": "02809"} -{"city": "RICHMOND", "loc": [-71.650279, 41.46941], "pop": 1011, "state": "RI", "_id": "02812"} -{"city": "CHARLESTOWN", "loc": [-71.661455, 41.400749], "pop": 6663, "state": "RI", "_id": "02813"} -{"city": "CHEPACHET", "loc": [-71.679483, 41.91549], "pop": 8191, "state": "RI", "_id": "02814"} -{"city": "CLAYVILLE", "loc": [-71.670589, 41.777762], "pop": 45, "state": "RI", "_id": "02815"} -{"city": "COVENTRY", "loc": [-71.576794, 41.69143], "pop": 29842, "state": "RI", "_id": "02816"} -{"city": "WEST GREENWICH", "loc": [-71.643549, 41.639977], "pop": 3246, "state": "RI", "_id": "02817"} -{"city": "EAST GREENWICH", "loc": [-71.474009, 41.649777], "pop": 16180, "state": "RI", "_id": "02818"} -{"city": "02821", "loc": [-71.780767, 41.626721], "pop": 246, "state": "RI", "_id": "02821"} -{"city": "EXETER", "loc": [-71.607626, 41.574031], "pop": 3774, "state": "RI", "_id": "02822"} -{"city": "FOSTER", "loc": [-71.718748, 41.781455], "pop": 5175, "state": "RI", "_id": "02825"} -{"city": "GREENE", "loc": [-71.735607, 41.706151], "pop": 1241, "state": "RI", "_id": "02827"} -{"city": "GREENVILLE", "loc": [-71.556923, 41.873409], "pop": 6945, "state": "RI", "_id": "02828"} -{"city": "HARRISVILLE", "loc": [-71.653405, 41.976379], "pop": 6384, "state": "RI", "_id": "02830"} -{"city": "HOPE", "loc": [-71.561225, 41.751603], "pop": 3653, "state": "RI", "_id": "02831"} -{"city": "RICHMOND", "loc": [-71.734862, 41.506974], "pop": 3466, "state": "RI", "_id": "02832"} -{"city": "JAMESTOWN", "loc": [-71.376108, 41.516405], "pop": 4999, "state": "RI", "_id": "02835"} -{"city": "RICHMOND", "loc": [-71.683992, 41.477694], "pop": 183, "state": "RI", "_id": "02836"} -{"city": "LITTLE COMPTON", "loc": [-71.161215, 41.52204], "pop": 3341, "state": "RI", "_id": "02837"} -{"city": "MANVILLE", "loc": [-71.474113, 41.96888], "pop": 3259, "state": "RI", "_id": "02838"} -{"city": "MIDDLETOWN", "loc": [-71.30348, 41.504502], "pop": 47687, "state": "RI", "_id": "02840"} -{"city": "NORTH KINGSTOWN", "loc": [-71.462494, 41.589426], "pop": 22325, "state": "RI", "_id": "02852"} -{"city": "NORTH SCITUATE", "loc": [-71.624187, 41.8439], "pop": 9563, "state": "RI", "_id": "02857"} -{"city": "OAKLAND", "loc": [-71.642925, 41.963637], "pop": 462, "state": "RI", "_id": "02858"} -{"city": "PASCOAG", "loc": [-71.709866, 41.962728], "pop": 7156, "state": "RI", "_id": "02859"} -{"city": "PAWTUCKET", "loc": [-71.390713, 41.872873], "pop": 45442, "state": "RI", "_id": "02860"} -{"city": "PAWTUCKET", "loc": [-71.356001, 41.881384], "pop": 27013, "state": "RI", "_id": "02861"} -{"city": "CENTRAL FALLS", "loc": [-71.394527, 41.888263], "pop": 17380, "state": "RI", "_id": "02863"} -{"city": "CUMBERLAND", "loc": [-71.415419, 41.948352], "pop": 29327, "state": "RI", "_id": "02864"} -{"city": "LINCOLN", "loc": [-71.434777, 41.908906], "pop": 14765, "state": "RI", "_id": "02865"} -{"city": "PORTSMOUTH", "loc": [-71.252018, 41.594397], "pop": 16707, "state": "RI", "_id": "02871"} -{"city": "PRUDENCE ISLAND", "loc": [-71.311827, 41.613606], "pop": 150, "state": "RI", "_id": "02872"} -{"city": "SAUNDERSTOWN", "loc": [-71.442693, 41.510528], "pop": 3196, "state": "RI", "_id": "02874"} -{"city": "SLATERSVILLE", "loc": [-71.5682, 42.001478], "pop": 639, "state": "RI", "_id": "02876"} -{"city": "SLOCUM", "loc": [-71.537169, 41.521237], "pop": 1114, "state": "RI", "_id": "02877"} -{"city": "TIVERTON", "loc": [-71.180823, 41.633839], "pop": 14310, "state": "RI", "_id": "02878"} -{"city": "NARRAGANSETT", "loc": [-71.525138, 41.430195], "pop": 13422, "state": "RI", "_id": "02879"} -{"city": "KINGSTON", "loc": [-71.529239, 41.480295], "pop": 7683, "state": "RI", "_id": "02881"} -{"city": "NARRAGANSETT", "loc": [-71.46164, 41.435313], "pop": 13596, "state": "RI", "_id": "02882"} -{"city": "PEACE DALE", "loc": [-71.500057, 41.45157], "pop": 1652, "state": "RI", "_id": "02883"} -{"city": "WARREN", "loc": [-71.270165, 41.725618], "pop": 11385, "state": "RI", "_id": "02885"} -{"city": "WARWICK", "loc": [-71.447591, 41.702601], "pop": 40845, "state": "RI", "_id": "02886"} -{"city": "WARWICK", "loc": [-71.40836, 41.74936], "pop": 20869, "state": "RI", "_id": "02888"} -{"city": "WARWICK", "loc": [-71.390146, 41.714069], "pop": 20849, "state": "RI", "_id": "02889"} -{"city": "WESTERLY", "loc": [-71.812643, 41.369128], "pop": 20290, "state": "RI", "_id": "02891"} -{"city": "RICHMOND", "loc": [-71.599076, 41.506716], "pop": 3943, "state": "RI", "_id": "02892"} -{"city": "WEST WARWICK", "loc": [-71.518349, 41.700433], "pop": 27821, "state": "RI", "_id": "02893"} -{"city": "WOOD RIVER JUNCT", "loc": [-71.709512, 41.453771], "pop": 684, "state": "RI", "_id": "02894"} -{"city": "NORTH SMITHFIELD", "loc": [-71.513683, 41.99948], "pop": 53733, "state": "RI", "_id": "02895"} -{"city": "RICHMOND", "loc": [-71.683973, 41.523362], "pop": 1508, "state": "RI", "_id": "02898"} -{"city": "PROVIDENCE", "loc": [-71.415801, 41.820002], "pop": 9093, "state": "RI", "_id": "02903"} -{"city": "CENTREDALE", "loc": [-71.438102, 41.860461], "pop": 28119, "state": "RI", "_id": "02904"} -{"city": "CRANSTON", "loc": [-71.403146, 41.786568], "pop": 24885, "state": "RI", "_id": "02905"} -{"city": "PROVIDENCE", "loc": [-71.397065, 41.835104], "pop": 31069, "state": "RI", "_id": "02906"} -{"city": "CRANSTON", "loc": [-71.424039, 41.800842], "pop": 25668, "state": "RI", "_id": "02907"} -{"city": "PROVIDENCE", "loc": [-71.437684, 41.838294], "pop": 35933, "state": "RI", "_id": "02908"} -{"city": "CRANSTON", "loc": [-71.448165, 41.816777], "pop": 34261, "state": "RI", "_id": "02909"} -{"city": "CRANSTON", "loc": [-71.438331, 41.776572], "pop": 21128, "state": "RI", "_id": "02910"} -{"city": "CENTREDALE", "loc": [-71.474058, 41.853412], "pop": 13858, "state": "RI", "_id": "02911"} -{"city": "EAST PROVIDENCE", "loc": [-71.368785, 41.813777], "pop": 22965, "state": "RI", "_id": "02914"} -{"city": "RIVERSIDE", "loc": [-71.354244, 41.772313], "pop": 18934, "state": "RI", "_id": "02915"} -{"city": "RUMFORD", "loc": [-71.355938, 41.842472], "pop": 8550, "state": "RI", "_id": "02916"} -{"city": "SMITHFIELD", "loc": [-71.520666, 41.896382], "pop": 12213, "state": "RI", "_id": "02917"} -{"city": "CRANSTON", "loc": [-71.497646, 41.826431], "pop": 26575, "state": "RI", "_id": "02919"} -{"city": "CRANSTON", "loc": [-71.465889, 41.77157], "pop": 37385, "state": "RI", "_id": "02920"} -{"city": "CRANSTON", "loc": [-71.506102, 41.761357], "pop": 6502, "state": "RI", "_id": "02921"} -{"city": "ALCOLU", "loc": [-80.178782, 33.768402], "pop": 2319, "state": "SC", "_id": "29001"} -{"city": "BAMBERG", "loc": [-81.017744, 33.277915], "pop": 7096, "state": "SC", "_id": "29003"} -{"city": "BATESBURG", "loc": [-81.548988, 33.938595], "pop": 9300, "state": "SC", "_id": "29006"} -{"city": "BETHUNE", "loc": [-80.366206, 34.42012], "pop": 2172, "state": "SC", "_id": "29009"} -{"city": "BISHOPVILLE", "loc": [-80.274988, 34.224101], "pop": 11965, "state": "SC", "_id": "29010"} -{"city": "BLACKSTOCK", "loc": [-81.124857, 34.577876], "pop": 249, "state": "SC", "_id": "29014"} -{"city": "BLAIR", "loc": [-81.345945, 34.496668], "pop": 895, "state": "SC", "_id": "29015"} -{"city": "BLYTHEWOOD", "loc": [-80.975756, 34.191112], "pop": 7321, "state": "SC", "_id": "29016"} -{"city": "BOWMAN", "loc": [-80.670868, 33.347466], "pop": 3993, "state": "SC", "_id": "29018"} -{"city": "CAMDEN", "loc": [-80.590997, 34.269636], "pop": 20667, "state": "SC", "_id": "29020"} -{"city": "CAMERON", "loc": [-80.646605, 33.557789], "pop": 2356, "state": "SC", "_id": "29030"} -{"city": "CARLISLE", "loc": [-81.509105, 34.614332], "pop": 2021, "state": "SC", "_id": "29031"} -{"city": "CASSATT", "loc": [-80.499993, 34.342414], "pop": 2164, "state": "SC", "_id": "29032"} -{"city": "CAYCE", "loc": [-81.067084, 33.962567], "pop": 12191, "state": "SC", "_id": "29033"} -{"city": "CHAPIN", "loc": [-81.33182, 34.131158], "pop": 8744, "state": "SC", "_id": "29036"} -{"city": "CHAPPELLS", "loc": [-81.835253, 34.235834], "pop": 940, "state": "SC", "_id": "29037"} -{"city": "COPE", "loc": [-80.963111, 33.372555], "pop": 1962, "state": "SC", "_id": "29038"} -{"city": "CORDOVA", "loc": [-80.885719, 33.42753], "pop": 2808, "state": "SC", "_id": "29039"} -{"city": "DALZELL", "loc": [-80.466533, 34.014412], "pop": 7540, "state": "SC", "_id": "29040"} -{"city": "DENMARK", "loc": [-81.14072, 33.320925], "pop": 6602, "state": "SC", "_id": "29042"} -{"city": "EASTOVER", "loc": [-80.699647, 33.915274], "pop": 4666, "state": "SC", "_id": "29044"} -{"city": "ELGIN", "loc": [-80.811292, 34.161963], "pop": 9447, "state": "SC", "_id": "29045"} -{"city": "ELLIOTT", "loc": [-80.175441, 34.117149], "pop": 1235, "state": "SC", "_id": "29046"} -{"city": "ELLOREE", "loc": [-80.56784, 33.490608], "pop": 4202, "state": "SC", "_id": "29047"} -{"city": "EUTAWVILLE", "loc": [-80.319983, 33.392189], "pop": 4298, "state": "SC", "_id": "29048"} -{"city": "GABLE", "loc": [-80.081377, 33.840989], "pop": 790, "state": "SC", "_id": "29051"} -{"city": "GADSDEN", "loc": [-80.753199, 33.845461], "pop": 2235, "state": "SC", "_id": "29052"} -{"city": "GASTON", "loc": [-81.117395, 33.833712], "pop": 7863, "state": "SC", "_id": "29053"} -{"city": "GILBERT", "loc": [-81.39136, 33.95805], "pop": 4018, "state": "SC", "_id": "29054"} -{"city": "GREAT FALLS", "loc": [-80.913263, 34.57053], "pop": 3562, "state": "SC", "_id": "29055"} -{"city": "GREELEYVILLE", "loc": [-79.98022, 33.59664], "pop": 2773, "state": "SC", "_id": "29056"} -{"city": "HEATH SPRINGS", "loc": [-80.71031, 34.602422], "pop": 5101, "state": "SC", "_id": "29058"} -{"city": "HOLLY HILL", "loc": [-80.402393, 33.327586], "pop": 4959, "state": "SC", "_id": "29059"} -{"city": "HOPKINS", "loc": [-80.844906, 33.934868], "pop": 12297, "state": "SC", "_id": "29061"} -{"city": "IRMO", "loc": [-81.196553, 34.110254], "pop": 15479, "state": "SC", "_id": "29063"} -{"city": "JENKINSVILLE", "loc": [-81.271153, 34.271693], "pop": 809, "state": "SC", "_id": "29065"} -{"city": "KERSHAW", "loc": [-80.554633, 34.557809], "pop": 8189, "state": "SC", "_id": "29067"} -{"city": "LAMAR", "loc": [-80.030134, 34.189044], "pop": 6964, "state": "SC", "_id": "29069"} -{"city": "LEESVILLE", "loc": [-81.459753, 33.913169], "pop": 8605, "state": "SC", "_id": "29070"} -{"city": "LEXINGTON", "loc": [-81.23586, 33.972383], "pop": 33576, "state": "SC", "_id": "29072"} -{"city": "LEXINGTON", "loc": [-81.235102, 33.863206], "pop": 4683, "state": "SC", "_id": "29073"} -{"city": "LITTLE MOUNTAIN", "loc": [-81.418375, 34.167569], "pop": 3322, "state": "SC", "_id": "29075"} -{"city": "LONE STAR", "loc": [-80.645025, 33.673574], "pop": 1030, "state": "SC", "_id": "29077"} -{"city": "LUGOFF", "loc": [-80.714712, 34.22961], "pop": 8991, "state": "SC", "_id": "29078"} -{"city": "LYNCHBURG", "loc": [-80.098821, 34.052603], "pop": 2091, "state": "SC", "_id": "29080"} -{"city": "EHRHARDT", "loc": [-81.022137, 33.104436], "pop": 1525, "state": "SC", "_id": "29081"} -{"city": "LODGE", "loc": [-80.934641, 32.982263], "pop": 1877, "state": "SC", "_id": "29082"} -{"city": "MC BEE", "loc": [-80.254434, 34.46056], "pop": 2331, "state": "SC", "_id": "29101"} -{"city": "PAXVILLE", "loc": [-80.222078, 33.667716], "pop": 14407, "state": "SC", "_id": "29102"} -{"city": "SAINT CHARLES", "loc": [-80.234927, 34.046463], "pop": 2403, "state": "SC", "_id": "29104"} -{"city": "MONETTA", "loc": [-81.536299, 33.774729], "pop": 2068, "state": "SC", "_id": "29105"} -{"city": "NEESES", "loc": [-81.083433, 33.534282], "pop": 3416, "state": "SC", "_id": "29107"} -{"city": "NEWBERRY", "loc": [-81.615741, 34.284661], "pop": 15468, "state": "SC", "_id": "29108"} -{"city": "NEW ZION", "loc": [-80.014753, 33.795736], "pop": 1072, "state": "SC", "_id": "29111"} -{"city": "NORTH", "loc": [-81.060096, 33.621121], "pop": 3410, "state": "SC", "_id": "29112"} -{"city": "NORWAY", "loc": [-81.109744, 33.453402], "pop": 2487, "state": "SC", "_id": "29113"} -{"city": "OLANTA", "loc": [-79.915349, 33.928545], "pop": 2415, "state": "SC", "_id": "29114"} -{"city": "ORANGEBURG", "loc": [-80.859282, 33.502545], "pop": 43095, "state": "SC", "_id": "29115"} -{"city": "PELION", "loc": [-81.250161, 33.776628], "pop": 3139, "state": "SC", "_id": "29123"} -{"city": "PINEWOOD", "loc": [-80.497761, 33.764216], "pop": 937, "state": "SC", "_id": "29125"} -{"city": "POMARIA", "loc": [-81.449973, 34.306281], "pop": 3278, "state": "SC", "_id": "29126"} -{"city": "PROSPERITY", "loc": [-81.532353, 34.183243], "pop": 4782, "state": "SC", "_id": "29127"} -{"city": "REMBERT", "loc": [-80.494544, 34.108467], "pop": 5577, "state": "SC", "_id": "29128"} -{"city": "RIDGE SPRING", "loc": [-81.655949, 33.871403], "pop": 2172, "state": "SC", "_id": "29129"} -{"city": "RIDGEWAY", "loc": [-80.928813, 34.316651], "pop": 4405, "state": "SC", "_id": "29130"} -{"city": "RIMINI", "loc": [-80.472671, 33.672155], "pop": 778, "state": "SC", "_id": "29131"} -{"city": "ROWESVILLE", "loc": [-80.833115, 33.370257], "pop": 488, "state": "SC", "_id": "29133"} -{"city": "FORT MOTTE", "loc": [-80.856286, 33.694212], "pop": 9367, "state": "SC", "_id": "29135"} -{"city": "SALLEY", "loc": [-81.318418, 33.597233], "pop": 2537, "state": "SC", "_id": "29137"} -{"city": "SALUDA", "loc": [-81.775398, 34.017451], "pop": 9586, "state": "SC", "_id": "29138"} -{"city": "SANTEE", "loc": [-80.480498, 33.483533], "pop": 1891, "state": "SC", "_id": "29142"} -{"city": "SILVERSTREET", "loc": [-81.703684, 34.235553], "pop": 1851, "state": "SC", "_id": "29145"} -{"city": "SPRINGFIELD", "loc": [-81.249858, 33.534265], "pop": 2184, "state": "SC", "_id": "29146"} -{"city": "SUMMERTON", "loc": [-80.360566, 33.594578], "pop": 5946, "state": "SC", "_id": "29148"} -{"city": "OSWEGO", "loc": [-80.321008, 33.928199], "pop": 46394, "state": "SC", "_id": "29150"} -{"city": "SHAW A F B", "loc": [-80.481093, 33.980262], "pop": 13353, "state": "SC", "_id": "29152"} -{"city": "SUMTER", "loc": [-80.402761, 33.882067], "pop": 24816, "state": "SC", "_id": "29154"} -{"city": "SWANSEA", "loc": [-81.093309, 33.733917], "pop": 3822, "state": "SC", "_id": "29160"} -{"city": "TIMMONSVILLE", "loc": [-79.937845, 34.101241], "pop": 6644, "state": "SC", "_id": "29161"} -{"city": "TURBEVILLE", "loc": [-79.985174, 33.87857], "pop": 3138, "state": "SC", "_id": "29162"} -{"city": "VANCE", "loc": [-80.461716, 33.413882], "pop": 3445, "state": "SC", "_id": "29163"} -{"city": "WAGENER", "loc": [-81.39952, 33.649417], "pop": 3278, "state": "SC", "_id": "29164"} -{"city": "WARD", "loc": [-81.728341, 33.884856], "pop": 823, "state": "SC", "_id": "29166"} -{"city": "WEDGEFIELD", "loc": [-80.498719, 33.943002], "pop": 5490, "state": "SC", "_id": "29168"} -{"city": "WEST COLUMBIA", "loc": [-81.088836, 33.995024], "pop": 18309, "state": "SC", "_id": "29169"} -{"city": "WEST COLUMBIA", "loc": [-81.140474, 33.956751], "pop": 17399, "state": "SC", "_id": "29170"} -{"city": "WEST COLUMBIA", "loc": [-81.091008, 33.900022], "pop": 9939, "state": "SC", "_id": "29172"} -{"city": "WESTVILLE", "loc": [-80.579554, 34.449177], "pop": 867, "state": "SC", "_id": "29175"} -{"city": "WHITMIRE", "loc": [-81.605981, 34.495485], "pop": 3677, "state": "SC", "_id": "29178"} -{"city": "WINNSBORO", "loc": [-81.10899, 34.381008], "pop": 16188, "state": "SC", "_id": "29180"} -{"city": "COLUMBIA", "loc": [-81.033418, 34.0004], "pop": 18758, "state": "SC", "_id": "29201"} -{"city": "COLUMBIA", "loc": [-81.026462, 34.063452], "pop": 45907, "state": "SC", "_id": "29203"} -{"city": "COLUMBIA", "loc": [-81.004647, 34.026037], "pop": 23682, "state": "SC", "_id": "29204"} -{"city": "COLUMBIA", "loc": [-80.999731, 33.990309], "pop": 27239, "state": "SC", "_id": "29205"} -{"city": "COLUMBIA", "loc": [-80.953152, 34.024655], "pop": 25605, "state": "SC", "_id": "29206"} -{"city": "COLUMBIA", "loc": [-80.935525, 33.965863], "pop": 26378, "state": "SC", "_id": "29209"} -{"city": "COLUMBIA", "loc": [-81.11006, 34.047863], "pop": 37526, "state": "SC", "_id": "29210"} -{"city": "COLUMBIA", "loc": [-81.179617, 34.072613], "pop": 22080, "state": "SC", "_id": "29212"} -{"city": "COLUMBIA", "loc": [-80.91667, 34.085267], "pop": 42346, "state": "SC", "_id": "29223"} -{"city": "SPARTANBURG", "loc": [-81.965377, 34.935211], "pop": 45811, "state": "SC", "_id": "29301"} -{"city": "SPARTANBURG", "loc": [-81.873625, 34.956283], "pop": 36446, "state": "SC", "_id": "29302"} -{"city": "VALLEY FALLS", "loc": [-81.957566, 34.993728], "pop": 29166, "state": "SC", "_id": "29303"} -{"city": "BUFFALO", "loc": [-81.682576, 34.724703], "pop": 1810, "state": "SC", "_id": "29321"} -{"city": "CAMPOBELLO", "loc": [-82.140343, 35.108003], "pop": 7479, "state": "SC", "_id": "29322"} -{"city": "CHESNEE", "loc": [-81.867827, 35.115398], "pop": 14714, "state": "SC", "_id": "29323"} -{"city": "CLINTON", "loc": [-81.87717, 34.470692], "pop": 16265, "state": "SC", "_id": "29325"} -{"city": "COWPENS", "loc": [-81.822019, 35.038979], "pop": 5279, "state": "SC", "_id": "29330"} -{"city": "CROSS HILL", "loc": [-81.984278, 34.269274], "pop": 1089, "state": "SC", "_id": "29332"} -{"city": "DUNCAN", "loc": [-82.125821, 34.917645], "pop": 5512, "state": "SC", "_id": "29334"} -{"city": "ENOREE", "loc": [-81.92373, 34.670952], "pop": 3310, "state": "SC", "_id": "29335"} -{"city": "GAFFNEY", "loc": [-81.649061, 35.061508], "pop": 32761, "state": "SC", "_id": "29340"} -{"city": "INMAN", "loc": [-82.054013, 35.052795], "pop": 23348, "state": "SC", "_id": "29349"} -{"city": "JOANNA", "loc": [-81.81907, 34.406795], "pop": 2592, "state": "SC", "_id": "29351"} -{"city": "KELTON", "loc": [-81.684218, 34.837368], "pop": 3504, "state": "SC", "_id": "29353"} -{"city": "KINARDS", "loc": [-81.71975, 34.355219], "pop": 678, "state": "SC", "_id": "29355"} -{"city": "LANDRUM", "loc": [-82.211473, 35.156486], "pop": 6521, "state": "SC", "_id": "29356"} -{"city": "ORA", "loc": [-82.026774, 34.500714], "pop": 21437, "state": "SC", "_id": "29360"} -{"city": "LYMAN", "loc": [-82.143479, 34.968443], "pop": 4515, "state": "SC", "_id": "29365"} -{"city": "MOORE", "loc": [-82.017893, 34.883453], "pop": 6791, "state": "SC", "_id": "29369"} -{"city": "MOUNTVILLE", "loc": [-81.958429, 34.340791], "pop": 1139, "state": "SC", "_id": "29370"} -{"city": "PACOLET", "loc": [-81.758703, 34.901708], "pop": 4129, "state": "SC", "_id": "29372"} -{"city": "GLENN SPRINGS", "loc": [-81.857853, 34.774808], "pop": 4588, "state": "SC", "_id": "29374"} -{"city": "ROEBUCK", "loc": [-81.95255, 34.8688], "pop": 5009, "state": "SC", "_id": "29376"} -{"city": "UNION", "loc": [-81.620235, 34.726855], "pop": 22553, "state": "SC", "_id": "29379"} -{"city": "WATERLOO", "loc": [-82.087968, 34.33673], "pop": 2992, "state": "SC", "_id": "29384"} -{"city": "WELLFORD", "loc": [-82.092706, 34.951394], "pop": 6637, "state": "SC", "_id": "29385"} -{"city": "WOODRUFF", "loc": [-82.044658, 34.757864], "pop": 10206, "state": "SC", "_id": "29388"} -{"city": "CHARLESTON", "loc": [-79.937069, 32.779506], "pop": 12475, "state": "SC", "_id": "29401"} -{"city": "CHARLESTON", "loc": [-79.949283, 32.797575], "pop": 24620, "state": "SC", "_id": "29403"} -{"city": "CHARLESTON", "loc": [-80.067687, 32.895816], "pop": 5420, "state": "SC", "_id": "29404"} -{"city": "CHARLESTON", "loc": [-79.976442, 32.851206], "pop": 30621, "state": "SC", "_id": "29405"} -{"city": "NORTH CHARLESTON", "loc": [-80.001053, 32.903035], "pop": 27726, "state": "SC", "_id": "29406"} -{"city": "CHARLESTON", "loc": [-80.005953, 32.799322], "pop": 38597, "state": "SC", "_id": "29407"} -{"city": "CHARLESTON", "loc": [-79.954727, 32.732319], "pop": 29969, "state": "SC", "_id": "29412"} -{"city": "CHARLESTON", "loc": [-80.056756, 32.821538], "pop": 19404, "state": "SC", "_id": "29414"} -{"city": "CHARLESTON", "loc": [-80.055126, 32.907135], "pop": 39784, "state": "SC", "_id": "29418"} -{"city": "CHARLESTON", "loc": [-80.086463, 32.933096], "pop": 11598, "state": "SC", "_id": "29420"} -{"city": "JERICHO", "loc": [-80.368197, 32.749318], "pop": 1723, "state": "SC", "_id": "29426"} -{"city": "AWENDAW", "loc": [-79.686075, 32.951475], "pop": 1831, "state": "SC", "_id": "29429"} -{"city": "BONNEAU", "loc": [-79.92164, 33.297332], "pop": 7431, "state": "SC", "_id": "29431"} -{"city": "BRANCHVILLE", "loc": [-80.805931, 33.262802], "pop": 2165, "state": "SC", "_id": "29432"} -{"city": "CORDESVILLE", "loc": [-79.920492, 33.162127], "pop": 1382, "state": "SC", "_id": "29434"} -{"city": "COTTAGEVILLE", "loc": [-80.479414, 32.961171], "pop": 4105, "state": "SC", "_id": "29435"} -{"city": "CROSS", "loc": [-80.185901, 33.336373], "pop": 3016, "state": "SC", "_id": "29436"} -{"city": "DORCHESTER", "loc": [-80.403398, 33.124688], "pop": 928, "state": "SC", "_id": "29437"} -{"city": "EDISTO ISLAND", "loc": [-80.30701, 32.548572], "pop": 1669, "state": "SC", "_id": "29438"} -{"city": "GEORGETOWN", "loc": [-79.323459, 33.430776], "pop": 29148, "state": "SC", "_id": "29440"} -{"city": "MOUNT HOLLY", "loc": [-80.019948, 32.988699], "pop": 44954, "state": "SC", "_id": "29445"} -{"city": "GREEN POND", "loc": [-80.528035, 32.662827], "pop": 2068, "state": "SC", "_id": "29446"} -{"city": "HARLEYVILLE", "loc": [-80.450094, 33.220511], "pop": 3289, "state": "SC", "_id": "29448"} -{"city": "MEGGETT", "loc": [-80.259566, 32.722306], "pop": 6397, "state": "SC", "_id": "29449"} -{"city": "HUGER", "loc": [-79.807254, 33.018436], "pop": 1964, "state": "SC", "_id": "29450"} -{"city": "ISLE OF PALMS", "loc": [-79.772949, 32.794252], "pop": 3680, "state": "SC", "_id": "29451"} -{"city": "SHULERVILLE", "loc": [-79.715551, 33.228797], "pop": 1852, "state": "SC", "_id": "29453"} -{"city": "JOHNS ISLAND", "loc": [-80.094294, 32.709697], "pop": 11756, "state": "SC", "_id": "29455"} -{"city": "LADSON", "loc": [-80.125701, 32.993019], "pop": 16216, "state": "SC", "_id": "29456"} -{"city": "MC CLELLANVILLE", "loc": [-79.479982, 33.10472], "pop": 3020, "state": "SC", "_id": "29458"} -{"city": "OAKLEY", "loc": [-80.036576, 33.163071], "pop": 17822, "state": "SC", "_id": "29461"} -{"city": "MOUNT PLEASANT", "loc": [-79.852031, 32.816211], "pop": 35432, "state": "SC", "_id": "29464"} -{"city": "PINEVILLE", "loc": [-80.093181, 33.419926], "pop": 1411, "state": "SC", "_id": "29468"} -{"city": "PINOPOLIS", "loc": [-80.039761, 33.224125], "pop": 958, "state": "SC", "_id": "29469"} -{"city": "RAVENEL", "loc": [-80.222346, 32.788088], "pop": 3248, "state": "SC", "_id": "29470"} -{"city": "REEVESVILLE", "loc": [-80.66725, 33.187213], "pop": 1342, "state": "SC", "_id": "29471"} -{"city": "RIDGEVILLE", "loc": [-80.308611, 33.108017], "pop": 7494, "state": "SC", "_id": "29472"} -{"city": "ROUND O", "loc": [-80.573882, 32.940516], "pop": 64, "state": "SC", "_id": "29474"} -{"city": "RUFFIN", "loc": [-80.813653, 33.010319], "pop": 269, "state": "SC", "_id": "29475"} -{"city": "SAINT GEORGE", "loc": [-80.573246, 33.184532], "pop": 6940, "state": "SC", "_id": "29477"} -{"city": "ALVIN", "loc": [-79.937635, 33.405953], "pop": 5483, "state": "SC", "_id": "29479"} -{"city": "SMOAKS", "loc": [-80.813021, 33.096319], "pop": 2074, "state": "SC", "_id": "29481"} -{"city": "SULLIVANS ISLAND", "loc": [-79.839905, 32.763652], "pop": 1623, "state": "SC", "_id": "29482"} -{"city": "SUMMERVILLE", "loc": [-80.173852, 33.028045], "pop": 45292, "state": "SC", "_id": "29483"} -{"city": "SUMMERVILLE", "loc": [-80.183082, 32.975556], "pop": 24856, "state": "SC", "_id": "29485"} -{"city": "WADMALAW ISLAND", "loc": [-80.182867, 32.652854], "pop": 2570, "state": "SC", "_id": "29487"} -{"city": "RITTER", "loc": [-80.679225, 32.897367], "pop": 24002, "state": "SC", "_id": "29488"} -{"city": "WANDO", "loc": [-79.86533, 32.962223], "pop": 1433, "state": "SC", "_id": "29492"} -{"city": "FLORENCE", "loc": [-79.772786, 34.18375], "pop": 66990, "state": "SC", "_id": "29501"} -{"city": "FLORENCE", "loc": [-79.775983, 34.256368], "pop": 1440, "state": "SC", "_id": "29505"} -{"city": "QUINBY", "loc": [-79.794547, 34.245178], "pop": 0, "state": "SC", "_id": "29506"} -{"city": "ANDREWS", "loc": [-79.560438, 33.452525], "pop": 9845, "state": "SC", "_id": "29510"} -{"city": "AYNOR", "loc": [-79.17773, 33.982271], "pop": 6786, "state": "SC", "_id": "29511"} -{"city": "BENNETTSVILLE", "loc": [-79.689826, 34.625501], "pop": 15751, "state": "SC", "_id": "29512"} -{"city": "BLENHEIM", "loc": [-79.659422, 34.502033], "pop": 2123, "state": "SC", "_id": "29516"} -{"city": "CADES", "loc": [-79.817567, 33.812017], "pop": 2748, "state": "SC", "_id": "29518"} -{"city": "CHERAW", "loc": [-79.917414, 34.686275], "pop": 13063, "state": "SC", "_id": "29520"} -{"city": "CLIO", "loc": [-79.545255, 34.58052], "pop": 2870, "state": "SC", "_id": "29525"} -{"city": "CONWAY", "loc": [-79.055712, 33.873066], "pop": 17560, "state": "SC", "_id": "29526"} -{"city": "BUCKSPORT", "loc": [-79.050836, 33.77922], "pop": 19781, "state": "SC", "_id": "29527"} -{"city": "COWARD", "loc": [-79.751523, 33.990514], "pop": 2098, "state": "SC", "_id": "29530"} -{"city": "DARLINGTON", "loc": [-79.873221, 34.300294], "pop": 22271, "state": "SC", "_id": "29532"} -{"city": "DILLON", "loc": [-79.377015, 34.414553], "pop": 9113, "state": "SC", "_id": "29536"} -{"city": "EFFINGHAM", "loc": [-79.791822, 34.094571], "pop": 8031, "state": "SC", "_id": "29541"} -{"city": "FORK", "loc": [-79.250063, 34.28733], "pop": 916, "state": "SC", "_id": "29543"} -{"city": "GALIVANTS FERRY", "loc": [-79.105915, 34.123801], "pop": 1129, "state": "SC", "_id": "29544"} -{"city": "GREEN SEA", "loc": [-79.051613, 34.205105], "pop": 1814, "state": "SC", "_id": "29545"} -{"city": "GRESHAM", "loc": [-79.356426, 33.906941], "pop": 2262, "state": "SC", "_id": "29546"} -{"city": "SOUTH OF THE BOR", "loc": [-79.377871, 34.484556], "pop": 4643, "state": "SC", "_id": "29547"} -{"city": "HARTSVILLE", "loc": [-80.084237, 34.375616], "pop": 27888, "state": "SC", "_id": "29550"} -{"city": "HEMINGWAY", "loc": [-79.448895, 33.741887], "pop": 5578, "state": "SC", "_id": "29554"} -{"city": "JOHNSONVILLE", "loc": [-79.478256, 33.829873], "pop": 6115, "state": "SC", "_id": "29555"} -{"city": "KINGSTREE", "loc": [-79.783221, 33.687841], "pop": 16365, "state": "SC", "_id": "29556"} -{"city": "LAKE CITY", "loc": [-79.741794, 33.865473], "pop": 11664, "state": "SC", "_id": "29560"} -{"city": "LAKE VIEW", "loc": [-79.192869, 34.34449], "pop": 3513, "state": "SC", "_id": "29563"} -{"city": "LANE", "loc": [-79.871545, 33.50827], "pop": 1730, "state": "SC", "_id": "29564"} -{"city": "LATTA", "loc": [-79.441704, 34.339829], "pop": 6435, "state": "SC", "_id": "29565"} -{"city": "LITTLE RIVER", "loc": [-78.650829, 33.876806], "pop": 6473, "state": "SC", "_id": "29566"} -{"city": "LITTLE ROCK", "loc": [-79.326948, 34.404803], "pop": 4489, "state": "SC", "_id": "29567"} -{"city": "LONGS", "loc": [-78.793359, 33.906408], "pop": 5936, "state": "SC", "_id": "29568"} -{"city": "LORIS", "loc": [-78.916096, 34.055756], "pop": 11189, "state": "SC", "_id": "29569"} -{"city": "MC COLL", "loc": [-79.559741, 34.670406], "pop": 5224, "state": "SC", "_id": "29570"} -{"city": "MARION", "loc": [-79.389822, 34.156195], "pop": 16285, "state": "SC", "_id": "29571"} -{"city": "MYRTLE BEACH", "loc": [-78.804448, 33.758701], "pop": 7039, "state": "SC", "_id": "29572"} -{"city": "MULLINS", "loc": [-79.254155, 34.204441], "pop": 12118, "state": "SC", "_id": "29574"} -{"city": "SURFSIDE BEACH", "loc": [-78.995228, 33.625245], "pop": 24545, "state": "SC", "_id": "29575"} -{"city": "MURRELLS INLET", "loc": [-79.05275, 33.550717], "pop": 4209, "state": "SC", "_id": "29576"} -{"city": "MYRTLE BEACH", "loc": [-78.913697, 33.699363], "pop": 31917, "state": "SC", "_id": "29577"} -{"city": "NESMITH", "loc": [-79.551301, 33.591652], "pop": 3297, "state": "SC", "_id": "29580"} -{"city": "NICHOLS", "loc": [-79.162493, 34.242629], "pop": 1258, "state": "SC", "_id": "29581"} -{"city": "CHERRY GROVE BEA", "loc": [-78.67792, 33.822801], "pop": 9910, "state": "SC", "_id": "29582"} -{"city": "PAMPLICO", "loc": [-79.592917, 33.992166], "pop": 6291, "state": "SC", "_id": "29583"} -{"city": "PATRICK", "loc": [-80.064984, 34.559823], "pop": 3800, "state": "SC", "_id": "29584"} -{"city": "PAWLEYS ISLAND", "loc": [-79.134128, 33.450825], "pop": 5445, "state": "SC", "_id": "29585"} -{"city": "SALTERS", "loc": [-79.830024, 33.561138], "pop": 1932, "state": "SC", "_id": "29590"} -{"city": "SCRANTON", "loc": [-79.773101, 33.92813], "pop": 4122, "state": "SC", "_id": "29591"} -{"city": "SELLERS", "loc": [-79.436948, 34.269522], "pop": 1976, "state": "SC", "_id": "29592"} -{"city": "SOCIETY HILL", "loc": [-79.886494, 34.451161], "pop": 3278, "state": "SC", "_id": "29593"} -{"city": "WALLACE", "loc": [-79.801142, 34.730704], "pop": 3398, "state": "SC", "_id": "29596"} -{"city": "GREENVILLE", "loc": [-82.406049, 34.847165], "pop": 11198, "state": "SC", "_id": "29601"} -{"city": "GREENVILLE", "loc": [-82.393218, 34.800117], "pop": 32092, "state": "SC", "_id": "29605"} -{"city": "GREENVILLE", "loc": [-82.35155, 34.828507], "pop": 26926, "state": "SC", "_id": "29607"} -{"city": "GREENVILLE", "loc": [-82.400195, 34.892101], "pop": 35346, "state": "SC", "_id": "29609"} -{"city": "GREENVILLE", "loc": [-82.449296, 34.85331], "pop": 43522, "state": "SC", "_id": "29611"} -{"city": "GREENVILLE", "loc": [-82.319815, 34.866095], "pop": 25818, "state": "SC", "_id": "29615"} -{"city": "ABBEVILLE", "loc": [-82.378452, 34.18186], "pop": 11344, "state": "SC", "_id": "29620"} -{"city": "ANDERSON", "loc": [-82.630436, 34.526051], "pop": 24256, "state": "SC", "_id": "29621"} -{"city": "ANDERSON", "loc": [-82.677052, 34.474807], "pop": 26962, "state": "SC", "_id": "29624"} -{"city": "ANDERSON", "loc": [-82.70868, 34.527134], "pop": 20105, "state": "SC", "_id": "29625"} -{"city": "BELTON", "loc": [-82.510106, 34.508715], "pop": 11371, "state": "SC", "_id": "29627"} -{"city": "CALHOUN FALLS", "loc": [-82.580544, 34.099912], "pop": 3289, "state": "SC", "_id": "29628"} -{"city": "CENTRAL", "loc": [-82.794727, 34.74007], "pop": 8226, "state": "SC", "_id": "29630"} -{"city": "CLEMSON", "loc": [-82.825003, 34.683061], "pop": 17987, "state": "SC", "_id": "29631"} -{"city": "CLEVELAND", "loc": [-82.609256, 35.06537], "pop": 960, "state": "SC", "_id": "29635"} -{"city": "SHOALS JUNCTION", "loc": [-82.366616, 34.399772], "pop": 1347, "state": "SC", "_id": "29638"} -{"city": "DUE WEST", "loc": [-82.400164, 34.334425], "pop": 3499, "state": "SC", "_id": "29639"} -{"city": "EASLEY", "loc": [-82.579552, 34.829031], "pop": 37862, "state": "SC", "_id": "29640"} -{"city": "EASLEY", "loc": [-82.561716, 34.960694], "pop": 830, "state": "SC", "_id": "29642"} -{"city": "FAIR PLAY", "loc": [-82.999874, 34.577548], "pop": 6110, "state": "SC", "_id": "29643"} -{"city": "FOUNTAIN INN", "loc": [-82.264593, 34.653334], "pop": 15233, "state": "SC", "_id": "29644"} -{"city": "ORA", "loc": [-82.157035, 34.580984], "pop": 9987, "state": "SC", "_id": "29645"} -{"city": "GREENWOOD", "loc": [-82.156211, 34.175802], "pop": 23138, "state": "SC", "_id": "29646"} -{"city": "GREENWOOD", "loc": [-82.158189, 34.222988], "pop": 18588, "state": "SC", "_id": "29649"} -{"city": "GREER", "loc": [-82.267442, 34.8968], "pop": 16460, "state": "SC", "_id": "29650"} -{"city": "GREER", "loc": [-82.220857, 34.94532], "pop": 26573, "state": "SC", "_id": "29651"} -{"city": "HODGES", "loc": [-82.214225, 34.288167], "pop": 5262, "state": "SC", "_id": "29653"} -{"city": "HONEA PATH", "loc": [-82.425471, 34.441723], "pop": 7621, "state": "SC", "_id": "29654"} -{"city": "IVA", "loc": [-82.657437, 34.319847], "pop": 5342, "state": "SC", "_id": "29655"} -{"city": "LIBERTY", "loc": [-82.697429, 34.787189], "pop": 9519, "state": "SC", "_id": "29657"} -{"city": "LONG CREEK", "loc": [-83.184728, 34.754954], "pop": 1972, "state": "SC", "_id": "29658"} -{"city": "LOWNDESVILLE", "loc": [-82.571021, 34.26624], "pop": 2565, "state": "SC", "_id": "29659"} -{"city": "MARIETTA", "loc": [-82.513622, 35.029644], "pop": 6321, "state": "SC", "_id": "29661"} -{"city": "MAULDIN", "loc": [-82.303507, 34.780675], "pop": 8871, "state": "SC", "_id": "29662"} -{"city": "MOUNTAIN REST", "loc": [-83.113402, 34.854119], "pop": 1717, "state": "SC", "_id": "29664"} -{"city": "NINETY SIX", "loc": [-82.029185, 34.165198], "pop": 8668, "state": "SC", "_id": "29666"} -{"city": "CATEECHEE", "loc": [-82.755492, 34.765472], "pop": 738, "state": "SC", "_id": "29667"} -{"city": "PELZER", "loc": [-82.467334, 34.646063], "pop": 3224, "state": "SC", "_id": "29669"} -{"city": "PENDLETON", "loc": [-82.740623, 34.63694], "pop": 12288, "state": "SC", "_id": "29670"} -{"city": "PICKENS", "loc": [-82.705784, 34.902403], "pop": 16227, "state": "SC", "_id": "29671"} -{"city": "PIEDMONT", "loc": [-82.470216, 34.724433], "pop": 24083, "state": "SC", "_id": "29673"} -{"city": "SALEM", "loc": [-82.960687, 34.872836], "pop": 4091, "state": "SC", "_id": "29676"} -{"city": "SENECA", "loc": [-82.936162, 34.682797], "pop": 20361, "state": "SC", "_id": "29678"} -{"city": "SIMPSONVILLE", "loc": [-82.255009, 34.751228], "pop": 28438, "state": "SC", "_id": "29681"} -{"city": "SIX MILE", "loc": [-82.827795, 34.828287], "pop": 2757, "state": "SC", "_id": "29682"} -{"city": "STARR", "loc": [-82.68971, 34.396196], "pop": 2995, "state": "SC", "_id": "29684"} -{"city": "SUNSET", "loc": [-82.807639, 34.970617], "pop": 850, "state": "SC", "_id": "29685"} -{"city": "TAMASSEE", "loc": [-82.986138, 34.898229], "pop": 156, "state": "SC", "_id": "29686"} -{"city": "TAYLORS", "loc": [-82.31968, 34.924517], "pop": 32831, "state": "SC", "_id": "29687"} -{"city": "TIGERVILLE", "loc": [-82.358236, 35.094443], "pop": 231, "state": "SC", "_id": "29688"} -{"city": "TOWNVILLE", "loc": [-82.89633, 34.522638], "pop": 1784, "state": "SC", "_id": "29689"} -{"city": "TRAVELERS REST", "loc": [-82.427179, 35.003929], "pop": 14029, "state": "SC", "_id": "29690"} -{"city": "WALHALLA", "loc": [-83.038778, 34.768902], "pop": 14047, "state": "SC", "_id": "29691"} -{"city": "WARE SHOALS", "loc": [-82.26779, 34.402491], "pop": 4442, "state": "SC", "_id": "29692"} -{"city": "MADISON", "loc": [-83.110782, 34.658857], "pop": 8933, "state": "SC", "_id": "29693"} -{"city": "WEST UNION", "loc": [-83.039887, 34.751572], "pop": 170, "state": "SC", "_id": "29696"} -{"city": "WILLIAMSTON", "loc": [-82.511006, 34.620572], "pop": 13511, "state": "SC", "_id": "29697"} -{"city": "CHEROKEE FALLS", "loc": [-81.508004, 35.115047], "pop": 7481, "state": "SC", "_id": "29702"} -{"city": "CATAWBA", "loc": [-80.934154, 34.850712], "pop": 2451, "state": "SC", "_id": "29704"} -{"city": "CHESTER", "loc": [-81.21856, 34.714897], "pop": 21920, "state": "SC", "_id": "29706"} -{"city": "CHESTERFIELD", "loc": [-80.09577, 34.727834], "pop": 6286, "state": "SC", "_id": "29709"} -{"city": "LAKE WYLIE", "loc": [-81.186358, 35.110728], "pop": 17905, "state": "SC", "_id": "29710"} -{"city": "EDGEMOOR", "loc": [-80.989095, 34.797006], "pop": 1725, "state": "SC", "_id": "29712"} -{"city": "FORT LAWN", "loc": [-80.909175, 34.70064], "pop": 2106, "state": "SC", "_id": "29714"} -{"city": "TEGA CAY", "loc": [-80.958559, 35.026001], "pop": 17343, "state": "SC", "_id": "29715"} -{"city": "HICKORY GROVE", "loc": [-81.430599, 34.970833], "pop": 932, "state": "SC", "_id": "29717"} -{"city": "JEFFERSON", "loc": [-80.363934, 34.654581], "pop": 3257, "state": "SC", "_id": "29718"} -{"city": "LANCASTER", "loc": [-80.761576, 34.749006], "pop": 43745, "state": "SC", "_id": "29720"} -{"city": "MC CONNELLS", "loc": [-81.236803, 34.867667], "pop": 1431, "state": "SC", "_id": "29726"} -{"city": "MOUNT CROGHAN", "loc": [-80.242791, 34.743895], "pop": 1584, "state": "SC", "_id": "29727"} -{"city": "PAGELAND", "loc": [-80.396615, 34.772193], "pop": 7778, "state": "SC", "_id": "29728"} -{"city": "RICHBURG", "loc": [-80.989061, 34.682185], "pop": 2393, "state": "SC", "_id": "29729"} -{"city": "ROCK HILL", "loc": [-81.012904, 34.915068], "pop": 41461, "state": "SC", "_id": "29730"} -{"city": "ROCK HILL", "loc": [-81.048881, 34.968066], "pop": 30532, "state": "SC", "_id": "29732"} -{"city": "RUBY", "loc": [-80.190966, 34.737928], "pop": 478, "state": "SC", "_id": "29741"} -{"city": "SHARON", "loc": [-81.372907, 34.908396], "pop": 1175, "state": "SC", "_id": "29742"} -{"city": "SMYRNA", "loc": [-81.389606, 35.030784], "pop": 877, "state": "SC", "_id": "29743"} -{"city": "YORK", "loc": [-81.224462, 34.994669], "pop": 17279, "state": "SC", "_id": "29745"} -{"city": "AIKEN", "loc": [-81.719429, 33.553024], "pop": 51233, "state": "SC", "_id": "29801"} -{"city": "AIKEN", "loc": [-81.594702, 33.531868], "pop": 743, "state": "SC", "_id": "29803"} -{"city": "NEW ELLENTON", "loc": [-81.687376, 33.417242], "pop": 2586, "state": "SC", "_id": "29809"} -{"city": "ALLENDALE", "loc": [-81.320254, 33.007676], "pop": 5704, "state": "SC", "_id": "29810"} -{"city": "BARNWELL", "loc": [-81.35231, 33.233492], "pop": 11016, "state": "SC", "_id": "29812"} -{"city": "BLACKVILLE", "loc": [-81.271844, 33.364476], "pop": 4254, "state": "SC", "_id": "29817"} -{"city": "BRADLEY", "loc": [-82.202723, 34.032587], "pop": 1287, "state": "SC", "_id": "29819"} -{"city": "CLARKS HILL", "loc": [-82.14875, 33.65052], "pop": 439, "state": "SC", "_id": "29821"} -{"city": "EDGEFIELD", "loc": [-81.96602, 33.805553], "pop": 6884, "state": "SC", "_id": "29824"} -{"city": "FAIRFAX", "loc": [-81.25864, 32.951455], "pop": 4017, "state": "SC", "_id": "29827"} -{"city": "GRANITEVILLE", "loc": [-81.814686, 33.563016], "pop": 2858, "state": "SC", "_id": "29829"} -{"city": "JACKSON", "loc": [-81.797628, 33.338886], "pop": 2810, "state": "SC", "_id": "29831"} -{"city": "JOHNSTON", "loc": [-81.797242, 33.823075], "pop": 4616, "state": "SC", "_id": "29832"} -{"city": "MC CORMICK", "loc": [-82.271862, 33.914646], "pop": 5426, "state": "SC", "_id": "29835"} -{"city": "MARTIN", "loc": [-81.500304, 33.109286], "pop": 566, "state": "SC", "_id": "29836"} -{"city": "MODOC", "loc": [-82.203469, 33.724894], "pop": 380, "state": "SC", "_id": "29838"} -{"city": "MOUNT CARMEL", "loc": [-82.43607, 33.970323], "pop": 1354, "state": "SC", "_id": "29840"} -{"city": "BEECH ISLAND", "loc": [-81.934794, 33.517766], "pop": 45886, "state": "SC", "_id": "29841"} -{"city": "OLAR", "loc": [-81.164042, 33.18364], "pop": 1503, "state": "SC", "_id": "29843"} -{"city": "PLUM BRANCH", "loc": [-82.248041, 33.832893], "pop": 1144, "state": "SC", "_id": "29845"} -{"city": "TRENTON", "loc": [-81.853392, 33.693757], "pop": 2825, "state": "SC", "_id": "29847"} -{"city": "TROY", "loc": [-82.294077, 33.955654], "pop": 125, "state": "SC", "_id": "29848"} -{"city": "ULMER", "loc": [-81.221292, 33.057597], "pop": 1241, "state": "SC", "_id": "29849"} -{"city": "WARRENVILLE", "loc": [-81.829397, 33.519917], "pop": 8739, "state": "SC", "_id": "29851"} -{"city": "WILLISTON", "loc": [-81.416146, 33.392608], "pop": 5011, "state": "SC", "_id": "29853"} -{"city": "WINDSOR", "loc": [-81.513626, 33.467446], "pop": 2264, "state": "SC", "_id": "29856"} -{"city": "BURTON", "loc": [-80.709026, 32.418035], "pop": 43849, "state": "SC", "_id": "29902"} -{"city": "BLUFFTON", "loc": [-80.872079, 32.251275], "pop": 6912, "state": "SC", "_id": "29910"} -{"city": "BRUNSON", "loc": [-81.180719, 32.941297], "pop": 1422, "state": "SC", "_id": "29911"} -{"city": "EARLY BRANCH", "loc": [-81.030011, 32.628893], "pop": 1494, "state": "SC", "_id": "29916"} -{"city": "ESTILL", "loc": [-81.217768, 32.737514], "pop": 6210, "state": "SC", "_id": "29918"} -{"city": "ST HELENA ISLAND", "loc": [-80.533863, 32.380213], "pop": 4669, "state": "SC", "_id": "29920"} -{"city": "GARNETT", "loc": [-81.239629, 32.600666], "pop": 61, "state": "SC", "_id": "29922"} -{"city": "HAMPTON", "loc": [-81.097333, 32.87288], "pop": 5987, "state": "SC", "_id": "29924"} -{"city": "HILTON HEAD ISLA", "loc": [-80.728088, 32.229426], "pop": 9480, "state": "SC", "_id": "29926"} -{"city": "HARDEEVILLE", "loc": [-81.066865, 32.263512], "pop": 4948, "state": "SC", "_id": "29927"} -{"city": "HILTON HEAD ISLA", "loc": [-80.756577, 32.158718], "pop": 14504, "state": "SC", "_id": "29928"} -{"city": "ISLANDTON", "loc": [-80.891589, 32.950324], "pop": 119, "state": "SC", "_id": "29929"} -{"city": "LURAY", "loc": [-81.223506, 32.86177], "pop": 687, "state": "SC", "_id": "29932"} -{"city": "PINELAND", "loc": [-81.176116, 32.602065], "pop": 925, "state": "SC", "_id": "29934"} -{"city": "PORT ROYAL", "loc": [-80.69278, 32.384205], "pop": 2526, "state": "SC", "_id": "29935"} -{"city": "COOSAWATCHIE", "loc": [-80.949435, 32.491938], "pop": 6342, "state": "SC", "_id": "29936"} -{"city": "SEABROOK", "loc": [-80.740793, 32.526884], "pop": 915, "state": "SC", "_id": "29940"} -{"city": "TILLMAN", "loc": [-81.077567, 32.492719], "pop": 1679, "state": "SC", "_id": "29943"} -{"city": "VARNVILLE", "loc": [-81.028839, 32.834893], "pop": 1890, "state": "SC", "_id": "29944"} -{"city": "YEMASSEE", "loc": [-80.812932, 32.642983], "pop": 5421, "state": "SC", "_id": "29945"} -{"city": "ALCESTER", "loc": [-96.633243, 43.004726], "pop": 2064, "state": "SD", "_id": "57001"} -{"city": "AURORA", "loc": [-96.704268, 44.283786], "pop": 1252, "state": "SD", "_id": "57002"} -{"city": "BALTIC", "loc": [-96.756272, 43.730908], "pop": 1839, "state": "SD", "_id": "57003"} -{"city": "BERESFORD", "loc": [-96.781256, 43.087409], "pop": 2639, "state": "SD", "_id": "57004"} -{"city": "CORSON", "loc": [-96.578201, 43.596413], "pop": 4155, "state": "SD", "_id": "57005"} -{"city": "BROOKINGS", "loc": [-96.791408, 44.305619], "pop": 18164, "state": "SD", "_id": "57006"} -{"city": "BURBANK", "loc": [-96.846569, 42.763798], "pop": 293, "state": "SD", "_id": "57010"} -{"city": "CANISTOTA", "loc": [-97.288901, 43.585639], "pop": 1255, "state": "SD", "_id": "57012"} -{"city": "CANTON", "loc": [-96.593796, 43.303819], "pop": 3210, "state": "SD", "_id": "57013"} -{"city": "CENTERVILLE", "loc": [-96.963637, 43.117635], "pop": 1048, "state": "SD", "_id": "57014"} -{"city": "CHANCELLOR", "loc": [-96.982695, 43.407962], "pop": 887, "state": "SD", "_id": "57015"} -{"city": "CHESTER", "loc": [-96.975883, 43.898077], "pop": 799, "state": "SD", "_id": "57016"} -{"city": "COLMAN", "loc": [-96.818882, 43.955761], "pop": 1013, "state": "SD", "_id": "57017"} -{"city": "COLTON", "loc": [-96.957202, 43.795102], "pop": 1242, "state": "SD", "_id": "57018"} -{"city": "CROOKS", "loc": [-96.818259, 43.64509], "pop": 1262, "state": "SD", "_id": "57020"} -{"city": "DAVIS", "loc": [-96.979206, 43.286365], "pop": 357, "state": "SD", "_id": "57021"} -{"city": "DELL RAPIDS", "loc": [-96.722315, 43.822759], "pop": 3128, "state": "SD", "_id": "57022"} -{"city": "EGAN", "loc": [-96.649252, 43.986592], "pop": 658, "state": "SD", "_id": "57024"} -{"city": "ELK POINT", "loc": [-96.686954, 42.738219], "pop": 2698, "state": "SD", "_id": "57025"} -{"city": "ELKTON", "loc": [-96.501095, 44.234984], "pop": 853, "state": "SD", "_id": "57026"} -{"city": "FAIRVIEW", "loc": [-96.574261, 43.208965], "pop": 472, "state": "SD", "_id": "57027"} -{"city": "FLANDREAU", "loc": [-96.622184, 44.06578], "pop": 3851, "state": "SD", "_id": "57028"} -{"city": "FREEMAN", "loc": [-97.460073, 43.360501], "pop": 2190, "state": "SD", "_id": "57029"} -{"city": "GARRETSON", "loc": [-96.519626, 43.71617], "pop": 1546, "state": "SD", "_id": "57030"} -{"city": "GAYVILLE", "loc": [-97.18295, 42.883516], "pop": 572, "state": "SD", "_id": "57031"} -{"city": "HARRISBURG", "loc": [-96.686389, 43.446021], "pop": 3387, "state": "SD", "_id": "57032"} -{"city": "HARTFORD", "loc": [-96.950052, 43.615472], "pop": 3110, "state": "SD", "_id": "57033"} -{"city": "HUDSON", "loc": [-96.53063, 43.128357], "pop": 774, "state": "SD", "_id": "57034"} -{"city": "HUMBOLDT", "loc": [-97.06971, 43.612026], "pop": 1090, "state": "SD", "_id": "57035"} -{"city": "HURLEY", "loc": [-97.190364, 43.289256], "pop": 1124, "state": "SD", "_id": "57036"} -{"city": "IRENE", "loc": [-97.255797, 43.102683], "pop": 1320, "state": "SD", "_id": "57037"} -{"city": "JEFFERSON", "loc": [-96.578392, 42.601341], "pop": 1262, "state": "SD", "_id": "57038"} -{"city": "LENNOX", "loc": [-96.882061, 43.345066], "pop": 2852, "state": "SD", "_id": "57039"} -{"city": "LESTERVILLE", "loc": [-97.548282, 43.054976], "pop": 680, "state": "SD", "_id": "57040"} -{"city": "MADISON", "loc": [-97.11486, 44.005434], "pop": 7745, "state": "SD", "_id": "57042"} -{"city": "MARION", "loc": [-97.277066, 43.41878], "pop": 1377, "state": "SD", "_id": "57043"} -{"city": "MECKLING", "loc": [-97.092249, 42.848994], "pop": 228, "state": "SD", "_id": "57044"} -{"city": "MENNO", "loc": [-97.564986, 43.233968], "pop": 1337, "state": "SD", "_id": "57045"} -{"city": "MISSION HILL", "loc": [-97.334877, 42.983611], "pop": 546, "state": "SD", "_id": "57046"} -{"city": "MONROE", "loc": [-97.218167, 43.477883], "pop": 239, "state": "SD", "_id": "57047"} -{"city": "MONTROSE", "loc": [-97.188493, 43.706262], "pop": 958, "state": "SD", "_id": "57048"} -{"city": "DAKOTA DUNES", "loc": [-96.50761, 42.532706], "pop": 2491, "state": "SD", "_id": "57049"} -{"city": "NUNDA", "loc": [-96.994209, 44.152459], "pop": 334, "state": "SD", "_id": "57050"} -{"city": "OLDHAM", "loc": [-97.269575, 44.245722], "pop": 655, "state": "SD", "_id": "57051"} -{"city": "OLIVET", "loc": [-97.718355, 43.292811], "pop": 555, "state": "SD", "_id": "57052"} -{"city": "PARKER", "loc": [-97.133298, 43.40204], "pop": 1494, "state": "SD", "_id": "57053"} -{"city": "RAMONA", "loc": [-97.234889, 44.122887], "pop": 641, "state": "SD", "_id": "57054"} -{"city": "RENNER", "loc": [-96.711925, 43.636625], "pop": 1444, "state": "SD", "_id": "57055"} -{"city": "RUTLAND", "loc": [-96.951856, 44.068282], "pop": 213, "state": "SD", "_id": "57057"} -{"city": "SALEM", "loc": [-97.379695, 43.735583], "pop": 1963, "state": "SD", "_id": "57058"} -{"city": "SCOTLAND", "loc": [-97.729596, 43.121208], "pop": 1611, "state": "SD", "_id": "57059"} -{"city": "SHERMAN", "loc": [-96.544396, 43.798677], "pop": 507, "state": "SD", "_id": "57060"} -{"city": "SINAI", "loc": [-97.054332, 44.239745], "pop": 296, "state": "SD", "_id": "57061"} -{"city": "SPRINGFIELD", "loc": [-97.928825, 42.868694], "pop": 2044, "state": "SD", "_id": "57062"} -{"city": "TABOR", "loc": [-97.692282, 42.938262], "pop": 853, "state": "SD", "_id": "57063"} -{"city": "TEA", "loc": [-96.817734, 43.471114], "pop": 2885, "state": "SD", "_id": "57064"} -{"city": "TRENT", "loc": [-96.632574, 43.894159], "pop": 865, "state": "SD", "_id": "57065"} -{"city": "TYNDALL", "loc": [-97.863285, 42.990043], "pop": 1451, "state": "SD", "_id": "57066"} -{"city": "UTICA", "loc": [-97.455095, 42.936629], "pop": 1183, "state": "SD", "_id": "57067"} -{"city": "VALLEY SPRINGS", "loc": [-96.495637, 43.577306], "pop": 1709, "state": "SD", "_id": "57068"} -{"city": "VERMILLION", "loc": [-96.925784, 42.795109], "pop": 11446, "state": "SD", "_id": "57069"} -{"city": "VIBORG", "loc": [-97.114048, 43.181497], "pop": 1664, "state": "SD", "_id": "57070"} -{"city": "VOLGA", "loc": [-96.925148, 44.322354], "pop": 1562, "state": "SD", "_id": "57071"} -{"city": "VOLIN", "loc": [-97.228231, 42.969617], "pop": 782, "state": "SD", "_id": "57072"} -{"city": "WAKONDA", "loc": [-97.069374, 42.996001], "pop": 938, "state": "SD", "_id": "57073"} -{"city": "WARD", "loc": [-96.481325, 44.155883], "pop": 120, "state": "SD", "_id": "57074"} -{"city": "WENTWORTH", "loc": [-96.961456, 43.985242], "pop": 394, "state": "SD", "_id": "57075"} -{"city": "WINFRED", "loc": [-97.309266, 43.959511], "pop": 424, "state": "SD", "_id": "57076"} -{"city": "WORTHING", "loc": [-96.752935, 43.292531], "pop": 882, "state": "SD", "_id": "57077"} -{"city": "YANKTON", "loc": [-97.398624, 42.882086], "pop": 14765, "state": "SD", "_id": "57078"} -{"city": "SIOUX FALLS", "loc": [-96.726927, 43.546131], "pop": 530, "state": "SD", "_id": "57102"} -{"city": "SIOUX FALLS", "loc": [-96.686415, 43.537386], "pop": 32508, "state": "SD", "_id": "57103"} -{"city": "SIOUX FALLS", "loc": [-96.737535, 43.551355], "pop": 22081, "state": "SD", "_id": "57104"} -{"city": "SIOUX FALLS", "loc": [-96.734141, 43.523972], "pop": 26347, "state": "SD", "_id": "57105"} -{"city": "SIOUX FALLS", "loc": [-96.792376, 43.517912], "pop": 16823, "state": "SD", "_id": "57106"} -{"city": "SIOUX FALLS", "loc": [-96.802811, 43.556628], "pop": 3331, "state": "SD", "_id": "57107"} -{"city": "BUFFALO RIDGE", "loc": [-96.834165, 43.516936], "pop": 731, "state": "SD", "_id": "57115"} -{"city": "SIOUX FALLS", "loc": [-96.766199, 43.508535], "pop": 426, "state": "SD", "_id": "57116"} -{"city": "WATERTOWN", "loc": [-97.123977, 44.904295], "pop": 20148, "state": "SD", "_id": "57201"} -{"city": "WAVERLY", "loc": [-96.946744, 45.003209], "pop": 163, "state": "SD", "_id": "57202"} -{"city": "ARLINGTON", "loc": [-97.068734, 44.366769], "pop": 358, "state": "SD", "_id": "57212"} -{"city": "ASTORIA", "loc": [-96.541634, 44.573515], "pop": 370, "state": "SD", "_id": "57213"} -{"city": "BADGER", "loc": [-97.218368, 44.491802], "pop": 354, "state": "SD", "_id": "57214"} -{"city": "BIG STONE CITY", "loc": [-96.561434, 45.28504], "pop": 1568, "state": "SD", "_id": "57216"} -{"city": "BRADLEY", "loc": [-97.638712, 45.075099], "pop": 399, "state": "SD", "_id": "57217"} -{"city": "BRANDT", "loc": [-96.643545, 44.67383], "pop": 655, "state": "SD", "_id": "57218"} -{"city": "BUTLER", "loc": [-97.746573, 45.308982], "pop": 769, "state": "SD", "_id": "57219"} -{"city": "BRUCE", "loc": [-96.910984, 44.467453], "pop": 929, "state": "SD", "_id": "57220"} -{"city": "BRYANT", "loc": [-97.453659, 44.598671], "pop": 627, "state": "SD", "_id": "57221"} -{"city": "CASTLEWOOD", "loc": [-97.020432, 44.731339], "pop": 1083, "state": "SD", "_id": "57223"} -{"city": "CLAIRE CITY", "loc": [-97.107274, 45.875522], "pop": 394, "state": "SD", "_id": "57224"} -{"city": "CLARK", "loc": [-97.726536, 44.878], "pop": 2062, "state": "SD", "_id": "57225"} -{"city": "ALTAMONT", "loc": [-96.700541, 44.763703], "pop": 1786, "state": "SD", "_id": "57226"} -{"city": "CORONA", "loc": [-96.664897, 45.359483], "pop": 613, "state": "SD", "_id": "57227"} -{"city": "57230", "loc": [-96.93657, 44.669557], "pop": 245, "state": "SD", "_id": "57230"} -{"city": "DE SMET", "loc": [-97.563432, 44.385169], "pop": 1956, "state": "SD", "_id": "57231"} -{"city": "EDEN", "loc": [-97.374091, 45.621074], "pop": 318, "state": "SD", "_id": "57232"} -{"city": "ERWIN", "loc": [-97.410291, 44.491484], "pop": 225, "state": "SD", "_id": "57233"} -{"city": "DEMPSTER", "loc": [-96.923678, 44.576998], "pop": 903, "state": "SD", "_id": "57234"} -{"city": "FLORENCE", "loc": [-97.286643, 45.055388], "pop": 664, "state": "SD", "_id": "57235"} -{"city": "GARDEN CITY", "loc": [-97.568025, 44.947413], "pop": 175, "state": "SD", "_id": "57236"} -{"city": "GARY", "loc": [-96.504362, 44.827022], "pop": 733, "state": "SD", "_id": "57237"} -{"city": "BEMIS", "loc": [-96.811368, 44.886232], "pop": 509, "state": "SD", "_id": "57238"} -{"city": "GRENVILLE", "loc": [-97.415457, 45.489673], "pop": 334, "state": "SD", "_id": "57239"} -{"city": "HAYTI", "loc": [-97.230509, 44.664737], "pop": 720, "state": "SD", "_id": "57241"} -{"city": "HAZEL", "loc": [-97.30829, 44.75717], "pop": 496, "state": "SD", "_id": "57242"} -{"city": "HENRY", "loc": [-97.444216, 44.88576], "pop": 435, "state": "SD", "_id": "57243"} -{"city": "HETLAND", "loc": [-97.141024, 44.367519], "pop": 1069, "state": "SD", "_id": "57244"} -{"city": "KRANZBURG", "loc": [-96.947013, 44.879264], "pop": 525, "state": "SD", "_id": "57245"} -{"city": "LABOLT", "loc": [-96.689209, 45.041458], "pop": 188, "state": "SD", "_id": "57246"} -{"city": "LAKE CITY", "loc": [-97.348131, 45.68991], "pop": 401, "state": "SD", "_id": "57247"} -{"city": "LAKE NORDEN", "loc": [-97.200867, 44.584351], "pop": 900, "state": "SD", "_id": "57248"} -{"city": "LAKE PRESTON", "loc": [-97.356283, 44.367298], "pop": 1074, "state": "SD", "_id": "57249"} -{"city": "MARVIN", "loc": [-96.90996, 45.272651], "pop": 146, "state": "SD", "_id": "57251"} -{"city": "MILBANK", "loc": [-96.62548, 45.206127], "pop": 5235, "state": "SD", "_id": "57252"} -{"city": "NEW EFFINGTON", "loc": [-96.914989, 45.865868], "pop": 391, "state": "SD", "_id": "57255"} -{"city": "ORTLEY", "loc": [-97.176985, 45.340633], "pop": 151, "state": "SD", "_id": "57256"} -{"city": "PEEVER", "loc": [-97.001205, 45.520647], "pop": 992, "state": "SD", "_id": "57257"} -{"city": "RAYMOND", "loc": [-97.916781, 44.863651], "pop": 415, "state": "SD", "_id": "57258"} -{"city": "ALBEE", "loc": [-96.562366, 45.022142], "pop": 357, "state": "SD", "_id": "57259"} -{"city": "ROSHOLT", "loc": [-96.71741, 45.875315], "pop": 841, "state": "SD", "_id": "57260"} -{"city": "ROSLYN", "loc": [-97.540105, 45.500564], "pop": 533, "state": "SD", "_id": "57261"} -{"city": "AGENCY VILLAGE", "loc": [-97.02322, 45.664413], "pop": 4968, "state": "SD", "_id": "57262"} -{"city": "SOUTH SHORE", "loc": [-96.985885, 45.104919], "pop": 487, "state": "SD", "_id": "57263"} -{"city": "STOCKHOLM", "loc": [-96.81059, 45.10309], "pop": 213, "state": "SD", "_id": "57264"} -{"city": "STRANDBURG", "loc": [-96.790129, 45.038872], "pop": 133, "state": "SD", "_id": "57265"} -{"city": "SUMMIT", "loc": [-97.042654, 45.352128], "pop": 469, "state": "SD", "_id": "57266"} -{"city": "TORONTO", "loc": [-96.70774, 44.578622], "pop": 469, "state": "SD", "_id": "57268"} -{"city": "TWIN BROOKS", "loc": [-96.995601, 45.211519], "pop": 532, "state": "SD", "_id": "57269"} -{"city": "VEBLEN", "loc": [-97.312193, 45.853508], "pop": 765, "state": "SD", "_id": "57270"} -{"city": "VIENNA", "loc": [-97.545559, 44.690019], "pop": 454, "state": "SD", "_id": "57271"} -{"city": "WALLACE", "loc": [-97.445751, 45.081542], "pop": 276, "state": "SD", "_id": "57272"} -{"city": "WAUBAY", "loc": [-97.294987, 45.37837], "pop": 1437, "state": "SD", "_id": "57273"} -{"city": "LILY", "loc": [-97.515316, 45.322573], "pop": 3194, "state": "SD", "_id": "57274"} -{"city": "WHITE", "loc": [-96.614963, 44.413237], "pop": 1793, "state": "SD", "_id": "57276"} -{"city": "WILLOW LAKE", "loc": [-97.674747, 44.627225], "pop": 661, "state": "SD", "_id": "57278"} -{"city": "WILMOT", "loc": [-96.856006, 45.412487], "pop": 1095, "state": "SD", "_id": "57279"} -{"city": "LOOMIS", "loc": [-98.027026, 43.710921], "pop": 16187, "state": "SD", "_id": "57301"} -{"city": "FARMER", "loc": [-97.782087, 43.623847], "pop": 934, "state": "SD", "_id": "57311"} -{"city": "ALPENA", "loc": [-98.396191, 44.170907], "pop": 414, "state": "SD", "_id": "57312"} -{"city": "ARMOUR", "loc": [-98.341372, 43.316045], "pop": 1118, "state": "SD", "_id": "57313"} -{"city": "FORESTBURG", "loc": [-97.953958, 44.035362], "pop": 640, "state": "SD", "_id": "57314"} -{"city": "AVON", "loc": [-98.028261, 43.039725], "pop": 1130, "state": "SD", "_id": "57315"} -{"city": "BANCROFT", "loc": [-97.776733, 44.494419], "pop": 200, "state": "SD", "_id": "57316"} -{"city": "BONESTEEL", "loc": [-98.987959, 43.069536], "pop": 688, "state": "SD", "_id": "57317"} -{"city": "DOLTON", "loc": [-97.49591, 43.554306], "pop": 1055, "state": "SD", "_id": "57319"} -{"city": "CANOVA", "loc": [-97.534163, 43.885517], "pop": 506, "state": "SD", "_id": "57321"} -{"city": "CARPENTER", "loc": [-97.916825, 44.664768], "pop": 101, "state": "SD", "_id": "57322"} -{"city": "CARTHAGE", "loc": [-97.711632, 44.1496], "pop": 380, "state": "SD", "_id": "57323"} -{"city": "CAVOUR", "loc": [-98.020797, 44.364951], "pop": 444, "state": "SD", "_id": "57324"} -{"city": "CHAMBERLAIN", "loc": [-99.311819, 43.795295], "pop": 3218, "state": "SD", "_id": "57325"} -{"city": "CORSICA", "loc": [-98.356358, 43.421319], "pop": 1569, "state": "SD", "_id": "57328"} -{"city": "DANTE", "loc": [-98.174637, 42.996661], "pop": 519, "state": "SD", "_id": "57329"} -{"city": "DELMONT", "loc": [-98.159612, 43.257261], "pop": 468, "state": "SD", "_id": "57330"} -{"city": "DIMOCK", "loc": [-97.9988, 43.470476], "pop": 426, "state": "SD", "_id": "57331"} -{"city": "EMERY", "loc": [-97.647489, 43.565647], "pop": 888, "state": "SD", "_id": "57332"} -{"city": "ETHAN", "loc": [-98.059074, 43.542653], "pop": 975, "state": "SD", "_id": "57334"} -{"city": "FAIRFAX", "loc": [-98.830757, 43.035103], "pop": 423, "state": "SD", "_id": "57335"} -{"city": "57336", "loc": [-97.71435, 43.715415], "pop": 252, "state": "SD", "_id": "57336"} -{"city": "FEDORA", "loc": [-97.789003, 43.984125], "pop": 288, "state": "SD", "_id": "57337"} -{"city": "FORT THOMPSON", "loc": [-99.397305, 44.051695], "pop": 1495, "state": "SD", "_id": "57339"} -{"city": "FULTON", "loc": [-97.871218, 43.758767], "pop": 572, "state": "SD", "_id": "57340"} -{"city": "GANN VALLEY", "loc": [-99.054334, 44.069303], "pop": 264, "state": "SD", "_id": "57341"} -{"city": "GEDDES", "loc": [-98.69256, 43.259677], "pop": 725, "state": "SD", "_id": "57342"} -{"city": "HARRISON", "loc": [-98.523338, 43.454923], "pop": 233, "state": "SD", "_id": "57344"} -{"city": "HIGHMORE", "loc": [-99.454349, 44.532604], "pop": 1652, "state": "SD", "_id": "57345"} -{"city": "HITCHCOCK", "loc": [-98.450914, 44.583444], "pop": 501, "state": "SD", "_id": "57348"} -{"city": "ROSWELL", "loc": [-97.516012, 44.029296], "pop": 2098, "state": "SD", "_id": "57349"} -{"city": "HURON", "loc": [-98.216293, 44.359022], "pop": 15277, "state": "SD", "_id": "57350"} -{"city": "IROQUOIS", "loc": [-97.854172, 44.345517], "pop": 576, "state": "SD", "_id": "57353"} -{"city": "KAYLOR", "loc": [-97.820094, 43.202393], "pop": 223, "state": "SD", "_id": "57354"} -{"city": "KIMBALL", "loc": [-98.934305, 43.712881], "pop": 1546, "state": "SD", "_id": "57355"} -{"city": "LAKE ANDES", "loc": [-98.496355, 43.130288], "pop": 2556, "state": "SD", "_id": "57356"} -{"city": "RAVINIA", "loc": [-98.426413, 43.136123], "pop": 79, "state": "SD", "_id": "57357"} -{"city": "LANE", "loc": [-98.406496, 44.067383], "pop": 152, "state": "SD", "_id": "57358"} -{"city": "LETCHER", "loc": [-98.174279, 43.892324], "pop": 912, "state": "SD", "_id": "57359"} -{"city": "MARTY", "loc": [-98.422429, 42.97639], "pop": 306, "state": "SD", "_id": "57361"} -{"city": "MILLER", "loc": [-98.989395, 44.496644], "pop": 2938, "state": "SD", "_id": "57362"} -{"city": "MOUNT VERNON", "loc": [-98.263288, 43.7204], "pop": 731, "state": "SD", "_id": "57363"} -{"city": "NEW HOLLAND", "loc": [-98.628697, 43.431042], "pop": 358, "state": "SD", "_id": "57364"} -{"city": "PARKSTON", "loc": [-97.967824, 43.397796], "pop": 2231, "state": "SD", "_id": "57366"} -{"city": "PLANKINTON", "loc": [-98.469395, 43.737287], "pop": 1129, "state": "SD", "_id": "57368"} -{"city": "ACADEMY", "loc": [-98.889656, 43.40242], "pop": 2425, "state": "SD", "_id": "57369"} -{"city": "PUKWANA", "loc": [-99.177884, 43.778326], "pop": 577, "state": "SD", "_id": "57370"} -{"city": "REE HEIGHTS", "loc": [-99.22864, 44.560275], "pop": 288, "state": "SD", "_id": "57371"} -{"city": "SAINT LAWRENCE", "loc": [-98.875427, 44.521523], "pop": 425, "state": "SD", "_id": "57373"} -{"city": "SPENCER", "loc": [-97.59361, 43.755684], "pop": 658, "state": "SD", "_id": "57374"} -{"city": "STICKNEY", "loc": [-98.508843, 43.58422], "pop": 1099, "state": "SD", "_id": "57375"} -{"city": "TRIPP", "loc": [-97.971285, 43.240377], "pop": 1128, "state": "SD", "_id": "57376"} -{"city": "VIRGIL", "loc": [-98.392146, 44.325582], "pop": 167, "state": "SD", "_id": "57379"} -{"city": "WAGNER", "loc": [-98.281876, 43.081931], "pop": 2665, "state": "SD", "_id": "57380"} -{"city": "WESSINGTON", "loc": [-98.691982, 44.41285], "pop": 618, "state": "SD", "_id": "57381"} -{"city": "WESSINGTON SPRIN", "loc": [-98.611625, 44.069935], "pop": 1864, "state": "SD", "_id": "57382"} -{"city": "WHITE LAKE", "loc": [-98.707613, 43.756441], "pop": 717, "state": "SD", "_id": "57383"} -{"city": "WOLSEY", "loc": [-98.474251, 44.399072], "pop": 751, "state": "SD", "_id": "57384"} -{"city": "WOONSOCKET", "loc": [-98.243017, 44.057186], "pop": 1471, "state": "SD", "_id": "57385"} -{"city": "YALE", "loc": [-97.993249, 44.495591], "pop": 462, "state": "SD", "_id": "57386"} -{"city": "ABERDEEN", "loc": [-98.485642, 45.466109], "pop": 28786, "state": "SD", "_id": "57401"} -{"city": "AKASKA", "loc": [-100.118614, 45.332447], "pop": 52, "state": "SD", "_id": "57420"} -{"city": "AMHERST", "loc": [-97.930078, 45.707426], "pop": 227, "state": "SD", "_id": "57421"} -{"city": "ANDOVER", "loc": [-97.917497, 45.422171], "pop": 348, "state": "SD", "_id": "57422"} -{"city": "ATHOL", "loc": [-98.442549, 45.012833], "pop": 306, "state": "SD", "_id": "57424"} -{"city": "57425", "loc": [-98.630074, 45.022779], "pop": 110, "state": "SD", "_id": "57425"} -{"city": "BARNARD", "loc": [-98.553379, 45.720469], "pop": 172, "state": "SD", "_id": "57426"} -{"city": "BATH", "loc": [-98.355209, 45.456352], "pop": 593, "state": "SD", "_id": "57427"} -{"city": "BOWDLE", "loc": [-99.635978, 45.432881], "pop": 882, "state": "SD", "_id": "57428"} -{"city": "BRENTFORD", "loc": [-98.319281, 45.153063], "pop": 257, "state": "SD", "_id": "57429"} -{"city": "BRITTON", "loc": [-97.741835, 45.802304], "pop": 2507, "state": "SD", "_id": "57430"} -{"city": "CLAREMONT", "loc": [-98.040367, 45.666149], "pop": 400, "state": "SD", "_id": "57432"} -{"city": "COLUMBIA", "loc": [-98.295152, 45.671721], "pop": 585, "state": "SD", "_id": "57433"} -{"city": "VERDON", "loc": [-98.034323, 45.155125], "pop": 611, "state": "SD", "_id": "57434"} -{"city": "CRESBARD", "loc": [-98.941124, 45.169125], "pop": 312, "state": "SD", "_id": "57435"} -{"city": "DOLAND", "loc": [-98.094709, 44.81587], "pop": 997, "state": "SD", "_id": "57436"} -{"city": "ARTAS", "loc": [-99.615926, 45.769768], "pop": 1753, "state": "SD", "_id": "57437"} -{"city": "MIRANDA", "loc": [-99.134054, 45.06845], "pop": 1593, "state": "SD", "_id": "57438"} -{"city": "FRANKFORT", "loc": [-98.293496, 44.808416], "pop": 533, "state": "SD", "_id": "57440"} -{"city": "FREDERICK", "loc": [-98.517564, 45.849332], "pop": 524, "state": "SD", "_id": "57441"} -{"city": "GETTYSBURG", "loc": [-99.976626, 45.02588], "pop": 2065, "state": "SD", "_id": "57442"} -{"city": "GROTON", "loc": [-98.105814, 45.450345], "pop": 1814, "state": "SD", "_id": "57445"} -{"city": "HECLA", "loc": [-98.191774, 45.872515], "pop": 754, "state": "SD", "_id": "57446"} -{"city": "HOSMER", "loc": [-99.485745, 45.568988], "pop": 454, "state": "SD", "_id": "57448"} -{"city": "HOUGHTON", "loc": [-98.095186, 45.796746], "pop": 147, "state": "SD", "_id": "57449"} -{"city": "HOVEN", "loc": [-99.776286, 45.227799], "pop": 677, "state": "SD", "_id": "57450"} -{"city": "IPSWICH", "loc": [-99.014807, 45.448905], "pop": 1771, "state": "SD", "_id": "57451"} -{"city": "JAVA", "loc": [-99.835153, 45.413541], "pop": 518, "state": "SD", "_id": "57452"} -{"city": "LANGFORD", "loc": [-97.792547, 45.617381], "pop": 626, "state": "SD", "_id": "57454"} -{"city": "LEBANON", "loc": [-99.736579, 45.012318], "pop": 259, "state": "SD", "_id": "57455"} -{"city": "LEOLA", "loc": [-98.901544, 45.732265], "pop": 1192, "state": "SD", "_id": "57456"} -{"city": "LONGLAKE", "loc": [-99.250677, 45.796812], "pop": 378, "state": "SD", "_id": "57457"} -{"city": "MANSFIELD", "loc": [-98.606568, 45.226727], "pop": 92, "state": "SD", "_id": "57460"} -{"city": "MELLETTE", "loc": [-98.48237, 45.16312], "pop": 362, "state": "SD", "_id": "57461"} -{"city": "MINA", "loc": [-98.756581, 45.439116], "pop": 515, "state": "SD", "_id": "57462"} -{"city": "NORTHVILLE", "loc": [-98.658853, 45.161112], "pop": 328, "state": "SD", "_id": "57465"} -{"city": "ONAKA", "loc": [-99.455052, 45.196527], "pop": 143, "state": "SD", "_id": "57466"} -{"city": "ORIENT", "loc": [-99.105787, 44.834347], "pop": 441, "state": "SD", "_id": "57467"} -{"city": "PIERPONT", "loc": [-97.812844, 45.495983], "pop": 328, "state": "SD", "_id": "57468"} -{"city": "REDFIELD", "loc": [-98.511234, 44.871853], "pop": 4033, "state": "SD", "_id": "57469"} -{"city": "ROCKHAM", "loc": [-98.768683, 44.971579], "pop": 237, "state": "SD", "_id": "57470"} -{"city": "ROSCOE", "loc": [-99.332631, 45.427119], "pop": 673, "state": "SD", "_id": "57471"} -{"city": "SELBY", "loc": [-100.054067, 45.478587], "pop": 1284, "state": "SD", "_id": "57472"} -{"city": "SENECA", "loc": [-99.460986, 45.026191], "pop": 246, "state": "SD", "_id": "57473"} -{"city": "STRATFORD", "loc": [-98.279153, 45.286595], "pop": 418, "state": "SD", "_id": "57474"} -{"city": "TOLSTOY", "loc": [-99.617553, 45.170199], "pop": 189, "state": "SD", "_id": "57475"} -{"city": "TULARE", "loc": [-98.553926, 44.730489], "pop": 526, "state": "SD", "_id": "57476"} -{"city": "TURTON", "loc": [-98.099641, 45.037938], "pop": 154, "state": "SD", "_id": "57477"} -{"city": "WARNER", "loc": [-98.475697, 45.348627], "pop": 1060, "state": "SD", "_id": "57479"} -{"city": "WETONKA", "loc": [-98.585623, 45.625025], "pop": 337, "state": "SD", "_id": "57481"} -{"city": "ZELL", "loc": [-98.831746, 44.854624], "pop": 131, "state": "SD", "_id": "57483"} -{"city": "PIERRE", "loc": [-100.321057, 44.369514], "pop": 14138, "state": "SD", "_id": "57501"} -{"city": "AGAR", "loc": [-100.071238, 44.839345], "pop": 89, "state": "SD", "_id": "57520"} -{"city": "BELVIDERE", "loc": [-101.238691, 43.886031], "pop": 177, "state": "SD", "_id": "57521"} -{"city": "BLUNT", "loc": [-99.946278, 44.502572], "pop": 484, "state": "SD", "_id": "57522"} -{"city": "LUCAS", "loc": [-99.268963, 43.210602], "pop": 1258, "state": "SD", "_id": "57523"} -{"city": "CARTER", "loc": [-100.172958, 43.368487], "pop": 118, "state": "SD", "_id": "57526"} -{"city": "CEDARBUTTE", "loc": [-101.131585, 43.64731], "pop": 208, "state": "SD", "_id": "57527"} -{"city": "COLOME", "loc": [-99.693273, 43.227334], "pop": 771, "state": "SD", "_id": "57528"} -{"city": "DALLAS", "loc": [-99.513994, 43.235075], "pop": 167, "state": "SD", "_id": "57529"} -{"city": "DRAPER", "loc": [-100.508514, 43.926035], "pop": 304, "state": "SD", "_id": "57531"} -{"city": "FORT PIERRE", "loc": [-100.404323, 44.342587], "pop": 2179, "state": "SD", "_id": "57532"} -{"city": "DIXON", "loc": [-99.430214, 43.226908], "pop": 2401, "state": "SD", "_id": "57533"} -{"city": "HAMILL", "loc": [-99.691752, 43.643942], "pop": 78, "state": "SD", "_id": "57534"} -{"city": "HARROLD", "loc": [-99.738166, 44.521476], "pop": 195, "state": "SD", "_id": "57536"} -{"city": "HAYES", "loc": [-100.735938, 44.421898], "pop": 191, "state": "SD", "_id": "57537"} -{"city": "HERRICK", "loc": [-99.217267, 43.101186], "pop": 337, "state": "SD", "_id": "57538"} -{"city": "HOLABIRD", "loc": [-99.594299, 44.517327], "pop": 44, "state": "SD", "_id": "57540"} -{"city": "IDEAL", "loc": [-99.927949, 43.559612], "pop": 473, "state": "SD", "_id": "57541"} -{"city": "IONA", "loc": [-99.488073, 43.575756], "pop": 146, "state": "SD", "_id": "57542"} -{"city": "KADOKA", "loc": [-101.552272, 43.84457], "pop": 1024, "state": "SD", "_id": "57543"} -{"city": "KENNEBEC", "loc": [-99.850191, 43.892139], "pop": 495, "state": "SD", "_id": "57544"} -{"city": "KEYAPAHA", "loc": [-100.163958, 43.073497], "pop": 48, "state": "SD", "_id": "57545"} -{"city": "LONG VALLEY", "loc": [-101.382145, 43.569784], "pop": 214, "state": "SD", "_id": "57547"} -{"city": "LOWER BRULE", "loc": [-99.613896, 44.093692], "pop": 1118, "state": "SD", "_id": "57548"} -{"city": "VETAL", "loc": [-101.740814, 43.178543], "pop": 2159, "state": "SD", "_id": "57551"} -{"city": "OTTUMWA", "loc": [-101.292442, 44.237443], "pop": 789, "state": "SD", "_id": "57552"} -{"city": "MILESVILLE", "loc": [-101.752276, 44.428259], "pop": 191, "state": "SD", "_id": "57553"} -{"city": "MISSION", "loc": [-100.595364, 43.285017], "pop": 3169, "state": "SD", "_id": "57555"} -{"city": "MISSION RIDGE", "loc": [-100.894553, 44.623851], "pop": 83, "state": "SD", "_id": "57557"} -{"city": "MURDO", "loc": [-100.712107, 43.896115], "pop": 855, "state": "SD", "_id": "57559"} -{"city": "NORRIS", "loc": [-101.151664, 43.466176], "pop": 322, "state": "SD", "_id": "57560"} -{"city": "OKATON", "loc": [-100.935054, 43.934157], "pop": 165, "state": "SD", "_id": "57562"} -{"city": "ONIDA", "loc": [-100.095667, 44.712543], "pop": 1500, "state": "SD", "_id": "57564"} -{"city": "PARMELEE", "loc": [-101.008089, 43.311716], "pop": 1272, "state": "SD", "_id": "57566"} -{"city": "PHILIP", "loc": [-101.687611, 44.055006], "pop": 1644, "state": "SD", "_id": "57567"} -{"city": "PRESHO", "loc": [-100.074645, 43.899538], "pop": 897, "state": "SD", "_id": "57568"} -{"city": "RELIANCE", "loc": [-99.485668, 43.830241], "pop": 790, "state": "SD", "_id": "57569"} -{"city": "SAINT CHARLES", "loc": [-99.090969, 43.108893], "pop": 85, "state": "SD", "_id": "57571"} -{"city": "SAINT FRANCIS", "loc": [-100.88502, 43.191017], "pop": 3917, "state": "SD", "_id": "57572"} -{"city": "TUTHILL", "loc": [-101.470116, 43.119623], "pop": 286, "state": "SD", "_id": "57574"} -{"city": "VIVIAN", "loc": [-100.285989, 43.95352], "pop": 192, "state": "SD", "_id": "57576"} -{"city": "WANBLEE", "loc": [-101.721856, 43.55893], "pop": 1269, "state": "SD", "_id": "57577"} -{"city": "WEWELA", "loc": [-99.747317, 43.079352], "pop": 207, "state": "SD", "_id": "57578"} -{"city": "WHITE RIVER", "loc": [-100.74487, 43.56662], "pop": 1259, "state": "SD", "_id": "57579"} -{"city": "CLEARFIELD", "loc": [-99.861907, 43.355504], "pop": 5111, "state": "SD", "_id": "57580"} -{"city": "WITTEN", "loc": [-100.078291, 43.44982], "pop": 118, "state": "SD", "_id": "57584"} -{"city": "WOOD", "loc": [-100.437929, 43.536599], "pop": 342, "state": "SD", "_id": "57585"} -{"city": "MOBRIDGE", "loc": [-100.431488, 45.540723], "pop": 4099, "state": "SD", "_id": "57601"} -{"city": "BISON", "loc": [-102.482707, 45.516126], "pop": 710, "state": "SD", "_id": "57620"} -{"city": "CHERRY CREEK", "loc": [-101.65178, 44.621018], "pop": 715, "state": "SD", "_id": "57622"} -{"city": "DUPREE", "loc": [-101.63368, 45.007851], "pop": 1441, "state": "SD", "_id": "57623"} -{"city": "FAITH", "loc": [-102.054142, 44.992609], "pop": 660, "state": "SD", "_id": "57626"} -{"city": "FIRESTEEL", "loc": [-101.223461, 45.430736], "pop": 50, "state": "SD", "_id": "57628"} -{"city": "GLAD VALLEY", "loc": [-101.795168, 45.43696], "pop": 64, "state": "SD", "_id": "57629"} -{"city": "GLENCROSS", "loc": [-100.894417, 45.450549], "pop": 56, "state": "SD", "_id": "57630"} -{"city": "GLENHAM", "loc": [-100.27156, 45.53351], "pop": 134, "state": "SD", "_id": "57631"} -{"city": "HERREID", "loc": [-100.048983, 45.845164], "pop": 829, "state": "SD", "_id": "57632"} -{"city": "ISABEL", "loc": [-101.273637, 45.064016], "pop": 3606, "state": "SD", "_id": "57633"} -{"city": "KELDRON", "loc": [-101.939465, 45.902221], "pop": 72, "state": "SD", "_id": "57634"} -{"city": "LEMMON", "loc": [-102.192772, 45.915892], "pop": 2127, "state": "SD", "_id": "57638"} -{"city": "LODGEPOLE", "loc": [-102.759917, 45.823193], "pop": 216, "state": "SD", "_id": "57640"} -{"city": "MC INTOSH", "loc": [-101.500393, 45.81244], "pop": 719, "state": "SD", "_id": "57641"} -{"city": "MC LAUGHLIN", "loc": [-100.877596, 45.748884], "pop": 2545, "state": "SD", "_id": "57642"} -{"city": "MAHTO", "loc": [-100.658897, 45.767644], "pop": 31, "state": "SD", "_id": "57643"} -{"city": "MEADOW", "loc": [-102.284425, 45.353769], "pop": 530, "state": "SD", "_id": "57644"} -{"city": "MORRISTOWN", "loc": [-101.699838, 45.900642], "pop": 190, "state": "SD", "_id": "57645"} -{"city": "MOUND CITY", "loc": [-100.047856, 45.678645], "pop": 548, "state": "SD", "_id": "57646"} -{"city": "PARADE", "loc": [-100.743399, 45.108068], "pop": 819, "state": "SD", "_id": "57647"} -{"city": "POLLOCK", "loc": [-100.287518, 45.889043], "pop": 493, "state": "SD", "_id": "57648"} -{"city": "PRAIRIE CITY", "loc": [-102.80847, 45.581342], "pop": 172, "state": "SD", "_id": "57649"} -{"city": "RALPH", "loc": [-103.035552, 45.855365], "pop": 101, "state": "SD", "_id": "57650"} -{"city": "REVA", "loc": [-103.069163, 45.527653], "pop": 332, "state": "SD", "_id": "57651"} -{"city": "SHADEHILL", "loc": [-102.189131, 45.669855], "pop": 49, "state": "SD", "_id": "57653"} -{"city": "TIMBER LAKE", "loc": [-101.035077, 45.392737], "pop": 939, "state": "SD", "_id": "57656"} -{"city": "TRAIL CITY", "loc": [-100.682902, 45.440082], "pop": 53, "state": "SD", "_id": "57657"} -{"city": "WAKPALA", "loc": [-100.533176, 45.700617], "pop": 582, "state": "SD", "_id": "57658"} -{"city": "WATAUGA", "loc": [-101.512936, 45.927593], "pop": 56, "state": "SD", "_id": "57660"} -{"city": "ROCKERVILLE", "loc": [-103.200259, 44.077041], "pop": 45328, "state": "SD", "_id": "57701"} -{"city": "SILVER CITY", "loc": [-103.283406, 44.069796], "pop": 20904, "state": "SD", "_id": "57702"} -{"city": "ELLSWORTH AFB", "loc": [-103.07591, 44.144655], "pop": 6355, "state": "SD", "_id": "57706"} -{"city": "BETHLEHEM", "loc": [-103.461246, 44.288967], "pop": 374, "state": "SD", "_id": "57708"} -{"city": "ALLEN", "loc": [-101.932858, 43.290818], "pop": 761, "state": "SD", "_id": "57714"} -{"city": "DENBY", "loc": [-102.173776, 43.078962], "pop": 435, "state": "SD", "_id": "57716"} -{"city": "BELLE FOURCHE", "loc": [-103.839601, 44.672281], "pop": 5841, "state": "SD", "_id": "57717"} -{"city": "BLACK HAWK", "loc": [-103.348634, 44.151173], "pop": 7418, "state": "SD", "_id": "57718"} -{"city": "BOX ELDER", "loc": [-103.068237, 44.119858], "pop": 3690, "state": "SD", "_id": "57719"} -{"city": "BUFFALO", "loc": [-103.582605, 45.574001], "pop": 913, "state": "SD", "_id": "57720"} -{"city": "BUFFALO GAP", "loc": [-103.315749, 43.495762], "pop": 302, "state": "SD", "_id": "57722"} -{"city": "SKY RANCH", "loc": [-103.963232, 45.595289], "pop": 258, "state": "SD", "_id": "57724"} -{"city": "CAPUTA", "loc": [-103.023406, 43.980116], "pop": 578, "state": "SD", "_id": "57725"} -{"city": "CREIGHTON", "loc": [-102.177043, 44.283087], "pop": 127, "state": "SD", "_id": "57729"} -{"city": "CRAZY HORSE", "loc": [-103.618465, 43.740886], "pop": 4781, "state": "SD", "_id": "57730"} -{"city": "DEADWOOD", "loc": [-103.699939, 44.356628], "pop": 2726, "state": "SD", "_id": "57732"} -{"city": "EDGEMONT", "loc": [-103.811018, 43.287361], "pop": 1264, "state": "SD", "_id": "57735"} -{"city": "ELM SPRINGS", "loc": [-102.631208, 44.239743], "pop": 222, "state": "SD", "_id": "57736"} -{"city": "ENNING", "loc": [-102.6208, 44.538884], "pop": 275, "state": "SD", "_id": "57737"} -{"city": "FAIRBURN", "loc": [-103.213335, 43.66228], "pop": 203, "state": "SD", "_id": "57738"} -{"city": "FORT MEADE", "loc": [-103.47233, 44.409097], "pop": 124, "state": "SD", "_id": "57741"} -{"city": "FRUITDALE", "loc": [-103.689568, 44.660039], "pop": 234, "state": "SD", "_id": "57742"} -{"city": "HERMOSA", "loc": [-103.20596, 43.818845], "pop": 919, "state": "SD", "_id": "57744"} -{"city": "HILL CITY", "loc": [-103.578158, 43.93768], "pop": 1671, "state": "SD", "_id": "57745"} -{"city": "HOT SPRINGS", "loc": [-103.476555, 43.422308], "pop": 5467, "state": "SD", "_id": "57747"} -{"city": "PLAINVIEW", "loc": [-102.215763, 44.534952], "pop": 153, "state": "SD", "_id": "57748"} -{"city": "INTERIOR", "loc": [-101.964238, 43.731892], "pop": 127, "state": "SD", "_id": "57750"} -{"city": "KEYSTONE", "loc": [-103.335235, 43.969604], "pop": 2573, "state": "SD", "_id": "57751"} -{"city": "KYLE", "loc": [-102.212419, 43.439437], "pop": 1424, "state": "SD", "_id": "57752"} -{"city": "SPEARFISH CANYON", "loc": [-103.769841, 44.349012], "pop": 4626, "state": "SD", "_id": "57754"} -{"city": "LUDLOW", "loc": [-103.31115, 45.874655], "pop": 142, "state": "SD", "_id": "57755"} -{"city": "MANDERSON", "loc": [-102.374493, 43.309844], "pop": 1027, "state": "SD", "_id": "57756"} -{"city": "MARCUS", "loc": [-102.330697, 44.674678], "pop": 28, "state": "SD", "_id": "57757"} -{"city": "MUD BUTTE", "loc": [-102.803129, 45.031046], "pop": 104, "state": "SD", "_id": "57758"} -{"city": "NEMO", "loc": [-103.544863, 44.209718], "pop": 393, "state": "SD", "_id": "57759"} -{"city": "NEWELL", "loc": [-103.391359, 44.740979], "pop": 1223, "state": "SD", "_id": "57760"} -{"city": "NEW UNDERWOOD", "loc": [-102.813635, 44.087354], "pop": 763, "state": "SD", "_id": "57761"} -{"city": "NISLAND", "loc": [-103.540176, 44.666539], "pop": 313, "state": "SD", "_id": "57762"} -{"city": "OELRICHS", "loc": [-103.216181, 43.155063], "pop": 284, "state": "SD", "_id": "57763"} -{"city": "OPAL", "loc": [-102.477752, 44.867111], "pop": 235, "state": "SD", "_id": "57765"} -{"city": "ORAL", "loc": [-103.183215, 43.387587], "pop": 265, "state": "SD", "_id": "57766"} -{"city": "OWANKA", "loc": [-102.562776, 44.063078], "pop": 69, "state": "SD", "_id": "57767"} -{"city": "PIEDMONT", "loc": [-103.368818, 44.228744], "pop": 1337, "state": "SD", "_id": "57769"} -{"city": "PINE RIDGE", "loc": [-102.598352, 43.112401], "pop": 5720, "state": "SD", "_id": "57770"} -{"city": "PORCUPINE", "loc": [-102.223448, 43.293979], "pop": 470, "state": "SD", "_id": "57772"} -{"city": "PROVO", "loc": [-103.866588, 43.173599], "pop": 32, "state": "SD", "_id": "57774"} -{"city": "COTTONWOOD", "loc": [-102.069706, 44.07721], "pop": 89, "state": "SD", "_id": "57775"} -{"city": "REDOWL", "loc": [-102.65923, 44.720224], "pop": 16, "state": "SD", "_id": "57777"} -{"city": "ROCHFORD", "loc": [-103.657621, 44.072318], "pop": 251, "state": "SD", "_id": "57778"} -{"city": "SAINT ONGE", "loc": [-103.734426, 44.552225], "pop": 317, "state": "SD", "_id": "57779"} -{"city": "SCENIC", "loc": [-102.535309, 43.799368], "pop": 128, "state": "SD", "_id": "57780"} -{"city": "SMITHWICK", "loc": [-103.198826, 43.26837], "pop": 15, "state": "SD", "_id": "57782"} -{"city": "SPEARFISH", "loc": [-103.864962, 44.494625], "pop": 11088, "state": "SD", "_id": "57783"} -{"city": "HEREFORD", "loc": [-103.477158, 44.413077], "pop": 8209, "state": "SD", "_id": "57785"} -{"city": "STONEVILLE", "loc": [-102.79796, 44.638004], "pop": 204, "state": "SD", "_id": "57787"} -{"city": "VALE", "loc": [-103.379499, 44.622235], "pop": 303, "state": "SD", "_id": "57788"} -{"city": "WALL", "loc": [-102.224546, 43.981232], "pop": 1070, "state": "SD", "_id": "57790"} -{"city": "WASTA", "loc": [-102.347077, 44.093707], "pop": 217, "state": "SD", "_id": "57791"} -{"city": "WHITE OWL", "loc": [-102.445195, 44.618247], "pop": 92, "state": "SD", "_id": "57792"} -{"city": "WHITEWOOD", "loc": [-103.637039, 44.458855], "pop": 1505, "state": "SD", "_id": "57793"} -{"city": "WOUNDED KNEE", "loc": [-102.402157, 43.185194], "pop": 826, "state": "SD", "_id": "57794"} -{"city": "ZEONA", "loc": [-102.7793, 45.251481], "pop": 8, "state": "SD", "_id": "57795"} -{"city": "ADAMS", "loc": [-87.122626, 36.558174], "pop": 2660, "state": "TN", "_id": "37010"} -{"city": "ALEXANDRIA", "loc": [-86.037171, 36.071147], "pop": 993, "state": "TN", "_id": "37012"} -{"city": "ANTIOCH", "loc": [-86.659151, 36.059517], "pop": 36988, "state": "TN", "_id": "37013"} -{"city": "ARRINGTON", "loc": [-86.564515, 35.904876], "pop": 12171, "state": "TN", "_id": "37014"} -{"city": "ASHLAND CITY", "loc": [-87.044719, 36.273132], "pop": 10940, "state": "TN", "_id": "37015"} -{"city": "AUBURNTOWN", "loc": [-86.107532, 35.974392], "pop": 933, "state": "TN", "_id": "37016"} -{"city": "BEECHGROVE", "loc": [-86.204644, 35.644733], "pop": 650, "state": "TN", "_id": "37018"} -{"city": "BELFAST", "loc": [-86.709482, 35.406905], "pop": 425, "state": "TN", "_id": "37019"} -{"city": "BELL BUCKLE", "loc": [-86.394908, 35.638105], "pop": 2899, "state": "TN", "_id": "37020"} -{"city": "BETHPAGE", "loc": [-86.314572, 36.518575], "pop": 4132, "state": "TN", "_id": "37022"} -{"city": "BIG ROCK", "loc": [-87.737838, 36.571606], "pop": 258, "state": "TN", "_id": "37023"} -{"city": "BON AQUA", "loc": [-87.299564, 35.947073], "pop": 3984, "state": "TN", "_id": "37025"} -{"city": "BRADYVILLE", "loc": [-86.09119, 35.705278], "pop": 1570, "state": "TN", "_id": "37026"} -{"city": "BRENTWOOD", "loc": [-86.790947, 36.006272], "pop": 24280, "state": "TN", "_id": "37027"} -{"city": "BUMPUS MILLS", "loc": [-87.861434, 36.622618], "pop": 411, "state": "TN", "_id": "37028"} -{"city": "BURNS", "loc": [-87.306061, 36.047066], "pop": 4449, "state": "TN", "_id": "37029"} -{"city": "DEFEATED", "loc": [-85.96969, 36.255617], "pop": 5318, "state": "TN", "_id": "37030"} -{"city": "CASTALIAN SPRING", "loc": [-86.315545, 36.382108], "pop": 1544, "state": "TN", "_id": "37031"} -{"city": "CEDAR HILL", "loc": [-87.027523, 36.506163], "pop": 2527, "state": "TN", "_id": "37032"} -{"city": "CENTERVILLE", "loc": [-87.477473, 35.779685], "pop": 5955, "state": "TN", "_id": "37033"} -{"city": "CHAPEL HILL", "loc": [-86.683584, 35.63542], "pop": 2912, "state": "TN", "_id": "37034"} -{"city": "CHAPMANSBORO", "loc": [-87.111288, 36.378078], "pop": 3248, "state": "TN", "_id": "37035"} -{"city": "CHARLOTTE", "loc": [-87.281554, 36.232612], "pop": 4205, "state": "TN", "_id": "37036"} -{"city": "CHRISTIANA", "loc": [-86.407932, 35.740877], "pop": 3173, "state": "TN", "_id": "37037"} -{"city": "CLARKSVILLE", "loc": [-87.348997, 36.522014], "pop": 24920, "state": "TN", "_id": "37040"} -{"city": "CLARKSVILLE", "loc": [-87.418621, 36.585315], "pop": 43296, "state": "TN", "_id": "37042"} -{"city": "CLARKSVILLE", "loc": [-87.275653, 36.5107], "pop": 23166, "state": "TN", "_id": "37043"} -{"city": "COLLEGE GROVE", "loc": [-86.749516, 35.783166], "pop": 4321, "state": "TN", "_id": "37046"} -{"city": "CORNERSVILLE", "loc": [-86.82862, 35.34088], "pop": 1631, "state": "TN", "_id": "37047"} -{"city": "COTTONTOWN", "loc": [-86.603338, 36.491231], "pop": 4724, "state": "TN", "_id": "37048"} -{"city": "CROSS PLAINS", "loc": [-86.676088, 36.553068], "pop": 2269, "state": "TN", "_id": "37049"} -{"city": "CUMBERLAND CITY", "loc": [-87.63494, 36.366924], "pop": 1208, "state": "TN", "_id": "37050"} -{"city": "CUMBERLAND FURNA", "loc": [-87.406585, 36.316146], "pop": 2288, "state": "TN", "_id": "37051"} -{"city": "CUNNINGHAM", "loc": [-87.424546, 36.378926], "pop": 2163, "state": "TN", "_id": "37052"} -{"city": "DICKSON", "loc": [-87.399532, 36.076014], "pop": 17724, "state": "TN", "_id": "37055"} -{"city": "DIXON SPRINGS", "loc": [-86.05335, 36.445538], "pop": 1269, "state": "TN", "_id": "37057"} -{"city": "DOVER", "loc": [-87.838333, 36.507521], "pop": 5972, "state": "TN", "_id": "37058"} -{"city": "DOWELLTOWN", "loc": [-85.905533, 35.972485], "pop": 1416, "state": "TN", "_id": "37059"} -{"city": "EAGLEVILLE", "loc": [-86.632652, 35.749243], "pop": 1562, "state": "TN", "_id": "37060"} -{"city": "ERIN", "loc": [-87.678964, 36.306684], "pop": 4804, "state": "TN", "_id": "37061"} -{"city": "FAIRVIEW", "loc": [-87.132065, 35.975528], "pop": 7203, "state": "TN", "_id": "37062"} -{"city": "FRANKLIN", "loc": [-86.878833, 35.932782], "pop": 40509, "state": "TN", "_id": "37064"} -{"city": "GALLATIN", "loc": [-86.45116, 36.383438], "pop": 27321, "state": "TN", "_id": "37066"} -{"city": "GOODLETTSVILLE", "loc": [-86.721215, 36.341677], "pop": 20825, "state": "TN", "_id": "37072"} -{"city": "GREENBRIER", "loc": [-86.791356, 36.422914], "pop": 8597, "state": "TN", "_id": "37073"} -{"city": "HARTSVILLE", "loc": [-86.170408, 36.394728], "pop": 5511, "state": "TN", "_id": "37074"} -{"city": "HENDERSONVILLE", "loc": [-86.607157, 36.305425], "pop": 38730, "state": "TN", "_id": "37075"} -{"city": "HERMITAGE", "loc": [-86.600162, 36.184814], "pop": 23765, "state": "TN", "_id": "37076"} -{"city": "HURRICANE MILLS", "loc": [-87.767129, 35.974859], "pop": 2200, "state": "TN", "_id": "37078"} -{"city": "INDIAN MOUND", "loc": [-87.680368, 36.494593], "pop": 1615, "state": "TN", "_id": "37079"} -{"city": "JOELTON", "loc": [-86.916306, 36.328974], "pop": 6749, "state": "TN", "_id": "37080"} -{"city": "KINGSTON SPRINGS", "loc": [-87.115646, 36.095324], "pop": 4037, "state": "TN", "_id": "37082"} -{"city": "LAFAYETTE", "loc": [-86.024217, 36.538955], "pop": 8686, "state": "TN", "_id": "37083"} -{"city": "LASCASSAS", "loc": [-86.311192, 35.949535], "pop": 2463, "state": "TN", "_id": "37085"} -{"city": "LA VERGNE", "loc": [-86.559969, 36.012714], "pop": 6115, "state": "TN", "_id": "37086"} -{"city": "LEBANON", "loc": [-86.302367, 36.209792], "pop": 33656, "state": "TN", "_id": "37087"} -{"city": "LEWISBURG", "loc": [-86.781204, 35.459615], "pop": 16217, "state": "TN", "_id": "37091"} -{"city": "GASSAWAY", "loc": [-85.985448, 35.974593], "pop": 2236, "state": "TN", "_id": "37095"} -{"city": "FLATWOODS", "loc": [-87.865582, 35.599784], "pop": 4172, "state": "TN", "_id": "37096"} -{"city": "LOBELVILLE", "loc": [-87.825106, 35.746659], "pop": 2357, "state": "TN", "_id": "37097"} -{"city": "WRIGLEY", "loc": [-87.317199, 35.892216], "pop": 3257, "state": "TN", "_id": "37098"} -{"city": "MC EWEN", "loc": [-87.64221, 36.118477], "pop": 3963, "state": "TN", "_id": "37101"} -{"city": "PLAZA", "loc": [-85.791609, 35.697358], "pop": 25113, "state": "TN", "_id": "37110"} -{"city": "MADISON", "loc": [-86.704557, 36.260386], "pop": 31511, "state": "TN", "_id": "37115"} -{"city": "MILTON", "loc": [-86.182377, 35.922085], "pop": 894, "state": "TN", "_id": "37118"} -{"city": "MOUNT JULIET", "loc": [-86.502344, 36.189684], "pop": 24498, "state": "TN", "_id": "37122"} -{"city": "MURFREESBORO", "loc": [-86.41809, 35.871019], "pop": 26477, "state": "TN", "_id": "37129"} -{"city": "MURFREESBORO", "loc": [-86.364675, 35.847792], "pop": 35559, "state": "TN", "_id": "37130"} -{"city": "NEW JOHNSONVILLE", "loc": [-87.954693, 36.008775], "pop": 2812, "state": "TN", "_id": "37134"} -{"city": "NOLENSVILLE", "loc": [-86.682868, 35.930723], "pop": 3813, "state": "TN", "_id": "37135"} -{"city": "NUNNELLY", "loc": [-87.506661, 35.87627], "pop": 2176, "state": "TN", "_id": "37137"} -{"city": "OLD HICKORY", "loc": [-86.611704, 36.241564], "pop": 17329, "state": "TN", "_id": "37138"} -{"city": "ONLY", "loc": [-87.665451, 35.867888], "pop": 199, "state": "TN", "_id": "37140"} -{"city": "ORLINDA", "loc": [-86.699002, 36.611045], "pop": 614, "state": "TN", "_id": "37141"} -{"city": "PALMYRA", "loc": [-87.491359, 36.417568], "pop": 1118, "state": "TN", "_id": "37142"} -{"city": "PEGRAM", "loc": [-87.031613, 36.11291], "pop": 2742, "state": "TN", "_id": "37143"} -{"city": "PETERSBURG", "loc": [-86.644728, 35.292416], "pop": 3214, "state": "TN", "_id": "37144"} -{"city": "PLEASANT SHADE", "loc": [-85.908057, 36.348924], "pop": 1901, "state": "TN", "_id": "37145"} -{"city": "PLEASANT VIEW", "loc": [-87.03948, 36.378281], "pop": 2424, "state": "TN", "_id": "37146"} -{"city": "PLEASANTVILLE", "loc": [-87.653664, 35.668373], "pop": 534, "state": "TN", "_id": "37147"} -{"city": "PORTLAND", "loc": [-86.505923, 36.567306], "pop": 12790, "state": "TN", "_id": "37148"} -{"city": "READYVILLE", "loc": [-86.241483, 35.798878], "pop": 4572, "state": "TN", "_id": "37149"} -{"city": "RED BOILING SPRI", "loc": [-85.846333, 36.531283], "pop": 4862, "state": "TN", "_id": "37150"} -{"city": "RIDDLETON", "loc": [-86.033482, 36.350581], "pop": 572, "state": "TN", "_id": "37151"} -{"city": "ROCKVALE", "loc": [-86.535196, 35.74503], "pop": 1776, "state": "TN", "_id": "37153"} -{"city": "ROYAL", "loc": [-86.463425, 35.488313], "pop": 22781, "state": "TN", "_id": "37160"} -{"city": "SMITHVILLE", "loc": [-85.804562, 35.929907], "pop": 10587, "state": "TN", "_id": "37166"} -{"city": "SMYRNA", "loc": [-86.504818, 35.965611], "pop": 23049, "state": "TN", "_id": "37167"} -{"city": "SOUTHSIDE", "loc": [-87.306087, 36.362588], "pop": 951, "state": "TN", "_id": "37171"} -{"city": "SPRINGFIELD", "loc": [-86.876901, 36.50182], "pop": 21214, "state": "TN", "_id": "37172"} -{"city": "SPRING HILL", "loc": [-86.904807, 35.717294], "pop": 2891, "state": "TN", "_id": "37174"} -{"city": "STEWART", "loc": [-87.872495, 36.324055], "pop": 897, "state": "TN", "_id": "37175"} -{"city": "TENNESSEE RIDGE", "loc": [-87.780761, 36.329712], "pop": 1558, "state": "TN", "_id": "37178"} -{"city": "THOMPSONS STATIO", "loc": [-87.004875, 35.831006], "pop": 3945, "state": "TN", "_id": "37179"} -{"city": "UNIONVILLE", "loc": [-86.563852, 35.622416], "pop": 1772, "state": "TN", "_id": "37180"} -{"city": "VANLEER", "loc": [-87.45653, 36.223738], "pop": 1531, "state": "TN", "_id": "37181"} -{"city": "WARTRACE", "loc": [-86.327736, 35.512299], "pop": 2119, "state": "TN", "_id": "37183"} -{"city": "WATERTOWN", "loc": [-86.143411, 36.095268], "pop": 3640, "state": "TN", "_id": "37184"} -{"city": "WAVERLY", "loc": [-87.799108, 36.099664], "pop": 6820, "state": "TN", "_id": "37185"} -{"city": "WESTMORELAND", "loc": [-86.235296, 36.575554], "pop": 6695, "state": "TN", "_id": "37186"} -{"city": "WHITE BLUFF", "loc": [-87.21901, 36.12531], "pop": 5230, "state": "TN", "_id": "37187"} -{"city": "WHITE HOUSE", "loc": [-86.670524, 36.460048], "pop": 6240, "state": "TN", "_id": "37188"} -{"city": "WHITES CREEK", "loc": [-86.82922, 36.274377], "pop": 4189, "state": "TN", "_id": "37189"} -{"city": "WOODBURY", "loc": [-86.050044, 35.814254], "pop": 6141, "state": "TN", "_id": "37190"} -{"city": "WOODLAWN", "loc": [-87.539331, 36.514695], "pop": 2568, "state": "TN", "_id": "37191"} -{"city": "NASHVILLE", "loc": [-86.778441, 36.167028], "pop": 1579, "state": "TN", "_id": "37201"} -{"city": "NASHVILLE", "loc": [-86.793922, 36.146802], "pop": 13524, "state": "TN", "_id": "37203"} -{"city": "MELROSE", "loc": [-86.781808, 36.114628], "pop": 12448, "state": "TN", "_id": "37204"} -{"city": "NASHVILLE", "loc": [-86.868954, 36.111432], "pop": 23323, "state": "TN", "_id": "37205"} -{"city": "NASHVILLE", "loc": [-86.741106, 36.179813], "pop": 28446, "state": "TN", "_id": "37206"} -{"city": "NASHVILLE", "loc": [-86.774008, 36.2195], "pop": 35260, "state": "TN", "_id": "37207"} -{"city": "NASHVILLE", "loc": [-86.807563, 36.176196], "pop": 15535, "state": "TN", "_id": "37208"} -{"city": "NASHVILLE", "loc": [-86.860212, 36.154592], "pop": 33560, "state": "TN", "_id": "37209"} -{"city": "NASHVILLE", "loc": [-86.741042, 36.137904], "pop": 17173, "state": "TN", "_id": "37210"} -{"city": "NASHVILLE", "loc": [-86.724038, 36.072486], "pop": 51478, "state": "TN", "_id": "37211"} -{"city": "NASHVILLE", "loc": [-86.800555, 36.133681], "pop": 16492, "state": "TN", "_id": "37212"} -{"city": "NASHVILLE", "loc": [-86.760556, 36.165512], "pop": 356, "state": "TN", "_id": "37213"} -{"city": "NASHVILLE", "loc": [-86.660854, 36.163339], "pop": 24935, "state": "TN", "_id": "37214"} -{"city": "NASHVILLE", "loc": [-86.821917, 36.098584], "pop": 20452, "state": "TN", "_id": "37215"} -{"city": "NASHVILLE", "loc": [-86.725687, 36.212491], "pop": 20045, "state": "TN", "_id": "37216"} -{"city": "NASHVILLE", "loc": [-86.666585, 36.10585], "pop": 26242, "state": "TN", "_id": "37217"} -{"city": "NASHVILLE", "loc": [-86.845583, 36.207062], "pop": 14312, "state": "TN", "_id": "37218"} -{"city": "NASHVILLE", "loc": [-86.783676, 36.167768], "pop": 268, "state": "TN", "_id": "37219"} -{"city": "NASHVILLE", "loc": [-86.769654, 36.064139], "pop": 6230, "state": "TN", "_id": "37220"} -{"city": "BELLEVUE", "loc": [-86.943674, 36.071512], "pop": 21880, "state": "TN", "_id": "37221"} -{"city": "NASHVILLE", "loc": [-86.805264, 36.190145], "pop": 612, "state": "TN", "_id": "37228"} -{"city": "ALTAMONT", "loc": [-85.763508, 35.425776], "pop": 988, "state": "TN", "_id": "37301"} -{"city": "APISON", "loc": [-85.016404, 35.014926], "pop": 1614, "state": "TN", "_id": "37302"} -{"city": "ATHENS", "loc": [-84.604261, 35.457389], "pop": 21571, "state": "TN", "_id": "37303"} -{"city": "BEERSHEBA SPRING", "loc": [-85.682128, 35.470371], "pop": 1067, "state": "TN", "_id": "37305"} -{"city": "BELVIDERE", "loc": [-86.172827, 35.141499], "pop": 4670, "state": "TN", "_id": "37306"} -{"city": "BENTON", "loc": [-84.654433, 35.172953], "pop": 3164, "state": "TN", "_id": "37307"} -{"city": "BIRCHWOOD", "loc": [-84.961835, 35.351968], "pop": 2732, "state": "TN", "_id": "37308"} -{"city": "CALHOUN", "loc": [-84.738105, 35.374614], "pop": 3597, "state": "TN", "_id": "37309"} -{"city": "CHARLESTON", "loc": [-84.766639, 35.255644], "pop": 1921, "state": "TN", "_id": "37310"} -{"city": "CLEVELAND", "loc": [-84.875006, 35.131257], "pop": 40633, "state": "TN", "_id": "37311"} -{"city": "CLEVELAND", "loc": [-84.847557, 35.202309], "pop": 28081, "state": "TN", "_id": "37312"} -{"city": "COALMONT", "loc": [-85.655097, 35.386197], "pop": 786, "state": "TN", "_id": "37313"} -{"city": "POSTELLE", "loc": [-84.383964, 35.024987], "pop": 3155, "state": "TN", "_id": "37317"} -{"city": "COWAN", "loc": [-86.019007, 35.170121], "pop": 2713, "state": "TN", "_id": "37318"} -{"city": "DAYTON", "loc": [-85.013455, 35.500186], "pop": 15965, "state": "TN", "_id": "37321"} -{"city": "DECATUR", "loc": [-84.808051, 35.507166], "pop": 3718, "state": "TN", "_id": "37322"} -{"city": "DECHERD", "loc": [-86.058859, 35.232598], "pop": 6576, "state": "TN", "_id": "37324"} -{"city": "DELANO", "loc": [-84.602366, 35.261234], "pop": 1830, "state": "TN", "_id": "37325"} -{"city": "DUNLAP", "loc": [-85.392513, 35.384175], "pop": 6883, "state": "TN", "_id": "37327"} -{"city": "ELORA", "loc": [-86.34811, 35.029496], "pop": 1257, "state": "TN", "_id": "37328"} -{"city": "ENGLEWOOD", "loc": [-84.483265, 35.427245], "pop": 3419, "state": "TN", "_id": "37329"} -{"city": "ESTILL SPRINGS", "loc": [-86.139561, 35.270508], "pop": 5778, "state": "TN", "_id": "37330"} -{"city": "ETOWAH", "loc": [-84.528305, 35.331443], "pop": 8246, "state": "TN", "_id": "37331"} -{"city": "EVENSVILLE", "loc": [-85.022773, 35.615346], "pop": 99, "state": "TN", "_id": "37332"} -{"city": "FARNER", "loc": [-84.320904, 35.144894], "pop": 672, "state": "TN", "_id": "37333"} -{"city": "FAYETTEVILLE", "loc": [-86.566448, 35.152678], "pop": 11009, "state": "TN", "_id": "37334"} -{"city": "FLINTVILLE", "loc": [-86.497974, 35.042782], "pop": 6020, "state": "TN", "_id": "37335"} -{"city": "GEORGETOWN", "loc": [-84.912684, 35.293241], "pop": 3768, "state": "TN", "_id": "37336"} -{"city": "GRANDVIEW", "loc": [-84.861464, 35.760843], "pop": 626, "state": "TN", "_id": "37337"} -{"city": "GRAYSVILLE", "loc": [-85.17904, 35.44842], "pop": 1067, "state": "TN", "_id": "37338"} -{"city": "GRUETLI LAAGER", "loc": [-85.669773, 35.363464], "pop": 1929, "state": "TN", "_id": "37339"} -{"city": "GUILD", "loc": [-85.511568, 35.017834], "pop": 1183, "state": "TN", "_id": "37340"} -{"city": "HARRISON", "loc": [-85.094532, 35.167898], "pop": 10247, "state": "TN", "_id": "37341"} -{"city": "HILLSBORO", "loc": [-85.972422, 35.369933], "pop": 2075, "state": "TN", "_id": "37342"} -{"city": "HIXSON", "loc": [-85.218215, 35.159112], "pop": 35100, "state": "TN", "_id": "37343"} -{"city": "HUNTLAND", "loc": [-86.269435, 35.051198], "pop": 2016, "state": "TN", "_id": "37345"} -{"city": "KIMBALL", "loc": [-85.614737, 35.066603], "pop": 7076, "state": "TN", "_id": "37347"} -{"city": "KELSO", "loc": [-86.468343, 35.102356], "pop": 759, "state": "TN", "_id": "37348"} -{"city": "LOOKOUT MOUNTAIN", "loc": [-85.350564, 34.994825], "pop": 1899, "state": "TN", "_id": "37350"} -{"city": "LYNCHBURG", "loc": [-86.372019, 35.270642], "pop": 2888, "state": "TN", "_id": "37352"} -{"city": "MC DONALD", "loc": [-84.989198, 35.086902], "pop": 819, "state": "TN", "_id": "37353"} -{"city": "HIWASSEE COLLEGE", "loc": [-84.351899, 35.500917], "pop": 10509, "state": "TN", "_id": "37354"} -{"city": "MANCHESTER", "loc": [-86.081568, 35.495846], "pop": 19173, "state": "TN", "_id": "37355"} -{"city": "MONTEAGLE", "loc": [-85.822795, 35.240172], "pop": 2206, "state": "TN", "_id": "37356"} -{"city": "MORRISON", "loc": [-85.889895, 35.60002], "pop": 5654, "state": "TN", "_id": "37357"} -{"city": "MULBERRY", "loc": [-86.421685, 35.19413], "pop": 495, "state": "TN", "_id": "37359"} -{"city": "NORMANDY", "loc": [-86.255712, 35.429628], "pop": 1308, "state": "TN", "_id": "37360"} -{"city": "OCOEE", "loc": [-84.713565, 35.102473], "pop": 1206, "state": "TN", "_id": "37361"} -{"city": "OLDFORT", "loc": [-84.721877, 35.03654], "pop": 1522, "state": "TN", "_id": "37362"} -{"city": "OOLTEWAH", "loc": [-85.063495, 35.078104], "pop": 17419, "state": "TN", "_id": "37363"} -{"city": "PALMER", "loc": [-85.564272, 35.374062], "pop": 1685, "state": "TN", "_id": "37365"} -{"city": "PELHAM", "loc": [-85.84408, 35.314045], "pop": 671, "state": "TN", "_id": "37366"} -{"city": "PIKEVILLE", "loc": [-85.207653, 35.640769], "pop": 7869, "state": "TN", "_id": "37367"} -{"city": "RELIANCE", "loc": [-84.54103, 35.180664], "pop": 1777, "state": "TN", "_id": "37369"} -{"city": "RICEVILLE", "loc": [-84.646247, 35.344615], "pop": 2785, "state": "TN", "_id": "37370"} -{"city": "SALE CREEK", "loc": [-85.102323, 35.385806], "pop": 2464, "state": "TN", "_id": "37373"} -{"city": "SEQUATCHIE", "loc": [-85.637084, 35.163396], "pop": 1463, "state": "TN", "_id": "37374"} -{"city": "SEWANEE", "loc": [-85.91259, 35.201101], "pop": 2785, "state": "TN", "_id": "37375"} -{"city": "SHERWOOD", "loc": [-85.923841, 35.099164], "pop": 885, "state": "TN", "_id": "37376"} -{"city": "SIGNAL MOUNTAIN", "loc": [-85.336243, 35.149424], "pop": 14032, "state": "TN", "_id": "37377"} -{"city": "SODDY DAISY", "loc": [-85.163009, 35.252686], "pop": 19646, "state": "TN", "_id": "37379"} -{"city": "SOUTH PITTSBURG", "loc": [-85.722498, 35.028046], "pop": 6151, "state": "TN", "_id": "37380"} -{"city": "SPRING CITY", "loc": [-84.841968, 35.682072], "pop": 7787, "state": "TN", "_id": "37381"} -{"city": "TELLICO PLAINS", "loc": [-84.306785, 35.356237], "pop": 6459, "state": "TN", "_id": "37385"} -{"city": "TRACY CITY", "loc": [-85.736187, 35.272081], "pop": 3980, "state": "TN", "_id": "37387"} -{"city": "DICKEL", "loc": [-86.22069, 35.35841], "pop": 22193, "state": "TN", "_id": "37388"} -{"city": "TURTLETOWN", "loc": [-84.354381, 35.108089], "pop": 910, "state": "TN", "_id": "37391"} -{"city": "WHITESIDE", "loc": [-85.398499, 35.066289], "pop": 192, "state": "TN", "_id": "37396"} -{"city": "WHITWELL", "loc": [-85.501112, 35.197228], "pop": 9051, "state": "TN", "_id": "37397"} -{"city": "WINCHESTER", "loc": [-86.113038, 35.186398], "pop": 5753, "state": "TN", "_id": "37398"} -{"city": "CHATTANOOGA", "loc": [-85.316126, 35.046288], "pop": 3455, "state": "TN", "_id": "37402"} -{"city": "CHATTANOOGA", "loc": [-85.296516, 35.045045], "pop": 3700, "state": "TN", "_id": "37403"} -{"city": "CHATTANOOGA", "loc": [-85.272229, 35.030634], "pop": 15345, "state": "TN", "_id": "37404"} -{"city": "CHATTANOOGA", "loc": [-85.308224, 35.076801], "pop": 12005, "state": "TN", "_id": "37405"} -{"city": "CHATTANOOGA", "loc": [-85.247839, 35.061446], "pop": 15684, "state": "TN", "_id": "37406"} -{"city": "CHATTANOOGA", "loc": [-85.284913, 35.002361], "pop": 8138, "state": "TN", "_id": "37407"} -{"city": "CHATTANOOGA", "loc": [-85.306809, 35.029236], "pop": 2131, "state": "TN", "_id": "37408"} -{"city": "CHATTANOOGA", "loc": [-85.331016, 34.99809], "pop": 2848, "state": "TN", "_id": "37409"} -{"city": "CHATTANOOGA", "loc": [-85.313762, 35.001787], "pop": 6068, "state": "TN", "_id": "37410"} -{"city": "CHATTANOOGA", "loc": [-85.235583, 35.02706], "pop": 18421, "state": "TN", "_id": "37411"} -{"city": "EAST RIDGE", "loc": [-85.237957, 34.996726], "pop": 20711, "state": "TN", "_id": "37412"} -{"city": "RED BANK", "loc": [-85.28633, 35.117668], "pop": 21549, "state": "TN", "_id": "37415"} -{"city": "CHATTANOOGA", "loc": [-85.175656, 35.094246], "pop": 14197, "state": "TN", "_id": "37416"} -{"city": "CHATTANOOGA", "loc": [-85.368698, 35.033092], "pop": 5253, "state": "TN", "_id": "37419"} -{"city": "CHATTANOOGA", "loc": [-85.14594, 35.024986], "pop": 32802, "state": "TN", "_id": "37421"} -{"city": "JOHNSON CITY", "loc": [-82.340775, 36.333872], "pop": 27978, "state": "TN", "_id": "37601"} -{"city": "JOHNSON CITY", "loc": [-82.381042, 36.310744], "pop": 31353, "state": "TN", "_id": "37604"} -{"city": "GRAY", "loc": [-82.447128, 36.41006], "pop": 12119, "state": "TN", "_id": "37615"} -{"city": "AFTON", "loc": [-82.746667, 36.204166], "pop": 3469, "state": "TN", "_id": "37616"} -{"city": "BLOUNTVILLE", "loc": [-82.365558, 36.53562], "pop": 14517, "state": "TN", "_id": "37617"} -{"city": "BLUFF CITY", "loc": [-82.236181, 36.477391], "pop": 11146, "state": "TN", "_id": "37618"} -{"city": "BRISTOL", "loc": [-82.181864, 36.568643], "pop": 36852, "state": "TN", "_id": "37620"} -{"city": "BUTLER", "loc": [-81.985614, 36.328158], "pop": 3479, "state": "TN", "_id": "37640"} -{"city": "CHUCKEY", "loc": [-82.667448, 36.221142], "pop": 6362, "state": "TN", "_id": "37641"} -{"city": "CHURCH HILL", "loc": [-82.725184, 36.539926], "pop": 9106, "state": "TN", "_id": "37642"} -{"city": "ELIZABETHTON", "loc": [-82.201481, 36.344548], "pop": 32046, "state": "TN", "_id": "37643"} -{"city": "MOUNT CARMEL", "loc": [-82.653217, 36.562913], "pop": 6249, "state": "TN", "_id": "37645"} -{"city": "ERWIN", "loc": [-82.41631, 36.134193], "pop": 11635, "state": "TN", "_id": "37650"} -{"city": "FALL BRANCH", "loc": [-82.625605, 36.415839], "pop": 1964, "state": "TN", "_id": "37656"} -{"city": "FLAG POND", "loc": [-82.562301, 36.008507], "pop": 1066, "state": "TN", "_id": "37657"} -{"city": "HAMPTON", "loc": [-82.189144, 36.257743], "pop": 4828, "state": "TN", "_id": "37658"} -{"city": "JONESBOROUGH", "loc": [-82.490225, 36.295426], "pop": 18736, "state": "TN", "_id": "37659"} -{"city": "BLOOMINGDALE", "loc": [-82.554034, 36.552766], "pop": 37726, "state": "TN", "_id": "37660"} -{"city": "COLONIAL HEIGHTS", "loc": [-82.4948, 36.4693], "pop": 12097, "state": "TN", "_id": "37663"} -{"city": "KINGSPORT", "loc": [-82.516835, 36.520834], "pop": 22289, "state": "TN", "_id": "37664"} -{"city": "LYNN GARDEN", "loc": [-82.569906, 36.578305], "pop": 6024, "state": "TN", "_id": "37665"} -{"city": "LAUREL BLOOMERY", "loc": [-81.725537, 36.574946], "pop": 670, "state": "TN", "_id": "37680"} -{"city": "WASHINGTON COLLE", "loc": [-82.61709, 36.236968], "pop": 4354, "state": "TN", "_id": "37681"} -{"city": "MOUNTAIN CITY", "loc": [-81.813999, 36.465724], "pop": 9205, "state": "TN", "_id": "37683"} -{"city": "PINEY FLATS", "loc": [-82.333957, 36.446122], "pop": 5130, "state": "TN", "_id": "37686"} -{"city": "ROAN MOUNTAIN", "loc": [-82.081041, 36.177333], "pop": 4234, "state": "TN", "_id": "37687"} -{"city": "SHADY VALLEY", "loc": [-81.906797, 36.527218], "pop": 1053, "state": "TN", "_id": "37688"} -{"city": "TELFORD", "loc": [-82.536935, 36.245053], "pop": 2662, "state": "TN", "_id": "37690"} -{"city": "TRADE", "loc": [-81.757234, 36.368328], "pop": 763, "state": "TN", "_id": "37691"} -{"city": "UNICOI", "loc": [-82.321957, 36.206585], "pop": 3838, "state": "TN", "_id": "37692"} -{"city": "WATAUGA", "loc": [-82.268262, 36.370157], "pop": 2033, "state": "TN", "_id": "37694"} -{"city": "ALCOA", "loc": [-83.980895, 35.78522], "pop": 5891, "state": "TN", "_id": "37701"} -{"city": "ANDERSONVILLE", "loc": [-84.054399, 36.191514], "pop": 5761, "state": "TN", "_id": "37705"} -{"city": "BEAN STATION", "loc": [-83.314248, 36.32493], "pop": 5255, "state": "TN", "_id": "37708"} -{"city": "BLAINE", "loc": [-83.678272, 36.15832], "pop": 2556, "state": "TN", "_id": "37709"} -{"city": "DEVONIA", "loc": [-84.215968, 36.185426], "pop": 2225, "state": "TN", "_id": "37710"} -{"city": "BULLS GAP", "loc": [-83.03149, 36.325707], "pop": 5876, "state": "TN", "_id": "37711"} -{"city": "BYBEE", "loc": [-83.163071, 36.073957], "pop": 2911, "state": "TN", "_id": "37713"} -{"city": "CARYVILLE", "loc": [-84.189794, 36.285422], "pop": 3811, "state": "TN", "_id": "37714"} -{"city": "CLAIRFIELD", "loc": [-83.939693, 36.565026], "pop": 1239, "state": "TN", "_id": "37715"} -{"city": "CLINTON", "loc": [-84.189735, 36.08688], "pop": 21075, "state": "TN", "_id": "37716"} -{"city": "CORRYTON", "loc": [-83.813, 36.120011], "pop": 7557, "state": "TN", "_id": "37721"} -{"city": "COSBY", "loc": [-83.218743, 35.834693], "pop": 3800, "state": "TN", "_id": "37722"} -{"city": "CRAB ORCHARD", "loc": [-84.858577, 35.889763], "pop": 2307, "state": "TN", "_id": "37723"} -{"city": "CUMBERLAND GAP", "loc": [-83.681049, 36.550381], "pop": 3142, "state": "TN", "_id": "37724"} -{"city": "DANDRIDGE", "loc": [-83.404435, 36.001798], "pop": 8707, "state": "TN", "_id": "37725"} -{"city": "DEER LODGE", "loc": [-84.819074, 36.217584], "pop": 538, "state": "TN", "_id": "37726"} -{"city": "DEL RIO", "loc": [-83.015008, 35.882967], "pop": 1616, "state": "TN", "_id": "37727"} -{"city": "DUFF", "loc": [-84.01247, 36.411035], "pop": 4265, "state": "TN", "_id": "37729"} -{"city": "EIDSON", "loc": [-83.08272, 36.499522], "pop": 901, "state": "TN", "_id": "37731"} -{"city": "FRIENDSVILLE", "loc": [-84.106069, 35.752302], "pop": 4186, "state": "TN", "_id": "37737"} -{"city": "GATLINBURG", "loc": [-83.487406, 35.728976], "pop": 4352, "state": "TN", "_id": "37738"} -{"city": "GREENBACK", "loc": [-84.170959, 35.656738], "pop": 3272, "state": "TN", "_id": "37742"} -{"city": "BAILEYTON", "loc": [-82.833136, 36.160807], "pop": 39225, "state": "TN", "_id": "37743"} -{"city": "HARRIMAN", "loc": [-84.515526, 35.934785], "pop": 18558, "state": "TN", "_id": "37748"} -{"city": "HARROGATE", "loc": [-83.607262, 36.576718], "pop": 4683, "state": "TN", "_id": "37752"} -{"city": "HARTFORD", "loc": [-83.099642, 35.825597], "pop": 733, "state": "TN", "_id": "37753"} -{"city": "HEISKELL", "loc": [-84.043826, 36.115027], "pop": 2945, "state": "TN", "_id": "37754"} -{"city": "HELENWOOD", "loc": [-84.538088, 36.43294], "pop": 2583, "state": "TN", "_id": "37755"} -{"city": "HUNTSVILLE", "loc": [-84.428832, 36.398329], "pop": 4760, "state": "TN", "_id": "37756"} -{"city": "JACKSBORO", "loc": [-84.192835, 36.326615], "pop": 4967, "state": "TN", "_id": "37757"} -{"city": "JEFFERSON CITY", "loc": [-83.480982, 36.11633], "pop": 9438, "state": "TN", "_id": "37760"} -{"city": "JELLICO", "loc": [-84.118067, 36.554407], "pop": 6317, "state": "TN", "_id": "37762"} -{"city": "KINGSTON", "loc": [-84.497054, 35.852807], "pop": 11002, "state": "TN", "_id": "37763"} -{"city": "KODAK", "loc": [-83.617111, 35.972215], "pop": 4104, "state": "TN", "_id": "37764"} -{"city": "KYLES FORD", "loc": [-83.050712, 36.572072], "pop": 931, "state": "TN", "_id": "37765"} -{"city": "MORLEY", "loc": [-84.111568, 36.368967], "pop": 14939, "state": "TN", "_id": "37766"} -{"city": "LAKE CITY", "loc": [-84.138567, 36.203223], "pop": 6099, "state": "TN", "_id": "37769"} -{"city": "LANCING", "loc": [-84.713519, 36.145644], "pop": 2345, "state": "TN", "_id": "37770"} -{"city": "LENOIR CITY", "loc": [-84.266501, 35.810391], "pop": 18523, "state": "TN", "_id": "37771"} -{"city": "LOUDON", "loc": [-84.34362, 35.729211], "pop": 11116, "state": "TN", "_id": "37774"} -{"city": "LOUISVILLE", "loc": [-84.008895, 35.837465], "pop": 9618, "state": "TN", "_id": "37777"} -{"city": "LOWLAND", "loc": [-83.244592, 36.164016], "pop": 166, "state": "TN", "_id": "37778"} -{"city": "LUTTRELL", "loc": [-83.759976, 36.200136], "pop": 3554, "state": "TN", "_id": "37779"} -{"city": "MARYVILLE", "loc": [-83.914541, 35.782449], "pop": 24011, "state": "TN", "_id": "37801"} -{"city": "MARYVILLE", "loc": [-84.004491, 35.715659], "pop": 31758, "state": "TN", "_id": "37804"} -{"city": "MASCOT", "loc": [-83.741101, 36.084421], "pop": 2781, "state": "TN", "_id": "37806"} -{"city": "MAYNARDVILLE", "loc": [-83.840261, 36.234274], "pop": 8998, "state": "TN", "_id": "37807"} -{"city": "MIDWAY", "loc": [-83.028141, 36.150995], "pop": 824, "state": "TN", "_id": "37809"} -{"city": "MOHAWK", "loc": [-83.090185, 36.186919], "pop": 1765, "state": "TN", "_id": "37810"} -{"city": "MOORESBURG", "loc": [-83.209499, 36.362082], "pop": 2628, "state": "TN", "_id": "37811"} -{"city": "MORRISTOWN", "loc": [-83.275519, 36.195672], "pop": 15333, "state": "TN", "_id": "37813"} -{"city": "MORRISTOWN", "loc": [-83.31185, 36.224782], "pop": 26285, "state": "TN", "_id": "37814"} -{"city": "MOSHEIM", "loc": [-82.967399, 36.183883], "pop": 4337, "state": "TN", "_id": "37818"} -{"city": "NEWCOMB", "loc": [-84.179848, 36.542768], "pop": 52, "state": "TN", "_id": "37819"} -{"city": "NEW MARKET", "loc": [-83.567253, 36.081037], "pop": 5751, "state": "TN", "_id": "37820"} -{"city": "NEWPORT", "loc": [-83.202749, 35.954431], "pop": 16780, "state": "TN", "_id": "37821"} -{"city": "NEW TAZEWELL", "loc": [-83.646949, 36.424475], "pop": 5602, "state": "TN", "_id": "37825"} -{"city": "NIOTA", "loc": [-84.57215, 35.581924], "pop": 2182, "state": "TN", "_id": "37826"} -{"city": "OAKDALE", "loc": [-84.575295, 36.009962], "pop": 3901, "state": "TN", "_id": "37829"} -{"city": "OAK RIDGE", "loc": [-84.262297, 36.01588], "pop": 27605, "state": "TN", "_id": "37830"} -{"city": "OLIVER SPRINGS", "loc": [-84.348632, 36.036616], "pop": 1324, "state": "TN", "_id": "37840"} -{"city": "ONEIDA", "loc": [-84.529319, 36.505259], "pop": 7010, "state": "TN", "_id": "37841"} -{"city": "PARROTTSVILLE", "loc": [-83.073627, 35.998297], "pop": 3301, "state": "TN", "_id": "37843"} -{"city": "PETROS", "loc": [-84.439914, 36.055903], "pop": 4330, "state": "TN", "_id": "37845"} -{"city": "PHILADELPHIA", "loc": [-84.450221, 35.664214], "pop": 1523, "state": "TN", "_id": "37846"} -{"city": "PIONEER", "loc": [-84.290672, 36.469465], "pop": 476, "state": "TN", "_id": "37847"} -{"city": "POWDER SPRINGS", "loc": [-83.672921, 36.254899], "pop": 381, "state": "TN", "_id": "37848"} -{"city": "POWELL", "loc": [-84.039987, 36.043454], "pop": 14646, "state": "TN", "_id": "37849"} -{"city": "ROBBINS", "loc": [-84.590417, 36.352693], "pop": 2639, "state": "TN", "_id": "37852"} -{"city": "ROCKFORD", "loc": [-83.941202, 35.830519], "pop": 3347, "state": "TN", "_id": "37853"} -{"city": "ROCKWOOD", "loc": [-84.684188, 35.858677], "pop": 10384, "state": "TN", "_id": "37854"} -{"city": "ROGERSVILLE", "loc": [-83.010786, 36.416196], "pop": 10374, "state": "TN", "_id": "37857"} -{"city": "RUSSELLVILLE", "loc": [-83.19138, 36.256684], "pop": 2329, "state": "TN", "_id": "37860"} -{"city": "RUTLEDGE", "loc": [-83.512468, 36.250078], "pop": 6019, "state": "TN", "_id": "37861"} -{"city": "SEVIERVILLE", "loc": [-83.538621, 35.845192], "pop": 30351, "state": "TN", "_id": "37862"} -{"city": "PIGEON FORGE", "loc": [-83.563821, 35.792222], "pop": 3063, "state": "TN", "_id": "37863"} -{"city": "SEYMOUR", "loc": [-83.749511, 35.869984], "pop": 8527, "state": "TN", "_id": "37865"} -{"city": "SHARPS CHAPEL", "loc": [-83.81268, 36.368498], "pop": 1405, "state": "TN", "_id": "37866"} -{"city": "SNEEDVILLE", "loc": [-83.252892, 36.527515], "pop": 5203, "state": "TN", "_id": "37869"} -{"city": "SPEEDWELL", "loc": [-83.813495, 36.479723], "pop": 2895, "state": "TN", "_id": "37870"} -{"city": "STRAWBERRY PLAIN", "loc": [-83.677724, 36.041462], "pop": 5600, "state": "TN", "_id": "37871"} -{"city": "SUNBRIGHT", "loc": [-84.69842, 36.262234], "pop": 2210, "state": "TN", "_id": "37872"} -{"city": "SURGOINSVILLE", "loc": [-82.830597, 36.474066], "pop": 7229, "state": "TN", "_id": "37873"} -{"city": "SWEETWATER", "loc": [-84.430424, 35.595703], "pop": 11415, "state": "TN", "_id": "37874"} -{"city": "TALBOTT", "loc": [-83.412864, 36.159958], "pop": 6279, "state": "TN", "_id": "37877"} -{"city": "TALLASSEE", "loc": [-84.003338, 35.583663], "pop": 376, "state": "TN", "_id": "37878"} -{"city": "TAZEWELL", "loc": [-83.555219, 36.471012], "pop": 8567, "state": "TN", "_id": "37879"} -{"city": "TEN MILE", "loc": [-84.654848, 35.686175], "pop": 4355, "state": "TN", "_id": "37880"} -{"city": "THORN HILL", "loc": [-83.365973, 36.391687], "pop": 1676, "state": "TN", "_id": "37881"} -{"city": "TOWNSEND", "loc": [-83.757157, 35.678385], "pop": 1951, "state": "TN", "_id": "37882"} -{"city": "TREADWAY", "loc": [-83.219138, 36.428202], "pop": 78, "state": "TN", "_id": "37883"} -{"city": "VONORE", "loc": [-84.177829, 35.535364], "pop": 1541, "state": "TN", "_id": "37885"} -{"city": "WALLAND", "loc": [-83.824307, 35.753311], "pop": 3972, "state": "TN", "_id": "37886"} -{"city": "WARTBURG", "loc": [-84.565188, 36.101609], "pop": 3976, "state": "TN", "_id": "37887"} -{"city": "WASHBURN", "loc": [-83.593685, 36.310649], "pop": 1737, "state": "TN", "_id": "37888"} -{"city": "BANEBERRY", "loc": [-83.287921, 36.090299], "pop": 4639, "state": "TN", "_id": "37890"} -{"city": "WHITESBURG", "loc": [-83.145844, 36.262125], "pop": 2265, "state": "TN", "_id": "37891"} -{"city": "WINFIELD", "loc": [-84.434026, 36.559825], "pop": 1375, "state": "TN", "_id": "37892"} -{"city": "KNOXVILLE", "loc": [-83.920915, 35.962516], "pop": 1457, "state": "TN", "_id": "37902"} -{"city": "KNOXVILLE", "loc": [-84.023501, 35.945978], "pop": 13265, "state": "TN", "_id": "37909"} -{"city": "KNOXVILLE", "loc": [-83.977317, 36.005492], "pop": 16919, "state": "TN", "_id": "37912"} -{"city": "KNOXVILLE", "loc": [-83.849624, 35.991755], "pop": 19867, "state": "TN", "_id": "37914"} -{"city": "KNOXVILLE", "loc": [-83.901005, 35.972074], "pop": 6546, "state": "TN", "_id": "37915"} -{"city": "KNOXVILLE", "loc": [-83.933576, 35.955584], "pop": 11449, "state": "TN", "_id": "37916"} -{"city": "KNOXVILLE", "loc": [-83.915216, 35.99803], "pop": 25362, "state": "TN", "_id": "37917"} -{"city": "KNOXVILLE", "loc": [-83.922558, 36.050054], "pop": 32073, "state": "TN", "_id": "37918"} -{"city": "KNOXVILLE", "loc": [-84.001468, 35.924385], "pop": 25653, "state": "TN", "_id": "37919"} -{"city": "KIMBERLIN HEIGHT", "loc": [-83.879793, 35.922976], "pop": 35762, "state": "TN", "_id": "37920"} -{"city": "KARNS", "loc": [-83.982894, 35.976297], "pop": 26904, "state": "TN", "_id": "37921"} -{"city": "CONCORD", "loc": [-84.127332, 35.877697], "pop": 31414, "state": "TN", "_id": "37922"} -{"city": "KNOXVILLE", "loc": [-84.076116, 35.933127], "pop": 22830, "state": "TN", "_id": "37923"} -{"city": "KNOXVILLE", "loc": [-83.80207, 36.032044], "pop": 8103, "state": "TN", "_id": "37924"} -{"city": "KNOXVILLE", "loc": [-84.120072, 35.992363], "pop": 17247, "state": "TN", "_id": "37931"} -{"city": "CONCORD FARRAGUT", "loc": [-84.169591, 35.923619], "pop": 7975, "state": "TN", "_id": "37932"} -{"city": "KNOXVILLE", "loc": [-83.945968, 36.105473], "pop": 10705, "state": "TN", "_id": "37938"} -{"city": "ALAMO", "loc": [-89.176465, 35.801697], "pop": 6528, "state": "TN", "_id": "38001"} -{"city": "ARLINGTON", "loc": [-89.729502, 35.275171], "pop": 6781, "state": "TN", "_id": "38002"} -{"city": "ATOKA", "loc": [-89.8216, 35.421337], "pop": 4848, "state": "TN", "_id": "38004"} -{"city": "BELLS", "loc": [-89.072857, 35.71976], "pop": 3092, "state": "TN", "_id": "38006"} -{"city": "BOLIVAR", "loc": [-89.000723, 35.246082], "pop": 10327, "state": "TN", "_id": "38008"} -{"city": "BRIGHTON", "loc": [-89.752477, 35.470328], "pop": 9668, "state": "TN", "_id": "38011"} -{"city": "BROWNSVILLE", "loc": [-89.262352, 35.609957], "pop": 16497, "state": "TN", "_id": "38012"} -{"city": "BURLISON", "loc": [-89.817729, 35.53995], "pop": 2314, "state": "TN", "_id": "38015"} -{"city": "COLLIERVILLE", "loc": [-89.676724, 35.055077], "pop": 16164, "state": "TN", "_id": "38017"} -{"city": "CORDOVA", "loc": [-89.778934, 35.156349], "pop": 21777, "state": "TN", "_id": "38018"} -{"city": "COVINGTON", "loc": [-89.650144, 35.559838], "pop": 13631, "state": "TN", "_id": "38019"} -{"city": "DRUMMONDS", "loc": [-89.923649, 35.445203], "pop": 4891, "state": "TN", "_id": "38023"} -{"city": "DYERSBURG", "loc": [-89.383644, 36.044447], "pop": 27839, "state": "TN", "_id": "38024"} -{"city": "EADS", "loc": [-89.676004, 35.15512], "pop": 1715, "state": "TN", "_id": "38028"} -{"city": "FINLEY", "loc": [-89.513403, 36.016938], "pop": 830, "state": "TN", "_id": "38030"} -{"city": "FRIENDSHIP", "loc": [-89.204136, 35.897642], "pop": 1626, "state": "TN", "_id": "38034"} -{"city": "GATES", "loc": [-89.459182, 35.843942], "pop": 2508, "state": "TN", "_id": "38037"} -{"city": "GRAND JUNCTION", "loc": [-89.153415, 35.065885], "pop": 1827, "state": "TN", "_id": "38039"} -{"city": "HALLS", "loc": [-89.414671, 35.886232], "pop": 3755, "state": "TN", "_id": "38040"} -{"city": "FORT PILLOW", "loc": [-89.637542, 35.682868], "pop": 4298, "state": "TN", "_id": "38041"} -{"city": "HICKORY VALLEY", "loc": [-89.130905, 35.157959], "pop": 269, "state": "TN", "_id": "38042"} -{"city": "HORNSBY", "loc": [-88.826315, 35.219706], "pop": 946, "state": "TN", "_id": "38044"} -{"city": "MASON", "loc": [-89.551805, 35.438012], "pop": 2275, "state": "TN", "_id": "38049"} -{"city": "MIDDLETON", "loc": [-88.904625, 35.0818], "pop": 3147, "state": "TN", "_id": "38052"} -{"city": "MILLINGTON", "loc": [-89.905422, 35.318463], "pop": 38216, "state": "TN", "_id": "38053"} -{"city": "MOSCOW", "loc": [-89.359508, 35.058796], "pop": 3436, "state": "TN", "_id": "38057"} -{"city": "NEWBERN", "loc": [-89.251364, 36.100875], "pop": 5426, "state": "TN", "_id": "38059"} -{"city": "OAKLAND", "loc": [-89.551764, 35.222929], "pop": 5935, "state": "TN", "_id": "38060"} -{"city": "POCAHONTAS", "loc": [-88.811559, 35.031476], "pop": 570, "state": "TN", "_id": "38061"} -{"city": "RIPLEY", "loc": [-89.534975, 35.752651], "pop": 12930, "state": "TN", "_id": "38063"} -{"city": "ROSSVILLE", "loc": [-89.567848, 35.076758], "pop": 3730, "state": "TN", "_id": "38066"} -{"city": "SAULSBURY", "loc": [-89.076868, 35.049786], "pop": 914, "state": "TN", "_id": "38067"} -{"city": "SOMERVILLE", "loc": [-89.391813, 35.277218], "pop": 11757, "state": "TN", "_id": "38068"} -{"city": "STANTON", "loc": [-89.33263, 35.448171], "pop": 2940, "state": "TN", "_id": "38069"} -{"city": "WHITEVILLE", "loc": [-89.133692, 35.319104], "pop": 3044, "state": "TN", "_id": "38075"} -{"city": "WILLISTON", "loc": [-89.390584, 35.166595], "pop": 701, "state": "TN", "_id": "38076"} -{"city": "TIPTONVILLE", "loc": [-89.464922, 36.384583], "pop": 4762, "state": "TN", "_id": "38079"} -{"city": "RIDGELY", "loc": [-89.485765, 36.263849], "pop": 1819, "state": "TN", "_id": "38080"} -{"city": "MEMPHIS", "loc": [-90.047995, 35.144001], "pop": 4144, "state": "TN", "_id": "38103"} -{"city": "MEMPHIS", "loc": [-90.004625, 35.133393], "pop": 29496, "state": "TN", "_id": "38104"} -{"city": "MEMPHIS", "loc": [-90.033042, 35.149748], "pop": 11143, "state": "TN", "_id": "38105"} -{"city": "MEMPHIS", "loc": [-90.032997, 35.102124], "pop": 40444, "state": "TN", "_id": "38106"} -{"city": "MEMPHIS", "loc": [-90.020077, 35.183136], "pop": 43502, "state": "TN", "_id": "38107"} -{"city": "MEMPHIS", "loc": [-89.968238, 35.178655], "pop": 31902, "state": "TN", "_id": "38108"} -{"city": "MEMPHIS", "loc": [-90.073238, 35.042538], "pop": 60508, "state": "TN", "_id": "38109"} -{"city": "MEMPHIS", "loc": [-89.945745, 35.107573], "pop": 43484, "state": "TN", "_id": "38111"} -{"city": "MEMPHIS", "loc": [-89.972895, 35.148277], "pop": 21266, "state": "TN", "_id": "38112"} -{"city": "MEMPHIS", "loc": [-90.079426, 35.111201], "pop": 68, "state": "TN", "_id": "38113"} -{"city": "MEMPHIS", "loc": [-89.98254, 35.098094], "pop": 38708, "state": "TN", "_id": "38114"} -{"city": "HICKORY HILL", "loc": [-89.86082, 35.054405], "pop": 34144, "state": "TN", "_id": "38115"} -{"city": "MEMPHIS", "loc": [-90.012314, 35.030298], "pop": 46906, "state": "TN", "_id": "38116"} -{"city": "MEMPHIS", "loc": [-89.903367, 35.112357], "pop": 27149, "state": "TN", "_id": "38117"} -{"city": "MEMPHIS", "loc": [-89.926538, 35.051421], "pop": 45507, "state": "TN", "_id": "38118"} -{"city": "MEMPHIS", "loc": [-89.850142, 35.082101], "pop": 20234, "state": "TN", "_id": "38119"} -{"city": "MEMPHIS", "loc": [-89.865119, 35.120654], "pop": 12552, "state": "TN", "_id": "38120"} -{"city": "MEMPHIS", "loc": [-89.926844, 35.157166], "pop": 23692, "state": "TN", "_id": "38122"} -{"city": "MEMPHIS", "loc": [-89.812357, 35.031249], "pop": 10280, "state": "TN", "_id": "38125"} -{"city": "MEMPHIS", "loc": [-90.042444, 35.125518], "pop": 16391, "state": "TN", "_id": "38126"} -{"city": "MEMPHIS", "loc": [-90.029623, 35.250982], "pop": 8047, "state": "TN", "_id": "38127"} -{"city": "MEMPHIS", "loc": [-89.941314, 35.221273], "pop": 54198, "state": "TN", "_id": "38128"} -{"city": "MEMPHIS", "loc": [-90.003699, 35.0655], "pop": 312, "state": "TN", "_id": "38131"} -{"city": "MEMPHIS", "loc": [-89.988627, 35.071967], "pop": 2, "state": "TN", "_id": "38132"} -{"city": "MEMPHIS", "loc": [-89.803564, 35.205362], "pop": 13227, "state": "TN", "_id": "38133"} -{"city": "BARTLETT", "loc": [-89.86409, 35.188639], "pop": 40805, "state": "TN", "_id": "38134"} -{"city": "MEMPHIS", "loc": [-89.850878, 35.232301], "pop": 13428, "state": "TN", "_id": "38135"} -{"city": "GERMANTOWN", "loc": [-89.80526, 35.088344], "pop": 22061, "state": "TN", "_id": "38138"} -{"city": "GERMANTOWN", "loc": [-89.770281, 35.087414], "pop": 11771, "state": "TN", "_id": "38139"} -{"city": "MEMPHIS", "loc": [-89.84916, 35.023091], "pop": 16247, "state": "TN", "_id": "38141"} -{"city": "MC KENZIE", "loc": [-88.513416, 36.127195], "pop": 6780, "state": "TN", "_id": "38201"} -{"city": "ATWOOD", "loc": [-88.624656, 35.966345], "pop": 2599, "state": "TN", "_id": "38220"} -{"city": "BIG SANDY", "loc": [-88.062732, 36.228515], "pop": 2647, "state": "TN", "_id": "38221"} -{"city": "BUCHANAN", "loc": [-88.151464, 36.414624], "pop": 3352, "state": "TN", "_id": "38222"} -{"city": "COTTAGE GROVE", "loc": [-88.461421, 36.34789], "pop": 1645, "state": "TN", "_id": "38224"} -{"city": "DRESDEN", "loc": [-88.696291, 36.295042], "pop": 5475, "state": "TN", "_id": "38225"} -{"city": "DUKEDOM", "loc": [-88.692553, 36.479645], "pop": 933, "state": "TN", "_id": "38226"} -{"city": "GLEASON", "loc": [-88.618441, 36.211737], "pop": 3307, "state": "TN", "_id": "38229"} -{"city": "GREENFIELD", "loc": [-88.74532, 36.148502], "pop": 4682, "state": "TN", "_id": "38230"} -{"city": "HENRY", "loc": [-88.453609, 36.201343], "pop": 1520, "state": "TN", "_id": "38231"} -{"city": "HORNBEAK", "loc": [-89.305617, 36.359594], "pop": 3042, "state": "TN", "_id": "38232"} -{"city": "KENTON", "loc": [-89.022879, 36.190583], "pop": 1446, "state": "TN", "_id": "38233"} -{"city": "MANSFIELD", "loc": [-88.285934, 36.184744], "pop": 1091, "state": "TN", "_id": "38236"} -{"city": "MARTIN", "loc": [-88.855395, 36.342467], "pop": 13540, "state": "TN", "_id": "38237"} -{"city": "OBION", "loc": [-89.28052, 36.265679], "pop": 3419, "state": "TN", "_id": "38240"} -{"city": "PALMERSVILLE", "loc": [-88.614178, 36.394772], "pop": 1471, "state": "TN", "_id": "38241"} -{"city": "PARIS", "loc": [-88.309328, 36.300519], "pop": 16597, "state": "TN", "_id": "38242"} -{"city": "PURYEAR", "loc": [-88.347193, 36.445632], "pop": 2292, "state": "TN", "_id": "38251"} -{"city": "RIVES", "loc": [-89.037632, 36.263665], "pop": 2424, "state": "TN", "_id": "38253"} -{"city": "SHARON", "loc": [-88.847661, 36.239454], "pop": 2564, "state": "TN", "_id": "38255"} -{"city": "SPRINGVILLE", "loc": [-88.145941, 36.265033], "pop": 1391, "state": "TN", "_id": "38256"} -{"city": "SOUTH FULTON", "loc": [-88.880781, 36.481386], "pop": 4676, "state": "TN", "_id": "38257"} -{"city": "TREZEVANT", "loc": [-88.610036, 36.017213], "pop": 2052, "state": "TN", "_id": "38258"} -{"city": "TRIMBLE", "loc": [-89.186265, 36.201113], "pop": 792, "state": "TN", "_id": "38259"} -{"city": "TROY", "loc": [-89.161058, 36.341644], "pop": 1588, "state": "TN", "_id": "38260"} -{"city": "UNION CITY", "loc": [-89.066662, 36.426311], "pop": 17007, "state": "TN", "_id": "38261"} -{"city": "JACKSON", "loc": [-88.814011, 35.610222], "pop": 39275, "state": "TN", "_id": "38301"} -{"city": "JACKSON", "loc": [-88.828127, 35.682875], "pop": 28992, "state": "TN", "_id": "38305"} -{"city": "ADAMSVILLE", "loc": [-88.413367, 35.25561], "pop": 3042, "state": "TN", "_id": "38310"} -{"city": "BATH SPRINGS", "loc": [-88.12863, 35.452111], "pop": 920, "state": "TN", "_id": "38311"} -{"city": "BEECH BLUFF", "loc": [-88.639634, 35.592156], "pop": 2682, "state": "TN", "_id": "38313"} -{"city": "BETHEL SPRINGS", "loc": [-88.643972, 35.228893], "pop": 2457, "state": "TN", "_id": "38315"} -{"city": "BRADFORD", "loc": [-88.804593, 36.064474], "pop": 2884, "state": "TN", "_id": "38316"} -{"city": "BRUCETON", "loc": [-88.251804, 36.026792], "pop": 2035, "state": "TN", "_id": "38317"} -{"city": "BUENA VISTA", "loc": [-88.292576, 35.943119], "pop": 165, "state": "TN", "_id": "38318"} -{"city": "CAMDEN", "loc": [-88.111853, 36.055578], "pop": 8922, "state": "TN", "_id": "38320"} -{"city": "CEDAR GROVE", "loc": [-88.551702, 35.861261], "pop": 1150, "state": "TN", "_id": "38321"} -{"city": "COUNCE", "loc": [-88.293567, 35.039415], "pop": 2015, "state": "TN", "_id": "38326"} -{"city": "CRUMP", "loc": [-88.335641, 35.221641], "pop": 1507, "state": "TN", "_id": "38327"} -{"city": "DARDEN", "loc": [-88.217688, 35.662962], "pop": 533, "state": "TN", "_id": "38328"} -{"city": "DECATURVILLE", "loc": [-88.133413, 35.558455], "pop": 2806, "state": "TN", "_id": "38329"} -{"city": "DYER", "loc": [-89.019226, 36.071578], "pop": 4695, "state": "TN", "_id": "38330"} -{"city": "ENVILLE", "loc": [-88.420613, 35.439321], "pop": 1041, "state": "TN", "_id": "38332"} -{"city": "EVA", "loc": [-88.028036, 36.078814], "pop": 1536, "state": "TN", "_id": "38333"} -{"city": "FINGER", "loc": [-88.606709, 35.325208], "pop": 1840, "state": "TN", "_id": "38334"} -{"city": "GADSDEN", "loc": [-88.992947, 35.779867], "pop": 2132, "state": "TN", "_id": "38337"} -{"city": "GUYS", "loc": [-88.520761, 35.014211], "pop": 689, "state": "TN", "_id": "38339"} -{"city": "HENDERSON", "loc": [-88.639774, 35.426929], "pop": 10311, "state": "TN", "_id": "38340"} -{"city": "HOLLADAY", "loc": [-88.091183, 35.895143], "pop": 1419, "state": "TN", "_id": "38341"} -{"city": "HOLLOW ROCK", "loc": [-88.290135, 36.056524], "pop": 2547, "state": "TN", "_id": "38342"} -{"city": "HUMBOLDT", "loc": [-88.905652, 35.836993], "pop": 13410, "state": "TN", "_id": "38343"} -{"city": "HUNTINGDON", "loc": [-88.420211, 36.006228], "pop": 7034, "state": "TN", "_id": "38344"} -{"city": "HURON", "loc": [-88.519549, 35.614377], "pop": 1665, "state": "TN", "_id": "38345"} -{"city": "JACKS CREEK", "loc": [-88.501865, 35.478206], "pop": 288, "state": "TN", "_id": "38347"} -{"city": "LAVINIA", "loc": [-88.632442, 35.85834], "pop": 1183, "state": "TN", "_id": "38348"} -{"city": "LEXINGTON", "loc": [-88.392743, 35.651195], "pop": 13554, "state": "TN", "_id": "38351"} -{"city": "LURAY", "loc": [-88.578422, 35.598846], "pop": 94, "state": "TN", "_id": "38352"} -{"city": "MEDINA", "loc": [-88.762655, 35.808088], "pop": 1303, "state": "TN", "_id": "38355"} -{"city": "MEDON", "loc": [-88.871656, 35.471804], "pop": 1486, "state": "TN", "_id": "38356"} -{"city": "MICHIE", "loc": [-88.440463, 35.060306], "pop": 2297, "state": "TN", "_id": "38357"} -{"city": "MILAN", "loc": [-88.768844, 35.925089], "pop": 11377, "state": "TN", "_id": "38358"} -{"city": "MILLEDGEVILLE", "loc": [-88.399125, 35.345175], "pop": 449, "state": "TN", "_id": "38359"} -{"city": "MORRIS CHAPEL", "loc": [-88.317599, 35.302562], "pop": 1272, "state": "TN", "_id": "38361"} -{"city": "OAKFIELD", "loc": [-88.802145, 35.754756], "pop": 2239, "state": "TN", "_id": "38362"} -{"city": "PARSONS", "loc": [-88.119532, 35.66635], "pop": 5906, "state": "TN", "_id": "38363"} -{"city": "PINSON", "loc": [-88.730396, 35.478059], "pop": 2598, "state": "TN", "_id": "38366"} -{"city": "RAMER", "loc": [-88.601733, 35.064181], "pop": 2804, "state": "TN", "_id": "38367"} -{"city": "REAGAN", "loc": [-88.350778, 35.50957], "pop": 804, "state": "TN", "_id": "38368"} -{"city": "RUTHERFORD", "loc": [-88.984863, 36.130027], "pop": 2265, "state": "TN", "_id": "38369"} -{"city": "SALTILLO", "loc": [-88.247225, 35.381467], "pop": 1007, "state": "TN", "_id": "38370"} -{"city": "SARDIS", "loc": [-88.3058, 35.438598], "pop": 1068, "state": "TN", "_id": "38371"} -{"city": "SAVANNAH", "loc": [-88.200541, 35.202272], "pop": 15773, "state": "TN", "_id": "38372"} -{"city": "SCOTTS HILL", "loc": [-88.240476, 35.504992], "pop": 1657, "state": "TN", "_id": "38374"} -{"city": "SELMER", "loc": [-88.595832, 35.169124], "pop": 7264, "state": "TN", "_id": "38375"} -{"city": "SHILOH", "loc": [-88.350717, 35.119545], "pop": 438, "state": "TN", "_id": "38376"} -{"city": "STANTONVILLE", "loc": [-88.436432, 35.180946], "pop": 1580, "state": "TN", "_id": "38379"} -{"city": "SUGAR TREE", "loc": [-88.032249, 35.791993], "pop": 179, "state": "TN", "_id": "38380"} -{"city": "TOONE", "loc": [-88.935286, 35.357421], "pop": 2333, "state": "TN", "_id": "38381"} -{"city": "TRENTON", "loc": [-88.950655, 35.971246], "pop": 9011, "state": "TN", "_id": "38382"} -{"city": "WESTPORT", "loc": [-88.336364, 35.916918], "pop": 906, "state": "TN", "_id": "38387"} -{"city": "WILDERSVILLE", "loc": [-88.438794, 35.769772], "pop": 2828, "state": "TN", "_id": "38388"} -{"city": "YUMA", "loc": [-88.381878, 35.867964], "pop": 1063, "state": "TN", "_id": "38390"} -{"city": "DENMARK", "loc": [-88.975892, 35.557059], "pop": 1760, "state": "TN", "_id": "38391"} -{"city": "MERCER", "loc": [-89.03728, 35.481828], "pop": 609, "state": "TN", "_id": "38392"} -{"city": "COLUMBIA", "loc": [-87.038032, 35.615577], "pop": 38459, "state": "TN", "_id": "38401"} -{"city": "CLIFTON", "loc": [-87.94997, 35.381948], "pop": 1775, "state": "TN", "_id": "38425"} -{"city": "ARDMORE", "loc": [-86.879555, 35.057445], "pop": 3560, "state": "TN", "_id": "38449"} -{"city": "COLLINWOOD", "loc": [-87.718488, 35.163525], "pop": 2558, "state": "TN", "_id": "38450"} -{"city": "CULLEOKA", "loc": [-87.000503, 35.474914], "pop": 2476, "state": "TN", "_id": "38451"} -{"city": "CYPRESS INN", "loc": [-87.78833, 35.057945], "pop": 764, "state": "TN", "_id": "38452"} -{"city": "ARDMORE", "loc": [-86.910024, 35.003654], "pop": 204, "state": "TN", "_id": "38453"} -{"city": "DUCK RIVER", "loc": [-87.342336, 35.738599], "pop": 1027, "state": "TN", "_id": "38454"} -{"city": "ETHRIDGE", "loc": [-87.303912, 35.332648], "pop": 3854, "state": "TN", "_id": "38456"} -{"city": "FIVE POINTS", "loc": [-87.296128, 35.031046], "pop": 798, "state": "TN", "_id": "38457"} -{"city": "FRANKEWING", "loc": [-86.781818, 35.177854], "pop": 1670, "state": "TN", "_id": "38459"} -{"city": "GOODSPRING", "loc": [-87.127788, 35.116709], "pop": 889, "state": "TN", "_id": "38460"} -{"city": "HAMPSHIRE", "loc": [-87.325135, 35.591482], "pop": 1059, "state": "TN", "_id": "38461"} -{"city": "KIMMINS", "loc": [-87.55461, 35.540837], "pop": 8004, "state": "TN", "_id": "38462"} -{"city": "IRON CITY", "loc": [-87.647321, 35.056283], "pop": 2249, "state": "TN", "_id": "38463"} -{"city": "LAWRENCEBURG", "loc": [-87.352582, 35.250668], "pop": 18681, "state": "TN", "_id": "38464"} -{"city": "LEOMA", "loc": [-87.316773, 35.138177], "pop": 3568, "state": "TN", "_id": "38468"} -{"city": "LORETTO", "loc": [-87.426978, 35.072797], "pop": 3369, "state": "TN", "_id": "38469"} -{"city": "LUTTS", "loc": [-87.892291, 35.113842], "pop": 1070, "state": "TN", "_id": "38471"} -{"city": "LYNNVILLE", "loc": [-87.062877, 35.379235], "pop": 2712, "state": "TN", "_id": "38472"} -{"city": "MINOR HILL", "loc": [-87.152194, 35.050034], "pop": 1482, "state": "TN", "_id": "38473"} -{"city": "MOUNT PLEASANT", "loc": [-87.203678, 35.530084], "pop": 7952, "state": "TN", "_id": "38474"} -{"city": "OLIVEHILL", "loc": [-88.039038, 35.266711], "pop": 443, "state": "TN", "_id": "38475"} -{"city": "PRIMM SPRINGS", "loc": [-87.253043, 35.810364], "pop": 346, "state": "TN", "_id": "38476"} -{"city": "PROSPECT", "loc": [-87.017384, 35.066626], "pop": 1843, "state": "TN", "_id": "38477"} -{"city": "PULASKI", "loc": [-87.039263, 35.209274], "pop": 15310, "state": "TN", "_id": "38478"} -{"city": "SAINT JOSEPH", "loc": [-87.501807, 35.037556], "pop": 881, "state": "TN", "_id": "38481"} -{"city": "SANTA FE", "loc": [-87.151543, 35.75877], "pop": 2001, "state": "TN", "_id": "38482"} -{"city": "SUMMERTOWN", "loc": [-87.31983, 35.430673], "pop": 3302, "state": "TN", "_id": "38483"} -{"city": "WAYNESBORO", "loc": [-87.739498, 35.322019], "pop": 6381, "state": "TN", "_id": "38485"} -{"city": "WESTPOINT", "loc": [-87.538025, 35.139302], "pop": 717, "state": "TN", "_id": "38486"} -{"city": "WILLIAMSPORT", "loc": [-87.225692, 35.64937], "pop": 1199, "state": "TN", "_id": "38487"} -{"city": "TAFT", "loc": [-86.644672, 35.051731], "pop": 4681, "state": "TN", "_id": "38488"} -{"city": "ALGOOD", "loc": [-85.49531, 36.174261], "pop": 41805, "state": "TN", "_id": "38501"} -{"city": "ALLARDT", "loc": [-84.850784, 36.374889], "pop": 3284, "state": "TN", "_id": "38504"} -{"city": "ALLONS", "loc": [-85.319744, 36.497002], "pop": 1641, "state": "TN", "_id": "38541"} -{"city": "ALLRED", "loc": [-85.176084, 36.366838], "pop": 2, "state": "TN", "_id": "38542"} -{"city": "ALPINE", "loc": [-85.152153, 36.380324], "pop": 441, "state": "TN", "_id": "38543"} -{"city": "BAXTER", "loc": [-85.637766, 36.124917], "pop": 5871, "state": "TN", "_id": "38544"} -{"city": "BLOOMINGTON SPRI", "loc": [-85.64786, 36.219454], "pop": 1089, "state": "TN", "_id": "38545"} -{"city": "BRUSH CREEK", "loc": [-86.020344, 36.110673], "pop": 656, "state": "TN", "_id": "38547"} -{"city": "BUFFALO VALLEY", "loc": [-85.758879, 36.183311], "pop": 704, "state": "TN", "_id": "38548"} -{"city": "BYRDSTOWN", "loc": [-85.145647, 36.570869], "pop": 2898, "state": "TN", "_id": "38549"} -{"city": "CELINA", "loc": [-85.496551, 36.547491], "pop": 4102, "state": "TN", "_id": "38551"} -{"city": "CHESTNUT MOUND", "loc": [-85.837402, 36.192877], "pop": 784, "state": "TN", "_id": "38552"} -{"city": "CLARKRANGE", "loc": [-84.977737, 36.211188], "pop": 1843, "state": "TN", "_id": "38553"} -{"city": "CRAWFORD", "loc": [-85.168765, 36.23611], "pop": 1213, "state": "TN", "_id": "38554"} -{"city": "FAIRFIELD GLADE", "loc": [-85.017171, 35.944041], "pop": 26993, "state": "TN", "_id": "38555"} -{"city": "JAMESTOWN", "loc": [-84.935721, 36.424471], "pop": 7728, "state": "TN", "_id": "38556"} -{"city": "DOYLE", "loc": [-85.498997, 35.872206], "pop": 1802, "state": "TN", "_id": "38559"} -{"city": "ELMWOOD", "loc": [-85.880843, 36.235468], "pop": 937, "state": "TN", "_id": "38560"} -{"city": "GAINESBORO", "loc": [-85.635509, 36.343767], "pop": 6883, "state": "TN", "_id": "38562"} -{"city": "GORDONSVILLE", "loc": [-86.000818, 36.184317], "pop": 2630, "state": "TN", "_id": "38563"} -{"city": "GRANVILLE", "loc": [-85.747533, 36.276847], "pop": 381, "state": "TN", "_id": "38564"} -{"city": "GRIMSLEY", "loc": [-85.01549, 36.243353], "pop": 1092, "state": "TN", "_id": "38565"} -{"city": "HICKMAN", "loc": [-85.902297, 36.119653], "pop": 836, "state": "TN", "_id": "38567"} -{"city": "HILHAM", "loc": [-85.436918, 36.391492], "pop": 1976, "state": "TN", "_id": "38568"} -{"city": "LANCASTER", "loc": [-85.855095, 36.09542], "pop": 261, "state": "TN", "_id": "38569"} -{"city": "LIVINGSTON", "loc": [-85.320523, 36.389012], "pop": 7274, "state": "TN", "_id": "38570"} -{"city": "MONROE", "loc": [-85.216385, 36.464201], "pop": 1951, "state": "TN", "_id": "38573"} -{"city": "MONTEREY", "loc": [-85.254198, 36.150862], "pop": 5030, "state": "TN", "_id": "38574"} -{"city": "MOSS", "loc": [-85.677235, 36.596623], "pop": 980, "state": "TN", "_id": "38575"} -{"city": "PALL MALL", "loc": [-85.038385, 36.578066], "pop": 1537, "state": "TN", "_id": "38577"} -{"city": "PLEASANT HILL", "loc": [-85.16694, 36.01121], "pop": 4954, "state": "TN", "_id": "38578"} -{"city": "QUEBECK", "loc": [-85.538189, 35.825379], "pop": 1250, "state": "TN", "_id": "38579"} -{"city": "RICKMAN", "loc": [-85.380572, 36.301933], "pop": 2109, "state": "TN", "_id": "38580"} -{"city": "BONE CAVE", "loc": [-85.635156, 35.743813], "pop": 3954, "state": "TN", "_id": "38581"} -{"city": "SILVER POINT", "loc": [-85.733801, 36.100562], "pop": 954, "state": "TN", "_id": "38582"} -{"city": "RAVENSCROFT", "loc": [-85.478582, 35.954664], "pop": 16814, "state": "TN", "_id": "38583"} -{"city": "SPENCER", "loc": [-85.428661, 35.727871], "pop": 3954, "state": "TN", "_id": "38585"} -{"city": "WALLING", "loc": [-85.618509, 35.869543], "pop": 641, "state": "TN", "_id": "38587"} -{"city": "WHITLEYVILLE", "loc": [-85.689873, 36.515839], "pop": 1272, "state": "TN", "_id": "38588"} -{"city": "WILDER", "loc": [-85.074717, 36.230526], "pop": 178, "state": "TN", "_id": "38589"} -{"city": "FORT CAMPBELL", "loc": [-87.558507, 36.599497], "pop": 728, "state": "TN", "_id": "42223"} -{"city": "ALLEN", "loc": [-96.645433, 33.093383], "pop": 24151, "state": "TX", "_id": "75002"} -{"city": "CARROLLTON", "loc": [-96.882464, 32.965736], "pop": 37699, "state": "TX", "_id": "75006"} -{"city": "CARROLLTON", "loc": [-96.881988, 33.003294], "pop": 54796, "state": "TX", "_id": "75007"} -{"city": "CARROLLTON", "loc": [-96.923197, 33.03524], "pop": 1482, "state": "TX", "_id": "75008"} -{"city": "CELINA", "loc": [-96.767325, 33.310316], "pop": 3373, "state": "TX", "_id": "75009"} -{"city": "CARROLLTON", "loc": [-96.877746, 33.030414], "pop": 4379, "state": "TX", "_id": "75010"} -{"city": "COPPELL", "loc": [-96.980516, 32.96727], "pop": 16862, "state": "TX", "_id": "75019"} -{"city": "DENISON", "loc": [-96.549574, 33.745009], "pop": 27172, "state": "TX", "_id": "75020"} -{"city": "PLANO", "loc": [-96.736454, 33.054972], "pop": 40832, "state": "TX", "_id": "75023"} -{"city": "PLANO", "loc": [-96.784307, 33.075211], "pop": 1439, "state": "TX", "_id": "75024"} -{"city": "PLANO", "loc": [-96.729142, 33.078377], "pop": 8562, "state": "TX", "_id": "75025"} -{"city": "FLOWER MOUND", "loc": [-97.074501, 33.038268], "pop": 16825, "state": "TX", "_id": "75028"} -{"city": "FRISCO", "loc": [-96.824105, 33.149901], "pop": 8045, "state": "TX", "_id": "75034"} -{"city": "IRVING", "loc": [-96.990503, 32.865309], "pop": 20152, "state": "TX", "_id": "75038"} -{"city": "IRVING", "loc": [-96.938876, 32.869669], "pop": 598, "state": "TX", "_id": "75039"} -{"city": "GARLAND", "loc": [-96.624804, 32.922744], "pop": 45359, "state": "TX", "_id": "75040"} -{"city": "GARLAND", "loc": [-96.641115, 32.87937], "pop": 26212, "state": "TX", "_id": "75041"} -{"city": "GARLAND", "loc": [-96.677545, 32.918486], "pop": 31807, "state": "TX", "_id": "75042"} -{"city": "GARLAND", "loc": [-96.599882, 32.856502], "pop": 46620, "state": "TX", "_id": "75043"} -{"city": "GARLAND", "loc": [-96.665383, 32.952228], "pop": 30455, "state": "TX", "_id": "75044"} -{"city": "SACHSE", "loc": [-96.591472, 32.973576], "pop": 5632, "state": "TX", "_id": "75048"} -{"city": "GRAND PRAIRIE", "loc": [-97.01121, 32.76488], "pop": 32148, "state": "TX", "_id": "75050"} -{"city": "GRAND PRAIRIE", "loc": [-97.006916, 32.711471], "pop": 52779, "state": "TX", "_id": "75051"} -{"city": "GRAND PRAIRIE", "loc": [-97.031142, 32.660475], "pop": 15850, "state": "TX", "_id": "75052"} -{"city": "THE COLONY", "loc": [-96.883574, 33.094023], "pop": 22549, "state": "TX", "_id": "75056"} -{"city": "LEWISVILLE", "loc": [-96.999882, 33.053162], "pop": 8052, "state": "TX", "_id": "75057"} -{"city": "GUNTER", "loc": [-96.734103, 33.449513], "pop": 1410, "state": "TX", "_id": "75058"} -{"city": "IRVING", "loc": [-96.959665, 32.80231], "pop": 41001, "state": "TX", "_id": "75060"} -{"city": "IRVING", "loc": [-96.963256, 32.826658], "pop": 42947, "state": "TX", "_id": "75061"} -{"city": "IRVING", "loc": [-96.974027, 32.847854], "pop": 40234, "state": "TX", "_id": "75062"} -{"city": "IRVING", "loc": [-96.959817, 32.924686], "pop": 9527, "state": "TX", "_id": "75063"} -{"city": "LAKE DALLAS", "loc": [-97.023709, 33.121903], "pop": 5452, "state": "TX", "_id": "75065"} -{"city": "HIGHLAND VILLAGE", "loc": [-97.026815, 33.04503], "pop": 46151, "state": "TX", "_id": "75067"} -{"city": "LAKEWOOD VILLAGE", "loc": [-96.967811, 33.178319], "pop": 3952, "state": "TX", "_id": "75068"} -{"city": "MC KINNEY", "loc": [-96.608488, 33.196558], "pop": 20865, "state": "TX", "_id": "75069"} -{"city": "MC KINNEY", "loc": [-96.664223, 33.195148], "pop": 5573, "state": "TX", "_id": "75070"} -{"city": "PLANO", "loc": [-96.67771, 33.027722], "pop": 29591, "state": "TX", "_id": "75074"} -{"city": "PLANO", "loc": [-96.739743, 33.024985], "pop": 33236, "state": "TX", "_id": "75075"} -{"city": "POTTSBORO", "loc": [-96.690562, 33.809526], "pop": 5458, "state": "TX", "_id": "75076"} -{"city": "PROSPER", "loc": [-96.795401, 33.236169], "pop": 1103, "state": "TX", "_id": "75078"} -{"city": "RICHARDSON", "loc": [-96.745249, 32.965986], "pop": 37227, "state": "TX", "_id": "75080"} -{"city": "RICHARDSON", "loc": [-96.705841, 32.946217], "pop": 30573, "state": "TX", "_id": "75081"} -{"city": "RICHARDSON", "loc": [-96.685957, 32.986461], "pop": 6678, "state": "TX", "_id": "75082"} -{"city": "HEATH", "loc": [-96.454497, 32.90456], "pop": 17438, "state": "TX", "_id": "75087"} -{"city": "ROWLETT", "loc": [-96.547161, 32.90315], "pop": 22057, "state": "TX", "_id": "75088"} -{"city": "SHERMAN", "loc": [-96.607521, 33.643525], "pop": 35260, "state": "TX", "_id": "75090"} -{"city": "PLANO", "loc": [-96.788903, 33.029866], "pop": 14376, "state": "TX", "_id": "75093"} -{"city": "MURPHY", "loc": [-96.609101, 33.004873], "pop": 1722, "state": "TX", "_id": "75094"} -{"city": "WYLIE", "loc": [-96.539383, 33.004102], "pop": 15418, "state": "TX", "_id": "75098"} -{"city": "BARRY", "loc": [-96.625141, 32.101356], "pop": 588, "state": "TX", "_id": "75102"} -{"city": "CANTON", "loc": [-95.904657, 32.514301], "pop": 12281, "state": "TX", "_id": "75103"} -{"city": "CEDAR HILL", "loc": [-96.943802, 32.58847], "pop": 19503, "state": "TX", "_id": "75104"} -{"city": "CHATFIELD", "loc": [-96.388668, 32.295416], "pop": 129, "state": "TX", "_id": "75105"} -{"city": "CORSICANA", "loc": [-96.476151, 32.086776], "pop": 28003, "state": "TX", "_id": "75110"} -{"city": "CRANDALL", "loc": [-96.46369, 32.597465], "pop": 3720, "state": "TX", "_id": "75114"} -{"city": "DE SOTO", "loc": [-96.854721, 32.593167], "pop": 33750, "state": "TX", "_id": "75115"} -{"city": "DUNCANVILLE", "loc": [-96.911392, 32.65873], "pop": 18023, "state": "TX", "_id": "75116"} -{"city": "EDGEWOOD", "loc": [-95.878011, 32.700326], "pop": 3328, "state": "TX", "_id": "75117"} -{"city": "ENNIS", "loc": [-96.622363, 32.332102], "pop": 19008, "state": "TX", "_id": "75119"} -{"city": "EUSTACE", "loc": [-96.013693, 32.296485], "pop": 2192, "state": "TX", "_id": "75124"} -{"city": "FERRIS", "loc": [-96.664321, 32.52232], "pop": 7592, "state": "TX", "_id": "75125"} -{"city": "FORNEY", "loc": [-96.459759, 32.749055], "pop": 6803, "state": "TX", "_id": "75126"} -{"city": "FRUITVALE", "loc": [-95.789903, 32.676981], "pop": 1622, "state": "TX", "_id": "75127"} -{"city": "LANCASTER", "loc": [-96.782997, 32.616056], "pop": 11306, "state": "TX", "_id": "75134"} -{"city": "CADDO MILLS", "loc": [-96.239093, 33.068267], "pop": 3148, "state": "TX", "_id": "75135"} -{"city": "DUNCANVILLE", "loc": [-96.911325, 32.634665], "pop": 16979, "state": "TX", "_id": "75137"} -{"city": "GRAND SALINE", "loc": [-95.706411, 32.663528], "pop": 4870, "state": "TX", "_id": "75140"} -{"city": "HUTCHINS", "loc": [-96.707021, 32.639586], "pop": 2716, "state": "TX", "_id": "75141"} -{"city": "KAUFMAN", "loc": [-96.285239, 32.54599], "pop": 9160, "state": "TX", "_id": "75142"} -{"city": "SEVEN POINTS", "loc": [-96.257768, 32.369146], "pop": 5268, "state": "TX", "_id": "75143"} -{"city": "KERENS", "loc": [-96.229828, 32.127463], "pop": 2991, "state": "TX", "_id": "75144"} -{"city": "LANCASTER", "loc": [-96.772805, 32.591395], "pop": 11762, "state": "TX", "_id": "75146"} -{"city": "GUN BARREL CITY", "loc": [-96.129524, 32.307513], "pop": 18113, "state": "TX", "_id": "75147"} -{"city": "MALAKOFF", "loc": [-96.005952, 32.170511], "pop": 4972, "state": "TX", "_id": "75148"} -{"city": "MESQUITE", "loc": [-96.608219, 32.767821], "pop": 45754, "state": "TX", "_id": "75149"} -{"city": "MESQUITE", "loc": [-96.630681, 32.815416], "pop": 51494, "state": "TX", "_id": "75150"} -{"city": "PALMER", "loc": [-96.679429, 32.438714], "pop": 2605, "state": "TX", "_id": "75152"} -{"city": "POWELL", "loc": [-96.332713, 32.119557], "pop": 127, "state": "TX", "_id": "75153"} -{"city": "OVILLA", "loc": [-96.820337, 32.516096], "pop": 16882, "state": "TX", "_id": "75154"} -{"city": "RICE", "loc": [-96.460613, 32.225788], "pop": 1812, "state": "TX", "_id": "75155"} -{"city": "SCURRY", "loc": [-96.392451, 32.48184], "pop": 2589, "state": "TX", "_id": "75158"} -{"city": "SEAGOVILLE", "loc": [-96.557967, 32.652522], "pop": 10569, "state": "TX", "_id": "75159"} -{"city": "TERRELL", "loc": [-96.251342, 32.714292], "pop": 24116, "state": "TX", "_id": "75160"} -{"city": "TRINIDAD", "loc": [-96.08307, 32.138341], "pop": 1246, "state": "TX", "_id": "75163"} -{"city": "WAXAHACHIE", "loc": [-96.837398, 32.380796], "pop": 22844, "state": "TX", "_id": "75165"} -{"city": "WILLS POINT", "loc": [-96.00788, 32.72834], "pop": 7310, "state": "TX", "_id": "75169"} -{"city": "WILMER", "loc": [-96.68376, 32.598133], "pop": 2407, "state": "TX", "_id": "75172"} -{"city": "NEVADA", "loc": [-96.387657, 33.05934], "pop": 3149, "state": "TX", "_id": "75173"} -{"city": "BALCH SPRINGS", "loc": [-96.615278, 32.720216], "pop": 18848, "state": "TX", "_id": "75180"} -{"city": "MESQUITE", "loc": [-96.566889, 32.727166], "pop": 5005, "state": "TX", "_id": "75181"} -{"city": "MESQUITE", "loc": [-96.567004, 32.801922], "pop": 1959, "state": "TX", "_id": "75182"} -{"city": "ROYSE CITY", "loc": [-96.36484, 32.962778], "pop": 5533, "state": "TX", "_id": "75189"} -{"city": "DALLAS", "loc": [-96.80439, 32.790439], "pop": 1505, "state": "TX", "_id": "75201"} -{"city": "DALLAS", "loc": [-96.805352, 32.778056], "pop": 3622, "state": "TX", "_id": "75202"} -{"city": "DALLAS", "loc": [-96.806976, 32.745985], "pop": 18850, "state": "TX", "_id": "75203"} -{"city": "DALLAS", "loc": [-96.785144, 32.803814], "pop": 16697, "state": "TX", "_id": "75204"} -{"city": "VILLAGE", "loc": [-96.793828, 32.836878], "pop": 23883, "state": "TX", "_id": "75205"} -{"city": "DALLAS", "loc": [-96.769219, 32.831029], "pop": 36526, "state": "TX", "_id": "75206"} -{"city": "DALLAS", "loc": [-96.831871, 32.793897], "pop": 1744, "state": "TX", "_id": "75207"} -{"city": "DALLAS", "loc": [-96.838898, 32.749208], "pop": 33527, "state": "TX", "_id": "75208"} -{"city": "DALLAS", "loc": [-96.825984, 32.84564], "pop": 15398, "state": "TX", "_id": "75209"} -{"city": "DALLAS", "loc": [-96.742974, 32.769919], "pop": 10216, "state": "TX", "_id": "75210"} -{"city": "COCKRELL HILL", "loc": [-96.881797, 32.736928], "pop": 54691, "state": "TX", "_id": "75211"} -{"city": "DALLAS", "loc": [-96.871396, 32.782884], "pop": 23556, "state": "TX", "_id": "75212"} -{"city": "DALLAS", "loc": [-96.749774, 32.824789], "pop": 32618, "state": "TX", "_id": "75214"} -{"city": "DALLAS", "loc": [-96.76226, 32.758206], "pop": 22120, "state": "TX", "_id": "75215"} -{"city": "DALLAS", "loc": [-96.795488, 32.708611], "pop": 55166, "state": "TX", "_id": "75216"} -{"city": "DALLAS", "loc": [-96.675481, 32.724429], "pop": 57605, "state": "TX", "_id": "75217"} -{"city": "DALLAS", "loc": [-96.697212, 32.846335], "pop": 22646, "state": "TX", "_id": "75218"} -{"city": "DALLAS", "loc": [-96.814166, 32.813245], "pop": 19178, "state": "TX", "_id": "75219"} -{"city": "DALLAS", "loc": [-96.862202, 32.868131], "pop": 30241, "state": "TX", "_id": "75220"} -{"city": "DALLAS", "loc": [-96.747475, 32.794173], "pop": 14700, "state": "TX", "_id": "75223"} -{"city": "DALLAS", "loc": [-96.838711, 32.711415], "pop": 26734, "state": "TX", "_id": "75224"} -{"city": "DALLAS", "loc": [-96.791753, 32.862808], "pop": 18255, "state": "TX", "_id": "75225"} -{"city": "DALLAS", "loc": [-96.767552, 32.78871], "pop": 1561, "state": "TX", "_id": "75226"} -{"city": "DALLAS", "loc": [-96.683586, 32.767226], "pop": 39631, "state": "TX", "_id": "75227"} -{"city": "DALLAS", "loc": [-96.678378, 32.824997], "pop": 55010, "state": "TX", "_id": "75228"} -{"city": "DALLAS", "loc": [-96.8588, 32.8958], "pop": 27621, "state": "TX", "_id": "75229"} -{"city": "DALLAS", "loc": [-96.789679, 32.89994], "pop": 24281, "state": "TX", "_id": "75230"} -{"city": "DALLAS", "loc": [-96.74953, 32.875621], "pop": 35407, "state": "TX", "_id": "75231"} -{"city": "DALLAS", "loc": [-96.838392, 32.664708], "pop": 28289, "state": "TX", "_id": "75232"} -{"city": "DALLAS", "loc": [-96.872547, 32.704638], "pop": 11206, "state": "TX", "_id": "75233"} -{"city": "FARMERS BRANCH", "loc": [-96.876848, 32.929803], "pop": 25992, "state": "TX", "_id": "75234"} -{"city": "DALLAS", "loc": [-96.838843, 32.825213], "pop": 14850, "state": "TX", "_id": "75235"} -{"city": "DALLAS", "loc": [-96.917737, 32.690002], "pop": 6124, "state": "TX", "_id": "75236"} -{"city": "DALLAS", "loc": [-96.876453, 32.658972], "pop": 12859, "state": "TX", "_id": "75237"} -{"city": "DALLAS", "loc": [-96.707982, 32.876976], "pop": 25855, "state": "TX", "_id": "75238"} -{"city": "DALLAS", "loc": [-96.732769, 32.659974], "pop": 541, "state": "TX", "_id": "75239"} -{"city": "DALLAS", "loc": [-96.787214, 32.937431], "pop": 37646, "state": "TX", "_id": "75240"} -{"city": "DALLAS", "loc": [-96.777421, 32.672216], "pop": 26407, "state": "TX", "_id": "75241"} -{"city": "DALLAS", "loc": [-96.728472, 32.910347], "pop": 48344, "state": "TX", "_id": "75243"} -{"city": "FARMERS BRANCH", "loc": [-96.842533, 32.925817], "pop": 16870, "state": "TX", "_id": "75244"} -{"city": "DALLAS", "loc": [-96.769696, 32.79484], "pop": 3129, "state": "TX", "_id": "75246"} -{"city": "DALLAS", "loc": [-96.887123, 32.801323], "pop": 124, "state": "TX", "_id": "75247"} -{"city": "DALLAS", "loc": [-96.794242, 32.968199], "pop": 34858, "state": "TX", "_id": "75248"} -{"city": "DALLAS", "loc": [-96.949266, 32.636024], "pop": 8677, "state": "TX", "_id": "75249"} -{"city": "DALLAS", "loc": [-96.771831, 32.912203], "pop": 75, "state": "TX", "_id": "75251"} -{"city": "DALLAS", "loc": [-96.792113, 32.996848], "pop": 15152, "state": "TX", "_id": "75252"} -{"city": "DALLAS", "loc": [-96.59643, 32.683311], "pop": 10252, "state": "TX", "_id": "75253"} -{"city": "DALLAS", "loc": [-96.83143, 33.000458], "pop": 11388, "state": "TX", "_id": "75287"} -{"city": "GREENVILLE", "loc": [-96.102416, 33.117476], "pop": 30183, "state": "TX", "_id": "75401"} -{"city": "PRINCETON", "loc": [-96.498073, 33.155542], "pop": 7552, "state": "TX", "_id": "75407"} -{"city": "ANNA", "loc": [-96.563862, 33.344516], "pop": 4413, "state": "TX", "_id": "75409"} -{"city": "ALBA", "loc": [-95.597102, 32.765235], "pop": 2599, "state": "TX", "_id": "75410"} -{"city": "ARTHUR CITY", "loc": [-95.494037, 33.840031], "pop": 1583, "state": "TX", "_id": "75411"} -{"city": "BAGWELL", "loc": [-95.14869, 33.836089], "pop": 979, "state": "TX", "_id": "75412"} -{"city": "BELLS", "loc": [-96.423668, 33.617817], "pop": 1829, "state": "TX", "_id": "75414"} -{"city": "BEN FRANKLIN", "loc": [-95.759107, 33.474146], "pop": 151, "state": "TX", "_id": "75415"} -{"city": "BLOSSOM", "loc": [-95.382341, 33.694547], "pop": 3253, "state": "TX", "_id": "75416"} -{"city": "BOGATA", "loc": [-95.193725, 33.469862], "pop": 2803, "state": "TX", "_id": "75417"} -{"city": "BONHAM", "loc": [-96.183566, 33.580559], "pop": 9003, "state": "TX", "_id": "75418"} -{"city": "BRASHEAR", "loc": [-95.73451, 33.115521], "pop": 272, "state": "TX", "_id": "75420"} -{"city": "BROOKSTON", "loc": [-95.688812, 33.624555], "pop": 287, "state": "TX", "_id": "75421"} -{"city": "CAMPBELL", "loc": [-95.943919, 33.151049], "pop": 2029, "state": "TX", "_id": "75422"} -{"city": "CELESTE", "loc": [-96.207635, 33.264913], "pop": 2697, "state": "TX", "_id": "75423"} -{"city": "BLUE RIDGE", "loc": [-96.390056, 33.306135], "pop": 2312, "state": "TX", "_id": "75424"} -{"city": "CLARKSVILLE", "loc": [-95.046094, 33.623563], "pop": 6675, "state": "TX", "_id": "75426"} -{"city": "COMMERCE", "loc": [-95.90968, 33.2493], "pop": 8421, "state": "TX", "_id": "75428"} -{"city": "COMO", "loc": [-95.362655, 33.064231], "pop": 1679, "state": "TX", "_id": "75431"} -{"city": "COOPER", "loc": [-95.662311, 33.381166], "pop": 3438, "state": "TX", "_id": "75432"} -{"city": "CUMBY", "loc": [-95.794536, 33.111761], "pop": 2894, "state": "TX", "_id": "75433"} -{"city": "DEPORT", "loc": [-95.365351, 33.522077], "pop": 1927, "state": "TX", "_id": "75435"} -{"city": "DETROIT", "loc": [-95.23848, 33.662691], "pop": 1594, "state": "TX", "_id": "75436"} -{"city": "DIKE", "loc": [-95.471125, 33.196485], "pop": 210, "state": "TX", "_id": "75437"} -{"city": "DODD CITY", "loc": [-96.06194, 33.564704], "pop": 1094, "state": "TX", "_id": "75438"} -{"city": "ECTOR", "loc": [-96.273533, 33.581913], "pop": 803, "state": "TX", "_id": "75439"} -{"city": "EMORY", "loc": [-95.741786, 32.875041], "pop": 3919, "state": "TX", "_id": "75440"} -{"city": "FARMERSVILLE", "loc": [-96.368619, 33.165862], "pop": 4777, "state": "TX", "_id": "75442"} -{"city": "HONEY GROVE", "loc": [-95.9109, 33.598505], "pop": 2563, "state": "TX", "_id": "75446"} -{"city": "IVANHOE", "loc": [-96.169811, 33.67364], "pop": 1004, "state": "TX", "_id": "75447"} -{"city": "KLONDIKE", "loc": [-95.801762, 33.303375], "pop": 729, "state": "TX", "_id": "75448"} -{"city": "LADONIA", "loc": [-95.945489, 33.424527], "pop": 899, "state": "TX", "_id": "75449"} -{"city": "LAKE CREEK", "loc": [-95.622131, 33.467493], "pop": 50, "state": "TX", "_id": "75450"} -{"city": "LEESBURG", "loc": [-95.107924, 32.976275], "pop": 1246, "state": "TX", "_id": "75451"} -{"city": "LEONARD", "loc": [-96.223772, 33.404363], "pop": 3229, "state": "TX", "_id": "75452"} -{"city": "LONE OAK", "loc": [-95.943412, 32.991571], "pop": 1822, "state": "TX", "_id": "75453"} -{"city": "MELISSA", "loc": [-96.574009, 33.284114], "pop": 703, "state": "TX", "_id": "75454"} -{"city": "MOUNT PLEASANT", "loc": [-94.969461, 33.173309], "pop": 16878, "state": "TX", "_id": "75455"} -{"city": "MOUNT VERNON", "loc": [-95.218106, 33.170204], "pop": 5948, "state": "TX", "_id": "75457"} -{"city": "HOWE", "loc": [-96.640723, 33.534867], "pop": 4843, "state": "TX", "_id": "75459"} -{"city": "PARIS", "loc": [-95.537881, 33.658077], "pop": 30317, "state": "TX", "_id": "75460"} -{"city": "PATTONVILLE", "loc": [-95.3908, 33.570234], "pop": 194, "state": "TX", "_id": "75468"} -{"city": "PECAN GAP", "loc": [-95.826196, 33.419641], "pop": 489, "state": "TX", "_id": "75469"} -{"city": "PETTY", "loc": [-95.789057, 33.609765], "pop": 247, "state": "TX", "_id": "75470"} -{"city": "PICKTON", "loc": [-95.462979, 33.042401], "pop": 1569, "state": "TX", "_id": "75471"} -{"city": "POINT", "loc": [-95.890285, 32.900743], "pop": 2796, "state": "TX", "_id": "75472"} -{"city": "POWDERLY", "loc": [-95.530692, 33.777877], "pop": 1570, "state": "TX", "_id": "75473"} -{"city": "QUINLAN", "loc": [-96.126083, 32.898347], "pop": 13826, "state": "TX", "_id": "75474"} -{"city": "RAVENNA", "loc": [-96.145157, 33.709757], "pop": 1635, "state": "TX", "_id": "75476"} -{"city": "ROXTON", "loc": [-95.741605, 33.542934], "pop": 975, "state": "TX", "_id": "75477"} -{"city": "SALTILLO", "loc": [-95.343324, 33.176678], "pop": 500, "state": "TX", "_id": "75478"} -{"city": "SAVOY", "loc": [-96.350156, 33.606571], "pop": 1726, "state": "TX", "_id": "75479"} -{"city": "SCROGGINS", "loc": [-95.239683, 32.991177], "pop": 2029, "state": "TX", "_id": "75480"} -{"city": "SULPHUR BLUFF", "loc": [-95.37396, 33.333379], "pop": 228, "state": "TX", "_id": "75481"} -{"city": "SULPHUR SPRINGS", "loc": [-95.592161, 33.134541], "pop": 21479, "state": "TX", "_id": "75482"} -{"city": "SUMNER", "loc": [-95.680671, 33.758941], "pop": 3596, "state": "TX", "_id": "75486"} -{"city": "TALCO", "loc": [-95.049718, 33.33436], "pop": 1932, "state": "TX", "_id": "75487"} -{"city": "TELEPHONE", "loc": [-96.044945, 33.797854], "pop": 709, "state": "TX", "_id": "75488"} -{"city": "TRENTON", "loc": [-96.339754, 33.423491], "pop": 1776, "state": "TX", "_id": "75490"} -{"city": "WHITEWRIGHT", "loc": [-96.451025, 33.519041], "pop": 4327, "state": "TX", "_id": "75491"} -{"city": "WINDOM", "loc": [-96.002002, 33.563295], "pop": 363, "state": "TX", "_id": "75492"} -{"city": "WINFIELD", "loc": [-95.078984, 33.161498], "pop": 1956, "state": "TX", "_id": "75493"} -{"city": "WINNSBORO", "loc": [-95.27265, 32.91462], "pop": 6204, "state": "TX", "_id": "75494"} -{"city": "VAN ALSTYNE", "loc": [-96.548632, 33.429169], "pop": 4251, "state": "TX", "_id": "75495"} -{"city": "WOLFE CITY", "loc": [-96.06907, 33.360479], "pop": 2225, "state": "TX", "_id": "75496"} -{"city": "YANTIS", "loc": [-95.531113, 32.925694], "pop": 1554, "state": "TX", "_id": "75497"} -{"city": "WAKE VILLAGE", "loc": [-94.118245, 33.407371], "pop": 40273, "state": "TX", "_id": "75501"} -{"city": "TEXARKANA", "loc": [-94.077374, 33.466906], "pop": 16443, "state": "TX", "_id": "75503"} -{"city": "ANNONA", "loc": [-94.899226, 33.553519], "pop": 949, "state": "TX", "_id": "75550"} -{"city": "ATLANTA", "loc": [-94.164617, 33.10898], "pop": 9024, "state": "TX", "_id": "75551"} -{"city": "AVERY", "loc": [-94.786708, 33.533935], "pop": 1317, "state": "TX", "_id": "75554"} -{"city": "BIVINS", "loc": [-94.140406, 32.96602], "pop": 1758, "state": "TX", "_id": "75555"} -{"city": "BLOOMBURG", "loc": [-94.064688, 33.133859], "pop": 855, "state": "TX", "_id": "75556"} -{"city": "COOKVILLE", "loc": [-94.873291, 33.181908], "pop": 2050, "state": "TX", "_id": "75558"} -{"city": "DE KALB", "loc": [-94.621092, 33.472741], "pop": 3364, "state": "TX", "_id": "75559"} -{"city": "DOUGLASSVILLE", "loc": [-94.346699, 33.175806], "pop": 1705, "state": "TX", "_id": "75560"} -{"city": "LEARY", "loc": [-94.269188, 33.477542], "pop": 5815, "state": "TX", "_id": "75561"} -{"city": "LINDEN", "loc": [-94.360509, 33.004821], "pop": 5727, "state": "TX", "_id": "75563"} -{"city": "MARIETTA", "loc": [-94.542098, 33.179618], "pop": 1526, "state": "TX", "_id": "75566"} -{"city": "MAUD", "loc": [-94.482267, 33.35504], "pop": 888, "state": "TX", "_id": "75567"} -{"city": "NAPLES", "loc": [-94.689061, 33.191186], "pop": 2752, "state": "TX", "_id": "75568"} -{"city": "NASH", "loc": [-94.142237, 33.439786], "pop": 3464, "state": "TX", "_id": "75569"} -{"city": "BOSTON", "loc": [-94.433882, 33.462119], "pop": 7801, "state": "TX", "_id": "75570"} -{"city": "OMAHA", "loc": [-94.763944, 33.180794], "pop": 1791, "state": "TX", "_id": "75571"} -{"city": "QUEEN CITY", "loc": [-94.154825, 33.18736], "pop": 4376, "state": "TX", "_id": "75572"} -{"city": "SIMMS", "loc": [-94.603862, 33.498727], "pop": 3617, "state": "TX", "_id": "75574"} -{"city": "LONGVIEW", "loc": [-94.72328, 32.526854], "pop": 27102, "state": "TX", "_id": "75601"} -{"city": "LONGVIEW", "loc": [-94.710078, 32.472373], "pop": 17399, "state": "TX", "_id": "75602"} -{"city": "LONGVIEW", "loc": [-94.711691, 32.426368], "pop": 6737, "state": "TX", "_id": "75603"} -{"city": "LONGVIEW", "loc": [-94.798957, 32.525139], "pop": 25570, "state": "TX", "_id": "75604"} -{"city": "LONGVIEW", "loc": [-94.776748, 32.554711], "pop": 9166, "state": "TX", "_id": "75605"} -{"city": "AVINGER", "loc": [-94.579534, 32.848514], "pop": 2764, "state": "TX", "_id": "75630"} -{"city": "BECKVILLE", "loc": [-94.455451, 32.245165], "pop": 3114, "state": "TX", "_id": "75631"} -{"city": "CARTHAGE", "loc": [-94.352721, 32.154379], "pop": 11062, "state": "TX", "_id": "75633"} -{"city": "DAINGERFIELD", "loc": [-94.735899, 33.031263], "pop": 5614, "state": "TX", "_id": "75638"} -{"city": "DE BERRY", "loc": [-94.13557, 32.254327], "pop": 4375, "state": "TX", "_id": "75639"} -{"city": "NEW DIANA", "loc": [-94.698122, 32.704585], "pop": 1930, "state": "TX", "_id": "75640"} -{"city": "GARY", "loc": [-94.281501, 32.019848], "pop": 1394, "state": "TX", "_id": "75643"} -{"city": "GILMER", "loc": [-94.971434, 32.724565], "pop": 10141, "state": "TX", "_id": "75644"} -{"city": "GLADEWATER", "loc": [-94.932001, 32.555858], "pop": 14791, "state": "TX", "_id": "75647"} -{"city": "HALLSVILLE", "loc": [-94.533308, 32.507283], "pop": 7463, "state": "TX", "_id": "75650"} -{"city": "HARLETON", "loc": [-94.465165, 32.657858], "pop": 2609, "state": "TX", "_id": "75651"} -{"city": "HENDERSON", "loc": [-94.791962, 32.152375], "pop": 19836, "state": "TX", "_id": "75652"} -{"city": "HUGHES SPRINGS", "loc": [-94.622758, 33.016763], "pop": 4564, "state": "TX", "_id": "75656"} -{"city": "SMITHLAND", "loc": [-94.371217, 32.779863], "pop": 8460, "state": "TX", "_id": "75657"} -{"city": "KARNACK", "loc": [-94.20005, 32.620508], "pop": 4434, "state": "TX", "_id": "75661"} -{"city": "KILGORE", "loc": [-94.865271, 32.383557], "pop": 22785, "state": "TX", "_id": "75662"} -{"city": "LANEVILLE", "loc": [-94.866027, 31.950821], "pop": 3032, "state": "TX", "_id": "75667"} -{"city": "LONE STAR", "loc": [-94.718303, 32.946982], "pop": 2578, "state": "TX", "_id": "75668"} -{"city": "LONG BRANCH", "loc": [-94.472028, 32.050224], "pop": 2101, "state": "TX", "_id": "75669"} -{"city": "MARSHALL", "loc": [-94.36191, 32.53378], "pop": 27482, "state": "TX", "_id": "75670"} -{"city": "MOUNT ENTERPRISE", "loc": [-94.623505, 31.9125], "pop": 2558, "state": "TX", "_id": "75681"} -{"city": "ORE CITY", "loc": [-94.751401, 32.785588], "pop": 5129, "state": "TX", "_id": "75683"} -{"city": "OVERTON", "loc": [-94.952914, 32.269041], "pop": 4776, "state": "TX", "_id": "75684"} -{"city": "PITTSBURG", "loc": [-94.960337, 32.96231], "pop": 12871, "state": "TX", "_id": "75686"} -{"city": "PRICE", "loc": [-94.941494, 32.100842], "pop": 945, "state": "TX", "_id": "75687"} -{"city": "TURNERTOWN", "loc": [-94.950805, 32.18734], "pop": 923, "state": "TX", "_id": "75689"} -{"city": "TATUM", "loc": [-94.596027, 32.326569], "pop": 4679, "state": "TX", "_id": "75691"} -{"city": "WASKOM", "loc": [-94.137884, 32.467183], "pop": 6516, "state": "TX", "_id": "75692"} -{"city": "CLARKSVILLE CITY", "loc": [-94.862115, 32.537232], "pop": 5887, "state": "TX", "_id": "75693"} -{"city": "TYLER", "loc": [-95.292179, 32.325366], "pop": 30794, "state": "TX", "_id": "75701"} -{"city": "TYLER", "loc": [-95.311652, 32.361969], "pop": 24885, "state": "TX", "_id": "75702"} -{"city": "TYLER", "loc": [-95.303147, 32.276827], "pop": 26345, "state": "TX", "_id": "75703"} -{"city": "TYLER", "loc": [-95.406977, 32.373781], "pop": 6100, "state": "TX", "_id": "75704"} -{"city": "TYLER", "loc": [-95.125225, 32.376599], "pop": 1539, "state": "TX", "_id": "75705"} -{"city": "TYLER", "loc": [-95.330993, 32.444148], "pop": 6770, "state": "TX", "_id": "75706"} -{"city": "TYLER", "loc": [-95.192692, 32.303782], "pop": 9853, "state": "TX", "_id": "75707"} -{"city": "EAST TEXAS CENTE", "loc": [-95.244354, 32.389193], "pop": 4338, "state": "TX", "_id": "75708"} -{"city": "TYLER", "loc": [-95.395563, 32.307817], "pop": 1737, "state": "TX", "_id": "75709"} -{"city": "ARP", "loc": [-95.063908, 32.241758], "pop": 1815, "state": "TX", "_id": "75750"} -{"city": "ATHENS", "loc": [-95.84318, 32.193499], "pop": 18579, "state": "TX", "_id": "75751"} -{"city": "BEN WHEELER", "loc": [-95.637085, 32.412588], "pop": 3992, "state": "TX", "_id": "75754"} -{"city": "BIG SANDY", "loc": [-95.088034, 32.61682], "pop": 3997, "state": "TX", "_id": "75755"} -{"city": "EDOM", "loc": [-95.62289, 32.290514], "pop": 2868, "state": "TX", "_id": "75756"} -{"city": "MOUNT SELMAN", "loc": [-95.375045, 32.135745], "pop": 4654, "state": "TX", "_id": "75757"} -{"city": "CHANDLER", "loc": [-95.502531, 32.270638], "pop": 4835, "state": "TX", "_id": "75758"} -{"city": "CUSHING", "loc": [-94.853887, 31.797767], "pop": 2068, "state": "TX", "_id": "75760"} -{"city": "FLINT", "loc": [-95.394848, 32.207927], "pop": 6365, "state": "TX", "_id": "75762"} -{"city": "FRANKSTON", "loc": [-95.516284, 32.053488], "pop": 5394, "state": "TX", "_id": "75763"} -{"city": "HAWKINS", "loc": [-95.222015, 32.643901], "pop": 4968, "state": "TX", "_id": "75765"} -{"city": "JACKSONVILLE", "loc": [-95.270328, 31.96177], "pop": 19652, "state": "TX", "_id": "75766"} -{"city": "LARUE", "loc": [-95.592661, 32.160758], "pop": 2826, "state": "TX", "_id": "75770"} -{"city": "MT SYLVAN", "loc": [-95.429932, 32.517152], "pop": 9838, "state": "TX", "_id": "75771"} -{"city": "MINEOLA", "loc": [-95.487032, 32.666059], "pop": 8904, "state": "TX", "_id": "75773"} -{"city": "MURCHISON", "loc": [-95.77372, 32.325732], "pop": 1801, "state": "TX", "_id": "75778"} -{"city": "QUITMAN", "loc": [-95.430161, 32.804862], "pop": 5864, "state": "TX", "_id": "75783"} -{"city": "REKLAW", "loc": [-95.011834, 31.885858], "pop": 334, "state": "TX", "_id": "75784"} -{"city": "DIALVILLE", "loc": [-95.173156, 31.80976], "pop": 10527, "state": "TX", "_id": "75785"} -{"city": "TROUP", "loc": [-95.122656, 32.104003], "pop": 8363, "state": "TX", "_id": "75789"} -{"city": "VAN", "loc": [-95.654538, 32.528265], "pop": 4019, "state": "TX", "_id": "75790"} -{"city": "WHITEHOUSE", "loc": [-95.226552, 32.221958], "pop": 8273, "state": "TX", "_id": "75791"} -{"city": "WINONA", "loc": [-95.124624, 32.466163], "pop": 2825, "state": "TX", "_id": "75792"} -{"city": "PALESTINE", "loc": [-95.634158, 31.758752], "pop": 26466, "state": "TX", "_id": "75801"} -{"city": "FREESTONE", "loc": [-96.058516, 31.457151], "pop": 3404, "state": "TX", "_id": "75831"} -{"city": "CENTERVILLE", "loc": [-95.921287, 31.272025], "pop": 2618, "state": "TX", "_id": "75833"} -{"city": "AUSTONIO", "loc": [-95.46833, 31.315067], "pop": 11698, "state": "TX", "_id": "75835"} -{"city": "DONIE", "loc": [-96.238687, 31.487285], "pop": 334, "state": "TX", "_id": "75838"} -{"city": "SLOCUM", "loc": [-95.553232, 31.635087], "pop": 4767, "state": "TX", "_id": "75839"} -{"city": "FAIRFIELD", "loc": [-96.157176, 31.736136], "pop": 6331, "state": "TX", "_id": "75840"} -{"city": "GRAPELAND", "loc": [-95.444713, 31.49721], "pop": 4705, "state": "TX", "_id": "75844"} -{"city": "GROVETON", "loc": [-95.096894, 31.065142], "pop": 2206, "state": "TX", "_id": "75845"} -{"city": "JEWETT", "loc": [-96.191841, 31.373925], "pop": 2112, "state": "TX", "_id": "75846"} -{"city": "KENNARD", "loc": [-95.154118, 31.338449], "pop": 1784, "state": "TX", "_id": "75847"} -{"city": "LEONA", "loc": [-95.928428, 31.14207], "pop": 475, "state": "TX", "_id": "75850"} -{"city": "LOVELADY", "loc": [-95.550057, 31.0564], "pop": 3649, "state": "TX", "_id": "75851"} -{"city": "MIDWAY", "loc": [-95.70894, 30.980579], "pop": 3522, "state": "TX", "_id": "75852"} -{"city": "MONTALBA", "loc": [-95.75926, 31.922165], "pop": 2070, "state": "TX", "_id": "75853"} -{"city": "OAKWOOD", "loc": [-95.902151, 31.602312], "pop": 2637, "state": "TX", "_id": "75855"} -{"city": "PENNINGTON", "loc": [-95.158678, 31.161843], "pop": 565, "state": "TX", "_id": "75856"} -{"city": "STREETMAN", "loc": [-96.298763, 31.888532], "pop": 488, "state": "TX", "_id": "75859"} -{"city": "TEAGUE", "loc": [-96.27778, 31.632772], "pop": 5318, "state": "TX", "_id": "75860"} -{"city": "TENNESSEE COLONY", "loc": [-95.899798, 31.792882], "pop": 11380, "state": "TX", "_id": "75861"} -{"city": "TRINITY", "loc": [-95.340295, 30.941951], "pop": 7585, "state": "TX", "_id": "75862"} -{"city": "KELTYS", "loc": [-94.734185, 31.336004], "pop": 46763, "state": "TX", "_id": "75901"} -{"city": "FOREST", "loc": [-95.079786, 31.647815], "pop": 3203, "state": "TX", "_id": "75925"} -{"city": "APPLE SPRINGS", "loc": [-94.981152, 31.226923], "pop": 1079, "state": "TX", "_id": "75926"} -{"city": "BON WIER", "loc": [-93.766465, 30.687557], "pop": 2283, "state": "TX", "_id": "75928"} -{"city": "BROADDUS", "loc": [-94.215552, 31.295241], "pop": 1786, "state": "TX", "_id": "75929"} -{"city": "BRONSON", "loc": [-93.999256, 31.339056], "pop": 1922, "state": "TX", "_id": "75930"} -{"city": "BROOKELAND", "loc": [-94.003566, 31.106273], "pop": 1883, "state": "TX", "_id": "75931"} -{"city": "BURKEVILLE", "loc": [-93.658517, 31.009934], "pop": 2275, "state": "TX", "_id": "75932"} -{"city": "CALL", "loc": [-93.833355, 30.574132], "pop": 1813, "state": "TX", "_id": "75933"} -{"city": "CENTER", "loc": [-94.186947, 31.786468], "pop": 11512, "state": "TX", "_id": "75935"} -{"city": "CHESTER", "loc": [-94.458099, 30.928532], "pop": 1588, "state": "TX", "_id": "75936"} -{"city": "CHIRENO", "loc": [-94.430244, 31.511935], "pop": 3983, "state": "TX", "_id": "75937"} -{"city": "ROCKLAND", "loc": [-94.421811, 30.909226], "pop": 836, "state": "TX", "_id": "75938"} -{"city": "BARNUM", "loc": [-94.7959, 31.000657], "pop": 3969, "state": "TX", "_id": "75939"} -{"city": "DIBOLL", "loc": [-94.772911, 31.195028], "pop": 7686, "state": "TX", "_id": "75941"} -{"city": "DOUGLASS", "loc": [-94.869649, 31.657846], "pop": 1005, "state": "TX", "_id": "75943"} -{"city": "GARRISON", "loc": [-94.526604, 31.811111], "pop": 2365, "state": "TX", "_id": "75946"} -{"city": "HEMPHILL", "loc": [-93.79045, 31.316123], "pop": 4180, "state": "TX", "_id": "75948"} -{"city": "HUNTINGTON", "loc": [-94.566237, 31.283714], "pop": 8415, "state": "TX", "_id": "75949"} -{"city": "SAM RAYBURN", "loc": [-94.021481, 30.925348], "pop": 14219, "state": "TX", "_id": "75951"} -{"city": "JOAQUIN", "loc": [-94.060833, 31.943989], "pop": 2754, "state": "TX", "_id": "75954"} -{"city": "BON AMI", "loc": [-93.927934, 30.688217], "pop": 5772, "state": "TX", "_id": "75956"} -{"city": "MAGNOLIA SPRINGS", "loc": [-94.070896, 30.762882], "pop": 111, "state": "TX", "_id": "75957"} -{"city": "MILAM", "loc": [-93.831816, 31.47001], "pop": 2187, "state": "TX", "_id": "75959"} -{"city": "MOSCOW", "loc": [-94.85437, 30.917902], "pop": 1018, "state": "TX", "_id": "75960"} -{"city": "APPLEBY", "loc": [-94.651093, 31.618534], "pop": 45332, "state": "TX", "_id": "75961"} -{"city": "NEWTON", "loc": [-93.7497, 30.835074], "pop": 4184, "state": "TX", "_id": "75966"} -{"city": "PINELAND", "loc": [-93.97542, 31.241782], "pop": 1279, "state": "TX", "_id": "75968"} -{"city": "POLLOK", "loc": [-94.825403, 31.429107], "pop": 4725, "state": "TX", "_id": "75969"} -{"city": "SAN AUGUSTINE", "loc": [-94.132581, 31.515173], "pop": 5916, "state": "TX", "_id": "75972"} -{"city": "SHELBYVILLE", "loc": [-93.969841, 31.713074], "pop": 2949, "state": "TX", "_id": "75973"} -{"city": "TENAHA", "loc": [-94.248773, 31.940812], "pop": 1759, "state": "TX", "_id": "75974"} -{"city": "TIMPSON", "loc": [-94.396733, 31.884089], "pop": 3060, "state": "TX", "_id": "75975"} -{"city": "WELLS", "loc": [-94.969351, 31.499755], "pop": 1550, "state": "TX", "_id": "75976"} -{"city": "WIERGATE", "loc": [-93.803854, 31.041417], "pop": 290, "state": "TX", "_id": "75977"} -{"city": "DOGWOOD", "loc": [-94.425494, 30.775133], "pop": 5942, "state": "TX", "_id": "75979"} -{"city": "ZAVALLA", "loc": [-94.387115, 31.156863], "pop": 2295, "state": "TX", "_id": "75980"} -{"city": "ARLINGTON", "loc": [-97.083425, 32.778494], "pop": 18003, "state": "TX", "_id": "76006"} -{"city": "ALEDO", "loc": [-97.603885, 32.700351], "pop": 5148, "state": "TX", "_id": "76008"} -{"city": "ALVARADO", "loc": [-97.212971, 32.439499], "pop": 13229, "state": "TX", "_id": "76009"} -{"city": "ARLINGTON", "loc": [-97.082576, 32.720368], "pop": 42405, "state": "TX", "_id": "76010"} -{"city": "ARLINGTON", "loc": [-97.100302, 32.758236], "pop": 23943, "state": "TX", "_id": "76011"} -{"city": "ARLINGTON", "loc": [-97.134808, 32.753962], "pop": 24141, "state": "TX", "_id": "76012"} -{"city": "ARLINGTON", "loc": [-97.14416, 32.719905], "pop": 30252, "state": "TX", "_id": "76013"} -{"city": "ARLINGTON", "loc": [-97.087556, 32.695425], "pop": 26087, "state": "TX", "_id": "76014"} -{"city": "ARLINGTON", "loc": [-97.134685, 32.693125], "pop": 14544, "state": "TX", "_id": "76015"} -{"city": "ARLINGTON", "loc": [-97.190466, 32.688898], "pop": 28219, "state": "TX", "_id": "76016"} -{"city": "ARLINGTON", "loc": [-97.159899, 32.65545], "pop": 42829, "state": "TX", "_id": "76017"} -{"city": "ARLINGTON", "loc": [-97.091987, 32.654752], "pop": 15590, "state": "TX", "_id": "76018"} -{"city": "AZLE", "loc": [-97.541153, 32.903453], "pop": 18198, "state": "TX", "_id": "76020"} -{"city": "BEDFORD", "loc": [-97.135797, 32.853579], "pop": 31798, "state": "TX", "_id": "76021"} -{"city": "BEDFORD", "loc": [-97.145351, 32.829749], "pop": 13186, "state": "TX", "_id": "76022"} -{"city": "BOYD", "loc": [-97.586797, 33.059367], "pop": 4292, "state": "TX", "_id": "76023"} -{"city": "BURLESON", "loc": [-97.308959, 32.531624], "pop": 33535, "state": "TX", "_id": "76028"} -{"city": "CLEBURNE", "loc": [-97.397957, 32.342891], "pop": 25844, "state": "TX", "_id": "76031"} -{"city": "COLLEYVILLE", "loc": [-97.146026, 32.88721], "pop": 11726, "state": "TX", "_id": "76034"} -{"city": "CRESSON", "loc": [-97.651931, 32.530714], "pop": 265, "state": "TX", "_id": "76035"} -{"city": "CROWLEY", "loc": [-97.370306, 32.581398], "pop": 8749, "state": "TX", "_id": "76036"} -{"city": "EULESS", "loc": [-97.083212, 32.858172], "pop": 22412, "state": "TX", "_id": "76039"} -{"city": "EULESS", "loc": [-97.097203, 32.826358], "pop": 17220, "state": "TX", "_id": "76040"} -{"city": "FORRESTON", "loc": [-96.887522, 32.281131], "pop": 106, "state": "TX", "_id": "76041"} -{"city": "GLEN ROSE", "loc": [-97.762911, 32.229762], "pop": 4432, "state": "TX", "_id": "76043"} -{"city": "GODLEY", "loc": [-97.534865, 32.428174], "pop": 2595, "state": "TX", "_id": "76044"} -{"city": "GRANBURY", "loc": [-97.774173, 32.42505], "pop": 13718, "state": "TX", "_id": "76048"} -{"city": "GRANBURY", "loc": [-97.728484, 32.448811], "pop": 10830, "state": "TX", "_id": "76049"} -{"city": "GRANDVIEW", "loc": [-97.235069, 32.277856], "pop": 4850, "state": "TX", "_id": "76050"} -{"city": "GRAPEVINE", "loc": [-97.096203, 32.93143], "pop": 30774, "state": "TX", "_id": "76051"} -{"city": "HASLET", "loc": [-97.337185, 32.955734], "pop": 866, "state": "TX", "_id": "76052"} -{"city": "HURST", "loc": [-97.175613, 32.821107], "pop": 24835, "state": "TX", "_id": "76053"} -{"city": "HURST", "loc": [-97.175521, 32.855832], "pop": 9953, "state": "TX", "_id": "76054"} -{"city": "ITASCA", "loc": [-97.146034, 32.163589], "pop": 2341, "state": "TX", "_id": "76055"} -{"city": "JOSHUA", "loc": [-97.401123, 32.466252], "pop": 12544, "state": "TX", "_id": "76058"} -{"city": "KEENE", "loc": [-97.32868, 32.393659], "pop": 5863, "state": "TX", "_id": "76059"} -{"city": "KENNEDALE", "loc": [-97.213853, 32.64316], "pop": 5362, "state": "TX", "_id": "76060"} -{"city": "MANSFIELD", "loc": [-97.141551, 32.577258], "pop": 17381, "state": "TX", "_id": "76063"} -{"city": "MAYPEARL", "loc": [-96.98803, 32.327878], "pop": 2842, "state": "TX", "_id": "76064"} -{"city": "MIDLOTHIAN", "loc": [-96.993551, 32.475743], "pop": 10271, "state": "TX", "_id": "76065"} -{"city": "MILLSAP", "loc": [-97.878386, 32.670019], "pop": 9110, "state": "TX", "_id": "76066"} -{"city": "MINERAL WELLS", "loc": [-98.063051, 32.810283], "pop": 23617, "state": "TX", "_id": "76067"} -{"city": "NEMO", "loc": [-97.656668, 32.271312], "pop": 206, "state": "TX", "_id": "76070"} -{"city": "NEWARK", "loc": [-97.510696, 33.007099], "pop": 2670, "state": "TX", "_id": "76071"} -{"city": "PARADISE", "loc": [-97.697423, 33.082607], "pop": 3009, "state": "TX", "_id": "76073"} -{"city": "RAINBOW", "loc": [-97.70652, 32.281216], "pop": 722, "state": "TX", "_id": "76077"} -{"city": "RHOME", "loc": [-97.481688, 33.054045], "pop": 1771, "state": "TX", "_id": "76078"} -{"city": "SPRINGTOWN", "loc": [-97.634951, 32.966021], "pop": 7377, "state": "TX", "_id": "76082"} -{"city": "VENUS", "loc": [-97.108734, 32.432975], "pop": 2726, "state": "TX", "_id": "76084"} -{"city": "WEATHERFORD", "loc": [-97.738591, 32.784074], "pop": 29937, "state": "TX", "_id": "76086"} -{"city": "WEATHERFORD", "loc": [-97.689439, 32.749473], "pop": 2502, "state": "TX", "_id": "76087"} -{"city": "GRAPEVINE", "loc": [-97.148066, 32.956456], "pop": 5638, "state": "TX", "_id": "76092"} -{"city": "RIO VISTA", "loc": [-97.367825, 32.253168], "pop": 1819, "state": "TX", "_id": "76093"} -{"city": "FORT WORTH", "loc": [-97.328023, 32.758897], "pop": 8550, "state": "TX", "_id": "76102"} -{"city": "FORT WORTH", "loc": [-97.260394, 32.747005], "pop": 12611, "state": "TX", "_id": "76103"} -{"city": "FORT WORTH", "loc": [-97.318409, 32.725551], "pop": 20012, "state": "TX", "_id": "76104"} -{"city": "FORT WORTH", "loc": [-97.26899, 32.723325], "pop": 20947, "state": "TX", "_id": "76105"} -{"city": "FORT WORTH", "loc": [-97.356008, 32.796849], "pop": 44367, "state": "TX", "_id": "76106"} -{"city": "FORT WORTH", "loc": [-97.385248, 32.739175], "pop": 27082, "state": "TX", "_id": "76107"} -{"city": "WHITE SETTLEMENT", "loc": [-97.474063, 32.759271], "pop": 22510, "state": "TX", "_id": "76108"} -{"city": "FORT WORTH", "loc": [-97.378876, 32.700246], "pop": 21893, "state": "TX", "_id": "76109"} -{"city": "FORT WORTH", "loc": [-97.337505, 32.706505], "pop": 27828, "state": "TX", "_id": "76110"} -{"city": "FORT WORTH", "loc": [-97.300327, 32.782382], "pop": 17740, "state": "TX", "_id": "76111"} -{"city": "FORT WORTH", "loc": [-97.218122, 32.749297], "pop": 35311, "state": "TX", "_id": "76112"} -{"city": "RIVER OAKS", "loc": [-97.401526, 32.775379], "pop": 21921, "state": "TX", "_id": "76114"} -{"city": "FORT WORTH", "loc": [-97.333634, 32.679618], "pop": 16544, "state": "TX", "_id": "76115"} -{"city": "FORT WORTH", "loc": [-97.448279, 32.723032], "pop": 38210, "state": "TX", "_id": "76116"} -{"city": "HALTOM CITY", "loc": [-97.270891, 32.808742], "pop": 27312, "state": "TX", "_id": "76117"} -{"city": "NORTH RICHLAND H", "loc": [-97.222781, 32.808944], "pop": 9764, "state": "TX", "_id": "76118"} -{"city": "FORT WORTH", "loc": [-97.267492, 32.691379], "pop": 36951, "state": "TX", "_id": "76119"} -{"city": "FORT WORTH", "loc": [-97.178112, 32.763912], "pop": 8601, "state": "TX", "_id": "76120"} -{"city": "FORT WORTH", "loc": [-97.365838, 32.625361], "pop": 5314, "state": "TX", "_id": "76123"} -{"city": "BENBROOK", "loc": [-97.464141, 32.670023], "pop": 14301, "state": "TX", "_id": "76126"} -{"city": "CARSWELL AFB", "loc": [-97.435453, 32.771846], "pop": 940, "state": "TX", "_id": "76127"} -{"city": "FORT WORTH", "loc": [-97.337656, 32.863156], "pop": 3738, "state": "TX", "_id": "76131"} -{"city": "FORT WORTH", "loc": [-97.405626, 32.671092], "pop": 13724, "state": "TX", "_id": "76132"} -{"city": "FORT WORTH", "loc": [-97.375849, 32.652561], "pop": 44032, "state": "TX", "_id": "76133"} -{"city": "FORT WORTH", "loc": [-97.332467, 32.646886], "pop": 17442, "state": "TX", "_id": "76134"} -{"city": "FORT WORTH", "loc": [-97.45191, 32.824844], "pop": 13135, "state": "TX", "_id": "76135"} -{"city": "FORT WORTH", "loc": [-97.289114, 32.866421], "pop": 15226, "state": "TX", "_id": "76137"} -{"city": "EVERMAN", "loc": [-97.270406, 32.631332], "pop": 17658, "state": "TX", "_id": "76140"} -{"city": "WATAUGA", "loc": [-97.249029, 32.8681], "pop": 22794, "state": "TX", "_id": "76148"} -{"city": "FORT WORTH", "loc": [-97.050285, 32.824742], "pop": 2096, "state": "TX", "_id": "76155"} -{"city": "FORT WORTH", "loc": [-97.332671, 32.901017], "pop": 61, "state": "TX", "_id": "76177"} -{"city": "SAGINAW", "loc": [-97.403149, 32.872961], "pop": 15143, "state": "TX", "_id": "76179"} -{"city": "NORTH RICHLAND H", "loc": [-97.220714, 32.853966], "pop": 44496, "state": "TX", "_id": "76180"} -{"city": "DENTON", "loc": [-97.131436, 33.22893], "pop": 48643, "state": "TX", "_id": "76201"} -{"city": "DENTON", "loc": [-97.101833, 33.180106], "pop": 27830, "state": "TX", "_id": "76205"} -{"city": "ALVORD", "loc": [-97.688488, 33.36982], "pop": 1748, "state": "TX", "_id": "76225"} -{"city": "ARGYLE", "loc": [-97.159977, 33.106244], "pop": 4420, "state": "TX", "_id": "76226"} -{"city": "AUBREY", "loc": [-96.987866, 33.291997], "pop": 3089, "state": "TX", "_id": "76227"} -{"city": "BELLEVUE", "loc": [-98.157373, 33.58789], "pop": 1697, "state": "TX", "_id": "76228"} -{"city": "BOWIE", "loc": [-97.837337, 33.556796], "pop": 8686, "state": "TX", "_id": "76230"} -{"city": "COLLINSVILLE", "loc": [-96.901365, 33.558012], "pop": 1681, "state": "TX", "_id": "76233"} -{"city": "DECATUR", "loc": [-97.573995, 33.235077], "pop": 9323, "state": "TX", "_id": "76234"} -{"city": "ERA", "loc": [-97.292358, 33.50101], "pop": 264, "state": "TX", "_id": "76238"} -{"city": "FORESTBURG", "loc": [-97.584774, 33.539778], "pop": 805, "state": "TX", "_id": "76239"} -{"city": "LAKE KIOWA", "loc": [-97.103208, 33.625943], "pop": 24108, "state": "TX", "_id": "76240"} -{"city": "GORDONVILLE", "loc": [-96.84027, 33.834283], "pop": 1664, "state": "TX", "_id": "76245"} -{"city": "JUSTIN", "loc": [-97.309254, 33.073375], "pop": 3422, "state": "TX", "_id": "76247"} -{"city": "KELLER", "loc": [-97.248883, 32.927556], "pop": 14313, "state": "TX", "_id": "76248"} -{"city": "KRUM", "loc": [-97.267452, 33.27337], "pop": 3198, "state": "TX", "_id": "76249"} -{"city": "LINDSAY", "loc": [-97.221436, 33.63601], "pop": 610, "state": "TX", "_id": "76250"} -{"city": "MONTAGUE", "loc": [-97.728226, 33.663899], "pop": 617, "state": "TX", "_id": "76251"} -{"city": "MUENSTER", "loc": [-97.362409, 33.659549], "pop": 3042, "state": "TX", "_id": "76252"} -{"city": "NOCONA", "loc": [-97.726982, 33.798163], "pop": 4365, "state": "TX", "_id": "76255"} -{"city": "PILOT POINT", "loc": [-96.944554, 33.370983], "pop": 4183, "state": "TX", "_id": "76258"} -{"city": "PONDER", "loc": [-97.284815, 33.177383], "pop": 1443, "state": "TX", "_id": "76259"} -{"city": "RINGGOLD", "loc": [-97.943982, 33.816392], "pop": 243, "state": "TX", "_id": "76261"} -{"city": "TROPHY CLUB", "loc": [-97.205346, 32.98639], "pop": 11484, "state": "TX", "_id": "76262"} -{"city": "ROSSTON", "loc": [-97.454172, 33.483795], "pop": 30, "state": "TX", "_id": "76263"} -{"city": "SADLER", "loc": [-96.840017, 33.730989], "pop": 349, "state": "TX", "_id": "76264"} -{"city": "SAINT JO", "loc": [-97.556756, 33.744024], "pop": 2071, "state": "TX", "_id": "76265"} -{"city": "SANGER", "loc": [-97.181432, 33.356266], "pop": 7440, "state": "TX", "_id": "76266"} -{"city": "SUNSET", "loc": [-97.770891, 33.453909], "pop": 487, "state": "TX", "_id": "76270"} -{"city": "TIOGA", "loc": [-96.909712, 33.467493], "pop": 857, "state": "TX", "_id": "76271"} -{"city": "VALLEY VIEW", "loc": [-97.231053, 33.502166], "pop": 2754, "state": "TX", "_id": "76272"} -{"city": "WHITESBORO", "loc": [-96.878984, 33.659021], "pop": 5920, "state": "TX", "_id": "76273"} -{"city": "WICHITA FALLS", "loc": [-98.497645, 33.905284], "pop": 15309, "state": "TX", "_id": "76301"} -{"city": "WICHITA FALLS", "loc": [-98.493987, 33.864278], "pop": 10724, "state": "TX", "_id": "76302"} -{"city": "WICHITA FALLS", "loc": [-98.460812, 33.899837], "pop": 3922, "state": "TX", "_id": "76303"} -{"city": "WICHITA FALLS", "loc": [-98.500491, 33.930806], "pop": 4529, "state": "TX", "_id": "76304"} -{"city": "WICHITA FALLS", "loc": [-98.540679, 33.937345], "pop": 8522, "state": "TX", "_id": "76305"} -{"city": "WICHITA FALLS", "loc": [-98.524835, 33.974595], "pop": 6808, "state": "TX", "_id": "76306"} -{"city": "WICHITA FALLS", "loc": [-98.533965, 33.863258], "pop": 19151, "state": "TX", "_id": "76308"} -{"city": "WICHITA FALLS", "loc": [-98.534288, 33.893084], "pop": 12500, "state": "TX", "_id": "76309"} -{"city": "WICHITA FALLS", "loc": [-98.575548, 33.858122], "pop": 11497, "state": "TX", "_id": "76310"} -{"city": "SHEPPARD AFB", "loc": [-98.508771, 33.982353], "pop": 7080, "state": "TX", "_id": "76311"} -{"city": "76350", "loc": [-98.371578, 33.431472], "pop": 76, "state": "TX", "_id": "76350"} -{"city": "BURKBURNETT", "loc": [-98.570842, 34.085989], "pop": 10558, "state": "TX", "_id": "76354"} -{"city": "BYERS", "loc": [-98.183929, 34.072812], "pop": 665, "state": "TX", "_id": "76357"} -{"city": "ELBERT", "loc": [-99.055214, 33.015687], "pop": 445, "state": "TX", "_id": "76359"} -{"city": "ELECTRA", "loc": [-98.91545, 34.036234], "pop": 3580, "state": "TX", "_id": "76360"} -{"city": "GOREE", "loc": [-99.525806, 33.474832], "pop": 523, "state": "TX", "_id": "76363"} -{"city": "HARROLD", "loc": [-99.035052, 34.097097], "pop": 369, "state": "TX", "_id": "76364"} -{"city": "HENRIETTA", "loc": [-98.259976, 33.819609], "pop": 6020, "state": "TX", "_id": "76365"} -{"city": "HOLLIDAY", "loc": [-98.657634, 33.675141], "pop": 5949, "state": "TX", "_id": "76366"} -{"city": "IOWA PARK", "loc": [-98.674497, 33.94235], "pop": 9443, "state": "TX", "_id": "76367"} -{"city": "MUNDAY", "loc": [-99.632624, 33.456088], "pop": 2028, "state": "TX", "_id": "76371"} -{"city": "NEWCASTLE", "loc": [-98.744644, 33.190103], "pop": 905, "state": "TX", "_id": "76372"} -{"city": "OKLAUNION", "loc": [-99.160234, 34.120372], "pop": 338, "state": "TX", "_id": "76373"} -{"city": "OLNEY", "loc": [-98.742695, 33.360135], "pop": 4554, "state": "TX", "_id": "76374"} -{"city": "PETROLIA", "loc": [-98.269223, 34.027331], "pop": 1642, "state": "TX", "_id": "76377"} -{"city": "76378", "loc": [-99.40547, 33.611763], "pop": 177, "state": "TX", "_id": "76378"} -{"city": "SCOTLAND", "loc": [-98.464983, 33.653486], "pop": 398, "state": "TX", "_id": "76379"} -{"city": "SEYMOUR", "loc": [-99.258727, 33.591445], "pop": 4208, "state": "TX", "_id": "76380"} -{"city": "VERA", "loc": [-99.759098, 33.615469], "pop": 481, "state": "TX", "_id": "76383"} -{"city": "VERNON", "loc": [-99.30301, 34.149135], "pop": 14414, "state": "TX", "_id": "76384"} -{"city": "WEINERT", "loc": [-99.666431, 33.324872], "pop": 301, "state": "TX", "_id": "76388"} -{"city": "WINDTHORST", "loc": [-98.437589, 33.57957], "pop": 381, "state": "TX", "_id": "76389"} -{"city": "STEPHENVILLE", "loc": [-98.222407, 32.221372], "pop": 19745, "state": "TX", "_id": "76401"} -{"city": "BRECKENRIDGE", "loc": [-98.909882, 32.753166], "pop": 8803, "state": "TX", "_id": "76424"} -{"city": "BRIDGEPORT", "loc": [-97.78098, 33.187027], "pop": 9158, "state": "TX", "_id": "76426"} -{"city": "BRYSON", "loc": [-98.370256, 33.15947], "pop": 915, "state": "TX", "_id": "76427"} -{"city": "CADDO", "loc": [-98.658983, 32.688617], "pop": 127, "state": "TX", "_id": "76429"} -{"city": "ALBANY", "loc": [-99.319581, 32.719005], "pop": 2858, "state": "TX", "_id": "76430"} -{"city": "CHICO", "loc": [-97.803133, 33.319315], "pop": 2657, "state": "TX", "_id": "76431"} -{"city": "BLANKET", "loc": [-98.831089, 31.78819], "pop": 1929, "state": "TX", "_id": "76432"} -{"city": "BLUFF DALE", "loc": [-98.163775, 32.401791], "pop": 1751, "state": "TX", "_id": "76433"} -{"city": "CARBON", "loc": [-98.83479, 32.270125], "pop": 462, "state": "TX", "_id": "76435"} -{"city": "CARLTON", "loc": [-98.152519, 31.911438], "pop": 235, "state": "TX", "_id": "76436"} -{"city": "CISCO", "loc": [-98.986507, 32.380043], "pop": 4906, "state": "TX", "_id": "76437"} -{"city": "COMANCHE", "loc": [-98.608227, 31.911637], "pop": 7208, "state": "TX", "_id": "76442"} -{"city": "CROSS PLAINS", "loc": [-99.18718, 32.148159], "pop": 1928, "state": "TX", "_id": "76443"} -{"city": "DE LEON", "loc": [-98.54894, 32.108742], "pop": 4401, "state": "TX", "_id": "76444"} -{"city": "DESDEMONA", "loc": [-98.567327, 32.281877], "pop": 366, "state": "TX", "_id": "76445"} -{"city": "DUBLIN", "loc": [-98.345469, 32.090873], "pop": 5029, "state": "TX", "_id": "76446"} -{"city": "76447", "loc": [-98.067535, 32.081163], "pop": 1466, "state": "TX", "_id": "76447"} -{"city": "EASTLAND", "loc": [-98.807101, 32.399418], "pop": 5837, "state": "TX", "_id": "76448"} -{"city": "GRAFORD", "loc": [-98.337002, 32.924192], "pop": 2235, "state": "TX", "_id": "76449"} -{"city": "GRAHAM", "loc": [-98.583212, 33.099283], "pop": 12511, "state": "TX", "_id": "76450"} -{"city": "GORDON", "loc": [-98.363211, 32.547828], "pop": 566, "state": "TX", "_id": "76453"} -{"city": "GORMAN", "loc": [-98.683408, 32.223441], "pop": 1773, "state": "TX", "_id": "76454"} -{"city": "GUSTINE", "loc": [-98.383488, 31.872448], "pop": 1680, "state": "TX", "_id": "76455"} -{"city": "HICO", "loc": [-98.024933, 31.959718], "pop": 1962, "state": "TX", "_id": "76457"} -{"city": "JACKSBORO", "loc": [-98.168138, 33.234655], "pop": 4664, "state": "TX", "_id": "76458"} -{"city": "JERMYN", "loc": [-98.393149, 33.263554], "pop": 154, "state": "TX", "_id": "76459"} -{"city": "LOVING", "loc": [-98.50237, 33.26886], "pop": 156, "state": "TX", "_id": "76460"} -{"city": "LIPAN", "loc": [-97.953614, 32.507218], "pop": 1582, "state": "TX", "_id": "76462"} -{"city": "MINGUS", "loc": [-98.426262, 32.562665], "pop": 278, "state": "TX", "_id": "76463"} -{"city": "MORAN", "loc": [-99.165567, 32.554909], "pop": 458, "state": "TX", "_id": "76464"} -{"city": "RANGER", "loc": [-98.674659, 32.46809], "pop": 3414, "state": "TX", "_id": "76470"} -{"city": "RISING STAR", "loc": [-98.985852, 32.127986], "pop": 1810, "state": "TX", "_id": "76471"} -{"city": "SANTO", "loc": [-98.179675, 32.597935], "pop": 1433, "state": "TX", "_id": "76472"} -{"city": "SIDNEY", "loc": [-98.767995, 31.932031], "pop": 92, "state": "TX", "_id": "76474"} -{"city": "STRAWN", "loc": [-98.499467, 32.5945], "pop": 968, "state": "TX", "_id": "76475"} -{"city": "TOLAR", "loc": [-97.880208, 32.377246], "pop": 2586, "state": "TX", "_id": "76476"} -{"city": "THROCKMORTON", "loc": [-99.183812, 33.179446], "pop": 1321, "state": "TX", "_id": "76483"} -{"city": "PALO PINTO", "loc": [-98.270262, 32.725315], "pop": 867, "state": "TX", "_id": "76484"} -{"city": "PERRIN", "loc": [-98.044006, 33.058453], "pop": 1065, "state": "TX", "_id": "76486"} -{"city": "POOLVILLE", "loc": [-97.847229, 32.968023], "pop": 1549, "state": "TX", "_id": "76487"} -{"city": "WHITT", "loc": [-98.021008, 32.955459], "pop": 305, "state": "TX", "_id": "76490"} -{"city": "WOODSON", "loc": [-99.015953, 33.301972], "pop": 114, "state": "TX", "_id": "76491"} -{"city": "TEMPLE", "loc": [-97.334264, 31.089518], "pop": 16400, "state": "TX", "_id": "76501"} -{"city": "TEMPLE", "loc": [-97.389781, 31.071004], "pop": 15632, "state": "TX", "_id": "76502"} -{"city": "TEMPLE", "loc": [-97.364764, 31.091742], "pop": 20273, "state": "TX", "_id": "76504"} -{"city": "BARTLETT", "loc": [-97.426302, 30.799056], "pop": 727, "state": "TX", "_id": "76511"} -{"city": "BELTON", "loc": [-97.472025, 31.072298], "pop": 20331, "state": "TX", "_id": "76513"} -{"city": "BUCKHOLTS", "loc": [-97.124135, 30.885756], "pop": 1072, "state": "TX", "_id": "76518"} -{"city": "BURLINGTON", "loc": [-96.885308, 30.945691], "pop": 1312, "state": "TX", "_id": "76519"} -{"city": "CAMERON", "loc": [-96.976562, 30.852713], "pop": 6965, "state": "TX", "_id": "76520"} -{"city": "IZORO", "loc": [-97.912132, 31.125799], "pop": 26431, "state": "TX", "_id": "76522"} -{"city": "DAVILLA", "loc": [-97.200865, 30.767471], "pop": 1079, "state": "TX", "_id": "76523"} -{"city": "EDDY", "loc": [-97.270926, 31.326724], "pop": 2544, "state": "TX", "_id": "76524"} -{"city": "BEE HOUSE", "loc": [-98.055175, 31.403967], "pop": 1179, "state": "TX", "_id": "76525"} -{"city": "FLAT", "loc": [-97.589777, 31.306475], "pop": 774, "state": "TX", "_id": "76526"} -{"city": "FLORENCE", "loc": [-97.834423, 30.78137], "pop": 3703, "state": "TX", "_id": "76527"} -{"city": "TURNERSVILLE", "loc": [-97.724286, 31.447646], "pop": 14415, "state": "TX", "_id": "76528"} -{"city": "GRANGER", "loc": [-97.445065, 30.739813], "pop": 3160, "state": "TX", "_id": "76530"} -{"city": "HAMILTON", "loc": [-98.113051, 31.678116], "pop": 5260, "state": "TX", "_id": "76531"} -{"city": "HOLLAND", "loc": [-97.385695, 30.879977], "pop": 2223, "state": "TX", "_id": "76534"} -{"city": "JARRELL", "loc": [-97.600833, 30.748406], "pop": 3430, "state": "TX", "_id": "76537"} -{"city": "JONESBORO", "loc": [-97.775155, 31.599601], "pop": 793, "state": "TX", "_id": "76538"} -{"city": "KEMPNER", "loc": [-97.972068, 31.073051], "pop": 3884, "state": "TX", "_id": "76539"} -{"city": "KILLEEN", "loc": [-97.727808, 31.116426], "pop": 22853, "state": "TX", "_id": "76541"} -{"city": "HARKER HEIGHTS", "loc": [-97.746736, 31.075056], "pop": 25829, "state": "TX", "_id": "76542"} -{"city": "HARKER HEIGHTS", "loc": [-97.676864, 31.100505], "pop": 35052, "state": "TX", "_id": "76543"} -{"city": "FORT HOOD", "loc": [-97.776404, 31.137953], "pop": 36657, "state": "TX", "_id": "76544"} -{"city": "LAMPASAS", "loc": [-98.183361, 31.067957], "pop": 7698, "state": "TX", "_id": "76550"} -{"city": "MILANO", "loc": [-96.803477, 30.736612], "pop": 2187, "state": "TX", "_id": "76556"} -{"city": "MOODY", "loc": [-97.409957, 31.253321], "pop": 4411, "state": "TX", "_id": "76557"} -{"city": "NOLANVILLE", "loc": [-97.594109, 31.083271], "pop": 1820, "state": "TX", "_id": "76559"} -{"city": "OGLESBY", "loc": [-97.550093, 31.443767], "pop": 1221, "state": "TX", "_id": "76561"} -{"city": "POTTSVILLE", "loc": [-98.356077, 31.68374], "pop": 279, "state": "TX", "_id": "76565"} -{"city": "PURMELA", "loc": [-97.888945, 31.472647], "pop": 1111, "state": "TX", "_id": "76566"} -{"city": "ROCKDALE", "loc": [-97.0079, 30.658282], "pop": 8052, "state": "TX", "_id": "76567"} -{"city": "ROGERS", "loc": [-97.222793, 30.955013], "pop": 2565, "state": "TX", "_id": "76569"} -{"city": "ROSEBUD", "loc": [-96.975455, 31.092208], "pop": 3093, "state": "TX", "_id": "76570"} -{"city": "SALADO", "loc": [-97.532999, 30.949388], "pop": 3454, "state": "TX", "_id": "76571"} -{"city": "TAYLOR", "loc": [-97.440103, 30.58071], "pop": 14135, "state": "TX", "_id": "76574"} -{"city": "THORNDALE", "loc": [-97.176446, 30.608237], "pop": 2279, "state": "TX", "_id": "76577"} -{"city": "THRALL", "loc": [-97.289261, 30.591981], "pop": 852, "state": "TX", "_id": "76578"} -{"city": "TROY", "loc": [-97.285205, 31.175855], "pop": 3791, "state": "TX", "_id": "76579"} -{"city": "ABBOTT", "loc": [-97.067146, 31.891642], "pop": 577, "state": "TX", "_id": "76621"} -{"city": "AQUILLA", "loc": [-97.22577, 31.858882], "pop": 1901, "state": "TX", "_id": "76622"} -{"city": "AXTELL", "loc": [-96.988178, 31.660966], "pop": 3235, "state": "TX", "_id": "76624"} -{"city": "BLOOMING GROVE", "loc": [-96.700991, 32.075839], "pop": 1594, "state": "TX", "_id": "76626"} -{"city": "BLUM", "loc": [-97.365183, 32.105183], "pop": 2737, "state": "TX", "_id": "76627"} -{"city": "BREMOND", "loc": [-96.669744, 31.156007], "pop": 1883, "state": "TX", "_id": "76629"} -{"city": "BRUCEVILLE", "loc": [-97.234244, 31.326708], "pop": 477, "state": "TX", "_id": "76630"} -{"city": "BYNUM", "loc": [-96.983702, 31.990668], "pop": 496, "state": "TX", "_id": "76631"} -{"city": "CHILTON", "loc": [-97.09002, 31.310018], "pop": 2304, "state": "TX", "_id": "76632"} -{"city": "CHINA SPRING", "loc": [-97.300221, 31.667266], "pop": 3030, "state": "TX", "_id": "76633"} -{"city": "LAGUNA PARK", "loc": [-97.515282, 31.799689], "pop": 6410, "state": "TX", "_id": "76634"} -{"city": "COOLIDGE", "loc": [-96.657744, 31.743804], "pop": 1042, "state": "TX", "_id": "76635"} -{"city": "COVINGTON", "loc": [-97.259091, 32.159538], "pop": 767, "state": "TX", "_id": "76636"} -{"city": "CRANFILLS GAP", "loc": [-97.78537, 31.781071], "pop": 687, "state": "TX", "_id": "76637"} -{"city": "CRAWFORD", "loc": [-97.389992, 31.559765], "pop": 3473, "state": "TX", "_id": "76638"} -{"city": "DAWSON", "loc": [-96.708483, 31.897429], "pop": 1457, "state": "TX", "_id": "76639"} -{"city": "ELM MOTT", "loc": [-97.113838, 31.672547], "pop": 4183, "state": "TX", "_id": "76640"} -{"city": "FROST", "loc": [-96.768437, 32.027545], "pop": 1831, "state": "TX", "_id": "76641"} -{"city": "GROESBECK", "loc": [-96.523381, 31.535667], "pop": 5538, "state": "TX", "_id": "76642"} -{"city": "HEWITT", "loc": [-97.196556, 31.458166], "pop": 8487, "state": "TX", "_id": "76643"} -{"city": "HILLSBORO", "loc": [-97.119791, 32.014942], "pop": 8966, "state": "TX", "_id": "76645"} -{"city": "HUBBARD", "loc": [-96.80001, 31.843559], "pop": 2015, "state": "TX", "_id": "76648"} -{"city": "IREDELL", "loc": [-97.879283, 31.972197], "pop": 813, "state": "TX", "_id": "76649"} -{"city": "ITALY", "loc": [-96.88229, 32.178508], "pop": 2321, "state": "TX", "_id": "76651"} -{"city": "KOPPERL", "loc": [-97.542085, 32.103491], "pop": 820, "state": "TX", "_id": "76652"} -{"city": "KOSSE", "loc": [-96.619475, 31.314704], "pop": 854, "state": "TX", "_id": "76653"} -{"city": "LORENA", "loc": [-97.230161, 31.409271], "pop": 4007, "state": "TX", "_id": "76655"} -{"city": "LOTT", "loc": [-97.058143, 31.192462], "pop": 2005, "state": "TX", "_id": "76656"} -{"city": "MC GREGOR", "loc": [-97.394318, 31.443099], "pop": 5853, "state": "TX", "_id": "76657"} -{"city": "MALONE", "loc": [-96.890682, 31.923979], "pop": 491, "state": "TX", "_id": "76660"} -{"city": "MARLIN", "loc": [-96.888942, 31.303592], "pop": 8810, "state": "TX", "_id": "76661"} -{"city": "MART", "loc": [-96.838133, 31.545798], "pop": 2588, "state": "TX", "_id": "76664"} -{"city": "MERIDIAN", "loc": [-97.64433, 31.929022], "pop": 2354, "state": "TX", "_id": "76665"} -{"city": "MERTENS", "loc": [-96.898128, 32.02753], "pop": 328, "state": "TX", "_id": "76666"} -{"city": "MEXIA", "loc": [-96.495186, 31.678386], "pop": 10971, "state": "TX", "_id": "76667"} -{"city": "MILFORD", "loc": [-96.96115, 32.148198], "pop": 1259, "state": "TX", "_id": "76670"} -{"city": "MORGAN", "loc": [-97.560829, 32.01946], "pop": 1403, "state": "TX", "_id": "76671"} -{"city": "MOUNT CALM", "loc": [-96.894393, 31.757504], "pop": 612, "state": "TX", "_id": "76673"} -{"city": "OTTO", "loc": [-96.875904, 31.432733], "pop": 776, "state": "TX", "_id": "76675"} -{"city": "PENELOPE", "loc": [-96.937164, 31.855148], "pop": 613, "state": "TX", "_id": "76676"} -{"city": "PRAIRIE HILL", "loc": [-96.809381, 31.659097], "pop": 657, "state": "TX", "_id": "76678"} -{"city": "PURDON", "loc": [-96.585619, 31.948285], "pop": 752, "state": "TX", "_id": "76679"} -{"city": "REAGAN", "loc": [-96.741943, 31.229713], "pop": 796, "state": "TX", "_id": "76680"} -{"city": "RICHLAND", "loc": [-96.437262, 31.901785], "pop": 512, "state": "TX", "_id": "76681"} -{"city": "RIESEL", "loc": [-96.94764, 31.500247], "pop": 2488, "state": "TX", "_id": "76682"} -{"city": "THORNTON", "loc": [-96.502378, 31.408326], "pop": 1438, "state": "TX", "_id": "76687"} -{"city": "VALLEY MILLS", "loc": [-97.493461, 31.659876], "pop": 1796, "state": "TX", "_id": "76689"} -{"city": "WALNUT SPRINGS", "loc": [-97.751423, 32.059268], "pop": 842, "state": "TX", "_id": "76690"} -{"city": "WEST", "loc": [-97.125843, 31.775385], "pop": 6222, "state": "TX", "_id": "76691"} -{"city": "BONANZA", "loc": [-97.33495, 31.959201], "pop": 5302, "state": "TX", "_id": "76692"} -{"city": "WORTHAM", "loc": [-96.420208, 31.786542], "pop": 1602, "state": "TX", "_id": "76693"} -{"city": "WACO", "loc": [-97.139608, 31.552452], "pop": 1752, "state": "TX", "_id": "76701"} -{"city": "BELLMEAD", "loc": [-97.126742, 31.575701], "pop": 8919, "state": "TX", "_id": "76704"} -{"city": "BELLMEAD", "loc": [-97.094575, 31.610787], "pop": 18763, "state": "TX", "_id": "76705"} -{"city": "WACO", "loc": [-97.119752, 31.517086], "pop": 31263, "state": "TX", "_id": "76706"} -{"city": "WACO", "loc": [-97.158824, 31.552709], "pop": 15905, "state": "TX", "_id": "76707"} -{"city": "WACO", "loc": [-97.178635, 31.576544], "pop": 18436, "state": "TX", "_id": "76708"} -{"city": "WACO", "loc": [-97.189891, 31.534981], "pop": 22014, "state": "TX", "_id": "76710"} -{"city": "BEVERLY HILLS", "loc": [-97.150254, 31.519863], "pop": 8736, "state": "TX", "_id": "76711"} -{"city": "WOODWAY", "loc": [-97.231062, 31.505074], "pop": 14756, "state": "TX", "_id": "76712"} -{"city": "EARLY", "loc": [-98.975164, 31.704658], "pop": 24634, "state": "TX", "_id": "76801"} -{"city": "ART", "loc": [-99.093732, 30.775419], "pop": 24, "state": "TX", "_id": "76820"} -{"city": "BALLINGER", "loc": [-99.958927, 31.746836], "pop": 4957, "state": "TX", "_id": "76821"} -{"city": "BANGS", "loc": [-99.107657, 31.768388], "pop": 5681, "state": "TX", "_id": "76823"} -{"city": "BEND", "loc": [-98.482102, 31.11231], "pop": 1, "state": "TX", "_id": "76824"} -{"city": "FIFE", "loc": [-99.3372, 31.128304], "pop": 7280, "state": "TX", "_id": "76825"} -{"city": "BROOKESMITH", "loc": [-99.127729, 31.517602], "pop": 240, "state": "TX", "_id": "76827"} -{"city": "BURKETT", "loc": [-99.255258, 31.998623], "pop": 237, "state": "TX", "_id": "76828"} -{"city": "CASTELL", "loc": [-98.931859, 30.697429], "pop": 64, "state": "TX", "_id": "76831"} -{"city": "CHEROKEE", "loc": [-98.66332, 30.980598], "pop": 127, "state": "TX", "_id": "76832"} -{"city": "COLEMAN", "loc": [-99.427007, 31.828651], "pop": 6300, "state": "TX", "_id": "76834"} -{"city": "DOOLE", "loc": [-99.550605, 31.415743], "pop": 86, "state": "TX", "_id": "76836"} -{"city": "EDEN", "loc": [-99.840658, 31.219219], "pop": 2028, "state": "TX", "_id": "76837"} -{"city": "FORT MC KAVETT", "loc": [-100.080928, 30.82903], "pop": 47, "state": "TX", "_id": "76841"} -{"city": "FREDONIA", "loc": [-99.12156, 30.921386], "pop": 72, "state": "TX", "_id": "76842"} -{"city": "GOLDTHWAITE", "loc": [-98.574405, 31.445769], "pop": 2523, "state": "TX", "_id": "76844"} -{"city": "GOULDBUSK", "loc": [-99.51363, 31.551075], "pop": 159, "state": "TX", "_id": "76845"} -{"city": "HEXT", "loc": [-99.554008, 30.881632], "pop": 60, "state": "TX", "_id": "76848"} -{"city": "JUNCTION", "loc": [-99.74731, 30.47544], "pop": 3248, "state": "TX", "_id": "76849"} -{"city": "76850", "loc": [-99.299552, 30.879689], "pop": 32, "state": "TX", "_id": "76850"} -{"city": "LOHN", "loc": [-99.38334, 31.317297], "pop": 233, "state": "TX", "_id": "76852"} -{"city": "LOMETA", "loc": [-98.400553, 31.216712], "pop": 1215, "state": "TX", "_id": "76853"} -{"city": "LONDON", "loc": [-99.625527, 30.617101], "pop": 465, "state": "TX", "_id": "76854"} -{"city": "MASON", "loc": [-99.226117, 30.743392], "pop": 3182, "state": "TX", "_id": "76856"} -{"city": "MAY", "loc": [-98.965646, 31.957082], "pop": 1454, "state": "TX", "_id": "76857"} -{"city": "MELVIN", "loc": [-99.5439, 31.185145], "pop": 361, "state": "TX", "_id": "76858"} -{"city": "MENARD", "loc": [-99.784721, 30.911898], "pop": 2145, "state": "TX", "_id": "76859"} -{"city": "MILES", "loc": [-100.182292, 31.612052], "pop": 1102, "state": "TX", "_id": "76861"} -{"city": "MILLERSVIEW", "loc": [-99.717137, 31.416745], "pop": 137, "state": "TX", "_id": "76862"} -{"city": "MULLIN", "loc": [-98.663542, 31.574829], "pop": 1281, "state": "TX", "_id": "76864"} -{"city": "NORTON", "loc": [-100.131515, 31.879498], "pop": 244, "state": "TX", "_id": "76865"} -{"city": "PAINT ROCK", "loc": [-99.913936, 31.504808], "pop": 400, "state": "TX", "_id": "76866"} -{"city": "PEAR VALLEY", "loc": [-99.494742, 31.297211], "pop": 110, "state": "TX", "_id": "76867"} -{"city": "PONTOTOC", "loc": [-99.021224, 30.890617], "pop": 113, "state": "TX", "_id": "76869"} -{"city": "PRIDDY", "loc": [-98.501464, 31.687418], "pop": 122, "state": "TX", "_id": "76870"} -{"city": "RICHLAND SPRINGS", "loc": [-98.850731, 31.275317], "pop": 1103, "state": "TX", "_id": "76871"} -{"city": "ROCHELLE", "loc": [-99.157229, 31.300011], "pop": 761, "state": "TX", "_id": "76872"} -{"city": "ROCKWOOD", "loc": [-99.374579, 31.503677], "pop": 63, "state": "TX", "_id": "76873"} -{"city": "ROOSEVELT", "loc": [-99.916501, 30.532203], "pop": 382, "state": "TX", "_id": "76874"} -{"city": "ROWENA", "loc": [-100.019094, 31.64355], "pop": 802, "state": "TX", "_id": "76875"} -{"city": "SAN SABA", "loc": [-98.730929, 31.162678], "pop": 4023, "state": "TX", "_id": "76877"} -{"city": "SANTA ANNA", "loc": [-99.321197, 31.721477], "pop": 1835, "state": "TX", "_id": "76878"} -{"city": "STAR", "loc": [-98.415752, 31.479039], "pop": 605, "state": "TX", "_id": "76880"} -{"city": "TALPA", "loc": [-99.674679, 31.803424], "pop": 240, "state": "TX", "_id": "76882"} -{"city": "TELEGRAPH", "loc": [-99.928369, 30.367365], "pop": 27, "state": "TX", "_id": "76883"} -{"city": "VALERA", "loc": [-99.563962, 31.77314], "pop": 201, "state": "TX", "_id": "76884"} -{"city": "VALLEY SPRING", "loc": [-98.832541, 30.836862], "pop": 270, "state": "TX", "_id": "76885"} -{"city": "VOCA", "loc": [-99.168203, 30.995874], "pop": 94, "state": "TX", "_id": "76887"} -{"city": "LEADAY", "loc": [-99.538536, 31.639194], "pop": 227, "state": "TX", "_id": "76888"} -{"city": "ZEPHYR", "loc": [-98.818199, 31.669429], "pop": 433, "state": "TX", "_id": "76890"} -{"city": "SAN ANGELO", "loc": [-100.481752, 31.478165], "pop": 23800, "state": "TX", "_id": "76901"} -{"city": "SAN ANGELO", "loc": [-100.438586, 31.470735], "pop": 32471, "state": "TX", "_id": "76903"} -{"city": "SAN ANGELO", "loc": [-100.480036, 31.419411], "pop": 25535, "state": "TX", "_id": "76904"} -{"city": "SAN ANGELO", "loc": [-100.390005, 31.464738], "pop": 11284, "state": "TX", "_id": "76905"} -{"city": "BARNHART", "loc": [-101.191752, 31.159647], "pop": 178, "state": "TX", "_id": "76930"} -{"city": "BEST", "loc": [-101.478776, 31.240592], "pop": 4514, "state": "TX", "_id": "76932"} -{"city": "BRONTE", "loc": [-100.298765, 31.878939], "pop": 1291, "state": "TX", "_id": "76933"} -{"city": "CARLSBAD", "loc": [-100.627501, 31.597995], "pop": 1886, "state": "TX", "_id": "76934"} -{"city": "CHRISTOVAL", "loc": [-100.52031, 31.23461], "pop": 1639, "state": "TX", "_id": "76935"} -{"city": "ELDORADO", "loc": [-100.58894, 30.86667], "pop": 2990, "state": "TX", "_id": "76936"} -{"city": "EOLA", "loc": [-100.072892, 31.429812], "pop": 479, "state": "TX", "_id": "76937"} -{"city": "MERETA", "loc": [-100.183391, 31.48632], "pop": 768, "state": "TX", "_id": "76940"} -{"city": "MERTZON", "loc": [-100.822101, 31.282884], "pop": 1451, "state": "TX", "_id": "76941"} -{"city": "OZONA", "loc": [-101.238802, 30.716369], "pop": 4076, "state": "TX", "_id": "76943"} -{"city": "ROBERT LEE", "loc": [-100.510366, 31.895091], "pop": 1826, "state": "TX", "_id": "76945"} -{"city": "SILVER", "loc": [-100.692229, 32.048371], "pop": 45, "state": "TX", "_id": "76949"} -{"city": "SONORA", "loc": [-100.630667, 30.555752], "pop": 4135, "state": "TX", "_id": "76950"} -{"city": "STERLING CITY", "loc": [-101.001729, 31.835063], "pop": 1438, "state": "TX", "_id": "76951"} -{"city": "VANCOURT", "loc": [-100.180367, 31.340417], "pop": 256, "state": "TX", "_id": "76955"} -{"city": "WALL", "loc": [-100.322132, 31.369445], "pop": 819, "state": "TX", "_id": "76957"} -{"city": "HOUSTON", "loc": [-95.359361, 29.759366], "pop": 7658, "state": "TX", "_id": "77002"} -{"city": "HOUSTON", "loc": [-95.339108, 29.748903], "pop": 8500, "state": "TX", "_id": "77003"} -{"city": "HOUSTON", "loc": [-95.362546, 29.724687], "pop": 29940, "state": "TX", "_id": "77004"} -{"city": "HOUSTON", "loc": [-95.426261, 29.717856], "pop": 21772, "state": "TX", "_id": "77005"} -{"city": "HOUSTON", "loc": [-95.392255, 29.740899], "pop": 17653, "state": "TX", "_id": "77006"} -{"city": "HOUSTON", "loc": [-95.403421, 29.773603], "pop": 22511, "state": "TX", "_id": "77007"} -{"city": "HOUSTON", "loc": [-95.411797, 29.799096], "pop": 29653, "state": "TX", "_id": "77008"} -{"city": "HOUSTON", "loc": [-95.367481, 29.793558], "pop": 42521, "state": "TX", "_id": "77009"} -{"city": "HOUSTON", "loc": [-95.356549, 29.75125], "pop": 0, "state": "TX", "_id": "77010"} -{"city": "HOUSTON", "loc": [-95.307262, 29.741992], "pop": 22311, "state": "TX", "_id": "77011"} -{"city": "HOUSTON", "loc": [-95.281925, 29.71491], "pop": 23344, "state": "TX", "_id": "77012"} -{"city": "HOUSTON", "loc": [-95.230134, 29.784169], "pop": 17011, "state": "TX", "_id": "77013"} -{"city": "HOUSTON", "loc": [-95.462497, 29.979637], "pop": 11970, "state": "TX", "_id": "77014"} -{"city": "HOUSTON", "loc": [-95.185189, 29.785287], "pop": 42008, "state": "TX", "_id": "77015"} -{"city": "HOUSTON", "loc": [-95.303199, 29.857855], "pop": 31180, "state": "TX", "_id": "77016"} -{"city": "HOUSTON", "loc": [-95.255485, 29.686301], "pop": 26502, "state": "TX", "_id": "77017"} -{"city": "HOUSTON", "loc": [-95.426631, 29.827166], "pop": 25857, "state": "TX", "_id": "77018"} -{"city": "HOUSTON", "loc": [-95.40539, 29.751651], "pop": 15280, "state": "TX", "_id": "77019"} -{"city": "HOUSTON", "loc": [-95.312101, 29.775759], "pop": 28011, "state": "TX", "_id": "77020"} -{"city": "HOUSTON", "loc": [-95.356151, 29.69538], "pop": 23815, "state": "TX", "_id": "77021"} -{"city": "HOUSTON", "loc": [-95.376862, 29.829862], "pop": 27417, "state": "TX", "_id": "77022"} -{"city": "HOUSTON", "loc": [-95.317777, 29.724179], "pop": 31983, "state": "TX", "_id": "77023"} -{"city": "HOUSTON", "loc": [-95.520063, 29.76958], "pop": 30766, "state": "TX", "_id": "77024"} -{"city": "HOUSTON", "loc": [-95.434107, 29.688897], "pop": 21618, "state": "TX", "_id": "77025"} -{"city": "HOUSTON", "loc": [-95.328775, 29.797168], "pop": 27744, "state": "TX", "_id": "77026"} -{"city": "HOUSTON", "loc": [-95.446032, 29.739571], "pop": 11422, "state": "TX", "_id": "77027"} -{"city": "HOUSTON", "loc": [-95.287886, 29.829657], "pop": 17943, "state": "TX", "_id": "77028"} -{"city": "JACINTO CITY", "loc": [-95.254861, 29.760326], "pop": 17739, "state": "TX", "_id": "77029"} -{"city": "V A HOSPITAL", "loc": [-95.40619, 29.70372], "pop": 10462, "state": "TX", "_id": "77030"} -{"city": "HOUSTON", "loc": [-95.541281, 29.658144], "pop": 14021, "state": "TX", "_id": "77031"} -{"city": "HOUSTON", "loc": [-95.329883, 29.93676], "pop": 8373, "state": "TX", "_id": "77032"} -{"city": "HOUSTON", "loc": [-95.338157, 29.668566], "pop": 28295, "state": "TX", "_id": "77033"} -{"city": "HOUSTON", "loc": [-95.221615, 29.636395], "pop": 21593, "state": "TX", "_id": "77034"} -{"city": "HOUSTON", "loc": [-95.485368, 29.651833], "pop": 30746, "state": "TX", "_id": "77035"} -{"city": "HOUSTON", "loc": [-95.540464, 29.698447], "pop": 55414, "state": "TX", "_id": "77036"} -{"city": "HOUSTON", "loc": [-95.393515, 29.889161], "pop": 14286, "state": "TX", "_id": "77037"} -{"city": "HOUSTON", "loc": [-95.438601, 29.91956], "pop": 15653, "state": "TX", "_id": "77038"} -{"city": "HOUSTON", "loc": [-95.33338, 29.906731], "pop": 23839, "state": "TX", "_id": "77039"} -{"city": "JERSEY VILLAGE", "loc": [-95.529969, 29.879613], "pop": 33052, "state": "TX", "_id": "77040"} -{"city": "HOUSTON", "loc": [-95.581663, 29.860187], "pop": 14790, "state": "TX", "_id": "77041"} -{"city": "HOUSTON", "loc": [-95.558895, 29.740446], "pop": 31505, "state": "TX", "_id": "77042"} -{"city": "HOUSTON", "loc": [-95.560734, 29.805181], "pop": 21752, "state": "TX", "_id": "77043"} -{"city": "HOUSTON", "loc": [-95.19757, 29.863485], "pop": 10669, "state": "TX", "_id": "77044"} -{"city": "HOUSTON", "loc": [-95.438166, 29.629717], "pop": 22145, "state": "TX", "_id": "77045"} -{"city": "HOUSTON", "loc": [-95.431845, 29.73279], "pop": 557, "state": "TX", "_id": "77046"} -{"city": "HOUSTON", "loc": [-95.374993, 29.625443], "pop": 9588, "state": "TX", "_id": "77047"} -{"city": "HOUSTON", "loc": [-95.341606, 29.632097], "pop": 13873, "state": "TX", "_id": "77048"} -{"city": "HOUSTON", "loc": [-95.184815, 29.823471], "pop": 14445, "state": "TX", "_id": "77049"} -{"city": "HOUSTON", "loc": [-95.284837, 29.901456], "pop": 3709, "state": "TX", "_id": "77050"} -{"city": "HOUSTON", "loc": [-95.368763, 29.65792], "pop": 13776, "state": "TX", "_id": "77051"} -{"city": "HOUSTON", "loc": [-95.458709, 29.596156], "pop": 20720, "state": "TX", "_id": "77053"} -{"city": "HOUSTON", "loc": [-95.401677, 29.685209], "pop": 12399, "state": "TX", "_id": "77054"} -{"city": "HOUSTON", "loc": [-95.495787, 29.797064], "pop": 36187, "state": "TX", "_id": "77055"} -{"city": "HOUSTON", "loc": [-95.468282, 29.744584], "pop": 13544, "state": "TX", "_id": "77056"} -{"city": "HOUSTON", "loc": [-95.490253, 29.74217], "pop": 28217, "state": "TX", "_id": "77057"} -{"city": "HOUSTON", "loc": [-95.057413, 29.574787], "pop": 5556, "state": "TX", "_id": "77058"} -{"city": "HOUSTON", "loc": [-95.113354, 29.597493], "pop": 6185, "state": "TX", "_id": "77059"} -{"city": "HOUSTON", "loc": [-95.398061, 29.933462], "pop": 31443, "state": "TX", "_id": "77060"} -{"city": "HOUSTON", "loc": [-95.278987, 29.665221], "pop": 20966, "state": "TX", "_id": "77061"} -{"city": "HOUSTON", "loc": [-95.130292, 29.572084], "pop": 25303, "state": "TX", "_id": "77062"} -{"city": "HOUSTON", "loc": [-95.522039, 29.734843], "pop": 24156, "state": "TX", "_id": "77063"} -{"city": "HOUSTON", "loc": [-95.556894, 29.918981], "pop": 21388, "state": "TX", "_id": "77064"} -{"city": "HOUSTON", "loc": [-95.61063, 29.931933], "pop": 17370, "state": "TX", "_id": "77065"} -{"city": "HOUSTON", "loc": [-95.494717, 29.961027], "pop": 23310, "state": "TX", "_id": "77066"} -{"city": "HOUSTON", "loc": [-95.452158, 29.954717], "pop": 18416, "state": "TX", "_id": "77067"} -{"city": "HOUSTON", "loc": [-95.489661, 30.006867], "pop": 7670, "state": "TX", "_id": "77068"} -{"city": "HOUSTON", "loc": [-95.520827, 29.986292], "pop": 11617, "state": "TX", "_id": "77069"} -{"city": "HOUSTON", "loc": [-95.58027, 29.978099], "pop": 25735, "state": "TX", "_id": "77070"} -{"city": "HOUSTON", "loc": [-95.517554, 29.651838], "pop": 21037, "state": "TX", "_id": "77071"} -{"city": "HOUSTON", "loc": [-95.586155, 29.699026], "pop": 41808, "state": "TX", "_id": "77072"} -{"city": "HOUSTON", "loc": [-95.408671, 30.019767], "pop": 10208, "state": "TX", "_id": "77073"} -{"city": "HOUSTON", "loc": [-95.510588, 29.689601], "pop": 32691, "state": "TX", "_id": "77074"} -{"city": "HOUSTON", "loc": [-95.259983, 29.622276], "pop": 16943, "state": "TX", "_id": "77075"} -{"city": "HOUSTON", "loc": [-95.383442, 29.85801], "pop": 23881, "state": "TX", "_id": "77076"} -{"city": "HOUSTON", "loc": [-95.602991, 29.747656], "pop": 32090, "state": "TX", "_id": "77077"} -{"city": "HOUSTON", "loc": [-95.258208, 29.849724], "pop": 12300, "state": "TX", "_id": "77078"} -{"city": "HOUSTON", "loc": [-95.597993, 29.773759], "pop": 29891, "state": "TX", "_id": "77079"} -{"city": "HOUSTON", "loc": [-95.522986, 29.815854], "pop": 39465, "state": "TX", "_id": "77080"} -{"city": "HOUSTON", "loc": [-95.484531, 29.711926], "pop": 33787, "state": "TX", "_id": "77081"} -{"city": "HOUSTON", "loc": [-95.628533, 29.722283], "pop": 23875, "state": "TX", "_id": "77082"} -{"city": "HOUSTON", "loc": [-95.651098, 29.694709], "pop": 38124, "state": "TX", "_id": "77083"} -{"city": "HOUSTON", "loc": [-95.662329, 29.844022], "pop": 45204, "state": "TX", "_id": "77084"} -{"city": "HOUSTON", "loc": [-95.481945, 29.621787], "pop": 6519, "state": "TX", "_id": "77085"} -{"city": "HOUSTON", "loc": [-95.493868, 29.922667], "pop": 16075, "state": "TX", "_id": "77086"} -{"city": "HOUSTON", "loc": [-95.301062, 29.687579], "pop": 31112, "state": "TX", "_id": "77087"} -{"city": "HOUSTON", "loc": [-95.453877, 29.881694], "pop": 44595, "state": "TX", "_id": "77088"} -{"city": "HOUSTON", "loc": [-95.221786, 29.593978], "pop": 37678, "state": "TX", "_id": "77089"} -{"city": "HOUSTON", "loc": [-95.447002, 30.016673], "pop": 22054, "state": "TX", "_id": "77090"} -{"city": "HOUSTON", "loc": [-95.443521, 29.853448], "pop": 21341, "state": "TX", "_id": "77091"} -{"city": "HOUSTON", "loc": [-95.472031, 29.832391], "pop": 31872, "state": "TX", "_id": "77092"} -{"city": "HOUSTON", "loc": [-95.340286, 29.861661], "pop": 39118, "state": "TX", "_id": "77093"} -{"city": "HOUSTON", "loc": [-95.710742, 29.770536], "pop": 1168, "state": "TX", "_id": "77094"} -{"city": "HOUSTON", "loc": [-95.648082, 29.894115], "pop": 24418, "state": "TX", "_id": "77095"} -{"city": "HOUSTON", "loc": [-95.486066, 29.672205], "pop": 32124, "state": "TX", "_id": "77096"} -{"city": "HOUSTON", "loc": [-95.411778, 29.734987], "pop": 10436, "state": "TX", "_id": "77098"} -{"city": "HOUSTON", "loc": [-95.586613, 29.670869], "pop": 40503, "state": "TX", "_id": "77099"} -{"city": "CONROE", "loc": [-95.452667, 30.312535], "pop": 20150, "state": "TX", "_id": "77301"} -{"city": "GRANGERLAND", "loc": [-95.416087, 30.250357], "pop": 5767, "state": "TX", "_id": "77302"} -{"city": "CUT AND SHOOT", "loc": [-95.369725, 30.344456], "pop": 14683, "state": "TX", "_id": "77303"} -{"city": "PANORAMA VILLAGE", "loc": [-95.495244, 30.327351], "pop": 9625, "state": "TX", "_id": "77304"} -{"city": "CLEVELAND", "loc": [-95.020152, 30.329977], "pop": 21298, "state": "TX", "_id": "77327"} -{"city": "COLDSPRING", "loc": [-95.10858, 30.602661], "pop": 3182, "state": "TX", "_id": "77331"} -{"city": "GOODRICH", "loc": [-94.95922, 30.607909], "pop": 1441, "state": "TX", "_id": "77335"} -{"city": "HUFFMAN", "loc": [-95.105069, 30.056491], "pop": 6529, "state": "TX", "_id": "77336"} -{"city": "HUMBLE", "loc": [-95.282476, 30.004091], "pop": 19090, "state": "TX", "_id": "77338"} -{"city": "HUMBLE", "loc": [-95.210716, 30.056333], "pop": 28083, "state": "TX", "_id": "77339"} -{"city": "HUNTSVILLE", "loc": [-95.534186, 30.73435], "pop": 48879, "state": "TX", "_id": "77340"} -{"city": "HUMBLE", "loc": [-95.170654, 30.056641], "pop": 13004, "state": "TX", "_id": "77345"} -{"city": "HUMBLE", "loc": [-95.172815, 30.004195], "pop": 15984, "state": "TX", "_id": "77346"} -{"city": "SEGNO", "loc": [-94.935278, 30.718018], "pop": 24261, "state": "TX", "_id": "77351"} -{"city": "MAGNOLIA", "loc": [-95.688989, 30.1678], "pop": 15638, "state": "TX", "_id": "77355"} -{"city": "MONTGOMERY", "loc": [-95.650342, 30.363932], "pop": 12002, "state": "TX", "_id": "77356"} -{"city": "NEW CANEY", "loc": [-95.197968, 30.157933], "pop": 13214, "state": "TX", "_id": "77357"} -{"city": "NEW WAVERLY", "loc": [-95.453194, 30.535357], "pop": 1977, "state": "TX", "_id": "77358"} -{"city": "OAKHURST", "loc": [-95.309478, 30.71262], "pop": 283, "state": "TX", "_id": "77359"} -{"city": "PINEHURST", "loc": [-95.681406, 30.158052], "pop": 2498, "state": "TX", "_id": "77362"} -{"city": "PLANTERSVILLE", "loc": [-95.849812, 30.296931], "pop": 1333, "state": "TX", "_id": "77363"} -{"city": "POINTBLANK", "loc": [-95.229494, 30.75926], "pop": 2911, "state": "TX", "_id": "77364"} -{"city": "PORTER", "loc": [-95.268613, 30.123731], "pop": 13541, "state": "TX", "_id": "77365"} -{"city": "SHEPHERD", "loc": [-95.092913, 30.483396], "pop": 9604, "state": "TX", "_id": "77371"} -{"city": "SPLENDORA", "loc": [-95.199308, 30.232609], "pop": 11287, "state": "TX", "_id": "77372"} -{"city": "SPRING", "loc": [-95.377329, 30.053241], "pop": 33118, "state": "TX", "_id": "77373"} -{"city": "TOMBALL", "loc": [-95.62006, 30.073923], "pop": 19801, "state": "TX", "_id": "77375"} -{"city": "WILLIS", "loc": [-95.497583, 30.432025], "pop": 9988, "state": "TX", "_id": "77378"} -{"city": "KLEIN", "loc": [-95.528481, 30.023377], "pop": 35275, "state": "TX", "_id": "77379"} -{"city": "THE WOODLANDS", "loc": [-95.468944, 30.13739], "pop": 16541, "state": "TX", "_id": "77380"} -{"city": "THE WOODLANDS", "loc": [-95.500743, 30.168887], "pop": 19466, "state": "TX", "_id": "77381"} -{"city": "CONROE", "loc": [-95.492392, 30.225725], "pop": 1635, "state": "TX", "_id": "77384"} -{"city": "CONROE", "loc": [-95.428789, 30.187695], "pop": 7311, "state": "TX", "_id": "77385"} -{"city": "SPRING", "loc": [-95.423943, 30.128805], "pop": 8379, "state": "TX", "_id": "77386"} -{"city": "SPRING", "loc": [-95.469456, 30.050546], "pop": 21805, "state": "TX", "_id": "77388"} -{"city": "SPRING", "loc": [-95.506624, 30.104398], "pop": 8540, "state": "TX", "_id": "77389"} -{"city": "HUMBLE", "loc": [-95.262186, 29.950697], "pop": 16163, "state": "TX", "_id": "77396"} -{"city": "BELLAIRE", "loc": [-95.461106, 29.702313], "pop": 13913, "state": "TX", "_id": "77401"} -{"city": "SARGENT", "loc": [-95.92817, 28.96183], "pop": 19305, "state": "TX", "_id": "77414"} -{"city": "BEASLEY", "loc": [-95.968145, 29.479045], "pop": 2151, "state": "TX", "_id": "77417"} -{"city": "BELLVILLE", "loc": [-96.253083, 29.96583], "pop": 6771, "state": "TX", "_id": "77418"} -{"city": "BLESSING", "loc": [-96.217956, 28.864947], "pop": 1285, "state": "TX", "_id": "77419"} -{"city": "BOLING", "loc": [-95.974045, 29.252874], "pop": 3459, "state": "TX", "_id": "77420"} -{"city": "BRAZORIA", "loc": [-95.586683, 29.023642], "pop": 12583, "state": "TX", "_id": "77422"} -{"city": "BROOKSHIRE", "loc": [-95.975537, 29.807168], "pop": 4990, "state": "TX", "_id": "77423"} -{"city": "CHAPPELL HILL", "loc": [-96.234739, 30.183271], "pop": 2916, "state": "TX", "_id": "77426"} -{"city": "CYPRESS", "loc": [-95.635778, 29.976608], "pop": 18527, "state": "TX", "_id": "77429"} -{"city": "DAMON", "loc": [-95.703577, 29.301381], "pop": 1493, "state": "TX", "_id": "77430"} -{"city": "DANEVANG", "loc": [-96.19765, 29.066969], "pop": 278, "state": "TX", "_id": "77432"} -{"city": "CYPRESS", "loc": [-95.702456, 29.883633], "pop": 1983, "state": "TX", "_id": "77433"} -{"city": "EAGLE LAKE", "loc": [-96.335367, 29.584236], "pop": 4543, "state": "TX", "_id": "77434"} -{"city": "EAST BERNARD", "loc": [-96.121115, 29.470458], "pop": 5773, "state": "TX", "_id": "77435"} -{"city": "EL CAMPO", "loc": [-96.274266, 29.200776], "pop": 15829, "state": "TX", "_id": "77437"} -{"city": "ELMATON", "loc": [-96.13351, 28.83209], "pop": 470, "state": "TX", "_id": "77440"} -{"city": "FULSHEAR", "loc": [-95.897682, 29.721698], "pop": 193, "state": "TX", "_id": "77441"} -{"city": "GARWOOD", "loc": [-96.491925, 29.476001], "pop": 2231, "state": "TX", "_id": "77442"} -{"city": "GUY", "loc": [-95.770227, 29.332676], "pop": 343, "state": "TX", "_id": "77444"} -{"city": "HEMPSTEAD", "loc": [-96.071648, 30.091973], "pop": 5883, "state": "TX", "_id": "77445"} -{"city": "HOCKLEY", "loc": [-95.810447, 30.072888], "pop": 5040, "state": "TX", "_id": "77447"} -{"city": "PARK ROW", "loc": [-95.729267, 29.819922], "pop": 22664, "state": "TX", "_id": "77449"} -{"city": "PARK ROW", "loc": [-95.744506, 29.767632], "pop": 28533, "state": "TX", "_id": "77450"} -{"city": "LOUISE", "loc": [-96.392374, 29.14908], "pop": 2431, "state": "TX", "_id": "77455"} -{"city": "MARKHAM", "loc": [-96.092768, 28.954701], "pop": 2015, "state": "TX", "_id": "77456"} -{"city": "MATAGORDA", "loc": [-95.951891, 28.764926], "pop": 1455, "state": "TX", "_id": "77457"} -{"city": "MIDFIELD", "loc": [-96.226527, 28.936166], "pop": 286, "state": "TX", "_id": "77458"} -{"city": "MISSOURI CITY", "loc": [-95.542284, 29.570434], "pop": 16067, "state": "TX", "_id": "77459"} -{"city": "NEEDVILLE", "loc": [-95.827288, 29.41165], "pop": 7609, "state": "TX", "_id": "77461"} -{"city": "PALACIOS", "loc": [-96.215439, 28.71504], "pop": 5607, "state": "TX", "_id": "77465"} -{"city": "PLEDGER", "loc": [-95.898591, 29.179208], "pop": 345, "state": "TX", "_id": "77468"} -{"city": "CLODINE", "loc": [-95.752122, 29.593226], "pop": 36014, "state": "TX", "_id": "77469"} -{"city": "ROSENBERG", "loc": [-95.798213, 29.549727], "pop": 22182, "state": "TX", "_id": "77471"} -{"city": "SEALY", "loc": [-96.159189, 29.782632], "pop": 8680, "state": "TX", "_id": "77474"} -{"city": "STAFFORD", "loc": [-95.567764, 29.622816], "pop": 18952, "state": "TX", "_id": "77477"} -{"city": "SUGAR LAND", "loc": [-95.621856, 29.634153], "pop": 36536, "state": "TX", "_id": "77478"} -{"city": "SUGAR LAND", "loc": [-95.606591, 29.578537], "pop": 20219, "state": "TX", "_id": "77479"} -{"city": "SWEENY", "loc": [-95.700363, 29.041508], "pop": 5077, "state": "TX", "_id": "77480"} -{"city": "VAN VLECK", "loc": [-95.938903, 29.013879], "pop": 6011, "state": "TX", "_id": "77482"} -{"city": "WADSWORTH", "loc": [-95.971307, 28.614288], "pop": 145, "state": "TX", "_id": "77483"} -{"city": "WALLER", "loc": [-95.961275, 30.086008], "pop": 10513, "state": "TX", "_id": "77484"} -{"city": "WALLIS", "loc": [-96.045574, 29.63968], "pop": 3380, "state": "TX", "_id": "77485"} -{"city": "WEST COLUMBIA", "loc": [-95.669388, 29.140823], "pop": 9777, "state": "TX", "_id": "77486"} -{"city": "WHARTON", "loc": [-96.085828, 29.320488], "pop": 12326, "state": "TX", "_id": "77488"} -{"city": "MISSOURI CITY", "loc": [-95.511512, 29.596206], "pop": 27760, "state": "TX", "_id": "77489"} -{"city": "PARK ROW", "loc": [-95.815988, 29.804876], "pop": 10312, "state": "TX", "_id": "77493"} -{"city": "PARK ROW", "loc": [-95.811675, 29.750893], "pop": 2684, "state": "TX", "_id": "77494"} -{"city": "PASADENA", "loc": [-95.198193, 29.678945], "pop": 31270, "state": "TX", "_id": "77502"} -{"city": "PASADENA", "loc": [-95.15721, 29.687696], "pop": 23019, "state": "TX", "_id": "77503"} -{"city": "PASADENA", "loc": [-95.188478, 29.650133], "pop": 18518, "state": "TX", "_id": "77504"} -{"city": "PASADENA", "loc": [-95.146388, 29.651753], "pop": 10978, "state": "TX", "_id": "77505"} -{"city": "PASADENA", "loc": [-95.19895, 29.70087], "pop": 33656, "state": "TX", "_id": "77506"} -{"city": "PASADENA", "loc": [-95.079365, 29.6055], "pop": 0, "state": "TX", "_id": "77507"} -{"city": "ALTA LOMA", "loc": [-95.089429, 29.371854], "pop": 11098, "state": "TX", "_id": "77510"} -{"city": "ALVIN", "loc": [-95.251535, 29.41195], "pop": 30979, "state": "TX", "_id": "77511"} -{"city": "MONROE CITY", "loc": [-94.554819, 29.780987], "pop": 7969, "state": "TX", "_id": "77514"} -{"city": "ANGLETON", "loc": [-95.446661, 29.181049], "pop": 29204, "state": "TX", "_id": "77515"} -{"city": "ARCADIA", "loc": [-95.129003, 29.380259], "pop": 3377, "state": "TX", "_id": "77517"} -{"city": "BACLIFF", "loc": [-94.989293, 29.505506], "pop": 5465, "state": "TX", "_id": "77518"} -{"city": "BATSON", "loc": [-94.60959, 30.22502], "pop": 1071, "state": "TX", "_id": "77519"} -{"city": "BAYTOWN", "loc": [-94.965265, 29.746063], "pop": 43386, "state": "TX", "_id": "77520"} -{"city": "BAYTOWN", "loc": [-94.969549, 29.770482], "pop": 30277, "state": "TX", "_id": "77521"} -{"city": "CHANNELVIEW", "loc": [-95.131655, 29.791438], "pop": 23408, "state": "TX", "_id": "77530"} -{"city": "CLUTE", "loc": [-95.402595, 29.032502], "pop": 13361, "state": "TX", "_id": "77531"} -{"city": "BARRETT", "loc": [-95.07522, 29.937812], "pop": 13679, "state": "TX", "_id": "77532"} -{"city": "DANBURY", "loc": [-95.343465, 29.229082], "pop": 2522, "state": "TX", "_id": "77534"} -{"city": "DAYTON", "loc": [-94.878747, 30.010208], "pop": 16229, "state": "TX", "_id": "77535"} -{"city": "DEER PARK", "loc": [-95.122192, 29.682571], "pop": 25806, "state": "TX", "_id": "77536"} -{"city": "DEVERS", "loc": [-94.574615, 29.997835], "pop": 570, "state": "TX", "_id": "77538"} -{"city": "SAN LEON", "loc": [-95.034496, 29.466033], "pop": 21905, "state": "TX", "_id": "77539"} -{"city": "QUINTANA", "loc": [-95.371389, 28.96968], "pop": 17049, "state": "TX", "_id": "77541"} -{"city": "FRESNO", "loc": [-95.462608, 29.52931], "pop": 3144, "state": "TX", "_id": "77545"} -{"city": "FRIENDSWOOD", "loc": [-95.187888, 29.522399], "pop": 32217, "state": "TX", "_id": "77546"} -{"city": "GALENA PARK", "loc": [-95.240001, 29.739204], "pop": 9306, "state": "TX", "_id": "77547"} -{"city": "GALVESTON", "loc": [-94.79297, 29.298272], "pop": 31879, "state": "TX", "_id": "77550"} -{"city": "GALVESTON", "loc": [-94.830334, 29.276584], "pop": 22680, "state": "TX", "_id": "77551"} -{"city": "GALVESTON", "loc": [-94.913716, 29.229638], "pop": 5495, "state": "TX", "_id": "77554"} -{"city": "HANKAMER", "loc": [-94.593846, 29.87524], "pop": 233, "state": "TX", "_id": "77560"} -{"city": "HIGHLANDS", "loc": [-95.039286, 29.829599], "pop": 17005, "state": "TX", "_id": "77562"} -{"city": "HITCHCOCK", "loc": [-94.992591, 29.339835], "pop": 8591, "state": "TX", "_id": "77563"} -{"city": "HULL", "loc": [-94.660382, 30.13337], "pop": 3386, "state": "TX", "_id": "77564"} -{"city": "CLEAR LAKE SHORE", "loc": [-95.039209, 29.543823], "pop": 4101, "state": "TX", "_id": "77565"} -{"city": "LAKE JACKSON", "loc": [-95.440119, 29.039275], "pop": 24000, "state": "TX", "_id": "77566"} -{"city": "LA MARQUE", "loc": [-94.974159, 29.3676], "pop": 13884, "state": "TX", "_id": "77568"} -{"city": "SHOREACRES", "loc": [-95.05721, 29.660098], "pop": 31556, "state": "TX", "_id": "77571"} -{"city": "LEAGUE CITY", "loc": [-95.096274, 29.517281], "pop": 41580, "state": "TX", "_id": "77573"} -{"city": "AMES", "loc": [-94.763819, 30.072794], "pop": 14511, "state": "TX", "_id": "77575"} -{"city": "LIVERPOOL", "loc": [-95.240754, 29.311457], "pop": 2023, "state": "TX", "_id": "77577"} -{"city": "MANVEL", "loc": [-95.35033, 29.469381], "pop": 4754, "state": "TX", "_id": "77578"} -{"city": "PEARLAND", "loc": [-95.272069, 29.561656], "pop": 20807, "state": "TX", "_id": "77581"} -{"city": "ROSHARON", "loc": [-95.453732, 29.420329], "pop": 8495, "state": "TX", "_id": "77583"} -{"city": "PEARLAND", "loc": [-95.320778, 29.540479], "pop": 14234, "state": "TX", "_id": "77584"} -{"city": "SARATOGA", "loc": [-94.571718, 30.339817], "pop": 1921, "state": "TX", "_id": "77585"} -{"city": "EL LAGO", "loc": [-95.028739, 29.572895], "pop": 9490, "state": "TX", "_id": "77586"} -{"city": "SOUTH HOUSTON", "loc": [-95.22582, 29.660097], "pop": 16109, "state": "TX", "_id": "77587"} -{"city": "TEXAS CITY", "loc": [-94.920298, 29.396984], "pop": 30108, "state": "TX", "_id": "77590"} -{"city": "TEXAS CITY", "loc": [-94.994204, 29.389097], "pop": 10099, "state": "TX", "_id": "77591"} -{"city": "WALLISVILLE", "loc": [-94.675896, 29.859096], "pop": 1072, "state": "TX", "_id": "77597"} -{"city": "WEBSTER", "loc": [-95.143985, 29.55641], "pop": 11453, "state": "TX", "_id": "77598"} -{"city": "BRIDGE CITY", "loc": [-93.847293, 30.04054], "pop": 13688, "state": "TX", "_id": "77611"} -{"city": "BUNA", "loc": [-93.991263, 30.413209], "pop": 9023, "state": "TX", "_id": "77612"} -{"city": "DEWEYVILLE", "loc": [-93.77305, 30.289295], "pop": 3133, "state": "TX", "_id": "77614"} -{"city": "FRED", "loc": [-94.185948, 30.598657], "pop": 1973, "state": "TX", "_id": "77616"} -{"city": "GROVES", "loc": [-93.915187, 29.944779], "pop": 16865, "state": "TX", "_id": "77619"} -{"city": "HAMSHIRE", "loc": [-94.318739, 29.866769], "pop": 571, "state": "TX", "_id": "77622"} -{"city": "HILLISTER", "loc": [-94.407588, 30.689911], "pop": 1326, "state": "TX", "_id": "77624"} -{"city": "KOUNTZE", "loc": [-94.325905, 30.370316], "pop": 7073, "state": "TX", "_id": "77625"} -{"city": "NEDERLAND", "loc": [-94.001192, 29.971609], "pop": 22035, "state": "TX", "_id": "77627"} -{"city": "WEST ORANGE", "loc": [-93.771883, 30.125167], "pop": 39125, "state": "TX", "_id": "77630"} -{"city": "PORT ACRES", "loc": [-93.962562, 29.882557], "pop": 24605, "state": "TX", "_id": "77640"} -{"city": "PORT ARTHUR", "loc": [-93.926962, 29.92119], "pop": 33943, "state": "TX", "_id": "77642"} -{"city": "CRYSTAL BEACH", "loc": [-94.611678, 29.459788], "pop": 2807, "state": "TX", "_id": "77650"} -{"city": "PORT NECHES", "loc": [-93.962624, 29.976983], "pop": 13009, "state": "TX", "_id": "77651"} -{"city": "SILSBEE", "loc": [-94.190726, 30.324387], "pop": 27117, "state": "TX", "_id": "77656"} -{"city": "SOUR LAKE", "loc": [-94.373341, 30.149134], "pop": 4138, "state": "TX", "_id": "77659"} -{"city": "SPURGER", "loc": [-94.212745, 30.778057], "pop": 2516, "state": "TX", "_id": "77660"} -{"city": "VIDOR", "loc": [-94.000779, 30.15018], "pop": 27702, "state": "TX", "_id": "77662"} -{"city": "WARREN", "loc": [-94.411974, 30.597776], "pop": 2465, "state": "TX", "_id": "77664"} -{"city": "WINNIE", "loc": [-94.339499, 29.815676], "pop": 2716, "state": "TX", "_id": "77665"} -{"city": "BEAUMONT", "loc": [-94.103896, 30.068805], "pop": 18121, "state": "TX", "_id": "77701"} -{"city": "BEAUMONT", "loc": [-94.125412, 30.087057], "pop": 4396, "state": "TX", "_id": "77702"} -{"city": "BEAUMONT", "loc": [-94.119698, 30.113201], "pop": 16003, "state": "TX", "_id": "77703"} -{"city": "BEAUMONT", "loc": [-94.115673, 30.021128], "pop": 26134, "state": "TX", "_id": "77705"} -{"city": "BEAUMONT", "loc": [-94.164816, 30.094834], "pop": 25388, "state": "TX", "_id": "77706"} -{"city": "BEAUMONT", "loc": [-94.175541, 30.068567], "pop": 15366, "state": "TX", "_id": "77707"} -{"city": "BEAUMONT", "loc": [-94.160357, 30.139957], "pop": 10782, "state": "TX", "_id": "77708"} -{"city": "BEAUMONT", "loc": [-94.260719, 30.084996], "pop": 10798, "state": "TX", "_id": "77713"} -{"city": "BRYAN", "loc": [-96.36616, 30.632698], "pop": 12190, "state": "TX", "_id": "77801"} -{"city": "BRYAN", "loc": [-96.335143, 30.658171], "pop": 19306, "state": "TX", "_id": "77802"} -{"city": "BRYAN", "loc": [-96.371398, 30.691293], "pop": 32052, "state": "TX", "_id": "77803"} -{"city": "ANDERSON", "loc": [-96.001822, 30.544291], "pop": 1792, "state": "TX", "_id": "77830"} -{"city": "SINGLETON", "loc": [-95.933569, 30.764059], "pop": 1079, "state": "TX", "_id": "77831"} -{"city": "BRENHAM", "loc": [-96.402769, 30.17736], "pop": 20178, "state": "TX", "_id": "77833"} -{"city": "BURTON", "loc": [-96.592475, 30.176744], "pop": 2444, "state": "TX", "_id": "77835"} -{"city": "CALDWELL", "loc": [-96.714292, 30.529819], "pop": 7873, "state": "TX", "_id": "77836"} -{"city": "CALVERT", "loc": [-96.67107, 30.978211], "pop": 1829, "state": "TX", "_id": "77837"} -{"city": "COLLEGE STATION", "loc": [-96.31227, 30.604476], "pop": 45766, "state": "TX", "_id": "77840"} -{"city": "COLLEGE STATION", "loc": [-96.340001, 30.614738], "pop": 10425, "state": "TX", "_id": "77843"} -{"city": "COLLEGE STATION", "loc": [-96.317113, 30.511811], "pop": 1908, "state": "TX", "_id": "77845"} -{"city": "CONCORD", "loc": [-96.102643, 31.263144], "pop": 143, "state": "TX", "_id": "77850"} -{"city": "DIME BOX", "loc": [-96.824781, 30.35786], "pop": 699, "state": "TX", "_id": "77853"} -{"city": "FRANKLIN", "loc": [-96.442643, 31.035013], "pop": 4144, "state": "TX", "_id": "77856"} -{"city": "HEARNE", "loc": [-96.584256, 30.86686], "pop": 7655, "state": "TX", "_id": "77859"} -{"city": "IOLA", "loc": [-96.091075, 30.732637], "pop": 1418, "state": "TX", "_id": "77861"} -{"city": "MADISONVILLE", "loc": [-95.909094, 30.953335], "pop": 5532, "state": "TX", "_id": "77864"} -{"city": "MARQUEZ", "loc": [-96.237499, 31.230889], "pop": 1047, "state": "TX", "_id": "77865"} -{"city": "NAVASOTA", "loc": [-96.059357, 30.357645], "pop": 12634, "state": "TX", "_id": "77868"} -{"city": "HILLTOP LAKES", "loc": [-96.147274, 31.087473], "pop": 2540, "state": "TX", "_id": "77871"} -{"city": "NORTH ZULCH", "loc": [-96.092486, 30.928531], "pop": 1887, "state": "TX", "_id": "77872"} -{"city": "RICHARDS", "loc": [-95.861053, 30.538166], "pop": 787, "state": "TX", "_id": "77873"} -{"city": "SOMERVILLE", "loc": [-96.535825, 30.407585], "pop": 5752, "state": "TX", "_id": "77879"} -{"city": "WASHINGTON", "loc": [-96.224948, 30.31819], "pop": 616, "state": "TX", "_id": "77880"} -{"city": "VICTORIA", "loc": [-96.999347, 28.808953], "pop": 50119, "state": "TX", "_id": "77901"} -{"city": "VICTORIA", "loc": [-96.998993, 28.867482], "pop": 18982, "state": "TX", "_id": "77904"} -{"city": "BLOOMINGTON", "loc": [-96.884076, 28.68898], "pop": 4452, "state": "TX", "_id": "77951"} -{"city": "CUERO", "loc": [-97.281247, 29.090969], "pop": 8321, "state": "TX", "_id": "77954"} -{"city": "EDNA", "loc": [-96.648766, 28.952714], "pop": 8987, "state": "TX", "_id": "77957"} -{"city": "GANADO", "loc": [-96.503078, 29.03084], "pop": 2876, "state": "TX", "_id": "77962"} -{"city": "GOLIAD", "loc": [-97.378321, 28.699257], "pop": 5980, "state": "TX", "_id": "77963"} -{"city": "HALLETTSVILLE", "loc": [-96.923423, 29.442609], "pop": 6905, "state": "TX", "_id": "77964"} -{"city": "INEZ", "loc": [-96.800294, 28.899416], "pop": 808, "state": "TX", "_id": "77968"} -{"city": "LOLITA", "loc": [-96.47933, 28.772449], "pop": 2632, "state": "TX", "_id": "77971"} -{"city": "MEYERSVILLE", "loc": [-97.304161, 28.922921], "pop": 45, "state": "TX", "_id": "77974"} -{"city": "MOULTON", "loc": [-97.103122, 29.569992], "pop": 2459, "state": "TX", "_id": "77975"} -{"city": "PORT LAVACA", "loc": [-96.625941, 28.601135], "pop": 15627, "state": "TX", "_id": "77979"} -{"city": "PORT O CONNOR", "loc": [-96.775131, 28.140032], "pop": 5, "state": "TX", "_id": "77982"} -{"city": "SEADRIFT", "loc": [-96.702279, 28.410113], "pop": 1965, "state": "TX", "_id": "77983"} -{"city": "SHINER", "loc": [-97.163955, 29.428038], "pop": 3596, "state": "TX", "_id": "77984"} -{"city": "TIVOLI", "loc": [-96.887172, 28.440122], "pop": 965, "state": "TX", "_id": "77990"} -{"city": "WESTHOFF", "loc": [-97.351102, 29.054983], "pop": 2069, "state": "TX", "_id": "77994"} -{"city": "YOAKUM", "loc": [-97.13063, 29.283954], "pop": 9747, "state": "TX", "_id": "77995"} -{"city": "ATASCOSA", "loc": [-98.74212, 29.270501], "pop": 2943, "state": "TX", "_id": "78002"} -{"city": "BANDERA", "loc": [-99.045279, 29.72755], "pop": 5538, "state": "TX", "_id": "78003"} -{"city": "BERGHEIM", "loc": [-98.593258, 29.839873], "pop": 763, "state": "TX", "_id": "78004"} -{"city": "BIGFOOT", "loc": [-98.85823, 29.053073], "pop": 328, "state": "TX", "_id": "78005"} -{"city": "SISTERDALE", "loc": [-98.713406, 29.77774], "pop": 14427, "state": "TX", "_id": "78006"} -{"city": "CALLIHAM", "loc": [-98.407873, 28.417682], "pop": 230, "state": "TX", "_id": "78007"} -{"city": "CAMPBELLTON", "loc": [-98.256573, 28.76699], "pop": 475, "state": "TX", "_id": "78008"} -{"city": "CASTROVILLE", "loc": [-98.882391, 29.35532], "pop": 4262, "state": "TX", "_id": "78009"} -{"city": "CAMP VERDE", "loc": [-99.00714, 29.939658], "pop": 2704, "state": "TX", "_id": "78010"} -{"city": "CHARLOTTE", "loc": [-98.707001, 28.864871], "pop": 1990, "state": "TX", "_id": "78011"} -{"city": "COMFORT", "loc": [-98.843772, 29.979072], "pop": 3253, "state": "TX", "_id": "78013"} -{"city": "COTULLA", "loc": [-99.232818, 28.439837], "pop": 4369, "state": "TX", "_id": "78014"} -{"city": "DEVINE", "loc": [-98.908994, 29.152078], "pop": 6887, "state": "TX", "_id": "78016"} -{"city": "DILLEY", "loc": [-99.174684, 28.678201], "pop": 3396, "state": "TX", "_id": "78017"} -{"city": "ENCINAL", "loc": [-99.340902, 28.051329], "pop": 821, "state": "TX", "_id": "78019"} -{"city": "FOWLERTON", "loc": [-98.851821, 28.488666], "pop": 76, "state": "TX", "_id": "78021"} -{"city": "GEORGE WEST", "loc": [-98.116159, 28.320388], "pop": 4093, "state": "TX", "_id": "78022"} -{"city": "GREY FOREST", "loc": [-98.703534, 29.592203], "pop": 3552, "state": "TX", "_id": "78023"} -{"city": "HUNT", "loc": [-99.482329, 30.002694], "pop": 263, "state": "TX", "_id": "78024"} -{"city": "INGRAM", "loc": [-99.269022, 30.073126], "pop": 7005, "state": "TX", "_id": "78025"} -{"city": "JOURDANTON", "loc": [-98.544037, 28.902985], "pop": 4615, "state": "TX", "_id": "78026"} -{"city": "KENDALIA", "loc": [-98.516556, 29.940619], "pop": 251, "state": "TX", "_id": "78027"} -{"city": "KERRVILLE", "loc": [-99.140817, 30.041647], "pop": 26128, "state": "TX", "_id": "78028"} -{"city": "LA COSTE", "loc": [-98.812466, 29.308178], "pop": 1320, "state": "TX", "_id": "78039"} -{"city": "LAREDO", "loc": [-99.498579, 27.515538], "pop": 43486, "state": "TX", "_id": "78040"} -{"city": "LAREDO", "loc": [-99.490653, 27.556933], "pop": 46156, "state": "TX", "_id": "78041"} -{"city": "RIO BRAVO", "loc": [-99.465488, 27.481537], "pop": 42505, "state": "TX", "_id": "78043"} -{"city": "LYTLE", "loc": [-98.794489, 29.236568], "pop": 2078, "state": "TX", "_id": "78052"} -{"city": "MC COY", "loc": [-98.371565, 28.792661], "pop": 34, "state": "TX", "_id": "78053"} -{"city": "MEDINA", "loc": [-99.306431, 29.790689], "pop": 1590, "state": "TX", "_id": "78055"} -{"city": "MICO", "loc": [-98.882106, 29.59105], "pop": 230, "state": "TX", "_id": "78056"} -{"city": "MOORE", "loc": [-98.987403, 29.035377], "pop": 767, "state": "TX", "_id": "78057"} -{"city": "MOUNTAIN HOME", "loc": [-99.318607, 30.213249], "pop": 207, "state": "TX", "_id": "78058"} -{"city": "NATALIA", "loc": [-98.855158, 29.211669], "pop": 4136, "state": "TX", "_id": "78059"} -{"city": "OAKVILLE", "loc": [-98.055392, 28.464402], "pop": 261, "state": "TX", "_id": "78060"} -{"city": "PEARSALL", "loc": [-99.094361, 28.892317], "pop": 8981, "state": "TX", "_id": "78061"} -{"city": "LAKEHILLS", "loc": [-98.922118, 29.631513], "pop": 3187, "state": "TX", "_id": "78063"} -{"city": "PLEASANTON", "loc": [-98.483061, 28.992368], "pop": 11977, "state": "TX", "_id": "78064"} -{"city": "POTEET", "loc": [-98.624071, 29.099405], "pop": 9523, "state": "TX", "_id": "78065"} -{"city": "RIOMEDINA", "loc": [-98.866865, 29.490395], "pop": 687, "state": "TX", "_id": "78066"} -{"city": "SAN YGNACIO", "loc": [-99.427148, 27.062523], "pop": 871, "state": "TX", "_id": "78067"} -{"city": "SOMERSET", "loc": [-98.621401, 29.211561], "pop": 3477, "state": "TX", "_id": "78069"} -{"city": "SPRING BRANCH", "loc": [-98.378786, 29.923815], "pop": 1544, "state": "TX", "_id": "78070"} -{"city": "THREE RIVERS", "loc": [-98.178162, 28.475646], "pop": 3190, "state": "TX", "_id": "78071"} -{"city": "TILDEN", "loc": [-98.569322, 28.419755], "pop": 575, "state": "TX", "_id": "78072"} -{"city": "VON ORMY", "loc": [-98.667741, 29.274979], "pop": 2911, "state": "TX", "_id": "78073"} -{"city": "WHITSETT", "loc": [-98.256484, 28.637316], "pop": 135, "state": "TX", "_id": "78075"} -{"city": "ZAPATA", "loc": [-99.250625, 26.88966], "pop": 8408, "state": "TX", "_id": "78076"} -{"city": "ADKINS", "loc": [-98.26504, 29.380542], "pop": 3611, "state": "TX", "_id": "78101"} -{"city": "BEEVILLE", "loc": [-97.761571, 28.422246], "pop": 23211, "state": "TX", "_id": "78102"} -{"city": "CIBOLO", "loc": [-98.227983, 29.574971], "pop": 6062, "state": "TX", "_id": "78108"} -{"city": "CONVERSE", "loc": [-98.321673, 29.517331], "pop": 13291, "state": "TX", "_id": "78109"} -{"city": "ECLETO", "loc": [-97.741185, 29.020128], "pop": 0, "state": "TX", "_id": "78111"} -{"city": "ELMENDORF", "loc": [-98.371982, 29.230793], "pop": 2251, "state": "TX", "_id": "78112"} -{"city": "FALLS CITY", "loc": [-98.015632, 28.981413], "pop": 964, "state": "TX", "_id": "78113"} -{"city": "FLORESVILLE", "loc": [-98.193589, 29.169338], "pop": 11510, "state": "TX", "_id": "78114"} -{"city": "GILLETT", "loc": [-97.834325, 29.051353], "pop": 641, "state": "TX", "_id": "78116"} -{"city": "HOBSON", "loc": [-97.970743, 28.944499], "pop": 302, "state": "TX", "_id": "78117"} -{"city": "KARNES CITY", "loc": [-97.90707, 28.882757], "pop": 3827, "state": "TX", "_id": "78118"} -{"city": "KENEDY", "loc": [-97.845601, 28.804584], "pop": 5118, "state": "TX", "_id": "78119"} -{"city": "LA VERNIA", "loc": [-98.112971, 29.350905], "pop": 4529, "state": "TX", "_id": "78121"} -{"city": "LEESVILLE", "loc": [-97.756622, 29.396142], "pop": 206, "state": "TX", "_id": "78122"} -{"city": "MC QUEENEY", "loc": [-98.037591, 29.605655], "pop": 2055, "state": "TX", "_id": "78123"} -{"city": "MARION", "loc": [-98.151709, 29.56835], "pop": 4167, "state": "TX", "_id": "78124"} -{"city": "CANYON LAKE", "loc": [-98.113041, 29.694733], "pop": 32975, "state": "TX", "_id": "78130"} -{"city": "CANYON LAKE", "loc": [-98.167212, 29.72939], "pop": 6412, "state": "TX", "_id": "78132"} -{"city": "CANYON LAKE", "loc": [-98.249412, 29.870984], "pop": 8428, "state": "TX", "_id": "78133"} -{"city": "NIXON", "loc": [-97.752898, 29.301649], "pop": 2953, "state": "TX", "_id": "78140"} -{"city": "NORDHEIM", "loc": [-97.594579, 28.914225], "pop": 655, "state": "TX", "_id": "78141"} -{"city": "POTH", "loc": [-98.08247, 29.06191], "pop": 3000, "state": "TX", "_id": "78147"} -{"city": "RANDOLPH A F B", "loc": [-98.306742, 29.551608], "pop": 13215, "state": "TX", "_id": "78148"} -{"city": "RANDOLPH A F B", "loc": [-98.279193, 29.53021], "pop": 4079, "state": "TX", "_id": "78150"} -{"city": "RUNGE", "loc": [-97.713824, 28.887556], "pop": 1603, "state": "TX", "_id": "78151"} -{"city": "SAINT HEDWIG", "loc": [-98.195223, 29.435284], "pop": 1685, "state": "TX", "_id": "78152"} -{"city": "SELMA", "loc": [-98.272214, 29.568159], "pop": 10332, "state": "TX", "_id": "78154"} -{"city": "SEGUIN", "loc": [-97.962801, 29.561316], "pop": 32348, "state": "TX", "_id": "78155"} -{"city": "SMILEY", "loc": [-97.622712, 29.265529], "pop": 972, "state": "TX", "_id": "78159"} -{"city": "STOCKDALE", "loc": [-97.935082, 29.231907], "pop": 3085, "state": "TX", "_id": "78160"} -{"city": "SUTHERLAND SPRIN", "loc": [-98.070794, 29.277831], "pop": 1051, "state": "TX", "_id": "78161"} -{"city": "WETMORE", "loc": [-98.437846, 29.780175], "pop": 5491, "state": "TX", "_id": "78163"} -{"city": "YORKTOWN", "loc": [-97.512056, 28.989191], "pop": 3733, "state": "TX", "_id": "78164"} -{"city": "BALCONES HEIGHTS", "loc": [-98.526352, 29.468525], "pop": 43037, "state": "TX", "_id": "78201"} -{"city": "SAN ANTONIO", "loc": [-98.460112, 29.427462], "pop": 12043, "state": "TX", "_id": "78202"} -{"city": "SAN ANTONIO", "loc": [-98.460127, 29.414799], "pop": 7261, "state": "TX", "_id": "78203"} -{"city": "SAN ANTONIO", "loc": [-98.5063, 29.400217], "pop": 11526, "state": "TX", "_id": "78204"} -{"city": "SAN ANTONIO", "loc": [-98.492509, 29.423711], "pop": 1714, "state": "TX", "_id": "78205"} -{"city": "SAN ANTONIO", "loc": [-98.525967, 29.422855], "pop": 58355, "state": "TX", "_id": "78207"} -{"city": "SAN ANTONIO", "loc": [-98.458983, 29.440039], "pop": 5007, "state": "TX", "_id": "78208"} -{"city": "ALAMO HEIGHTS", "loc": [-98.455774, 29.488623], "pop": 34701, "state": "TX", "_id": "78209"} -{"city": "SAN ANTONIO", "loc": [-98.465796, 29.397718], "pop": 39300, "state": "TX", "_id": "78210"} -{"city": "SAN ANTONIO", "loc": [-98.545219, 29.358366], "pop": 30417, "state": "TX", "_id": "78211"} -{"city": "OLMOS PARK", "loc": [-98.495815, 29.461181], "pop": 29762, "state": "TX", "_id": "78212"} -{"city": "CASTLE HILLS", "loc": [-98.522679, 29.513406], "pop": 36060, "state": "TX", "_id": "78213"} -{"city": "SAN ANTONIO", "loc": [-98.492436, 29.364115], "pop": 23338, "state": "TX", "_id": "78214"} -{"city": "SAN ANTONIO", "loc": [-98.479338, 29.441338], "pop": 1264, "state": "TX", "_id": "78215"} -{"city": "SAN ANTONIO", "loc": [-98.497511, 29.533387], "pop": 30435, "state": "TX", "_id": "78216"} -{"city": "SAN ANTONIO", "loc": [-98.419444, 29.539525], "pop": 27925, "state": "TX", "_id": "78217"} -{"city": "SAN ANTONIO", "loc": [-98.403184, 29.496852], "pop": 29276, "state": "TX", "_id": "78218"} -{"city": "KIRBY", "loc": [-98.397315, 29.448794], "pop": 14249, "state": "TX", "_id": "78219"} -{"city": "SAN ANTONIO", "loc": [-98.412791, 29.410641], "pop": 17035, "state": "TX", "_id": "78220"} -{"city": "SAN ANTONIO", "loc": [-98.505417, 29.330913], "pop": 35392, "state": "TX", "_id": "78221"} -{"city": "SAN ANTONIO", "loc": [-98.396005, 29.383113], "pop": 12729, "state": "TX", "_id": "78222"} -{"city": "SAN ANTONIO", "loc": [-98.435628, 29.357869], "pop": 38381, "state": "TX", "_id": "78223"} -{"city": "SAN ANTONIO", "loc": [-98.539335, 29.337432], "pop": 14894, "state": "TX", "_id": "78224"} -{"city": "SAN ANTONIO", "loc": [-98.524494, 29.387497], "pop": 13803, "state": "TX", "_id": "78225"} -{"city": "SAN ANTONIO", "loc": [-98.551095, 29.393001], "pop": 7141, "state": "TX", "_id": "78226"} -{"city": "SAN ANTONIO", "loc": [-98.643311, 29.402687], "pop": 42329, "state": "TX", "_id": "78227"} -{"city": "SAN ANTONIO", "loc": [-98.569871, 29.458937], "pop": 58136, "state": "TX", "_id": "78228"} -{"city": "SAN ANTONIO", "loc": [-98.569726, 29.504228], "pop": 22681, "state": "TX", "_id": "78229"} -{"city": "SAN ANTONIO", "loc": [-98.552117, 29.540738], "pop": 30253, "state": "TX", "_id": "78230"} -{"city": "SHAVANO PARK", "loc": [-98.536817, 29.571434], "pop": 7504, "state": "TX", "_id": "78231"} -{"city": "HOLLYWOOD PARK", "loc": [-98.4673, 29.582833], "pop": 27332, "state": "TX", "_id": "78232"} -{"city": "LIVE OAK", "loc": [-98.369128, 29.554741], "pop": 36334, "state": "TX", "_id": "78233"} -{"city": "FORT SAM HOUSTON", "loc": [-98.435404, 29.461961], "pop": 8258, "state": "TX", "_id": "78234"} -{"city": "BROOKS A F B", "loc": [-98.439444, 29.341733], "pop": 885, "state": "TX", "_id": "78235"} -{"city": "WILFORD HALL U S", "loc": [-98.613367, 29.394267], "pop": 8707, "state": "TX", "_id": "78236"} -{"city": "SAN ANTONIO", "loc": [-98.564546, 29.420758], "pop": 38900, "state": "TX", "_id": "78237"} -{"city": "LEON VALLEY", "loc": [-98.615451, 29.476833], "pop": 20840, "state": "TX", "_id": "78238"} -{"city": "WINDCREST", "loc": [-98.361604, 29.515686], "pop": 21781, "state": "TX", "_id": "78239"} -{"city": "SAN ANTONIO", "loc": [-98.600566, 29.518896], "pop": 33776, "state": "TX", "_id": "78240"} -{"city": "KELLY A F B", "loc": [-98.578063, 29.392432], "pop": 1784, "state": "TX", "_id": "78241"} -{"city": "SAN ANTONIO", "loc": [-98.610927, 29.350905], "pop": 24343, "state": "TX", "_id": "78242"} -{"city": "SAN ANTONIO", "loc": [-98.347585, 29.479264], "pop": 13798, "state": "TX", "_id": "78244"} -{"city": "SAN ANTONIO", "loc": [-98.689494, 29.418927], "pop": 20410, "state": "TX", "_id": "78245"} -{"city": "WETMORE", "loc": [-98.409783, 29.577604], "pop": 25572, "state": "TX", "_id": "78247"} -{"city": "SAN ANTONIO", "loc": [-98.520105, 29.58936], "pop": 4469, "state": "TX", "_id": "78248"} -{"city": "SAN ANTONIO", "loc": [-98.611666, 29.561245], "pop": 19127, "state": "TX", "_id": "78249"} -{"city": "SAN ANTONIO", "loc": [-98.668765, 29.505394], "pop": 43845, "state": "TX", "_id": "78250"} -{"city": "SAN ANTONIO", "loc": [-98.655472, 29.459743], "pop": 15297, "state": "TX", "_id": "78251"} -{"city": "SAN ANTONIO", "loc": [-98.646395, 29.346015], "pop": 1378, "state": "TX", "_id": "78252"} -{"city": "SAN ANTONIO", "loc": [-98.747931, 29.459923], "pop": 3861, "state": "TX", "_id": "78253"} -{"city": "SAN ANTONIO", "loc": [-98.724841, 29.54091], "pop": 1340, "state": "TX", "_id": "78254"} -{"city": "SAN ANTONIO", "loc": [-98.655572, 29.636875], "pop": 2544, "state": "TX", "_id": "78255"} -{"city": "SAN ANTONIO", "loc": [-98.625215, 29.616946], "pop": 1237, "state": "TX", "_id": "78256"} -{"city": "SAN ANTONIO", "loc": [-98.613701, 29.64953], "pop": 1360, "state": "TX", "_id": "78257"} -{"city": "SAN ANTONIO", "loc": [-98.496699, 29.65624], "pop": 2877, "state": "TX", "_id": "78258"} -{"city": "SAN ANTONIO", "loc": [-98.444495, 29.628331], "pop": 3865, "state": "TX", "_id": "78259"} -{"city": "SAN ANTONIO", "loc": [-98.475908, 29.702578], "pop": 1684, "state": "TX", "_id": "78260"} -{"city": "SAN ANTONIO", "loc": [-98.419092, 29.705463], "pop": 487, "state": "TX", "_id": "78261"} -{"city": "SAN ANTONIO", "loc": [-98.317386, 29.36143], "pop": 2836, "state": "TX", "_id": "78263"} -{"city": "SAN ANTONIO", "loc": [-98.472272, 29.173345], "pop": 3723, "state": "TX", "_id": "78264"} -{"city": "GARDEN RIDGE", "loc": [-98.312774, 29.644226], "pop": 2016, "state": "TX", "_id": "78266"} -{"city": "ALICE", "loc": [-98.083622, 27.743171], "pop": 28292, "state": "TX", "_id": "78332"} -{"city": "ARANSAS PASS", "loc": [-97.159091, 27.909498], "pop": 9087, "state": "TX", "_id": "78336"} -{"city": "ARMSTRONG", "loc": [-97.709291, 26.738706], "pop": 148, "state": "TX", "_id": "78338"} -{"city": "BAYSIDE", "loc": [-97.210643, 28.096758], "pop": 454, "state": "TX", "_id": "78340"} -{"city": "BISHOP", "loc": [-97.783031, 27.588564], "pop": 4608, "state": "TX", "_id": "78343"} -{"city": "BRUNI", "loc": [-98.850108, 27.435329], "pop": 508, "state": "TX", "_id": "78344"} -{"city": "CONCEPCION", "loc": [-98.381489, 27.544266], "pop": 3458, "state": "TX", "_id": "78349"} -{"city": "ENCINO", "loc": [-98.192168, 26.924862], "pop": 703, "state": "TX", "_id": "78353"} -{"city": "FALFURRIAS", "loc": [-98.140844, 27.22416], "pop": 7501, "state": "TX", "_id": "78355"} -{"city": "FREER", "loc": [-98.606129, 27.879984], "pop": 3922, "state": "TX", "_id": "78357"} -{"city": "FULTON", "loc": [-96.808377, 28.223448], "pop": 0, "state": "TX", "_id": "78358"} -{"city": "GUERRA", "loc": [-98.918886, 26.912753], "pop": 34, "state": "TX", "_id": "78360"} -{"city": "HEBBRONVILLE", "loc": [-98.682888, 27.2997], "pop": 5078, "state": "TX", "_id": "78361"} -{"city": "INGLESIDE", "loc": [-97.206906, 27.868238], "pop": 5871, "state": "TX", "_id": "78362"} -{"city": "KINGSVILLE NAVAL", "loc": [-97.859598, 27.507418], "pop": 28435, "state": "TX", "_id": "78363"} -{"city": "MATHIS", "loc": [-97.809659, 28.080208], "pop": 11068, "state": "TX", "_id": "78368"} -{"city": "MIRANDO CITY", "loc": [-99.001117, 27.445038], "pop": 584, "state": "TX", "_id": "78369"} -{"city": "ODEM", "loc": [-97.583752, 27.940306], "pop": 3299, "state": "TX", "_id": "78370"} -{"city": "ORANGE GROVE", "loc": [-97.983835, 27.948659], "pop": 4339, "state": "TX", "_id": "78372"} -{"city": "PORTLAND", "loc": [-97.316931, 27.893547], "pop": 15830, "state": "TX", "_id": "78374"} -{"city": "PREMONT", "loc": [-98.133018, 27.354444], "pop": 4051, "state": "TX", "_id": "78375"} -{"city": "REALITOS", "loc": [-98.535493, 27.416283], "pop": 520, "state": "TX", "_id": "78376"} -{"city": "REFUGIO", "loc": [-97.276665, 28.316944], "pop": 4004, "state": "TX", "_id": "78377"} -{"city": "RIVIERA", "loc": [-97.778707, 27.321735], "pop": 1839, "state": "TX", "_id": "78379"} -{"city": "ROBSTOWN", "loc": [-97.699523, 27.798395], "pop": 24903, "state": "TX", "_id": "78380"} -{"city": "ROCKPORT", "loc": [-97.068773, 28.030778], "pop": 16944, "state": "TX", "_id": "78382"} -{"city": "SANDIA", "loc": [-97.897849, 28.070879], "pop": 2053, "state": "TX", "_id": "78383"} -{"city": "SAN DIEGO", "loc": [-98.250297, 27.76535], "pop": 5018, "state": "TX", "_id": "78384"} -{"city": "SARITA", "loc": [-97.857627, 27.149622], "pop": 312, "state": "TX", "_id": "78385"} -{"city": "SINTON", "loc": [-97.519582, 28.033895], "pop": 9051, "state": "TX", "_id": "78387"} -{"city": "SKIDMORE", "loc": [-97.666071, 28.230577], "pop": 1785, "state": "TX", "_id": "78389"} -{"city": "TAFT", "loc": [-97.396623, 27.976517], "pop": 6140, "state": "TX", "_id": "78390"} -{"city": "TYNAN", "loc": [-97.754881, 28.169341], "pop": 327, "state": "TX", "_id": "78391"} -{"city": "WOODSBORO", "loc": [-97.31921, 28.223222], "pop": 2559, "state": "TX", "_id": "78393"} -{"city": "CORPUS CHRISTI", "loc": [-97.402994, 27.794086], "pop": 5811, "state": "TX", "_id": "78401"} -{"city": "CORPUS CHRISTI", "loc": [-97.385659, 27.82621], "pop": 451, "state": "TX", "_id": "78402"} -{"city": "CORPUS CHRISTI", "loc": [-97.401255, 27.768329], "pop": 17395, "state": "TX", "_id": "78404"} -{"city": "CORPUS CHRISTI", "loc": [-97.427132, 27.776234], "pop": 17437, "state": "TX", "_id": "78405"} -{"city": "CORPUS CHRISTI", "loc": [-97.51445, 27.768412], "pop": 1556, "state": "TX", "_id": "78406"} -{"city": "CORPUS CHRISTI", "loc": [-97.435597, 27.804195], "pop": 6334, "state": "TX", "_id": "78407"} -{"city": "CORPUS CHRISTI", "loc": [-97.43815, 27.794477], "pop": 10071, "state": "TX", "_id": "78408"} -{"city": "CORPUS CHRISTI", "loc": [-97.527034, 27.814555], "pop": 2655, "state": "TX", "_id": "78409"} -{"city": "CORPUS CHRISTI", "loc": [-97.596002, 27.84585], "pop": 20860, "state": "TX", "_id": "78410"} -{"city": "CORPUS CHRISTI", "loc": [-97.387732, 27.731139], "pop": 27625, "state": "TX", "_id": "78411"} -{"city": "CORPUS CHRISTI", "loc": [-97.353694, 27.70608], "pop": 33510, "state": "TX", "_id": "78412"} -{"city": "CORPUS CHRISTI", "loc": [-97.39832, 27.691041], "pop": 27278, "state": "TX", "_id": "78413"} -{"city": "CORPUS CHRISTI", "loc": [-97.365016, 27.677016], "pop": 8600, "state": "TX", "_id": "78414"} -{"city": "CORPUS CHRISTI", "loc": [-97.40778, 27.726204], "pop": 39998, "state": "TX", "_id": "78415"} -{"city": "CORPUS CHRISTI", "loc": [-97.43468, 27.753593], "pop": 16634, "state": "TX", "_id": "78416"} -{"city": "CORPUS CHRISTI", "loc": [-97.449429, 27.728964], "pop": 3075, "state": "TX", "_id": "78417"} -{"city": "CORPUS CHRISTI", "loc": [-97.266558, 27.668531], "pop": 20449, "state": "TX", "_id": "78418"} -{"city": "CORPUS CHRISTI", "loc": [-97.27636, 27.692502], "pop": 1873, "state": "TX", "_id": "78419"} -{"city": "CORPUS CHRISTI", "loc": [-97.396624, 27.79515], "pop": 0, "state": "TX", "_id": "78473"} -{"city": "MCALLEN", "loc": [-98.235871, 26.21544], "pop": 53932, "state": "TX", "_id": "78501"} -{"city": "MCALLEN", "loc": [-98.251974, 26.177115], "pop": 14820, "state": "TX", "_id": "78503"} -{"city": "MCALLEN", "loc": [-98.230253, 26.255645], "pop": 15182, "state": "TX", "_id": "78504"} -{"city": "ALAMO", "loc": [-98.116445, 26.190578], "pop": 16555, "state": "TX", "_id": "78516"} -{"city": "BROWNSVILLE", "loc": [-97.517413, 25.933743], "pop": 50091, "state": "TX", "_id": "78520"} -{"city": "BROWNSVILLE", "loc": [-97.461236, 25.922103], "pop": 79463, "state": "TX", "_id": "78521"} -{"city": "DELMITA", "loc": [-98.396553, 26.656571], "pop": 55, "state": "TX", "_id": "78536"} -{"city": "DONNA", "loc": [-98.052925, 26.167138], "pop": 20909, "state": "TX", "_id": "78537"} -{"city": "MONTE ALTO", "loc": [-97.975473, 26.304208], "pop": 16225, "state": "TX", "_id": "78538"} -{"city": "EDINBURG", "loc": [-98.156922, 26.304221], "pop": 52534, "state": "TX", "_id": "78539"} -{"city": "GARCIASVILLE", "loc": [-98.669112, 26.312894], "pop": 5741, "state": "TX", "_id": "78547"} -{"city": "GRULLA", "loc": [-98.596041, 26.293714], "pop": 303, "state": "TX", "_id": "78548"} -{"city": "HARGILL", "loc": [-97.99933, 26.433812], "pop": 1294, "state": "TX", "_id": "78549"} -{"city": "HARLINGEN", "loc": [-97.688981, 26.195142], "pop": 43292, "state": "TX", "_id": "78550"} -{"city": "HARLINGEN", "loc": [-97.746771, 26.183069], "pop": 21672, "state": "TX", "_id": "78552"} -{"city": "HIDALGO", "loc": [-98.253641, 26.102832], "pop": 3300, "state": "TX", "_id": "78557"} -{"city": "LA FERIA", "loc": [-97.826115, 26.166556], "pop": 8446, "state": "TX", "_id": "78559"} -{"city": "LINN", "loc": [-98.17987, 26.542054], "pop": 1244, "state": "TX", "_id": "78563"} -{"city": "BAYVIEW", "loc": [-97.490402, 26.091068], "pop": 9033, "state": "TX", "_id": "78566"} -{"city": "LYFORD", "loc": [-97.781653, 26.408926], "pop": 2757, "state": "TX", "_id": "78569"} -{"city": "MERCEDES", "loc": [-97.918503, 26.15133], "pop": 21450, "state": "TX", "_id": "78570"} -{"city": "ALTON", "loc": [-98.342647, 26.24153], "pop": 67604, "state": "TX", "_id": "78572"} -{"city": "PHARR", "loc": [-98.187022, 26.177053], "pop": 36070, "state": "TX", "_id": "78577"} -{"city": "PORT ISABEL", "loc": [-97.254389, 26.08764], "pop": 9335, "state": "TX", "_id": "78578"} -{"city": "RAYMONDVILLE", "loc": [-97.79666, 26.479243], "pop": 11642, "state": "TX", "_id": "78580"} -{"city": "RIO GRANDE CITY", "loc": [-98.810444, 26.394194], "pop": 19113, "state": "TX", "_id": "78582"} -{"city": "RIO HONDO", "loc": [-97.551311, 26.233855], "pop": 4990, "state": "TX", "_id": "78583"} -{"city": "ROMA", "loc": [-99.002479, 26.421545], "pop": 14612, "state": "TX", "_id": "78584"} -{"city": "SAN BENITO", "loc": [-97.644748, 26.133659], "pop": 28609, "state": "TX", "_id": "78586"} -{"city": "SAN ISIDRO", "loc": [-98.416147, 26.721404], "pop": 468, "state": "TX", "_id": "78588"} -{"city": "SAN JUAN", "loc": [-98.153729, 26.204375], "pop": 18419, "state": "TX", "_id": "78589"} -{"city": "SAN PERLITA", "loc": [-97.615699, 26.483156], "pop": 886, "state": "TX", "_id": "78590"} -{"city": "SANTA ELENA", "loc": [-98.519412, 26.73364], "pop": 223, "state": "TX", "_id": "78591"} -{"city": "SANTA ROSA", "loc": [-97.825663, 26.255508], "pop": 3393, "state": "TX", "_id": "78593"} -{"city": "SEBASTIAN", "loc": [-97.774728, 26.34524], "pop": 2137, "state": "TX", "_id": "78594"} -{"city": "SULLIVAN CITY", "loc": [-98.562713, 26.27197], "pop": 3351, "state": "TX", "_id": "78595"} -{"city": "WESLACO", "loc": [-97.988714, 26.169444], "pop": 40652, "state": "TX", "_id": "78596"} -{"city": "SOUTH PADRE ISLA", "loc": [-97.167294, 26.111105], "pop": 1796, "state": "TX", "_id": "78597"} -{"city": "PORT MANSFIELD", "loc": [-97.427982, 26.555237], "pop": 287, "state": "TX", "_id": "78598"} -{"city": "BASTROP", "loc": [-97.292101, 30.13883], "pop": 10588, "state": "TX", "_id": "78602"} -{"city": "BEBE", "loc": [-97.626594, 29.422211], "pop": 82, "state": "TX", "_id": "78603"} -{"city": "BERTRAM", "loc": [-98.052932, 30.741082], "pop": 1622, "state": "TX", "_id": "78605"} -{"city": "BLANCO", "loc": [-98.410663, 30.087359], "pop": 3152, "state": "TX", "_id": "78606"} -{"city": "BLUFFTON", "loc": [-98.51503, 30.825633], "pop": 31, "state": "TX", "_id": "78607"} -{"city": "BRIGGS", "loc": [-97.970228, 30.932546], "pop": 715, "state": "TX", "_id": "78608"} -{"city": "BUCHANAN DAM", "loc": [-98.453226, 30.759765], "pop": 1457, "state": "TX", "_id": "78609"} -{"city": "BUDA", "loc": [-97.853422, 30.091758], "pop": 7487, "state": "TX", "_id": "78610"} -{"city": "BURNET", "loc": [-98.26424, 30.776597], "pop": 8460, "state": "TX", "_id": "78611"} -{"city": "CEDAR CREEK", "loc": [-97.497602, 30.096636], "pop": 8026, "state": "TX", "_id": "78612"} -{"city": "CEDAR PARK", "loc": [-97.817571, 30.477165], "pop": 22192, "state": "TX", "_id": "78613"} -{"city": "COST", "loc": [-97.553124, 29.432098], "pop": 286, "state": "TX", "_id": "78614"} -{"city": "COUPLAND", "loc": [-97.330364, 30.532357], "pop": 2311, "state": "TX", "_id": "78615"} -{"city": "DALE", "loc": [-97.580968, 29.952785], "pop": 2666, "state": "TX", "_id": "78616"} -{"city": "DEL VALLE", "loc": [-97.613443, 30.174492], "pop": 5635, "state": "TX", "_id": "78617"} -{"city": "DOSS", "loc": [-99.17072, 30.461319], "pop": 54, "state": "TX", "_id": "78618"} -{"city": "DRIFTWOOD", "loc": [-98.052372, 30.159351], "pop": 1064, "state": "TX", "_id": "78619"} -{"city": "DRIPPING SPRINGS", "loc": [-98.102947, 30.226768], "pop": 3453, "state": "TX", "_id": "78620"} -{"city": "ELGIN", "loc": [-97.373748, 30.323136], "pop": 9852, "state": "TX", "_id": "78621"} -{"city": "FISCHER", "loc": [-98.258283, 29.969588], "pop": 459, "state": "TX", "_id": "78623"} -{"city": "FREDERICKSBURG", "loc": [-98.879928, 30.281658], "pop": 15125, "state": "TX", "_id": "78624"} -{"city": "GEORGETOWN", "loc": [-97.670704, 30.633038], "pop": 8153, "state": "TX", "_id": "78626"} -{"city": "ANDICE", "loc": [-97.700939, 30.659364], "pop": 14100, "state": "TX", "_id": "78628"} -{"city": "GONZALES", "loc": [-97.449456, 29.50857], "pop": 11297, "state": "TX", "_id": "78629"} -{"city": "HARPER", "loc": [-99.241048, 30.281637], "pop": 1144, "state": "TX", "_id": "78631"} -{"city": "HARWOOD", "loc": [-97.490622, 29.666108], "pop": 295, "state": "TX", "_id": "78632"} -{"city": "HUTTO", "loc": [-97.567203, 30.525725], "pop": 2314, "state": "TX", "_id": "78634"} -{"city": "HYE", "loc": [-98.539607, 30.226866], "pop": 341, "state": "TX", "_id": "78635"} -{"city": "JOHNSON CITY", "loc": [-98.369082, 30.294806], "pop": 2219, "state": "TX", "_id": "78636"} -{"city": "KINGSBURY", "loc": [-97.830634, 29.672566], "pop": 1073, "state": "TX", "_id": "78638"} -{"city": "KINGSLAND", "loc": [-98.447492, 30.666212], "pop": 2797, "state": "TX", "_id": "78639"} -{"city": "UHLAND", "loc": [-97.837106, 30.0043], "pop": 8770, "state": "TX", "_id": "78640"} -{"city": "LEANDER", "loc": [-97.875166, 30.552888], "pop": 10882, "state": "TX", "_id": "78641"} -{"city": "LIBERTY HILL", "loc": [-97.931598, 30.662953], "pop": 2028, "state": "TX", "_id": "78642"} -{"city": "SUNRISE BEACH", "loc": [-98.652727, 30.722533], "pop": 4802, "state": "TX", "_id": "78643"} -{"city": "LOCKHART", "loc": [-97.676922, 29.886759], "pop": 11690, "state": "TX", "_id": "78644"} -{"city": "JONESTOWN", "loc": [-97.97023, 30.459796], "pop": 4060, "state": "TX", "_id": "78645"} -{"city": "LULING", "loc": [-97.649947, 29.682621], "pop": 6587, "state": "TX", "_id": "78648"} -{"city": "MC DADE", "loc": [-97.238556, 30.296816], "pop": 721, "state": "TX", "_id": "78650"} -{"city": "MANCHACA", "loc": [-97.853793, 30.127267], "pop": 4049, "state": "TX", "_id": "78652"} -{"city": "MANOR", "loc": [-97.532295, 30.338817], "pop": 5359, "state": "TX", "_id": "78653"} -{"city": "CYPRESS MILL", "loc": [-98.306992, 30.57228], "pop": 13422, "state": "TX", "_id": "78654"} -{"city": "MARTINDALE", "loc": [-97.79332, 29.793328], "pop": 2081, "state": "TX", "_id": "78655"} -{"city": "MAXWELL", "loc": [-97.852852, 29.878628], "pop": 3974, "state": "TX", "_id": "78656"} -{"city": "PAIGE", "loc": [-97.119771, 30.185799], "pop": 1294, "state": "TX", "_id": "78659"} -{"city": "PFLUGERVILLE", "loc": [-97.629895, 30.442133], "pop": 12297, "state": "TX", "_id": "78660"} -{"city": "RED ROCK", "loc": [-97.408058, 29.990616], "pop": 1963, "state": "TX", "_id": "78662"} -{"city": "ROUND MOUNTAIN", "loc": [-98.436514, 30.442879], "pop": 169, "state": "TX", "_id": "78663"} -{"city": "ROUND ROCK", "loc": [-97.668028, 30.51452], "pop": 20142, "state": "TX", "_id": "78664"} -{"city": "SANDY", "loc": [-98.509846, 30.339127], "pop": 91, "state": "TX", "_id": "78665"} -{"city": "SAN MARCOS", "loc": [-97.940418, 29.875359], "pop": 39087, "state": "TX", "_id": "78666"} -{"city": "SPICEWOOD", "loc": [-98.05392, 30.389945], "pop": 2235, "state": "TX", "_id": "78669"} -{"city": "ALBERT", "loc": [-98.655468, 30.224921], "pop": 777, "state": "TX", "_id": "78671"} -{"city": "TOW", "loc": [-98.459647, 30.860867], "pop": 874, "state": "TX", "_id": "78672"} -{"city": "WILLOW CITY", "loc": [-98.664589, 30.454889], "pop": 104, "state": "TX", "_id": "78675"} -{"city": "WIMBERLEY", "loc": [-98.11231, 30.026471], "pop": 7004, "state": "TX", "_id": "78676"} -{"city": "WRIGHTSBORO", "loc": [-97.503531, 29.357772], "pop": 26, "state": "TX", "_id": "78677"} -{"city": "ROUND ROCK", "loc": [-97.706171, 30.508431], "pop": 17196, "state": "TX", "_id": "78681"} -{"city": "AUSTIN", "loc": [-97.742559, 30.271289], "pop": 3857, "state": "TX", "_id": "78701"} -{"city": "AUSTIN", "loc": [-97.716589, 30.263817], "pop": 21432, "state": "TX", "_id": "78702"} -{"city": "AUSTIN", "loc": [-97.764809, 30.290671], "pop": 18253, "state": "TX", "_id": "78703"} -{"city": "AUSTIN", "loc": [-97.765788, 30.242831], "pop": 39211, "state": "TX", "_id": "78704"} -{"city": "AUSTIN", "loc": [-97.739627, 30.289619], "pop": 23679, "state": "TX", "_id": "78705"} -{"city": "AUSTIN", "loc": [-97.747187, 30.505972], "pop": 2516, "state": "TX", "_id": "78717"} -{"city": "AUSTIN", "loc": [-97.666701, 30.180243], "pop": 5368, "state": "TX", "_id": "78719"} -{"city": "AUSTIN", "loc": [-97.686798, 30.272144], "pop": 9091, "state": "TX", "_id": "78721"} -{"city": "AUSTIN", "loc": [-97.71495, 30.289305], "pop": 5588, "state": "TX", "_id": "78722"} -{"city": "AUSTIN", "loc": [-97.684941, 30.308515], "pop": 22972, "state": "TX", "_id": "78723"} -{"city": "AUSTIN", "loc": [-97.639587, 30.295982], "pop": 6465, "state": "TX", "_id": "78724"} -{"city": "AUSTIN", "loc": [-97.624301, 30.256186], "pop": 2764, "state": "TX", "_id": "78725"} -{"city": "AUSTIN", "loc": [-97.832649, 30.43], "pop": 871, "state": "TX", "_id": "78726"} -{"city": "AUSTIN", "loc": [-97.719488, 30.425422], "pop": 14276, "state": "TX", "_id": "78727"} -{"city": "AUSTIN", "loc": [-97.681123, 30.441679], "pop": 8051, "state": "TX", "_id": "78728"} -{"city": "AUSTIN", "loc": [-97.768787, 30.45206], "pop": 16611, "state": "TX", "_id": "78729"} -{"city": "AUSTIN", "loc": [-97.824062, 30.360745], "pop": 1021, "state": "TX", "_id": "78730"} -{"city": "AUSTIN", "loc": [-97.760887, 30.347129], "pop": 23276, "state": "TX", "_id": "78731"} -{"city": "AUSTIN", "loc": [-97.900685, 30.375233], "pop": 919, "state": "TX", "_id": "78732"} -{"city": "AUSTIN", "loc": [-97.866633, 30.331355], "pop": 4083, "state": "TX", "_id": "78733"} -{"city": "LAKEWAY", "loc": [-97.957558, 30.377404], "pop": 7495, "state": "TX", "_id": "78734"} -{"city": "AUSTIN", "loc": [-97.841423, 30.248978], "pop": 3463, "state": "TX", "_id": "78735"} -{"city": "AUSTIN", "loc": [-97.915968, 30.244433], "pop": 5812, "state": "TX", "_id": "78736"} -{"city": "AUSTIN", "loc": [-97.942749, 30.210692], "pop": 4135, "state": "TX", "_id": "78737"} -{"city": "AUSTIN", "loc": [-97.982367, 30.333708], "pop": 606, "state": "TX", "_id": "78738"} -{"city": "AUSTIN", "loc": [-97.878433, 30.172026], "pop": 3104, "state": "TX", "_id": "78739"} -{"city": "AUSTIN", "loc": [-97.722317, 30.231513], "pop": 25424, "state": "TX", "_id": "78741"} -{"city": "AUSTIN", "loc": [-97.670349, 30.231296], "pop": 1653, "state": "TX", "_id": "78742"} -{"city": "AUSTIN", "loc": [-97.74723, 30.18764], "pop": 23418, "state": "TX", "_id": "78744"} -{"city": "AUSTIN", "loc": [-97.795599, 30.206298], "pop": 48151, "state": "TX", "_id": "78745"} -{"city": "WEST LAKE HILLS", "loc": [-97.808129, 30.285009], "pop": 18855, "state": "TX", "_id": "78746"} -{"city": "CREEDMOOR", "loc": [-97.762127, 30.130235], "pop": 3070, "state": "TX", "_id": "78747"} -{"city": "AUSTIN", "loc": [-97.822474, 30.174311], "pop": 16288, "state": "TX", "_id": "78748"} -{"city": "AUSTIN", "loc": [-97.850755, 30.216641], "pop": 13674, "state": "TX", "_id": "78749"} -{"city": "AUSTIN", "loc": [-97.796676, 30.422401], "pop": 8867, "state": "TX", "_id": "78750"} -{"city": "AUSTIN", "loc": [-97.724163, 30.309288], "pop": 12715, "state": "TX", "_id": "78751"} -{"city": "AUSTIN", "loc": [-97.700394, 30.331562], "pop": 13311, "state": "TX", "_id": "78752"} -{"city": "AUSTIN", "loc": [-97.682658, 30.36485], "pop": 26823, "state": "TX", "_id": "78753"} -{"city": "AUSTIN", "loc": [-97.667267, 30.342331], "pop": 2328, "state": "TX", "_id": "78754"} -{"city": "AUSTIN", "loc": [-97.739032, 30.322312], "pop": 7525, "state": "TX", "_id": "78756"} -{"city": "AUSTIN", "loc": [-97.731617, 30.343732], "pop": 11723, "state": "TX", "_id": "78757"} -{"city": "AUSTIN", "loc": [-97.707758, 30.376431], "pop": 43246, "state": "TX", "_id": "78758"} -{"city": "AUSTIN", "loc": [-97.752602, 30.403614], "pop": 27479, "state": "TX", "_id": "78759"} -{"city": "UVALDE", "loc": [-99.793074, 29.217238], "pop": 19725, "state": "TX", "_id": "78801"} -{"city": "ASHERTON", "loc": [-99.748611, 28.436421], "pop": 1869, "state": "TX", "_id": "78827"} -{"city": "BARKSDALE", "loc": [-100.070983, 29.708488], "pop": 353, "state": "TX", "_id": "78828"} -{"city": "BATESVILLE", "loc": [-99.611457, 28.928648], "pop": 1512, "state": "TX", "_id": "78829"} -{"city": "BIG WELLS", "loc": [-99.578083, 28.569344], "pop": 882, "state": "TX", "_id": "78830"} -{"city": "BRACKETTVILLE", "loc": [-100.415495, 29.30963], "pop": 3119, "state": "TX", "_id": "78832"} -{"city": "CAMP WOOD", "loc": [-100.00838, 29.679491], "pop": 1017, "state": "TX", "_id": "78833"} -{"city": "CARRIZO SPRINGS", "loc": [-99.863513, 28.52779], "pop": 7682, "state": "TX", "_id": "78834"} -{"city": "COMSTOCK", "loc": [-101.262755, 29.74842], "pop": 497, "state": "TX", "_id": "78837"} -{"city": "CONCAN", "loc": [-99.684244, 29.524057], "pop": 201, "state": "TX", "_id": "78838"} -{"city": "CRYSTAL CITY", "loc": [-99.826412, 28.686953], "pop": 9130, "state": "TX", "_id": "78839"} -{"city": "LAUGHLIN A F B", "loc": [-100.891555, 29.373881], "pop": 38233, "state": "TX", "_id": "78840"} -{"city": "D HANIS", "loc": [-99.283488, 29.3398], "pop": 1337, "state": "TX", "_id": "78850"} -{"city": "DRYDEN", "loc": [-102.33207, 30.165108], "pop": 1410, "state": "TX", "_id": "78851"} -{"city": "EAGLE PASS", "loc": [-100.48176, 28.702786], "pop": 35136, "state": "TX", "_id": "78852"} -{"city": "DUNLAY", "loc": [-99.127637, 29.356065], "pop": 8114, "state": "TX", "_id": "78861"} -{"city": "KNIPPA", "loc": [-99.637171, 29.291119], "pop": 618, "state": "TX", "_id": "78870"} -{"city": "LA PRYOR", "loc": [-99.85074, 28.948848], "pop": 1520, "state": "TX", "_id": "78872"} -{"city": "LEAKEY", "loc": [-99.747913, 29.768951], "pop": 1142, "state": "TX", "_id": "78873"} -{"city": "SPOFFORD", "loc": [-100.574181, 28.942688], "pop": 1242, "state": "TX", "_id": "78877"} -{"city": "RIO FRIO", "loc": [-99.772337, 29.658295], "pop": 253, "state": "TX", "_id": "78879"} -{"city": "ROCKSPRINGS", "loc": [-100.231029, 30.018756], "pop": 1904, "state": "TX", "_id": "78880"} -{"city": "SABINAL", "loc": [-99.478109, 29.326668], "pop": 2203, "state": "TX", "_id": "78881"} -{"city": "TARPLEY", "loc": [-99.246874, 29.645494], "pop": 20, "state": "TX", "_id": "78883"} -{"city": "UTOPIA", "loc": [-99.558454, 29.597197], "pop": 593, "state": "TX", "_id": "78884"} -{"city": "VANDERPOOL", "loc": [-99.555542, 29.741779], "pop": 227, "state": "TX", "_id": "78885"} -{"city": "YANCEY", "loc": [-99.14277, 29.140398], "pop": 368, "state": "TX", "_id": "78886"} -{"city": "BLEIBLERVILLE", "loc": [-96.418719, 29.967572], "pop": 1226, "state": "TX", "_id": "78931"} -{"city": "CARMINE", "loc": [-96.686077, 30.140351], "pop": 356, "state": "TX", "_id": "78932"} -{"city": "CAT SPRING", "loc": [-96.390029, 29.751204], "pop": 658, "state": "TX", "_id": "78933"} -{"city": "COLUMBUS", "loc": [-96.5527, 29.703247], "pop": 5234, "state": "TX", "_id": "78934"} -{"city": "ALLEYTON", "loc": [-96.46312, 29.745286], "pop": 273, "state": "TX", "_id": "78935"} -{"city": "ELLINGER", "loc": [-96.69663, 29.845915], "pop": 374, "state": "TX", "_id": "78938"} -{"city": "FAYETTEVILLE", "loc": [-96.646317, 29.886789], "pop": 2290, "state": "TX", "_id": "78940"} -{"city": "FLATONIA", "loc": [-97.098685, 29.709493], "pop": 2934, "state": "TX", "_id": "78941"} -{"city": "GIDDINGS", "loc": [-96.93322, 30.177725], "pop": 6799, "state": "TX", "_id": "78942"} -{"city": "INDUSTRY", "loc": [-96.518849, 29.971789], "pop": 1258, "state": "TX", "_id": "78944"} -{"city": "LA GRANGE", "loc": [-96.885988, 29.903978], "pop": 10019, "state": "TX", "_id": "78945"} -{"city": "LEDBETTER", "loc": [-96.761276, 30.238307], "pop": 806, "state": "TX", "_id": "78946"} -{"city": "LEXINGTON", "loc": [-97.052387, 30.419144], "pop": 3138, "state": "TX", "_id": "78947"} -{"city": "LINCOLN", "loc": [-96.970159, 30.317693], "pop": 1228, "state": "TX", "_id": "78948"} -{"city": "MULDOON", "loc": [-97.100562, 29.799305], "pop": 8, "state": "TX", "_id": "78949"} -{"city": "NEW ULM", "loc": [-96.537963, 29.814248], "pop": 689, "state": "TX", "_id": "78950"} -{"city": "ROSANKY", "loc": [-97.311875, 29.924122], "pop": 1345, "state": "TX", "_id": "78953"} -{"city": "ROUND TOP", "loc": [-96.734058, 30.041131], "pop": 1127, "state": "TX", "_id": "78954"} -{"city": "SCHULENBURG", "loc": [-96.910564, 29.688247], "pop": 3703, "state": "TX", "_id": "78956"} -{"city": "SMITHVILLE", "loc": [-97.149396, 30.017813], "pop": 5244, "state": "TX", "_id": "78957"} -{"city": "WAELDER", "loc": [-97.295798, 29.6868], "pop": 1105, "state": "TX", "_id": "78959"} -{"city": "WEIMAR", "loc": [-96.755045, 29.678707], "pop": 3854, "state": "TX", "_id": "78962"} -{"city": "WEST POINT", "loc": [-97.036129, 29.95235], "pop": 58, "state": "TX", "_id": "78963"} -{"city": "ADRIAN", "loc": [-102.69699, 35.275807], "pop": 307, "state": "TX", "_id": "79001"} -{"city": "BOOKER", "loc": [-100.523791, 36.442948], "pop": 1454, "state": "TX", "_id": "79005"} -{"city": "PHILLIPS", "loc": [-101.403245, 35.664299], "pop": 17339, "state": "TX", "_id": "79007"} -{"city": "BOVINA", "loc": [-102.806115, 34.481504], "pop": 2805, "state": "TX", "_id": "79009"} -{"city": "BRISCOE", "loc": [-100.167923, 35.58547], "pop": 469, "state": "TX", "_id": "79011"} -{"city": "GLAZIER", "loc": [-100.353875, 35.885906], "pop": 3720, "state": "TX", "_id": "79014"} -{"city": "CANYON", "loc": [-101.924705, 34.977222], "pop": 14389, "state": "TX", "_id": "79015"} -{"city": "CHANNING", "loc": [-102.334269, 35.782572], "pop": 648, "state": "TX", "_id": "79018"} -{"city": "CLAUDE", "loc": [-101.381271, 35.096715], "pop": 1903, "state": "TX", "_id": "79019"} -{"city": "DALHART", "loc": [-102.517658, 36.073183], "pop": 7051, "state": "TX", "_id": "79022"} -{"city": "DIMMITT", "loc": [-102.304634, 34.534089], "pop": 7088, "state": "TX", "_id": "79027"} -{"city": "DUMAS", "loc": [-101.967984, 35.882315], "pop": 15902, "state": "TX", "_id": "79029"} -{"city": "EARTH", "loc": [-102.421284, 34.241521], "pop": 1704, "state": "TX", "_id": "79031"} -{"city": "FOLLETT", "loc": [-100.206776, 36.430923], "pop": 983, "state": "TX", "_id": "79034"} -{"city": "BLACK", "loc": [-102.719139, 34.640298], "pop": 4788, "state": "TX", "_id": "79035"} -{"city": "FRITCH", "loc": [-101.584482, 35.644121], "pop": 5073, "state": "TX", "_id": "79036"} -{"city": "GROOM", "loc": [-101.12846, 35.216761], "pop": 827, "state": "TX", "_id": "79039"} -{"city": "GRUVER", "loc": [-101.408906, 36.286805], "pop": 2053, "state": "TX", "_id": "79040"} -{"city": "HALE CENTER", "loc": [-101.873623, 34.066943], "pop": 3139, "state": "TX", "_id": "79041"} -{"city": "HAPPY", "loc": [-101.825632, 34.721899], "pop": 839, "state": "TX", "_id": "79042"} -{"city": "HART", "loc": [-102.115534, 34.387838], "pop": 1430, "state": "TX", "_id": "79043"} -{"city": "HARTLEY", "loc": [-102.52213, 35.931122], "pop": 724, "state": "TX", "_id": "79044"} -{"city": "HEREFORD", "loc": [-102.40503, 34.837037], "pop": 19090, "state": "TX", "_id": "79045"} -{"city": "HIGGINS", "loc": [-100.095191, 36.136504], "pop": 672, "state": "TX", "_id": "79046"} -{"city": "KRESS", "loc": [-101.73705, 34.373703], "pop": 1643, "state": "TX", "_id": "79052"} -{"city": "LIPSCOMB", "loc": [-100.270164, 36.223031], "pop": 34, "state": "TX", "_id": "79056"} -{"city": "KELLERVILLE", "loc": [-100.611303, 35.234051], "pop": 1151, "state": "TX", "_id": "79057"} -{"city": "MIAMI", "loc": [-100.702715, 35.719301], "pop": 1025, "state": "TX", "_id": "79059"} -{"city": "MOBEETIE", "loc": [-100.424158, 35.529717], "pop": 421, "state": "TX", "_id": "79061"} -{"city": "MORSE", "loc": [-101.47288, 36.059579], "pop": 169, "state": "TX", "_id": "79062"} -{"city": "NAZARETH", "loc": [-102.106914, 34.544356], "pop": 443, "state": "TX", "_id": "79063"} -{"city": "OLTON", "loc": [-102.141415, 34.184418], "pop": 2747, "state": "TX", "_id": "79064"} -{"city": "PAMPA", "loc": [-100.957885, 35.538043], "pop": 22816, "state": "TX", "_id": "79065"} -{"city": "PANHANDLE", "loc": [-101.43037, 35.380841], "pop": 3669, "state": "TX", "_id": "79068"} -{"city": "PERRYTON", "loc": [-100.815558, 36.374825], "pop": 9128, "state": "TX", "_id": "79070"} -{"city": "PLAINVIEW", "loc": [-101.725896, 34.196194], "pop": 27037, "state": "TX", "_id": "79072"} -{"city": "TWITTY", "loc": [-100.262152, 35.220596], "pop": 2927, "state": "TX", "_id": "79079"} -{"city": "SKELLYTOWN", "loc": [-101.172134, 35.568489], "pop": 739, "state": "TX", "_id": "79080"} -{"city": "SPEARMAN", "loc": [-101.195234, 36.192689], "pop": 3626, "state": "TX", "_id": "79081"} -{"city": "SPRINGLAKE", "loc": [-102.308999, 34.23932], "pop": 331, "state": "TX", "_id": "79082"} -{"city": "STINNETT", "loc": [-101.450011, 35.837661], "pop": 3277, "state": "TX", "_id": "79083"} -{"city": "STRATFORD", "loc": [-101.988568, 36.33349], "pop": 2858, "state": "TX", "_id": "79084"} -{"city": "SUMMERFIELD", "loc": [-102.49919, 34.727244], "pop": 109, "state": "TX", "_id": "79085"} -{"city": "SUNRAY", "loc": [-101.812334, 36.009707], "pop": 1963, "state": "TX", "_id": "79086"} -{"city": "TEXLINE", "loc": [-102.984516, 36.374034], "pop": 672, "state": "TX", "_id": "79087"} -{"city": "VIGO PARK", "loc": [-101.762864, 34.540258], "pop": 5651, "state": "TX", "_id": "79088"} -{"city": "VEGA", "loc": [-102.356572, 35.373212], "pop": 1738, "state": "TX", "_id": "79092"} -{"city": "WAYSIDE", "loc": [-101.545434, 34.803353], "pop": 118, "state": "TX", "_id": "79094"} -{"city": "WELLINGTON", "loc": [-100.220721, 34.871726], "pop": 3324, "state": "TX", "_id": "79095"} -{"city": "WHEELER", "loc": [-100.256824, 35.431852], "pop": 2062, "state": "TX", "_id": "79096"} -{"city": "WHITE DEER", "loc": [-101.173993, 35.42781], "pop": 1341, "state": "TX", "_id": "79097"} -{"city": "WILDORADO", "loc": [-102.211754, 35.19156], "pop": 296, "state": "TX", "_id": "79098"} -{"city": "AMARILLO", "loc": [-101.842052, 35.203238], "pop": 2355, "state": "TX", "_id": "79101"} -{"city": "AMARILLO", "loc": [-101.84963, 35.199854], "pop": 9091, "state": "TX", "_id": "79102"} -{"city": "AMARILLO", "loc": [-101.797587, 35.175134], "pop": 5336, "state": "TX", "_id": "79103"} -{"city": "AMARILLO", "loc": [-101.797503, 35.193918], "pop": 12468, "state": "TX", "_id": "79104"} -{"city": "AMARILLO", "loc": [-101.894918, 35.197741], "pop": 29728, "state": "TX", "_id": "79106"} -{"city": "AMARILLO", "loc": [-101.805962, 35.230866], "pop": 29593, "state": "TX", "_id": "79107"} -{"city": "AMARILLO", "loc": [-101.830025, 35.277866], "pop": 8461, "state": "TX", "_id": "79108"} -{"city": "AMARILLO", "loc": [-101.886764, 35.166332], "pop": 41676, "state": "TX", "_id": "79109"} -{"city": "AMARILLO", "loc": [-101.864063, 35.154468], "pop": 17159, "state": "TX", "_id": "79110"} -{"city": "AMARILLO", "loc": [-101.670342, 35.228619], "pop": 2194, "state": "TX", "_id": "79111"} -{"city": "AMARILLO", "loc": [-101.834936, 35.07629], "pop": 6981, "state": "TX", "_id": "79118"} -{"city": "AMARILLO", "loc": [-101.97432, 35.064214], "pop": 968, "state": "TX", "_id": "79119"} -{"city": "AMARILLO", "loc": [-101.926594, 35.169689], "pop": 3454, "state": "TX", "_id": "79121"} -{"city": "AMARILLO", "loc": [-101.942952, 35.270269], "pop": 3694, "state": "TX", "_id": "79124"} -{"city": "KIRKLAND", "loc": [-100.21017, 34.428124], "pop": 5862, "state": "TX", "_id": "79201"} -{"city": "AFTON", "loc": [-100.802131, 33.771837], "pop": 132, "state": "TX", "_id": "79220"} -{"city": "CHILLICOTHE", "loc": [-99.515669, 34.243946], "pop": 1186, "state": "TX", "_id": "79225"} -{"city": "CLARENDON", "loc": [-100.895157, 34.952878], "pop": 2884, "state": "TX", "_id": "79226"} -{"city": "CROWELL", "loc": [-99.698345, 33.991208], "pop": 1794, "state": "TX", "_id": "79227"} -{"city": "DICKENS", "loc": [-100.819695, 33.627996], "pop": 569, "state": "TX", "_id": "79229"} -{"city": "DODSON", "loc": [-100.028593, 34.764354], "pop": 197, "state": "TX", "_id": "79230"} -{"city": "DUMONT", "loc": [-100.319829, 33.656735], "pop": 344, "state": "TX", "_id": "79232"} -{"city": "FLOMOT", "loc": [-101.003824, 34.232076], "pop": 38, "state": "TX", "_id": "79234"} -{"city": "FLOYDADA", "loc": [-101.334564, 33.974296], "pop": 5075, "state": "TX", "_id": "79235"} -{"city": "HEDLEY", "loc": [-100.680632, 34.869787], "pop": 812, "state": "TX", "_id": "79237"} -{"city": "LAKEVIEW", "loc": [-100.725921, 34.672437], "pop": 358, "state": "TX", "_id": "79239"} -{"city": "LOCKNEY", "loc": [-101.425934, 34.145827], "pop": 3422, "state": "TX", "_id": "79241"} -{"city": "MCADOO", "loc": [-100.983293, 33.741265], "pop": 149, "state": "TX", "_id": "79243"} -{"city": "MATADOR", "loc": [-100.836138, 34.052507], "pop": 1173, "state": "TX", "_id": "79244"} -{"city": "MEMPHIS", "loc": [-100.534653, 34.712237], "pop": 2843, "state": "TX", "_id": "79245"} -{"city": "CHALK", "loc": [-100.305917, 34.019154], "pop": 2260, "state": "TX", "_id": "79248"} -{"city": "PETERSBURG", "loc": [-101.604591, 33.876806], "pop": 1608, "state": "TX", "_id": "79250"} -{"city": "QUAIL", "loc": [-100.425243, 34.917969], "pop": 73, "state": "TX", "_id": "79251"} -{"city": "QUANAH", "loc": [-99.749438, 34.29555], "pop": 4097, "state": "TX", "_id": "79252"} -{"city": "QUITAQUE", "loc": [-101.046458, 34.379605], "pop": 674, "state": "TX", "_id": "79255"} -{"city": "ROARING SPRINGS", "loc": [-100.852818, 33.897669], "pop": 321, "state": "TX", "_id": "79256"} -{"city": "SILVERTON", "loc": [-101.316899, 34.464089], "pop": 1297, "state": "TX", "_id": "79257"} -{"city": "TELL", "loc": [-100.396482, 34.378164], "pop": 70, "state": "TX", "_id": "79259"} -{"city": "TRUSCOTT", "loc": [-99.662455, 33.75323], "pop": 54, "state": "TX", "_id": "79260"} -{"city": "TURKEY", "loc": [-100.844875, 34.403644], "pop": 704, "state": "TX", "_id": "79261"} -{"city": "ABERNATHY", "loc": [-101.861111, 33.849961], "pop": 3482, "state": "TX", "_id": "79311"} -{"city": "AMHERST", "loc": [-102.441614, 33.997608], "pop": 1266, "state": "TX", "_id": "79312"} -{"city": "ANTON", "loc": [-102.165165, 33.804278], "pop": 1625, "state": "TX", "_id": "79313"} -{"city": "BROWNFIELD", "loc": [-102.276157, 33.169801], "pop": 11954, "state": "TX", "_id": "79316"} -{"city": "BULA", "loc": [-102.656426, 33.89696], "pop": 113, "state": "TX", "_id": "79320"} -{"city": "CROSBYTON", "loc": [-101.228733, 33.656159], "pop": 2711, "state": "TX", "_id": "79322"} -{"city": "DENVER CITY", "loc": [-102.831251, 32.971114], "pop": 6697, "state": "TX", "_id": "79323"} -{"city": "ENOCHS", "loc": [-102.764107, 33.850225], "pop": 53, "state": "TX", "_id": "79324"} -{"city": "FARWELL", "loc": [-102.990064, 34.386025], "pop": 2270, "state": "TX", "_id": "79325"} -{"city": "FIELDTON", "loc": [-102.269692, 34.091878], "pop": 48, "state": "TX", "_id": "79326"} -{"city": "IDALOU", "loc": [-101.678579, 33.650376], "pop": 4632, "state": "TX", "_id": "79329"} -{"city": "LAMESA", "loc": [-101.956914, 32.736677], "pop": 12989, "state": "TX", "_id": "79331"} -{"city": "LEVELLAND", "loc": [-102.367591, 33.578778], "pop": 20952, "state": "TX", "_id": "79336"} -{"city": "LITTLEFIELD", "loc": [-102.320697, 33.921195], "pop": 7766, "state": "TX", "_id": "79339"} -{"city": "LOOP", "loc": [-102.422084, 32.916169], "pop": 393, "state": "TX", "_id": "79342"} -{"city": "LORENZO", "loc": [-101.527736, 33.666626], "pop": 1753, "state": "TX", "_id": "79343"} -{"city": "MAPLE", "loc": [-102.926588, 33.8622], "pop": 234, "state": "TX", "_id": "79344"} -{"city": "MEADOW", "loc": [-102.249196, 33.332098], "pop": 1234, "state": "TX", "_id": "79345"} -{"city": "MORTON", "loc": [-102.779619, 33.715718], "pop": 3569, "state": "TX", "_id": "79346"} -{"city": "MULESHOE", "loc": [-102.749631, 34.219308], "pop": 6664, "state": "TX", "_id": "79347"} -{"city": "ODONNELL", "loc": [-101.827099, 32.977331], "pop": 1381, "state": "TX", "_id": "79351"} -{"city": "PEP", "loc": [-102.56558, 33.810434], "pop": 66, "state": "TX", "_id": "79353"} -{"city": "PLAINS", "loc": [-102.829367, 33.189354], "pop": 2089, "state": "TX", "_id": "79355"} -{"city": "POST", "loc": [-101.39216, 33.201695], "pop": 5143, "state": "TX", "_id": "79356"} -{"city": "CONE", "loc": [-101.382738, 33.678977], "pop": 2840, "state": "TX", "_id": "79357"} -{"city": "ROPESVILLE", "loc": [-102.15841, 33.457499], "pop": 1556, "state": "TX", "_id": "79358"} -{"city": "SEAGRAVES", "loc": [-102.578075, 32.93172], "pop": 3307, "state": "TX", "_id": "79359"} -{"city": "SEMINOLE", "loc": [-102.682761, 32.721045], "pop": 10423, "state": "TX", "_id": "79360"} -{"city": "SHALLOWATER", "loc": [-101.983626, 33.691859], "pop": 4342, "state": "TX", "_id": "79363"} -{"city": "RANSOM CANYON", "loc": [-101.65192, 33.436795], "pop": 7118, "state": "TX", "_id": "79364"} -{"city": "RANSOM CANYON", "loc": [-101.690805, 33.533019], "pop": 1248, "state": "TX", "_id": "79366"} -{"city": "SPUR", "loc": [-100.857116, 33.479016], "pop": 1718, "state": "TX", "_id": "79370"} -{"city": "SUDAN", "loc": [-102.525525, 34.069661], "pop": 1210, "state": "TX", "_id": "79371"} -{"city": "TAHOKA", "loc": [-101.821976, 33.198803], "pop": 4309, "state": "TX", "_id": "79373"} -{"city": "TOKIO", "loc": [-102.576753, 33.18259], "pop": 30, "state": "TX", "_id": "79376"} -{"city": "WELCH", "loc": [-102.106792, 32.893827], "pop": 575, "state": "TX", "_id": "79377"} -{"city": "WHITEFACE", "loc": [-102.638941, 33.586575], "pop": 808, "state": "TX", "_id": "79379"} -{"city": "WILSON", "loc": [-101.712231, 33.329862], "pop": 1068, "state": "TX", "_id": "79381"} -{"city": "WOLFFORTH", "loc": [-102.026101, 33.530381], "pop": 3101, "state": "TX", "_id": "79382"} -{"city": "LUBBOCK", "loc": [-101.860634, 33.586527], "pop": 10240, "state": "TX", "_id": "79401"} -{"city": "LUBBOCK", "loc": [-101.80982, 33.619573], "pop": 17237, "state": "TX", "_id": "79403"} -{"city": "LUBBOCK", "loc": [-101.833263, 33.525979], "pop": 12158, "state": "TX", "_id": "79404"} -{"city": "LUBBOCK", "loc": [-101.850655, 33.570972], "pop": 2903, "state": "TX", "_id": "79405"} -{"city": "LUBBOCK", "loc": [-101.877828, 33.581934], "pop": 5582, "state": "TX", "_id": "79406"} -{"city": "LUBBOCK", "loc": [-101.942333, 33.568369], "pop": 12783, "state": "TX", "_id": "79407"} -{"city": "LUBBOCK", "loc": [-101.890377, 33.56931], "pop": 9955, "state": "TX", "_id": "79410"} -{"city": "LUBBOCK", "loc": [-101.862593, 33.570393], "pop": 4715, "state": "TX", "_id": "79411"} -{"city": "LUBBOCK", "loc": [-101.857737, 33.546313], "pop": 14245, "state": "TX", "_id": "79412"} -{"city": "LUBBOCK", "loc": [-101.887142, 33.546597], "pop": 20238, "state": "TX", "_id": "79413"} -{"city": "LUBBOCK", "loc": [-101.918666, 33.549728], "pop": 16893, "state": "TX", "_id": "79414"} -{"city": "LUBBOCK", "loc": [-101.876015, 33.602117], "pop": 13384, "state": "TX", "_id": "79415"} -{"city": "LUBBOCK", "loc": [-101.936705, 33.592397], "pop": 20774, "state": "TX", "_id": "79416"} -{"city": "LUBBOCK", "loc": [-101.87946, 33.514604], "pop": 17308, "state": "TX", "_id": "79423"} -{"city": "LUBBOCK", "loc": [-101.93439, 33.515866], "pop": 22873, "state": "TX", "_id": "79424"} -{"city": "REESE AIR FORCE", "loc": [-102.028792, 33.594344], "pop": 312, "state": "TX", "_id": "79489"} -{"city": "ANSON", "loc": [-99.895301, 32.748894], "pop": 3724, "state": "TX", "_id": "79501"} -{"city": "ASPERMONT", "loc": [-100.234372, 33.130059], "pop": 1424, "state": "TX", "_id": "79502"} -{"city": "AVOCA", "loc": [-99.69642, 32.883225], "pop": 248, "state": "TX", "_id": "79503"} -{"city": "BAIRD", "loc": [-99.37766, 32.391557], "pop": 2267, "state": "TX", "_id": "79504"} -{"city": "BLACKWELL", "loc": [-100.311078, 32.082044], "pop": 697, "state": "TX", "_id": "79506"} -{"city": "CLYDE", "loc": [-99.518445, 32.380289], "pop": 7664, "state": "TX", "_id": "79510"} -{"city": "COAHOMA", "loc": [-101.319681, 32.294215], "pop": 2365, "state": "TX", "_id": "79511"} -{"city": "COLORADO CITY", "loc": [-100.860948, 32.398736], "pop": 5874, "state": "TX", "_id": "79512"} -{"city": "FLUVANNA", "loc": [-101.041966, 32.781022], "pop": 707, "state": "TX", "_id": "79517"} -{"city": "GIRARD", "loc": [-100.693688, 33.363202], "pop": 132, "state": "TX", "_id": "79518"} -{"city": "GOLDSBORO", "loc": [-99.677457, 32.048213], "pop": 27, "state": "TX", "_id": "79519"} -{"city": "HAMLIN", "loc": [-100.128014, 32.879893], "pop": 3271, "state": "TX", "_id": "79520"} -{"city": "HASKELL", "loc": [-99.730923, 33.157993], "pop": 3917, "state": "TX", "_id": "79521"} -{"city": "HAWLEY", "loc": [-99.795994, 32.594965], "pop": 4405, "state": "TX", "_id": "79525"} -{"city": "HERMLEIGH", "loc": [-100.754714, 32.629441], "pop": 942, "state": "TX", "_id": "79526"} -{"city": "IRA", "loc": [-101.025419, 32.621274], "pop": 949, "state": "TX", "_id": "79527"} -{"city": "JAYTON", "loc": [-100.582482, 33.251774], "pop": 708, "state": "TX", "_id": "79528"} -{"city": "KNOX CITY", "loc": [-99.8137, 33.418294], "pop": 1751, "state": "TX", "_id": "79529"} -{"city": "LAWN", "loc": [-99.735081, 32.136012], "pop": 619, "state": "TX", "_id": "79530"} -{"city": "LORAINE", "loc": [-100.72851, 32.385047], "pop": 1300, "state": "TX", "_id": "79532"} -{"city": "LUEDERS", "loc": [-99.672792, 32.762339], "pop": 839, "state": "TX", "_id": "79533"} -{"city": "MC CAULLEY", "loc": [-100.216784, 32.778654], "pop": 272, "state": "TX", "_id": "79534"} -{"city": "MARYNEAL", "loc": [-100.497178, 32.202639], "pop": 123, "state": "TX", "_id": "79535"} -{"city": "MERKEL", "loc": [-99.992443, 32.444371], "pop": 4906, "state": "TX", "_id": "79536"} -{"city": "NOLAN", "loc": [-100.267053, 32.279333], "pop": 254, "state": "TX", "_id": "79537"} -{"city": "NOVICE", "loc": [-99.59353, 32.000927], "pop": 421, "state": "TX", "_id": "79538"} -{"city": "O BRIEN", "loc": [-99.847303, 33.374869], "pop": 314, "state": "TX", "_id": "79539"} -{"city": "OLD GLORY", "loc": [-100.153155, 33.184162], "pop": 431, "state": "TX", "_id": "79540"} -{"city": "OVALO", "loc": [-99.822873, 32.155257], "pop": 326, "state": "TX", "_id": "79541"} -{"city": "79542", "loc": [-100.394624, 33.204065], "pop": 158, "state": "TX", "_id": "79542"} -{"city": "ROBY", "loc": [-100.400766, 32.722016], "pop": 1333, "state": "TX", "_id": "79543"} -{"city": "ROCHESTER", "loc": [-99.859366, 33.310457], "pop": 707, "state": "TX", "_id": "79544"} -{"city": "ROSCOE", "loc": [-100.539088, 32.427558], "pop": 2168, "state": "TX", "_id": "79545"} -{"city": "ROTAN", "loc": [-100.470489, 32.855521], "pop": 2449, "state": "TX", "_id": "79546"} -{"city": "RULE", "loc": [-99.889354, 33.184163], "pop": 993, "state": "TX", "_id": "79547"} -{"city": "SAGERTON", "loc": [-99.892329, 33.051321], "pop": 351, "state": "TX", "_id": "79548"} -{"city": "DERMOTT", "loc": [-100.907485, 32.715105], "pop": 16206, "state": "TX", "_id": "79549"} -{"city": "STAMFORD", "loc": [-99.78593, 32.945323], "pop": 4338, "state": "TX", "_id": "79553"} -{"city": "SWEETWATER", "loc": [-100.397908, 32.472589], "pop": 14064, "state": "TX", "_id": "79556"} -{"city": "SYLVESTER", "loc": [-100.242497, 32.717909], "pop": 240, "state": "TX", "_id": "79560"} -{"city": "TRENT", "loc": [-100.119488, 32.489752], "pop": 494, "state": "TX", "_id": "79561"} -{"city": "TUSCOLA", "loc": [-99.82439, 32.234955], "pop": 2980, "state": "TX", "_id": "79562"} -{"city": "TYE", "loc": [-99.868434, 32.447728], "pop": 903, "state": "TX", "_id": "79563"} -{"city": "WESTBROOK", "loc": [-100.990719, 32.353195], "pop": 842, "state": "TX", "_id": "79565"} -{"city": "WINGATE", "loc": [-100.118311, 32.031777], "pop": 313, "state": "TX", "_id": "79566"} -{"city": "WINTERS", "loc": [-99.955114, 31.962028], "pop": 3876, "state": "TX", "_id": "79567"} -{"city": "ABILENE", "loc": [-99.718208, 32.468155], "pop": 16713, "state": "TX", "_id": "79601"} -{"city": "ABILENE", "loc": [-99.721448, 32.41783], "pop": 16432, "state": "TX", "_id": "79602"} -{"city": "ABILENE", "loc": [-99.761916, 32.467852], "pop": 24123, "state": "TX", "_id": "79603"} -{"city": "ABILENE", "loc": [-99.772374, 32.431987], "pop": 29862, "state": "TX", "_id": "79605"} -{"city": "ABILENE", "loc": [-99.774578, 32.392038], "pop": 17332, "state": "TX", "_id": "79606"} -{"city": "DYESS AFB", "loc": [-99.82214, 32.418956], "pop": 4965, "state": "TX", "_id": "79607"} -{"city": "MIDLAND", "loc": [-102.06261, 31.989636], "pop": 32926, "state": "TX", "_id": "79701"} -{"city": "MIDLAND", "loc": [-102.136854, 31.972106], "pop": 23167, "state": "TX", "_id": "79703"} -{"city": "MIDLAND", "loc": [-102.091483, 32.029473], "pop": 27708, "state": "TX", "_id": "79705"} -{"city": "MIDLAND", "loc": [-102.147599, 32.019911], "pop": 22810, "state": "TX", "_id": "79707"} -{"city": "ACKERLY", "loc": [-101.795132, 32.609085], "pop": 793, "state": "TX", "_id": "79713"} -{"city": "ANDREWS", "loc": [-102.540926, 32.320125], "pop": 14338, "state": "TX", "_id": "79714"} -{"city": "BALMORHEA", "loc": [-103.693316, 31.012361], "pop": 1655, "state": "TX", "_id": "79718"} -{"city": "BARSTOW", "loc": [-103.397086, 31.463908], "pop": 701, "state": "TX", "_id": "79719"} -{"city": "VEALMOOR", "loc": [-101.467517, 32.241767], "pop": 29733, "state": "TX", "_id": "79720"} -{"city": "COYANOSA", "loc": [-103.053815, 31.229547], "pop": 341, "state": "TX", "_id": "79730"} -{"city": "CRANE", "loc": [-102.354382, 31.396949], "pop": 4652, "state": "TX", "_id": "79731"} -{"city": "FORT DAVIS", "loc": [-103.936434, 30.613144], "pop": 1607, "state": "TX", "_id": "79734"} -{"city": "FORT STOCKTON", "loc": [-102.879942, 30.890785], "pop": 11755, "state": "TX", "_id": "79735"} -{"city": "GAIL", "loc": [-101.450032, 32.752334], "pop": 799, "state": "TX", "_id": "79738"} -{"city": "GARDEN CITY", "loc": [-101.526912, 31.849078], "pop": 1447, "state": "TX", "_id": "79739"} -{"city": "GOLDSMITH", "loc": [-102.625047, 31.95412], "pop": 442, "state": "TX", "_id": "79741"} -{"city": "GRANDFALLS", "loc": [-102.856778, 31.345856], "pop": 748, "state": "TX", "_id": "79742"} -{"city": "IMPERIAL", "loc": [-102.710918, 31.237264], "pop": 557, "state": "TX", "_id": "79743"} -{"city": "IRAAN", "loc": [-101.915175, 30.915564], "pop": 1596, "state": "TX", "_id": "79744"} -{"city": "KERMIT", "loc": [-103.091269, 31.85496], "pop": 7370, "state": "TX", "_id": "79745"} -{"city": "KNOTT", "loc": [-101.651731, 32.412489], "pop": 245, "state": "TX", "_id": "79748"} -{"city": "LENORAH", "loc": [-101.761137, 32.439157], "pop": 529, "state": "TX", "_id": "79749"} -{"city": "MC CAMEY", "loc": [-102.215325, 31.131894], "pop": 2950, "state": "TX", "_id": "79752"} -{"city": "MENTONE", "loc": [-103.550316, 31.738374], "pop": 107, "state": "TX", "_id": "79754"} -{"city": "MIDKIFF", "loc": [-101.926913, 31.306761], "pop": 1497, "state": "TX", "_id": "79755"} -{"city": "MONAHANS", "loc": [-102.900273, 31.581294], "pop": 11296, "state": "TX", "_id": "79756"} -{"city": "GARDENDALE", "loc": [-102.357237, 32.024476], "pop": 1536, "state": "TX", "_id": "79758"} -{"city": "ODESSA", "loc": [-102.352252, 31.857945], "pop": 30126, "state": "TX", "_id": "79761"} -{"city": "ODESSA", "loc": [-102.354806, 31.889029], "pop": 34327, "state": "TX", "_id": "79762"} -{"city": "ODESSA", "loc": [-102.416179, 31.834085], "pop": 30550, "state": "TX", "_id": "79763"} -{"city": "ODESSA", "loc": [-102.437465, 31.876683], "pop": 17919, "state": "TX", "_id": "79764"} -{"city": "ODESSA", "loc": [-102.394403, 31.937548], "pop": 2240, "state": "TX", "_id": "79765"} -{"city": "ODESSA", "loc": [-102.344863, 31.782683], "pop": 1794, "state": "TX", "_id": "79766"} -{"city": "VERHALEN", "loc": [-103.508129, 31.414384], "pop": 14197, "state": "TX", "_id": "79772"} -{"city": "PYOTE", "loc": [-103.139676, 31.539491], "pop": 370, "state": "TX", "_id": "79777"} -{"city": "SHEFFIELD", "loc": [-101.859963, 30.693549], "pop": 428, "state": "TX", "_id": "79781"} -{"city": "STANTON", "loc": [-101.809887, 32.139992], "pop": 3776, "state": "TX", "_id": "79782"} -{"city": "TARZAN", "loc": [-101.960723, 32.357523], "pop": 643, "state": "TX", "_id": "79783"} -{"city": "WINK", "loc": [-103.156084, 31.752673], "pop": 1256, "state": "TX", "_id": "79789"} -{"city": "ANTHONY", "loc": [-106.597625, 31.990718], "pop": 3341, "state": "TX", "_id": "79821"} -{"city": "ALPINE", "loc": [-103.654089, 30.263111], "pop": 7648, "state": "TX", "_id": "79830"} -{"city": "BIG BEND NATIONA", "loc": [-103.330626, 29.147657], "pop": 249, "state": "TX", "_id": "79834"} -{"city": "CANUTILLO", "loc": [-106.592888, 31.934379], "pop": 8585, "state": "TX", "_id": "79835"} -{"city": "CLINT", "loc": [-106.20383, 31.549418], "pop": 17337, "state": "TX", "_id": "79836"} -{"city": "DELL CITY", "loc": [-105.209902, 31.923975], "pop": 915, "state": "TX", "_id": "79837"} -{"city": "FORT HANCOCK", "loc": [-105.823448, 31.296887], "pop": 1108, "state": "TX", "_id": "79839"} -{"city": "MARATHON", "loc": [-103.221397, 30.18858], "pop": 725, "state": "TX", "_id": "79842"} -{"city": "MARFA", "loc": [-104.084835, 30.292982], "pop": 3155, "state": "TX", "_id": "79843"} -{"city": "PRESIDIO", "loc": [-104.35511, 29.557302], "pop": 3482, "state": "TX", "_id": "79845"} -{"city": "SALT FLAT", "loc": [-104.611467, 31.359027], "pop": 317, "state": "TX", "_id": "79847"} -{"city": "SIERRA BLANCA", "loc": [-105.321874, 31.193821], "pop": 892, "state": "TX", "_id": "79851"} -{"city": "TERLINGUA", "loc": [-103.559671, 29.31648], "pop": 59, "state": "TX", "_id": "79852"} -{"city": "VALENTINE", "loc": [-104.481096, 30.620043], "pop": 339, "state": "TX", "_id": "79854"} -{"city": "KENT", "loc": [-104.832249, 31.042879], "pop": 3090, "state": "TX", "_id": "79855"} -{"city": "EL PASO", "loc": [-106.478311, 31.758411], "pop": 17467, "state": "TX", "_id": "79901"} -{"city": "EL PASO", "loc": [-106.493165, 31.776317], "pop": 26404, "state": "TX", "_id": "79902"} -{"city": "EL PASO", "loc": [-106.440569, 31.786213], "pop": 20768, "state": "TX", "_id": "79903"} -{"city": "EL PASO", "loc": [-106.438135, 31.853334], "pop": 35732, "state": "TX", "_id": "79904"} -{"city": "EL PASO", "loc": [-106.430445, 31.767447], "pop": 32865, "state": "TX", "_id": "79905"} -{"city": "FORT BLISS", "loc": [-106.421611, 31.807631], "pop": 11364, "state": "TX", "_id": "79906"} -{"city": "EL PASO", "loc": [-106.329281, 31.708908], "pop": 58052, "state": "TX", "_id": "79907"} -{"city": "FORT BLISS", "loc": [-106.386711, 31.82753], "pop": 2918, "state": "TX", "_id": "79908"} -{"city": "EL PASO", "loc": [-106.536433, 31.838309], "pop": 46537, "state": "TX", "_id": "79912"} -{"city": "EL PASO", "loc": [-106.368605, 31.743234], "pop": 46356, "state": "TX", "_id": "79915"} -{"city": "FORT BLISS", "loc": [-106.159157, 31.794873], "pop": 6703, "state": "TX", "_id": "79916"} -{"city": "EL PASO", "loc": [-106.573176, 31.821767], "pop": 8540, "state": "TX", "_id": "79922"} -{"city": "EL PASO", "loc": [-106.414857, 31.902098], "pop": 57215, "state": "TX", "_id": "79924"} -{"city": "EL PASO", "loc": [-106.361317, 31.781402], "pop": 41235, "state": "TX", "_id": "79925"} -{"city": "HORIZON CITY", "loc": [-106.273064, 31.653014], "pop": 30011, "state": "TX", "_id": "79927"} -{"city": "EL PASO", "loc": [-106.456754, 31.804795], "pop": 29792, "state": "TX", "_id": "79930"} -{"city": "EL PASO", "loc": [-106.593186, 31.862334], "pop": 14909, "state": "TX", "_id": "79932"} -{"city": "EL PASO", "loc": [-106.407328, 31.938585], "pop": 2983, "state": "TX", "_id": "79934"} -{"city": "EL PASO", "loc": [-106.330258, 31.771847], "pop": 20465, "state": "TX", "_id": "79935"} -{"city": "EL PASO", "loc": [-106.30159, 31.767655], "pop": 52031, "state": "TX", "_id": "79936"} -{"city": "ALTAMONT", "loc": [-110.446356, 40.370225], "pop": 146, "state": "UT", "_id": "84001"} -{"city": "ALTONAH", "loc": [-110.438499, 40.441894], "pop": 10, "state": "UT", "_id": "84002"} -{"city": "AMERICAN FORK", "loc": [-111.794107, 40.392784], "pop": 21864, "state": "UT", "_id": "84003"} -{"city": "ALPINE", "loc": [-111.768861, 40.461591], "pop": 3665, "state": "UT", "_id": "84004"} -{"city": "BINGHAM CANYON", "loc": [-112.097718, 40.564614], "pop": 631, "state": "UT", "_id": "84006"} -{"city": "BLUEBELL", "loc": [-110.294122, 40.351728], "pop": 1443, "state": "UT", "_id": "84007"} -{"city": "BOUNTIFUL", "loc": [-111.872658, 40.877513], "pop": 41077, "state": "UT", "_id": "84010"} -{"city": "BRIDGELAND", "loc": [-110.160264, 40.230411], "pop": 849, "state": "UT", "_id": "84012"} -{"city": "CEDAR VALLEY", "loc": [-111.968985, 40.142516], "pop": 1836, "state": "UT", "_id": "84013"} -{"city": "CENTERVILLE", "loc": [-111.87701, 40.926772], "pop": 11989, "state": "UT", "_id": "84014"} -{"city": "CLEARFIELD", "loc": [-112.048224, 41.129388], "pop": 25972, "state": "UT", "_id": "84015"} -{"city": "COALVILLE", "loc": [-111.407108, 40.924385], "pop": 3217, "state": "UT", "_id": "84017"} -{"city": "CROYDON", "loc": [-111.523092, 41.068915], "pop": 117, "state": "UT", "_id": "84018"} -{"city": "DRAPER", "loc": [-111.88096, 40.504599], "pop": 5602, "state": "UT", "_id": "84020"} -{"city": "DUCHESNE", "loc": [-110.618094, 39.95398], "pop": 38, "state": "UT", "_id": "84021"} -{"city": "DUGWAY", "loc": [-112.872905, 40.526892], "pop": 79, "state": "UT", "_id": "84022"} -{"city": "DUTCH JOHN", "loc": [-109.354255, 40.932244], "pop": 174, "state": "UT", "_id": "84023"} -{"city": "FARMINGTON", "loc": [-111.893785, 40.988913], "pop": 10307, "state": "UT", "_id": "84025"} -{"city": "FORT DUCHESNE", "loc": [-109.863726, 40.301411], "pop": 1649, "state": "UT", "_id": "84026"} -{"city": "GARDEN CITY", "loc": [-111.407033, 41.93764], "pop": 254, "state": "UT", "_id": "84028"} -{"city": "GRANTSVILLE", "loc": [-112.461766, 40.60054], "pop": 4741, "state": "UT", "_id": "84029"} -{"city": "HANNA", "loc": [-110.809748, 40.450135], "pop": 54, "state": "UT", "_id": "84031"} -{"city": "HEBER CITY", "loc": [-111.405088, 40.494703], "pop": 7913, "state": "UT", "_id": "84032"} -{"city": "JENSEN", "loc": [-109.350982, 40.378715], "pop": 471, "state": "UT", "_id": "84035"} -{"city": "KAMAS", "loc": [-111.261877, 40.641432], "pop": 2433, "state": "UT", "_id": "84036"} -{"city": "KAYSVILLE", "loc": [-111.932607, 41.037527], "pop": 21132, "state": "UT", "_id": "84037"} -{"city": "LAKETOWN", "loc": [-111.268853, 41.81068], "pop": 430, "state": "UT", "_id": "84038"} -{"city": "LAPOINT", "loc": [-109.804102, 40.400285], "pop": 362, "state": "UT", "_id": "84039"} -{"city": "LAYTON", "loc": [-111.927365, 41.084576], "pop": 13289, "state": "UT", "_id": "84040"} -{"city": "LAYTON", "loc": [-111.970354, 41.087905], "pop": 33600, "state": "UT", "_id": "84041"} -{"city": "LINDON", "loc": [-111.714358, 40.34119], "pop": 3819, "state": "UT", "_id": "84042"} -{"city": "LEHI", "loc": [-111.850606, 40.395845], "pop": 9188, "state": "UT", "_id": "84043"} -{"city": "MAGNA", "loc": [-112.080867, 40.700879], "pop": 17841, "state": "UT", "_id": "84044"} -{"city": "MANILA", "loc": [-109.723503, 40.968494], "pop": 516, "state": "UT", "_id": "84046"} -{"city": "MIDVALE", "loc": [-111.885066, 40.615178], "pop": 25001, "state": "UT", "_id": "84047"} -{"city": "MIDWAY", "loc": [-111.477575, 40.50934], "pop": 1837, "state": "UT", "_id": "84049"} -{"city": "MORGAN", "loc": [-111.716339, 41.067832], "pop": 5411, "state": "UT", "_id": "84050"} -{"city": "MOUNTAIN HOME", "loc": [-110.767018, 40.191999], "pop": 236, "state": "UT", "_id": "84051"} -{"city": "MYTON", "loc": [-110.048056, 40.194049], "pop": 940, "state": "UT", "_id": "84052"} -{"city": "NEOLA", "loc": [-110.037221, 40.449149], "pop": 810, "state": "UT", "_id": "84053"} -{"city": "NORTH SALT LAKE", "loc": [-111.904116, 40.844064], "pop": 7216, "state": "UT", "_id": "84054"} -{"city": "HILL AIR FORCE B", "loc": [-111.995565, 41.116962], "pop": 5432, "state": "UT", "_id": "84056"} -{"city": "OREM", "loc": [-111.695293, 40.313407], "pop": 38292, "state": "UT", "_id": "84057"} -{"city": "VINEYARD", "loc": [-111.694301, 40.280761], "pop": 29323, "state": "UT", "_id": "84058"} -{"city": "PARK CITY", "loc": [-111.528021, 40.695724], "pop": 8976, "state": "UT", "_id": "84060"} -{"city": "PEOA", "loc": [-111.302467, 40.720937], "pop": 892, "state": "UT", "_id": "84061"} -{"city": "PLEASANT GROVE", "loc": [-111.733284, 40.371986], "pop": 15703, "state": "UT", "_id": "84062"} -{"city": "RANDLETT", "loc": [-109.730102, 40.21865], "pop": 78, "state": "UT", "_id": "84063"} -{"city": "RANDOLPH", "loc": [-111.185578, 41.656328], "pop": 682, "state": "UT", "_id": "84064"} -{"city": "LARK", "loc": [-111.954661, 40.53789], "pop": 28444, "state": "UT", "_id": "84065"} -{"city": "ROOSEVELT", "loc": [-110.010782, 40.310229], "pop": 6725, "state": "UT", "_id": "84066"} -{"city": "ROY", "loc": [-112.038177, 41.172365], "pop": 22166, "state": "UT", "_id": "84067"} -{"city": "RUSH VALLEY", "loc": [-112.744033, 40.233625], "pop": 1893, "state": "UT", "_id": "84069"} -{"city": "SANDY", "loc": [-111.881625, 40.579379], "pop": 19422, "state": "UT", "_id": "84070"} -{"city": "STOCKTON", "loc": [-112.425214, 40.350451], "pop": 490, "state": "UT", "_id": "84071"} -{"city": "TABIONA", "loc": [-110.702108, 40.382691], "pop": 440, "state": "UT", "_id": "84072"} -{"city": "TALMAGE", "loc": [-110.396529, 40.174569], "pop": 1743, "state": "UT", "_id": "84073"} -{"city": "TOOELE", "loc": [-112.300214, 40.545445], "pop": 17588, "state": "UT", "_id": "84074"} -{"city": "SYRACUSE", "loc": [-112.0451, 41.086423], "pop": 9737, "state": "UT", "_id": "84075"} -{"city": "TRIDELL", "loc": [-109.835906, 40.443593], "pop": 363, "state": "UT", "_id": "84076"} -{"city": "VERNAL", "loc": [-109.546883, 40.440613], "pop": 17641, "state": "UT", "_id": "84078"} -{"city": "VERNON", "loc": [-112.425961, 40.082609], "pop": 200, "state": "UT", "_id": "84080"} -{"city": "WALLSBURG", "loc": [-111.464934, 40.365697], "pop": 649, "state": "UT", "_id": "84082"} -{"city": "TROUT CREEK", "loc": [-113.993573, 40.597396], "pop": 1801, "state": "UT", "_id": "84083"} -{"city": "WEST JORDAN", "loc": [-111.967662, 40.625429], "pop": 36145, "state": "UT", "_id": "84084"} -{"city": "WHITEROCKS", "loc": [-109.917244, 40.452771], "pop": 858, "state": "UT", "_id": "84085"} -{"city": "WOODRUFF", "loc": [-111.186848, 41.488783], "pop": 359, "state": "UT", "_id": "84086"} -{"city": "WOODS CROSS", "loc": [-111.902712, 40.887447], "pop": 8170, "state": "UT", "_id": "84087"} -{"city": "WEST JORDAN", "loc": [-111.964385, 40.595913], "pop": 18592, "state": "UT", "_id": "84088"} -{"city": "ALTA", "loc": [-111.82736, 40.560245], "pop": 25465, "state": "UT", "_id": "84092"} -{"city": "SANDY", "loc": [-111.830989, 40.592651], "pop": 26702, "state": "UT", "_id": "84093"} -{"city": "SANDY", "loc": [-111.861716, 40.568757], "pop": 26375, "state": "UT", "_id": "84094"} -{"city": "SALT LAKE CITY", "loc": [-111.896657, 40.755851], "pop": 2449, "state": "UT", "_id": "84101"} -{"city": "SALT LAKE CITY", "loc": [-111.862721, 40.760034], "pop": 15367, "state": "UT", "_id": "84102"} -{"city": "SALT LAKE CITY", "loc": [-111.874891, 40.777584], "pop": 21427, "state": "UT", "_id": "84103"} -{"city": "SALT LAKE CITY", "loc": [-111.925979, 40.74985], "pop": 16951, "state": "UT", "_id": "84104"} -{"city": "SALT LAKE CITY", "loc": [-111.858087, 40.737236], "pop": 22228, "state": "UT", "_id": "84105"} -{"city": "SALT LAKE CITY", "loc": [-111.854841, 40.705597], "pop": 30496, "state": "UT", "_id": "84106"} -{"city": "MURRAY", "loc": [-111.878383, 40.659014], "pop": 28403, "state": "UT", "_id": "84107"} -{"city": "SALT LAKE CITY", "loc": [-111.825822, 40.737136], "pop": 16873, "state": "UT", "_id": "84108"} -{"city": "SALT LAKE CITY", "loc": [-111.814218, 40.704251], "pop": 24049, "state": "UT", "_id": "84109"} -{"city": "SALT LAKE CITY", "loc": [-111.881, 40.754834], "pop": 8978, "state": "UT", "_id": "84111"} -{"city": "SALT LAKE CITY", "loc": [-111.827827, 40.752372], "pop": 2555, "state": "UT", "_id": "84112"} -{"city": "SALT LAKE CITY", "loc": [-111.841825, 40.763057], "pop": 1093, "state": "UT", "_id": "84113"} -{"city": "SOUTH SALT LAKE", "loc": [-111.883828, 40.715797], "pop": 21776, "state": "UT", "_id": "84115"} -{"city": "SALT LAKE CITY", "loc": [-111.929054, 40.785697], "pop": 23880, "state": "UT", "_id": "84116"} -{"city": "HOLLADAY", "loc": [-111.832943, 40.666302], "pop": 23063, "state": "UT", "_id": "84117"} -{"city": "KEARNS", "loc": [-111.98521, 40.652759], "pop": 55999, "state": "UT", "_id": "84118"} -{"city": "WEST VALLEY CITY", "loc": [-111.952964, 40.690977], "pop": 38892, "state": "UT", "_id": "84119"} -{"city": "WEST VALLEY CITY", "loc": [-112.009783, 40.68708], "pop": 52854, "state": "UT", "_id": "84120"} -{"city": "COTTONWOOD", "loc": [-111.82468, 40.623247], "pop": 40235, "state": "UT", "_id": "84121"} -{"city": "MURRAY", "loc": [-111.919483, 40.660479], "pop": 27766, "state": "UT", "_id": "84123"} -{"city": "HOLLADAY", "loc": [-111.820833, 40.67966], "pop": 20402, "state": "UT", "_id": "84124"} -{"city": "BRIGHAM CITY", "loc": [-112.015177, 41.507921], "pop": 17119, "state": "UT", "_id": "84302"} -{"city": "CLARKSTON", "loc": [-112.04859, 41.91877], "pop": 665, "state": "UT", "_id": "84305"} -{"city": "COLLINSTON", "loc": [-112.124219, 41.78777], "pop": 952, "state": "UT", "_id": "84306"} -{"city": "CORINNE", "loc": [-112.151388, 41.544986], "pop": 1201, "state": "UT", "_id": "84307"} -{"city": "CORNISH", "loc": [-111.954241, 41.970178], "pop": 250, "state": "UT", "_id": "84308"} -{"city": "DEWEYVILLE", "loc": [-112.094717, 41.697226], "pop": 491, "state": "UT", "_id": "84309"} -{"city": "EDEN", "loc": [-111.855765, 41.330279], "pop": 1707, "state": "UT", "_id": "84310"} -{"city": "FIELDING", "loc": [-112.118978, 41.811817], "pop": 703, "state": "UT", "_id": "84311"} -{"city": "GARLAND", "loc": [-112.151635, 41.741319], "pop": 2208, "state": "UT", "_id": "84312"} -{"city": "GROUSE CREEK", "loc": [-113.854043, 41.629727], "pop": 124, "state": "UT", "_id": "84313"} -{"city": "HONEYVILLE", "loc": [-112.097425, 41.623754], "pop": 2236, "state": "UT", "_id": "84314"} -{"city": "HOOPER", "loc": [-112.090371, 41.18267], "pop": 10540, "state": "UT", "_id": "84315"} -{"city": "HUNTSVILLE", "loc": [-111.761821, 41.272139], "pop": 2247, "state": "UT", "_id": "84317"} -{"city": "HYRUM", "loc": [-111.849, 41.631096], "pop": 5352, "state": "UT", "_id": "84319"} -{"city": "LEWISTON", "loc": [-111.876814, 41.970087], "pop": 1418, "state": "UT", "_id": "84320"} -{"city": "LOGAN", "loc": [-111.822613, 41.747025], "pop": 40074, "state": "UT", "_id": "84321"} -{"city": "MANTUA", "loc": [-111.941646, 41.497496], "pop": 715, "state": "UT", "_id": "84324"} -{"city": "MENDON", "loc": [-111.981692, 41.709989], "pop": 1015, "state": "UT", "_id": "84325"} -{"city": "PARADISE", "loc": [-111.829665, 41.560009], "pop": 916, "state": "UT", "_id": "84328"} -{"city": "PARK VALLEY", "loc": [-113.34776, 41.855125], "pop": 281, "state": "UT", "_id": "84329"} -{"city": "PROVIDENCE", "loc": [-111.824389, 41.69522], "pop": 7650, "state": "UT", "_id": "84332"} -{"city": "RICHMOND", "loc": [-111.806922, 41.928223], "pop": 2556, "state": "UT", "_id": "84333"} -{"city": "SMITHFIELD", "loc": [-111.852813, 41.840328], "pop": 7376, "state": "UT", "_id": "84335"} -{"city": "SNOWVILLE", "loc": [-112.353391, 41.840321], "pop": 1982, "state": "UT", "_id": "84336"} -{"city": "TREMONTON", "loc": [-112.181293, 41.701564], "pop": 6439, "state": "UT", "_id": "84337"} -{"city": "TRENTON", "loc": [-111.934033, 41.910453], "pop": 450, "state": "UT", "_id": "84338"} -{"city": "WELLSVILLE", "loc": [-111.931676, 41.634302], "pop": 2461, "state": "UT", "_id": "84339"} -{"city": "WILLARD", "loc": [-112.031653, 41.398944], "pop": 2034, "state": "UT", "_id": "84340"} -{"city": "OGDEN", "loc": [-111.962121, 41.22148], "pop": 21276, "state": "UT", "_id": "84401"} -{"city": "OGDEN", "loc": [-111.948927, 41.189412], "pop": 28751, "state": "UT", "_id": "84403"} -{"city": "OGDEN", "loc": [-111.983686, 41.262727], "pop": 35790, "state": "UT", "_id": "84404"} -{"city": "OGDEN", "loc": [-111.980945, 41.173928], "pop": 18982, "state": "UT", "_id": "84405"} -{"city": "OGDEN", "loc": [-111.968924, 41.311201], "pop": 16891, "state": "UT", "_id": "84414"} -{"city": "PRICE", "loc": [-110.808117, 39.602013], "pop": 11741, "state": "UT", "_id": "84501"} -{"city": "ANETH", "loc": [-109.298281, 37.214671], "pop": 3442, "state": "UT", "_id": "84510"} -{"city": "BLANDING", "loc": [-109.486599, 37.586342], "pop": 4469, "state": "UT", "_id": "84511"} -{"city": "EAST CARBON", "loc": [-110.411308, 39.546088], "pop": 1628, "state": "UT", "_id": "84520"} -{"city": "FERRON", "loc": [-111.146698, 39.069196], "pop": 2349, "state": "UT", "_id": "84523"} -{"city": "GREEN RIVER", "loc": [-110.159817, 39.000243], "pop": 919, "state": "UT", "_id": "84525"} -{"city": "HELPER", "loc": [-110.856, 39.673684], "pop": 4005, "state": "UT", "_id": "84526"} -{"city": "HUNTINGTON", "loc": [-110.974094, 39.29202], "pop": 7137, "state": "UT", "_id": "84528"} -{"city": "MEXICAN HAT", "loc": [-109.991865, 37.118429], "pop": 647, "state": "UT", "_id": "84531"} -{"city": "MOAB", "loc": [-109.527087, 38.567674], "pop": 6337, "state": "UT", "_id": "84532"} -{"city": "BULLFROG", "loc": [-110.506917, 37.459898], "pop": 125, "state": "UT", "_id": "84533"} -{"city": "MONTICELLO", "loc": [-109.315289, 37.921709], "pop": 2527, "state": "UT", "_id": "84535"} -{"city": "MONUMENT VALLEY", "loc": [-110.427335, 37.061425], "pop": 1411, "state": "UT", "_id": "84536"} -{"city": "THOMPSON", "loc": [-109.794482, 38.988065], "pop": 210, "state": "UT", "_id": "84540"} -{"city": "WELLINGTON", "loc": [-110.736954, 39.537257], "pop": 2854, "state": "UT", "_id": "84542"} -{"city": "PROVO", "loc": [-111.675504, 40.231949], "pop": 20121, "state": "UT", "_id": "84601"} -{"city": "PROVO", "loc": [-111.654906, 40.260681], "pop": 43841, "state": "UT", "_id": "84604"} -{"city": "PROVO", "loc": [-111.644724, 40.234675], "pop": 23536, "state": "UT", "_id": "84606"} -{"city": "AXTELL", "loc": [-111.824303, 39.053248], "pop": 167, "state": "UT", "_id": "84621"} -{"city": "CENTERFIELD", "loc": [-111.818516, 39.125083], "pop": 945, "state": "UT", "_id": "84622"} -{"city": "DELTA", "loc": [-112.531892, 39.375534], "pop": 5673, "state": "UT", "_id": "84624"} -{"city": "EPHRAIM", "loc": [-111.582301, 39.359983], "pop": 3492, "state": "UT", "_id": "84627"} -{"city": "EUREKA", "loc": [-112.117448, 39.954094], "pop": 600, "state": "UT", "_id": "84628"} -{"city": "FAIRVIEW", "loc": [-111.495287, 39.645318], "pop": 2089, "state": "UT", "_id": "84629"} -{"city": "FAYETTE", "loc": [-111.849712, 39.231533], "pop": 207, "state": "UT", "_id": "84630"} -{"city": "FILLMORE", "loc": [-112.331321, 38.980539], "pop": 3930, "state": "UT", "_id": "84631"} -{"city": "GUNNISON", "loc": [-111.816701, 39.154464], "pop": 1330, "state": "UT", "_id": "84634"} -{"city": "HINCKLEY", "loc": [-112.671577, 39.330842], "pop": 983, "state": "UT", "_id": "84635"} -{"city": "MANTI", "loc": [-111.651372, 39.235389], "pop": 3088, "state": "UT", "_id": "84642"} -{"city": "MONA", "loc": [-111.848001, 39.838165], "pop": 826, "state": "UT", "_id": "84645"} -{"city": "MOUNT PLEASANT", "loc": [-111.503854, 39.523227], "pop": 4979, "state": "UT", "_id": "84647"} -{"city": "NEPHI", "loc": [-111.8359, 39.692275], "pop": 4200, "state": "UT", "_id": "84648"} -{"city": "OASIS", "loc": [-112.648079, 39.27067], "pop": 500, "state": "UT", "_id": "84650"} -{"city": "PAYSON", "loc": [-111.732138, 40.044866], "pop": 13960, "state": "UT", "_id": "84651"} -{"city": "WOODLAND HILLS", "loc": [-111.65906, 40.042702], "pop": 3766, "state": "UT", "_id": "84653"} -{"city": "SALINA", "loc": [-111.881072, 38.956028], "pop": 3716, "state": "UT", "_id": "84654"} -{"city": "GENOLA", "loc": [-111.793819, 39.980356], "pop": 3647, "state": "UT", "_id": "84655"} -{"city": "SPANISH FORK", "loc": [-111.646246, 40.10991], "pop": 12851, "state": "UT", "_id": "84660"} -{"city": "SPRINGVILLE", "loc": [-111.598664, 40.162528], "pop": 14403, "state": "UT", "_id": "84663"} -{"city": "MAPLETON", "loc": [-111.580122, 40.133711], "pop": 3427, "state": "UT", "_id": "84664"} -{"city": "VENICE", "loc": [-112.062253, 38.757075], "pop": 8412, "state": "UT", "_id": "84701"} -{"city": "ALTON", "loc": [-112.548389, 37.469905], "pop": 159, "state": "UT", "_id": "84710"} -{"city": "ANTIMONY", "loc": [-111.993029, 38.1015], "pop": 90, "state": "UT", "_id": "84712"} -{"city": "BEAVER", "loc": [-112.629916, 38.28071], "pop": 2560, "state": "UT", "_id": "84713"} -{"city": "BERYL", "loc": [-113.619586, 37.96005], "pop": 12, "state": "UT", "_id": "84714"} -{"city": "BOULDER", "loc": [-111.426646, 37.916606], "pop": 131, "state": "UT", "_id": "84716"} -{"city": "BRYCE CANYON", "loc": [-112.074311, 37.608427], "pop": 958, "state": "UT", "_id": "84717"} -{"city": "BRIAN HEAD", "loc": [-112.843698, 37.698465], "pop": 111, "state": "UT", "_id": "84719"} -{"city": "PINTURA", "loc": [-113.074513, 37.689544], "pop": 17392, "state": "UT", "_id": "84720"} -{"city": "CENTRAL", "loc": [-113.717199, 37.567734], "pop": 1127, "state": "UT", "_id": "84722"} -{"city": "ESCALANTE", "loc": [-111.603695, 37.769839], "pop": 955, "state": "UT", "_id": "84726"} -{"city": "GARRISON", "loc": [-113.894858, 39.074382], "pop": 247, "state": "UT", "_id": "84728"} -{"city": "GLENDALE", "loc": [-112.603491, 37.321897], "pop": 287, "state": "UT", "_id": "84729"} -{"city": "GREENVILLE", "loc": [-112.708404, 38.263582], "pop": 116, "state": "UT", "_id": "84731"} -{"city": "HANKSVILLE", "loc": [-110.813744, 38.252051], "pop": 373, "state": "UT", "_id": "84734"} -{"city": "HURRICANE", "loc": [-113.224706, 37.169098], "pop": 9174, "state": "UT", "_id": "84737"} -{"city": "JOSEPH", "loc": [-112.225119, 38.622092], "pop": 289, "state": "UT", "_id": "84739"} -{"city": "BIG WATER", "loc": [-112.345374, 37.072085], "pop": 4174, "state": "UT", "_id": "84741"} -{"city": "KINGSTON", "loc": [-112.204864, 38.215772], "pop": 825, "state": "UT", "_id": "84743"} -{"city": "FREMONT", "loc": [-111.629233, 38.417266], "pop": 987, "state": "UT", "_id": "84747"} -{"city": "MARYSVALE", "loc": [-112.251848, 38.444924], "pop": 452, "state": "UT", "_id": "84750"} -{"city": "MILFORD", "loc": [-112.993377, 38.331071], "pop": 2089, "state": "UT", "_id": "84751"} -{"city": "MODENA", "loc": [-113.919282, 37.799452], "pop": 9, "state": "UT", "_id": "84753"} -{"city": "AUSTIN", "loc": [-112.132434, 38.645305], "pop": 2995, "state": "UT", "_id": "84754"} -{"city": "MOUNT CARMEL", "loc": [-112.670034, 37.237821], "pop": 43, "state": "UT", "_id": "84755"} -{"city": "NEWCASTLE", "loc": [-113.661479, 37.736049], "pop": 758, "state": "UT", "_id": "84756"} -{"city": "ORDERVILLE", "loc": [-112.642045, 37.274419], "pop": 506, "state": "UT", "_id": "84758"} -{"city": "PANGUITCH", "loc": [-112.436886, 37.80777], "pop": 1797, "state": "UT", "_id": "84759"} -{"city": "PARAGONAH", "loc": [-112.773972, 37.89172], "pop": 334, "state": "UT", "_id": "84760"} -{"city": "PAROWAN", "loc": [-112.832251, 37.844861], "pop": 1988, "state": "UT", "_id": "84761"} -{"city": "SEVIER", "loc": [-112.392126, 38.590709], "pop": 19, "state": "UT", "_id": "84766"} -{"city": "ST GEORGE", "loc": [-113.595261, 37.106651], "pop": 33146, "state": "UT", "_id": "84770"} -{"city": "SUMMIT", "loc": [-112.913407, 37.803397], "pop": 185, "state": "UT", "_id": "84772"} -{"city": "TEASDALE", "loc": [-111.43346, 38.285334], "pop": 430, "state": "UT", "_id": "84773"} -{"city": "TORREY", "loc": [-111.547439, 38.334993], "pop": 436, "state": "UT", "_id": "84775"} -{"city": "WASHINGTON", "loc": [-113.505043, 37.136379], "pop": 4134, "state": "UT", "_id": "84780"} -{"city": "PINE VALLEY", "loc": [-113.517708, 37.389772], "pop": 31, "state": "UT", "_id": "84781"} -{"city": "VEYO", "loc": [-113.666758, 37.35896], "pop": 449, "state": "UT", "_id": "84782"} -{"city": "DAMMERON VALLEY", "loc": [-113.658553, 37.284899], "pop": 499, "state": "UT", "_id": "84783"} -{"city": "WHITE RIVER JUNC", "loc": [-72.350351, 43.663431], "pop": 9500, "state": "VT", "_id": "05001"} -{"city": "BETHEL", "loc": [-72.652807, 43.819464], "pop": 2725, "state": "VT", "_id": "05032"} -{"city": "BRADFORD", "loc": [-72.140638, 44.0006], "pop": 2480, "state": "VT", "_id": "05033"} -{"city": "BRIDGEWATER", "loc": [-72.646704, 43.580428], "pop": 130, "state": "VT", "_id": "05034"} -{"city": "BRIDGEWATER CORN", "loc": [-72.682212, 43.602147], "pop": 662, "state": "VT", "_id": "05035"} -{"city": "BROOKFIELD", "loc": [-72.595259, 44.032083], "pop": 453, "state": "VT", "_id": "05036"} -{"city": "BROWNSVILLE", "loc": [-72.494405, 43.464584], "pop": 415, "state": "VT", "_id": "05037"} -{"city": "CHELSEA", "loc": [-72.479303, 44.003931], "pop": 1022, "state": "VT", "_id": "05038"} -{"city": "CORINTH", "loc": [-72.282241, 44.032533], "pop": 1035, "state": "VT", "_id": "05039"} -{"city": "EAST CORINTH", "loc": [-72.215144, 44.061851], "pop": 279, "state": "VT", "_id": "05040"} -{"city": "EAST RANDOLPH", "loc": [-72.548746, 43.955297], "pop": 322, "state": "VT", "_id": "05041"} -{"city": "RYEGATE", "loc": [-72.072669, 44.193453], "pop": 328, "state": "VT", "_id": "05042"} -{"city": "EAST THETFORD", "loc": [-72.19668, 43.825757], "pop": 657, "state": "VT", "_id": "05043"} -{"city": "FAIRLEE", "loc": [-72.190214, 43.90105], "pop": 1631, "state": "VT", "_id": "05045"} -{"city": "GROTON", "loc": [-72.217548, 44.220435], "pop": 862, "state": "VT", "_id": "05046"} -{"city": "HARTLAND", "loc": [-72.413103, 43.570764], "pop": 1730, "state": "VT", "_id": "05048"} -{"city": "NEWBURY", "loc": [-72.107231, 44.096555], "pop": 1488, "state": "VT", "_id": "05051"} -{"city": "NORTH HARTLAND", "loc": [-72.352591, 43.596721], "pop": 429, "state": "VT", "_id": "05052"} -{"city": "NORTH POMFRET", "loc": [-72.494287, 43.720401], "pop": 254, "state": "VT", "_id": "05053"} -{"city": "NORWICH", "loc": [-72.30162, 43.740373], "pop": 2969, "state": "VT", "_id": "05055"} -{"city": "PLYMOUTH", "loc": [-72.710655, 43.498653], "pop": 319, "state": "VT", "_id": "05056"} -{"city": "POST MILLS", "loc": [-72.262031, 43.893288], "pop": 511, "state": "VT", "_id": "05058"} -{"city": "RANDOLPH", "loc": [-72.67258, 43.944399], "pop": 4421, "state": "VT", "_id": "05060"} -{"city": "RANDOLPH CENTER", "loc": [-72.596975, 43.9379], "pop": 1567, "state": "VT", "_id": "05061"} -{"city": "READING", "loc": [-72.570446, 43.47935], "pop": 614, "state": "VT", "_id": "05062"} -{"city": "SHARON", "loc": [-72.424283, 43.774843], "pop": 599, "state": "VT", "_id": "05065"} -{"city": "SOUTH POMFRET", "loc": [-72.534907, 43.679665], "pop": 281, "state": "VT", "_id": "05067"} -{"city": "SOUTH ROYALTON", "loc": [-72.523228, 43.808931], "pop": 3169, "state": "VT", "_id": "05068"} -{"city": "SOUTH RYEGATE", "loc": [-72.129558, 44.211075], "pop": 730, "state": "VT", "_id": "05069"} -{"city": "SOUTH STRAFFORD", "loc": [-72.369959, 43.828238], "pop": 331, "state": "VT", "_id": "05070"} -{"city": "SOUTH WOODSTOCK", "loc": [-72.538383, 43.560137], "pop": 406, "state": "VT", "_id": "05071"} -{"city": "STRAFFORD", "loc": [-72.36363, 43.872306], "pop": 554, "state": "VT", "_id": "05072"} -{"city": "TAFTSVILLE", "loc": [-72.467331, 43.629819], "pop": 35, "state": "VT", "_id": "05073"} -{"city": "THETFORD CENTER", "loc": [-72.266204, 43.814883], "pop": 1014, "state": "VT", "_id": "05075"} -{"city": "TUNBRIDGE", "loc": [-72.478578, 43.9169], "pop": 1352, "state": "VT", "_id": "05077"} -{"city": "VERSHIRE", "loc": [-72.319015, 43.961811], "pop": 565, "state": "VT", "_id": "05079"} -{"city": "WELLS RIVER", "loc": [-72.052958, 44.152438], "pop": 473, "state": "VT", "_id": "05081"} -{"city": "WEST FAIRLEE", "loc": [-72.26163, 43.92232], "pop": 153, "state": "VT", "_id": "05083"} -{"city": "WEST HARTFORD", "loc": [-72.444618, 43.716068], "pop": 135, "state": "VT", "_id": "05084"} -{"city": "WEST TOPSHAM", "loc": [-72.270051, 44.119925], "pop": 1050, "state": "VT", "_id": "05086"} -{"city": "WINDSOR", "loc": [-72.410998, 43.476032], "pop": 5406, "state": "VT", "_id": "05089"} -{"city": "WOODSTOCK", "loc": [-72.538455, 43.62482], "pop": 3742, "state": "VT", "_id": "05091"} -{"city": "BELLOWS FALLS", "loc": [-72.466373, 43.148198], "pop": 5433, "state": "VT", "_id": "05101"} -{"city": "CAMBRIDGEPORT", "loc": [-72.565511, 43.153363], "pop": 118, "state": "VT", "_id": "05141"} -{"city": "CAVENDISH", "loc": [-72.609247, 43.376621], "pop": 258, "state": "VT", "_id": "05142"} -{"city": "CHESTER", "loc": [-72.603125, 43.283124], "pop": 3449, "state": "VT", "_id": "05143"} -{"city": "GRAFTON", "loc": [-72.597229, 43.160095], "pop": 823, "state": "VT", "_id": "05146"} -{"city": "BROMLEY MTN", "loc": [-72.801756, 43.223961], "pop": 986, "state": "VT", "_id": "05148"} -{"city": "LUDLOW", "loc": [-72.700184, 43.392909], "pop": 2420, "state": "VT", "_id": "05149"} -{"city": "NORTH SPRINGFIEL", "loc": [-72.529096, 43.335452], "pop": 824, "state": "VT", "_id": "05150"} -{"city": "PERKINSVILLE", "loc": [-72.503813, 43.387885], "pop": 1181, "state": "VT", "_id": "05151"} -{"city": "PERU", "loc": [-72.886797, 43.256532], "pop": 458, "state": "VT", "_id": "05152"} -{"city": "PROCTORSVILLE", "loc": [-72.61962, 43.4005], "pop": 1065, "state": "VT", "_id": "05153"} -{"city": "SAXTONS RIVER", "loc": [-72.508107, 43.137861], "pop": 681, "state": "VT", "_id": "05154"} -{"city": "SOUTH LONDONDERR", "loc": [-72.811787, 43.183082], "pop": 546, "state": "VT", "_id": "05155"} -{"city": "SPRINGFIELD", "loc": [-72.477769, 43.303433], "pop": 9231, "state": "VT", "_id": "05156"} -{"city": "WESTON", "loc": [-72.793523, 43.29409], "pop": 488, "state": "VT", "_id": "05161"} -{"city": "BENNINGTON", "loc": [-73.192321, 42.882658], "pop": 14757, "state": "VT", "_id": "05201"} -{"city": "ARLINGTON", "loc": [-73.159364, 43.085681], "pop": 3006, "state": "VT", "_id": "05250"} -{"city": "DORSET", "loc": [-73.076603, 43.263561], "pop": 1402, "state": "VT", "_id": "05251"} -{"city": "EAST ARLINGTON", "loc": [-73.138904, 43.063801], "pop": 443, "state": "VT", "_id": "05252"} -{"city": "EAST DORSET", "loc": [-73.008146, 43.236982], "pop": 516, "state": "VT", "_id": "05253"} -{"city": "MANCHESTER CENTE", "loc": [-73.052313, 43.175223], "pop": 3622, "state": "VT", "_id": "05255"} -{"city": "NORTH BENNINGTON", "loc": [-73.237531, 42.92385], "pop": 2037, "state": "VT", "_id": "05257"} -{"city": "NORTH POWNAL", "loc": [-73.25343, 42.809775], "pop": 523, "state": "VT", "_id": "05260"} -{"city": "POWNAL", "loc": [-73.21624, 42.787955], "pop": 2962, "state": "VT", "_id": "05261"} -{"city": "SHAFTSBURY", "loc": [-73.216591, 42.96144], "pop": 3363, "state": "VT", "_id": "05262"} -{"city": "BRATTLEBORO", "loc": [-72.593322, 42.857353], "pop": 17522, "state": "VT", "_id": "05301"} -{"city": "BONDVILLE", "loc": [-72.912768, 43.161831], "pop": 482, "state": "VT", "_id": "05340"} -{"city": "EAST DOVER", "loc": [-72.783032, 42.950508], "pop": 253, "state": "VT", "_id": "05341"} -{"city": "JACKSONVILLE", "loc": [-72.807121, 42.777752], "pop": 406, "state": "VT", "_id": "05342"} -{"city": "JAMAICA", "loc": [-72.794409, 43.104235], "pop": 603, "state": "VT", "_id": "05343"} -{"city": "NEWFANE", "loc": [-72.678078, 42.97043], "pop": 1389, "state": "VT", "_id": "05345"} -{"city": "PUTNEY", "loc": [-72.512272, 43.035561], "pop": 4971, "state": "VT", "_id": "05346"} -{"city": "READSBORO", "loc": [-72.960886, 42.78367], "pop": 762, "state": "VT", "_id": "05350"} -{"city": "STAMFORD", "loc": [-73.069068, 42.76962], "pop": 773, "state": "VT", "_id": "05352"} -{"city": "TOWNSHEND", "loc": [-72.668676, 43.062174], "pop": 920, "state": "VT", "_id": "05353"} -{"city": "VERNON", "loc": [-72.511547, 42.756468], "pop": 1850, "state": "VT", "_id": "05354"} -{"city": "WARDSBORO", "loc": [-72.807687, 43.032649], "pop": 573, "state": "VT", "_id": "05355"} -{"city": "MOUNT SNOW", "loc": [-72.854235, 42.956029], "pop": 743, "state": "VT", "_id": "05356"} -{"city": "WEST HALIFAX", "loc": [-72.750142, 42.776103], "pop": 202, "state": "VT", "_id": "05358"} -{"city": "WEST TOWNSHEND", "loc": [-72.720985, 43.130044], "pop": 402, "state": "VT", "_id": "05359"} -{"city": "WEST WARDSBORO", "loc": [-72.880537, 43.047221], "pop": 228, "state": "VT", "_id": "05360"} -{"city": "WHITINGHAM", "loc": [-72.856001, 42.790071], "pop": 921, "state": "VT", "_id": "05361"} -{"city": "WILLIAMSVILLE", "loc": [-72.658406, 42.948151], "pop": 166, "state": "VT", "_id": "05362"} -{"city": "WILMINGTON", "loc": [-72.861316, 42.881216], "pop": 2053, "state": "VT", "_id": "05363"} -{"city": "BURLINGTON", "loc": [-73.219875, 44.484023], "pop": 39127, "state": "VT", "_id": "05401"} -{"city": "SOUTH BURLINGTON", "loc": [-73.179617, 44.451324], "pop": 12809, "state": "VT", "_id": "05403"} -{"city": "WINOOSKI", "loc": [-73.187413, 44.494898], "pop": 6649, "state": "VT", "_id": "05404"} -{"city": "UNIV OF VERMONT", "loc": [-73.2002, 44.477733], "pop": 0, "state": "VT", "_id": "05405"} -{"city": "ALBURG", "loc": [-73.280963, 44.968454], "pop": 1362, "state": "VT", "_id": "05440"} -{"city": "BAKERSFIELD", "loc": [-72.796834, 44.790518], "pop": 973, "state": "VT", "_id": "05441"} -{"city": "BRISTOL", "loc": [-73.071742, 44.146031], "pop": 6685, "state": "VT", "_id": "05443"} -{"city": "CAMBRIDGE", "loc": [-72.865728, 44.651013], "pop": 3491, "state": "VT", "_id": "05444"} -{"city": "CHARLOTTE", "loc": [-73.227969, 44.311277], "pop": 3009, "state": "VT", "_id": "05445"} -{"city": "COLCHESTER", "loc": [-73.202216, 44.535956], "pop": 14731, "state": "VT", "_id": "05446"} -{"city": "EAST BERKSHIRE", "loc": [-72.706564, 44.929798], "pop": 94, "state": "VT", "_id": "05447"} -{"city": "EAST FAIRFIELD", "loc": [-72.910995, 44.801805], "pop": 1076, "state": "VT", "_id": "05448"} -{"city": "ENOSBURG FALLS", "loc": [-72.791037, 44.898491], "pop": 2268, "state": "VT", "_id": "05450"} -{"city": "ESSEX JUNCTION", "loc": [-73.090644, 44.503466], "pop": 17050, "state": "VT", "_id": "05452"} -{"city": "FAIRFAX", "loc": [-73.024078, 44.692387], "pop": 2990, "state": "VT", "_id": "05454"} -{"city": "FAIRFIELD", "loc": [-73.022187, 44.784087], "pop": 359, "state": "VT", "_id": "05455"} -{"city": "FERRISBURG", "loc": [-73.258575, 44.212822], "pop": 951, "state": "VT", "_id": "05456"} -{"city": "FRANKLIN", "loc": [-72.903694, 44.961408], "pop": 1475, "state": "VT", "_id": "05457"} -{"city": "GRAND ISLE", "loc": [-73.30561, 44.719711], "pop": 1590, "state": "VT", "_id": "05458"} -{"city": "HIGHGATE CENTER", "loc": [-73.015468, 44.940395], "pop": 1567, "state": "VT", "_id": "05459"} -{"city": "HINESBURG", "loc": [-73.097955, 44.334621], "pop": 3187, "state": "VT", "_id": "05461"} -{"city": "HUNTINGTON", "loc": [-72.996432, 44.322655], "pop": 2223, "state": "VT", "_id": "05462"} -{"city": "ISLE LA MOTTE", "loc": [-73.339095, 44.874829], "pop": 408, "state": "VT", "_id": "05463"} -{"city": "SMUGGLERS NOTCH", "loc": [-72.780577, 44.662499], "pop": 300, "state": "VT", "_id": "05464"} -{"city": "JERICHO CENTER", "loc": [-72.979398, 44.484318], "pop": 3449, "state": "VT", "_id": "05465"} -{"city": "MILTON", "loc": [-73.131692, 44.648275], "pop": 10627, "state": "VT", "_id": "05468"} -{"city": "MONTGOMERY CENTE", "loc": [-72.599352, 44.874606], "pop": 603, "state": "VT", "_id": "05471"} -{"city": "NEW HAVEN", "loc": [-73.17347, 44.112575], "pop": 858, "state": "VT", "_id": "05472"} -{"city": "NORTH FERRISBURG", "loc": [-73.203254, 44.251461], "pop": 1126, "state": "VT", "_id": "05473"} -{"city": "NORTH HERO", "loc": [-73.284169, 44.829392], "pop": 502, "state": "VT", "_id": "05474"} -{"city": "RICHFORD", "loc": [-72.690472, 44.971453], "pop": 3794, "state": "VT", "_id": "05476"} -{"city": "BOLTON VALLEY", "loc": [-72.993256, 44.399846], "pop": 3758, "state": "VT", "_id": "05477"} -{"city": "SAINT ALBANS", "loc": [-73.089025, 44.811138], "pop": 13555, "state": "VT", "_id": "05478"} -{"city": "SHELBURNE", "loc": [-73.217124, 44.389967], "pop": 5871, "state": "VT", "_id": "05482"} -{"city": "SHELDON", "loc": [-72.952989, 44.887167], "pop": 1556, "state": "VT", "_id": "05483"} -{"city": "SOUTH HERO", "loc": [-73.311344, 44.640044], "pop": 1456, "state": "VT", "_id": "05486"} -{"city": "STARKSBORO", "loc": [-73.0157, 44.2261], "pop": 1020, "state": "VT", "_id": "05487"} -{"city": "SWANTON", "loc": [-73.121124, 44.916754], "pop": 6570, "state": "VT", "_id": "05488"} -{"city": "UNDERHILL", "loc": [-72.925845, 44.539069], "pop": 3637, "state": "VT", "_id": "05489"} -{"city": "VERGENNES", "loc": [-73.279293, 44.132569], "pop": 6041, "state": "VT", "_id": "05491"} -{"city": "WATERVILLE", "loc": [-72.759557, 44.712375], "pop": 532, "state": "VT", "_id": "05492"} -{"city": "WESTFORD", "loc": [-73.006021, 44.618191], "pop": 1102, "state": "VT", "_id": "05494"} -{"city": "WILLISTON", "loc": [-73.095704, 44.436745], "pop": 5592, "state": "VT", "_id": "05495"} -{"city": "MONTPELIER", "loc": [-72.576992, 44.264082], "pop": 12475, "state": "VT", "_id": "05602"} -{"city": "ADAMANT", "loc": [-72.504649, 44.35747], "pop": 456, "state": "VT", "_id": "05640"} -{"city": "BARRE", "loc": [-72.493619, 44.194522], "pop": 14994, "state": "VT", "_id": "05641"} -{"city": "CABOT", "loc": [-72.306443, 44.404215], "pop": 1043, "state": "VT", "_id": "05647"} -{"city": "CALAIS", "loc": [-72.474113, 44.393438], "pop": 319, "state": "VT", "_id": "05648"} -{"city": "EAST BARRE", "loc": [-72.446003, 44.158277], "pop": 381, "state": "VT", "_id": "05649"} -{"city": "EAST CALAIS", "loc": [-72.439829, 44.355599], "pop": 746, "state": "VT", "_id": "05650"} -{"city": "EAST MONTPELIER", "loc": [-72.492636, 44.282844], "pop": 1018, "state": "VT", "_id": "05651"} -{"city": "EDEN", "loc": [-72.5863, 44.734267], "pop": 913, "state": "VT", "_id": "05652"} -{"city": "EDEN MILLS", "loc": [-72.513447, 44.693123], "pop": 155, "state": "VT", "_id": "05653"} -{"city": "GRANITEVILLE", "loc": [-72.484529, 44.157326], "pop": 2321, "state": "VT", "_id": "05654"} -{"city": "HYDE PARK", "loc": [-72.594971, 44.622453], "pop": 2094, "state": "VT", "_id": "05655"} -{"city": "JOHNSON", "loc": [-72.671154, 44.64204], "pop": 3156, "state": "VT", "_id": "05656"} -{"city": "MARSHFIELD", "loc": [-72.374994, 44.321841], "pop": 1331, "state": "VT", "_id": "05658"} -{"city": "MORETOWN", "loc": [-72.749395, 44.259602], "pop": 1896, "state": "VT", "_id": "05660"} -{"city": "MORRISVILLE", "loc": [-72.602625, 44.554335], "pop": 4935, "state": "VT", "_id": "05661"} -{"city": "RIVERTON", "loc": [-72.659795, 44.149961], "pop": 6245, "state": "VT", "_id": "05663"} -{"city": "NORTH MONTPELIER", "loc": [-72.482119, 44.256612], "pop": 469, "state": "VT", "_id": "05666"} -{"city": "PLAINFIELD", "loc": [-72.42243, 44.262164], "pop": 1304, "state": "VT", "_id": "05667"} -{"city": "ROXBURY", "loc": [-72.720151, 44.084571], "pop": 575, "state": "VT", "_id": "05669"} -{"city": "STOWE", "loc": [-72.692282, 44.469512], "pop": 3433, "state": "VT", "_id": "05672"} -{"city": "WAITSFIELD", "loc": [-72.828321, 44.188917], "pop": 1751, "state": "VT", "_id": "05673"} -{"city": "SUGARBUSH VALLEY", "loc": [-72.848179, 44.114222], "pop": 1172, "state": "VT", "_id": "05674"} -{"city": "WASHGTIN", "loc": [-72.430002, 44.086448], "pop": 1022, "state": "VT", "_id": "05675"} -{"city": "WATERBURY", "loc": [-72.779768, 44.345103], "pop": 4751, "state": "VT", "_id": "05676"} -{"city": "WATERBURY CENTER", "loc": [-72.708006, 44.382797], "pop": 1645, "state": "VT", "_id": "05677"} -{"city": "WILLIAMSTOWN", "loc": [-72.53811, 44.118191], "pop": 2964, "state": "VT", "_id": "05679"} -{"city": "WOLCOTT", "loc": [-72.484416, 44.555753], "pop": 1850, "state": "VT", "_id": "05680"} -{"city": "WOODBURY", "loc": [-72.417766, 44.432507], "pop": 766, "state": "VT", "_id": "05681"} -{"city": "WORCESTER", "loc": [-72.560925, 44.394389], "pop": 906, "state": "VT", "_id": "05682"} -{"city": "RUTLAND", "loc": [-72.970773, 43.614131], "pop": 22576, "state": "VT", "_id": "05701"} -{"city": "BELMONT", "loc": [-72.825701, 43.428441], "pop": 223, "state": "VT", "_id": "05730"} -{"city": "HUBBARDTON", "loc": [-73.206424, 43.622413], "pop": 914, "state": "VT", "_id": "05732"} -{"city": "BRANDON", "loc": [-73.088182, 43.806467], "pop": 5027, "state": "VT", "_id": "05733"} -{"city": "BRIDPORT", "loc": [-73.347557, 43.95383], "pop": 641, "state": "VT", "_id": "05734"} -{"city": "CASTLETON", "loc": [-73.170797, 43.622312], "pop": 2897, "state": "VT", "_id": "05735"} -{"city": "CENTER RUTLAND", "loc": [-73.016986, 43.602345], "pop": 299, "state": "VT", "_id": "05736"} -{"city": "CHITTENDEN", "loc": [-72.936001, 43.703382], "pop": 526, "state": "VT", "_id": "05737"} -{"city": "CUTTINGSVILLE", "loc": [-72.869089, 43.522084], "pop": 1101, "state": "VT", "_id": "05738"} -{"city": "DANBY", "loc": [-73.01285, 43.358341], "pop": 1170, "state": "VT", "_id": "05739"} -{"city": "EAST WALLINGFORD", "loc": [-72.884375, 43.446063], "pop": 588, "state": "VT", "_id": "05742"} -{"city": "FAIR HAVEN", "loc": [-73.270068, 43.62345], "pop": 4283, "state": "VT", "_id": "05743"} -{"city": "FLORENCE", "loc": [-73.078988, 43.709294], "pop": 257, "state": "VT", "_id": "05744"} -{"city": "GAYSVILLE", "loc": [-72.731149, 43.752912], "pop": 198, "state": "VT", "_id": "05746"} -{"city": "GRANVILLE", "loc": [-72.8245, 43.984912], "pop": 309, "state": "VT", "_id": "05747"} -{"city": "HANCOCK", "loc": [-72.913285, 43.912525], "pop": 98, "state": "VT", "_id": "05748"} -{"city": "KILLINGTON", "loc": [-72.79631, 43.663364], "pop": 738, "state": "VT", "_id": "05751"} -{"city": "BREAD LOAF", "loc": [-73.16613, 44.007042], "pop": 9990, "state": "VT", "_id": "05753"} -{"city": "MIDDLETOWN SPRIN", "loc": [-73.11452, 43.484864], "pop": 966, "state": "VT", "_id": "05757"} -{"city": "MOUNT HOLLY", "loc": [-72.795562, 43.44865], "pop": 693, "state": "VT", "_id": "05758"} -{"city": "NORTH CLARENDON", "loc": [-72.956096, 43.552208], "pop": 2517, "state": "VT", "_id": "05759"} -{"city": "ORWELL", "loc": [-73.294855, 43.783722], "pop": 1315, "state": "VT", "_id": "05760"} -{"city": "PAWLET", "loc": [-73.144404, 43.358679], "pop": 935, "state": "VT", "_id": "05761"} -{"city": "PITTSFIELD", "loc": [-72.853376, 43.753823], "pop": 450, "state": "VT", "_id": "05762"} -{"city": "PITTSFORD", "loc": [-73.013492, 43.71524], "pop": 2864, "state": "VT", "_id": "05763"} -{"city": "POULTNEY", "loc": [-73.225281, 43.53321], "pop": 4012, "state": "VT", "_id": "05764"} -{"city": "PROCTOR", "loc": [-73.034816, 43.657951], "pop": 1868, "state": "VT", "_id": "05765"} -{"city": "RIPTON", "loc": [-73.01874, 43.992862], "pop": 433, "state": "VT", "_id": "05766"} -{"city": "ROCHESTER", "loc": [-72.815917, 43.880382], "pop": 1551, "state": "VT", "_id": "05767"} -{"city": "SALISBURY", "loc": [-73.100793, 43.90172], "pop": 1885, "state": "VT", "_id": "05769"} -{"city": "SHOREHAM", "loc": [-73.305385, 43.886214], "pop": 946, "state": "VT", "_id": "05770"} -{"city": "STOCKBRIDGE", "loc": [-72.781394, 43.773808], "pop": 124, "state": "VT", "_id": "05772"} -{"city": "WALLINGFORD", "loc": [-72.987722, 43.4573], "pop": 2172, "state": "VT", "_id": "05773"} -{"city": "WELLS", "loc": [-73.202684, 43.430456], "pop": 837, "state": "VT", "_id": "05774"} -{"city": "WEST PAWLET", "loc": [-73.231327, 43.36673], "pop": 604, "state": "VT", "_id": "05775"} -{"city": "WEST RUPERT", "loc": [-73.202847, 43.261792], "pop": 654, "state": "VT", "_id": "05776"} -{"city": "WEST RUTLAND", "loc": [-73.04242, 43.578072], "pop": 3415, "state": "VT", "_id": "05777"} -{"city": "LEICESTER JUNCTI", "loc": [-73.213312, 43.874972], "pop": 615, "state": "VT", "_id": "05778"} -{"city": "SAINT JOHNSBURY", "loc": [-72.005062, 44.427195], "pop": 8797, "state": "VT", "_id": "05819"} -{"city": "ALBANY", "loc": [-72.34181, 44.734427], "pop": 782, "state": "VT", "_id": "05820"} -{"city": "BARNET", "loc": [-72.078326, 44.317923], "pop": 1415, "state": "VT", "_id": "05821"} -{"city": "BARTON", "loc": [-72.160005, 44.739465], "pop": 1270, "state": "VT", "_id": "05822"} -{"city": "CONCORD", "loc": [-71.867554, 44.446055], "pop": 1242, "state": "VT", "_id": "05824"} -{"city": "COVENTRY", "loc": [-72.254471, 44.880853], "pop": 722, "state": "VT", "_id": "05825"} -{"city": "CRAFTSBURY", "loc": [-72.366007, 44.64341], "pop": 623, "state": "VT", "_id": "05826"} -{"city": "CRAFTSBURY COMMO", "loc": [-72.414578, 44.652917], "pop": 399, "state": "VT", "_id": "05827"} -{"city": "DANVILLE", "loc": [-72.113319, 44.433241], "pop": 1482, "state": "VT", "_id": "05828"} -{"city": "DERBY", "loc": [-72.137555, 44.950182], "pop": 2607, "state": "VT", "_id": "05829"} -{"city": "DERBY LINE", "loc": [-72.082367, 44.990183], "pop": 2040, "state": "VT", "_id": "05830"} -{"city": "EAST BURKE", "loc": [-71.935674, 44.607849], "pop": 875, "state": "VT", "_id": "05832"} -{"city": "EAST CHARLESTON", "loc": [-71.97627, 44.829896], "pop": 246, "state": "VT", "_id": "05833"} -{"city": "EAST HARDWICK", "loc": [-72.303351, 44.528833], "pop": 493, "state": "VT", "_id": "05836"} -{"city": "EAST HAVEN", "loc": [-71.867491, 44.651724], "pop": 269, "state": "VT", "_id": "05837"} -{"city": "GLOVER", "loc": [-72.203819, 44.719979], "pop": 795, "state": "VT", "_id": "05839"} -{"city": "GREENSBORO", "loc": [-72.285017, 44.595069], "pop": 461, "state": "VT", "_id": "05841"} -{"city": "GREENSBORO BEND", "loc": [-72.252478, 44.548488], "pop": 405, "state": "VT", "_id": "05842"} -{"city": "HARDWICK", "loc": [-72.363818, 44.507026], "pop": 2450, "state": "VT", "_id": "05843"} -{"city": "IRASBURG", "loc": [-72.276269, 44.808056], "pop": 907, "state": "VT", "_id": "05845"} -{"city": "ISLAND POND", "loc": [-71.883964, 44.809452], "pop": 1585, "state": "VT", "_id": "05846"} -{"city": "LOWELL", "loc": [-72.450132, 44.796107], "pop": 594, "state": "VT", "_id": "05847"} -{"city": "LYNDON CENTER", "loc": [-72.031062, 44.54719], "pop": 519, "state": "VT", "_id": "05850"} -{"city": "LYNDONVILLE", "loc": [-72.009168, 44.540766], "pop": 5417, "state": "VT", "_id": "05851"} -{"city": "MORGAN CTR", "loc": [-71.994052, 44.90659], "pop": 497, "state": "VT", "_id": "05853"} -{"city": "NEWPORT", "loc": [-72.206505, 44.939344], "pop": 4773, "state": "VT", "_id": "05855"} -{"city": "NEWPORT CENTER", "loc": [-72.297412, 44.944175], "pop": 1367, "state": "VT", "_id": "05857"} -{"city": "NORTH CONCORD", "loc": [-71.786662, 44.550099], "pop": 191, "state": "VT", "_id": "05858"} -{"city": "JAY PEAK", "loc": [-72.457231, 44.961861], "pop": 370, "state": "VT", "_id": "05859"} -{"city": "ORLEANS", "loc": [-72.165328, 44.80544], "pop": 2467, "state": "VT", "_id": "05860"} -{"city": "PEACHAM", "loc": [-72.177039, 44.335563], "pop": 627, "state": "VT", "_id": "05862"} -{"city": "SHEFFIELD", "loc": [-72.123927, 44.61784], "pop": 600, "state": "VT", "_id": "05866"} -{"city": "SUTTON", "loc": [-72.021133, 44.646543], "pop": 854, "state": "VT", "_id": "05867"} -{"city": "TROY", "loc": [-72.393808, 44.959381], "pop": 1609, "state": "VT", "_id": "05868"} -{"city": "WEST BURKE", "loc": [-71.95718, 44.663647], "pop": 885, "state": "VT", "_id": "05871"} -{"city": "WEST CHARLESTON", "loc": [-72.05209, 44.872894], "pop": 598, "state": "VT", "_id": "05872"} -{"city": "WEST DANVILLE", "loc": [-72.219771, 44.446509], "pop": 1130, "state": "VT", "_id": "05873"} -{"city": "WESTFIELD", "loc": [-72.439628, 44.882457], "pop": 398, "state": "VT", "_id": "05874"} -{"city": "WEST GLOVER", "loc": [-72.259936, 44.703145], "pop": 265, "state": "VT", "_id": "05875"} -{"city": "AVERILL", "loc": [-71.700268, 44.992304], "pop": 7, "state": "VT", "_id": "05901"} -{"city": "BEECHER FALLS", "loc": [-71.519901, 45.006103], "pop": 579, "state": "VT", "_id": "05902"} -{"city": "CANAAN", "loc": [-71.560214, 44.976439], "pop": 542, "state": "VT", "_id": "05903"} -{"city": "GILMAN", "loc": [-71.71786, 44.413985], "pop": 322, "state": "VT", "_id": "05904"} -{"city": "GUILDHALL", "loc": [-71.599057, 44.690218], "pop": 848, "state": "VT", "_id": "05905"} -{"city": "LUNENBURG", "loc": [-71.69304, 44.466262], "pop": 854, "state": "VT", "_id": "05906"} -{"city": "NORTON", "loc": [-71.78844, 44.988781], "pop": 171, "state": "VT", "_id": "05907"} -{"city": "ALDIE", "loc": [-77.602281, 38.957878], "pop": 1574, "state": "VA", "_id": "22001"} -{"city": "AMISSVILLE", "loc": [-78.038053, 38.710055], "pop": 1581, "state": "VA", "_id": "22002"} -{"city": "ANNANDALE", "loc": [-77.214245, 38.830699], "pop": 50054, "state": "VA", "_id": "22003"} -{"city": "ARCOLA", "loc": [-77.538721, 38.973626], "pop": 61, "state": "VA", "_id": "22010"} -{"city": "ASHBURN", "loc": [-77.480612, 39.039918], "pop": 3979, "state": "VA", "_id": "22011"} -{"city": "BLUEMONT", "loc": [-77.831416, 39.097732], "pop": 557, "state": "VA", "_id": "22012"} -{"city": "BRISTOW", "loc": [-77.578843, 38.74644], "pop": 1568, "state": "VA", "_id": "22013"} -{"city": "BROAD RUN", "loc": [-77.68611, 38.828653], "pop": 344, "state": "VA", "_id": "22014"} -{"city": "BURKE", "loc": [-77.28175, 38.789408], "pop": 41783, "state": "VA", "_id": "22015"} -{"city": "CATHARPIN", "loc": [-77.561155, 38.853007], "pop": 647, "state": "VA", "_id": "22018"} -{"city": "CATLETT", "loc": [-77.653548, 38.635067], "pop": 4180, "state": "VA", "_id": "22019"} -{"city": "CENTREVILLE", "loc": [-77.451175, 38.841805], "pop": 33039, "state": "VA", "_id": "22020"} -{"city": "CHANTILLY", "loc": [-77.435436, 38.884131], "pop": 14835, "state": "VA", "_id": "22021"} -{"city": "CLIFTON", "loc": [-77.406955, 38.800662], "pop": 9554, "state": "VA", "_id": "22024"} -{"city": "DELAPLANE", "loc": [-77.930176, 38.911293], "pop": 916, "state": "VA", "_id": "22025"} -{"city": "DUMFRIES", "loc": [-77.338704, 38.602344], "pop": 17563, "state": "VA", "_id": "22026"} -{"city": "DUNN LORING", "loc": [-77.221351, 38.895368], "pop": 1084, "state": "VA", "_id": "22027"} -{"city": "FAIRFAX", "loc": [-77.324151, 38.845826], "pop": 32754, "state": "VA", "_id": "22030"} -{"city": "FAIRFAX", "loc": [-77.264937, 38.860353], "pop": 22035, "state": "VA", "_id": "22031"} -{"city": "FAIRFAX", "loc": [-77.292527, 38.817729], "pop": 29971, "state": "VA", "_id": "22032"} -{"city": "FAIRFAX", "loc": [-77.388451, 38.877627], "pop": 20873, "state": "VA", "_id": "22033"} -{"city": "FAIRFAX STATION", "loc": [-77.306388, 38.760205], "pop": 14491, "state": "VA", "_id": "22039"} -{"city": "BAILEYS CROSSROA", "loc": [-77.136928, 38.848506], "pop": 21801, "state": "VA", "_id": "22041"} -{"city": "MOSBY", "loc": [-77.192271, 38.866272], "pop": 28441, "state": "VA", "_id": "22042"} -{"city": "PIMMIT", "loc": [-77.20005, 38.901226], "pop": 20928, "state": "VA", "_id": "22043"} -{"city": "SEVEN CORNERS", "loc": [-77.150819, 38.863544], "pop": 10100, "state": "VA", "_id": "22044"} -{"city": "FALLS CHURCH", "loc": [-77.180231, 38.88559], "pop": 14864, "state": "VA", "_id": "22046"} -{"city": "FORT BELVOIR", "loc": [-77.143315, 38.694699], "pop": 6013, "state": "VA", "_id": "22060"} -{"city": "GAINESVILLE", "loc": [-77.618673, 38.823192], "pop": 3929, "state": "VA", "_id": "22065"} -{"city": "GREAT FALLS", "loc": [-77.308287, 39.003893], "pop": 13542, "state": "VA", "_id": "22066"} -{"city": "HAMILTON", "loc": [-77.663538, 39.135741], "pop": 2821, "state": "VA", "_id": "22068"} -{"city": "HAYMARKET", "loc": [-77.647485, 38.876234], "pop": 5103, "state": "VA", "_id": "22069"} -{"city": "HERNDON", "loc": [-77.386573, 38.977833], "pop": 30669, "state": "VA", "_id": "22070"} -{"city": "HERNDON", "loc": [-77.395155, 38.919537], "pop": 21490, "state": "VA", "_id": "22071"} -{"city": "LEESBURG", "loc": [-77.565918, 39.116517], "pop": 21011, "state": "VA", "_id": "22075"} -{"city": "MASON NECK", "loc": [-77.218004, 38.704716], "pop": 17438, "state": "VA", "_id": "22079"} -{"city": "LOVETTSVILLE", "loc": [-77.639144, 39.265367], "pop": 3631, "state": "VA", "_id": "22080"} -{"city": "LAKE ANNE", "loc": [-77.337607, 38.964439], "pop": 12710, "state": "VA", "_id": "22090"} -{"city": "RESTON", "loc": [-77.35144, 38.935007], "pop": 28381, "state": "VA", "_id": "22091"} -{"city": "RESTON", "loc": [-77.350757, 38.975627], "pop": 5491, "state": "VA", "_id": "22094"} -{"city": "MC LEAN", "loc": [-77.170628, 38.932624], "pop": 27236, "state": "VA", "_id": "22101"} -{"city": "WEST MCLEAN", "loc": [-77.221934, 38.936318], "pop": 15809, "state": "VA", "_id": "22102"} -{"city": "MANASSAS", "loc": [-77.489474, 38.768922], "pop": 50680, "state": "VA", "_id": "22110"} -{"city": "MANASSAS PARK", "loc": [-77.44568, 38.736143], "pop": 32739, "state": "VA", "_id": "22111"} -{"city": "MARSHALL", "loc": [-77.887567, 38.831584], "pop": 5195, "state": "VA", "_id": "22115"} -{"city": "MIDDLEBURG", "loc": [-77.735511, 38.996438], "pop": 2908, "state": "VA", "_id": "22117"} -{"city": "NOKESVILLE", "loc": [-77.534249, 38.685015], "pop": 6729, "state": "VA", "_id": "22123"} -{"city": "OAKTON", "loc": [-77.323345, 38.885187], "pop": 13019, "state": "VA", "_id": "22124"} -{"city": "PAEONIAN SPRINGS", "loc": [-77.619845, 39.159792], "pop": 488, "state": "VA", "_id": "22129"} -{"city": "PARIS", "loc": [-77.893855, 38.989926], "pop": 317, "state": "VA", "_id": "22130"} -{"city": "HILLSBORO", "loc": [-77.721305, 39.150484], "pop": 5373, "state": "VA", "_id": "22132"} -{"city": "QUANTICO", "loc": [-77.335819, 38.531039], "pop": 9039, "state": "VA", "_id": "22134"} -{"city": "ROUND HILL", "loc": [-77.783305, 39.113378], "pop": 1926, "state": "VA", "_id": "22141"} -{"city": "SPRINGFIELD", "loc": [-77.186582, 38.779718], "pop": 16811, "state": "VA", "_id": "22150"} -{"city": "NORTH SPRINGFIEL", "loc": [-77.213908, 38.803323], "pop": 15979, "state": "VA", "_id": "22151"} -{"city": "WEST SPRINGFIELD", "loc": [-77.233243, 38.776488], "pop": 26803, "state": "VA", "_id": "22152"} -{"city": "SPRINGFIELD", "loc": [-77.237026, 38.744859], "pop": 32161, "state": "VA", "_id": "22153"} -{"city": "STERLING", "loc": [-77.398624, 39.02147], "pop": 38891, "state": "VA", "_id": "22170"} -{"city": "THE PLAINS", "loc": [-77.772092, 38.87393], "pop": 2071, "state": "VA", "_id": "22171"} -{"city": "TRIANGLE", "loc": [-77.322877, 38.550894], "pop": 5181, "state": "VA", "_id": "22172"} -{"city": "UPPERVILLE", "loc": [-77.848438, 39.008747], "pop": 755, "state": "VA", "_id": "22176"} -{"city": "VIENNA", "loc": [-77.253219, 38.893527], "pop": 20795, "state": "VA", "_id": "22180"} -{"city": "VIENNA", "loc": [-77.288048, 38.897695], "pop": 12462, "state": "VA", "_id": "22181"} -{"city": "VIENNA", "loc": [-77.264876, 38.928005], "pop": 19121, "state": "VA", "_id": "22182"} -{"city": "AIRLIE", "loc": [-77.764809, 38.72593], "pop": 24248, "state": "VA", "_id": "22186"} -{"city": "WATERFORD", "loc": [-77.642134, 39.208102], "pop": 264, "state": "VA", "_id": "22190"} -{"city": "WOODBRIDGE", "loc": [-77.268264, 38.635573], "pop": 30657, "state": "VA", "_id": "22191"} -{"city": "LAKERIDGE", "loc": [-77.305221, 38.68328], "pop": 38960, "state": "VA", "_id": "22192"} -{"city": "DALE CITY", "loc": [-77.343238, 38.647188], "pop": 47213, "state": "VA", "_id": "22193"} -{"city": "ARLINGTON", "loc": [-77.093197, 38.887103], "pop": 22034, "state": "VA", "_id": "22201"} -{"city": "ARLINGTON", "loc": [-77.059228, 38.856547], "pop": 17453, "state": "VA", "_id": "22202"} -{"city": "ARLINGTON", "loc": [-77.114191, 38.873799], "pop": 14553, "state": "VA", "_id": "22203"} -{"city": "ARLINGTON", "loc": [-77.099688, 38.858962], "pop": 41844, "state": "VA", "_id": "22204"} -{"city": "ARLINGTON", "loc": [-77.139488, 38.883557], "pop": 15285, "state": "VA", "_id": "22205"} -{"city": "ARLINGTON", "loc": [-77.09046, 38.841508], "pop": 17628, "state": "VA", "_id": "22206"} -{"city": "ARLINGTON", "loc": [-77.126287, 38.903321], "pop": 27999, "state": "VA", "_id": "22207"} -{"city": "ARLINGTON", "loc": [-77.07531, 38.8926], "pop": 10118, "state": "VA", "_id": "22209"} -{"city": "ARLINGTON", "loc": [-77.080258, 38.8795], "pop": 2432, "state": "VA", "_id": "22211"} -{"city": "ARLINGTON", "loc": [-77.163295, 38.895375], "pop": 2095, "state": "VA", "_id": "22213"} -{"city": "ALEXANDRIA", "loc": [-77.058901, 38.820042], "pop": 12109, "state": "VA", "_id": "22301"} -{"city": "ALEXANDRIA", "loc": [-77.092412, 38.83354], "pop": 17021, "state": "VA", "_id": "22302"} -{"city": "JEFFERSON MANOR", "loc": [-77.076608, 38.791143], "pop": 12662, "state": "VA", "_id": "22303"} -{"city": "ALEXANDRIA", "loc": [-77.120989, 38.814871], "pop": 33747, "state": "VA", "_id": "22304"} -{"city": "ALEXANDRIA", "loc": [-77.064039, 38.837184], "pop": 13514, "state": "VA", "_id": "22305"} -{"city": "COMMUNITY", "loc": [-77.085389, 38.755769], "pop": 24374, "state": "VA", "_id": "22306"} -{"city": "BELLE VIEW", "loc": [-77.062511, 38.77056], "pop": 9821, "state": "VA", "_id": "22307"} -{"city": "WELLINGTON", "loc": [-77.060639, 38.729122], "pop": 12023, "state": "VA", "_id": "22308"} -{"city": "ENGLESIDE", "loc": [-77.108139, 38.727855], "pop": 27479, "state": "VA", "_id": "22309"} -{"city": "FRANCONIA", "loc": [-77.131707, 38.769132], "pop": 39438, "state": "VA", "_id": "22310"} -{"city": "ALEXANDRIA", "loc": [-77.119962, 38.832039], "pop": 11388, "state": "VA", "_id": "22311"} -{"city": "ALEXANDRIA", "loc": [-77.148438, 38.819099], "pop": 23848, "state": "VA", "_id": "22312"} -{"city": "ALEXANDRIA", "loc": [-77.052867, 38.806018], "pop": 20606, "state": "VA", "_id": "22314"} -{"city": "FREDERICKSBURG", "loc": [-77.477152, 38.299538], "pop": 19027, "state": "VA", "_id": "22401"} -{"city": "FALMOUTH", "loc": [-77.404537, 38.314557], "pop": 20039, "state": "VA", "_id": "22405"} -{"city": "FREDERICKSBURG", "loc": [-77.534892, 38.379627], "pop": 7583, "state": "VA", "_id": "22406"} -{"city": "FREDERICKSBURG", "loc": [-77.547584, 38.268803], "pop": 27852, "state": "VA", "_id": "22407"} -{"city": "FREDERICKSBURG", "loc": [-77.468068, 38.248141], "pop": 9433, "state": "VA", "_id": "22408"} -{"city": "BOWLING GREEN", "loc": [-77.180242, 38.013651], "pop": 559, "state": "VA", "_id": "22427"} -{"city": "BURGESS", "loc": [-76.354164, 37.868682], "pop": 478, "state": "VA", "_id": "22432"} -{"city": "BURR HILL", "loc": [-77.876166, 38.365841], "pop": 121, "state": "VA", "_id": "22433"} -{"city": "CALLAO", "loc": [-76.573245, 37.977268], "pop": 1808, "state": "VA", "_id": "22435"} -{"city": "CARET", "loc": [-76.915254, 37.971255], "pop": 675, "state": "VA", "_id": "22436"} -{"city": "CENTER CROSS", "loc": [-76.754837, 37.792875], "pop": 408, "state": "VA", "_id": "22437"} -{"city": "CHAMPLAIN", "loc": [-76.971942, 38.021422], "pop": 187, "state": "VA", "_id": "22438"} -{"city": "CHANCE", "loc": [-77.021051, 38.084061], "pop": 45, "state": "VA", "_id": "22439"} -{"city": "OAK GROVE", "loc": [-76.981158, 38.229076], "pop": 6864, "state": "VA", "_id": "22443"} -{"city": "DAHLGREN", "loc": [-77.042905, 38.337485], "pop": 1509, "state": "VA", "_id": "22448"} -{"city": "HOWERTONS", "loc": [-76.837022, 37.840429], "pop": 1795, "state": "VA", "_id": "22454"} -{"city": "FARNHAM", "loc": [-76.605011, 37.873972], "pop": 1356, "state": "VA", "_id": "22460"} -{"city": "HAGUE", "loc": [-76.661574, 38.057289], "pop": 2243, "state": "VA", "_id": "22469"} -{"city": "HEATHSVILLE", "loc": [-76.41783, 37.907187], "pop": 2670, "state": "VA", "_id": "22473"} -{"city": "HUSTLE", "loc": [-77.036426, 38.022219], "pop": 782, "state": "VA", "_id": "22476"} -{"city": "IRVINGTON", "loc": [-76.416016, 37.664516], "pop": 717, "state": "VA", "_id": "22480"} -{"city": "KILMARNOCK", "loc": [-76.381986, 37.716222], "pop": 1436, "state": "VA", "_id": "22482"} -{"city": "KING GEORGE", "loc": [-77.126023, 38.281142], "pop": 9088, "state": "VA", "_id": "22485"} -{"city": "KINSALE", "loc": [-76.585525, 38.050472], "pop": 1537, "state": "VA", "_id": "22488"} -{"city": "LANCASTER", "loc": [-76.5102, 37.750091], "pop": 4140, "state": "VA", "_id": "22503"} -{"city": "LANEVIEW", "loc": [-76.711732, 37.768138], "pop": 179, "state": "VA", "_id": "22504"} -{"city": "LOCUST GROVE", "loc": [-77.770874, 38.335202], "pop": 4357, "state": "VA", "_id": "22508"} -{"city": "LORETTO", "loc": [-76.966888, 38.062055], "pop": 38, "state": "VA", "_id": "22509"} -{"city": "LOTTSBURG", "loc": [-76.501763, 37.979228], "pop": 1250, "state": "VA", "_id": "22511"} -{"city": "MILFORD", "loc": [-77.318498, 38.005834], "pop": 3385, "state": "VA", "_id": "22514"} -{"city": "MONTROSS", "loc": [-76.782806, 38.110483], "pop": 4836, "state": "VA", "_id": "22520"} -{"city": "PARTLOW", "loc": [-77.658574, 38.064146], "pop": 2057, "state": "VA", "_id": "22534"} -{"city": "PORT ROYAL", "loc": [-77.183657, 38.162088], "pop": 421, "state": "VA", "_id": "22535"} -{"city": "RAPPAHANNOCK ACA", "loc": [-77.287437, 38.201817], "pop": 637, "state": "VA", "_id": "22538"} -{"city": "REEDVILLE", "loc": [-76.282864, 37.856964], "pop": 2037, "state": "VA", "_id": "22539"} -{"city": "RHOADESVILLE", "loc": [-77.923044, 38.286324], "pop": 1072, "state": "VA", "_id": "22542"} -{"city": "RUTHER GLEN", "loc": [-77.47135, 37.945059], "pop": 7242, "state": "VA", "_id": "22546"} -{"city": "SNELL", "loc": [-77.648688, 38.19953], "pop": 17034, "state": "VA", "_id": "22553"} -{"city": "STAFFORD", "loc": [-77.430618, 38.458629], "pop": 36544, "state": "VA", "_id": "22554"} -{"city": "SUPPLY", "loc": [-77.109689, 38.082544], "pop": 387, "state": "VA", "_id": "22559"} -{"city": "TAPPAHANNOCK", "loc": [-76.912455, 37.914551], "pop": 4270, "state": "VA", "_id": "22560"} -{"city": "UNIONVILLE", "loc": [-77.919299, 38.238302], "pop": 1754, "state": "VA", "_id": "22567"} -{"city": "MINE RUN", "loc": [-77.819493, 38.266106], "pop": 314, "state": "VA", "_id": "22568"} -{"city": "NOMINI GROVE", "loc": [-76.736517, 37.949992], "pop": 5917, "state": "VA", "_id": "22572"} -{"city": "WEEMS", "loc": [-76.431323, 37.684678], "pop": 2125, "state": "VA", "_id": "22576"} -{"city": "WINDMILL POINT", "loc": [-76.368626, 37.647458], "pop": 2474, "state": "VA", "_id": "22578"} -{"city": "WICOMICO CHURCH", "loc": [-76.356522, 37.779027], "pop": 2285, "state": "VA", "_id": "22579"} -{"city": "WOODFORD", "loc": [-77.439879, 38.084664], "pop": 6047, "state": "VA", "_id": "22580"} -{"city": "WINCHESTER", "loc": [-78.182697, 39.185803], "pop": 45537, "state": "VA", "_id": "22601"} -{"city": "BROWNTOWN", "loc": [-78.279111, 38.827272], "pop": 1522, "state": "VA", "_id": "22610"} -{"city": "BERRYVILLE", "loc": [-77.968759, 39.153178], "pop": 8339, "state": "VA", "_id": "22611"} -{"city": "BOYCE", "loc": [-78.020302, 39.069797], "pop": 2984, "state": "VA", "_id": "22620"} -{"city": "CLEAR BROOK", "loc": [-78.098788, 39.265461], "pop": 2656, "state": "VA", "_id": "22624"} -{"city": "WHITACRE", "loc": [-78.299724, 39.3762], "pop": 1229, "state": "VA", "_id": "22625"} -{"city": "FLINT HILL", "loc": [-78.075558, 38.733653], "pop": 276, "state": "VA", "_id": "22627"} -{"city": "FRONT ROYAL", "loc": [-78.179617, 38.926783], "pop": 22951, "state": "VA", "_id": "22630"} -{"city": "GORE", "loc": [-78.341687, 39.288685], "pop": 2000, "state": "VA", "_id": "22637"} -{"city": "HUME", "loc": [-77.994892, 38.827318], "pop": 517, "state": "VA", "_id": "22639"} -{"city": "HUNTLY", "loc": [-78.130104, 38.813333], "pop": 782, "state": "VA", "_id": "22640"} -{"city": "LEBANON CHURCH", "loc": [-78.359511, 39.056242], "pop": 1075, "state": "VA", "_id": "22641"} -{"city": "LINDEN", "loc": [-78.047847, 38.881181], "pop": 352, "state": "VA", "_id": "22642"} -{"city": "MARKHAM", "loc": [-77.994252, 38.930001], "pop": 429, "state": "VA", "_id": "22643"} -{"city": "MAURERTOWN", "loc": [-78.465815, 38.944052], "pop": 1483, "state": "VA", "_id": "22644"} -{"city": "MIDDLETOWN", "loc": [-78.281455, 39.036676], "pop": 2239, "state": "VA", "_id": "22645"} -{"city": "RELIANCE", "loc": [-78.249152, 39.000946], "pop": 145, "state": "VA", "_id": "22649"} -{"city": "RILEYVILLE", "loc": [-78.387326, 38.756374], "pop": 997, "state": "VA", "_id": "22650"} -{"city": "SAINT DAVIDS CHU", "loc": [-78.439989, 38.827683], "pop": 1004, "state": "VA", "_id": "22652"} -{"city": "STAR TANNERY", "loc": [-78.434306, 39.090803], "pop": 332, "state": "VA", "_id": "22654"} -{"city": "STEPHENS CITY", "loc": [-78.190677, 39.083415], "pop": 10954, "state": "VA", "_id": "22655"} -{"city": "STEPHENSON", "loc": [-78.094105, 39.197285], "pop": 2721, "state": "VA", "_id": "22656"} -{"city": "STRASBURG", "loc": [-78.33862, 38.988417], "pop": 5756, "state": "VA", "_id": "22657"} -{"city": "TOMS BROOK", "loc": [-78.403757, 38.97016], "pop": 2796, "state": "VA", "_id": "22660"} -{"city": "WHITE POST", "loc": [-78.11046, 39.057956], "pop": 778, "state": "VA", "_id": "22663"} -{"city": "WOODSTOCK", "loc": [-78.521704, 38.887024], "pop": 6899, "state": "VA", "_id": "22664"} -{"city": "RACCOON FORD", "loc": [-78.00406, 38.48115], "pop": 21359, "state": "VA", "_id": "22701"} -{"city": "ARODA", "loc": [-78.236615, 38.325626], "pop": 1509, "state": "VA", "_id": "22709"} -{"city": "MORRISVILLE", "loc": [-77.800436, 38.569667], "pop": 3863, "state": "VA", "_id": "22712"} -{"city": "BOSTON", "loc": [-78.142285, 38.538241], "pop": 557, "state": "VA", "_id": "22713"} -{"city": "BRANDY STATION", "loc": [-77.903745, 38.511003], "pop": 721, "state": "VA", "_id": "22714"} -{"city": "BRIGHTWOOD", "loc": [-78.169801, 38.411365], "pop": 1048, "state": "VA", "_id": "22715"} -{"city": "CASTLETON", "loc": [-78.120801, 38.603235], "pop": 536, "state": "VA", "_id": "22716"} -{"city": "ELKWOOD", "loc": [-77.817031, 38.465229], "pop": 414, "state": "VA", "_id": "22718"} -{"city": "ETLAN", "loc": [-78.263793, 38.509458], "pop": 167, "state": "VA", "_id": "22719"} -{"city": "GOLDVEIN", "loc": [-77.639488, 38.488641], "pop": 1154, "state": "VA", "_id": "22720"} -{"city": "HAYWOOD", "loc": [-78.179759, 38.46861], "pop": 139, "state": "VA", "_id": "22722"} -{"city": "JEFFERSONTON", "loc": [-77.906943, 38.625578], "pop": 1000, "state": "VA", "_id": "22724"} -{"city": "LEON", "loc": [-78.154579, 38.45724], "pop": 109, "state": "VA", "_id": "22725"} -{"city": "LIGNUM", "loc": [-77.829957, 38.395625], "pop": 176, "state": "VA", "_id": "22726"} -{"city": "AYLOR", "loc": [-78.296458, 38.384495], "pop": 4344, "state": "VA", "_id": "22727"} -{"city": "MIDLAND", "loc": [-77.712709, 38.567061], "pop": 1847, "state": "VA", "_id": "22728"} -{"city": "MITCHELLS", "loc": [-78.010546, 38.374429], "pop": 39, "state": "VA", "_id": "22729"} -{"city": "PRATTS", "loc": [-78.272212, 38.349207], "pop": 88, "state": "VA", "_id": "22731"} -{"city": "RADIANT", "loc": [-78.179799, 38.322706], "pop": 756, "state": "VA", "_id": "22732"} -{"city": "RAPIDAN", "loc": [-78.047607, 38.339217], "pop": 1216, "state": "VA", "_id": "22733"} -{"city": "REMINGTON", "loc": [-77.761365, 38.535091], "pop": 2377, "state": "VA", "_id": "22734"} -{"city": "REVA", "loc": [-78.157195, 38.460446], "pop": 2134, "state": "VA", "_id": "22735"} -{"city": "RICHARDSVILLE", "loc": [-77.719519, 38.392388], "pop": 238, "state": "VA", "_id": "22736"} -{"city": "RIXEYVILLE", "loc": [-78.028229, 38.585168], "pop": 2540, "state": "VA", "_id": "22737"} -{"city": "UNO", "loc": [-78.265358, 38.274342], "pop": 477, "state": "VA", "_id": "22738"} -{"city": "SPERRYVILLE", "loc": [-78.246571, 38.620252], "pop": 1553, "state": "VA", "_id": "22740"} -{"city": "STEVENSBURG", "loc": [-77.88423, 38.444135], "pop": 220, "state": "VA", "_id": "22741"} -{"city": "SUMERDUCK", "loc": [-77.70436, 38.467355], "pop": 1146, "state": "VA", "_id": "22742"} -{"city": "SYRIA", "loc": [-78.322939, 38.497229], "pop": 526, "state": "VA", "_id": "22743"} -{"city": "VIEWTOWN", "loc": [-78.025769, 38.645704], "pop": 172, "state": "VA", "_id": "22746"} -{"city": "WASHINGTON", "loc": [-78.1566, 38.707838], "pop": 1448, "state": "VA", "_id": "22747"} -{"city": "WOODVILLE", "loc": [-78.173855, 38.648176], "pop": 371, "state": "VA", "_id": "22749"} -{"city": "HARRISONBURG", "loc": [-78.871438, 38.448864], "pop": 42027, "state": "VA", "_id": "22801"} -{"city": "BASYE", "loc": [-78.767363, 38.795806], "pop": 43, "state": "VA", "_id": "22810"} -{"city": "BERGTON", "loc": [-78.966752, 38.792509], "pop": 341, "state": "VA", "_id": "22811"} -{"city": "BRIDGEWATER", "loc": [-78.993684, 38.385866], "pop": 7160, "state": "VA", "_id": "22812"} -{"city": "BROADWAY", "loc": [-78.787494, 38.608336], "pop": 7106, "state": "VA", "_id": "22815"} -{"city": "CRIDERS", "loc": [-78.997357, 38.749656], "pop": 445, "state": "VA", "_id": "22820"} -{"city": "MONTEZUMA", "loc": [-78.993731, 38.433864], "pop": 4777, "state": "VA", "_id": "22821"} -{"city": "EDINBURG", "loc": [-78.60033, 38.843206], "pop": 5079, "state": "VA", "_id": "22824"} -{"city": "ELKTON", "loc": [-78.632143, 38.40252], "pop": 10331, "state": "VA", "_id": "22827"} -{"city": "FULKS RUN", "loc": [-78.935799, 38.633871], "pop": 1255, "state": "VA", "_id": "22830"} -{"city": "HINTON", "loc": [-79.010084, 38.483378], "pop": 798, "state": "VA", "_id": "22831"} -{"city": "KEEZLETOWN", "loc": [-78.776998, 38.439588], "pop": 1204, "state": "VA", "_id": "22832"} -{"city": "LINVILLE", "loc": [-78.896102, 38.555733], "pop": 1866, "state": "VA", "_id": "22834"} -{"city": "LURAY", "loc": [-78.459559, 38.654839], "pop": 10559, "state": "VA", "_id": "22835"} -{"city": "MC GAHEYSVILLE", "loc": [-78.741128, 38.371228], "pop": 2100, "state": "VA", "_id": "22840"} -{"city": "MOUNT CRAWFORD", "loc": [-78.895679, 38.345656], "pop": 2070, "state": "VA", "_id": "22841"} -{"city": "CONICVILLE", "loc": [-78.676655, 38.768819], "pop": 4601, "state": "VA", "_id": "22842"} -{"city": "MOUNT SOLON", "loc": [-79.102832, 38.332755], "pop": 1995, "state": "VA", "_id": "22843"} -{"city": "NEW MARKET", "loc": [-78.671433, 38.660555], "pop": 3502, "state": "VA", "_id": "22844"} -{"city": "ORKNEY SPRINGS", "loc": [-78.811106, 38.793708], "pop": 266, "state": "VA", "_id": "22845"} -{"city": "MONTEVIDEO", "loc": [-78.797903, 38.369729], "pop": 704, "state": "VA", "_id": "22846"} -{"city": "SHENANDOAH CAVER", "loc": [-78.698866, 38.714443], "pop": 352, "state": "VA", "_id": "22847"} -{"city": "SHENANDOAH", "loc": [-78.608975, 38.501034], "pop": 5174, "state": "VA", "_id": "22849"} -{"city": "STANLEY", "loc": [-78.509263, 38.566036], "pop": 4604, "state": "VA", "_id": "22851"} -{"city": "TIMBERVILLE", "loc": [-78.771702, 38.647606], "pop": 3494, "state": "VA", "_id": "22853"} -{"city": "CHARLOTTESVILLE", "loc": [-78.490869, 38.054752], "pop": 62708, "state": "VA", "_id": "22901"} -{"city": "UNIVERSITY", "loc": [-78.505758, 38.032728], "pop": 25301, "state": "VA", "_id": "22903"} -{"city": "AFTON", "loc": [-78.841046, 37.962624], "pop": 2783, "state": "VA", "_id": "22920"} -{"city": "TYE RIVER", "loc": [-78.918473, 37.687713], "pop": 1439, "state": "VA", "_id": "22922"} -{"city": "BURNLEYS", "loc": [-78.312716, 38.199648], "pop": 1389, "state": "VA", "_id": "22923"} -{"city": "COBHAM", "loc": [-78.37849, 38.037835], "pop": 45, "state": "VA", "_id": "22929"} -{"city": "COVESVILLE", "loc": [-78.712741, 37.907974], "pop": 304, "state": "VA", "_id": "22931"} -{"city": "YANCEY MILLS", "loc": [-78.704405, 38.081288], "pop": 4292, "state": "VA", "_id": "22932"} -{"city": "BOONESVILLE", "loc": [-78.554612, 38.253276], "pop": 566, "state": "VA", "_id": "22935"} -{"city": "EARLYSVILLE", "loc": [-78.491945, 38.157761], "pop": 4294, "state": "VA", "_id": "22936"} -{"city": "ESMONT", "loc": [-78.610646, 37.812543], "pop": 1157, "state": "VA", "_id": "22937"} -{"city": "FABER", "loc": [-78.756495, 37.847545], "pop": 958, "state": "VA", "_id": "22938"} -{"city": "WOODROW WILSON", "loc": [-78.969579, 38.097361], "pop": 1374, "state": "VA", "_id": "22939"} -{"city": "MISSION HOME", "loc": [-78.595906, 38.206217], "pop": 1023, "state": "VA", "_id": "22940"} -{"city": "CASHS CORNER", "loc": [-78.195195, 38.107261], "pop": 5852, "state": "VA", "_id": "22942"} -{"city": "GREENWOOD", "loc": [-78.783282, 38.041545], "pop": 516, "state": "VA", "_id": "22943"} -{"city": "KEENE", "loc": [-78.561386, 37.931273], "pop": 26, "state": "VA", "_id": "22946"} -{"city": "BOYD TAVERN", "loc": [-78.340429, 38.001265], "pop": 2081, "state": "VA", "_id": "22947"} -{"city": "LOCUST DALE", "loc": [-78.101576, 38.334043], "pop": 155, "state": "VA", "_id": "22948"} -{"city": "LOVINGSTON", "loc": [-78.868433, 37.792405], "pop": 1459, "state": "VA", "_id": "22949"} -{"city": "LOWESVILLE", "loc": [-79.083346, 37.749797], "pop": 175, "state": "VA", "_id": "22951"} -{"city": "SHERANDO", "loc": [-78.951506, 37.997903], "pop": 2267, "state": "VA", "_id": "22952"} -{"city": "WINTERGREEN", "loc": [-78.893972, 37.860398], "pop": 53, "state": "VA", "_id": "22958"} -{"city": "ALBERENE", "loc": [-78.695414, 37.961992], "pop": 3134, "state": "VA", "_id": "22959"} -{"city": "MONTFORD", "loc": [-78.091224, 38.233937], "pop": 8781, "state": "VA", "_id": "22960"} -{"city": "BYBEE", "loc": [-78.297983, 37.881807], "pop": 5689, "state": "VA", "_id": "22963"} -{"city": "PINEY RIVER", "loc": [-79.020571, 37.715785], "pop": 257, "state": "VA", "_id": "22964"} -{"city": "ROSELAND", "loc": [-78.971173, 37.807697], "pop": 2333, "state": "VA", "_id": "22967"} -{"city": "ADVANCE MILLS", "loc": [-78.391033, 38.233871], "pop": 5764, "state": "VA", "_id": "22968"} -{"city": "SCHUYLER", "loc": [-78.692475, 37.797584], "pop": 1128, "state": "VA", "_id": "22969"} -{"city": "ROCKFISH", "loc": [-78.824608, 37.747974], "pop": 1100, "state": "VA", "_id": "22971"} -{"city": "SOMERSET", "loc": [-78.25042, 38.204657], "pop": 720, "state": "VA", "_id": "22972"} -{"city": "GEER", "loc": [-78.470755, 38.302556], "pop": 4114, "state": "VA", "_id": "22973"} -{"city": "TROY", "loc": [-78.253494, 37.9636], "pop": 1868, "state": "VA", "_id": "22974"} -{"city": "WAYNESBORO", "loc": [-78.903461, 38.077412], "pop": 26450, "state": "VA", "_id": "22980"} -{"city": "AMELIA COURT HOU", "loc": [-77.955177, 37.35017], "pop": 6923, "state": "VA", "_id": "23002"} -{"city": "ARVONIA", "loc": [-78.38888, 37.671355], "pop": 1114, "state": "VA", "_id": "23004"} -{"city": "ASHLAND", "loc": [-77.478373, 37.73948], "pop": 14800, "state": "VA", "_id": "23005"} -{"city": "AYLETT", "loc": [-77.188473, 37.822113], "pop": 1374, "state": "VA", "_id": "23009"} -{"city": "BARHAMSVILLE", "loc": [-76.832097, 37.461662], "pop": 698, "state": "VA", "_id": "23011"} -{"city": "23013", "loc": [-76.279433, 37.331428], "pop": 103, "state": "VA", "_id": "23013"} -{"city": "BEAVERDAM", "loc": [-77.630829, 37.903767], "pop": 2328, "state": "VA", "_id": "23015"} -{"city": "BEAVERLETT", "loc": [-76.322425, 37.41587], "pop": 262, "state": "VA", "_id": "23016"} -{"city": "23020", "loc": [-76.363655, 37.502082], "pop": 724, "state": "VA", "_id": "23020"} -{"city": "BOHANNON", "loc": [-76.36136, 37.390457], "pop": 143, "state": "VA", "_id": "23021"} -{"city": "BREMO BLUFF", "loc": [-78.267158, 37.745256], "pop": 1058, "state": "VA", "_id": "23022"} -{"city": "BRUINGTON", "loc": [-76.940199, 37.780112], "pop": 172, "state": "VA", "_id": "23023"} -{"city": "BUMPASS", "loc": [-77.796614, 37.898381], "pop": 4399, "state": "VA", "_id": "23024"} -{"city": "MILES", "loc": [-76.393959, 37.427339], "pop": 39, "state": "VA", "_id": "23025"} -{"city": "TAMWORTH", "loc": [-78.128699, 37.641314], "pop": 1143, "state": "VA", "_id": "23027"} -{"city": "CAUTHORNVILLE", "loc": [-77.039625, 37.858957], "pop": 380, "state": "VA", "_id": "23029"} -{"city": "CHARLES CITY", "loc": [-77.108183, 37.366271], "pop": 4216, "state": "VA", "_id": "23030"} -{"city": "CHURCH VIEW", "loc": [-76.66287, 37.674956], "pop": 493, "state": "VA", "_id": "23032"} -{"city": "COLOGNE", "loc": [-76.683356, 37.555866], "pop": 95, "state": "VA", "_id": "23037"} -{"city": "COLUMBIA", "loc": [-78.179403, 37.714707], "pop": 641, "state": "VA", "_id": "23038"} -{"city": "CROZIER", "loc": [-77.793958, 37.671764], "pop": 937, "state": "VA", "_id": "23039"} -{"city": "CUMBERLAND", "loc": [-78.258149, 37.501861], "pop": 3429, "state": "VA", "_id": "23040"} -{"city": "23042", "loc": [-77.812016, 37.751953], "pop": 207, "state": "VA", "_id": "23042"} -{"city": "DELTAVILLE", "loc": [-76.346781, 37.554852], "pop": 1565, "state": "VA", "_id": "23043"} -{"city": "DIGGS", "loc": [-76.287172, 37.436273], "pop": 491, "state": "VA", "_id": "23045"} -{"city": "DOSWELL", "loc": [-77.512521, 37.841682], "pop": 2111, "state": "VA", "_id": "23047"} -{"city": "DUTTON", "loc": [-76.435782, 37.486897], "pop": 416, "state": "VA", "_id": "23050"} -{"city": "FORK UNION", "loc": [-78.235498, 37.771526], "pop": 620, "state": "VA", "_id": "23055"} -{"city": "GLEN ALLEN", "loc": [-77.534016, 37.662848], "pop": 19953, "state": "VA", "_id": "23060"} -{"city": "PINERO", "loc": [-76.553312, 37.41922], "pop": 14924, "state": "VA", "_id": "23061"} -{"city": "GLOUCESTER POINT", "loc": [-76.49586, 37.258534], "pop": 2558, "state": "VA", "_id": "23062"} -{"city": "GOOCHLAND", "loc": [-78.011216, 37.768789], "pop": 4772, "state": "VA", "_id": "23063"} -{"city": "GUM SPRING", "loc": [-77.907049, 37.818374], "pop": 480, "state": "VA", "_id": "23065"} -{"city": "GWYNN", "loc": [-76.29042, 37.500594], "pop": 681, "state": "VA", "_id": "23066"} -{"city": "HANOVER", "loc": [-77.321572, 37.770116], "pop": 3796, "state": "VA", "_id": "23069"} -{"city": "HARDYVILLE", "loc": [-76.418937, 37.547821], "pop": 546, "state": "VA", "_id": "23070"} -{"city": "HARTFIELD", "loc": [-76.476949, 37.559437], "pop": 797, "state": "VA", "_id": "23071"} -{"city": "HAYES", "loc": [-76.490457, 37.291577], "pop": 11051, "state": "VA", "_id": "23072"} -{"city": "HIGHLAND SPRINGS", "loc": [-77.32261, 37.543723], "pop": 9373, "state": "VA", "_id": "23075"} -{"city": "JAMAICA", "loc": [-76.688962, 37.729997], "pop": 383, "state": "VA", "_id": "23079"} -{"city": "JAMES STORE", "loc": [-76.464972, 37.480594], "pop": 795, "state": "VA", "_id": "23080"} -{"city": "JETERSVILLE", "loc": [-78.104011, 37.31757], "pop": 1396, "state": "VA", "_id": "23083"} -{"city": "KENTS STORE", "loc": [-78.120776, 37.89425], "pop": 882, "state": "VA", "_id": "23084"} -{"city": "KING AND QUEEN C", "loc": [-76.863059, 37.717644], "pop": 548, "state": "VA", "_id": "23085"} -{"city": "KING WILLIAM", "loc": [-77.099837, 37.720194], "pop": 3887, "state": "VA", "_id": "23086"} -{"city": "LANEXA", "loc": [-76.902684, 37.419391], "pop": 3609, "state": "VA", "_id": "23089"} -{"city": "LITTLE PLYMOUTH", "loc": [-76.825456, 37.664218], "pop": 103, "state": "VA", "_id": "23091"} -{"city": "LOCUST HILL", "loc": [-76.501857, 37.598328], "pop": 375, "state": "VA", "_id": "23092"} -{"city": "LOUISA", "loc": [-78.034666, 38.01317], "pop": 7503, "state": "VA", "_id": "23093"} -{"city": "DABNEYS", "loc": [-77.871032, 37.703193], "pop": 2927, "state": "VA", "_id": "23102"} -{"city": "MANAKIN SABOT", "loc": [-77.707699, 37.638036], "pop": 5177, "state": "VA", "_id": "23103"} -{"city": "MANQUIN", "loc": [-77.186016, 37.718441], "pop": 628, "state": "VA", "_id": "23106"} -{"city": "MASCOT", "loc": [-76.738324, 37.60683], "pop": 104, "state": "VA", "_id": "23108"} -{"city": "MATHEWS", "loc": [-76.354416, 37.438349], "pop": 760, "state": "VA", "_id": "23109"} -{"city": "SHANGHAI", "loc": [-76.78398, 37.641698], "pop": 331, "state": "VA", "_id": "23110"} -{"city": "MECHANICSVILLE", "loc": [-77.339464, 37.628146], "pop": 35675, "state": "VA", "_id": "23111"} -{"city": "MIDLOTHIAN", "loc": [-77.654458, 37.431027], "pop": 24114, "state": "VA", "_id": "23112"} -{"city": "MIDLOTHIAN", "loc": [-77.642864, 37.510893], "pop": 21858, "state": "VA", "_id": "23113"} -{"city": "23114", "loc": [-76.367526, 37.411851], "pop": 443, "state": "VA", "_id": "23114"} -{"city": "MINERAL", "loc": [-77.878069, 37.998609], "pop": 5080, "state": "VA", "_id": "23117"} -{"city": "MOBJACK", "loc": [-76.355244, 37.377579], "pop": 204, "state": "VA", "_id": "23118"} -{"city": "MOON", "loc": [-76.306472, 37.449276], "pop": 629, "state": "VA", "_id": "23119"} -{"city": "MOSELEY", "loc": [-77.758458, 37.426026], "pop": 1852, "state": "VA", "_id": "23120"} -{"city": "NEW CANTON", "loc": [-78.311231, 37.664499], "pop": 1546, "state": "VA", "_id": "23123"} -{"city": "NEW KENT", "loc": [-77.074242, 37.553013], "pop": 1376, "state": "VA", "_id": "23124"} -{"city": "NEW POINT", "loc": [-76.283465, 37.349291], "pop": 153, "state": "VA", "_id": "23125"} -{"city": "NEWTOWN", "loc": [-77.113825, 37.878236], "pop": 926, "state": "VA", "_id": "23126"} -{"city": "NORTH", "loc": [-76.370661, 37.477066], "pop": 1621, "state": "VA", "_id": "23128"} -{"city": "OILVILLE", "loc": [-77.769955, 37.708879], "pop": 198, "state": "VA", "_id": "23129"} -{"city": "ONEMO", "loc": [-76.294056, 37.398517], "pop": 582, "state": "VA", "_id": "23130"} -{"city": "23137", "loc": [-76.724658, 37.518018], "pop": 1563, "state": "VA", "_id": "23137"} -{"city": "BAVON", "loc": [-76.304463, 37.374252], "pop": 475, "state": "VA", "_id": "23138"} -{"city": "POWHATAN", "loc": [-77.879761, 37.556442], "pop": 14488, "state": "VA", "_id": "23139"} -{"city": "PROVIDENCE FORGE", "loc": [-77.071117, 37.425907], "pop": 3211, "state": "VA", "_id": "23140"} -{"city": "QUINTON", "loc": [-77.148607, 37.518466], "pop": 4589, "state": "VA", "_id": "23141"} -{"city": "ROCKVILLE", "loc": [-77.699994, 37.733729], "pop": 1872, "state": "VA", "_id": "23146"} -{"city": "INDIAN NECK", "loc": [-77.084206, 37.905052], "pop": 191, "state": "VA", "_id": "23148"} -{"city": "SALUDA", "loc": [-76.592082, 37.594944], "pop": 3215, "state": "VA", "_id": "23149"} -{"city": "SANDSTON", "loc": [-77.27578, 37.515658], "pop": 9402, "state": "VA", "_id": "23150"} -{"city": "SANDY HOOK", "loc": [-77.904553, 37.751892], "pop": 152, "state": "VA", "_id": "23153"} -{"city": "PLAIN VIEW", "loc": [-76.726901, 37.569811], "pop": 696, "state": "VA", "_id": "23156"} -{"city": "23157", "loc": [-76.297135, 37.340897], "pop": 313, "state": "VA", "_id": "23157"} -{"city": "STEVENSVILLE", "loc": [-76.935228, 37.714577], "pop": 59, "state": "VA", "_id": "23161"} -{"city": "SHADOW", "loc": [-76.318617, 37.361945], "pop": 309, "state": "VA", "_id": "23163"} -{"city": "TOANO", "loc": [-76.825263, 37.39013], "pop": 2784, "state": "VA", "_id": "23168"} -{"city": "SYRINGA", "loc": [-76.454876, 37.597824], "pop": 706, "state": "VA", "_id": "23169"} -{"city": "REMLIK", "loc": [-76.612611, 37.655003], "pop": 698, "state": "VA", "_id": "23175"} -{"city": "WAKE", "loc": [-76.432327, 37.568077], "pop": 368, "state": "VA", "_id": "23176"} -{"city": "WALKERTON", "loc": [-77.018475, 37.755437], "pop": 1152, "state": "VA", "_id": "23177"} -{"city": "WARNER", "loc": [-76.646167, 37.651127], "pop": 37, "state": "VA", "_id": "23179"} -{"city": "WATER VIEW", "loc": [-76.62268, 37.710486], "pop": 273, "state": "VA", "_id": "23180"} -{"city": "WEST POINT", "loc": [-76.82373, 37.555604], "pop": 4365, "state": "VA", "_id": "23181"} -{"city": "MERRIMAC", "loc": [-76.701686, 37.262698], "pop": 34777, "state": "VA", "_id": "23185"} -{"city": "WILLIAMSBURG", "loc": [-76.763424, 37.317768], "pop": 14957, "state": "VA", "_id": "23188"} -{"city": "MONTPELIER", "loc": [-77.692441, 37.817729], "pop": 4472, "state": "VA", "_id": "23192"} -{"city": "RICHMOND", "loc": [-77.437798, 37.546265], "pop": 2780, "state": "VA", "_id": "23219"} -{"city": "RICHMOND", "loc": [-77.458798, 37.549808], "pop": 29776, "state": "VA", "_id": "23220"} -{"city": "RICHMOND", "loc": [-77.4845, 37.558301], "pop": 14452, "state": "VA", "_id": "23221"} -{"city": "RICHMOND", "loc": [-77.426725, 37.574802], "pop": 30214, "state": "VA", "_id": "23222"} -{"city": "RICHMOND", "loc": [-77.394772, 37.547721], "pop": 42379, "state": "VA", "_id": "23223"} -{"city": "RICHMOND", "loc": [-77.471014, 37.495512], "pop": 32681, "state": "VA", "_id": "23224"} -{"city": "RICHMOND", "loc": [-77.504709, 37.515842], "pop": 37289, "state": "VA", "_id": "23225"} -{"city": "RICHMOND", "loc": [-77.519657, 37.582473], "pop": 18085, "state": "VA", "_id": "23226"} -{"city": "BELLEVUE", "loc": [-77.446309, 37.604181], "pop": 21661, "state": "VA", "_id": "23227"} -{"city": "LAKESIDE", "loc": [-77.493308, 37.623503], "pop": 30611, "state": "VA", "_id": "23228"} -{"city": "REGENCY", "loc": [-77.566202, 37.596351], "pop": 32028, "state": "VA", "_id": "23229"} -{"city": "WEST END", "loc": [-77.496828, 37.588376], "pop": 6447, "state": "VA", "_id": "23230"} -{"city": "RICHMOND", "loc": [-77.368002, 37.491529], "pop": 23773, "state": "VA", "_id": "23231"} -{"city": "RIDGE", "loc": [-77.614933, 37.619354], "pop": 32811, "state": "VA", "_id": "23233"} -{"city": "AMPTHILL", "loc": [-77.469798, 37.453158], "pop": 36331, "state": "VA", "_id": "23234"} -{"city": "BON AIR", "loc": [-77.565103, 37.512034], "pop": 30860, "state": "VA", "_id": "23235"} -{"city": "RICHMOND", "loc": [-77.585413, 37.478165], "pop": 24305, "state": "VA", "_id": "23236"} -{"city": "RICHMOND", "loc": [-77.461471, 37.401145], "pop": 19154, "state": "VA", "_id": "23237"} -{"city": "RICHMOND", "loc": [-77.545125, 37.632923], "pop": 14713, "state": "VA", "_id": "23294"} -{"city": "ACCOMAC", "loc": [-75.680272, 37.71588], "pop": 2562, "state": "VA", "_id": "23301"} -{"city": "ASSAWOMAN", "loc": [-75.52966, 37.86629], "pop": 82, "state": "VA", "_id": "23302"} -{"city": "BELLE HAVEN", "loc": [-75.869443, 37.56448], "pop": 344, "state": "VA", "_id": "23306"} -{"city": "BIRDSNEST", "loc": [-75.904347, 37.432077], "pop": 1057, "state": "VA", "_id": "23307"} -{"city": "BLOXOM", "loc": [-75.615568, 37.827264], "pop": 1581, "state": "VA", "_id": "23308"} -{"city": "CAPE CHARLES", "loc": [-75.972129, 37.279874], "pop": 6259, "state": "VA", "_id": "23310"} -{"city": "CARROLLTON", "loc": [-76.542963, 36.954994], "pop": 3990, "state": "VA", "_id": "23314"} -{"city": "CARRSVILLE", "loc": [-76.83648, 36.74544], "pop": 1273, "state": "VA", "_id": "23315"} -{"city": "CHESAPEAKE", "loc": [-76.23843, 36.735246], "pop": 55836, "state": "VA", "_id": "23320"} -{"city": "BOWERS HILL", "loc": [-76.411012, 36.827964], "pop": 23561, "state": "VA", "_id": "23321"} -{"city": "FENTRESS", "loc": [-76.213064, 36.634008], "pop": 10023, "state": "VA", "_id": "23322"} -{"city": "CHESAPEAKE", "loc": [-76.339743, 36.763424], "pop": 24412, "state": "VA", "_id": "23323"} -{"city": "CHESAPEAKE", "loc": [-76.266557, 36.805568], "pop": 21531, "state": "VA", "_id": "23324"} -{"city": "CHESAPEAKE", "loc": [-76.240555, 36.813963], "pop": 16543, "state": "VA", "_id": "23325"} -{"city": "CHINCOTEAGUE", "loc": [-75.371369, 37.927682], "pop": 3581, "state": "VA", "_id": "23336"} -{"city": "WALLOPS ISLAND", "loc": [-75.506503, 37.827338], "pop": 0, "state": "VA", "_id": "23337"} -{"city": "EXMORE", "loc": [-75.852624, 37.511689], "pop": 4678, "state": "VA", "_id": "23350"} -{"city": "FRANKTOWN", "loc": [-75.888971, 37.462009], "pop": 474, "state": "VA", "_id": "23354"} -{"city": "GREENBACKVILLE", "loc": [-75.402899, 38.006404], "pop": 400, "state": "VA", "_id": "23356"} -{"city": "GREENBUSH", "loc": [-75.6664, 37.768121], "pop": 821, "state": "VA", "_id": "23357"} -{"city": "HALLWOOD", "loc": [-75.561334, 37.853644], "pop": 322, "state": "VA", "_id": "23359"} -{"city": "HORNTOWN", "loc": [-75.471608, 37.969916], "pop": 132, "state": "VA", "_id": "23395"} -{"city": "HORSEY", "loc": [-75.56236, 37.933232], "pop": 200, "state": "VA", "_id": "23396"} -{"city": "JENKINS BRIDGE", "loc": [-75.631943, 37.919219], "pop": 301, "state": "VA", "_id": "23399"} -{"city": "LOCUSTVILLE", "loc": [-75.673508, 37.6534], "pop": 92, "state": "VA", "_id": "23404"} -{"city": "MACHIPONGO", "loc": [-75.908241, 37.401429], "pop": 593, "state": "VA", "_id": "23405"} -{"city": "MAPPSVILLE", "loc": [-75.569886, 37.835419], "pop": 835, "state": "VA", "_id": "23407"} -{"city": "MEARS", "loc": [-75.635443, 37.869189], "pop": 110, "state": "VA", "_id": "23409"} -{"city": "MELFA", "loc": [-75.745355, 37.6407], "pop": 2987, "state": "VA", "_id": "23410"} -{"city": "NEW CHURCH", "loc": [-75.511188, 37.952527], "pop": 2915, "state": "VA", "_id": "23415"} -{"city": "OAK HALL", "loc": [-75.606483, 37.953686], "pop": 130, "state": "VA", "_id": "23416"} -{"city": "ONANCOCK", "loc": [-75.752798, 37.710237], "pop": 3914, "state": "VA", "_id": "23417"} -{"city": "ONLEY", "loc": [-75.6992, 37.670422], "pop": 97, "state": "VA", "_id": "23418"} -{"city": "PAINTER", "loc": [-75.806612, 37.582821], "pop": 3782, "state": "VA", "_id": "23420"} -{"city": "PARKSLEY", "loc": [-75.638634, 37.774404], "pop": 3466, "state": "VA", "_id": "23421"} -{"city": "SANFORD", "loc": [-75.695704, 37.92433], "pop": 679, "state": "VA", "_id": "23426"} -{"city": "SMITHFIELD", "loc": [-76.63682, 36.981057], "pop": 11389, "state": "VA", "_id": "23430"} -{"city": "SUFFOLK", "loc": [-76.559811, 36.866823], "pop": 1396, "state": "VA", "_id": "23432"} -{"city": "SUFFOLK", "loc": [-76.49286, 36.909027], "pop": 1219, "state": "VA", "_id": "23433"} -{"city": "SUFFOLK", "loc": [-76.593147, 36.730433], "pop": 33717, "state": "VA", "_id": "23434"} -{"city": "SUFFOLK", "loc": [-76.466397, 36.854427], "pop": 8556, "state": "VA", "_id": "23435"} -{"city": "SUFFOLK", "loc": [-76.514157, 36.892625], "pop": 766, "state": "VA", "_id": "23436"} -{"city": "SUFFOLK", "loc": [-76.792043, 36.652611], "pop": 4414, "state": "VA", "_id": "23437"} -{"city": "SUFFOLK", "loc": [-76.687097, 36.591311], "pop": 2073, "state": "VA", "_id": "23438"} -{"city": "TANGIER", "loc": [-75.993003, 37.824485], "pop": 700, "state": "VA", "_id": "23440"} -{"city": "TEMPERANCEVILLE", "loc": [-75.55283, 37.895421], "pop": 1670, "state": "VA", "_id": "23442"} -{"city": "VIRGINIA BEACH", "loc": [-76.001928, 36.858451], "pop": 37212, "state": "VA", "_id": "23451"} -{"city": "VIRGINIA BEACH", "loc": [-76.096142, 36.83481], "pop": 61895, "state": "VA", "_id": "23452"} -{"city": "VIRGINIA BEACH", "loc": [-76.023723, 36.828187], "pop": 59937, "state": "VA", "_id": "23454"} -{"city": "VIRGINIA BEACH", "loc": [-76.144552, 36.888121], "pop": 43055, "state": "VA", "_id": "23455"} -{"city": "VIRGINIA BEACH", "loc": [-76.089162, 36.779851], "pop": 55909, "state": "VA", "_id": "23456"} -{"city": "BLACKWATER BRIDG", "loc": [-76.037816, 36.624793], "pop": 3448, "state": "VA", "_id": "23457"} -{"city": "VIRGINIA BEACH", "loc": [-76.017122, 36.9216], "pop": 1164, "state": "VA", "_id": "23459"} -{"city": "VIRGINIA BEACH", "loc": [-76.152184, 36.839193], "pop": 58632, "state": "VA", "_id": "23462"} -{"city": "VIRGINIA BEACH", "loc": [-76.175909, 36.797772], "pop": 67276, "state": "VA", "_id": "23464"} -{"city": "WALTERS", "loc": [-76.870156, 36.708974], "pop": 698, "state": "VA", "_id": "23481"} -{"city": "WINDSOR", "loc": [-76.732372, 36.836881], "pop": 4773, "state": "VA", "_id": "23487"} -{"city": "NORFOLK", "loc": [-76.214253, 36.854648], "pop": 21227, "state": "VA", "_id": "23502"} -{"city": "NORFOLK", "loc": [-76.252008, 36.944196], "pop": 32435, "state": "VA", "_id": "23503"} -{"city": "NORFOLK", "loc": [-76.268628, 36.858554], "pop": 25706, "state": "VA", "_id": "23504"} -{"city": "NORFOLK", "loc": [-76.28748, 36.91675], "pop": 30687, "state": "VA", "_id": "23505"} -{"city": "NORFOLK", "loc": [-76.300385, 36.864506], "pop": 6514, "state": "VA", "_id": "23507"} -{"city": "NORFOLK", "loc": [-76.300356, 36.885922], "pop": 19729, "state": "VA", "_id": "23508"} -{"city": "NORFOLK", "loc": [-76.260361, 36.878743], "pop": 12539, "state": "VA", "_id": "23509"} -{"city": "NORFOLK", "loc": [-76.287784, 36.852929], "pop": 4731, "state": "VA", "_id": "23510"} -{"city": "FLEET", "loc": [-76.309206, 36.951164], "pop": 28108, "state": "VA", "_id": "23511"} -{"city": "NORFOLK", "loc": [-76.239578, 36.891395], "pop": 31348, "state": "VA", "_id": "23513"} -{"city": "NORFOLK", "loc": [-76.294519, 36.869547], "pop": 4433, "state": "VA", "_id": "23517"} -{"city": "NORFOLK", "loc": [-76.216027, 36.920246], "pop": 35119, "state": "VA", "_id": "23518"} -{"city": "NAVAL AMPHIBIOUS", "loc": [-76.163715, 36.916923], "pop": 4541, "state": "VA", "_id": "23521"} -{"city": "NORFOLK", "loc": [-76.270125, 36.82942], "pop": 8682, "state": "VA", "_id": "23523"} -{"city": "NEWPORT NEWS", "loc": [-76.460722, 37.057951], "pop": 24325, "state": "VA", "_id": "23601"} -{"city": "NEWPORT NEWS", "loc": [-76.532125, 37.131684], "pop": 68525, "state": "VA", "_id": "23602"} -{"city": "NEWPORT NEWS", "loc": [-76.582059, 37.198887], "pop": 3368, "state": "VA", "_id": "23603"} -{"city": "NEWPORT NEWS", "loc": [-76.589727, 37.122112], "pop": 9967, "state": "VA", "_id": "23604"} -{"city": "NEWPORT NEWS", "loc": [-76.433158, 37.015583], "pop": 12649, "state": "VA", "_id": "23605"} -{"city": "NEWPORT NEWS", "loc": [-76.496724, 37.076777], "pop": 22429, "state": "VA", "_id": "23606"} -{"city": "NEWPORT NEWS", "loc": [-76.416469, 36.986352], "pop": 28785, "state": "VA", "_id": "23607"} -{"city": "HAMPTON", "loc": [-76.296896, 37.029671], "pop": 3325, "state": "VA", "_id": "23651"} -{"city": "HAMPTON", "loc": [-76.380085, 37.007432], "pop": 15313, "state": "VA", "_id": "23661"} -{"city": "POQUOSON", "loc": [-76.380702, 37.131252], "pop": 11005, "state": "VA", "_id": "23662"} -{"city": "HAMPTON", "loc": [-76.319875, 37.03181], "pop": 16889, "state": "VA", "_id": "23663"} -{"city": "HAMPTON", "loc": [-76.296639, 37.056611], "pop": 7832, "state": "VA", "_id": "23664"} -{"city": "HAMPTON", "loc": [-76.409939, 37.100565], "pop": 15146, "state": "VA", "_id": "23665"} -{"city": "HAMPTON", "loc": [-76.409617, 37.046241], "pop": 44985, "state": "VA", "_id": "23666"} -{"city": "HAMPTON", "loc": [-76.342573, 37.043559], "pop": 41855, "state": "VA", "_id": "23669"} -{"city": "YORKTOWN", "loc": [-76.542346, 37.228657], "pop": 3216, "state": "VA", "_id": "23690"} -{"city": "GRAFTON", "loc": [-76.459648, 37.170859], "pop": 14188, "state": "VA", "_id": "23692"} -{"city": "TABB", "loc": [-76.450743, 37.122586], "pop": 2807, "state": "VA", "_id": "23693"} -{"city": "SEAFORD", "loc": [-76.428992, 37.188468], "pop": 3009, "state": "VA", "_id": "23696"} -{"city": "PORTSMOUTH", "loc": [-76.36714, 36.808902], "pop": 29788, "state": "VA", "_id": "23701"} -{"city": "PORTSMOUTH", "loc": [-76.326979, 36.803534], "pop": 12599, "state": "VA", "_id": "23702"} -{"city": "PORTSMOUTH", "loc": [-76.386872, 36.869501], "pop": 22512, "state": "VA", "_id": "23703"} -{"city": "PORTSMOUTH", "loc": [-76.314604, 36.829821], "pop": 23634, "state": "VA", "_id": "23704"} -{"city": "PORTSMOUTH", "loc": [-76.344011, 36.836234], "pop": 15199, "state": "VA", "_id": "23707"} -{"city": "PORTSMOUTH", "loc": [-76.305188, 36.813883], "pop": 216, "state": "VA", "_id": "23709"} -{"city": "FORT LEE", "loc": [-77.33405, 37.244738], "pop": 8817, "state": "VA", "_id": "23801"} -{"city": "ETTRICK", "loc": [-77.432594, 37.220001], "pop": 41772, "state": "VA", "_id": "23803"} -{"city": "PETERSBURG", "loc": [-77.385385, 37.181937], "pop": 16432, "state": "VA", "_id": "23805"} -{"city": "ALBERTA", "loc": [-77.905518, 36.880609], "pop": 1553, "state": "VA", "_id": "23821"} -{"city": "BLACKSTONE", "loc": [-77.985063, 37.091578], "pop": 6351, "state": "VA", "_id": "23824"} -{"city": "BOYKINS", "loc": [-77.197519, 36.595061], "pop": 1595, "state": "VA", "_id": "23827"} -{"city": "BRANCHVILLE", "loc": [-77.270441, 36.578701], "pop": 619, "state": "VA", "_id": "23828"} -{"city": "CAPRON", "loc": [-77.239363, 36.724332], "pop": 2364, "state": "VA", "_id": "23829"} -{"city": "CARSON", "loc": [-77.435093, 37.039311], "pop": 1343, "state": "VA", "_id": "23830"} -{"city": "CHESTER", "loc": [-77.41557, 37.342934], "pop": 24788, "state": "VA", "_id": "23831"} -{"city": "CHESTERFIELD", "loc": [-77.566799, 37.392327], "pop": 25716, "state": "VA", "_id": "23832"} -{"city": "CHURCH ROAD", "loc": [-77.664588, 37.194964], "pop": 1515, "state": "VA", "_id": "23833"} -{"city": "COLONIAL HEIGHTS", "loc": [-77.403829, 37.269968], "pop": 21056, "state": "VA", "_id": "23834"} -{"city": "COURTLAND", "loc": [-77.078272, 36.722516], "pop": 3570, "state": "VA", "_id": "23837"} -{"city": "DENDRON", "loc": [-76.896642, 37.098076], "pop": 805, "state": "VA", "_id": "23839"} -{"city": "DEWITT", "loc": [-77.642558, 37.053522], "pop": 1385, "state": "VA", "_id": "23840"} -{"city": "DINWIDDIE", "loc": [-77.558539, 37.083283], "pop": 2781, "state": "VA", "_id": "23841"} -{"city": "DISPUTANTA", "loc": [-77.273172, 37.148268], "pop": 4938, "state": "VA", "_id": "23842"} -{"city": "DOLPHIN", "loc": [-77.788719, 36.831774], "pop": 982, "state": "VA", "_id": "23843"} -{"city": "DREWRYVILLE", "loc": [-77.359098, 36.685433], "pop": 743, "state": "VA", "_id": "23844"} -{"city": "EBONY", "loc": [-77.999431, 36.584993], "pop": 568, "state": "VA", "_id": "23845"} -{"city": "ELBERON", "loc": [-76.833723, 37.070133], "pop": 715, "state": "VA", "_id": "23846"} -{"city": "EMPORIA", "loc": [-77.562953, 36.685689], "pop": 13950, "state": "VA", "_id": "23847"} -{"city": "AMMON", "loc": [-77.737189, 37.139672], "pop": 1315, "state": "VA", "_id": "23850"} -{"city": "FRANKLIN", "loc": [-76.939108, 36.678625], "pop": 12786, "state": "VA", "_id": "23851"} -{"city": "FREEMAN", "loc": [-77.720772, 36.789342], "pop": 920, "state": "VA", "_id": "23856"} -{"city": "GASBURG", "loc": [-77.902927, 36.576552], "pop": 771, "state": "VA", "_id": "23857"} -{"city": "HANDSOM", "loc": [-77.023797, 36.609854], "pop": 371, "state": "VA", "_id": "23859"} -{"city": "HOPEWELL", "loc": [-77.295013, 37.287576], "pop": 25764, "state": "VA", "_id": "23860"} -{"city": "IVOR", "loc": [-76.886146, 36.907099], "pop": 2205, "state": "VA", "_id": "23866"} -{"city": "JARRATT", "loc": [-77.483162, 36.819129], "pop": 2055, "state": "VA", "_id": "23867"} -{"city": "TRIPLET", "loc": [-77.838437, 36.744874], "pop": 6357, "state": "VA", "_id": "23868"} -{"city": "MC KENNEY", "loc": [-77.739556, 36.998609], "pop": 1866, "state": "VA", "_id": "23872"} -{"city": "NEWSOMS", "loc": [-77.106949, 36.614814], "pop": 1193, "state": "VA", "_id": "23874"} -{"city": "PRINCE GEORGE", "loc": [-77.274706, 37.233339], "pop": 7713, "state": "VA", "_id": "23875"} -{"city": "RAWLINGS", "loc": [-77.82372, 36.953043], "pop": 527, "state": "VA", "_id": "23876"} -{"city": "SEDLEY", "loc": [-77.012531, 36.790752], "pop": 491, "state": "VA", "_id": "23878"} -{"city": "SKIPPERS", "loc": [-77.543663, 36.579685], "pop": 196, "state": "VA", "_id": "23879"} -{"city": "SPRING GROVE", "loc": [-76.992337, 37.190098], "pop": 1746, "state": "VA", "_id": "23881"} -{"city": "STONY CREEK", "loc": [-77.444276, 36.936132], "pop": 2889, "state": "VA", "_id": "23882"} -{"city": "SURRY", "loc": [-76.76514, 37.126024], "pop": 2271, "state": "VA", "_id": "23883"} -{"city": "SUTHERLAND", "loc": [-77.565463, 37.190149], "pop": 2470, "state": "VA", "_id": "23885"} -{"city": "VALENTINES", "loc": [-77.838405, 36.565024], "pop": 188, "state": "VA", "_id": "23887"} -{"city": "WAKEFIELD", "loc": [-76.97898, 36.975734], "pop": 2790, "state": "VA", "_id": "23888"} -{"city": "WARFIELD", "loc": [-77.767317, 36.901122], "pop": 580, "state": "VA", "_id": "23889"} -{"city": "WAVERLY", "loc": [-77.105453, 37.025032], "pop": 4581, "state": "VA", "_id": "23890"} -{"city": "WHITE PLAINS", "loc": [-77.959245, 36.633549], "pop": 522, "state": "VA", "_id": "23893"} -{"city": "WILSONS", "loc": [-77.835193, 37.115678], "pop": 592, "state": "VA", "_id": "23894"} -{"city": "YALE", "loc": [-77.286986, 36.837279], "pop": 626, "state": "VA", "_id": "23897"} -{"city": "ZUNI", "loc": [-76.810967, 36.843666], "pop": 2162, "state": "VA", "_id": "23898"} -{"city": "FARMVILLE", "loc": [-78.407592, 37.302692], "pop": 14084, "state": "VA", "_id": "23901"} -{"city": "BASKERVILLE", "loc": [-78.279047, 36.723595], "pop": 1114, "state": "VA", "_id": "23915"} -{"city": "BOYDTON", "loc": [-78.375174, 36.654422], "pop": 2613, "state": "VA", "_id": "23917"} -{"city": "BRACEY", "loc": [-78.105862, 36.576295], "pop": 1127, "state": "VA", "_id": "23919"} -{"city": "BRODNAX", "loc": [-77.989843, 36.731879], "pop": 2571, "state": "VA", "_id": "23920"} -{"city": "BUCKINGHAM", "loc": [-78.598537, 37.583335], "pop": 2056, "state": "VA", "_id": "23921"} -{"city": "BURKEVILLE", "loc": [-78.196084, 37.195203], "pop": 3751, "state": "VA", "_id": "23922"} -{"city": "CHARLOTTE COURT", "loc": [-78.660622, 37.086379], "pop": 2061, "state": "VA", "_id": "23923"} -{"city": "CHASE CITY", "loc": [-78.455268, 36.805305], "pop": 5715, "state": "VA", "_id": "23924"} -{"city": "CLARKSVILLE", "loc": [-78.535294, 36.631437], "pop": 2736, "state": "VA", "_id": "23927"} -{"city": "CREWE", "loc": [-78.105866, 37.165676], "pop": 5565, "state": "VA", "_id": "23930"} -{"city": "CULLEN", "loc": [-78.645912, 37.155211], "pop": 596, "state": "VA", "_id": "23934"} -{"city": "SPROUSES CORNER", "loc": [-78.460929, 37.545804], "pop": 5330, "state": "VA", "_id": "23936"} -{"city": "DRAKES BRANCH", "loc": [-78.561542, 36.968767], "pop": 1509, "state": "VA", "_id": "23937"} -{"city": "DUNDAS", "loc": [-78.010016, 36.90532], "pop": 407, "state": "VA", "_id": "23938"} -{"city": "GREEN BAY", "loc": [-78.307195, 37.123417], "pop": 838, "state": "VA", "_id": "23942"} -{"city": "KENBRIDGE", "loc": [-78.124238, 36.932965], "pop": 4269, "state": "VA", "_id": "23944"} -{"city": "KEYSVILLE", "loc": [-78.469912, 37.041086], "pop": 3670, "state": "VA", "_id": "23947"} -{"city": "BLACKRIDGE", "loc": [-78.095251, 36.68609], "pop": 3036, "state": "VA", "_id": "23950"} -{"city": "LUNENBURG", "loc": [-78.290644, 36.922624], "pop": 156, "state": "VA", "_id": "23952"} -{"city": "MEHERRIN", "loc": [-78.387357, 37.101283], "pop": 1429, "state": "VA", "_id": "23954"} -{"city": "PAMPLIN", "loc": [-78.65166, 37.265333], "pop": 2298, "state": "VA", "_id": "23958"} -{"city": "PHENIX", "loc": [-78.791195, 37.092506], "pop": 900, "state": "VA", "_id": "23959"} -{"city": "PROSPECT", "loc": [-78.546162, 37.305838], "pop": 1661, "state": "VA", "_id": "23960"} -{"city": "RANDOLPH", "loc": [-78.699249, 36.963079], "pop": 600, "state": "VA", "_id": "23962"} -{"city": "RED HOUSE", "loc": [-78.814467, 37.191431], "pop": 616, "state": "VA", "_id": "23963"} -{"city": "RED OAK", "loc": [-78.632057, 36.772403], "pop": 667, "state": "VA", "_id": "23964"} -{"city": "RICE", "loc": [-78.279262, 37.272132], "pop": 1973, "state": "VA", "_id": "23966"} -{"city": "SAXE", "loc": [-78.605699, 36.90569], "pop": 1095, "state": "VA", "_id": "23967"} -{"city": "SKIPWITH", "loc": [-78.530552, 36.731704], "pop": 1177, "state": "VA", "_id": "23968"} -{"city": "SOUTH HILL", "loc": [-78.153388, 36.712388], "pop": 7056, "state": "VA", "_id": "23970"} -{"city": "23973", "loc": [-78.219555, 36.766267], "pop": 796, "state": "VA", "_id": "23973"} -{"city": "VICTORIA", "loc": [-78.237243, 36.983543], "pop": 4027, "state": "VA", "_id": "23974"} -{"city": "WYLLIESBURG", "loc": [-78.612655, 36.838968], "pop": 945, "state": "VA", "_id": "23976"} -{"city": "ROANOKE", "loc": [-79.942019, 37.268997], "pop": 150, "state": "VA", "_id": "24011"} -{"city": "ROANOKE", "loc": [-79.932179, 37.302912], "pop": 22982, "state": "VA", "_id": "24012"} -{"city": "ROANOKE", "loc": [-79.924747, 37.267685], "pop": 8347, "state": "VA", "_id": "24013"} -{"city": "ROANOKE", "loc": [-79.946332, 37.23268], "pop": 21753, "state": "VA", "_id": "24014"} -{"city": "ROANOKE", "loc": [-79.980694, 37.258363], "pop": 15358, "state": "VA", "_id": "24015"} -{"city": "ROANOKE", "loc": [-79.953495, 37.270407], "pop": 9782, "state": "VA", "_id": "24016"} -{"city": "ROANOKE", "loc": [-79.990248, 37.293655], "pop": 24776, "state": "VA", "_id": "24017"} -{"city": "CAVE SPRING", "loc": [-80.021749, 37.231554], "pop": 24993, "state": "VA", "_id": "24018"} -{"city": "HOLLINS", "loc": [-79.956328, 37.33585], "pop": 23189, "state": "VA", "_id": "24019"} -{"city": "ARARAT", "loc": [-80.50934, 36.613946], "pop": 960, "state": "VA", "_id": "24053"} -{"city": "AXTON", "loc": [-79.73964, 36.654839], "pop": 4428, "state": "VA", "_id": "24054"} -{"city": "BASSETT", "loc": [-80.005506, 36.753425], "pop": 14101, "state": "VA", "_id": "24055"} -{"city": "BENT MOUNTAIN", "loc": [-80.145501, 37.206793], "pop": 2388, "state": "VA", "_id": "24059"} -{"city": "WHITETHORNE", "loc": [-80.427313, 37.228804], "pop": 42796, "state": "VA", "_id": "24060"} -{"city": "BLUE RIDGE", "loc": [-79.817178, 37.388542], "pop": 1669, "state": "VA", "_id": "24064"} -{"city": "BOONES MILL", "loc": [-79.955554, 37.13313], "pop": 6767, "state": "VA", "_id": "24065"} -{"city": "LITHIA", "loc": [-79.705287, 37.52844], "pop": 4145, "state": "VA", "_id": "24066"} -{"city": "CALLAWAY", "loc": [-80.049592, 37.028495], "pop": 2012, "state": "VA", "_id": "24067"} -{"city": "CASCADE", "loc": [-79.657438, 36.592697], "pop": 1435, "state": "VA", "_id": "24069"} -{"city": "CATAWBA", "loc": [-80.128384, 37.369649], "pop": 3747, "state": "VA", "_id": "24070"} -{"city": "SIMPSONS", "loc": [-80.236747, 37.032642], "pop": 1048, "state": "VA", "_id": "24072"} -{"city": "CHRISTIANSBURG", "loc": [-80.418774, 37.135286], "pop": 20714, "state": "VA", "_id": "24073"} -{"city": "CLAUDVILLE", "loc": [-80.444179, 36.583899], "pop": 1576, "state": "VA", "_id": "24076"} -{"city": "CLOVERDALE", "loc": [-79.912765, 37.366293], "pop": 582, "state": "VA", "_id": "24077"} -{"city": "COLLINSVILLE", "loc": [-79.914192, 36.72379], "pop": 7668, "state": "VA", "_id": "24078"} -{"city": "COPPER HILL", "loc": [-80.152469, 37.055568], "pop": 1765, "state": "VA", "_id": "24079"} -{"city": "CRITZ", "loc": [-80.129765, 36.621059], "pop": 260, "state": "VA", "_id": "24082"} -{"city": "DALEVILLE", "loc": [-79.921095, 37.41253], "pop": 1408, "state": "VA", "_id": "24083"} -{"city": "DUBLIN", "loc": [-80.669696, 37.098879], "pop": 9521, "state": "VA", "_id": "24084"} -{"city": "EAGLE ROCK", "loc": [-79.817215, 37.666727], "pop": 2939, "state": "VA", "_id": "24085"} -{"city": "EGGLESTON", "loc": [-80.652758, 37.290576], "pop": 374, "state": "VA", "_id": "24086"} -{"city": "IRONTO", "loc": [-80.249658, 37.212651], "pop": 4216, "state": "VA", "_id": "24087"} -{"city": "FERRUM", "loc": [-80.034923, 36.916834], "pop": 4389, "state": "VA", "_id": "24088"} -{"city": "FIELDALE", "loc": [-79.965245, 36.706414], "pop": 2594, "state": "VA", "_id": "24089"} -{"city": "FINCASTLE", "loc": [-79.851116, 37.491099], "pop": 2843, "state": "VA", "_id": "24090"} -{"city": "ALUM RIDGE", "loc": [-80.319054, 36.919523], "pop": 4833, "state": "VA", "_id": "24091"} -{"city": "GLADEHILL", "loc": [-79.776271, 37.016866], "pop": 2047, "state": "VA", "_id": "24092"} -{"city": "GLEN LYN", "loc": [-80.863358, 37.366853], "pop": 126, "state": "VA", "_id": "24093"} -{"city": "GOLDBOND", "loc": [-80.66315, 37.387221], "pop": 53, "state": "VA", "_id": "24094"} -{"city": "GOODVIEW", "loc": [-79.726284, 37.347167], "pop": 1686, "state": "VA", "_id": "24095"} -{"city": "HARDY", "loc": [-79.812661, 37.214473], "pop": 5955, "state": "VA", "_id": "24101"} -{"city": "HENRY", "loc": [-79.99036, 36.839322], "pop": 1393, "state": "VA", "_id": "24102"} -{"city": "HUDDLESTON", "loc": [-79.491017, 37.144155], "pop": 2299, "state": "VA", "_id": "24104"} -{"city": "INDIAN VALLEY", "loc": [-80.57571, 36.898993], "pop": 502, "state": "VA", "_id": "24105"} -{"city": "MARTINSVILLE", "loc": [-79.869136, 36.687067], "pop": 35994, "state": "VA", "_id": "24112"} -{"city": "MEADOWS OF DAN", "loc": [-80.402227, 36.72504], "pop": 1532, "state": "VA", "_id": "24120"} -{"city": "MONETA", "loc": [-79.652111, 37.178383], "pop": 4586, "state": "VA", "_id": "24121"} -{"city": "MONTVALE", "loc": [-79.717462, 37.40696], "pop": 1747, "state": "VA", "_id": "24122"} -{"city": "NARROWS", "loc": [-80.85485, 37.319846], "pop": 5250, "state": "VA", "_id": "24124"} -{"city": "NEW CASTLE", "loc": [-80.17042, 37.487132], "pop": 3400, "state": "VA", "_id": "24127"} -{"city": "NEWPORT", "loc": [-80.50992, 37.306852], "pop": 1578, "state": "VA", "_id": "24128"} -{"city": "PAINT BANK", "loc": [-80.254436, 37.574482], "pop": 153, "state": "VA", "_id": "24131"} -{"city": "PATRICK SPRINGS", "loc": [-80.138769, 36.674435], "pop": 2789, "state": "VA", "_id": "24133"} -{"city": "PEARISBURG", "loc": [-80.726703, 37.304121], "pop": 5279, "state": "VA", "_id": "24134"} -{"city": "MOUNTAIN LAKE", "loc": [-80.615507, 37.33027], "pop": 3087, "state": "VA", "_id": "24136"} -{"city": "PENHOOK", "loc": [-79.664493, 36.920125], "pop": 1768, "state": "VA", "_id": "24137"} -{"city": "PILOT", "loc": [-80.322901, 37.056476], "pop": 882, "state": "VA", "_id": "24138"} -{"city": "PITTSVILLE", "loc": [-79.479413, 36.971578], "pop": 415, "state": "VA", "_id": "24139"} -{"city": "FAIRLAWN", "loc": [-80.571721, 37.135816], "pop": 22335, "state": "VA", "_id": "24141"} -{"city": "RICH CREEK", "loc": [-80.822718, 37.385048], "pop": 947, "state": "VA", "_id": "24147"} -{"city": "RIDGEWAY", "loc": [-79.86859, 36.5874], "pop": 7953, "state": "VA", "_id": "24148"} -{"city": "RINER", "loc": [-80.435309, 37.032209], "pop": 2947, "state": "VA", "_id": "24149"} -{"city": "RIPPLEMEAD", "loc": [-80.671728, 37.366455], "pop": 37, "state": "VA", "_id": "24150"} -{"city": "ROCKY MOUNT", "loc": [-79.883959, 36.989101], "pop": 15579, "state": "VA", "_id": "24151"} -{"city": "SALEM", "loc": [-80.069185, 37.2853], "pop": 30570, "state": "VA", "_id": "24153"} -{"city": "SANDY LEVEL", "loc": [-79.561404, 36.991042], "pop": 512, "state": "VA", "_id": "24161"} -{"city": "SHAWSVILLE", "loc": [-80.27153, 37.146619], "pop": 2571, "state": "VA", "_id": "24162"} -{"city": "SPENCER", "loc": [-80.037345, 36.59676], "pop": 1537, "state": "VA", "_id": "24165"} -{"city": "STAFFORDSVILLE", "loc": [-80.740334, 37.245378], "pop": 464, "state": "VA", "_id": "24167"} -{"city": "STANLEYTOWN", "loc": [-79.935482, 36.734759], "pop": 153, "state": "VA", "_id": "24168"} -{"city": "STUART", "loc": [-80.23923, 36.651744], "pop": 7990, "state": "VA", "_id": "24171"} -{"city": "THAXTON", "loc": [-79.652187, 37.360008], "pop": 1136, "state": "VA", "_id": "24174"} -{"city": "TROUTVILLE", "loc": [-79.878636, 37.401855], "pop": 9211, "state": "VA", "_id": "24175"} -{"city": "UNION HALL", "loc": [-79.686447, 37.013111], "pop": 804, "state": "VA", "_id": "24176"} -{"city": "STEWARTSVILLE", "loc": [-79.835466, 37.271139], "pop": 18205, "state": "VA", "_id": "24179"} -{"city": "WIRTZ", "loc": [-79.757254, 37.081794], "pop": 1675, "state": "VA", "_id": "24184"} -{"city": "WOOLWINE", "loc": [-80.277426, 36.792076], "pop": 898, "state": "VA", "_id": "24185"} -{"city": "BRISTOL", "loc": [-82.182297, 36.618093], "pop": 23166, "state": "VA", "_id": "24201"} -{"city": "ABINGDON", "loc": [-82.019989, 36.691644], "pop": 25429, "state": "VA", "_id": "24210"} -{"city": "EXETER", "loc": [-82.791394, 36.906001], "pop": 2916, "state": "VA", "_id": "24216"} -{"city": "BEE", "loc": [-82.185632, 37.101358], "pop": 495, "state": "VA", "_id": "24217"} -{"city": "BIG STONE GAP", "loc": [-82.762727, 36.858073], "pop": 10858, "state": "VA", "_id": "24219"} -{"city": "BIRCHLEAF", "loc": [-82.248346, 37.169481], "pop": 997, "state": "VA", "_id": "24220"} -{"city": "BLACKWATER", "loc": [-82.98658, 36.639011], "pop": 1008, "state": "VA", "_id": "24221"} -{"city": "CASTLEWOOD", "loc": [-82.287574, 36.876409], "pop": 7166, "state": "VA", "_id": "24224"} -{"city": "CLEVELAND", "loc": [-82.131758, 36.950215], "pop": 1966, "state": "VA", "_id": "24225"} -{"city": "CLINCHCO", "loc": [-82.342476, 37.140581], "pop": 2059, "state": "VA", "_id": "24226"} -{"city": "CLINTWOOD", "loc": [-82.445339, 37.159206], "pop": 7866, "state": "VA", "_id": "24228"} -{"city": "COEBURN", "loc": [-82.473497, 36.960489], "pop": 11745, "state": "VA", "_id": "24230"} -{"city": "DAMASCUS", "loc": [-81.777633, 36.640264], "pop": 3312, "state": "VA", "_id": "24236"} -{"city": "DANTE", "loc": [-82.281519, 37.005419], "pop": 1168, "state": "VA", "_id": "24237"} -{"city": "DAVENPORT", "loc": [-82.122946, 37.068044], "pop": 49, "state": "VA", "_id": "24239"} -{"city": "DRYDEN", "loc": [-82.930478, 36.781825], "pop": 2185, "state": "VA", "_id": "24243"} -{"city": "CLINCHPORT", "loc": [-82.80651, 36.704402], "pop": 5688, "state": "VA", "_id": "24244"} -{"city": "DUNGANNON", "loc": [-82.496025, 36.82416], "pop": 1231, "state": "VA", "_id": "24245"} -{"city": "EWING", "loc": [-83.504737, 36.623694], "pop": 2599, "state": "VA", "_id": "24248"} -{"city": "FORT BLACKMORE", "loc": [-82.610178, 36.743828], "pop": 1085, "state": "VA", "_id": "24250"} -{"city": "GATE CITY", "loc": [-82.611179, 36.646026], "pop": 7641, "state": "VA", "_id": "24251"} -{"city": "HAYSI", "loc": [-82.285306, 37.220552], "pop": 3813, "state": "VA", "_id": "24256"} -{"city": "HILTONS", "loc": [-82.429929, 36.649778], "pop": 1937, "state": "VA", "_id": "24258"} -{"city": "COUNCIL", "loc": [-81.996883, 37.027292], "pop": 6162, "state": "VA", "_id": "24260"} -{"city": "JONESVILLE", "loc": [-83.13615, 36.689647], "pop": 6275, "state": "VA", "_id": "24263"} -{"city": "KEOKEE", "loc": [-82.977161, 36.823931], "pop": 1807, "state": "VA", "_id": "24265"} -{"city": "LEBANON", "loc": [-82.09563, 36.880895], "pop": 7818, "state": "VA", "_id": "24266"} -{"city": "MC CLURE", "loc": [-82.380553, 37.081412], "pop": 124, "state": "VA", "_id": "24269"} -{"city": "MENDOTA", "loc": [-82.264888, 36.722302], "pop": 675, "state": "VA", "_id": "24270"} -{"city": "NICKELSVILLE", "loc": [-82.420172, 36.750221], "pop": 2749, "state": "VA", "_id": "24271"} -{"city": "NORA", "loc": [-82.350023, 37.018229], "pop": 514, "state": "VA", "_id": "24272"} -{"city": "NORTON", "loc": [-82.624923, 36.937797], "pop": 4145, "state": "VA", "_id": "24273"} -{"city": "PENNINGTON GAP", "loc": [-83.022299, 36.750752], "pop": 5859, "state": "VA", "_id": "24277"} -{"city": "POUND", "loc": [-82.601555, 37.092734], "pop": 5330, "state": "VA", "_id": "24279"} -{"city": "ROSEDALE", "loc": [-81.888793, 36.973981], "pop": 2492, "state": "VA", "_id": "24280"} -{"city": "ROSE HILL", "loc": [-83.348565, 36.658257], "pop": 2404, "state": "VA", "_id": "24281"} -{"city": "SAINT CHARLES", "loc": [-83.051838, 36.831515], "pop": 485, "state": "VA", "_id": "24282"} -{"city": "SAINT PAUL", "loc": [-82.341844, 36.932295], "pop": 2680, "state": "VA", "_id": "24283"} -{"city": "STONEGA", "loc": [-82.819194, 36.950176], "pop": 670, "state": "VA", "_id": "24285"} -{"city": "TRAMMEL", "loc": [-82.217563, 37.022959], "pop": 51, "state": "VA", "_id": "24289"} -{"city": "WEBER CITY", "loc": [-82.545227, 36.620213], "pop": 2981, "state": "VA", "_id": "24290"} -{"city": "WHITETOP", "loc": [-81.583938, 36.610559], "pop": 642, "state": "VA", "_id": "24292"} -{"city": "WISE", "loc": [-82.594679, 36.975], "pop": 8957, "state": "VA", "_id": "24293"} -{"city": "PULASKI", "loc": [-80.770961, 37.056708], "pop": 16576, "state": "VA", "_id": "24301"} -{"city": "ATKINS", "loc": [-81.404791, 36.866532], "pop": 1108, "state": "VA", "_id": "24311"} -{"city": "AUSTINVILLE", "loc": [-80.858322, 36.819461], "pop": 2488, "state": "VA", "_id": "24312"} -{"city": "BARREN SPRINGS", "loc": [-80.809006, 36.907787], "pop": 575, "state": "VA", "_id": "24313"} -{"city": "BASTIAN", "loc": [-81.198855, 37.15742], "pop": 1656, "state": "VA", "_id": "24314"} -{"city": "BLAND", "loc": [-81.020064, 37.137644], "pop": 3261, "state": "VA", "_id": "24315"} -{"city": "BROADFORD", "loc": [-81.659388, 36.93299], "pop": 132, "state": "VA", "_id": "24316"} -{"city": "CANA", "loc": [-80.670452, 36.59574], "pop": 3363, "state": "VA", "_id": "24317"} -{"city": "CERES", "loc": [-81.364272, 37.00456], "pop": 833, "state": "VA", "_id": "24318"} -{"city": "CHILHOWIE", "loc": [-81.665103, 36.771912], "pop": 6087, "state": "VA", "_id": "24319"} -{"city": "CRIPPLE CREEK", "loc": [-81.103928, 36.808612], "pop": 157, "state": "VA", "_id": "24322"} -{"city": "CROCKETT", "loc": [-81.208883, 36.876755], "pop": 347, "state": "VA", "_id": "24323"} -{"city": "DRAPER", "loc": [-80.818752, 36.969743], "pop": 1161, "state": "VA", "_id": "24324"} -{"city": "DUGSPUR", "loc": [-80.612348, 36.814546], "pop": 1241, "state": "VA", "_id": "24325"} -{"city": "ELK CREEK", "loc": [-81.191488, 36.730579], "pop": 1161, "state": "VA", "_id": "24326"} -{"city": "FANCY GAP", "loc": [-80.69075, 36.663972], "pop": 1929, "state": "VA", "_id": "24328"} -{"city": "24329", "loc": [-80.827174, 36.889764], "pop": 159, "state": "VA", "_id": "24329"} -{"city": "FRIES", "loc": [-81.004157, 36.724683], "pop": 4077, "state": "VA", "_id": "24330"} -{"city": "GALAX", "loc": [-80.911744, 36.656503], "pop": 17442, "state": "VA", "_id": "24333"} -{"city": "GLADE SPRING", "loc": [-81.767639, 36.7779], "pop": 4384, "state": "VA", "_id": "24340"} -{"city": "HILLSVILLE", "loc": [-80.71973, 36.744225], "pop": 10578, "state": "VA", "_id": "24343"} -{"city": "ALLISONIA", "loc": [-80.638173, 36.997815], "pop": 2062, "state": "VA", "_id": "24347"} -{"city": "INDEPENDENCE", "loc": [-81.158202, 36.629444], "pop": 3760, "state": "VA", "_id": "24348"} -{"city": "IVANHOE", "loc": [-80.977911, 36.827225], "pop": 1006, "state": "VA", "_id": "24350"} -{"city": "LAMBSBURG", "loc": [-80.760133, 36.577483], "pop": 528, "state": "VA", "_id": "24351"} -{"city": "LAUREL FORK", "loc": [-80.514776, 36.707347], "pop": 714, "state": "VA", "_id": "24352"} -{"city": "MARION", "loc": [-81.534914, 36.827316], "pop": 16672, "state": "VA", "_id": "24354"} -{"city": "FOSTER FALLS", "loc": [-80.921391, 36.91708], "pop": 4621, "state": "VA", "_id": "24360"} -{"city": "MEADOWVIEW", "loc": [-81.851205, 36.761175], "pop": 6288, "state": "VA", "_id": "24361"} -{"city": "MOUTH OF WILSON", "loc": [-81.395452, 36.610379], "pop": 1735, "state": "VA", "_id": "24363"} -{"city": "ROCKY GAP", "loc": [-81.155236, 37.244242], "pop": 510, "state": "VA", "_id": "24366"} -{"city": "RURAL RETREAT", "loc": [-81.287872, 36.883616], "pop": 4350, "state": "VA", "_id": "24368"} -{"city": "SALTVILLE", "loc": [-81.740201, 36.892699], "pop": 6739, "state": "VA", "_id": "24370"} -{"city": "SEVEN MILE FORD", "loc": [-81.645672, 36.815329], "pop": 155, "state": "VA", "_id": "24373"} -{"city": "SPEEDWELL", "loc": [-81.183404, 36.799843], "pop": 633, "state": "VA", "_id": "24374"} -{"city": "SUGAR GROVE", "loc": [-81.408443, 36.773633], "pop": 1237, "state": "VA", "_id": "24375"} -{"city": "TANNERSVILLE", "loc": [-81.628017, 36.976331], "pop": 271, "state": "VA", "_id": "24377"} -{"city": "TROUT DALE", "loc": [-81.435004, 36.68546], "pop": 1261, "state": "VA", "_id": "24378"} -{"city": "WILLIS", "loc": [-80.490861, 36.874927], "pop": 2769, "state": "VA", "_id": "24380"} -{"city": "WOODLAWN", "loc": [-80.882437, 36.787948], "pop": 218, "state": "VA", "_id": "24381"} -{"city": "WYTHEVILLE", "loc": [-81.094082, 36.940669], "pop": 13788, "state": "VA", "_id": "24382"} -{"city": "WOODRUM", "loc": [-79.07519, 38.145104], "pop": 34323, "state": "VA", "_id": "24401"} -{"city": "BLUE GRASS", "loc": [-79.561272, 38.515231], "pop": 201, "state": "VA", "_id": "24413"} -{"city": "BUENA VISTA", "loc": [-79.352311, 37.739585], "pop": 8464, "state": "VA", "_id": "24416"} -{"city": "CHURCHVILLE", "loc": [-79.179126, 38.234605], "pop": 3411, "state": "VA", "_id": "24421"} -{"city": "CLIFTON FORGE", "loc": [-79.805423, 37.820342], "pop": 7486, "state": "VA", "_id": "24422"} -{"city": "ALLEGHANY", "loc": [-80.003176, 37.784203], "pop": 16958, "state": "VA", "_id": "24426"} -{"city": "CRAIGSVILLE", "loc": [-79.361883, 38.076826], "pop": 3008, "state": "VA", "_id": "24430"} -{"city": "CRIMORA", "loc": [-78.841275, 38.168422], "pop": 2153, "state": "VA", "_id": "24431"} -{"city": "DEERFIELD", "loc": [-79.415186, 38.184231], "pop": 389, "state": "VA", "_id": "24432"} -{"city": "DOE HILL", "loc": [-79.482176, 38.390248], "pop": 265, "state": "VA", "_id": "24433"} -{"city": "FAIRFIELD", "loc": [-79.297922, 37.877825], "pop": 1314, "state": "VA", "_id": "24435"} -{"city": "FORT DEFIANCE", "loc": [-78.932583, 38.210928], "pop": 824, "state": "VA", "_id": "24437"} -{"city": "GOSHEN", "loc": [-79.477325, 37.987813], "pop": 1173, "state": "VA", "_id": "24439"} -{"city": "GREENVILLE", "loc": [-79.135876, 38.001804], "pop": 1975, "state": "VA", "_id": "24440"} -{"city": "GROTTOES", "loc": [-78.825528, 38.248371], "pop": 3879, "state": "VA", "_id": "24441"} -{"city": "HEAD WATERS", "loc": [-79.435272, 38.294611], "pop": 87, "state": "VA", "_id": "24442"} -{"city": "HIGHTOWN", "loc": [-79.592927, 38.464597], "pop": 192, "state": "VA", "_id": "24444"} -{"city": "HOT SPRINGS", "loc": [-79.871662, 37.963784], "pop": 2506, "state": "VA", "_id": "24445"} -{"city": "LEXINGTON", "loc": [-79.458111, 37.788463], "pop": 14306, "state": "VA", "_id": "24450"} -{"city": "MC DOWELL", "loc": [-79.498798, 38.326588], "pop": 353, "state": "VA", "_id": "24458"} -{"city": "MIDDLEBROOK", "loc": [-79.281165, 38.024158], "pop": 527, "state": "VA", "_id": "24459"} -{"city": "MILLBORO SPRING", "loc": [-79.640649, 38.005795], "pop": 1586, "state": "VA", "_id": "24460"} -{"city": "MONTEBELLO", "loc": [-79.061079, 37.839931], "pop": 579, "state": "VA", "_id": "24464"} -{"city": "MONTEREY", "loc": [-79.59407, 38.405821], "pop": 1335, "state": "VA", "_id": "24465"} -{"city": "MOUNT SIDNEY", "loc": [-78.97296, 38.261185], "pop": 1787, "state": "VA", "_id": "24467"} -{"city": "MUSTOE", "loc": [-79.659137, 38.286216], "pop": 202, "state": "VA", "_id": "24468"} -{"city": "PORT REPUBLIC", "loc": [-78.810388, 38.317167], "pop": 1196, "state": "VA", "_id": "24471"} -{"city": "RAPHINE", "loc": [-79.221903, 37.937353], "pop": 2238, "state": "VA", "_id": "24472"} -{"city": "ROCKBRIDGE BATHS", "loc": [-79.387421, 37.896458], "pop": 677, "state": "VA", "_id": "24473"} -{"city": "SPOTTSWOOD", "loc": [-79.221141, 37.968418], "pop": 393, "state": "VA", "_id": "24475"} -{"city": "STUARTS DRAFT", "loc": [-79.029821, 38.014423], "pop": 5317, "state": "VA", "_id": "24477"} -{"city": "SWOOPE", "loc": [-79.187281, 38.159117], "pop": 1477, "state": "VA", "_id": "24479"} -{"city": "VERONA", "loc": [-79.005872, 38.203726], "pop": 4491, "state": "VA", "_id": "24482"} -{"city": "VESUVIUS", "loc": [-79.213485, 37.83777], "pop": 548, "state": "VA", "_id": "24483"} -{"city": "BOLAR", "loc": [-79.807792, 38.085119], "pop": 940, "state": "VA", "_id": "24484"} -{"city": "WEST AUGUSTA", "loc": [-79.320139, 38.274397], "pop": 350, "state": "VA", "_id": "24485"} -{"city": "WEYERS CAVE", "loc": [-78.923454, 38.293145], "pop": 2190, "state": "VA", "_id": "24486"} -{"city": "BURNSVILLE", "loc": [-79.650607, 38.177907], "pop": 170, "state": "VA", "_id": "24487"} -{"city": "LYNCHBURG", "loc": [-79.171464, 37.386228], "pop": 27264, "state": "VA", "_id": "24501"} -{"city": "TIMBERLAKE", "loc": [-79.211783, 37.359635], "pop": 28755, "state": "VA", "_id": "24502"} -{"city": "LYNCHBURG", "loc": [-79.204982, 37.437646], "pop": 17816, "state": "VA", "_id": "24503"} -{"city": "LYNCHBURG", "loc": [-79.12142, 37.390422], "pop": 10889, "state": "VA", "_id": "24504"} -{"city": "ALTAVISTA", "loc": [-79.291145, 37.122162], "pop": 4043, "state": "VA", "_id": "24517"} -{"city": "ALTON", "loc": [-79.020237, 36.589352], "pop": 2557, "state": "VA", "_id": "24520"} -{"city": "AMHERST", "loc": [-79.050572, 37.602677], "pop": 7343, "state": "VA", "_id": "24521"} -{"city": "APPOMATTOX", "loc": [-78.822445, 37.352951], "pop": 8904, "state": "VA", "_id": "24522"} -{"city": "BEDFORD", "loc": [-79.53311, 37.315345], "pop": 16245, "state": "VA", "_id": "24523"} -{"city": "BIG ISLAND", "loc": [-79.382706, 37.530556], "pop": 1313, "state": "VA", "_id": "24526"} -{"city": "BLAIRS", "loc": [-79.378869, 36.667408], "pop": 5007, "state": "VA", "_id": "24527"} -{"city": "BROOKNEAL", "loc": [-78.922679, 37.082663], "pop": 4364, "state": "VA", "_id": "24528"} -{"city": "BUFFALO JUNCTION", "loc": [-78.609266, 36.61676], "pop": 3300, "state": "VA", "_id": "24529"} -{"city": "CALLANDS", "loc": [-79.628779, 36.764748], "pop": 2143, "state": "VA", "_id": "24530"} -{"city": "CHATHAM", "loc": [-79.429659, 36.83099], "pop": 7825, "state": "VA", "_id": "24531"} -{"city": "CLOVER", "loc": [-78.78618, 36.863695], "pop": 1516, "state": "VA", "_id": "24534"} -{"city": "COLEMAN FALLS", "loc": [-79.315757, 37.488659], "pop": 286, "state": "VA", "_id": "24536"} -{"city": "CONCORD", "loc": [-78.980353, 37.336953], "pop": 3845, "state": "VA", "_id": "24538"} -{"city": "CRYSTAL HILL", "loc": [-78.970634, 36.873217], "pop": 280, "state": "VA", "_id": "24539"} -{"city": "DANVILLE", "loc": [-79.412441, 36.621789], "pop": 32061, "state": "VA", "_id": "24540"} -{"city": "DANVILLE", "loc": [-79.441112, 36.577937], "pop": 33751, "state": "VA", "_id": "24541"} -{"city": "DRY FORK", "loc": [-79.457966, 36.743008], "pop": 1989, "state": "VA", "_id": "24549"} -{"city": "EVINGTON", "loc": [-79.231723, 37.261049], "pop": 4961, "state": "VA", "_id": "24550"} -{"city": "FOREST", "loc": [-79.279116, 37.337878], "pop": 12905, "state": "VA", "_id": "24551"} -{"city": "GLADSTONE", "loc": [-78.850838, 37.547252], "pop": 2025, "state": "VA", "_id": "24553"} -{"city": "GLADYS", "loc": [-79.104794, 37.138581], "pop": 3511, "state": "VA", "_id": "24554"} -{"city": "GLASGOW", "loc": [-79.45898, 37.643], "pop": 1604, "state": "VA", "_id": "24555"} -{"city": "GOODE", "loc": [-79.381277, 37.348098], "pop": 2516, "state": "VA", "_id": "24556"} -{"city": "GRETNA", "loc": [-79.338874, 36.969515], "pop": 8304, "state": "VA", "_id": "24557"} -{"city": "HALIFAX", "loc": [-78.941494, 36.762571], "pop": 7127, "state": "VA", "_id": "24558"} -{"city": "HOWARDSVILLE", "loc": [-78.582355, 37.765891], "pop": 21, "state": "VA", "_id": "24562"} -{"city": "HURT", "loc": [-79.299995, 37.079787], "pop": 5283, "state": "VA", "_id": "24563"} -{"city": "JAVA", "loc": [-79.187034, 36.858582], "pop": 1111, "state": "VA", "_id": "24565"} -{"city": "KEELING", "loc": [-79.278284, 36.715577], "pop": 2651, "state": "VA", "_id": "24566"} -{"city": "LONG ISLAND", "loc": [-79.121933, 37.064374], "pop": 1308, "state": "VA", "_id": "24569"} -{"city": "LOWRY", "loc": [-79.455599, 37.33361], "pop": 1342, "state": "VA", "_id": "24570"} -{"city": "LYNCH STATION", "loc": [-79.329716, 37.152826], "pop": 2819, "state": "VA", "_id": "24571"} -{"city": "MADISON HEIGHTS", "loc": [-79.114089, 37.453102], "pop": 15389, "state": "VA", "_id": "24572"} -{"city": "MONROE", "loc": [-79.17034, 37.541366], "pop": 5050, "state": "VA", "_id": "24574"} -{"city": "LENNIG", "loc": [-78.989766, 36.944179], "pop": 5018, "state": "VA", "_id": "24577"} -{"city": "NATURAL BRIDGE", "loc": [-79.533045, 37.660625], "pop": 1236, "state": "VA", "_id": "24578"} -{"city": "NATURAL BRIDGE S", "loc": [-79.503905, 37.595667], "pop": 1419, "state": "VA", "_id": "24579"} -{"city": "NELSON", "loc": [-78.670959, 36.558558], "pop": 131, "state": "VA", "_id": "24580"} -{"city": "RINGGOLD", "loc": [-79.298835, 36.603483], "pop": 4728, "state": "VA", "_id": "24586"} -{"city": "RUSTBURG", "loc": [-79.121507, 37.25454], "pop": 4723, "state": "VA", "_id": "24588"} -{"city": "SCOTTSBURG", "loc": [-78.786601, 36.786194], "pop": 2965, "state": "VA", "_id": "24589"} -{"city": "SCOTTSVILLE", "loc": [-78.473981, 37.804937], "pop": 5769, "state": "VA", "_id": "24590"} -{"city": "SOUTH BOSTON", "loc": [-78.918829, 36.696335], "pop": 12624, "state": "VA", "_id": "24592"} -{"city": "SPOUT SPRING", "loc": [-78.905887, 37.364345], "pop": 872, "state": "VA", "_id": "24593"} -{"city": "SUTHERLIN", "loc": [-79.19495, 36.625831], "pop": 1018, "state": "VA", "_id": "24594"} -{"city": "INGRAM", "loc": [-79.110874, 36.786684], "pop": 1648, "state": "VA", "_id": "24597"} -{"city": "VIRGILINA", "loc": [-78.760516, 36.606231], "pop": 2435, "state": "VA", "_id": "24598"} -{"city": "WINGINA", "loc": [-78.755709, 37.641473], "pop": 833, "state": "VA", "_id": "24599"} -{"city": "BANDY", "loc": [-81.6508, 37.166112], "pop": 2348, "state": "VA", "_id": "24602"} -{"city": "CONAWAY", "loc": [-82.224596, 37.318638], "pop": 1151, "state": "VA", "_id": "24603"} -{"city": "BLUEFIELD", "loc": [-81.325114, 37.249512], "pop": 10931, "state": "VA", "_id": "24605"} -{"city": "CEDAR BLUFF", "loc": [-81.766788, 37.079076], "pop": 3486, "state": "VA", "_id": "24609"} -{"city": "FALLS MILLS", "loc": [-81.318234, 37.271023], "pop": 960, "state": "VA", "_id": "24613"} -{"city": "GRUNDY", "loc": [-82.106077, 37.296678], "pop": 10245, "state": "VA", "_id": "24614"} -{"city": "HURLEY", "loc": [-82.026164, 37.401741], "pop": 5296, "state": "VA", "_id": "24620"} -{"city": "JEWELL VALLEY", "loc": [-81.810654, 37.222372], "pop": 1213, "state": "VA", "_id": "24622"} -{"city": "MAVISDALE", "loc": [-82.221599, 37.200817], "pop": 141, "state": "VA", "_id": "24627"} -{"city": "TIPTOP", "loc": [-81.525256, 37.152447], "pop": 6771, "state": "VA", "_id": "24630"} -{"city": "PATTERSON", "loc": [-81.991577, 37.184074], "pop": 1356, "state": "VA", "_id": "24631"} -{"city": "24633", "loc": [-81.988789, 37.235477], "pop": 1571, "state": "VA", "_id": "24633"} -{"city": "PILGRIMS KNOB", "loc": [-81.911896, 37.298944], "pop": 766, "state": "VA", "_id": "24634"} -{"city": "POUNDING MILL", "loc": [-81.730052, 37.059584], "pop": 3942, "state": "VA", "_id": "24637"} -{"city": "RAVEN", "loc": [-81.889557, 37.148056], "pop": 3441, "state": "VA", "_id": "24639"} -{"city": "RICHLANDS", "loc": [-81.812286, 37.094051], "pop": 8026, "state": "VA", "_id": "24641"} -{"city": "ROWE", "loc": [-82.027381, 37.127593], "pop": 1531, "state": "VA", "_id": "24646"} -{"city": "SWORDS CREEK", "loc": [-81.908385, 37.073792], "pop": 2756, "state": "VA", "_id": "24649"} -{"city": "TAZEWELL", "loc": [-81.509976, 37.107773], "pop": 7023, "state": "VA", "_id": "24651"} -{"city": "VANSANT", "loc": [-82.127715, 37.173811], "pop": 4724, "state": "VA", "_id": "24656"} -{"city": "WHITEWOOD", "loc": [-81.890361, 37.264409], "pop": 731, "state": "VA", "_id": "24657"} -{"city": "ALGONA", "loc": [-122.270057, 47.316339], "pop": 22846, "state": "WA", "_id": "98001"} -{"city": "AUBURN", "loc": [-122.206741, 47.30503], "pop": 38163, "state": "WA", "_id": "98002"} -{"city": "FEDERAL WAY", "loc": [-122.311726, 47.3203], "pop": 34573, "state": "WA", "_id": "98003"} -{"city": "BEAUX ARTS", "loc": [-122.207371, 47.619899], "pop": 23724, "state": "WA", "_id": "98004"} -{"city": "BELLEVUE", "loc": [-122.166288, 47.614961], "pop": 14297, "state": "WA", "_id": "98005"} -{"city": "BELLEVUE", "loc": [-122.155179, 47.561425], "pop": 26775, "state": "WA", "_id": "98006"} -{"city": "BELLEVUE", "loc": [-122.142572, 47.617443], "pop": 21887, "state": "WA", "_id": "98007"} -{"city": "BELLEVUE", "loc": [-122.116173, 47.611468], "pop": 24046, "state": "WA", "_id": "98008"} -{"city": "BLACK DIAMOND", "loc": [-122.005265, 47.311372], "pop": 1817, "state": "WA", "_id": "98010"} -{"city": "BOTHELL", "loc": [-122.2159, 47.749692], "pop": 32985, "state": "WA", "_id": "98011"} -{"city": "MILL CREEK", "loc": [-122.206981, 47.848941], "pop": 19247, "state": "WA", "_id": "98012"} -{"city": "CARNATION", "loc": [-121.911095, 47.638007], "pop": 2808, "state": "WA", "_id": "98014"} -{"city": "DUVALL", "loc": [-121.936906, 47.724987], "pop": 7866, "state": "WA", "_id": "98019"} -{"city": "WOODWAY", "loc": [-122.366949, 47.800693], "pop": 17396, "state": "WA", "_id": "98020"} -{"city": "BOTHELL", "loc": [-122.224339, 47.791806], "pop": 18013, "state": "WA", "_id": "98021"} -{"city": "ENUMCLAW", "loc": [-122.031429, 47.266545], "pop": 34850, "state": "WA", "_id": "98022"} -{"city": "FEDERAL WAY", "loc": [-122.36123, 47.310358], "pop": 38292, "state": "WA", "_id": "98023"} -{"city": "FALL CITY", "loc": [-121.889646, 47.568233], "pop": 3213, "state": "WA", "_id": "98024"} -{"city": "EDMONDS", "loc": [-122.334463, 47.823324], "pop": 33385, "state": "WA", "_id": "98026"} -{"city": "ISSAQUAH", "loc": [-122.033517, 47.550911], "pop": 37255, "state": "WA", "_id": "98027"} -{"city": "KENT", "loc": [-122.193184, 47.388004], "pop": 50515, "state": "WA", "_id": "98031"} -{"city": "KENT", "loc": [-122.285362, 47.377633], "pop": 31379, "state": "WA", "_id": "98032"} -{"city": "KIRKLAND", "loc": [-122.189442, 47.678597], "pop": 28211, "state": "WA", "_id": "98033"} -{"city": "KIRKLAND", "loc": [-122.196571, 47.718777], "pop": 38266, "state": "WA", "_id": "98034"} -{"city": "BRIER", "loc": [-122.287789, 47.811825], "pop": 28602, "state": "WA", "_id": "98036"} -{"city": "LYNNWOOD", "loc": [-122.282139, 47.850532], "pop": 36995, "state": "WA", "_id": "98037"} -{"city": "MAPLE VALLEY", "loc": [-122.057413, 47.384526], "pop": 13768, "state": "WA", "_id": "98038"} -{"city": "MERCER ISLAND", "loc": [-122.226562, 47.563149], "pop": 20816, "state": "WA", "_id": "98040"} -{"city": "KENT", "loc": [-122.120615, 47.368044], "pop": 22576, "state": "WA", "_id": "98042"} -{"city": "MOUNTLAKE TERRAC", "loc": [-122.304036, 47.793061], "pop": 20059, "state": "WA", "_id": "98043"} -{"city": "NORTH BEND", "loc": [-121.757142, 47.475546], "pop": 10083, "state": "WA", "_id": "98045"} -{"city": "PACIFIC", "loc": [-122.243481, 47.266605], "pop": 3902, "state": "WA", "_id": "98047"} -{"city": "RAVENSDALE", "loc": [-121.987897, 47.415476], "pop": 3778, "state": "WA", "_id": "98051"} -{"city": "REDMOND", "loc": [-122.123242, 47.671796], "pop": 37639, "state": "WA", "_id": "98052"} -{"city": "REDMOND", "loc": [-122.038578, 47.646238], "pop": 22112, "state": "WA", "_id": "98053"} -{"city": "RENTON", "loc": [-122.207484, 47.464759], "pop": 17902, "state": "WA", "_id": "98055"} -{"city": "RENTON", "loc": [-122.181942, 47.507336], "pop": 23790, "state": "WA", "_id": "98056"} -{"city": "RENTON", "loc": [-122.121586, 47.446507], "pop": 10153, "state": "WA", "_id": "98058"} -{"city": "RENTON", "loc": [-122.151178, 47.467383], "pop": 48197, "state": "WA", "_id": "98059"} -{"city": "SNOQUALMIE", "loc": [-121.822533, 47.529286], "pop": 3913, "state": "WA", "_id": "98065"} -{"city": "VASHON", "loc": [-122.464415, 47.425949], "pop": 9309, "state": "WA", "_id": "98070"} -{"city": "WOODINVILLE", "loc": [-122.127087, 47.768384], "pop": 40666, "state": "WA", "_id": "98072"} -{"city": "SEATTLE", "loc": [-122.330456, 47.611435], "pop": 5801, "state": "WA", "_id": "98101"} -{"city": "SEATTLE", "loc": [-122.320993, 47.63025], "pop": 19000, "state": "WA", "_id": "98102"} -{"city": "SEATTLE", "loc": [-122.342621, 47.67335], "pop": 39491, "state": "WA", "_id": "98103"} -{"city": "SEATTLE", "loc": [-122.325644, 47.603631], "pop": 9680, "state": "WA", "_id": "98104"} -{"city": "SEATTLE", "loc": [-122.302236, 47.663266], "pop": 37120, "state": "WA", "_id": "98105"} -{"city": "SEATTLE", "loc": [-122.354688, 47.534362], "pop": 17510, "state": "WA", "_id": "98106"} -{"city": "SEATTLE", "loc": [-122.37626, 47.67012], "pop": 18288, "state": "WA", "_id": "98107"} -{"city": "TUKWILA", "loc": [-122.306823, 47.547448], "pop": 18776, "state": "WA", "_id": "98108"} -{"city": "SEATTLE", "loc": [-122.347615, 47.633875], "pop": 13401, "state": "WA", "_id": "98109"} -{"city": "BAINBRIDGE ISLAN", "loc": [-122.531297, 47.645048], "pop": 15846, "state": "WA", "_id": "98110"} -{"city": "SEATTLE", "loc": [-122.297157, 47.630115], "pop": 19760, "state": "WA", "_id": "98112"} -{"city": "SEATTLE", "loc": [-122.296828, 47.684918], "pop": 40454, "state": "WA", "_id": "98115"} -{"city": "SEATTLE", "loc": [-122.393445, 47.574591], "pop": 20408, "state": "WA", "_id": "98116"} -{"city": "SEATTLE", "loc": [-122.377223, 47.687263], "pop": 28572, "state": "WA", "_id": "98117"} -{"city": "SEATTLE", "loc": [-122.275021, 47.541234], "pop": 36684, "state": "WA", "_id": "98118"} -{"city": "SEATTLE", "loc": [-122.364272, 47.637917], "pop": 19064, "state": "WA", "_id": "98119"} -{"city": "SEATTLE", "loc": [-122.344696, 47.615135], "pop": 4091, "state": "WA", "_id": "98121"} -{"city": "SEATTLE", "loc": [-122.305608, 47.611633], "pop": 25105, "state": "WA", "_id": "98122"} -{"city": "SEATTLE", "loc": [-122.301546, 47.717002], "pop": 31928, "state": "WA", "_id": "98125"} -{"city": "SEATTLE", "loc": [-122.373458, 47.544361], "pop": 18627, "state": "WA", "_id": "98126"} -{"city": "SEATTLE", "loc": [-122.343132, 47.737717], "pop": 39634, "state": "WA", "_id": "98133"} -{"city": "SEATTLE", "loc": [-122.326346, 47.590276], "pop": 1437, "state": "WA", "_id": "98134"} -{"city": "SEATTLE", "loc": [-122.387768, 47.539769], "pop": 13816, "state": "WA", "_id": "98136"} -{"city": "SEATTLE", "loc": [-122.300457, 47.584624], "pop": 23333, "state": "WA", "_id": "98144"} -{"city": "BURIEN", "loc": [-122.353989, 47.501069], "pop": 25963, "state": "WA", "_id": "98146"} -{"city": "NORMANDY PARK", "loc": [-122.326112, 47.450209], "pop": 8818, "state": "WA", "_id": "98148"} -{"city": "LK FOREST PARK", "loc": [-122.296305, 47.758161], "pop": 38296, "state": "WA", "_id": "98155"} -{"city": "SEATAC", "loc": [-122.318454, 47.442739], "pop": 97, "state": "WA", "_id": "98158"} -{"city": "NORMANDY PARK", "loc": [-122.347392, 47.455052], "pop": 19331, "state": "WA", "_id": "98166"} -{"city": "TUKWILA", "loc": [-122.302376, 47.48851], "pop": 27990, "state": "WA", "_id": "98168"} -{"city": "SEATTLE", "loc": [-122.368585, 47.746678], "pop": 18532, "state": "WA", "_id": "98177"} -{"city": "TUKWILA", "loc": [-122.247366, 47.499489], "pop": 19522, "state": "WA", "_id": "98178"} -{"city": "TUKWILA", "loc": [-122.281159, 47.449808], "pop": 18001, "state": "WA", "_id": "98188"} -{"city": "DES MOINES", "loc": [-122.309559, 47.407286], "pop": 20550, "state": "WA", "_id": "98198"} -{"city": "SEATTLE", "loc": [-122.396357, 47.648845], "pop": 18360, "state": "WA", "_id": "98199"} -{"city": "EVERETT", "loc": [-122.200571, 47.988431], "pop": 26440, "state": "WA", "_id": "98201"} -{"city": "EVERETT", "loc": [-122.221846, 47.941937], "pop": 26506, "state": "WA", "_id": "98203"} -{"city": "EVERETT", "loc": [-122.247217, 47.901659], "pop": 20496, "state": "WA", "_id": "98204"} -{"city": "EVERETT", "loc": [-122.115759, 47.990065], "pop": 10083, "state": "WA", "_id": "98205"} -{"city": "EVERETT", "loc": [-122.198722, 47.894822], "pop": 32818, "state": "WA", "_id": "98208"} -{"city": "ACME", "loc": [-122.191391, 48.675248], "pop": 471, "state": "WA", "_id": "98220"} -{"city": "ANACORTES", "loc": [-122.630873, 48.500438], "pop": 12986, "state": "WA", "_id": "98221"} -{"city": "ARLINGTON", "loc": [-122.112126, 48.18293], "pop": 24435, "state": "WA", "_id": "98223"} -{"city": "BARING", "loc": [-121.44757, 47.757787], "pop": 210, "state": "WA", "_id": "98224"} -{"city": "BELLINGHAM", "loc": [-122.488676, 48.748957], "pop": 38415, "state": "WA", "_id": "98225"} -{"city": "BELLINGHAM", "loc": [-122.441457, 48.762763], "pop": 38518, "state": "WA", "_id": "98226"} -{"city": "BLAINE", "loc": [-122.732327, 48.963572], "pop": 7057, "state": "WA", "_id": "98230"} -{"city": "BOW", "loc": [-122.413438, 48.562037], "pop": 3245, "state": "WA", "_id": "98232"} -{"city": "BURLINGTON", "loc": [-122.33449, 48.478577], "pop": 9113, "state": "WA", "_id": "98233"} -{"city": "CLINTON", "loc": [-122.391588, 47.950845], "pop": 3242, "state": "WA", "_id": "98236"} -{"city": "CONCRETE", "loc": [-121.664294, 48.530962], "pop": 3217, "state": "WA", "_id": "98237"} -{"city": "COUPEVILLE", "loc": [-122.682346, 48.218911], "pop": 5753, "state": "WA", "_id": "98239"} -{"city": "CUSTER", "loc": [-122.622571, 48.937412], "pop": 2791, "state": "WA", "_id": "98240"} -{"city": "DARRINGTON", "loc": [-121.591807, 48.249285], "pop": 1761, "state": "WA", "_id": "98241"} -{"city": "GLACIER", "loc": [-122.153957, 48.803366], "pop": 2110, "state": "WA", "_id": "98244"} -{"city": "EASTSOUND", "loc": [-122.937045, 48.665554], "pop": 2259, "state": "WA", "_id": "98245"} -{"city": "EVERSON", "loc": [-122.332474, 48.90447], "pop": 6986, "state": "WA", "_id": "98247"} -{"city": "FERNDALE", "loc": [-122.595293, 48.862531], "pop": 13697, "state": "WA", "_id": "98248"} -{"city": "FREELAND", "loc": [-122.564086, 48.03417], "pop": 2321, "state": "WA", "_id": "98249"} -{"city": "FRIDAY HARBOR", "loc": [-123.094717, 48.545416], "pop": 2508, "state": "WA", "_id": "98250"} -{"city": "GRANITE FALLS", "loc": [-121.942752, 48.078977], "pop": 3634, "state": "WA", "_id": "98252"} -{"city": "GREENBANK", "loc": [-122.587086, 48.124432], "pop": 926, "state": "WA", "_id": "98253"} -{"city": "LA CONNER", "loc": [-122.53134, 48.409306], "pop": 3154, "state": "WA", "_id": "98257"} -{"city": "LAKE STEVENS", "loc": [-122.067153, 48.017103], "pop": 12728, "state": "WA", "_id": "98258"} -{"city": "LANGLEY", "loc": [-122.452992, 48.018672], "pop": 6036, "state": "WA", "_id": "98260"} -{"city": "LOPEZ", "loc": [-122.967434, 48.520804], "pop": 4147, "state": "WA", "_id": "98261"} -{"city": "LUMMI ISLAND", "loc": [-122.682285, 48.712765], "pop": 628, "state": "WA", "_id": "98262"} -{"city": "LYMAN", "loc": [-122.016183, 48.525744], "pop": 1887, "state": "WA", "_id": "98263"} -{"city": "LYNDEN", "loc": [-122.459153, 48.937225], "pop": 12896, "state": "WA", "_id": "98264"} -{"city": "MARYSVILLE", "loc": [-122.156168, 48.065639], "pop": 19966, "state": "WA", "_id": "98270"} -{"city": "MARYSVILLE", "loc": [-122.197956, 48.096572], "pop": 19743, "state": "WA", "_id": "98271"} -{"city": "MONROE", "loc": [-121.947376, 47.85853], "pop": 14143, "state": "WA", "_id": "98272"} -{"city": "MOUNT VERNON", "loc": [-122.326548, 48.416427], "pop": 30295, "state": "WA", "_id": "98273"} -{"city": "MUKILTEO", "loc": [-122.301906, 47.919896], "pop": 10373, "state": "WA", "_id": "98275"} -{"city": "OAK HARBOR", "loc": [-122.637439, 48.315096], "pop": 32450, "state": "WA", "_id": "98277"} -{"city": "WHIDBEY ISLAND N", "loc": [-122.69005, 48.295271], "pop": 3434, "state": "WA", "_id": "98278"} -{"city": "OLGA", "loc": [-122.836224, 48.655526], "pop": 1029, "state": "WA", "_id": "98279"} -{"city": "POINT ROBERTS", "loc": [-123.055474, 48.987876], "pop": 923, "state": "WA", "_id": "98281"} -{"city": "ROCKPORT", "loc": [-121.555352, 48.470388], "pop": 157, "state": "WA", "_id": "98283"} -{"city": "SEDRO WOOLLEY", "loc": [-122.232943, 48.527405], "pop": 14902, "state": "WA", "_id": "98284"} -{"city": "SKYKOMISH", "loc": [-121.371297, 47.692158], "pop": 388, "state": "WA", "_id": "98288"} -{"city": "SNOHOMISH", "loc": [-122.071562, 47.895381], "pop": 37327, "state": "WA", "_id": "98290"} -{"city": "STANWOOD", "loc": [-122.377978, 48.201067], "pop": 19288, "state": "WA", "_id": "98292"} -{"city": "SULTAN", "loc": [-121.736869, 47.858942], "pop": 7010, "state": "WA", "_id": "98294"} -{"city": "SUMAS", "loc": [-122.207425, 48.970763], "pop": 2537, "state": "WA", "_id": "98295"} -{"city": "ANDERSON ISLAND", "loc": [-122.696025, 47.15872], "pop": 548, "state": "WA", "_id": "98303"} -{"city": "ASHFORD", "loc": [-121.989791, 46.753121], "pop": 339, "state": "WA", "_id": "98304"} -{"city": "BEAVER", "loc": [-124.305424, 48.067263], "pop": 695, "state": "WA", "_id": "98305"} -{"city": "BREMERTON", "loc": [-122.629913, 47.601916], "pop": 49057, "state": "WA", "_id": "98310"} -{"city": "BREMERTON", "loc": [-122.695786, 47.575424], "pop": 28858, "state": "WA", "_id": "98312"} -{"city": "PUGET SOUND NAVA", "loc": [-122.724354, 47.746255], "pop": 708, "state": "WA", "_id": "98314"} -{"city": "SILVERDALE", "loc": [-122.716106, 47.692017], "pop": 3702, "state": "WA", "_id": "98315"} -{"city": "BRINNON", "loc": [-122.937509, 47.677596], "pop": 1049, "state": "WA", "_id": "98320"} -{"city": "BUCKLEY", "loc": [-122.062098, 47.152449], "pop": 10153, "state": "WA", "_id": "98321"} -{"city": "CARBONADO", "loc": [-122.051339, 47.080242], "pop": 495, "state": "WA", "_id": "98323"} -{"city": "CHIMACUM", "loc": [-122.788323, 47.98607], "pop": 1191, "state": "WA", "_id": "98325"} -{"city": "CLALLAM BAY", "loc": [-124.201512, 48.225486], "pop": 384, "state": "WA", "_id": "98326"} -{"city": "EATONVILLE", "loc": [-122.269626, 46.870778], "pop": 3972, "state": "WA", "_id": "98328"} -{"city": "GIG HARBOR", "loc": [-122.7, 47.378579], "pop": 6678, "state": "WA", "_id": "98329"} -{"city": "ELBE", "loc": [-122.150344, 46.766607], "pop": 103, "state": "WA", "_id": "98330"} -{"city": "FORKS", "loc": [-124.398949, 47.928732], "pop": 6657, "state": "WA", "_id": "98331"} -{"city": "GIG HARBOR", "loc": [-122.600144, 47.3607], "pop": 9138, "state": "WA", "_id": "98332"} -{"city": "FOX ISLAND", "loc": [-122.628579, 47.25238], "pop": 2017, "state": "WA", "_id": "98333"} -{"city": "GIG HARBOR", "loc": [-122.608377, 47.300154], "pop": 17299, "state": "WA", "_id": "98335"} -{"city": "GLENOMA", "loc": [-122.099014, 46.528266], "pop": 657, "state": "WA", "_id": "98336"} -{"city": "GRAHAM", "loc": [-122.293648, 47.024575], "pop": 11136, "state": "WA", "_id": "98338"} -{"city": "PORT HADLOCK", "loc": [-122.768151, 48.034531], "pop": 2574, "state": "WA", "_id": "98339"} -{"city": "HANSVILLE", "loc": [-122.565509, 47.906143], "pop": 1256, "state": "WA", "_id": "98340"} -{"city": "KINGSTON", "loc": [-122.525503, 47.810844], "pop": 5507, "state": "WA", "_id": "98346"} -{"city": "HOME", "loc": [-122.74273, 47.247366], "pop": 3373, "state": "WA", "_id": "98349"} -{"city": "LONGBRANCH", "loc": [-122.756126, 47.200737], "pop": 733, "state": "WA", "_id": "98351"} -{"city": "MILTON", "loc": [-122.315514, 47.24827], "pop": 3562, "state": "WA", "_id": "98354"} -{"city": "MINERAL", "loc": [-122.186056, 46.709635], "pop": 814, "state": "WA", "_id": "98355"} -{"city": "MORTON", "loc": [-122.249573, 46.558056], "pop": 2530, "state": "WA", "_id": "98356"} -{"city": "NORDLAND", "loc": [-122.692553, 48.04321], "pop": 738, "state": "WA", "_id": "98358"} -{"city": "OLALLA", "loc": [-122.574512, 47.424088], "pop": 3173, "state": "WA", "_id": "98359"} -{"city": "ORTING", "loc": [-122.185978, 47.082206], "pop": 4493, "state": "WA", "_id": "98360"} -{"city": "PACKWOOD", "loc": [-121.655254, 46.650038], "pop": 495, "state": "WA", "_id": "98361"} -{"city": "PORT ANGELES", "loc": [-123.438442, 48.106489], "pop": 30373, "state": "WA", "_id": "98362"} -{"city": "PORT LUDLOW", "loc": [-122.689615, 47.922192], "pop": 2229, "state": "WA", "_id": "98365"} -{"city": "SOUTH PARK VILLA", "loc": [-122.615276, 47.504838], "pop": 44359, "state": "WA", "_id": "98366"} -{"city": "PORT TOWNSEND", "loc": [-122.794457, 48.104012], "pop": 9870, "state": "WA", "_id": "98368"} -{"city": "POULSBO", "loc": [-122.627721, 47.742278], "pop": 20554, "state": "WA", "_id": "98370"} -{"city": "PUYALLUP", "loc": [-122.315097, 47.199123], "pop": 18207, "state": "WA", "_id": "98371"} -{"city": "PUYALLUP", "loc": [-122.273415, 47.204202], "pop": 17053, "state": "WA", "_id": "98372"} -{"city": "PUYALLUP", "loc": [-122.321868, 47.128363], "pop": 23219, "state": "WA", "_id": "98373"} -{"city": "PUYALLUP", "loc": [-122.265248, 47.142427], "pop": 21982, "state": "WA", "_id": "98374"} -{"city": "QUILCENE", "loc": [-122.858304, 47.832429], "pop": 1592, "state": "WA", "_id": "98376"} -{"city": "RANDLE", "loc": [-121.855533, 46.549195], "pop": 2620, "state": "WA", "_id": "98377"} -{"city": "SEABECK", "loc": [-122.822685, 47.625497], "pop": 2555, "state": "WA", "_id": "98380"} -{"city": "SEKIU", "loc": [-124.468467, 48.303166], "pop": 2582, "state": "WA", "_id": "98381"} -{"city": "SEQUIM", "loc": [-123.119814, 48.088136], "pop": 16523, "state": "WA", "_id": "98382"} -{"city": "SILVERDALE", "loc": [-122.698054, 47.662139], "pop": 12276, "state": "WA", "_id": "98383"} -{"city": "SPANAWAY", "loc": [-122.394336, 47.073218], "pop": 24035, "state": "WA", "_id": "98387"} -{"city": "STEILACOOM", "loc": [-122.588837, 47.170369], "pop": 6099, "state": "WA", "_id": "98388"} -{"city": "BONNEY LAKE", "loc": [-122.180275, 47.188801], "pop": 35436, "state": "WA", "_id": "98390"} -{"city": "SUQUAMISH", "loc": [-122.557295, 47.734303], "pop": 1880, "state": "WA", "_id": "98392"} -{"city": "VAUGHN", "loc": [-122.773598, 47.330921], "pop": 804, "state": "WA", "_id": "98394"} -{"city": "TACOMA", "loc": [-122.440536, 47.254508], "pop": 2994, "state": "WA", "_id": "98402"} -{"city": "TACOMA", "loc": [-122.457538, 47.26428], "pop": 7493, "state": "WA", "_id": "98403"} -{"city": "TACOMA", "loc": [-122.412625, 47.211312], "pop": 27135, "state": "WA", "_id": "98404"} -{"city": "TACOMA", "loc": [-122.46435, 47.248351], "pop": 23918, "state": "WA", "_id": "98405"} -{"city": "TACOMA", "loc": [-122.499349, 47.26325], "pop": 22971, "state": "WA", "_id": "98406"} -{"city": "TACOMA", "loc": [-122.503881, 47.282479], "pop": 19881, "state": "WA", "_id": "98407"} -{"city": "TACOMA", "loc": [-122.444381, 47.207267], "pop": 28753, "state": "WA", "_id": "98408"} -{"city": "TACOMA", "loc": [-122.482503, 47.20381], "pop": 25045, "state": "WA", "_id": "98409"} -{"city": "TACOMA", "loc": [-122.401457, 47.266373], "pop": 508, "state": "WA", "_id": "98421"} -{"city": "TACOMA", "loc": [-122.398349, 47.294805], "pop": 10385, "state": "WA", "_id": "98422"} -{"city": "FIFE", "loc": [-122.350962, 47.243632], "pop": 5626, "state": "WA", "_id": "98424"} -{"city": "FORT LEWIS", "loc": [-122.583486, 47.100864], "pop": 27463, "state": "WA", "_id": "98433"} -{"city": "LAKEWOOD CENTER", "loc": [-122.529326, 47.122905], "pop": 3064, "state": "WA", "_id": "98439"} -{"city": "TACOMA", "loc": [-122.372815, 47.204369], "pop": 5457, "state": "WA", "_id": "98443"} -{"city": "PARKLAND", "loc": [-122.448842, 47.156553], "pop": 27406, "state": "WA", "_id": "98444"} -{"city": "PARKLAND", "loc": [-122.411614, 47.133967], "pop": 20298, "state": "WA", "_id": "98445"} -{"city": "PARKLAND", "loc": [-122.37189, 47.14041], "pop": 7156, "state": "WA", "_id": "98446"} -{"city": "TACOMA", "loc": [-122.527272, 47.249139], "pop": 6919, "state": "WA", "_id": "98465"} -{"city": "FIRCREST", "loc": [-122.53503, 47.22788], "pop": 22719, "state": "WA", "_id": "98466"} -{"city": "TACOMA", "loc": [-122.533562, 47.205395], "pop": 12823, "state": "WA", "_id": "98467"} -{"city": "LAKEWOOD CENTER", "loc": [-122.555357, 47.164269], "pop": 28193, "state": "WA", "_id": "98498"} -{"city": "LAKEWOOD CENTER", "loc": [-122.509074, 47.160786], "pop": 20970, "state": "WA", "_id": "98499"} -{"city": "OLYMPIA", "loc": [-122.876311, 47.012906], "pop": 25979, "state": "WA", "_id": "98501"} -{"city": "OLYMPIA", "loc": [-122.95214, 47.029828], "pop": 40246, "state": "WA", "_id": "98502"} -{"city": "LACEY", "loc": [-122.782665, 47.023967], "pop": 43492, "state": "WA", "_id": "98503"} -{"city": "LACEY", "loc": [-122.832844, 47.076259], "pop": 24889, "state": "WA", "_id": "98506"} -{"city": "ABERDEEN", "loc": [-123.79629, 46.984293], "pop": 22346, "state": "WA", "_id": "98520"} -{"city": "ALLYN", "loc": [-122.853571, 47.385004], "pop": 2049, "state": "WA", "_id": "98524"} -{"city": "AMANDA PARK", "loc": [-123.907375, 47.470579], "pop": 470, "state": "WA", "_id": "98526"} -{"city": "BEAR CREEK", "loc": [-122.822381, 47.454956], "pop": 2351, "state": "WA", "_id": "98528"} -{"city": "CENTRALIA", "loc": [-122.967068, 46.724635], "pop": 17633, "state": "WA", "_id": "98531"} -{"city": "CHEHALIS", "loc": [-122.965764, 46.638193], "pop": 18065, "state": "WA", "_id": "98532"} -{"city": "CINEBAR", "loc": [-122.566005, 46.567131], "pop": 796, "state": "WA", "_id": "98533"} -{"city": "COPALIS BEACH", "loc": [-124.135881, 47.065044], "pop": 703, "state": "WA", "_id": "98535"} -{"city": "COPALIS CROSSING", "loc": [-124.13471, 47.12535], "pop": 685, "state": "WA", "_id": "98536"} -{"city": "COSMOPOLIS", "loc": [-123.77394, 46.953789], "pop": 1424, "state": "WA", "_id": "98537"} -{"city": "CURTIS", "loc": [-123.156974, 46.558372], "pop": 449, "state": "WA", "_id": "98538"} -{"city": "ELMA", "loc": [-123.39969, 47.005813], "pop": 7356, "state": "WA", "_id": "98541"} -{"city": "ETHEL", "loc": [-122.776009, 46.53581], "pop": 320, "state": "WA", "_id": "98542"} -{"city": "GRAPEVIEW", "loc": [-122.949742, 47.305783], "pop": 3234, "state": "WA", "_id": "98546"} -{"city": "GRAYLAND", "loc": [-124.056194, 46.853201], "pop": 1677, "state": "WA", "_id": "98547"} -{"city": "HOODSPORT", "loc": [-123.173932, 47.423526], "pop": 1165, "state": "WA", "_id": "98548"} -{"city": "HOQUIAM", "loc": [-123.884169, 46.982269], "pop": 9597, "state": "WA", "_id": "98550"} -{"city": "HUMPTULIPS", "loc": [-123.971695, 47.135632], "pop": 1301, "state": "WA", "_id": "98552"} -{"city": "LILLIWAUP", "loc": [-123.063119, 47.512773], "pop": 635, "state": "WA", "_id": "98555"} -{"city": "MC CLEARY", "loc": [-123.273301, 47.053987], "pop": 2644, "state": "WA", "_id": "98557"} -{"city": "MATLOCK", "loc": [-123.337638, 47.177545], "pop": 1716, "state": "WA", "_id": "98560"} -{"city": "MOCLIPS", "loc": [-124.20438, 47.222619], "pop": 574, "state": "WA", "_id": "98562"} -{"city": "MONTESANO", "loc": [-123.500584, 47.09013], "pop": 10079, "state": "WA", "_id": "98563"} -{"city": "MOSSYROCK", "loc": [-122.478935, 46.513136], "pop": 1390, "state": "WA", "_id": "98564"} -{"city": "OAKVILLE", "loc": [-123.249329, 46.843366], "pop": 1915, "state": "WA", "_id": "98568"} -{"city": "OCEAN CITY", "loc": [-124.15323, 46.982905], "pop": 2307, "state": "WA", "_id": "98569"} -{"city": "ONALASKA", "loc": [-122.707503, 46.573016], "pop": 3077, "state": "WA", "_id": "98570"} -{"city": "PACIFIC BEACH", "loc": [-124.158833, 47.198144], "pop": 122, "state": "WA", "_id": "98571"} -{"city": "PE ELL", "loc": [-123.285244, 46.56558], "pop": 921, "state": "WA", "_id": "98572"} -{"city": "QUINAULT", "loc": [-123.803744, 47.448505], "pop": 545, "state": "WA", "_id": "98575"} -{"city": "RAINIER", "loc": [-122.679468, 46.882942], "pop": 2397, "state": "WA", "_id": "98576"} -{"city": "RAYMOND", "loc": [-123.692889, 46.671046], "pop": 6144, "state": "WA", "_id": "98577"} -{"city": "ROCHESTER", "loc": [-123.040634, 46.819295], "pop": 8231, "state": "WA", "_id": "98579"} -{"city": "ROY", "loc": [-122.448271, 46.956048], "pop": 7730, "state": "WA", "_id": "98580"} -{"city": "RYDERWOOD", "loc": [-123.043134, 46.375176], "pop": 330, "state": "WA", "_id": "98581"} -{"city": "SALKUM", "loc": [-122.645364, 46.515059], "pop": 336, "state": "WA", "_id": "98582"} -{"city": "SHELTON", "loc": [-123.072862, 47.20863], "pop": 19074, "state": "WA", "_id": "98584"} -{"city": "SILVER CREEK", "loc": [-122.475716, 46.549077], "pop": 697, "state": "WA", "_id": "98585"} -{"city": "SOUTH BEND", "loc": [-123.820315, 46.6544], "pop": 2575, "state": "WA", "_id": "98586"} -{"city": "TAHOLAH", "loc": [-124.2827, 47.340711], "pop": 851, "state": "WA", "_id": "98587"} -{"city": "TAHUYA", "loc": [-122.921126, 47.435618], "pop": 3794, "state": "WA", "_id": "98588"} -{"city": "TENINO", "loc": [-122.849269, 46.864119], "pop": 6451, "state": "WA", "_id": "98589"} -{"city": "TOKELAND", "loc": [-124.046008, 46.746874], "pop": 891, "state": "WA", "_id": "98590"} -{"city": "TOLEDO", "loc": [-122.826559, 46.439552], "pop": 2829, "state": "WA", "_id": "98591"} -{"city": "UNION", "loc": [-123.034364, 47.351305], "pop": 1592, "state": "WA", "_id": "98592"} -{"city": "VADER", "loc": [-122.958493, 46.398505], "pop": 523, "state": "WA", "_id": "98593"} -{"city": "WESTPORT", "loc": [-124.106055, 46.883619], "pop": 2463, "state": "WA", "_id": "98595"} -{"city": "WINLOCK", "loc": [-122.915806, 46.494014], "pop": 5206, "state": "WA", "_id": "98596"} -{"city": "YELM", "loc": [-122.588049, 46.920589], "pop": 9553, "state": "WA", "_id": "98597"} -{"city": "AMBOY", "loc": [-122.457418, 45.9195], "pop": 1910, "state": "WA", "_id": "98601"} -{"city": "APPLETON", "loc": [-121.148618, 45.909041], "pop": 13, "state": "WA", "_id": "98602"} -{"city": "ARIEL", "loc": [-122.46769, 45.995154], "pop": 735, "state": "WA", "_id": "98603"} -{"city": "BATTLE GROUND", "loc": [-122.531845, 45.790667], "pop": 16072, "state": "WA", "_id": "98604"} -{"city": "COOK", "loc": [-121.175778, 45.6341], "pop": 954, "state": "WA", "_id": "98605"} -{"city": "BRUSH PRAIRIE", "loc": [-122.484342, 45.730432], "pop": 6663, "state": "WA", "_id": "98606"} -{"city": "CAMAS", "loc": [-122.414231, 45.605772], "pop": 12058, "state": "WA", "_id": "98607"} -{"city": "CARSON", "loc": [-121.835138, 45.749332], "pop": 2009, "state": "WA", "_id": "98610"} -{"city": "CASTLE ROCK", "loc": [-122.9139, 46.278291], "pop": 8455, "state": "WA", "_id": "98611"} -{"city": "CATHLAMET", "loc": [-123.362716, 46.195383], "pop": 2114, "state": "WA", "_id": "98612"} -{"city": "CENTERVILLE", "loc": [-120.945973, 45.703183], "pop": 681, "state": "WA", "_id": "98613"} -{"city": "COUGAR", "loc": [-122.294186, 46.069012], "pop": 122, "state": "WA", "_id": "98616"} -{"city": "GLENWOOD", "loc": [-121.28849, 46.007104], "pop": 549, "state": "WA", "_id": "98619"} -{"city": "GOLDENDALE", "loc": [-120.812981, 45.832021], "pop": 5761, "state": "WA", "_id": "98620"} -{"city": "GRAYS RIVER", "loc": [-123.588845, 46.353481], "pop": 209, "state": "WA", "_id": "98621"} -{"city": "ILWACO", "loc": [-124.02822, 46.314214], "pop": 1210, "state": "WA", "_id": "98624"} -{"city": "KALAMA", "loc": [-122.816588, 46.011229], "pop": 3627, "state": "WA", "_id": "98625"} -{"city": "KELSO", "loc": [-122.886994, 46.148491], "pop": 20593, "state": "WA", "_id": "98626"} -{"city": "KLICKITAT", "loc": [-121.229231, 45.751534], "pop": 23, "state": "WA", "_id": "98628"} -{"city": "LA CENTER", "loc": [-122.623972, 45.880587], "pop": 3969, "state": "WA", "_id": "98629"} -{"city": "LONG BEACH", "loc": [-124.047041, 46.377369], "pop": 3648, "state": "WA", "_id": "98631"} -{"city": "LONGVIEW", "loc": [-122.963421, 46.151354], "pop": 42028, "state": "WA", "_id": "98632"} -{"city": "LYLE", "loc": [-121.250112, 45.74495], "pop": 1583, "state": "WA", "_id": "98635"} -{"city": "NASELLE", "loc": [-123.804381, 46.352758], "pop": 2177, "state": "WA", "_id": "98638"} -{"city": "OCEAN PARK", "loc": [-124.043582, 46.502867], "pop": 2601, "state": "WA", "_id": "98640"} -{"city": "RIDGEFIELD", "loc": [-122.693354, 45.784634], "pop": 7845, "state": "WA", "_id": "98642"} -{"city": "ROSBURG", "loc": [-123.657105, 46.307076], "pop": 279, "state": "WA", "_id": "98643"} -{"city": "SILVERLAKE", "loc": [-122.764886, 46.316322], "pop": 927, "state": "WA", "_id": "98645"} -{"city": "SKAMOKAWA", "loc": [-123.43316, 46.295186], "pop": 361, "state": "WA", "_id": "98647"} -{"city": "STEVENSON", "loc": [-121.909346, 45.688173], "pop": 3203, "state": "WA", "_id": "98648"} -{"city": "TOUTLE", "loc": [-122.647696, 46.295605], "pop": 599, "state": "WA", "_id": "98649"} -{"city": "TROUT LAKE", "loc": [-121.516272, 45.982789], "pop": 766, "state": "WA", "_id": "98650"} -{"city": "UNDERWOOD", "loc": [-121.597408, 45.740872], "pop": 1161, "state": "WA", "_id": "98651"} -{"city": "VANCOUVER", "loc": [-122.68014, 45.64183], "pop": 10432, "state": "WA", "_id": "98660"} -{"city": "VANCOUVER", "loc": [-122.625146, 45.641807], "pop": 28837, "state": "WA", "_id": "98661"} -{"city": "ORCHARDS", "loc": [-122.576182, 45.674519], "pop": 17842, "state": "WA", "_id": "98662"} -{"city": "VANCOUVER", "loc": [-122.660385, 45.6514], "pop": 13198, "state": "WA", "_id": "98663"} -{"city": "VANCOUVER", "loc": [-122.576741, 45.623086], "pop": 17179, "state": "WA", "_id": "98664"} -{"city": "HAZEL DELL", "loc": [-122.664223, 45.68217], "pop": 16488, "state": "WA", "_id": "98665"} -{"city": "WAHKIACUS", "loc": [-121.148586, 45.815567], "pop": 442, "state": "WA", "_id": "98670"} -{"city": "WASHOUGAL", "loc": [-122.310396, 45.595921], "pop": 10873, "state": "WA", "_id": "98671"} -{"city": "WHITE SALMON", "loc": [-121.479459, 45.755142], "pop": 5396, "state": "WA", "_id": "98672"} -{"city": "WOODLAND", "loc": [-122.71256, 45.921859], "pop": 6266, "state": "WA", "_id": "98674"} -{"city": "YACOLT", "loc": [-122.427545, 45.862247], "pop": 3295, "state": "WA", "_id": "98675"} -{"city": "VANCOUVER", "loc": [-122.521224, 45.664399], "pop": 21359, "state": "WA", "_id": "98682"} -{"city": "CASCADE PARK", "loc": [-122.524969, 45.617522], "pop": 26400, "state": "WA", "_id": "98684"} -{"city": "FELIDA", "loc": [-122.682474, 45.707313], "pop": 13972, "state": "WA", "_id": "98685"} -{"city": "VANCOUVER", "loc": [-122.632226, 45.712017], "pop": 9966, "state": "WA", "_id": "98686"} -{"city": "WENATCHEE", "loc": [-120.327345, 47.425269], "pop": 28906, "state": "WA", "_id": "98801"} -{"city": "EAST WENATCHEE", "loc": [-120.273136, 47.418596], "pop": 17975, "state": "WA", "_id": "98802"} -{"city": "BREWSTER", "loc": [-119.771999, 48.120641], "pop": 3177, "state": "WA", "_id": "98812"} -{"city": "BRIDGEPORT", "loc": [-119.702772, 48.016083], "pop": 2757, "state": "WA", "_id": "98813"} -{"city": "CARLTON", "loc": [-120.10551, 48.252615], "pop": 332, "state": "WA", "_id": "98814"} -{"city": "CASHMERE", "loc": [-120.503274, 47.517293], "pop": 7504, "state": "WA", "_id": "98815"} -{"city": "CHELAN", "loc": [-120.027306, 47.848263], "pop": 4949, "state": "WA", "_id": "98816"} -{"city": "ENTIAT", "loc": [-120.276031, 47.705653], "pop": 1507, "state": "WA", "_id": "98822"} -{"city": "EPHRATA", "loc": [-119.533582, 47.277051], "pop": 8779, "state": "WA", "_id": "98823"} -{"city": "LEAVENWORTH", "loc": [-120.674792, 47.6438], "pop": 4288, "state": "WA", "_id": "98826"} -{"city": "LOOMIS", "loc": [-119.642675, 48.869627], "pop": 329, "state": "WA", "_id": "98827"} -{"city": "MALAGA", "loc": [-120.208562, 47.355306], "pop": 1633, "state": "WA", "_id": "98828"} -{"city": "MANSFIELD", "loc": [-119.405315, 47.902136], "pop": 960, "state": "WA", "_id": "98830"} -{"city": "MANSON", "loc": [-120.148963, 47.895764], "pop": 2309, "state": "WA", "_id": "98831"} -{"city": "MARLIN", "loc": [-119.090897, 47.320929], "pop": 856, "state": "WA", "_id": "98832"} -{"city": "MAZAMA", "loc": [-120.38796, 48.597728], "pop": 115, "state": "WA", "_id": "98833"} -{"city": "METHOW", "loc": [-120.0059, 48.09001], "pop": 623, "state": "WA", "_id": "98834"} -{"city": "MOSES LAKE", "loc": [-119.289149, 47.137363], "pop": 22935, "state": "WA", "_id": "98837"} -{"city": "OKANOGAN", "loc": [-119.604563, 48.351328], "pop": 3782, "state": "WA", "_id": "98840"} -{"city": "OMAK", "loc": [-119.527156, 48.414347], "pop": 6059, "state": "WA", "_id": "98841"} -{"city": "ORONDO", "loc": [-120.172143, 47.696928], "pop": 1424, "state": "WA", "_id": "98843"} -{"city": "OROVILLE", "loc": [-119.403236, 48.939681], "pop": 3283, "state": "WA", "_id": "98844"} -{"city": "PALISADES", "loc": [-119.802264, 47.469367], "pop": 36, "state": "WA", "_id": "98845"} -{"city": "PATEROS", "loc": [-119.913322, 48.059147], "pop": 696, "state": "WA", "_id": "98846"} -{"city": "PESHASTIN", "loc": [-120.613928, 47.581294], "pop": 1030, "state": "WA", "_id": "98847"} -{"city": "QUINCY", "loc": [-119.845922, 47.197574], "pop": 7429, "state": "WA", "_id": "98848"} -{"city": "RIVERSIDE", "loc": [-119.580316, 48.487567], "pop": 1871, "state": "WA", "_id": "98849"} -{"city": "ROCK ISLAND", "loc": [-120.137794, 47.370558], "pop": 1336, "state": "WA", "_id": "98850"} -{"city": "SOAP LAKE", "loc": [-119.485962, 47.383034], "pop": 2482, "state": "WA", "_id": "98851"} -{"city": "STEHEKIN", "loc": [-120.755185, 48.298034], "pop": 124, "state": "WA", "_id": "98852"} -{"city": "TONASKET", "loc": [-119.394252, 48.71944], "pop": 4921, "state": "WA", "_id": "98855"} -{"city": "TWISP", "loc": [-120.135035, 48.363231], "pop": 1938, "state": "WA", "_id": "98856"} -{"city": "WARDEN", "loc": [-119.053932, 46.97697], "pop": 2678, "state": "WA", "_id": "98857"} -{"city": "WATERVILLE", "loc": [-119.988743, 47.62951], "pop": 1717, "state": "WA", "_id": "98858"} -{"city": "WAUCONDA", "loc": [-118.946947, 48.822449], "pop": 325, "state": "WA", "_id": "98859"} -{"city": "WINTHROP", "loc": [-120.180468, 48.475607], "pop": 1263, "state": "WA", "_id": "98862"} -{"city": "TERRACE HEIGHTS", "loc": [-120.477336, 46.606991], "pop": 22057, "state": "WA", "_id": "98901"} -{"city": "YAKIMA", "loc": [-120.531084, 46.593393], "pop": 39091, "state": "WA", "_id": "98902"} -{"city": "UNION GAP", "loc": [-120.556587, 46.5572], "pop": 10498, "state": "WA", "_id": "98903"} -{"city": "WIDE HOLLOW", "loc": [-120.605175, 46.605865], "pop": 27078, "state": "WA", "_id": "98908"} -{"city": "CLE ELUM", "loc": [-120.968505, 47.206319], "pop": 5299, "state": "WA", "_id": "98922"} -{"city": "COWICHE", "loc": [-120.714893, 46.66611], "pop": 920, "state": "WA", "_id": "98923"} -{"city": "ELLENSBURG", "loc": [-120.516274, 46.999632], "pop": 20344, "state": "WA", "_id": "98926"} -{"city": "GRANDVIEW", "loc": [-119.915734, 46.253846], "pop": 10558, "state": "WA", "_id": "98930"} -{"city": "GRANGER", "loc": [-120.181848, 46.348045], "pop": 3562, "state": "WA", "_id": "98932"} -{"city": "HARRAH", "loc": [-120.573606, 46.410383], "pop": 2643, "state": "WA", "_id": "98933"} -{"city": "MABTON", "loc": [-120.015141, 46.212082], "pop": 3586, "state": "WA", "_id": "98935"} -{"city": "MOXEE", "loc": [-120.368463, 46.554205], "pop": 4194, "state": "WA", "_id": "98936"} -{"city": "WHITE PASS", "loc": [-120.826699, 46.735335], "pop": 2996, "state": "WA", "_id": "98937"} -{"city": "OUTLOOK", "loc": [-120.097005, 46.352497], "pop": 1705, "state": "WA", "_id": "98938"} -{"city": "SELAH", "loc": [-120.540813, 46.67671], "pop": 12507, "state": "WA", "_id": "98942"} -{"city": "SUNNYSIDE", "loc": [-120.012631, 46.321273], "pop": 16774, "state": "WA", "_id": "98944"} -{"city": "THORP", "loc": [-120.678557, 47.017785], "pop": 1082, "state": "WA", "_id": "98946"} -{"city": "TIETON", "loc": [-120.747275, 46.706331], "pop": 2079, "state": "WA", "_id": "98947"} -{"city": "TOPPENISH", "loc": [-120.330534, 46.375123], "pop": 11101, "state": "WA", "_id": "98948"} -{"city": "WAPATO", "loc": [-120.426484, 46.45066], "pop": 10262, "state": "WA", "_id": "98951"} -{"city": "WHITE SWAN", "loc": [-120.745317, 46.371558], "pop": 1941, "state": "WA", "_id": "98952"} -{"city": "ZILLAH", "loc": [-120.266161, 46.415777], "pop": 5266, "state": "WA", "_id": "98953"} -{"city": "CHATTAROY", "loc": [-117.29209, 47.919178], "pop": 1991, "state": "WA", "_id": "99003"} -{"city": "CHENEY", "loc": [-117.583372, 47.494257], "pop": 12257, "state": "WA", "_id": "99004"} -{"city": "COLBERT", "loc": [-117.375895, 47.841093], "pop": 3926, "state": "WA", "_id": "99005"} -{"city": "DEER PARK", "loc": [-117.443559, 47.948615], "pop": 7287, "state": "WA", "_id": "99006"} -{"city": "EDWALL", "loc": [-117.907095, 47.537896], "pop": 379, "state": "WA", "_id": "99008"} -{"city": "ELK", "loc": [-117.296253, 48.020523], "pop": 2386, "state": "WA", "_id": "99009"} -{"city": "FAIRCHILD AIR FO", "loc": [-117.643746, 47.613068], "pop": 4854, "state": "WA", "_id": "99011"} -{"city": "FAIRFIELD", "loc": [-117.192054, 47.398726], "pop": 641, "state": "WA", "_id": "99012"} -{"city": "FORD", "loc": [-117.811858, 47.916873], "pop": 790, "state": "WA", "_id": "99013"} -{"city": "GREENACRES", "loc": [-117.156801, 47.658357], "pop": 5312, "state": "WA", "_id": "99016"} -{"city": "LAMONT", "loc": [-117.830239, 47.17304], "pop": 317, "state": "WA", "_id": "99017"} -{"city": "LATAH", "loc": [-117.16884, 47.303815], "pop": 477, "state": "WA", "_id": "99018"} -{"city": "LIBERTY LAKE", "loc": [-117.083808, 47.651672], "pop": 2372, "state": "WA", "_id": "99019"} -{"city": "MEAD", "loc": [-117.311716, 47.793268], "pop": 5903, "state": "WA", "_id": "99021"} -{"city": "ESPANOLA", "loc": [-117.679351, 47.583696], "pop": 6436, "state": "WA", "_id": "99022"} -{"city": "MICA", "loc": [-117.163711, 47.553814], "pop": 921, "state": "WA", "_id": "99023"} -{"city": "NEWMAN LAKE", "loc": [-117.064041, 47.727371], "pop": 3092, "state": "WA", "_id": "99025"} -{"city": "NINE MILE FALLS", "loc": [-117.589359, 47.801945], "pop": 4872, "state": "WA", "_id": "99026"} -{"city": "OTIS ORCHARDS", "loc": [-117.11209, 47.70273], "pop": 5922, "state": "WA", "_id": "99027"} -{"city": "REARDAN", "loc": [-117.866264, 47.705407], "pop": 1072, "state": "WA", "_id": "99029"} -{"city": "ROCKFORD", "loc": [-117.131842, 47.452804], "pop": 844, "state": "WA", "_id": "99030"} -{"city": "SPANGLE", "loc": [-117.382696, 47.4338], "pop": 1434, "state": "WA", "_id": "99031"} -{"city": "SPRAGUE", "loc": [-117.989684, 47.324725], "pop": 672, "state": "WA", "_id": "99032"} -{"city": "TEKOA", "loc": [-117.081919, 47.227081], "pop": 950, "state": "WA", "_id": "99033"} -{"city": "TUMTUM", "loc": [-117.698996, 47.898173], "pop": 312, "state": "WA", "_id": "99034"} -{"city": "VALLEYFORD", "loc": [-117.268601, 47.529176], "pop": 698, "state": "WA", "_id": "99036"} -{"city": "VERADALE", "loc": [-117.197706, 47.642103], "pop": 8397, "state": "WA", "_id": "99037"} -{"city": "WAVERLY", "loc": [-117.233108, 47.335393], "pop": 166, "state": "WA", "_id": "99039"} -{"city": "WELLPINIT", "loc": [-117.985646, 47.86974], "pop": 488, "state": "WA", "_id": "99040"} -{"city": "ADDY", "loc": [-117.892383, 48.44769], "pop": 1226, "state": "WA", "_id": "99101"} -{"city": "ALMIRA", "loc": [-118.91225, 47.763175], "pop": 646, "state": "WA", "_id": "99103"} -{"city": "BENGE", "loc": [-117.969895, 46.867636], "pop": 2, "state": "WA", "_id": "99105"} -{"city": "BOYDS", "loc": [-118.19906, 48.691919], "pop": 782, "state": "WA", "_id": "99107"} -{"city": "CHEWELAH", "loc": [-117.77539, 48.287585], "pop": 5239, "state": "WA", "_id": "99109"} -{"city": "CLAYTON", "loc": [-117.574021, 47.955206], "pop": 1621, "state": "WA", "_id": "99110"} -{"city": "COLFAX", "loc": [-117.366975, 46.879996], "pop": 3592, "state": "WA", "_id": "99111"} -{"city": "COLTON", "loc": [-117.169243, 46.590098], "pop": 650, "state": "WA", "_id": "99113"} -{"city": "COLVILLE", "loc": [-117.864463, 48.57799], "pop": 8805, "state": "WA", "_id": "99114"} -{"city": "COULEE CITY", "loc": [-119.27582, 47.596571], "pop": 1013, "state": "WA", "_id": "99115"} -{"city": "COULEE DAM", "loc": [-119.180907, 48.173861], "pop": 4636, "state": "WA", "_id": "99116"} -{"city": "CRESTON", "loc": [-118.530656, 47.797681], "pop": 527, "state": "WA", "_id": "99117"} -{"city": "CURLEW", "loc": [-118.645182, 48.910775], "pop": 986, "state": "WA", "_id": "99118"} -{"city": "CUSICK", "loc": [-117.329464, 48.391513], "pop": 439, "state": "WA", "_id": "99119"} -{"city": "DANVILLE", "loc": [-118.488408, 48.972524], "pop": 48, "state": "WA", "_id": "99121"} -{"city": "DAVENPORT", "loc": [-118.166657, 47.680855], "pop": 2383, "state": "WA", "_id": "99122"} -{"city": "ELECTRIC CITY", "loc": [-119.036728, 47.926446], "pop": 1152, "state": "WA", "_id": "99123"} -{"city": "ENDICOTT", "loc": [-117.723005, 46.936407], "pop": 673, "state": "WA", "_id": "99125"} -{"city": "EVANS", "loc": [-118.00012, 48.745787], "pop": 350, "state": "WA", "_id": "99126"} -{"city": "FARMINGTON", "loc": [-117.076327, 47.084742], "pop": 355, "state": "WA", "_id": "99128"} -{"city": "FRUITLAND", "loc": [-118.215906, 47.979746], "pop": 515, "state": "WA", "_id": "99129"} -{"city": "GARFIELD", "loc": [-117.152293, 46.994639], "pop": 795, "state": "WA", "_id": "99130"} -{"city": "GIFFORD", "loc": [-118.12989, 48.22287], "pop": 60, "state": "WA", "_id": "99131"} -{"city": "GRAND COULEE", "loc": [-118.997835, 47.938511], "pop": 1073, "state": "WA", "_id": "99133"} -{"city": "HARRINGTON", "loc": [-118.277793, 47.4555], "pop": 786, "state": "WA", "_id": "99134"} -{"city": "HARTLINE", "loc": [-119.104467, 47.725631], "pop": 315, "state": "WA", "_id": "99135"} -{"city": "HUNTERS", "loc": [-118.152491, 48.133261], "pop": 349, "state": "WA", "_id": "99137"} -{"city": "INCHELIUM", "loc": [-118.355166, 48.292411], "pop": 1297, "state": "WA", "_id": "99138"} -{"city": "IONE", "loc": [-117.404859, 48.713023], "pop": 1204, "state": "WA", "_id": "99139"} -{"city": "KELLER", "loc": [-118.654731, 48.023594], "pop": 255, "state": "WA", "_id": "99140"} -{"city": "KETTLE FALLS", "loc": [-118.054801, 48.636375], "pop": 3668, "state": "WA", "_id": "99141"} -{"city": "LACROSSE", "loc": [-117.770277, 46.771684], "pop": 1066, "state": "WA", "_id": "99143"} -{"city": "LINCOLN", "loc": [-118.481012, 47.78204], "pop": 31, "state": "WA", "_id": "99147"} -{"city": "LOON LAKE", "loc": [-117.632496, 48.078393], "pop": 1885, "state": "WA", "_id": "99148"} -{"city": "MALO", "loc": [-118.623712, 48.81862], "pop": 396, "state": "WA", "_id": "99150"} -{"city": "METALINE FALLS", "loc": [-117.36332, 48.859747], "pop": 729, "state": "WA", "_id": "99153"} -{"city": "NEWPORT", "loc": [-117.150784, 48.169465], "pop": 5881, "state": "WA", "_id": "99156"} -{"city": "NORTHPORT", "loc": [-117.793052, 48.924663], "pop": 462, "state": "WA", "_id": "99157"} -{"city": "OAKESDALE", "loc": [-117.280326, 47.080556], "pop": 764, "state": "WA", "_id": "99158"} -{"city": "ODESSA", "loc": [-118.698316, 47.339491], "pop": 1324, "state": "WA", "_id": "99159"} -{"city": "PALOUSE", "loc": [-117.085475, 46.907555], "pop": 1270, "state": "WA", "_id": "99161"} -{"city": "PULLMAN", "loc": [-117.172936, 46.735247], "pop": 25909, "state": "WA", "_id": "99163"} -{"city": "REPUBLIC", "loc": [-118.699942, 48.670366], "pop": 2531, "state": "WA", "_id": "99166"} -{"city": "RICE", "loc": [-118.124865, 48.406169], "pop": 615, "state": "WA", "_id": "99167"} -{"city": "RITZVILLE", "loc": [-118.395812, 47.131528], "pop": 2472, "state": "WA", "_id": "99169"} -{"city": "ROSALIA", "loc": [-117.41468, 47.221777], "pop": 1081, "state": "WA", "_id": "99170"} -{"city": "SAINT JOHN", "loc": [-117.573002, 47.075539], "pop": 765, "state": "WA", "_id": "99171"} -{"city": "SPRINGDALE", "loc": [-117.873273, 47.992737], "pop": 361, "state": "WA", "_id": "99173"} -{"city": "THORNTON", "loc": [-117.386416, 47.12525], "pop": 186, "state": "WA", "_id": "99176"} -{"city": "UNIONTOWN", "loc": [-117.090756, 46.525818], "pop": 433, "state": "WA", "_id": "99179"} -{"city": "USK", "loc": [-117.318947, 48.295969], "pop": 501, "state": "WA", "_id": "99180"} -{"city": "VALLEY", "loc": [-117.760967, 48.135114], "pop": 1328, "state": "WA", "_id": "99181"} -{"city": "WILBUR", "loc": [-118.706271, 47.741012], "pop": 1238, "state": "WA", "_id": "99185"} -{"city": "SPOKANE", "loc": [-117.436527, 47.666485], "pop": 9599, "state": "WA", "_id": "99201"} -{"city": "SPOKANE", "loc": [-117.380972, 47.654741], "pop": 17424, "state": "WA", "_id": "99202"} -{"city": "SPOKANE", "loc": [-117.404121, 47.629443], "pop": 20454, "state": "WA", "_id": "99203"} -{"city": "SPOKANE", "loc": [-117.471896, 47.640682], "pop": 24611, "state": "WA", "_id": "99204"} -{"city": "SPOKANE", "loc": [-117.439912, 47.69641], "pop": 42032, "state": "WA", "_id": "99205"} -{"city": "SPOKANE", "loc": [-117.258126, 47.649588], "pop": 29077, "state": "WA", "_id": "99206"} -{"city": "SPOKANE", "loc": [-117.374565, 47.697712], "pop": 46237, "state": "WA", "_id": "99207"} -{"city": "SPOKANE", "loc": [-117.435207, 47.737434], "pop": 27989, "state": "WA", "_id": "99208"} -{"city": "SPOKANE", "loc": [-117.304853, 47.668598], "pop": 16771, "state": "WA", "_id": "99212"} -{"city": "SPOKANE", "loc": [-117.219307, 47.663389], "pop": 18834, "state": "WA", "_id": "99216"} -{"city": "SPOKANE", "loc": [-117.4146, 47.755648], "pop": 11902, "state": "WA", "_id": "99218"} -{"city": "SPOKANE", "loc": [-117.362215, 47.61558], "pop": 19056, "state": "WA", "_id": "99223"} -{"city": "PASCO", "loc": [-119.104387, 46.249183], "pop": 33988, "state": "WA", "_id": "99301"} -{"city": "BENTON CITY", "loc": [-119.491349, 46.280624], "pop": 5047, "state": "WA", "_id": "99320"} -{"city": "BEVERLY", "loc": [-119.912074, 46.848429], "pop": 315, "state": "WA", "_id": "99321"} -{"city": "BICKLETON", "loc": [-120.104223, 45.959687], "pop": 329, "state": "WA", "_id": "99322"} -{"city": "COLLEGE PLACE", "loc": [-118.385338, 46.045723], "pop": 6904, "state": "WA", "_id": "99324"} -{"city": "CONNELL", "loc": [-118.85454, 46.66426], "pop": 2699, "state": "WA", "_id": "99326"} -{"city": "CUNNINGHAM", "loc": [-119.107604, 46.757333], "pop": 511, "state": "WA", "_id": "99327"} -{"city": "DAYTON", "loc": [-117.973791, 46.307459], "pop": 3373, "state": "WA", "_id": "99328"} -{"city": "ELTOPIA", "loc": [-119.101333, 46.474996], "pop": 758, "state": "WA", "_id": "99330"} -{"city": "KENNEWICK", "loc": [-119.167951, 46.210913], "pop": 33860, "state": "WA", "_id": "99336"} -{"city": "KENNEWICK", "loc": [-119.138289, 46.181387], "pop": 25962, "state": "WA", "_id": "99337"} -{"city": "LIND", "loc": [-118.706057, 46.955954], "pop": 1134, "state": "WA", "_id": "99341"} -{"city": "MESA", "loc": [-119.137324, 46.578223], "pop": 2382, "state": "WA", "_id": "99343"} -{"city": "MATTAWA", "loc": [-119.316405, 46.792518], "pop": 11812, "state": "WA", "_id": "99344"} -{"city": "PATERSON", "loc": [-119.755873, 45.991139], "pop": 94, "state": "WA", "_id": "99345"} -{"city": "PLYMOUTH", "loc": [-119.502998, 46.038184], "pop": 219, "state": "WA", "_id": "99346"} -{"city": "POMEROY", "loc": [-117.599282, 46.469838], "pop": 2467, "state": "WA", "_id": "99347"} -{"city": "PRESCOTT", "loc": [-118.409663, 46.353879], "pop": 638, "state": "WA", "_id": "99348"} -{"city": "PROSSER", "loc": [-119.771014, 46.223183], "pop": 9714, "state": "WA", "_id": "99350"} -{"city": "RICHLAND", "loc": [-119.289201, 46.283265], "pop": 37664, "state": "WA", "_id": "99352"} -{"city": "ROOSEVELT", "loc": [-120.356611, 45.82962], "pop": 172, "state": "WA", "_id": "99356"} -{"city": "ROYAL CITY", "loc": [-119.581473, 46.91557], "pop": 3388, "state": "WA", "_id": "99357"} -{"city": "LOWDEN", "loc": [-118.655411, 46.04851], "pop": 1234, "state": "WA", "_id": "99360"} -{"city": "WAITSBURG", "loc": [-118.144734, 46.269092], "pop": 2138, "state": "WA", "_id": "99361"} -{"city": "WALLA WALLA", "loc": [-118.331544, 46.061373], "pop": 34993, "state": "WA", "_id": "99362"} -{"city": "WASHTUCNA", "loc": [-118.286203, 46.820912], "pop": 625, "state": "WA", "_id": "99371"} -{"city": "ANATONE", "loc": [-117.088316, 46.128466], "pop": 141, "state": "WA", "_id": "99401"} -{"city": "ASOTIN", "loc": [-117.001548, 46.134318], "pop": 89, "state": "WA", "_id": "99402"} -{"city": "CLARKSTON", "loc": [-117.064457, 46.394622], "pop": 17375, "state": "WA", "_id": "99403"} -{"city": "BLUEWELL", "loc": [-81.229023, 37.279788], "pop": 22561, "state": "WV", "_id": "24701"} -{"city": "ATHENS", "loc": [-80.997362, 37.432298], "pop": 2863, "state": "WV", "_id": "24712"} -{"city": "BEESON", "loc": [-81.206215, 37.490894], "pop": 140, "state": "WV", "_id": "24714"} -{"city": "BRAMWELL", "loc": [-81.32556, 37.332114], "pop": 762, "state": "WV", "_id": "24715"} -{"city": "HERNDON", "loc": [-81.346777, 37.526431], "pop": 2220, "state": "WV", "_id": "24726"} -{"city": "KEGLEY", "loc": [-81.140117, 37.408384], "pop": 259, "state": "WV", "_id": "24731"} -{"city": "LASHMEET", "loc": [-81.213845, 37.441997], "pop": 730, "state": "WV", "_id": "24733"} -{"city": "DOTT", "loc": [-81.268017, 37.441778], "pop": 1365, "state": "WV", "_id": "24736"} -{"city": "ELGOOD", "loc": [-81.086386, 37.366817], "pop": 29167, "state": "WV", "_id": "24740"} -{"city": "DUHRING", "loc": [-81.218766, 37.38889], "pop": 5336, "state": "WV", "_id": "24747"} -{"city": "WELCH", "loc": [-81.534412, 37.400032], "pop": 11143, "state": "WV", "_id": "24801"} -{"city": "MC DOWELL", "loc": [-81.387601, 37.414348], "pop": 1968, "state": "WV", "_id": "24810"} -{"city": "BRENTON", "loc": [-81.649502, 37.584362], "pop": 1908, "state": "WV", "_id": "24818"} -{"city": "VALLSCREEK", "loc": [-81.69552, 37.295548], "pop": 4476, "state": "WV", "_id": "24819"} -{"city": "CLEAR FORK", "loc": [-81.689268, 37.646301], "pop": 1173, "state": "WV", "_id": "24822"} -{"city": "COAL MOUNTAIN", "loc": [-81.707862, 37.688486], "pop": 850, "state": "WV", "_id": "24823"} -{"city": "CYCLONE", "loc": [-81.650458, 37.74466], "pop": 1328, "state": "WV", "_id": "24827"} -{"city": "ASCO", "loc": [-81.667889, 37.468339], "pop": 2765, "state": "WV", "_id": "24828"} -{"city": "FANROCK", "loc": [-81.623631, 37.562721], "pop": 120, "state": "WV", "_id": "24834"} -{"city": "HANOVER", "loc": [-81.813617, 37.563029], "pop": 1523, "state": "WV", "_id": "24839"} -{"city": "IAEGER", "loc": [-81.811992, 37.449878], "pop": 5720, "state": "WV", "_id": "24844"} -{"city": "JESSE", "loc": [-81.562831, 37.660844], "pop": 585, "state": "WV", "_id": "24849"} -{"city": "JOLO", "loc": [-81.831602, 37.332431], "pop": 3086, "state": "WV", "_id": "24850"} -{"city": "MARIANNA", "loc": [-81.576052, 37.611425], "pop": 1008, "state": "WV", "_id": "24859"} -{"city": "MATHENY", "loc": [-81.595711, 37.661956], "pop": 1139, "state": "WV", "_id": "24860"} -{"city": "MOHAWK", "loc": [-81.914174, 37.487926], "pop": 1705, "state": "WV", "_id": "24862"} -{"city": "ALGOMA", "loc": [-81.415839, 37.395262], "pop": 2158, "state": "WV", "_id": "24868"} -{"city": "NORTH SPRING", "loc": [-81.797276, 37.517954], "pop": 226, "state": "WV", "_id": "24869"} -{"city": "OCEANA", "loc": [-81.621564, 37.703874], "pop": 4243, "state": "WV", "_id": "24870"} -{"city": "PAYNESVILLE", "loc": [-81.68449, 37.365273], "pop": 1895, "state": "WV", "_id": "24873"} -{"city": "PINEVILLE", "loc": [-81.533642, 37.584022], "pop": 4142, "state": "WV", "_id": "24874"} -{"city": "SIMON", "loc": [-81.758566, 37.619708], "pop": 782, "state": "WV", "_id": "24882"} -{"city": "SQUIRE", "loc": [-81.580478, 37.237019], "pop": 995, "state": "WV", "_id": "24884"} -{"city": "LEWISBURG", "loc": [-80.440669, 37.808253], "pop": 8645, "state": "WV", "_id": "24901"} -{"city": "DAWSON", "loc": [-80.671306, 37.730148], "pop": 4475, "state": "WV", "_id": "24910"} -{"city": "ARBOVALE", "loc": [-79.793403, 38.454575], "pop": 528, "state": "WV", "_id": "24915"} -{"city": "ASBURY", "loc": [-80.565319, 37.848766], "pop": 598, "state": "WV", "_id": "24916"} -{"city": "AUTO", "loc": [-80.117105, 37.981697], "pop": 379, "state": "WV", "_id": "24917"} -{"city": "BALLARD", "loc": [-80.76139, 37.49803], "pop": 1194, "state": "WV", "_id": "24918"} -{"city": "BALLENGEE", "loc": [-80.740339, 37.616931], "pop": 184, "state": "WV", "_id": "24919"} -{"city": "BARTOW", "loc": [-79.795242, 38.551266], "pop": 748, "state": "WV", "_id": "24920"} -{"city": "BOZOO", "loc": [-80.814954, 37.457015], "pop": 651, "state": "WV", "_id": "24923"} -{"city": "BUCKEYE", "loc": [-80.139342, 38.181506], "pop": 550, "state": "WV", "_id": "24924"} -{"city": "CALDWELL", "loc": [-80.377298, 37.773488], "pop": 312, "state": "WV", "_id": "24925"} -{"city": "STONY BOTTOM", "loc": [-79.885278, 38.433659], "pop": 844, "state": "WV", "_id": "24927"} -{"city": "CLINTONVILLE", "loc": [-80.626192, 37.906141], "pop": 373, "state": "WV", "_id": "24928"} -{"city": "CRAWLEY", "loc": [-80.6309, 37.939676], "pop": 286, "state": "WV", "_id": "24931"} -{"city": "DUNMORE", "loc": [-79.855557, 38.363605], "pop": 265, "state": "WV", "_id": "24934"} -{"city": "INDIAN MILLS", "loc": [-80.814923, 37.56005], "pop": 368, "state": "WV", "_id": "24935"} -{"city": "FORT SPRING", "loc": [-80.566723, 37.745661], "pop": 397, "state": "WV", "_id": "24936"} -{"city": "ANTHONY", "loc": [-80.364692, 37.90361], "pop": 814, "state": "WV", "_id": "24938"} -{"city": "24939", "loc": [-80.458919, 38.027868], "pop": 141, "state": "WV", "_id": "24939"} -{"city": "GAP MILLS", "loc": [-80.390497, 37.527978], "pop": 289, "state": "WV", "_id": "24941"} -{"city": "GLACE", "loc": [-80.374716, 37.609381], "pop": 399, "state": "WV", "_id": "24942"} -{"city": "GRASSY MEADOWS", "loc": [-80.73024, 37.842757], "pop": 185, "state": "WV", "_id": "24943"} -{"city": "GREEN BANK", "loc": [-79.801535, 38.409664], "pop": 365, "state": "WV", "_id": "24944"} -{"city": "GREENVILLE", "loc": [-80.676128, 37.521184], "pop": 1139, "state": "WV", "_id": "24945"} -{"city": "DROOP", "loc": [-80.244578, 38.125942], "pop": 1393, "state": "WV", "_id": "24946"} -{"city": "KIEFFER", "loc": [-80.59022, 37.942998], "pop": 2, "state": "WV", "_id": "24950"} -{"city": "LINDSIDE", "loc": [-80.62028, 37.481651], "pop": 810, "state": "WV", "_id": "24951"} -{"city": "MINNEHAHA SPRING", "loc": [-80.060884, 38.229303], "pop": 3713, "state": "WV", "_id": "24954"} -{"city": "MAXWELTON", "loc": [-80.427492, 37.900465], "pop": 139, "state": "WV", "_id": "24957"} -{"city": "MEADOW BLUFF", "loc": [-80.728162, 37.89587], "pop": 233, "state": "WV", "_id": "24958"} -{"city": "PENCE SPRINGS", "loc": [-80.709234, 37.676694], "pop": 310, "state": "WV", "_id": "24962"} -{"city": "PETERSTOWN", "loc": [-80.768605, 37.414709], "pop": 3117, "state": "WV", "_id": "24963"} -{"city": "RENICK", "loc": [-80.365294, 37.992168], "pop": 1530, "state": "WV", "_id": "24966"} -{"city": "RONCEVERTE", "loc": [-80.462583, 37.741758], "pop": 4167, "state": "WV", "_id": "24970"} -{"city": "SECONDCREEK", "loc": [-80.418079, 37.660246], "pop": 452, "state": "WV", "_id": "24974"} -{"city": "PICKAWAY", "loc": [-80.516905, 37.662078], "pop": 971, "state": "WV", "_id": "24976"} -{"city": "SMOOT", "loc": [-80.668038, 37.877166], "pop": 634, "state": "WV", "_id": "24977"} -{"city": "SWEET SPRINGS", "loc": [-80.298959, 37.611159], "pop": 571, "state": "WV", "_id": "24980"} -{"city": "TALCOTT", "loc": [-80.746143, 37.649079], "pop": 511, "state": "WV", "_id": "24981"} -{"city": "UNION", "loc": [-80.524767, 37.580175], "pop": 1737, "state": "WV", "_id": "24983"} -{"city": "WAITEVILLE", "loc": [-80.463447, 37.464514], "pop": 62, "state": "WV", "_id": "24984"} -{"city": "WAYSIDE", "loc": [-80.745806, 37.593657], "pop": 421, "state": "WV", "_id": "24985"} -{"city": "NEOLA", "loc": [-80.27464, 37.814618], "pop": 5096, "state": "WV", "_id": "24986"} -{"city": "TROUT", "loc": [-80.517321, 37.94421], "pop": 998, "state": "WV", "_id": "24991"} -{"city": "WOLFCREEK", "loc": [-80.611254, 37.618788], "pop": 249, "state": "WV", "_id": "24993"} -{"city": "ALUM CREEK", "loc": [-81.786258, 38.285814], "pop": 1450, "state": "WV", "_id": "25003"} -{"city": "AMEAGLE", "loc": [-81.406507, 37.964068], "pop": 623, "state": "WV", "_id": "25004"} -{"city": "AMMA", "loc": [-81.253182, 38.545518], "pop": 414, "state": "WV", "_id": "25005"} -{"city": "ARNETT", "loc": [-81.435484, 37.850214], "pop": 2073, "state": "WV", "_id": "25007"} -{"city": "ARTIE", "loc": [-81.378772, 37.918139], "pop": 407, "state": "WV", "_id": "25008"} -{"city": "ASHFORD", "loc": [-81.711324, 38.182291], "pop": 1329, "state": "WV", "_id": "25009"} -{"city": "BALD KNOB", "loc": [-81.622943, 37.844023], "pop": 666, "state": "WV", "_id": "25010"} -{"city": "BARRETT", "loc": [-81.64831, 37.880201], "pop": 50, "state": "WV", "_id": "25013"} -{"city": "DIAMOND", "loc": [-81.505812, 38.225381], "pop": 7120, "state": "WV", "_id": "25015"} -{"city": "BENTREE", "loc": [-81.21565, 38.297088], "pop": 377, "state": "WV", "_id": "25018"} -{"city": "FOLA", "loc": [-81.106838, 38.365947], "pop": 793, "state": "WV", "_id": "25019"} -{"city": "BIM", "loc": [-81.688047, 37.893437], "pop": 1061, "state": "WV", "_id": "25021"} -{"city": "BLOOMINGROSE", "loc": [-81.636737, 38.141189], "pop": 191, "state": "WV", "_id": "25024"} -{"city": "BLOUNT", "loc": [-81.405194, 38.312116], "pop": 583, "state": "WV", "_id": "25025"} -{"city": "BOB WHITE", "loc": [-81.720884, 37.958711], "pop": 570, "state": "WV", "_id": "25028"} -{"city": "BOMONT", "loc": [-81.21999, 38.407295], "pop": 129, "state": "WV", "_id": "25030"} -{"city": "BUFFALO", "loc": [-81.949971, 38.60924], "pop": 1898, "state": "WV", "_id": "25033"} -{"city": "BURNWELL", "loc": [-81.37647, 38.053014], "pop": 23, "state": "WV", "_id": "25034"} -{"city": "CABIN CREEK", "loc": [-81.515258, 38.185795], "pop": 1289, "state": "WV", "_id": "25035"} -{"city": "CEDAR GROVE", "loc": [-81.406711, 38.213286], "pop": 3463, "state": "WV", "_id": "25039"} -{"city": "CLAY", "loc": [-81.069588, 38.476354], "pop": 2881, "state": "WV", "_id": "25043"} -{"city": "CLEAR CREEK", "loc": [-81.320866, 37.89158], "pop": 262, "state": "WV", "_id": "25044"} -{"city": "QUICK", "loc": [-81.356039, 38.477687], "pop": 5864, "state": "WV", "_id": "25045"} -{"city": "CLIO", "loc": [-81.307188, 38.572686], "pop": 458, "state": "WV", "_id": "25046"} -{"city": "CLOTHIER", "loc": [-81.988142, 37.79333], "pop": 1379, "state": "WV", "_id": "25047"} -{"city": "COLCORD", "loc": [-81.443699, 37.949166], "pop": 405, "state": "WV", "_id": "25048"} -{"city": "COMFORT", "loc": [-81.596177, 38.137191], "pop": 889, "state": "WV", "_id": "25049"} -{"city": "COSTA", "loc": [-81.708015, 38.164693], "pop": 134, "state": "WV", "_id": "25051"} -{"city": "DANVILLE", "loc": [-81.854227, 38.03356], "pop": 4129, "state": "WV", "_id": "25053"} -{"city": "DIXIE", "loc": [-81.186249, 38.26631], "pop": 795, "state": "WV", "_id": "25059"} -{"city": "DOROTHY", "loc": [-81.496842, 37.96219], "pop": 524, "state": "WV", "_id": "25060"} -{"city": "DRY CREEK", "loc": [-81.481398, 37.860686], "pop": 209, "state": "WV", "_id": "25062"} -{"city": "DUCK", "loc": [-80.976416, 38.564652], "pop": 679, "state": "WV", "_id": "25063"} -{"city": "DUNBAR", "loc": [-81.742467, 38.368271], "pop": 11543, "state": "WV", "_id": "25064"} -{"city": "FRAME", "loc": [-81.481739, 38.432317], "pop": 10333, "state": "WV", "_id": "25071"} -{"city": "FALLING ROCK", "loc": [-81.380874, 38.475579], "pop": 353, "state": "WV", "_id": "25079"} -{"city": "FOSTER", "loc": [-81.765876, 38.090819], "pop": 1584, "state": "WV", "_id": "25081"} -{"city": "FRAZIERS BOTTOM", "loc": [-82.011068, 38.551678], "pop": 911, "state": "WV", "_id": "25082"} -{"city": "WHITTAKER", "loc": [-81.386231, 38.189406], "pop": 2542, "state": "WV", "_id": "25083"} -{"city": "GAULEY BRIDGE", "loc": [-81.19596, 38.168899], "pop": 826, "state": "WV", "_id": "25085"} -{"city": "GLEN", "loc": [-81.223401, 38.368989], "pop": 74, "state": "WV", "_id": "25088"} -{"city": "GORDON", "loc": [-81.67132, 37.978191], "pop": 251, "state": "WV", "_id": "25093"} -{"city": "HANSFORD", "loc": [-81.395026, 38.127158], "pop": 165, "state": "WV", "_id": "25103"} -{"city": "HARRISON", "loc": [-80.916068, 38.498979], "pop": 163, "state": "WV", "_id": "25105"} -{"city": "HENDERSON", "loc": [-82.136381, 38.826512], "pop": 747, "state": "WV", "_id": "25106"} -{"city": "HERNSHAW", "loc": [-81.612986, 38.194212], "pop": 745, "state": "WV", "_id": "25107"} -{"city": "HEWETT", "loc": [-81.850919, 37.958097], "pop": 625, "state": "WV", "_id": "25108"} -{"city": "INDORE", "loc": [-81.157495, 38.339672], "pop": 1173, "state": "WV", "_id": "25111"} -{"city": "BIG OTTER", "loc": [-81.033506, 38.530476], "pop": 145, "state": "WV", "_id": "25113"} -{"city": "RAMAGE", "loc": [-81.800305, 37.957023], "pop": 593, "state": "WV", "_id": "25114"} -{"city": "KANAWHA FALLS", "loc": [-81.162753, 38.107932], "pop": 98, "state": "WV", "_id": "25115"} -{"city": "KIMBERLY", "loc": [-81.321721, 38.124928], "pop": 108, "state": "WV", "_id": "25118"} -{"city": "KINCAID", "loc": [-81.26897, 38.032065], "pop": 94, "state": "WV", "_id": "25119"} -{"city": "LAKE", "loc": [-81.951152, 37.946191], "pop": 3384, "state": "WV", "_id": "25121"} -{"city": "CARBON", "loc": [-81.451228, 38.098249], "pop": 2581, "state": "WV", "_id": "25122"} -{"city": "ARBUCKLE", "loc": [-81.909244, 38.753046], "pop": 2325, "state": "WV", "_id": "25123"} -{"city": "LIBERTY", "loc": [-81.767813, 38.619978], "pop": 1050, "state": "WV", "_id": "25124"} -{"city": "LIZEMORES", "loc": [-81.163255, 38.336609], "pop": 84, "state": "WV", "_id": "25125"} -{"city": "MADISON", "loc": [-81.793757, 38.044426], "pop": 4476, "state": "WV", "_id": "25130"} -{"city": "MAMMOTH", "loc": [-81.350893, 38.258319], "pop": 486, "state": "WV", "_id": "25132"} -{"city": "MAYSEL", "loc": [-81.122653, 38.493924], "pop": 223, "state": "WV", "_id": "25133"} -{"city": "MONTGOMERY", "loc": [-81.281354, 38.141232], "pop": 10392, "state": "WV", "_id": "25136"} -{"city": "MOUNT CARBON", "loc": [-81.333373, 38.160097], "pop": 0, "state": "WV", "_id": "25139"} -{"city": "NAOMA", "loc": [-81.521372, 37.915381], "pop": 864, "state": "WV", "_id": "25140"} -{"city": "NEBO", "loc": [-81.025657, 38.627069], "pop": 222, "state": "WV", "_id": "25141"} -{"city": "NELLIS", "loc": [-81.739905, 38.152066], "pop": 104, "state": "WV", "_id": "25142"} -{"city": "NITRO", "loc": [-81.829311, 38.419323], "pop": 9525, "state": "WV", "_id": "25143"} -{"city": "ORGAS", "loc": [-81.57383, 38.073646], "pop": 840, "state": "WV", "_id": "25148"} -{"city": "OVAPA", "loc": [-81.14846, 38.523398], "pop": 525, "state": "WV", "_id": "25150"} -{"city": "PEYTONA", "loc": [-81.701836, 38.124952], "pop": 754, "state": "WV", "_id": "25154"} -{"city": "PLINY", "loc": [-82.015494, 38.614206], "pop": 352, "state": "WV", "_id": "25158"} -{"city": "LANHAM", "loc": [-81.80511, 38.488297], "pop": 5813, "state": "WV", "_id": "25159"} -{"city": "POND GAP", "loc": [-81.28658, 38.290434], "pop": 394, "state": "WV", "_id": "25160"} -{"city": "POWELLTON", "loc": [-81.317923, 38.099172], "pop": 1148, "state": "WV", "_id": "25161"} -{"city": "WILLIAMS MOUNTAI", "loc": [-81.623901, 38.008351], "pop": 684, "state": "WV", "_id": "25163"} -{"city": "PIGEON", "loc": [-81.226286, 38.472167], "pop": 1064, "state": "WV", "_id": "25164"} -{"city": "RACINE", "loc": [-81.655742, 38.149524], "pop": 539, "state": "WV", "_id": "25165"} -{"city": "RED HOUSE", "loc": [-81.904474, 38.546298], "pop": 3472, "state": "WV", "_id": "25168"} -{"city": "RIDGEVIEW", "loc": [-81.772548, 38.148576], "pop": 793, "state": "WV", "_id": "25169"} -{"city": "ROBERTSBURG", "loc": [-81.9109, 38.644143], "pop": 590, "state": "WV", "_id": "25172"} -{"city": "ROBSON", "loc": [-81.248758, 38.097263], "pop": 39, "state": "WV", "_id": "25173"} -{"city": "ROCK CREEK", "loc": [-81.483674, 37.836673], "pop": 533, "state": "WV", "_id": "25174"} -{"city": "SAINT ALBANS", "loc": [-81.830463, 38.37743], "pop": 24409, "state": "WV", "_id": "25177"} -{"city": "SAXON", "loc": [-81.432279, 37.786108], "pop": 154, "state": "WV", "_id": "25180"} -{"city": "SETH", "loc": [-81.639258, 38.097057], "pop": 1095, "state": "WV", "_id": "25181"} -{"city": "SOUTHSIDE", "loc": [-81.996596, 38.711994], "pop": 446, "state": "WV", "_id": "25187"} -{"city": "STICKNEY", "loc": [-81.497001, 37.876844], "pop": 18, "state": "WV", "_id": "25189"} -{"city": "SYLVESTER", "loc": [-81.549151, 38.009075], "pop": 1020, "state": "WV", "_id": "25193"} -{"city": "TORNADO", "loc": [-81.847555, 38.335697], "pop": 1500, "state": "WV", "_id": "25202"} -{"city": "TURTLE CREEK", "loc": [-81.853499, 38.042715], "pop": 669, "state": "WV", "_id": "25203"} -{"city": "BANDYTOWN", "loc": [-81.635204, 37.921015], "pop": 500, "state": "WV", "_id": "25204"} -{"city": "VAN", "loc": [-81.702175, 37.971152], "pop": 9, "state": "WV", "_id": "25206"} -{"city": "GARRISON", "loc": [-81.534013, 37.978677], "pop": 681, "state": "WV", "_id": "25209"} -{"city": "WINFIELD", "loc": [-81.889965, 38.507178], "pop": 3958, "state": "WV", "_id": "25213"} -{"city": "WINIFREDE", "loc": [-81.558747, 38.185245], "pop": 126, "state": "WV", "_id": "25214"} -{"city": "ADVENT", "loc": [-81.572063, 38.623595], "pop": 189, "state": "WV", "_id": "25231"} -{"city": "ARNOLDSBURG", "loc": [-81.155135, 38.822704], "pop": 1683, "state": "WV", "_id": "25234"} -{"city": "FLOE", "loc": [-81.090126, 38.671201], "pop": 886, "state": "WV", "_id": "25235"} -{"city": "COTTAGEVILLE", "loc": [-81.818609, 38.863285], "pop": 1183, "state": "WV", "_id": "25239"} -{"city": "EVANS", "loc": [-81.790858, 38.811281], "pop": 1296, "state": "WV", "_id": "25241"} -{"city": "25242", "loc": [-81.034134, 38.646191], "pop": 129, "state": "WV", "_id": "25242"} -{"city": "GANDEEVILLE", "loc": [-81.424329, 38.703382], "pop": 715, "state": "WV", "_id": "25243"} -{"city": "GAY", "loc": [-81.570301, 38.797842], "pop": 1037, "state": "WV", "_id": "25244"} -{"city": "GIVEN", "loc": [-81.682542, 38.713046], "pop": 994, "state": "WV", "_id": "25245"} -{"city": "HARMONY", "loc": [-81.507636, 38.692086], "pop": 372, "state": "WV", "_id": "25246"} -{"city": "ROMANCE", "loc": [-81.651717, 38.623894], "pop": 1880, "state": "WV", "_id": "25248"} -{"city": "KENTUCK", "loc": [-81.596018, 38.663886], "pop": 718, "state": "WV", "_id": "25249"} -{"city": "LEFT HAND", "loc": [-81.246675, 38.606288], "pop": 397, "state": "WV", "_id": "25251"} -{"city": "DUNCAN", "loc": [-81.57245, 38.957552], "pop": 743, "state": "WV", "_id": "25252"} -{"city": "LETART", "loc": [-81.975129, 38.931079], "pop": 4478, "state": "WV", "_id": "25253"} -{"city": "LETTER GAP", "loc": [-80.883768, 38.897074], "pop": 217, "state": "WV", "_id": "25255"} -{"city": "LINDEN", "loc": [-81.229061, 38.698938], "pop": 270, "state": "WV", "_id": "25256"} -{"city": "LOCKNEY", "loc": [-80.945075, 38.852919], "pop": 89, "state": "WV", "_id": "25258"} -{"city": "LOONEYVILLE", "loc": [-81.28219, 38.664113], "pop": 600, "state": "WV", "_id": "25259"} -{"city": "MASON", "loc": [-82.028215, 39.006442], "pop": 2598, "state": "WV", "_id": "25260"} -{"city": "MILLSTONE", "loc": [-81.086352, 38.86029], "pop": 748, "state": "WV", "_id": "25261"} -{"city": "MILLWOOD", "loc": [-81.824832, 38.896206], "pop": 1379, "state": "WV", "_id": "25262"} -{"city": "MOUNT ALTO", "loc": [-81.878374, 38.863937], "pop": 275, "state": "WV", "_id": "25264"} -{"city": "ULER", "loc": [-81.167579, 38.605219], "pop": 724, "state": "WV", "_id": "25266"} -{"city": "NORMANTOWN", "loc": [-80.941936, 38.880173], "pop": 183, "state": "WV", "_id": "25267"} -{"city": "MINNORA", "loc": [-81.09614, 38.735979], "pop": 641, "state": "WV", "_id": "25268"} -{"city": "REEDY", "loc": [-81.419785, 38.837119], "pop": 3014, "state": "WV", "_id": "25270"} -{"city": "RIPLEY", "loc": [-81.701987, 38.809042], "pop": 6580, "state": "WV", "_id": "25271"} -{"city": "ROCK CASTLE", "loc": [-81.767414, 38.708357], "pop": 82, "state": "WV", "_id": "25272"} -{"city": "SAND RIDGE", "loc": [-81.032372, 38.850721], "pop": 163, "state": "WV", "_id": "25274"} -{"city": "SANDYVILLE", "loc": [-81.654315, 38.909282], "pop": 1611, "state": "WV", "_id": "25275"} -{"city": "SPENCER", "loc": [-81.33026, 38.789696], "pop": 6007, "state": "WV", "_id": "25276"} -{"city": "STATTS MILLS", "loc": [-81.615579, 38.74076], "pop": 368, "state": "WV", "_id": "25279"} -{"city": "STUMPTOWN", "loc": [-80.970026, 38.831619], "pop": 275, "state": "WV", "_id": "25280"} -{"city": "TARIFF", "loc": [-81.212361, 38.652707], "pop": 191, "state": "WV", "_id": "25281"} -{"city": "VALLEY FORK", "loc": [-81.152177, 38.486906], "pop": 530, "state": "WV", "_id": "25283"} -{"city": "WALLBACK", "loc": [-81.093535, 38.57254], "pop": 181, "state": "WV", "_id": "25285"} -{"city": "WALTON", "loc": [-81.395757, 38.602264], "pop": 1961, "state": "WV", "_id": "25286"} -{"city": "WEST COLUMBIA", "loc": [-82.090529, 38.926169], "pop": 1009, "state": "WV", "_id": "25287"} -{"city": "CHARLESTON", "loc": [-81.630606, 38.349], "pop": 4139, "state": "WV", "_id": "25301"} -{"city": "BIG CHIMNEY", "loc": [-81.623876, 38.383178], "pop": 20267, "state": "WV", "_id": "25302"} -{"city": "SOUTH CHARLESTON", "loc": [-81.684079, 38.359226], "pop": 8198, "state": "WV", "_id": "25303"} -{"city": "CHARLESTON", "loc": [-81.590272, 38.317289], "pop": 9368, "state": "WV", "_id": "25304"} -{"city": "MALDEN", "loc": [-81.536813, 38.30028], "pop": 7992, "state": "WV", "_id": "25306"} -{"city": "SOUTH CHARLESTON", "loc": [-81.734462, 38.344903], "pop": 10179, "state": "WV", "_id": "25309"} -{"city": "CHARLESTON", "loc": [-81.599282, 38.349032], "pop": 10091, "state": "WV", "_id": "25311"} -{"city": "CHARLESTON", "loc": [-81.674688, 38.409563], "pop": 13224, "state": "WV", "_id": "25312"} -{"city": "CROSS LANES", "loc": [-81.764877, 38.424982], "pop": 13061, "state": "WV", "_id": "25313"} -{"city": "CHARLESTON", "loc": [-81.668988, 38.327442], "pop": 17108, "state": "WV", "_id": "25314"} -{"city": "MARMET", "loc": [-81.554361, 38.233309], "pop": 4317, "state": "WV", "_id": "25315"} -{"city": "SISSONVILLE", "loc": [-81.629585, 38.509586], "pop": 7220, "state": "WV", "_id": "25320"} -{"city": "MARTINSBURG", "loc": [-77.958915, 39.459959], "pop": 36800, "state": "WV", "_id": "25401"} -{"city": "HANCOCK", "loc": [-78.219217, 39.58736], "pop": 9219, "state": "WV", "_id": "25411"} -{"city": "BUNKER HILL", "loc": [-78.048718, 39.327554], "pop": 5419, "state": "WV", "_id": "25413"} -{"city": "CHARLES TOWN", "loc": [-77.856254, 39.277085], "pop": 14351, "state": "WV", "_id": "25414"} -{"city": "FALLING WATERS", "loc": [-77.885152, 39.586473], "pop": 4428, "state": "WV", "_id": "25419"} -{"city": "GERRARDSTOWN", "loc": [-78.114119, 39.378039], "pop": 2481, "state": "WV", "_id": "25420"} -{"city": "GREAT CACAPON", "loc": [-78.33312, 39.583557], "pop": 1108, "state": "WV", "_id": "25422"} -{"city": "HARPERS FERRY", "loc": [-77.769449, 39.315283], "pop": 7126, "state": "WV", "_id": "25425"} -{"city": "CHERRY RUN", "loc": [-78.056016, 39.547034], "pop": 7415, "state": "WV", "_id": "25427"} -{"city": "INWOOD", "loc": [-78.024189, 39.370093], "pop": 3646, "state": "WV", "_id": "25428"} -{"city": "KEARNEYSVILLE", "loc": [-77.958762, 39.321994], "pop": 3468, "state": "WV", "_id": "25430"} -{"city": "LEVELS", "loc": [-78.55078, 39.497637], "pop": 28, "state": "WV", "_id": "25431"} -{"city": "PAW PAW", "loc": [-78.458573, 39.492297], "pop": 1696, "state": "WV", "_id": "25434"} -{"city": "POINTS", "loc": [-78.564148, 39.457208], "pop": 106, "state": "WV", "_id": "25437"} -{"city": "RANSON", "loc": [-77.860572, 39.299531], "pop": 3010, "state": "WV", "_id": "25438"} -{"city": "SHENANDOAH JUNCT", "loc": [-77.88924, 39.371084], "pop": 1907, "state": "WV", "_id": "25442"} -{"city": "SHEPHERDSTOWN", "loc": [-77.815819, 39.431124], "pop": 5388, "state": "WV", "_id": "25443"} -{"city": "SLANESVILLE", "loc": [-78.505971, 39.402631], "pop": 468, "state": "WV", "_id": "25444"} -{"city": "SUMMIT POINT", "loc": [-77.958552, 39.233753], "pop": 676, "state": "WV", "_id": "25446"} -{"city": "ALKOL", "loc": [-81.983972, 38.160884], "pop": 1161, "state": "WV", "_id": "25501"} -{"city": "APPLE GROVE", "loc": [-82.172556, 38.713066], "pop": 115, "state": "WV", "_id": "25502"} -{"city": "ASHTON", "loc": [-82.108907, 38.657102], "pop": 1494, "state": "WV", "_id": "25503"} -{"city": "BARBOURSVILLE", "loc": [-82.295973, 38.389272], "pop": 10205, "state": "WV", "_id": "25504"} -{"city": "BIG CREEK", "loc": [-82.016056, 38.004555], "pop": 712, "state": "WV", "_id": "25505"} -{"city": "BRANCHLAND", "loc": [-82.268367, 38.269444], "pop": 642, "state": "WV", "_id": "25506"} -{"city": "CHAPMANVILLE", "loc": [-82.051241, 37.938535], "pop": 8874, "state": "WV", "_id": "25508"} -{"city": "CULLODEN", "loc": [-82.068963, 38.419831], "pop": 2569, "state": "WV", "_id": "25510"} -{"city": "DUNLOW", "loc": [-82.38266, 38.02829], "pop": 835, "state": "WV", "_id": "25511"} -{"city": "EAST LYNN", "loc": [-82.327931, 38.199474], "pop": 1722, "state": "WV", "_id": "25512"} -{"city": "FORT GAY", "loc": [-82.554124, 38.133496], "pop": 3499, "state": "WV", "_id": "25514"} -{"city": "GALLIPOLIS FERRY", "loc": [-82.153496, 38.765028], "pop": 2121, "state": "WV", "_id": "25515"} -{"city": "RADNOR", "loc": [-82.46692, 38.088908], "pop": 2111, "state": "WV", "_id": "25517"} -{"city": "GLENWOOD", "loc": [-82.143431, 38.544936], "pop": 3201, "state": "WV", "_id": "25520"} -{"city": "GRIFFITHSVILLE", "loc": [-81.982028, 38.246985], "pop": 521, "state": "WV", "_id": "25521"} -{"city": "HAMLIN", "loc": [-82.104695, 38.289556], "pop": 2993, "state": "WV", "_id": "25523"} -{"city": "FERRELLSBURG", "loc": [-82.155358, 38.008878], "pop": 1611, "state": "WV", "_id": "25524"} -{"city": "HURRICANE", "loc": [-81.994329, 38.425741], "pop": 16250, "state": "WV", "_id": "25526"} -{"city": "JULIAN", "loc": [-81.864185, 38.126437], "pop": 670, "state": "WV", "_id": "25529"} -{"city": "KENOVA", "loc": [-82.573649, 38.386031], "pop": 7491, "state": "WV", "_id": "25530"} -{"city": "COVE GAP", "loc": [-82.256206, 38.077744], "pop": 326, "state": "WV", "_id": "25534"} -{"city": "LAVALETTE", "loc": [-82.428707, 38.33046], "pop": 5928, "state": "WV", "_id": "25535"} -{"city": "25536", "loc": [-82.076761, 38.087553], "pop": 61, "state": "WV", "_id": "25536"} -{"city": "LESAGE", "loc": [-82.295667, 38.477028], "pop": 2972, "state": "WV", "_id": "25537"} -{"city": "MIDKIFF", "loc": [-82.154227, 38.143908], "pop": 470, "state": "WV", "_id": "25540"} -{"city": "MILTON", "loc": [-82.145784, 38.413981], "pop": 7168, "state": "WV", "_id": "25541"} -{"city": "MYRA", "loc": [-82.135097, 38.21978], "pop": 146, "state": "WV", "_id": "25544"} -{"city": "ONA", "loc": [-82.218093, 38.452851], "pop": 5959, "state": "WV", "_id": "25545"} -{"city": "PALERMO", "loc": [-82.046018, 38.12817], "pop": 115, "state": "WV", "_id": "25546"} -{"city": "PECKS MILL", "loc": [-81.965382, 37.929784], "pop": 117, "state": "WV", "_id": "25547"} -{"city": "POINT PLEASANT", "loc": [-82.113247, 38.863249], "pop": 7981, "state": "WV", "_id": "25550"} -{"city": "PRICHARD", "loc": [-82.575297, 38.230009], "pop": 1215, "state": "WV", "_id": "25555"} -{"city": "RANGER", "loc": [-82.19078, 38.155608], "pop": 5703, "state": "WV", "_id": "25557"} -{"city": "SALT ROCK", "loc": [-82.247041, 38.326542], "pop": 1482, "state": "WV", "_id": "25559"} -{"city": "SCOTT DEPOT", "loc": [-81.900481, 38.450063], "pop": 5893, "state": "WV", "_id": "25560"} -{"city": "SIAS", "loc": [-82.085779, 38.189434], "pop": 384, "state": "WV", "_id": "25563"} -{"city": "SOD", "loc": [-81.86814, 38.262967], "pop": 3529, "state": "WV", "_id": "25564"} -{"city": "MORRISVALE", "loc": [-81.991778, 38.077015], "pop": 1449, "state": "WV", "_id": "25565"} -{"city": "SUMERCO", "loc": [-81.894317, 38.205292], "pop": 638, "state": "WV", "_id": "25567"} -{"city": "SWEETLAND", "loc": [-82.034464, 38.262874], "pop": 378, "state": "WV", "_id": "25568"} -{"city": "WAYNE", "loc": [-82.433926, 38.225814], "pop": 4992, "state": "WV", "_id": "25570"} -{"city": "WEST HAMLIN", "loc": [-82.184348, 38.288571], "pop": 1612, "state": "WV", "_id": "25571"} -{"city": "WOODVILLE", "loc": [-81.911281, 38.157027], "pop": 262, "state": "WV", "_id": "25572"} -{"city": "YAWKEY", "loc": [-81.956828, 38.219252], "pop": 690, "state": "WV", "_id": "25573"} -{"city": "WEST LOGAN", "loc": [-82.002092, 37.847488], "pop": 14143, "state": "WV", "_id": "25601"} -{"city": "ROBINETTE", "loc": [-81.758912, 37.795586], "pop": 2986, "state": "WV", "_id": "25607"} -{"city": "BAISDEN", "loc": [-81.932758, 37.571492], "pop": 1183, "state": "WV", "_id": "25608"} -{"city": "DAVIN", "loc": [-81.805706, 37.726499], "pop": 2388, "state": "WV", "_id": "25617"} -{"city": "GILBERT", "loc": [-81.866468, 37.594153], "pop": 2027, "state": "WV", "_id": "25621"} -{"city": "HAMPDEN", "loc": [-81.947014, 37.639691], "pop": 1352, "state": "WV", "_id": "25623"} -{"city": "EARLING", "loc": [-81.929291, 37.774266], "pop": 847, "state": "WV", "_id": "25632"} -{"city": "HUNT", "loc": [-81.865228, 37.729525], "pop": 3932, "state": "WV", "_id": "25635"} -{"city": "BARNABUS", "loc": [-82.004931, 37.722368], "pop": 2369, "state": "WV", "_id": "25638"} -{"city": "VERNER", "loc": [-81.864801, 37.64554], "pop": 1487, "state": "WV", "_id": "25650"} -{"city": "WHARNCLIFFE", "loc": [-81.956604, 37.545161], "pop": 428, "state": "WV", "_id": "25651"} -{"city": "DEHUE", "loc": [-81.872699, 37.866513], "pop": 2524, "state": "WV", "_id": "25654"} -{"city": "WILLIAMSON", "loc": [-82.264163, 37.690248], "pop": 8866, "state": "WV", "_id": "25661"} -{"city": "BREEDEN", "loc": [-82.254748, 37.919751], "pop": 773, "state": "WV", "_id": "25666"} -{"city": "CRUM", "loc": [-82.462359, 37.938879], "pop": 763, "state": "WV", "_id": "25669"} -{"city": "MYRTLE", "loc": [-82.131922, 37.703833], "pop": 4639, "state": "WV", "_id": "25670"} -{"city": "DINGESS", "loc": [-82.195108, 37.870336], "pop": 2012, "state": "WV", "_id": "25671"} -{"city": "KERMIT", "loc": [-82.374065, 37.864246], "pop": 3549, "state": "WV", "_id": "25674"} -{"city": "LENORE", "loc": [-82.269336, 37.799436], "pop": 2521, "state": "WV", "_id": "25676"} -{"city": "LOBATA", "loc": [-82.145694, 37.649539], "pop": 3232, "state": "WV", "_id": "25678"} -{"city": "MEADOR", "loc": [-82.065904, 37.592373], "pop": 1640, "state": "WV", "_id": "25682"} -{"city": "THACKER", "loc": [-82.127333, 37.608293], "pop": 1064, "state": "WV", "_id": "25694"} -{"city": "WILSONDALE", "loc": [-82.381358, 37.951598], "pop": 897, "state": "WV", "_id": "25699"} -{"city": "HUNTINGTON", "loc": [-82.442348, 38.409726], "pop": 20896, "state": "WV", "_id": "25701"} -{"city": "HUNTINGTON", "loc": [-82.391083, 38.42862], "pop": 7290, "state": "WV", "_id": "25702"} -{"city": "HUNTINGTON", "loc": [-82.422666, 38.421116], "pop": 6570, "state": "WV", "_id": "25703"} -{"city": "HUNTINGTON", "loc": [-82.503646, 38.384943], "pop": 18311, "state": "WV", "_id": "25704"} -{"city": "HUNTINGTON", "loc": [-82.36901, 38.409588], "pop": 22276, "state": "WV", "_id": "25705"} -{"city": "BECKLEY", "loc": [-81.206084, 37.793214], "pop": 45196, "state": "WV", "_id": "25801"} -{"city": "AMIGO", "loc": [-81.349145, 37.59211], "pop": 684, "state": "WV", "_id": "25811"} -{"city": "ANSTED", "loc": [-81.095487, 38.13686], "pop": 1451, "state": "WV", "_id": "25812"} -{"city": "BEAVER", "loc": [-81.073104, 37.751581], "pop": 4868, "state": "WV", "_id": "25813"} -{"city": "BOLT", "loc": [-81.415612, 37.767303], "pop": 524, "state": "WV", "_id": "25817"} -{"city": "CAMP CREEK", "loc": [-81.111073, 37.484053], "pop": 65, "state": "WV", "_id": "25820"} -{"city": "COOL RIDGE", "loc": [-81.10528, 37.65735], "pop": 1731, "state": "WV", "_id": "25825"} -{"city": "CRAB ORCHARD", "loc": [-81.249098, 37.720515], "pop": 5209, "state": "WV", "_id": "25827"} -{"city": "CLIFFTOP", "loc": [-80.947114, 37.903615], "pop": 1432, "state": "WV", "_id": "25831"} -{"city": "DANIELS", "loc": [-81.139108, 37.732247], "pop": 2533, "state": "WV", "_id": "25832"} -{"city": "EDMOND", "loc": [-81.032634, 38.054441], "pop": 69, "state": "WV", "_id": "25837"} -{"city": "FAIRDALE", "loc": [-81.401758, 37.786578], "pop": 175, "state": "WV", "_id": "25839"} -{"city": "CUNARD", "loc": [-81.080969, 38.062774], "pop": 8013, "state": "WV", "_id": "25840"} -{"city": "FLAT TOP", "loc": [-81.127976, 37.550028], "pop": 623, "state": "WV", "_id": "25841"} -{"city": "GHENT", "loc": [-81.101921, 37.62079], "pop": 749, "state": "WV", "_id": "25843"} -{"city": "GLEN DANIEL", "loc": [-81.403294, 37.754698], "pop": 123, "state": "WV", "_id": "25844"} -{"city": "GLEN FORK", "loc": [-81.540209, 37.644691], "pop": 189, "state": "WV", "_id": "25845"} -{"city": "SULLIVAN", "loc": [-81.13048, 37.790384], "pop": 99, "state": "WV", "_id": "25847"} -{"city": "GLEN ROGERS", "loc": [-81.428989, 37.721685], "pop": 367, "state": "WV", "_id": "25848"} -{"city": "HICO", "loc": [-80.963763, 38.109373], "pop": 721, "state": "WV", "_id": "25854"} -{"city": "JOSEPHINE", "loc": [-81.217584, 37.622135], "pop": 476, "state": "WV", "_id": "25857"} -{"city": "LANSING", "loc": [-81.02772, 38.110793], "pop": 89, "state": "WV", "_id": "25862"} -{"city": "LAWTON", "loc": [-80.992449, 37.865779], "pop": 17, "state": "WV", "_id": "25864"} -{"city": "LESTER", "loc": [-81.309542, 37.726522], "pop": 1157, "state": "WV", "_id": "25865"} -{"city": "LOOKOUT", "loc": [-80.92006, 38.052822], "pop": 338, "state": "WV", "_id": "25868"} -{"city": "MABEN", "loc": [-81.391371, 37.654294], "pop": 415, "state": "WV", "_id": "25870"} -{"city": "SAULSVILLE", "loc": [-81.457706, 37.655573], "pop": 624, "state": "WV", "_id": "25876"} -{"city": "MOUNT HOPE", "loc": [-81.170419, 37.911048], "pop": 4348, "state": "WV", "_id": "25880"} -{"city": "MULLENS", "loc": [-81.389015, 37.584172], "pop": 2590, "state": "WV", "_id": "25882"} -{"city": "HARVEY", "loc": [-81.134425, 37.984874], "pop": 14805, "state": "WV", "_id": "25901"} -{"city": "ODD", "loc": [-81.187183, 37.592997], "pop": 577, "state": "WV", "_id": "25902"} -{"city": "WINDING GULF", "loc": [-81.231022, 37.643456], "pop": 13, "state": "WV", "_id": "25908"} -{"city": "RAMSEY", "loc": [-81.015047, 38.178142], "pop": 460, "state": "WV", "_id": "25912"} -{"city": "RAVENCLIFF", "loc": [-81.496736, 37.697961], "pop": 1457, "state": "WV", "_id": "25913"} -{"city": "EAST GULF", "loc": [-81.294753, 37.628587], "pop": 1183, "state": "WV", "_id": "25915"} -{"city": "SCARBRO", "loc": [-81.245731, 37.947073], "pop": 1074, "state": "WV", "_id": "25917"} -{"city": "ABRAHAM", "loc": [-81.1023, 37.697802], "pop": 2548, "state": "WV", "_id": "25918"} -{"city": "SLAB FORK", "loc": [-81.227928, 37.679605], "pop": 2872, "state": "WV", "_id": "25920"} -{"city": "SPANISHBURG", "loc": [-81.106941, 37.46314], "pop": 883, "state": "WV", "_id": "25922"} -{"city": "STEPHENSON", "loc": [-81.333914, 37.579847], "pop": 441, "state": "WV", "_id": "25928"} -{"city": "SURVEYOR", "loc": [-81.33919, 37.748], "pop": 416, "state": "WV", "_id": "25932"} -{"city": "THURMOND", "loc": [-81.071428, 37.955437], "pop": 62, "state": "WV", "_id": "25936"} -{"city": "VICTOR", "loc": [-81.032223, 38.140194], "pop": 516, "state": "WV", "_id": "25938"} -{"city": "HINTON", "loc": [-80.867779, 37.663983], "pop": 6150, "state": "WV", "_id": "25951"} -{"city": "CHARMCO", "loc": [-80.707745, 37.98948], "pop": 1643, "state": "WV", "_id": "25958"} -{"city": "RAINELLE", "loc": [-80.782064, 37.966112], "pop": 3320, "state": "WV", "_id": "25962"} -{"city": "ELTON", "loc": [-80.825207, 37.846825], "pop": 208, "state": "WV", "_id": "25965"} -{"city": "GREEN SULPHUR SP", "loc": [-80.839922, 37.808553], "pop": 742, "state": "WV", "_id": "25966"} -{"city": "STREETER", "loc": [-80.995717, 37.639732], "pop": 1270, "state": "WV", "_id": "25969"} -{"city": "LERONA", "loc": [-80.979574, 37.492498], "pop": 507, "state": "WV", "_id": "25971"} -{"city": "MEADOW BRIDGE", "loc": [-80.852144, 37.878048], "pop": 1446, "state": "WV", "_id": "25976"} -{"city": "MEADOW CREEK", "loc": [-80.91742, 37.804824], "pop": 157, "state": "WV", "_id": "25977"} -{"city": "NIMITZ", "loc": [-80.94462, 37.628584], "pop": 577, "state": "WV", "_id": "25978"} -{"city": "PIPESTEM", "loc": [-80.946345, 37.530345], "pop": 677, "state": "WV", "_id": "25979"} -{"city": "MARFRANCE", "loc": [-80.717286, 38.051937], "pop": 1936, "state": "WV", "_id": "25981"} -{"city": "KESSLER", "loc": [-80.689971, 37.963882], "pop": 1464, "state": "WV", "_id": "25984"} -{"city": "SANDSTONE", "loc": [-80.871564, 37.759879], "pop": 435, "state": "WV", "_id": "25985"} -{"city": "SPRING DALE", "loc": [-80.814996, 37.86947], "pop": 79, "state": "WV", "_id": "25986"} -{"city": "TRUE", "loc": [-80.930837, 37.580031], "pop": 340, "state": "WV", "_id": "25988"} -{"city": "WHITE OAK", "loc": [-81.058875, 37.68008], "pop": 284, "state": "WV", "_id": "25989"} -{"city": "ELM GROVE", "loc": [-80.685126, 40.072736], "pop": 49136, "state": "WV", "_id": "26003"} -{"city": "BENWOOD", "loc": [-80.71603, 40.013299], "pop": 4032, "state": "WV", "_id": "26031"} -{"city": "BETHANY", "loc": [-80.55463, 40.202], "pop": 487, "state": "WV", "_id": "26032"} -{"city": "CAMERON", "loc": [-80.566876, 39.82272], "pop": 2743, "state": "WV", "_id": "26033"} -{"city": "CHESTER", "loc": [-80.558439, 40.598109], "pop": 5966, "state": "WV", "_id": "26034"} -{"city": "COLLIERS", "loc": [-80.549341, 40.348391], "pop": 2602, "state": "WV", "_id": "26035"} -{"city": "DALLAS", "loc": [-80.550804, 39.978343], "pop": 822, "state": "WV", "_id": "26036"} -{"city": "FOLLANSBEE", "loc": [-80.584954, 40.329552], "pop": 7676, "state": "WV", "_id": "26037"} -{"city": "GLEN DALE", "loc": [-80.732263, 39.959732], "pop": 3418, "state": "WV", "_id": "26038"} -{"city": "GLEN EASTON", "loc": [-80.666069, 39.853121], "pop": 2477, "state": "WV", "_id": "26039"} -{"city": "MC MECHEN", "loc": [-80.73082, 39.987793], "pop": 2130, "state": "WV", "_id": "26040"} -{"city": "MOUNDSVILLE", "loc": [-80.730971, 39.914819], "pop": 18627, "state": "WV", "_id": "26041"} -{"city": "NEW CUMBERLAND", "loc": [-80.590108, 40.522777], "pop": 5779, "state": "WV", "_id": "26047"} -{"city": "NEWELL", "loc": [-80.601126, 40.613971], "pop": 2316, "state": "WV", "_id": "26050"} -{"city": "PROCTOR", "loc": [-80.761779, 39.720683], "pop": 1785, "state": "WV", "_id": "26055"} -{"city": "TRIADELPHIA", "loc": [-80.59529, 40.069945], "pop": 2320, "state": "WV", "_id": "26059"} -{"city": "VALLEY GROVE", "loc": [-80.555059, 40.094649], "pop": 1705, "state": "WV", "_id": "26060"} -{"city": "WEIRTON", "loc": [-80.568307, 40.413673], "pop": 25107, "state": "WV", "_id": "26062"} -{"city": "WELLSBURG", "loc": [-80.596061, 40.254792], "pop": 12292, "state": "WV", "_id": "26070"} -{"city": "PARKERSBURG", "loc": [-81.535412, 39.264433], "pop": 35886, "state": "WV", "_id": "26101"} -{"city": "NORTH PARKERSBUR", "loc": [-81.526109, 39.285316], "pop": 9260, "state": "WV", "_id": "26104"} -{"city": "VIENNA", "loc": [-81.541143, 39.325089], "pop": 11696, "state": "WV", "_id": "26105"} -{"city": "BELLEVILLE", "loc": [-81.692316, 39.131417], "pop": 1240, "state": "WV", "_id": "26133"} -{"city": "WILLOW ISLAND", "loc": [-81.27881, 39.367927], "pop": 1562, "state": "WV", "_id": "26134"} -{"city": "BENS RUN", "loc": [-81.066534, 39.44641], "pop": 282, "state": "WV", "_id": "26135"} -{"city": "BIG BEND", "loc": [-81.13603, 38.971941], "pop": 844, "state": "WV", "_id": "26136"} -{"city": "NOBE", "loc": [-81.069813, 38.996306], "pop": 371, "state": "WV", "_id": "26137"} -{"city": "BROHARD", "loc": [-81.177528, 39.033756], "pop": 23, "state": "WV", "_id": "26138"} -{"city": "CRESTON", "loc": [-81.236905, 38.937805], "pop": 340, "state": "WV", "_id": "26141"} -{"city": "DAVISVILLE", "loc": [-81.479863, 39.212635], "pop": 3667, "state": "WV", "_id": "26142"} -{"city": "ELIZABETH", "loc": [-81.398889, 39.056618], "pop": 3052, "state": "WV", "_id": "26143"} -{"city": "FIVE FORKS", "loc": [-81.086623, 38.975331], "pop": 78, "state": "WV", "_id": "26145"} -{"city": "FRIENDLY", "loc": [-81.034544, 39.502444], "pop": 1073, "state": "WV", "_id": "26146"} -{"city": "GRANTSVILLE", "loc": [-81.076294, 38.932436], "pop": 1873, "state": "WV", "_id": "26147"} -{"city": "MACFARLAN", "loc": [-81.177627, 39.08058], "pop": 437, "state": "WV", "_id": "26148"} -{"city": "MIDDLEBOURNE", "loc": [-80.886067, 39.50537], "pop": 2527, "state": "WV", "_id": "26149"} -{"city": "MINERALWELLS", "loc": [-81.519096, 39.179846], "pop": 5463, "state": "WV", "_id": "26150"} -{"city": "MOUNT ZION", "loc": [-81.119473, 38.877455], "pop": 227, "state": "WV", "_id": "26151"} -{"city": "MUNDAY", "loc": [-81.204048, 39.021423], "pop": 117, "state": "WV", "_id": "26152"} -{"city": "MURRAYSVILLE", "loc": [-81.771368, 39.059143], "pop": 452, "state": "WV", "_id": "26153"} -{"city": "NEW MARTINSVILLE", "loc": [-80.856541, 39.658798], "pop": 7617, "state": "WV", "_id": "26155"} -{"city": "PADEN CITY", "loc": [-80.926482, 39.604408], "pop": 3513, "state": "WV", "_id": "26159"} -{"city": "PALESTINE", "loc": [-81.42803, 38.974141], "pop": 935, "state": "WV", "_id": "26160"} -{"city": "PETROLEUM", "loc": [-81.240607, 39.174139], "pop": 419, "state": "WV", "_id": "26161"} -{"city": "RAVENSWOOD", "loc": [-81.752377, 38.94675], "pop": 6428, "state": "WV", "_id": "26164"} -{"city": "READER", "loc": [-80.751583, 39.583595], "pop": 2289, "state": "WV", "_id": "26167"} -{"city": "ROCKPORT", "loc": [-81.571333, 39.079774], "pop": 1068, "state": "WV", "_id": "26169"} -{"city": "SAINT MARYS", "loc": [-81.172499, 39.38597], "pop": 5523, "state": "WV", "_id": "26170"} -{"city": "SHERMAN", "loc": [-81.700916, 39.013094], "pop": 749, "state": "WV", "_id": "26173"} -{"city": "SISTERSVILLE", "loc": [-80.981828, 39.558365], "pop": 3113, "state": "WV", "_id": "26175"} -{"city": "SMITHVILLE", "loc": [-81.061953, 39.067078], "pop": 590, "state": "WV", "_id": "26178"} -{"city": "TANNER", "loc": [-80.959719, 38.966435], "pop": 389, "state": "WV", "_id": "26179"} -{"city": "WALKER", "loc": [-81.386577, 39.182082], "pop": 3218, "state": "WV", "_id": "26180"} -{"city": "NEW ENGLAND", "loc": [-81.640719, 39.233911], "pop": 8532, "state": "WV", "_id": "26181"} -{"city": "WAVERLY", "loc": [-81.381938, 39.315681], "pop": 2836, "state": "WV", "_id": "26184"} -{"city": "WICK", "loc": [-80.968044, 39.421653], "pop": 479, "state": "WV", "_id": "26185"} -{"city": "WILEYVILLE", "loc": [-80.647251, 39.662593], "pop": 343, "state": "WV", "_id": "26186"} -{"city": "WILLIAMSTOWN", "loc": [-81.456608, 39.38243], "pop": 5207, "state": "WV", "_id": "26187"} -{"city": "TENNERTON", "loc": [-80.217824, 38.98407], "pop": 16829, "state": "WV", "_id": "26201"} -{"city": "FENWICK", "loc": [-80.634972, 38.244657], "pop": 393, "state": "WV", "_id": "26202"} -{"city": "ERBACON", "loc": [-80.574232, 38.541864], "pop": 318, "state": "WV", "_id": "26203"} -{"city": "CRAIGSVILLE", "loc": [-80.64438, 38.325389], "pop": 3070, "state": "WV", "_id": "26205"} -{"city": "COWEN", "loc": [-80.535807, 38.413273], "pop": 2469, "state": "WV", "_id": "26206"} -{"city": "GAULEY MILLS", "loc": [-80.591408, 38.379246], "pop": 1191, "state": "WV", "_id": "26208"} -{"city": "ADRIAN", "loc": [-80.288663, 38.913422], "pop": 794, "state": "WV", "_id": "26210"} -{"city": "CENTURY", "loc": [-80.175688, 39.093287], "pop": 235, "state": "WV", "_id": "26214"} -{"city": "CLEVELAND", "loc": [-80.342849, 38.839225], "pop": 456, "state": "WV", "_id": "26215"} -{"city": "DIANA", "loc": [-80.431962, 38.575471], "pop": 1172, "state": "WV", "_id": "26217"} -{"city": "ALEXANDER", "loc": [-80.274339, 38.851145], "pop": 2398, "state": "WV", "_id": "26218"} -{"city": "REPLETE", "loc": [-80.402345, 38.679583], "pop": 730, "state": "WV", "_id": "26222"} -{"city": "HELVETIA", "loc": [-80.138167, 38.771258], "pop": 27, "state": "WV", "_id": "26224"} -{"city": "KANAWHA HEAD", "loc": [-80.372734, 38.76183], "pop": 171, "state": "WV", "_id": "26228"} -{"city": "PICKENS", "loc": [-80.209784, 38.684242], "pop": 382, "state": "WV", "_id": "26230"} -{"city": "ROCK CAVE", "loc": [-80.316276, 38.781128], "pop": 649, "state": "WV", "_id": "26234"} -{"city": "SELBYVILLE", "loc": [-80.312955, 38.719393], "pop": 40, "state": "WV", "_id": "26236"} -{"city": "TALLMANSVILLE", "loc": [-80.15799, 38.856405], "pop": 507, "state": "WV", "_id": "26237"} -{"city": "VOLGA", "loc": [-80.143209, 39.068445], "pop": 969, "state": "WV", "_id": "26238"} -{"city": "ELKINS", "loc": [-79.847115, 38.925304], "pop": 14027, "state": "WV", "_id": "26241"} -{"city": "BELINGTON", "loc": [-79.949508, 39.024439], "pop": 5189, "state": "WV", "_id": "26250"} -{"city": "BEVERLY", "loc": [-79.865121, 38.828021], "pop": 3206, "state": "WV", "_id": "26253"} -{"city": "WYMER", "loc": [-79.619347, 38.934863], "pop": 128, "state": "WV", "_id": "26254"} -{"city": "COALTON", "loc": [-79.976724, 38.91438], "pop": 1422, "state": "WV", "_id": "26257"} -{"city": "DAVIS", "loc": [-79.456278, 39.088227], "pop": 1459, "state": "WV", "_id": "26260"} -{"city": "RICHWOOD", "loc": [-80.544481, 38.222988], "pop": 4129, "state": "WV", "_id": "26261"} -{"city": "DRYFORK", "loc": [-79.453403, 38.957616], "pop": 116, "state": "WV", "_id": "26263"} -{"city": "DURBIN", "loc": [-79.793464, 38.616313], "pop": 75, "state": "WV", "_id": "26264"} -{"city": "UPPERGLADE", "loc": [-80.496464, 38.418528], "pop": 383, "state": "WV", "_id": "26266"} -{"city": "ELLAMORE", "loc": [-80.092611, 38.933966], "pop": 482, "state": "WV", "_id": "26267"} -{"city": "GLADY", "loc": [-79.734202, 38.866925], "pop": 615, "state": "WV", "_id": "26268"} -{"city": "HAMBLETON", "loc": [-79.641565, 39.116836], "pop": 930, "state": "WV", "_id": "26269"} -{"city": "HARMAN", "loc": [-79.524599, 38.923369], "pop": 700, "state": "WV", "_id": "26270"} -{"city": "HENDRICKS", "loc": [-79.630302, 39.074814], "pop": 318, "state": "WV", "_id": "26271"} -{"city": "HUTTONSVILLE", "loc": [-79.977544, 38.678433], "pop": 1072, "state": "WV", "_id": "26273"} -{"city": "KERENS", "loc": [-79.775427, 39.029854], "pop": 569, "state": "WV", "_id": "26276"} -{"city": "MABIE", "loc": [-80.013061, 38.822158], "pop": 427, "state": "WV", "_id": "26278"} -{"city": "MILL CREEK", "loc": [-79.978569, 38.732493], "pop": 2219, "state": "WV", "_id": "26280"} -{"city": "MONTERVILLE", "loc": [-80.122391, 38.551707], "pop": 79, "state": "WV", "_id": "26282"} -{"city": "MONTROSE", "loc": [-79.789577, 39.096357], "pop": 1513, "state": "WV", "_id": "26283"} -{"city": "PARSONS", "loc": [-79.673448, 39.105857], "pop": 2432, "state": "WV", "_id": "26287"} -{"city": "BOLAIR", "loc": [-80.399798, 38.476362], "pop": 3914, "state": "WV", "_id": "26288"} -{"city": "RED CREEK", "loc": [-79.53009, 39.019628], "pop": 318, "state": "WV", "_id": "26289"} -{"city": "SLATYFORK", "loc": [-80.040353, 38.381207], "pop": 448, "state": "WV", "_id": "26291"} -{"city": "THOMAS", "loc": [-79.503, 39.150517], "pop": 966, "state": "WV", "_id": "26292"} -{"city": "VALLEY BEND", "loc": [-79.916037, 38.784903], "pop": 804, "state": "WV", "_id": "26293"} -{"city": "MINGO", "loc": [-80.044911, 38.536637], "pop": 1013, "state": "WV", "_id": "26294"} -{"city": "JOB", "loc": [-79.557752, 38.808289], "pop": 384, "state": "WV", "_id": "26296"} -{"city": "BOGGS", "loc": [-80.607389, 38.482119], "pop": 544, "state": "WV", "_id": "26299"} -{"city": "NUTTER FORT STON", "loc": [-80.348683, 39.278422], "pop": 33089, "state": "WV", "_id": "26301"} -{"city": "WILBUR", "loc": [-80.817157, 39.429757], "pop": 1201, "state": "WV", "_id": "26320"} -{"city": "ALUM BRIDGE", "loc": [-80.688196, 39.042474], "pop": 1015, "state": "WV", "_id": "26321"} -{"city": "ALVY", "loc": [-80.668921, 39.451106], "pop": 143, "state": "WV", "_id": "26322"} -{"city": "AUBURN", "loc": [-80.883656, 39.086382], "pop": 326, "state": "WV", "_id": "26325"} -{"city": "BEREA", "loc": [-80.922322, 39.130452], "pop": 106, "state": "WV", "_id": "26327"} -{"city": "BLANDVILLE", "loc": [-80.735856, 39.265169], "pop": 35, "state": "WV", "_id": "26328"} -{"city": "BRIDGEPORT", "loc": [-80.242692, 39.29537], "pop": 11747, "state": "WV", "_id": "26330"} -{"city": "BRISTOL", "loc": [-80.510541, 39.290425], "pop": 1424, "state": "WV", "_id": "26332"} -{"city": "GEM", "loc": [-80.658989, 38.850391], "pop": 1513, "state": "WV", "_id": "26335"} -{"city": "CAIRO", "loc": [-81.155427, 39.23249], "pop": 1379, "state": "WV", "_id": "26337"} -{"city": "CAMDEN", "loc": [-80.591531, 39.089268], "pop": 584, "state": "WV", "_id": "26338"} -{"city": "CENTER POINT", "loc": [-80.635227, 39.412472], "pop": 121, "state": "WV", "_id": "26339"} -{"city": "COXS MILLS", "loc": [-80.874592, 39.002102], "pop": 796, "state": "WV", "_id": "26342"} -{"city": "CRAWFORD", "loc": [-80.400749, 38.840065], "pop": 517, "state": "WV", "_id": "26343"} -{"city": "HIGHLAND", "loc": [-81.060382, 39.282364], "pop": 880, "state": "WV", "_id": "26346"} -{"city": "WENDEL", "loc": [-80.128179, 39.271069], "pop": 2620, "state": "WV", "_id": "26347"} -{"city": "FOLSOM", "loc": [-80.525806, 39.469345], "pop": 359, "state": "WV", "_id": "26348"} -{"city": "BALDWIN", "loc": [-80.82783, 38.933619], "pop": 2768, "state": "WV", "_id": "26351"} -{"city": "GRAFTON", "loc": [-80.028509, 39.341386], "pop": 10102, "state": "WV", "_id": "26354"} -{"city": "GREENWOOD", "loc": [-80.878899, 39.290761], "pop": 454, "state": "WV", "_id": "26360"} -{"city": "MAHONE", "loc": [-81.045967, 39.199608], "pop": 2962, "state": "WV", "_id": "26362"} -{"city": "HAZELGREEN", "loc": [-80.980578, 39.063075], "pop": 203, "state": "WV", "_id": "26367"} -{"city": "HORNER", "loc": [-80.365576, 38.972628], "pop": 365, "state": "WV", "_id": "26372"} -{"city": "INDEPENDENCE", "loc": [-79.882161, 39.446372], "pop": 1227, "state": "WV", "_id": "26374"} -{"city": "WILDCAT", "loc": [-80.477002, 38.767689], "pop": 589, "state": "WV", "_id": "26376"} -{"city": "JACKSONBURG", "loc": [-80.643697, 39.541553], "pop": 406, "state": "WV", "_id": "26377"} -{"city": "JANE LEW", "loc": [-80.431059, 39.109989], "pop": 4529, "state": "WV", "_id": "26378"} -{"city": "LIMA", "loc": [-80.724703, 39.438973], "pop": 153, "state": "WV", "_id": "26383"} -{"city": "LINN", "loc": [-80.730046, 38.957294], "pop": 759, "state": "WV", "_id": "26384"} -{"city": "LOST CREEK", "loc": [-80.377674, 39.172528], "pop": 3740, "state": "WV", "_id": "26385"} -{"city": "LUMBERPORT", "loc": [-80.367635, 39.378108], "pop": 2300, "state": "WV", "_id": "26386"} -{"city": "MEADOWBROOK", "loc": [-80.325668, 39.335367], "pop": 1373, "state": "WV", "_id": "26404"} -{"city": "KASSON", "loc": [-79.921574, 39.212271], "pop": 1382, "state": "WV", "_id": "26405"} -{"city": "MOUNT CLARE", "loc": [-80.289948, 39.210708], "pop": 1558, "state": "WV", "_id": "26408"} -{"city": "NEWBERNE", "loc": [-80.902838, 39.040743], "pop": 86, "state": "WV", "_id": "26409"} -{"city": "NEWBURG", "loc": [-79.828036, 39.402722], "pop": 1206, "state": "WV", "_id": "26410"} -{"city": "NEW MILTON", "loc": [-80.707617, 39.189917], "pop": 783, "state": "WV", "_id": "26411"} -{"city": "ORLANDO", "loc": [-80.560122, 38.885349], "pop": 209, "state": "WV", "_id": "26412"} -{"city": "TOLL GATE", "loc": [-80.958482, 39.292064], "pop": 2597, "state": "WV", "_id": "26415"} -{"city": "BROADDUS", "loc": [-80.038531, 39.150789], "pop": 7934, "state": "WV", "_id": "26416"} -{"city": "HASTINGS", "loc": [-80.677213, 39.565069], "pop": 881, "state": "WV", "_id": "26419"} -{"city": "PULLMAN", "loc": [-80.934685, 39.190013], "pop": 247, "state": "WV", "_id": "26421"} -{"city": "ROANOKE", "loc": [-80.498537, 38.930494], "pop": 576, "state": "WV", "_id": "26423"} -{"city": "MANHEIM", "loc": [-79.642977, 39.221575], "pop": 404, "state": "WV", "_id": "26425"} -{"city": "SALEM", "loc": [-80.589792, 39.294761], "pop": 5874, "state": "WV", "_id": "26426"} -{"city": "SHINNSTON", "loc": [-80.283399, 39.388809], "pop": 6044, "state": "WV", "_id": "26431"} -{"city": "SMITHFIELD", "loc": [-80.558245, 39.5134], "pop": 775, "state": "WV", "_id": "26437"} -{"city": "STOUTS MILLS", "loc": [-80.741131, 38.893335], "pop": 717, "state": "WV", "_id": "26439"} -{"city": "THORNTON", "loc": [-79.909645, 39.332879], "pop": 1144, "state": "WV", "_id": "26440"} -{"city": "TROY", "loc": [-80.754921, 39.083881], "pop": 115, "state": "WV", "_id": "26443"} -{"city": "TUNNELTON", "loc": [-79.747818, 39.362502], "pop": 4076, "state": "WV", "_id": "26444"} -{"city": "WALKERSVILLE", "loc": [-80.480221, 38.850738], "pop": 737, "state": "WV", "_id": "26447"} -{"city": "WALLACE", "loc": [-80.486468, 39.411667], "pop": 1372, "state": "WV", "_id": "26448"} -{"city": "WEST MILFORD", "loc": [-80.363049, 39.203906], "pop": 1365, "state": "WV", "_id": "26451"} -{"city": "WESTON", "loc": [-80.473656, 39.041104], "pop": 9912, "state": "WV", "_id": "26452"} -{"city": "WEST UNION", "loc": [-80.791057, 39.276185], "pop": 3133, "state": "WV", "_id": "26456"} -{"city": "WOLF SUMMIT", "loc": [-80.438704, 39.232985], "pop": 1116, "state": "WV", "_id": "26462"} -{"city": "STAR CITY", "loc": [-79.954225, 39.633858], "pop": 70185, "state": "WV", "_id": "26505"} -{"city": "ALBRIGHT", "loc": [-79.635811, 39.570116], "pop": 858, "state": "WV", "_id": "26519"} -{"city": "BLACKSVILLE", "loc": [-80.232646, 39.695821], "pop": 13, "state": "WV", "_id": "26521"} -{"city": "BRUCETON MILLS", "loc": [-79.615941, 39.645394], "pop": 3857, "state": "WV", "_id": "26525"} -{"city": "CORE", "loc": [-80.23068, 39.654037], "pop": 453, "state": "WV", "_id": "26529"} -{"city": "KINGWOOD", "loc": [-79.70629, 39.481912], "pop": 6716, "state": "WV", "_id": "26537"} -{"city": "MAIDSVILLE", "loc": [-80.083193, 39.709308], "pop": 2047, "state": "WV", "_id": "26541"} -{"city": "CASCADE", "loc": [-79.800993, 39.576413], "pop": 658, "state": "WV", "_id": "26542"} -{"city": "PURSGLOVE", "loc": [-80.061798, 39.699543], "pop": 322, "state": "WV", "_id": "26546"} -{"city": "REEDSVILLE", "loc": [-79.810061, 39.51823], "pop": 3551, "state": "WV", "_id": "26547"} -{"city": "MONONGAH", "loc": [-80.146041, 39.472677], "pop": 41565, "state": "WV", "_id": "26554"} -{"city": "BAXTER", "loc": [-80.141691, 39.536056], "pop": 748, "state": "WV", "_id": "26560"} -{"city": "BIG RUN", "loc": [-80.583717, 39.59414], "pop": 647, "state": "WV", "_id": "26561"} -{"city": "COBURN", "loc": [-80.405931, 39.667984], "pop": 697, "state": "WV", "_id": "26562"} -{"city": "ENTERPRISE", "loc": [-80.316704, 39.425998], "pop": 1800, "state": "WV", "_id": "26568"} -{"city": "FAIRVIEW", "loc": [-80.252918, 39.610335], "pop": 2406, "state": "WV", "_id": "26570"} -{"city": "FARMINGTON", "loc": [-80.256643, 39.50287], "pop": 3622, "state": "WV", "_id": "26571"} -{"city": "HUNDRED", "loc": [-80.474552, 39.691744], "pop": 1214, "state": "WV", "_id": "26575"} -{"city": "LITTLETON", "loc": [-80.569478, 39.659205], "pop": 458, "state": "WV", "_id": "26581"} -{"city": "MANNINGTON", "loc": [-80.355243, 39.522031], "pop": 5323, "state": "WV", "_id": "26582"} -{"city": "METZ", "loc": [-80.416815, 39.607762], "pop": 924, "state": "WV", "_id": "26585"} -{"city": "RACHEL", "loc": [-80.295694, 39.52254], "pop": 274, "state": "WV", "_id": "26587"} -{"city": "RIVESVILLE", "loc": [-80.141744, 39.556839], "pop": 2938, "state": "WV", "_id": "26588"} -{"city": "WADESTOWN", "loc": [-80.327462, 39.636926], "pop": 329, "state": "WV", "_id": "26589"} -{"city": "WANA", "loc": [-80.305106, 39.702072], "pop": 272, "state": "WV", "_id": "26590"} -{"city": "WORTHINGTON", "loc": [-80.290468, 39.452213], "pop": 1506, "state": "WV", "_id": "26591"} -{"city": "HEROLD", "loc": [-80.681072, 38.673855], "pop": 3517, "state": "WV", "_id": "26601"} -{"city": "BIRCH RIVER", "loc": [-80.745873, 38.491424], "pop": 1440, "state": "WV", "_id": "26610"} -{"city": "FLOWER", "loc": [-80.840342, 38.839711], "pop": 308, "state": "WV", "_id": "26611"} -{"city": "COPEN", "loc": [-80.728481, 38.830556], "pop": 161, "state": "WV", "_id": "26615"} -{"city": "DILLE", "loc": [-80.853784, 38.474704], "pop": 410, "state": "WV", "_id": "26617"} -{"city": "ELMIRA", "loc": [-80.984972, 38.658907], "pop": 276, "state": "WV", "_id": "26618"} -{"city": "RIFFLE", "loc": [-80.768123, 38.806866], "pop": 230, "state": "WV", "_id": "26619"} -{"city": "FALLS MILL", "loc": [-80.550618, 38.77566], "pop": 25, "state": "WV", "_id": "26620"} -{"city": "CORLEY", "loc": [-80.638478, 38.724475], "pop": 658, "state": "WV", "_id": "26621"} -{"city": "CLEM", "loc": [-80.877416, 38.636828], "pop": 1276, "state": "WV", "_id": "26623"} -{"city": "GASSAWAY", "loc": [-80.786339, 38.686193], "pop": 2665, "state": "WV", "_id": "26624"} -{"city": "GLENDON", "loc": [-80.939459, 38.610857], "pop": 223, "state": "WV", "_id": "26626"} -{"city": "HEATERS", "loc": [-80.642884, 38.755807], "pop": 368, "state": "WV", "_id": "26627"} -{"city": "TESLA", "loc": [-80.706293, 38.581046], "pop": 1325, "state": "WV", "_id": "26629"} -{"city": "NAPIER", "loc": [-80.584104, 38.77462], "pop": 107, "state": "WV", "_id": "26631"} -{"city": "NICUT", "loc": [-81.011, 38.715133], "pop": 151, "state": "WV", "_id": "26633"} -{"city": "PERKINS", "loc": [-80.919336, 38.809049], "pop": 242, "state": "WV", "_id": "26634"} -{"city": "ROSEDALE", "loc": [-80.906127, 38.770103], "pop": 88, "state": "WV", "_id": "26636"} -{"city": "SHOCK", "loc": [-80.968663, 38.757527], "pop": 250, "state": "WV", "_id": "26638"} -{"city": "STRANGE CREEK", "loc": [-80.903077, 38.573823], "pop": 206, "state": "WV", "_id": "26639"} -{"city": "WILSIE", "loc": [-80.868051, 38.672723], "pop": 150, "state": "WV", "_id": "26641"} -{"city": "SUMMERSVILLE", "loc": [-80.835278, 38.30089], "pop": 6520, "state": "WV", "_id": "26651"} -{"city": "BELVA", "loc": [-81.156572, 38.249105], "pop": 415, "state": "WV", "_id": "26656"} -{"city": "CALVIN", "loc": [-80.70163, 38.327679], "pop": 603, "state": "WV", "_id": "26660"} -{"city": "CANVAS", "loc": [-80.744637, 38.262445], "pop": 1163, "state": "WV", "_id": "26662"} -{"city": "DRENNEN", "loc": [-81.008868, 38.258729], "pop": 263, "state": "WV", "_id": "26667"} -{"city": "GILBOA", "loc": [-80.952232, 38.298304], "pop": 422, "state": "WV", "_id": "26671"} -{"city": "JODIE", "loc": [-81.134657, 38.208319], "pop": 510, "state": "WV", "_id": "26674"} -{"city": "KESLERS CROSS LA", "loc": [-80.933365, 38.225762], "pop": 188, "state": "WV", "_id": "26675"} -{"city": "LEIVASY", "loc": [-80.717213, 38.135019], "pop": 1107, "state": "WV", "_id": "26676"} -{"city": "MOUNT LOOKOUT", "loc": [-80.91077, 38.182109], "pop": 1029, "state": "WV", "_id": "26678"} -{"city": "RUNA", "loc": [-80.820204, 38.197064], "pop": 1698, "state": "WV", "_id": "26679"} -{"city": "RUSSELVILLE", "loc": [-80.885777, 38.10829], "pop": 99, "state": "WV", "_id": "26680"} -{"city": "NETTIE", "loc": [-80.695271, 38.209042], "pop": 1196, "state": "WV", "_id": "26681"} -{"city": "POE", "loc": [-80.952167, 38.256458], "pop": 818, "state": "WV", "_id": "26683"} -{"city": "POOL", "loc": [-80.885907, 38.146004], "pop": 112, "state": "WV", "_id": "26684"} -{"city": "SWISS", "loc": [-81.080703, 38.279058], "pop": 684, "state": "WV", "_id": "26690"} -{"city": "TIOGA", "loc": [-80.661663, 38.375989], "pop": 808, "state": "WV", "_id": "26691"} -{"city": "AUGUSTA", "loc": [-78.593089, 39.299821], "pop": 2769, "state": "WV", "_id": "26704"} -{"city": "AMBOY", "loc": [-79.571088, 39.304719], "pop": 1143, "state": "WV", "_id": "26705"} -{"city": "BURLINGTON", "loc": [-78.912278, 39.388709], "pop": 3883, "state": "WV", "_id": "26710"} -{"city": "CAPON BRIDGE", "loc": [-78.464413, 39.280198], "pop": 997, "state": "WV", "_id": "26711"} -{"city": "CORINTH", "loc": [-79.503965, 39.43183], "pop": 503, "state": "WV", "_id": "26713"} -{"city": "DELRAY", "loc": [-78.639427, 39.179564], "pop": 467, "state": "WV", "_id": "26714"} -{"city": "EGLON", "loc": [-79.510919, 39.292514], "pop": 249, "state": "WV", "_id": "26716"} -{"city": "ELK GARDEN", "loc": [-79.153871, 39.363088], "pop": 1168, "state": "WV", "_id": "26717"} -{"city": "FORT ASHBY", "loc": [-78.774296, 39.497045], "pop": 2212, "state": "WV", "_id": "26719"} -{"city": "GORMANIA", "loc": [-79.340136, 39.271499], "pop": 823, "state": "WV", "_id": "26720"} -{"city": "GREEN SPRING", "loc": [-78.627191, 39.502931], "pop": 739, "state": "WV", "_id": "26722"} -{"city": "SCHERR", "loc": [-78.988667, 39.430406], "pop": 9579, "state": "WV", "_id": "26726"} -{"city": "KIRBY", "loc": [-78.731468, 39.195588], "pop": 303, "state": "WV", "_id": "26729"} -{"city": "LAHMANSVILLE", "loc": [-79.043319, 39.164225], "pop": 32, "state": "WV", "_id": "26731"} -{"city": "MEDLEY", "loc": [-79.0984, 39.236079], "pop": 430, "state": "WV", "_id": "26734"} -{"city": "MOUNT STORM", "loc": [-79.225454, 39.265224], "pop": 861, "state": "WV", "_id": "26739"} -{"city": "NEW CREEK", "loc": [-79.045212, 39.361155], "pop": 1417, "state": "WV", "_id": "26743"} -{"city": "PIEDMONT", "loc": [-79.048862, 39.479901], "pop": 1094, "state": "WV", "_id": "26750"} -{"city": "PATTERSON CREEK", "loc": [-78.792006, 39.581796], "pop": 5929, "state": "WV", "_id": "26753"} -{"city": "RIO", "loc": [-78.693548, 39.180663], "pop": 47, "state": "WV", "_id": "26755"} -{"city": "ROMNEY", "loc": [-78.748075, 39.342369], "pop": 4007, "state": "WV", "_id": "26757"} -{"city": "SHANKS", "loc": [-78.699531, 39.284859], "pop": 779, "state": "WV", "_id": "26761"} -{"city": "SPRINGFIELD", "loc": [-78.695019, 39.462996], "pop": 1321, "state": "WV", "_id": "26763"} -{"city": "HOPEMONT", "loc": [-79.557234, 39.441575], "pop": 4487, "state": "WV", "_id": "26764"} -{"city": "THREE CHURCHES", "loc": [-78.661061, 39.351578], "pop": 1253, "state": "WV", "_id": "26765"} -{"city": "WILEY FORD", "loc": [-78.774992, 39.613308], "pop": 1139, "state": "WV", "_id": "26767"} -{"city": "HORSE SHOE RUN", "loc": [-79.512312, 39.252842], "pop": 367, "state": "WV", "_id": "26769"} -{"city": "BAKER", "loc": [-78.774826, 39.065588], "pop": 1321, "state": "WV", "_id": "26801"} -{"city": "BRANDYWINE", "loc": [-79.209629, 38.635701], "pop": 941, "state": "WV", "_id": "26802"} -{"city": "CIRCLEVILLE", "loc": [-79.53672, 38.623039], "pop": 658, "state": "WV", "_id": "26804"} -{"city": "FORT SEYBERT", "loc": [-79.140781, 38.739489], "pop": 167, "state": "WV", "_id": "26806"} -{"city": "FRANKLIN", "loc": [-79.353776, 38.639985], "pop": 2733, "state": "WV", "_id": "26807"} -{"city": "HIGH VIEW", "loc": [-78.442548, 39.222497], "pop": 922, "state": "WV", "_id": "26808"} -{"city": "LOST CITY", "loc": [-78.822684, 38.895361], "pop": 190, "state": "WV", "_id": "26810"} -{"city": "LOST RIVER", "loc": [-78.79317, 38.93448], "pop": 167, "state": "WV", "_id": "26811"} -{"city": "MATHIAS", "loc": [-78.881413, 38.874443], "pop": 1228, "state": "WV", "_id": "26812"} -{"city": "MOYERS", "loc": [-79.383011, 38.500561], "pop": 461, "state": "WV", "_id": "26813"} -{"city": "RIVERTON", "loc": [-79.465531, 38.697871], "pop": 348, "state": "WV", "_id": "26814"} -{"city": "SUGAR GROVE", "loc": [-79.290329, 38.52011], "pop": 512, "state": "WV", "_id": "26815"} -{"city": "ARTHUR", "loc": [-79.099576, 39.101237], "pop": 581, "state": "WV", "_id": "26816"} -{"city": "BLOOMERY", "loc": [-78.381561, 39.328606], "pop": 675, "state": "WV", "_id": "26817"} -{"city": "FISHER", "loc": [-78.988756, 39.04796], "pop": 1, "state": "WV", "_id": "26818"} -{"city": "JUNCTION", "loc": [-78.832785, 39.330441], "pop": 200, "state": "WV", "_id": "26824"} -{"city": "MAYSVILLE", "loc": [-79.1937, 39.071174], "pop": 2615, "state": "WV", "_id": "26833"} -{"city": "RIG", "loc": [-78.97793, 39.056812], "pop": 6314, "state": "WV", "_id": "26836"} -{"city": "MILAM", "loc": [-79.072936, 38.812526], "pop": 30, "state": "WV", "_id": "26838"} -{"city": "OLD FIELDS", "loc": [-78.950944, 39.150245], "pop": 120, "state": "WV", "_id": "26845"} -{"city": "DORCAS", "loc": [-79.130701, 38.976602], "pop": 4991, "state": "WV", "_id": "26847"} -{"city": "WARDENSVILLE", "loc": [-78.617457, 39.056238], "pop": 1606, "state": "WV", "_id": "26851"} -{"city": "PURGITSVILLE", "loc": [-78.905951, 39.268065], "pop": 672, "state": "WV", "_id": "26852"} -{"city": "CABINS", "loc": [-79.278332, 38.951199], "pop": 95, "state": "WV", "_id": "26855"} -{"city": "LEHEW", "loc": [-78.494998, 39.183038], "pop": 190, "state": "WV", "_id": "26865"} -{"city": "UPPER TRACT", "loc": [-79.258589, 38.794952], "pop": 1161, "state": "WV", "_id": "26866"} -{"city": "SENECA ROCKS", "loc": [-79.386577, 38.829615], "pop": 840, "state": "WV", "_id": "26884"} -{"city": "ONEGO", "loc": [-79.450702, 38.801718], "pop": 233, "state": "WV", "_id": "26886"} -{"city": "ADELL", "loc": [-88.025394, 43.615071], "pop": 1221, "state": "WI", "_id": "53001"} -{"city": "ALLENTON", "loc": [-88.353901, 43.468065], "pop": 1449, "state": "WI", "_id": "53002"} -{"city": "BELGIUM", "loc": [-87.850908, 43.499465], "pop": 2333, "state": "WI", "_id": "53004"} -{"city": "BROOKFIELD", "loc": [-88.098, 43.062173], "pop": 19793, "state": "WI", "_id": "53005"} -{"city": "SOUTH BYRON", "loc": [-88.509729, 43.610934], "pop": 1842, "state": "WI", "_id": "53006"} -{"city": "BUTLER", "loc": [-88.071043, 43.105405], "pop": 2079, "state": "WI", "_id": "53007"} -{"city": "CAMPBELLSPORT", "loc": [-88.272855, 43.604183], "pop": 6057, "state": "WI", "_id": "53010"} -{"city": "CASCADE", "loc": [-88.094658, 43.659106], "pop": 632, "state": "WI", "_id": "53011"} -{"city": "CEDARBURG", "loc": [-88.002867, 43.303413], "pop": 17552, "state": "WI", "_id": "53012"} -{"city": "CEDAR GROVE", "loc": [-87.840108, 43.575076], "pop": 2656, "state": "WI", "_id": "53013"} -{"city": "CHILTON", "loc": [-88.182689, 44.024242], "pop": 7495, "state": "WI", "_id": "53014"} -{"city": "CLEVELAND", "loc": [-87.767314, 43.921732], "pop": 2490, "state": "WI", "_id": "53015"} -{"city": "COLGATE", "loc": [-88.240614, 43.199767], "pop": 4155, "state": "WI", "_id": "53017"} -{"city": "DELAFIELD", "loc": [-88.397248, 43.050019], "pop": 4837, "state": "WI", "_id": "53018"} -{"city": "EDEN", "loc": [-88.326607, 43.695878], "pop": 1874, "state": "WI", "_id": "53019"} -{"city": "ELKHART LAKE", "loc": [-87.97667, 43.843553], "pop": 4665, "state": "WI", "_id": "53020"} -{"city": "WAUBEKA", "loc": [-87.982243, 43.484319], "pop": 3942, "state": "WI", "_id": "53021"} -{"city": "GERMANTOWN", "loc": [-88.116508, 43.218871], "pop": 13053, "state": "WI", "_id": "53022"} -{"city": "GLENBEULAH", "loc": [-88.104886, 43.766611], "pop": 1965, "state": "WI", "_id": "53023"} -{"city": "GRAFTON", "loc": [-87.952087, 43.323146], "pop": 12526, "state": "WI", "_id": "53024"} -{"city": "HARTFORD", "loc": [-88.370727, 43.315749], "pop": 15889, "state": "WI", "_id": "53027"} -{"city": "HARTLAND", "loc": [-88.344572, 43.117153], "pop": 14530, "state": "WI", "_id": "53029"} -{"city": "HORICON", "loc": [-88.631049, 43.446859], "pop": 4724, "state": "WI", "_id": "53032"} -{"city": "HUBERTUS", "loc": [-88.231147, 43.234269], "pop": 4823, "state": "WI", "_id": "53033"} -{"city": "IRON RIDGE", "loc": [-88.544072, 43.393415], "pop": 2504, "state": "WI", "_id": "53035"} -{"city": "IXONIA", "loc": [-88.580567, 43.170667], "pop": 2454, "state": "WI", "_id": "53036"} -{"city": "JACKSON", "loc": [-88.162592, 43.325243], "pop": 3999, "state": "WI", "_id": "53037"} -{"city": "JOHNSON CREEK", "loc": [-88.783602, 43.075051], "pop": 2469, "state": "WI", "_id": "53038"} -{"city": "JUNEAU", "loc": [-88.684517, 43.379199], "pop": 5857, "state": "WI", "_id": "53039"} -{"city": "KEWASKUM", "loc": [-88.19253, 43.521446], "pop": 6394, "state": "WI", "_id": "53040"} -{"city": "KIEL", "loc": [-87.995644, 43.934105], "pop": 5345, "state": "WI", "_id": "53042"} -{"city": "KOHLER", "loc": [-87.78673, 43.738096], "pop": 1900, "state": "WI", "_id": "53044"} -{"city": "BROOKFIELD", "loc": [-88.146946, 43.066791], "pop": 15412, "state": "WI", "_id": "53045"} -{"city": "LANNON", "loc": [-88.163863, 43.149651], "pop": 924, "state": "WI", "_id": "53046"} -{"city": "KNOWLES", "loc": [-88.441309, 43.588489], "pop": 2101, "state": "WI", "_id": "53048"} -{"city": "MALONE", "loc": [-88.307289, 43.869002], "pop": 3453, "state": "WI", "_id": "53049"} -{"city": "MAYVILLE", "loc": [-88.545084, 43.504496], "pop": 6394, "state": "WI", "_id": "53050"} -{"city": "MENOMONEE FALLS", "loc": [-88.112774, 43.160174], "pop": 26840, "state": "WI", "_id": "53051"} -{"city": "MOUNT CALVARY", "loc": [-88.239922, 43.814211], "pop": 1403, "state": "WI", "_id": "53057"} -{"city": "NASHOTAH", "loc": [-88.408913, 43.111791], "pop": 2996, "state": "WI", "_id": "53058"} -{"city": "NEOSHO", "loc": [-88.520482, 43.297841], "pop": 2051, "state": "WI", "_id": "53059"} -{"city": "NEW HOLSTEIN", "loc": [-88.091083, 43.944639], "pop": 5536, "state": "WI", "_id": "53061"} -{"city": "NEWTON", "loc": [-87.784764, 43.983621], "pop": 1394, "state": "WI", "_id": "53063"} -{"city": "OAKFIELD", "loc": [-88.556919, 43.686371], "pop": 2377, "state": "WI", "_id": "53065"} -{"city": "OCONOMOWOC", "loc": [-88.486229, 43.109497], "pop": 24795, "state": "WI", "_id": "53066"} -{"city": "OKAUCHEE", "loc": [-88.432287, 43.113011], "pop": 848, "state": "WI", "_id": "53069"} -{"city": "OOSTBURG", "loc": [-87.796955, 43.622939], "pop": 3916, "state": "WI", "_id": "53070"} -{"city": "PEWAUKEE", "loc": [-88.272922, 43.078777], "pop": 13337, "state": "WI", "_id": "53072"} -{"city": "PLYMOUTH", "loc": [-87.977906, 43.75258], "pop": 11811, "state": "WI", "_id": "53073"} -{"city": "PORT WASHINGTON", "loc": [-87.879659, 43.395463], "pop": 10829, "state": "WI", "_id": "53074"} -{"city": "RANDOM LAKE", "loc": [-87.981553, 43.567206], "pop": 3299, "state": "WI", "_id": "53075"} -{"city": "RICHFIELD", "loc": [-88.215467, 43.273925], "pop": 2810, "state": "WI", "_id": "53076"} -{"city": "RUBICON", "loc": [-88.452793, 43.312144], "pop": 1023, "state": "WI", "_id": "53078"} -{"city": "SAINT CLOUD", "loc": [-88.184482, 43.807431], "pop": 1524, "state": "WI", "_id": "53079"} -{"city": "SAUKVILLE", "loc": [-87.956765, 43.391265], "pop": 5460, "state": "WI", "_id": "53080"} -{"city": "SHEBOYGAN", "loc": [-87.724667, 43.740981], "pop": 42246, "state": "WI", "_id": "53081"} -{"city": "HOWARDS GROVE", "loc": [-87.748552, 43.788609], "pop": 17004, "state": "WI", "_id": "53083"} -{"city": "SHEBOYGAN FALLS", "loc": [-87.824228, 43.726598], "pop": 9457, "state": "WI", "_id": "53085"} -{"city": "SLINGER", "loc": [-88.282377, 43.332211], "pop": 4671, "state": "WI", "_id": "53086"} -{"city": "SUSSEX", "loc": [-88.227064, 43.144059], "pop": 11913, "state": "WI", "_id": "53089"} -{"city": "THERESA", "loc": [-88.447766, 43.504477], "pop": 1555, "state": "WI", "_id": "53091"} -{"city": "MEQUON", "loc": [-87.959357, 43.225145], "pop": 22294, "state": "WI", "_id": "53092"} -{"city": "WALDO", "loc": [-87.97217, 43.657087], "pop": 2471, "state": "WI", "_id": "53093"} -{"city": "WATERTOWN", "loc": [-88.71851, 43.192971], "pop": 25240, "state": "WI", "_id": "53094"} -{"city": "WEST BEND", "loc": [-88.184549, 43.422444], "pop": 38743, "state": "WI", "_id": "53095"} -{"city": "BIG BEND", "loc": [-88.212182, 42.888463], "pop": 4274, "state": "WI", "_id": "53103"} -{"city": "BRISTOL", "loc": [-88.047779, 42.532525], "pop": 4514, "state": "WI", "_id": "53104"} -{"city": "BURLINGTON", "loc": [-88.274886, 42.666034], "pop": 23978, "state": "WI", "_id": "53105"} -{"city": "CALEDONIA", "loc": [-87.92278, 42.829473], "pop": 2453, "state": "WI", "_id": "53108"} -{"city": "CUDAHY", "loc": [-87.861983, 42.948976], "pop": 18659, "state": "WI", "_id": "53110"} -{"city": "DARIEN", "loc": [-88.714217, 42.6435], "pop": 3954, "state": "WI", "_id": "53114"} -{"city": "DELAVAN", "loc": [-88.627717, 42.622715], "pop": 9967, "state": "WI", "_id": "53115"} -{"city": "DOUSMAN", "loc": [-88.456754, 42.98506], "pop": 5699, "state": "WI", "_id": "53118"} -{"city": "EAGLE", "loc": [-88.46742, 42.880942], "pop": 3129, "state": "WI", "_id": "53119"} -{"city": "EAST TROY", "loc": [-88.409215, 42.803531], "pop": 8712, "state": "WI", "_id": "53120"} -{"city": "ELKHORN", "loc": [-88.546209, 42.700928], "pop": 11680, "state": "WI", "_id": "53121"} -{"city": "ELM GROVE", "loc": [-88.085374, 43.047943], "pop": 6394, "state": "WI", "_id": "53122"} -{"city": "FONTANA", "loc": [-88.568424, 42.542928], "pop": 1697, "state": "WI", "_id": "53125"} -{"city": "FRANKSVILLE", "loc": [-87.987273, 42.785892], "pop": 6685, "state": "WI", "_id": "53126"} -{"city": "GENOA CITY", "loc": [-88.348456, 42.532276], "pop": 5027, "state": "WI", "_id": "53128"} -{"city": "GREENDALE", "loc": [-87.994277, 42.937293], "pop": 15109, "state": "WI", "_id": "53129"} -{"city": "HALES CORNERS", "loc": [-88.0503, 42.941034], "pop": 7643, "state": "WI", "_id": "53130"} -{"city": "FRANKLIN", "loc": [-88.008582, 42.901728], "pop": 21840, "state": "WI", "_id": "53132"} -{"city": "HELENVILLE", "loc": [-88.726887, 43.01007], "pop": 1440, "state": "WI", "_id": "53137"} -{"city": "KANSASVILLE", "loc": [-88.118037, 42.701246], "pop": 2911, "state": "WI", "_id": "53139"} -{"city": "KENOSHA", "loc": [-87.829945, 42.605228], "pop": 28062, "state": "WI", "_id": "53140"} -{"city": "KENOSHA", "loc": [-87.870526, 42.556038], "pop": 28495, "state": "WI", "_id": "53142"} -{"city": "KENOSHA", "loc": [-87.830053, 42.561726], "pop": 26551, "state": "WI", "_id": "53143"} -{"city": "KENOSHA", "loc": [-87.876171, 42.605788], "pop": 18824, "state": "WI", "_id": "53144"} -{"city": "NEW BERLIN", "loc": [-88.155274, 42.97397], "pop": 8149, "state": "WI", "_id": "53146"} -{"city": "LAKE GENEVA", "loc": [-88.455379, 42.588111], "pop": 11183, "state": "WI", "_id": "53147"} -{"city": "MUKWONAGO", "loc": [-88.345116, 42.882054], "pop": 12846, "state": "WI", "_id": "53149"} -{"city": "MUSKEGO", "loc": [-88.121411, 42.904651], "pop": 17098, "state": "WI", "_id": "53150"} -{"city": "NEW BERLIN", "loc": [-88.094642, 42.982151], "pop": 25731, "state": "WI", "_id": "53151"} -{"city": "NORTH PRAIRIE", "loc": [-88.394988, 42.938501], "pop": 1732, "state": "WI", "_id": "53153"} -{"city": "OAK CREEK", "loc": [-87.902661, 42.88916], "pop": 19513, "state": "WI", "_id": "53154"} -{"city": "PALMYRA", "loc": [-88.590255, 42.879263], "pop": 2748, "state": "WI", "_id": "53156"} -{"city": "SALEM", "loc": [-88.128731, 42.570922], "pop": 8746, "state": "WI", "_id": "53168"} -{"city": "SOUTH MILWAUKEE", "loc": [-87.864626, 42.910468], "pop": 20958, "state": "WI", "_id": "53172"} -{"city": "STURTEVANT", "loc": [-87.903082, 42.69673], "pop": 4774, "state": "WI", "_id": "53177"} -{"city": "SULLIVAN", "loc": [-88.602569, 42.99825], "pop": 3381, "state": "WI", "_id": "53178"} -{"city": "TREVOR", "loc": [-88.13869, 42.520112], "pop": 3527, "state": "WI", "_id": "53179"} -{"city": "TWIN LAKES", "loc": [-88.257318, 42.523173], "pop": 6103, "state": "WI", "_id": "53181"} -{"city": "UNION GROVE", "loc": [-88.039004, 42.689643], "pop": 6233, "state": "WI", "_id": "53182"} -{"city": "WALES", "loc": [-88.378742, 43.008787], "pop": 3171, "state": "WI", "_id": "53183"} -{"city": "WALWORTH", "loc": [-88.602738, 42.535005], "pop": 2831, "state": "WI", "_id": "53184"} -{"city": "WIND LAKE", "loc": [-88.19335, 42.796882], "pop": 11362, "state": "WI", "_id": "53185"} -{"city": "WAUKESHA", "loc": [-88.219559, 42.999304], "pop": 46445, "state": "WI", "_id": "53186"} -{"city": "WAUKESHA", "loc": [-88.27048, 43.012848], "pop": 33794, "state": "WI", "_id": "53188"} -{"city": "WHITEWATER", "loc": [-88.742864, 42.827174], "pop": 16723, "state": "WI", "_id": "53190"} -{"city": "WILLIAMS BAY", "loc": [-88.543089, 42.576678], "pop": 2208, "state": "WI", "_id": "53191"} -{"city": "MILWAUKEE", "loc": [-87.896792, 43.050601], "pop": 20178, "state": "WI", "_id": "53202"} -{"city": "MILWAUKEE", "loc": [-87.915375, 43.040299], "pop": 456, "state": "WI", "_id": "53203"} -{"city": "MILWAUKEE", "loc": [-87.931685, 43.015778], "pop": 41978, "state": "WI", "_id": "53204"} -{"city": "MILWAUKEE", "loc": [-87.935332, 43.052841], "pop": 14708, "state": "WI", "_id": "53205"} -{"city": "MILWAUKEE", "loc": [-87.934714, 43.075324], "pop": 42009, "state": "WI", "_id": "53206"} -{"city": "BAY VIEW", "loc": [-87.894598, 42.981405], "pop": 49199, "state": "WI", "_id": "53207"} -{"city": "MILWAUKEE", "loc": [-87.962454, 43.048775], "pop": 42238, "state": "WI", "_id": "53208"} -{"city": "MILWAUKEE", "loc": [-87.947834, 43.118765], "pop": 51008, "state": "WI", "_id": "53209"} -{"city": "MILWAUKEE", "loc": [-87.971466, 43.068545], "pop": 32111, "state": "WI", "_id": "53210"} -{"city": "SHOREWOOD", "loc": [-87.885078, 43.080517], "pop": 37036, "state": "WI", "_id": "53211"} -{"city": "MILWAUKEE", "loc": [-87.908415, 43.071195], "pop": 37237, "state": "WI", "_id": "53212"} -{"city": "WAUWATOSA", "loc": [-88.000757, 43.051316], "pop": 27606, "state": "WI", "_id": "53213"} -{"city": "WEST ALLIS", "loc": [-88.010757, 43.019113], "pop": 38491, "state": "WI", "_id": "53214"} -{"city": "WEST MILWAUKEE", "loc": [-87.94174, 43.000411], "pop": 48228, "state": "WI", "_id": "53215"} -{"city": "MILWAUKEE", "loc": [-87.974218, 43.085868], "pop": 34881, "state": "WI", "_id": "53216"} -{"city": "MILWAUKEE", "loc": [-87.907261, 43.14086], "pop": 30065, "state": "WI", "_id": "53217"} -{"city": "MILWAUKEE", "loc": [-87.993161, 43.11218], "pop": 40443, "state": "WI", "_id": "53218"} -{"city": "MILWAUKEE", "loc": [-87.994368, 42.995909], "pop": 35271, "state": "WI", "_id": "53219"} -{"city": "GREENFIELD", "loc": [-87.992209, 42.968186], "pop": 25819, "state": "WI", "_id": "53220"} -{"city": "MILWAUKEE", "loc": [-87.944734, 42.954864], "pop": 35767, "state": "WI", "_id": "53221"} -{"city": "MILWAUKEE", "loc": [-88.02687, 43.08283], "pop": 25406, "state": "WI", "_id": "53222"} -{"city": "MILWAUKEE", "loc": [-87.989818, 43.162374], "pop": 30272, "state": "WI", "_id": "53223"} -{"city": "MILWAUKEE", "loc": [-88.032744, 43.159415], "pop": 18182, "state": "WI", "_id": "53224"} -{"city": "MILWAUKEE", "loc": [-88.03464, 43.115416], "pop": 25395, "state": "WI", "_id": "53225"} -{"city": "WAUWATOSA", "loc": [-88.041386, 43.050006], "pop": 19216, "state": "WI", "_id": "53226"} -{"city": "MILWAUKEE", "loc": [-88.036384, 42.994919], "pop": 23150, "state": "WI", "_id": "53227"} -{"city": "GREENFIELD", "loc": [-88.034638, 42.970251], "pop": 12634, "state": "WI", "_id": "53228"} -{"city": "MILWAUKEE", "loc": [-87.93566, 43.040738], "pop": 16569, "state": "WI", "_id": "53233"} -{"city": "RACINE", "loc": [-87.795985, 42.772596], "pop": 31959, "state": "WI", "_id": "53402"} -{"city": "RACINE", "loc": [-87.801375, 42.706015], "pop": 26329, "state": "WI", "_id": "53403"} -{"city": "RACINE", "loc": [-87.8053, 42.743348], "pop": 17129, "state": "WI", "_id": "53404"} -{"city": "RACINE", "loc": [-87.823329, 42.716112], "pop": 26652, "state": "WI", "_id": "53405"} -{"city": "RACINE", "loc": [-87.855104, 42.724162], "pop": 20925, "state": "WI", "_id": "53406"} -{"city": "ALBANY", "loc": [-89.435695, 42.715535], "pop": 1886, "state": "WI", "_id": "53502"} -{"city": "ARENA", "loc": [-89.938614, 43.152111], "pop": 1554, "state": "WI", "_id": "53503"} -{"city": "ARGYLE", "loc": [-89.859834, 42.695532], "pop": 2048, "state": "WI", "_id": "53504"} -{"city": "AVALON", "loc": [-88.837142, 42.65979], "pop": 373, "state": "WI", "_id": "53505"} -{"city": "AVOCA", "loc": [-90.288859, 43.157137], "pop": 1072, "state": "WI", "_id": "53506"} -{"city": "BARNEVELD", "loc": [-89.904229, 43.015797], "pop": 1301, "state": "WI", "_id": "53507"} -{"city": "BELLEVILLE", "loc": [-89.53773, 42.866906], "pop": 3500, "state": "WI", "_id": "53508"} -{"city": "BELMONT", "loc": [-90.335881, 42.730784], "pop": 1477, "state": "WI", "_id": "53510"} -{"city": "SHOPIERE", "loc": [-89.039897, 42.522871], "pop": 46738, "state": "WI", "_id": "53511"} -{"city": "BLACK EARTH", "loc": [-89.738974, 43.132213], "pop": 1824, "state": "WI", "_id": "53515"} -{"city": "BLANCHARDVILLE", "loc": [-89.855511, 42.806186], "pop": 1810, "state": "WI", "_id": "53516"} -{"city": "BLUE MOUNDS", "loc": [-89.834455, 43.004964], "pop": 639, "state": "WI", "_id": "53517"} -{"city": "BLUE RIVER", "loc": [-90.587312, 43.236567], "pop": 1520, "state": "WI", "_id": "53518"} -{"city": "BRODHEAD", "loc": [-89.371409, 42.611074], "pop": 5825, "state": "WI", "_id": "53520"} -{"city": "BROOKLYN", "loc": [-89.381814, 42.842327], "pop": 2329, "state": "WI", "_id": "53521"} -{"city": "BROWNTOWN", "loc": [-89.78158, 42.557872], "pop": 1146, "state": "WI", "_id": "53522"} -{"city": "CAMBRIDGE", "loc": [-89.020901, 42.99176], "pop": 4102, "state": "WI", "_id": "53523"} -{"city": "CLINTON", "loc": [-88.870477, 42.553481], "pop": 3627, "state": "WI", "_id": "53525"} -{"city": "COBB", "loc": [-90.332408, 42.966009], "pop": 542, "state": "WI", "_id": "53526"} -{"city": "COTTAGE GROVE", "loc": [-89.201692, 43.078405], "pop": 4349, "state": "WI", "_id": "53527"} -{"city": "CROSS PLAINS", "loc": [-89.639665, 43.113214], "pop": 3622, "state": "WI", "_id": "53528"} -{"city": "DANE", "loc": [-89.51174, 43.242414], "pop": 1572, "state": "WI", "_id": "53529"} -{"city": "DARLINGTON", "loc": [-90.110648, 42.687816], "pop": 4224, "state": "WI", "_id": "53530"} -{"city": "DEERFIELD", "loc": [-89.086199, 43.057106], "pop": 2668, "state": "WI", "_id": "53531"} -{"city": "DE FOREST", "loc": [-89.329652, 43.235748], "pop": 9321, "state": "WI", "_id": "53532"} -{"city": "DODGEVILLE", "loc": [-90.140433, 42.969779], "pop": 5663, "state": "WI", "_id": "53533"} -{"city": "EDGERTON", "loc": [-89.064157, 42.838605], "pop": 9749, "state": "WI", "_id": "53534"} -{"city": "EVANSVILLE", "loc": [-89.287712, 42.773216], "pop": 5986, "state": "WI", "_id": "53536"} -{"city": "FORT ATKINSON", "loc": [-88.846689, 42.922902], "pop": 15624, "state": "WI", "_id": "53538"} -{"city": "GRATIOT", "loc": [-90.024344, 42.575852], "pop": 1129, "state": "WI", "_id": "53541"} -{"city": "HIGHLAND", "loc": [-90.36501, 43.052368], "pop": 1586, "state": "WI", "_id": "53543"} -{"city": "HOLLANDALE", "loc": [-89.912973, 42.877321], "pop": 591, "state": "WI", "_id": "53544"} -{"city": "JANESVILLE", "loc": [-89.033124, 42.691542], "pop": 40339, "state": "WI", "_id": "53545"} -{"city": "JANESVILLE", "loc": [-89.002534, 42.668254], "pop": 22324, "state": "WI", "_id": "53546"} -{"city": "JEFFERSON", "loc": [-88.807765, 43.000557], "pop": 8540, "state": "WI", "_id": "53549"} -{"city": "JUDA", "loc": [-89.502608, 42.567874], "pop": 1177, "state": "WI", "_id": "53550"} -{"city": "LAKE MILLS", "loc": [-88.913335, 43.081576], "pop": 6196, "state": "WI", "_id": "53551"} -{"city": "LINDEN", "loc": [-90.279726, 42.918419], "pop": 522, "state": "WI", "_id": "53553"} -{"city": "LIVINGSTON", "loc": [-90.441986, 42.904432], "pop": 964, "state": "WI", "_id": "53554"} -{"city": "LODI", "loc": [-89.555421, 43.326964], "pop": 5765, "state": "WI", "_id": "53555"} -{"city": "LONE ROCK", "loc": [-90.235229, 43.220773], "pop": 2367, "state": "WI", "_id": "53556"} -{"city": "LOWELL", "loc": [-88.787263, 43.342018], "pop": 231, "state": "WI", "_id": "53557"} -{"city": "MC FARLAND", "loc": [-89.294773, 43.010651], "pop": 8040, "state": "WI", "_id": "53558"} -{"city": "MARSHALL", "loc": [-89.075331, 43.163639], "pop": 4175, "state": "WI", "_id": "53559"} -{"city": "MAZOMANIE", "loc": [-89.763616, 43.184746], "pop": 3176, "state": "WI", "_id": "53560"} -{"city": "MERRIMAC", "loc": [-89.632254, 43.385527], "pop": 1366, "state": "WI", "_id": "53561"} -{"city": "MIDDLETON", "loc": [-89.507258, 43.105196], "pop": 16575, "state": "WI", "_id": "53562"} -{"city": "MILTON", "loc": [-88.953087, 42.779087], "pop": 7309, "state": "WI", "_id": "53563"} -{"city": "MINERAL POINT", "loc": [-90.164556, 42.854696], "pop": 4566, "state": "WI", "_id": "53565"} -{"city": "MONROE", "loc": [-89.6403, 42.599076], "pop": 13720, "state": "WI", "_id": "53566"} -{"city": "MONTFORT", "loc": [-90.44473, 42.975745], "pop": 1129, "state": "WI", "_id": "53569"} -{"city": "MONTICELLO", "loc": [-89.608069, 42.741482], "pop": 2315, "state": "WI", "_id": "53570"} -{"city": "MOUNT HOREB", "loc": [-89.741448, 43.002022], "pop": 7193, "state": "WI", "_id": "53572"} -{"city": "MUSCODA", "loc": [-90.456998, 43.207375], "pop": 3774, "state": "WI", "_id": "53573"} -{"city": "NEW GLARUS", "loc": [-89.643666, 42.814344], "pop": 2540, "state": "WI", "_id": "53574"} -{"city": "OREGON", "loc": [-89.387002, 42.929485], "pop": 9719, "state": "WI", "_id": "53575"} -{"city": "ORFORDVILLE", "loc": [-89.253292, 42.627766], "pop": 1923, "state": "WI", "_id": "53576"} -{"city": "PLAIN", "loc": [-90.055825, 43.292693], "pop": 1292, "state": "WI", "_id": "53577"} -{"city": "PRAIRIE DU SAC", "loc": [-89.745233, 43.295631], "pop": 4060, "state": "WI", "_id": "53578"} -{"city": "REESEVILLE", "loc": [-88.857117, 43.301278], "pop": 2027, "state": "WI", "_id": "53579"} -{"city": "REWEY", "loc": [-90.380361, 42.859393], "pop": 601, "state": "WI", "_id": "53580"} -{"city": "GILLINGHAM", "loc": [-90.391378, 43.362714], "pop": 10306, "state": "WI", "_id": "53581"} -{"city": "RIDGEWAY", "loc": [-89.988946, 43.007654], "pop": 924, "state": "WI", "_id": "53582"} -{"city": "SAUK CITY", "loc": [-89.741777, 43.268558], "pop": 5186, "state": "WI", "_id": "53583"} -{"city": "SHARON", "loc": [-88.726522, 42.519367], "pop": 2297, "state": "WI", "_id": "53585"} -{"city": "SHULLSBURG", "loc": [-90.226619, 42.57856], "pop": 2124, "state": "WI", "_id": "53586"} -{"city": "SOUTH WAYNE", "loc": [-89.888844, 42.582199], "pop": 1444, "state": "WI", "_id": "53587"} -{"city": "SPRING GREEN", "loc": [-90.067608, 43.18835], "pop": 3439, "state": "WI", "_id": "53588"} -{"city": "STOUGHTON", "loc": [-89.223989, 42.929007], "pop": 15469, "state": "WI", "_id": "53589"} -{"city": "SUN PRAIRIE", "loc": [-89.222662, 43.186901], "pop": 18776, "state": "WI", "_id": "53590"} -{"city": "VERONA", "loc": [-89.552247, 42.999913], "pop": 9932, "state": "WI", "_id": "53593"} -{"city": "WATERLOO", "loc": [-88.983835, 43.183171], "pop": 4106, "state": "WI", "_id": "53594"} -{"city": "WAUNAKEE", "loc": [-89.45317, 43.181828], "pop": 9691, "state": "WI", "_id": "53597"} -{"city": "WINDSOR", "loc": [-89.341469, 43.214121], "pop": 1825, "state": "WI", "_id": "53598"} -{"city": "MADISON", "loc": [-89.383068, 43.077535], "pop": 25721, "state": "WI", "_id": "53703"} -{"city": "MADISON", "loc": [-89.352295, 43.120526], "pop": 40639, "state": "WI", "_id": "53704"} -{"city": "MADISON", "loc": [-89.452823, 43.072999], "pop": 29114, "state": "WI", "_id": "53705"} -{"city": "MADISON", "loc": [-89.409362, 43.076929], "pop": 3587, "state": "WI", "_id": "53706"} -{"city": "MADISON", "loc": [-89.452558, 43.035644], "pop": 42198, "state": "WI", "_id": "53711"} -{"city": "FITCHBURG", "loc": [-89.390008, 43.037381], "pop": 18082, "state": "WI", "_id": "53713"} -{"city": "MADISON", "loc": [-89.311758, 43.097735], "pop": 17978, "state": "WI", "_id": "53714"} -{"city": "MADISON", "loc": [-89.400045, 43.065287], "pop": 13545, "state": "WI", "_id": "53715"} -{"city": "MONONA", "loc": [-89.315921, 43.067413], "pop": 20296, "state": "WI", "_id": "53716"} -{"city": "MADISON", "loc": [-89.507984, 43.073587], "pop": 7675, "state": "WI", "_id": "53717"} -{"city": "MADISON", "loc": [-89.407339, 43.152143], "pop": 63, "state": "WI", "_id": "53718"} -{"city": "MADISON", "loc": [-89.499324, 43.03207], "pop": 9669, "state": "WI", "_id": "53719"} -{"city": "BAGLEY", "loc": [-91.068502, 42.920734], "pop": 879, "state": "WI", "_id": "53801"} -{"city": "BENTON", "loc": [-90.338045, 42.534901], "pop": 153, "state": "WI", "_id": "53803"} -{"city": "BLOOMINGTON", "loc": [-90.909905, 42.872598], "pop": 1485, "state": "WI", "_id": "53804"} -{"city": "BOSCOBEL", "loc": [-90.706101, 43.139327], "pop": 4755, "state": "WI", "_id": "53805"} -{"city": "CASSVILLE", "loc": [-90.960751, 42.728791], "pop": 2134, "state": "WI", "_id": "53806"} -{"city": "CUBA CITY", "loc": [-90.474907, 42.599073], "pop": 6856, "state": "WI", "_id": "53807"} -{"city": "FENNIMORE", "loc": [-90.655009, 42.988558], "pop": 3668, "state": "WI", "_id": "53809"} -{"city": "GLEN HAVEN", "loc": [-91.000451, 42.820372], "pop": 614, "state": "WI", "_id": "53810"} -{"city": "HAZEL GREEN", "loc": [-90.511782, 42.535913], "pop": 3228, "state": "WI", "_id": "53811"} -{"city": "LANCASTER", "loc": [-90.710906, 42.844469], "pop": 6537, "state": "WI", "_id": "53813"} -{"city": "MOUNT HOPE", "loc": [-90.86648, 42.968862], "pop": 737, "state": "WI", "_id": "53816"} -{"city": "PLATTEVILLE", "loc": [-90.485406, 42.73932], "pop": 13067, "state": "WI", "_id": "53818"} -{"city": "POTOSI", "loc": [-90.700153, 42.6884], "pop": 2845, "state": "WI", "_id": "53820"} -{"city": "PRAIRIE DU CHIEN", "loc": [-91.119305, 43.042649], "pop": 7950, "state": "WI", "_id": "53821"} -{"city": "STITZER", "loc": [-90.607924, 42.92142], "pop": 469, "state": "WI", "_id": "53825"} -{"city": "WAUZEKA", "loc": [-90.923885, 43.114264], "pop": 1455, "state": "WI", "_id": "53826"} -{"city": "WOODMAN", "loc": [-90.824539, 43.057697], "pop": 412, "state": "WI", "_id": "53827"} -{"city": "PORTAGE", "loc": [-89.47139, 43.550145], "pop": 11280, "state": "WI", "_id": "53901"} -{"city": "ADAMS", "loc": [-89.821869, 43.896688], "pop": 2342, "state": "WI", "_id": "53910"} -{"city": "ARLINGTON", "loc": [-89.363082, 43.326868], "pop": 1329, "state": "WI", "_id": "53911"} -{"city": "BARABOO", "loc": [-89.746168, 43.482531], "pop": 14955, "state": "WI", "_id": "53913"} -{"city": "BEAVER DAM", "loc": [-88.840681, 43.461574], "pop": 19539, "state": "WI", "_id": "53916"} -{"city": "BRANDON", "loc": [-88.784099, 43.72579], "pop": 2953, "state": "WI", "_id": "53919"} -{"city": "BRIGGSVILLE", "loc": [-89.580259, 43.675865], "pop": 951, "state": "WI", "_id": "53920"} -{"city": "BURNETT", "loc": [-88.717606, 43.511609], "pop": 909, "state": "WI", "_id": "53922"} -{"city": "CAMBRIA", "loc": [-89.115614, 43.57239], "pop": 2566, "state": "WI", "_id": "53923"} -{"city": "CAZENOVIA", "loc": [-90.279572, 43.498715], "pop": 717, "state": "WI", "_id": "53924"} -{"city": "COLUMBUS", "loc": [-89.027089, 43.331494], "pop": 6847, "state": "WI", "_id": "53925"} -{"city": "DALTON", "loc": [-89.191768, 43.67178], "pop": 685, "state": "WI", "_id": "53926"} -{"city": "ELROY", "loc": [-90.288857, 43.751616], "pop": 3270, "state": "WI", "_id": "53929"} -{"city": "ENDEAVOR", "loc": [-89.461699, 43.696305], "pop": 773, "state": "WI", "_id": "53930"} -{"city": "FALL RIVER", "loc": [-89.062513, 43.400838], "pop": 1996, "state": "WI", "_id": "53932"} -{"city": "FOX LAKE", "loc": [-88.904081, 43.581507], "pop": 3233, "state": "WI", "_id": "53933"} -{"city": "FRIENDSHIP", "loc": [-89.814875, 43.989629], "pop": 4859, "state": "WI", "_id": "53934"} -{"city": "GRAND MARSH", "loc": [-89.655663, 43.856789], "pop": 1907, "state": "WI", "_id": "53936"} -{"city": "HILLPOINT", "loc": [-90.158792, 43.395129], "pop": 980, "state": "WI", "_id": "53937"} -{"city": "KINGSTON", "loc": [-89.131997, 43.69236], "pop": 346, "state": "WI", "_id": "53939"} -{"city": "LA VALLE", "loc": [-90.127966, 43.569874], "pop": 2383, "state": "WI", "_id": "53941"} -{"city": "LOGANVILLE", "loc": [-90.033688, 43.405046], "pop": 877, "state": "WI", "_id": "53943"} -{"city": "LYNDON STATION", "loc": [-89.891543, 43.689875], "pop": 2010, "state": "WI", "_id": "53944"} -{"city": "MARKESAN", "loc": [-89.006971, 43.714015], "pop": 4015, "state": "WI", "_id": "53946"} -{"city": "MARQUETTE", "loc": [-89.139654, 43.745886], "pop": 183, "state": "WI", "_id": "53947"} -{"city": "MAUSTON", "loc": [-90.064085, 43.793383], "pop": 7325, "state": "WI", "_id": "53948"} -{"city": "MONTELLO", "loc": [-89.358641, 43.768984], "pop": 4392, "state": "WI", "_id": "53949"} -{"city": "NEW LISBON", "loc": [-90.152407, 43.88775], "pop": 3320, "state": "WI", "_id": "53950"} -{"city": "NORTH FREEDOM", "loc": [-89.849019, 43.410562], "pop": 1858, "state": "WI", "_id": "53951"} -{"city": "OXFORD", "loc": [-89.563175, 43.779914], "pop": 1217, "state": "WI", "_id": "53952"} -{"city": "PARDEEVILLE", "loc": [-89.326514, 43.533975], "pop": 6321, "state": "WI", "_id": "53954"} -{"city": "POYNETTE", "loc": [-89.418857, 43.401342], "pop": 4264, "state": "WI", "_id": "53955"} -{"city": "RANDOLPH", "loc": [-89.002992, 43.539696], "pop": 3206, "state": "WI", "_id": "53956"} -{"city": "REEDSBURG", "loc": [-89.995948, 43.531581], "pop": 9118, "state": "WI", "_id": "53959"} -{"city": "RIO", "loc": [-89.235362, 43.423053], "pop": 2766, "state": "WI", "_id": "53960"} -{"city": "ROCK SPRINGS", "loc": [-89.921834, 43.468098], "pop": 735, "state": "WI", "_id": "53961"} -{"city": "WAUPUN", "loc": [-88.737253, 43.632441], "pop": 11222, "state": "WI", "_id": "53963"} -{"city": "WESTFIELD", "loc": [-89.502015, 43.896788], "pop": 3309, "state": "WI", "_id": "53964"} -{"city": "WISCONSIN DELLS", "loc": [-89.76652, 43.650789], "pop": 7650, "state": "WI", "_id": "53965"} -{"city": "WONEWOC", "loc": [-90.239357, 43.62848], "pop": 3180, "state": "WI", "_id": "53968"} -{"city": "DERONDA", "loc": [-92.361527, 45.325238], "pop": 4929, "state": "WI", "_id": "54001"} -{"city": "BALDWIN", "loc": [-92.365498, 44.9657], "pop": 4214, "state": "WI", "_id": "54002"} -{"city": "BELDENVILLE", "loc": [-92.437202, 44.7721], "pop": 1442, "state": "WI", "_id": "54003"} -{"city": "CLAYTON", "loc": [-92.139462, 45.306297], "pop": 2414, "state": "WI", "_id": "54004"} -{"city": "CLEAR LAKE", "loc": [-92.27488, 45.236481], "pop": 3254, "state": "WI", "_id": "54005"} -{"city": "CUSHING", "loc": [-92.624136, 45.560359], "pop": 2750, "state": "WI", "_id": "54006"} -{"city": "DEER PARK", "loc": [-92.358464, 45.169046], "pop": 876, "state": "WI", "_id": "54007"} -{"city": "DRESSER", "loc": [-92.5623, 45.354585], "pop": 4019, "state": "WI", "_id": "54009"} -{"city": "ELLSWORTH", "loc": [-92.489038, 44.730191], "pop": 4293, "state": "WI", "_id": "54011"} -{"city": "EMERALD", "loc": [-92.312786, 45.078578], "pop": 630, "state": "WI", "_id": "54012"} -{"city": "GLENWOOD CITY", "loc": [-92.181588, 45.062339], "pop": 1835, "state": "WI", "_id": "54013"} -{"city": "HAGER CITY", "loc": [-92.550146, 44.626969], "pop": 2075, "state": "WI", "_id": "54014"} -{"city": "HAMMOND", "loc": [-92.447215, 44.967004], "pop": 1921, "state": "WI", "_id": "54015"} -{"city": "HUDSON", "loc": [-92.727062, 44.984187], "pop": 16136, "state": "WI", "_id": "54016"} -{"city": "NEW RICHMOND", "loc": [-92.551333, 45.159819], "pop": 14190, "state": "WI", "_id": "54017"} -{"city": "OSCEOLA", "loc": [-92.697108, 45.321757], "pop": 2062, "state": "WI", "_id": "54020"} -{"city": "PRESCOTT", "loc": [-92.753528, 44.747648], "pop": 4807, "state": "WI", "_id": "54021"} -{"city": "RIVER FALLS", "loc": [-92.631275, 44.85501], "pop": 16037, "state": "WI", "_id": "54022"} -{"city": "ROBERTS", "loc": [-92.559773, 44.987356], "pop": 2063, "state": "WI", "_id": "54023"} -{"city": "SAINT CROIX FALL", "loc": [-92.638371, 45.413215], "pop": 1640, "state": "WI", "_id": "54024"} -{"city": "SOMERSET", "loc": [-92.686466, 45.133064], "pop": 2937, "state": "WI", "_id": "54025"} -{"city": "STAR PRAIRIE", "loc": [-92.551353, 45.2013], "pop": 40, "state": "WI", "_id": "54026"} -{"city": "WILSON", "loc": [-92.196538, 44.94658], "pop": 1415, "state": "WI", "_id": "54027"} -{"city": "WOODVILLE", "loc": [-92.284803, 44.940806], "pop": 1326, "state": "WI", "_id": "54028"} -{"city": "SAINT JOSEPH", "loc": [-92.760803, 45.070123], "pop": 987, "state": "WI", "_id": "54082"} -{"city": "ABRAMS", "loc": [-88.057101, 44.78693], "pop": 1712, "state": "WI", "_id": "54101"} -{"city": "AMBERG", "loc": [-87.964256, 45.498878], "pop": 917, "state": "WI", "_id": "54102"} -{"city": "ARMSTRONG CREEK", "loc": [-88.490847, 45.67261], "pop": 460, "state": "WI", "_id": "54103"} -{"city": "ATHELSTANE", "loc": [-88.175282, 45.422819], "pop": 696, "state": "WI", "_id": "54104"} -{"city": "CENTER VALLEY", "loc": [-88.444776, 44.485595], "pop": 4859, "state": "WI", "_id": "54106"} -{"city": "NAVARINO", "loc": [-88.439026, 44.73794], "pop": 2148, "state": "WI", "_id": "54107"} -{"city": "BRILLION", "loc": [-88.083337, 44.17792], "pop": 4618, "state": "WI", "_id": "54110"} -{"city": "CECIL", "loc": [-88.418253, 44.811027], "pop": 2966, "state": "WI", "_id": "54111"} -{"city": "COLEMAN", "loc": [-88.058667, 45.072157], "pop": 2828, "state": "WI", "_id": "54112"} -{"city": "COMBINED LOCKS", "loc": [-88.313271, 44.266581], "pop": 2190, "state": "WI", "_id": "54113"} -{"city": "BEAVER", "loc": [-88.061265, 45.254053], "pop": 3395, "state": "WI", "_id": "54114"} -{"city": "DE PERE", "loc": [-88.080613, 44.438779], "pop": 22430, "state": "WI", "_id": "54115"} -{"city": "DUNBAR", "loc": [-88.106669, 45.600944], "pop": 838, "state": "WI", "_id": "54119"} -{"city": "FENCE", "loc": [-88.364873, 45.759652], "pop": 362, "state": "WI", "_id": "54120"} -{"city": "FLORENCE", "loc": [-88.228921, 45.902724], "pop": 2616, "state": "WI", "_id": "54121"} -{"city": "FOREST JUNCTION", "loc": [-88.151716, 44.20613], "pop": 395, "state": "WI", "_id": "54123"} -{"city": "GILLETT", "loc": [-88.324221, 44.901753], "pop": 3317, "state": "WI", "_id": "54124"} -{"city": "GOODMAN", "loc": [-88.362901, 45.646895], "pop": 758, "state": "WI", "_id": "54125"} -{"city": "GREENLEAF", "loc": [-88.027519, 44.29373], "pop": 3443, "state": "WI", "_id": "54126"} -{"city": "GRESHAM", "loc": [-88.78873, 44.852963], "pop": 1689, "state": "WI", "_id": "54128"} -{"city": "HILBERT", "loc": [-88.206961, 44.127129], "pop": 3938, "state": "WI", "_id": "54129"} -{"city": "KAUKAUNA", "loc": [-88.271692, 44.29561], "pop": 19823, "state": "WI", "_id": "54130"} -{"city": "KESHENA", "loc": [-88.635686, 44.914635], "pop": 3588, "state": "WI", "_id": "54135"} -{"city": "KIMBERLY", "loc": [-88.338374, 44.27008], "pop": 5406, "state": "WI", "_id": "54136"} -{"city": "KRAKOW", "loc": [-88.25933, 44.763457], "pop": 883, "state": "WI", "_id": "54137"} -{"city": "LAKEWOOD", "loc": [-88.503189, 45.316285], "pop": 626, "state": "WI", "_id": "54138"} -{"city": "STILES", "loc": [-88.054308, 44.940494], "pop": 2684, "state": "WI", "_id": "54139"} -{"city": "LITTLE CHUTE", "loc": [-88.311989, 44.284248], "pop": 7769, "state": "WI", "_id": "54140"} -{"city": "LITTLE SUAMICO", "loc": [-88.027245, 44.67751], "pop": 3506, "state": "WI", "_id": "54141"} -{"city": "MARINETTE", "loc": [-87.669684, 45.087403], "pop": 16297, "state": "WI", "_id": "54143"} -{"city": "MOUNTAIN", "loc": [-88.45828, 45.199127], "pop": 1261, "state": "WI", "_id": "54149"} -{"city": "NEOPIT", "loc": [-88.861116, 44.987323], "pop": 14, "state": "WI", "_id": "54150"} -{"city": "NIAGARA", "loc": [-88.030927, 45.765343], "pop": 4123, "state": "WI", "_id": "54151"} -{"city": "OCONTO", "loc": [-87.891728, 44.891359], "pop": 6710, "state": "WI", "_id": "54153"} -{"city": "OCONTO FALLS", "loc": [-88.155528, 44.875499], "pop": 4481, "state": "WI", "_id": "54154"} -{"city": "ONEIDA", "loc": [-88.185851, 44.516641], "pop": 3674, "state": "WI", "_id": "54155"} -{"city": "PEMBINE", "loc": [-87.970363, 45.607398], "pop": 1443, "state": "WI", "_id": "54156"} -{"city": "PESHTIGO", "loc": [-87.729795, 45.045778], "pop": 4956, "state": "WI", "_id": "54157"} -{"city": "PORTERFIELD", "loc": [-87.806243, 45.193399], "pop": 1974, "state": "WI", "_id": "54159"} -{"city": "POUND", "loc": [-88.139621, 45.127317], "pop": 2084, "state": "WI", "_id": "54161"} -{"city": "PULASKI", "loc": [-88.263447, 44.66112], "pop": 6933, "state": "WI", "_id": "54162"} -{"city": "SEYMOUR", "loc": [-88.317243, 44.509096], "pop": 5744, "state": "WI", "_id": "54165"} -{"city": "SHAWANO", "loc": [-88.603599, 44.785133], "pop": 13367, "state": "WI", "_id": "54166"} -{"city": "SHIOCTON", "loc": [-88.556236, 44.497202], "pop": 3708, "state": "WI", "_id": "54170"} -{"city": "SOBIESKI", "loc": [-88.107605, 44.710495], "pop": 1644, "state": "WI", "_id": "54171"} -{"city": "SURING", "loc": [-88.362767, 45.018551], "pop": 2591, "state": "WI", "_id": "54174"} -{"city": "TOWNSEND", "loc": [-88.610743, 45.314819], "pop": 729, "state": "WI", "_id": "54175"} -{"city": "UNDERHILL", "loc": [-88.168898, 44.731836], "pop": 69, "state": "WI", "_id": "54176"} -{"city": "WAUSAUKEE", "loc": [-87.909877, 45.349507], "pop": 2875, "state": "WI", "_id": "54177"} -{"city": "WRIGHTSTOWN", "loc": [-88.121769, 44.338377], "pop": 2359, "state": "WI", "_id": "54180"} -{"city": "ALGOMA", "loc": [-87.457792, 44.610051], "pop": 5387, "state": "WI", "_id": "54201"} -{"city": "BAILEYS HARBOR", "loc": [-87.167626, 45.056549], "pop": 1167, "state": "WI", "_id": "54202"} -{"city": "BRUSSELS", "loc": [-87.62252, 44.747558], "pop": 1785, "state": "WI", "_id": "54204"} -{"city": "CASCO", "loc": [-87.623122, 44.58328], "pop": 2066, "state": "WI", "_id": "54205"} -{"city": "CATO", "loc": [-87.864688, 44.148521], "pop": 1097, "state": "WI", "_id": "54206"} -{"city": "DENMARK", "loc": [-87.827036, 44.359392], "pop": 3968, "state": "WI", "_id": "54208"} -{"city": "EGG HARBOR", "loc": [-87.280521, 45.010039], "pop": 738, "state": "WI", "_id": "54209"} -{"city": "ELLISON BAY", "loc": [-87.051209, 45.257206], "pop": 804, "state": "WI", "_id": "54210"} -{"city": "FISH CREEK", "loc": [-87.206432, 45.11554], "pop": 1200, "state": "WI", "_id": "54212"} -{"city": "FORESTVILLE", "loc": [-87.492832, 44.696863], "pop": 1680, "state": "WI", "_id": "54213"} -{"city": "FRANCIS CREEK", "loc": [-87.720007, 44.199964], "pop": 562, "state": "WI", "_id": "54214"} -{"city": "KELLNERSVILLE", "loc": [-87.803596, 44.22493], "pop": 350, "state": "WI", "_id": "54215"} -{"city": "KEWAUNEE", "loc": [-87.559442, 44.440143], "pop": 6262, "state": "WI", "_id": "54216"} -{"city": "LUXEMBURG", "loc": [-87.715606, 44.550612], "pop": 5188, "state": "WI", "_id": "54217"} -{"city": "MANITOWOC", "loc": [-87.682303, 44.097122], "pop": 39178, "state": "WI", "_id": "54220"} -{"city": "MARIBEL", "loc": [-87.805482, 44.285151], "pop": 2251, "state": "WI", "_id": "54227"} -{"city": "MISHICOT", "loc": [-87.644703, 44.260942], "pop": 3245, "state": "WI", "_id": "54228"} -{"city": "NEW FRANKEN", "loc": [-87.823482, 44.559193], "pop": 2640, "state": "WI", "_id": "54229"} -{"city": "REEDSVILLE", "loc": [-87.966028, 44.157566], "pop": 3073, "state": "WI", "_id": "54230"} -{"city": "SAINT NAZIANZ", "loc": [-87.922906, 44.006425], "pop": 680, "state": "WI", "_id": "54232"} -{"city": "SISTER BAY", "loc": [-87.113865, 45.187544], "pop": 1376, "state": "WI", "_id": "54234"} -{"city": "STURGEON BAY", "loc": [-87.375311, 44.84384], "pop": 16149, "state": "WI", "_id": "54235"} -{"city": "TWO RIVERS", "loc": [-87.585504, 44.166008], "pop": 16768, "state": "WI", "_id": "54241"} -{"city": "VALDERS", "loc": [-87.881183, 44.041977], "pop": 2056, "state": "WI", "_id": "54245"} -{"city": "WASHINGTON ISLAN", "loc": [-86.913664, 45.374042], "pop": 623, "state": "WI", "_id": "54246"} -{"city": "WHITELAW", "loc": [-87.826698, 44.157269], "pop": 1402, "state": "WI", "_id": "54247"} -{"city": "ALLOUEZ", "loc": [-88.016868, 44.485313], "pop": 25108, "state": "WI", "_id": "54301"} -{"city": "GREEN BAY", "loc": [-87.977136, 44.502508], "pop": 27273, "state": "WI", "_id": "54302"} -{"city": "HOWARD", "loc": [-88.045262, 44.530146], "pop": 27046, "state": "WI", "_id": "54303"} -{"city": "ASHWAUBENON", "loc": [-88.066799, 44.505525], "pop": 31339, "state": "WI", "_id": "54304"} -{"city": "GREEN BAY", "loc": [-87.926685, 44.491405], "pop": 19373, "state": "WI", "_id": "54311"} -{"city": "GREEN BAY", "loc": [-88.102054, 44.546289], "pop": 23360, "state": "WI", "_id": "54313"} -{"city": "WAUSAU", "loc": [-89.633955, 44.963433], "pop": 51083, "state": "WI", "_id": "54401"} -{"city": "ABBOTSFORD", "loc": [-90.299438, 44.964057], "pop": 2480, "state": "WI", "_id": "54405"} -{"city": "AMHERST", "loc": [-89.303482, 44.423052], "pop": 2885, "state": "WI", "_id": "54406"} -{"city": "AMHERST JUNCTION", "loc": [-89.303754, 44.506458], "pop": 1448, "state": "WI", "_id": "54407"} -{"city": "ANIWA", "loc": [-89.268254, 45.01039], "pop": 1510, "state": "WI", "_id": "54408"} -{"city": "ANTIGO", "loc": [-89.141871, 45.131362], "pop": 13087, "state": "WI", "_id": "54409"} -{"city": "ARPIN", "loc": [-90.037049, 44.54194], "pop": 2301, "state": "WI", "_id": "54410"} -{"city": "HAMBURG", "loc": [-90.062612, 45.032037], "pop": 4263, "state": "WI", "_id": "54411"} -{"city": "AUBURNDALE", "loc": [-90.013577, 44.637836], "pop": 1667, "state": "WI", "_id": "54412"} -{"city": "BABCOCK", "loc": [-90.098574, 44.303064], "pop": 409, "state": "WI", "_id": "54413"} -{"city": "BIRNAMWOOD", "loc": [-89.212433, 44.926896], "pop": 2464, "state": "WI", "_id": "54414"} -{"city": "BOWLER", "loc": [-88.976071, 44.933667], "pop": 2777, "state": "WI", "_id": "54416"} -{"city": "BRYANT", "loc": [-88.998325, 45.202962], "pop": 1029, "state": "WI", "_id": "54418"} -{"city": "CHELSEA", "loc": [-90.303685, 45.288166], "pop": 122, "state": "WI", "_id": "54419"} -{"city": "CHILI", "loc": [-90.359952, 44.630135], "pop": 728, "state": "WI", "_id": "54420"} -{"city": "COLBY", "loc": [-90.314432, 44.911651], "pop": 3621, "state": "WI", "_id": "54421"} -{"city": "CURTISS", "loc": [-90.459223, 44.960186], "pop": 1111, "state": "WI", "_id": "54422"} -{"city": "CUSTER", "loc": [-89.415275, 44.56622], "pop": 2010, "state": "WI", "_id": "54423"} -{"city": "DEERBROOK", "loc": [-89.187257, 45.236791], "pop": 902, "state": "WI", "_id": "54424"} -{"city": "DORCHESTER", "loc": [-90.359648, 45.004192], "pop": 1860, "state": "WI", "_id": "54425"} -{"city": "FENWOOD", "loc": [-89.998029, 44.903831], "pop": 3810, "state": "WI", "_id": "54426"} -{"city": "ELAND", "loc": [-89.246117, 44.830888], "pop": 1278, "state": "WI", "_id": "54427"} -{"city": "ELCHO", "loc": [-89.15157, 45.440906], "pop": 988, "state": "WI", "_id": "54428"} -{"city": "ELTON", "loc": [-88.883743, 45.156216], "pop": 135, "state": "WI", "_id": "54430"} -{"city": "GILMAN", "loc": [-90.825672, 45.18906], "pop": 1780, "state": "WI", "_id": "54433"} -{"city": "GLEASON", "loc": [-89.475264, 45.338428], "pop": 2347, "state": "WI", "_id": "54435"} -{"city": "GRANTON", "loc": [-90.448383, 44.560558], "pop": 2072, "state": "WI", "_id": "54436"} -{"city": "GREENWOOD", "loc": [-90.622913, 44.740373], "pop": 3029, "state": "WI", "_id": "54437"} -{"city": "HATLEY", "loc": [-89.377728, 44.825638], "pop": 2129, "state": "WI", "_id": "54440"} -{"city": "HEWITT", "loc": [-90.103054, 44.643651], "pop": 595, "state": "WI", "_id": "54441"} -{"city": "IRMA", "loc": [-89.661138, 45.348483], "pop": 1006, "state": "WI", "_id": "54442"} -{"city": "JUNCTION CITY", "loc": [-89.743758, 44.59634], "pop": 2396, "state": "WI", "_id": "54443"} -{"city": "LILY", "loc": [-88.838451, 45.318723], "pop": 196, "state": "WI", "_id": "54445"} -{"city": "LOYAL", "loc": [-90.492238, 44.727243], "pop": 3809, "state": "WI", "_id": "54446"} -{"city": "LUBLIN", "loc": [-90.733678, 45.073933], "pop": 564, "state": "WI", "_id": "54447"} -{"city": "MARATHON", "loc": [-89.829948, 44.965664], "pop": 5202, "state": "WI", "_id": "54448"} -{"city": "MARSHFIELD", "loc": [-90.17845, 44.661453], "pop": 25791, "state": "WI", "_id": "54449"} -{"city": "MEDFORD", "loc": [-90.350306, 45.151185], "pop": 10388, "state": "WI", "_id": "54451"} -{"city": "MERRILL", "loc": [-89.690696, 45.180924], "pop": 17891, "state": "WI", "_id": "54452"} -{"city": "MILLADORE", "loc": [-89.887532, 44.604416], "pop": 1347, "state": "WI", "_id": "54454"} -{"city": "MOSINEE", "loc": [-89.684491, 44.799736], "pop": 13664, "state": "WI", "_id": "54455"} -{"city": "NEILLSVILLE", "loc": [-90.611197, 44.549429], "pop": 4904, "state": "WI", "_id": "54456"} -{"city": "NEKOOSA", "loc": [-89.881394, 44.281979], "pop": 7312, "state": "WI", "_id": "54457"} -{"city": "OGEMA", "loc": [-90.256513, 45.439187], "pop": 1459, "state": "WI", "_id": "54459"} -{"city": "OWEN", "loc": [-90.5469, 44.957817], "pop": 1759, "state": "WI", "_id": "54460"} -{"city": "PEARSON", "loc": [-89.075824, 45.388119], "pop": 679, "state": "WI", "_id": "54462"} -{"city": "PELICAN LAKE", "loc": [-89.181707, 45.504886], "pop": 635, "state": "WI", "_id": "54463"} -{"city": "PICKEREL", "loc": [-88.91365, 45.356658], "pop": 269, "state": "WI", "_id": "54465"} -{"city": "PITTSVILLE", "loc": [-90.189174, 44.43747], "pop": 2754, "state": "WI", "_id": "54466"} -{"city": "PLOVER", "loc": [-89.542805, 44.450673], "pop": 6142, "state": "WI", "_id": "54467"} -{"city": "PORT EDWARDS", "loc": [-89.865246, 44.349705], "pop": 1828, "state": "WI", "_id": "54469"} -{"city": "RIB LAKE", "loc": [-90.176322, 45.298376], "pop": 2002, "state": "WI", "_id": "54470"} -{"city": "RINGLE", "loc": [-89.432845, 44.92383], "pop": 1370, "state": "WI", "_id": "54471"} -{"city": "ROSHOLT", "loc": [-89.335637, 44.662224], "pop": 3147, "state": "WI", "_id": "54473"} -{"city": "ROTHSCHILD", "loc": [-89.619262, 44.88429], "pop": 3867, "state": "WI", "_id": "54474"} -{"city": "RUDOLPH", "loc": [-89.800252, 44.474061], "pop": 1648, "state": "WI", "_id": "54475"} -{"city": "SCHOFIELD", "loc": [-89.581789, 44.906407], "pop": 12583, "state": "WI", "_id": "54476"} -{"city": "SPENCER", "loc": [-90.311406, 44.739148], "pop": 2997, "state": "WI", "_id": "54479"} -{"city": "STETSONVILLE", "loc": [-90.282931, 45.07923], "pop": 1241, "state": "WI", "_id": "54480"} -{"city": "STEVENS POINT", "loc": [-89.558764, 44.521221], "pop": 38950, "state": "WI", "_id": "54481"} -{"city": "STRATFORD", "loc": [-90.058317, 44.789878], "pop": 4201, "state": "WI", "_id": "54484"} -{"city": "SUMMIT LAKE", "loc": [-89.180743, 45.320414], "pop": 616, "state": "WI", "_id": "54485"} -{"city": "TIGERTON", "loc": [-89.056893, 44.729938], "pop": 3310, "state": "WI", "_id": "54486"} -{"city": "TOMAHAWK", "loc": [-89.727269, 45.496308], "pop": 8130, "state": "WI", "_id": "54487"} -{"city": "UNITY", "loc": [-90.322074, 44.822676], "pop": 1797, "state": "WI", "_id": "54488"} -{"city": "VESPER", "loc": [-89.982553, 44.467656], "pop": 1616, "state": "WI", "_id": "54489"} -{"city": "WESTBORO", "loc": [-90.307194, 45.344539], "pop": 766, "state": "WI", "_id": "54490"} -{"city": "WHITE LAKE", "loc": [-88.755489, 45.171465], "pop": 1324, "state": "WI", "_id": "54491"} -{"city": "WILLARD", "loc": [-90.749897, 44.727378], "pop": 682, "state": "WI", "_id": "54493"} -{"city": "WISCONSIN RAPIDS", "loc": [-89.806229, 44.375803], "pop": 33296, "state": "WI", "_id": "54494"} -{"city": "WITHEE", "loc": [-90.604862, 44.975726], "pop": 2286, "state": "WI", "_id": "54498"} -{"city": "WITTENBERG", "loc": [-89.166479, 44.823247], "pop": 1850, "state": "WI", "_id": "54499"} -{"city": "MONICO", "loc": [-89.409285, 45.646718], "pop": 19039, "state": "WI", "_id": "54501"} -{"city": "CAVOUR", "loc": [-88.888566, 45.658309], "pop": 1533, "state": "WI", "_id": "54511"} -{"city": "BOULDER JUNCTION", "loc": [-89.632454, 46.111183], "pop": 563, "state": "WI", "_id": "54512"} -{"city": "BRANTWOOD", "loc": [-90.115952, 45.551413], "pop": 540, "state": "WI", "_id": "54513"} -{"city": "BUTTERNUT", "loc": [-90.482114, 46.018358], "pop": 2054, "state": "WI", "_id": "54514"} -{"city": "CATAWBA", "loc": [-90.514564, 45.508123], "pop": 616, "state": "WI", "_id": "54515"} -{"city": "CLAM LAKE", "loc": [-90.884107, 46.245675], "pop": 2, "state": "WI", "_id": "54517"} -{"city": "CONOVER", "loc": [-89.238919, 46.043968], "pop": 1039, "state": "WI", "_id": "54519"} -{"city": "CRANDON", "loc": [-88.911619, 45.541553], "pop": 3419, "state": "WI", "_id": "54520"} -{"city": "EAGLE RIVER", "loc": [-89.253058, 45.916084], "pop": 6726, "state": "WI", "_id": "54521"} -{"city": "FIFIELD", "loc": [-90.4246, 45.862148], "pop": 496, "state": "WI", "_id": "54524"} -{"city": "INGRAM", "loc": [-90.855183, 45.488807], "pop": 949, "state": "WI", "_id": "54526"} -{"city": "GLIDDEN", "loc": [-90.588856, 46.133668], "pop": 1061, "state": "WI", "_id": "54527"} -{"city": "HARSHAW", "loc": [-89.650346, 45.676244], "pop": 854, "state": "WI", "_id": "54529"} -{"city": "HAWKINS", "loc": [-90.711373, 45.524129], "pop": 747, "state": "WI", "_id": "54530"} -{"city": "HAZELHURST", "loc": [-89.747836, 45.77446], "pop": 1057, "state": "WI", "_id": "54531"} -{"city": "HURLEY", "loc": [-90.1975, 46.44482], "pop": 2658, "state": "WI", "_id": "54534"} -{"city": "IRON BELT", "loc": [-90.324528, 46.395545], "pop": 265, "state": "WI", "_id": "54536"} -{"city": "KENNAN", "loc": [-90.590987, 45.53497], "pop": 628, "state": "WI", "_id": "54537"} -{"city": "LAC DU FLAMBEAU", "loc": [-89.890078, 45.97175], "pop": 2085, "state": "WI", "_id": "54538"} -{"city": "LAKE TOMAHAWK", "loc": [-89.585609, 45.803455], "pop": 1223, "state": "WI", "_id": "54539"} -{"city": "LAND O LAKES", "loc": [-89.321787, 46.156305], "pop": 742, "state": "WI", "_id": "54540"} -{"city": "LAONA", "loc": [-88.671525, 45.553416], "pop": 1823, "state": "WI", "_id": "54541"} -{"city": "ALVIN", "loc": [-88.695308, 45.921841], "pop": 550, "state": "WI", "_id": "54542"} -{"city": "MANITOWISH WATER", "loc": [-89.830767, 46.123909], "pop": 901, "state": "WI", "_id": "54545"} -{"city": "MELLEN", "loc": [-90.655233, 46.300659], "pop": 1752, "state": "WI", "_id": "54546"} -{"city": "MERCER", "loc": [-90.067914, 46.169594], "pop": 1321, "state": "WI", "_id": "54547"} -{"city": "MINOCQUA", "loc": [-89.757954, 45.871909], "pop": 3793, "state": "WI", "_id": "54548"} -{"city": "PENCE", "loc": [-90.244195, 46.425736], "pop": 1019, "state": "WI", "_id": "54550"} -{"city": "PARK FALLS", "loc": [-90.424648, 45.936345], "pop": 5316, "state": "WI", "_id": "54552"} -{"city": "PHELPS", "loc": [-89.081547, 46.064481], "pop": 1183, "state": "WI", "_id": "54554"} -{"city": "PHILLIPS", "loc": [-90.404131, 45.697463], "pop": 5155, "state": "WI", "_id": "54555"} -{"city": "PRENTICE", "loc": [-90.294217, 45.548201], "pop": 1177, "state": "WI", "_id": "54556"} -{"city": "WINCHESTER", "loc": [-89.772795, 46.221735], "pop": 735, "state": "WI", "_id": "54557"} -{"city": "SAINT GERMAIN", "loc": [-89.486556, 45.918315], "pop": 1201, "state": "WI", "_id": "54558"} -{"city": "SAXON", "loc": [-90.438273, 46.495855], "pop": 478, "state": "WI", "_id": "54559"} -{"city": "SAYNER", "loc": [-89.519437, 45.986664], "pop": 559, "state": "WI", "_id": "54560"} -{"city": "THREE LAKES", "loc": [-89.132792, 45.804633], "pop": 1755, "state": "WI", "_id": "54562"} -{"city": "TONY", "loc": [-90.986938, 45.474082], "pop": 814, "state": "WI", "_id": "54563"} -{"city": "TRIPOLI", "loc": [-89.985218, 45.581387], "pop": 374, "state": "WI", "_id": "54564"} -{"city": "UPSON", "loc": [-90.405135, 46.376124], "pop": 69, "state": "WI", "_id": "54565"} -{"city": "WABENO", "loc": [-88.662456, 45.433172], "pop": 1308, "state": "WI", "_id": "54566"} -{"city": "WOODRUFF", "loc": [-89.69545, 45.918583], "pop": 4088, "state": "WI", "_id": "54568"} -{"city": "LA CROSSE", "loc": [-91.217494, 43.798938], "pop": 48348, "state": "WI", "_id": "54601"} -{"city": "LA CROSSE", "loc": [-91.248439, 43.848675], "pop": 15292, "state": "WI", "_id": "54603"} -{"city": "ALMA", "loc": [-91.807931, 44.429437], "pop": 3824, "state": "WI", "_id": "54610"} -{"city": "ALMA CENTER", "loc": [-90.935602, 44.445218], "pop": 989, "state": "WI", "_id": "54611"} -{"city": "ARCADIA", "loc": [-91.480453, 44.253937], "pop": 4707, "state": "WI", "_id": "54612"} -{"city": "ARKDALE", "loc": [-89.896215, 44.052886], "pop": 1582, "state": "WI", "_id": "54613"} -{"city": "BANGOR", "loc": [-90.980888, 43.868697], "pop": 1952, "state": "WI", "_id": "54614"} -{"city": "BLACK RIVER FALL", "loc": [-90.847952, 44.292101], "pop": 8248, "state": "WI", "_id": "54615"} -{"city": "BLAIR", "loc": [-91.223501, 44.296843], "pop": 2347, "state": "WI", "_id": "54616"} -{"city": "BLOOM CITY", "loc": [-90.666458, 43.509034], "pop": 437, "state": "WI", "_id": "54617"} -{"city": "CUTLER", "loc": [-90.268861, 43.936705], "pop": 1613, "state": "WI", "_id": "54618"} -{"city": "CASHTON", "loc": [-90.761197, 43.745649], "pop": 1547, "state": "WI", "_id": "54619"} -{"city": "CHASEBURG", "loc": [-91.079934, 43.679372], "pop": 550, "state": "WI", "_id": "54621"} -{"city": "WAUMANDEE", "loc": [-91.858677, 44.251591], "pop": 2271, "state": "WI", "_id": "54622"} -{"city": "COON VALLEY", "loc": [-91.014338, 43.721783], "pop": 1402, "state": "WI", "_id": "54623"} -{"city": "VICTORY", "loc": [-91.193015, 43.494707], "pop": 1422, "state": "WI", "_id": "54624"} -{"city": "DODGE", "loc": [-91.524341, 44.12886], "pop": 397, "state": "WI", "_id": "54625"} -{"city": "EASTMAN", "loc": [-91.01927, 43.19592], "pop": 1064, "state": "WI", "_id": "54626"} -{"city": "ETTRICK", "loc": [-91.263546, 44.172428], "pop": 1644, "state": "WI", "_id": "54627"} -{"city": "FERRYVILLE", "loc": [-91.044578, 43.395857], "pop": 1411, "state": "WI", "_id": "54628"} -{"city": "FOUNTAIN CITY", "loc": [-91.677861, 44.136436], "pop": 2481, "state": "WI", "_id": "54629"} -{"city": "GALESVILLE", "loc": [-91.358789, 44.088848], "pop": 3111, "state": "WI", "_id": "54630"} -{"city": "GAYS MILLS", "loc": [-90.867636, 43.293639], "pop": 2577, "state": "WI", "_id": "54631"} -{"city": "GENOA", "loc": [-91.134999, 43.605134], "pop": 1432, "state": "WI", "_id": "54632"} -{"city": "YUBA", "loc": [-90.370595, 43.635009], "pop": 2309, "state": "WI", "_id": "54634"} -{"city": "NORTHFIELD", "loc": [-91.039086, 44.397778], "pop": 1513, "state": "WI", "_id": "54635"} -{"city": "HOLMEN", "loc": [-91.2497, 43.976125], "pop": 7098, "state": "WI", "_id": "54636"} -{"city": "KENDALL", "loc": [-90.410691, 43.81614], "pop": 1848, "state": "WI", "_id": "54638"} -{"city": "WEST LIMA", "loc": [-90.648257, 43.616955], "pop": 3280, "state": "WI", "_id": "54639"} -{"city": "MATHER", "loc": [-90.295264, 44.147706], "pop": 57, "state": "WI", "_id": "54641"} -{"city": "MELROSE", "loc": [-91.044463, 44.138156], "pop": 1806, "state": "WI", "_id": "54642"} -{"city": "MINDORO", "loc": [-91.064181, 44.02873], "pop": 1157, "state": "WI", "_id": "54644"} -{"city": "NECEDAH", "loc": [-90.059976, 44.034481], "pop": 2741, "state": "WI", "_id": "54646"} -{"city": "NORWALK", "loc": [-90.680134, 43.812823], "pop": 2360, "state": "WI", "_id": "54648"} -{"city": "ONALASKA", "loc": [-91.231419, 43.897392], "pop": 16435, "state": "WI", "_id": "54650"} -{"city": "ONTARIO", "loc": [-90.620534, 43.717197], "pop": 1032, "state": "WI", "_id": "54651"} -{"city": "READSTOWN", "loc": [-90.757872, 43.450791], "pop": 736, "state": "WI", "_id": "54652"} -{"city": "ROCKLAND", "loc": [-90.948846, 43.938159], "pop": 1486, "state": "WI", "_id": "54653"} -{"city": "SOLDIERS GROVE", "loc": [-90.766293, 43.391857], "pop": 1621, "state": "WI", "_id": "54655"} -{"city": "SPARTA", "loc": [-90.814566, 43.954045], "pop": 14151, "state": "WI", "_id": "54656"} -{"city": "STEUBEN", "loc": [-90.860878, 43.195464], "pop": 63, "state": "WI", "_id": "54657"} -{"city": "STODDARD", "loc": [-91.19188, 43.691227], "pop": 2116, "state": "WI", "_id": "54658"} -{"city": "TAYLOR", "loc": [-91.112752, 44.289855], "pop": 1205, "state": "WI", "_id": "54659"} -{"city": "WYEVILLE", "loc": [-90.491704, 43.994833], "pop": 14294, "state": "WI", "_id": "54660"} -{"city": "TREMPEALEAU", "loc": [-91.4282, 44.021843], "pop": 2370, "state": "WI", "_id": "54661"} -{"city": "VIOLA", "loc": [-90.655416, 43.504758], "pop": 718, "state": "WI", "_id": "54664"} -{"city": "VIROQUA", "loc": [-90.893865, 43.54419], "pop": 7071, "state": "WI", "_id": "54665"} -{"city": "WARRENS", "loc": [-90.496692, 44.15697], "pop": 1089, "state": "WI", "_id": "54666"} -{"city": "WESTBY", "loc": [-90.87403, 43.663123], "pop": 4290, "state": "WI", "_id": "54667"} -{"city": "WEST SALEM", "loc": [-91.089311, 43.906253], "pop": 5244, "state": "WI", "_id": "54669"} -{"city": "WILTON", "loc": [-90.516185, 43.832209], "pop": 1188, "state": "WI", "_id": "54670"} -{"city": "EAU CLAIRE", "loc": [-91.487686, 44.783972], "pop": 31593, "state": "WI", "_id": "54701"} -{"city": "EAU CLAIRE", "loc": [-91.499701, 44.827122], "pop": 38400, "state": "WI", "_id": "54703"} -{"city": "ALTOONA", "loc": [-91.43825, 44.802142], "pop": 6065, "state": "WI", "_id": "54720"} -{"city": "ARKANSAW", "loc": [-92.056646, 44.626659], "pop": 1099, "state": "WI", "_id": "54721"} -{"city": "AUGUSTA", "loc": [-91.123154, 44.694737], "pop": 3402, "state": "WI", "_id": "54722"} -{"city": "BAY CITY", "loc": [-92.446889, 44.6166], "pop": 1540, "state": "WI", "_id": "54723"} -{"city": "BLOOMER", "loc": [-91.489974, 45.102477], "pop": 6410, "state": "WI", "_id": "54724"} -{"city": "BOYCEVILLE", "loc": [-92.024803, 45.064238], "pop": 2520, "state": "WI", "_id": "54725"} -{"city": "BOYD", "loc": [-91.029355, 44.943737], "pop": 2549, "state": "WI", "_id": "54726"} -{"city": "CADOTT", "loc": [-91.169884, 44.963014], "pop": 4397, "state": "WI", "_id": "54727"} -{"city": "CHETEK", "loc": [-91.654154, 45.317046], "pop": 5453, "state": "WI", "_id": "54728"} -{"city": "CHIPPEWA FALLS", "loc": [-91.384376, 44.932202], "pop": 25627, "state": "WI", "_id": "54729"} -{"city": "COLFAX", "loc": [-91.735737, 44.999326], "pop": 4073, "state": "WI", "_id": "54730"} -{"city": "CONRATH", "loc": [-91.062132, 45.353281], "pop": 758, "state": "WI", "_id": "54731"} -{"city": "CORNELL", "loc": [-91.173252, 45.16191], "pop": 2799, "state": "WI", "_id": "54732"} -{"city": "DALLAS", "loc": [-91.836844, 45.254883], "pop": 973, "state": "WI", "_id": "54733"} -{"city": "DOWNING", "loc": [-92.113384, 45.103032], "pop": 1029, "state": "WI", "_id": "54734"} -{"city": "DURAND", "loc": [-91.929476, 44.630042], "pop": 3746, "state": "WI", "_id": "54736"} -{"city": "EAU GALLE", "loc": [-92.040816, 44.721433], "pop": 921, "state": "WI", "_id": "54737"} -{"city": "ELEVA", "loc": [-91.48037, 44.598493], "pop": 2291, "state": "WI", "_id": "54738"} -{"city": "ELK MOUND", "loc": [-91.675229, 44.866973], "pop": 3539, "state": "WI", "_id": "54739"} -{"city": "ELMWOOD", "loc": [-92.202195, 44.756088], "pop": 1759, "state": "WI", "_id": "54740"} -{"city": "FAIRCHILD", "loc": [-90.990568, 44.596253], "pop": 1319, "state": "WI", "_id": "54741"} -{"city": "FALL CREEK", "loc": [-91.285631, 44.768385], "pop": 3814, "state": "WI", "_id": "54742"} -{"city": "HILLSDALE", "loc": [-91.859857, 45.32136], "pop": 560, "state": "WI", "_id": "54744"} -{"city": "HOLCOMBE", "loc": [-91.132955, 45.251263], "pop": 1752, "state": "WI", "_id": "54745"} -{"city": "HUMBIRD", "loc": [-90.888257, 44.536676], "pop": 633, "state": "WI", "_id": "54746"} -{"city": "INDEPENDENCE", "loc": [-91.453511, 44.395926], "pop": 2826, "state": "WI", "_id": "54747"} -{"city": "JIM FALLS", "loc": [-91.264909, 45.064439], "pop": 974, "state": "WI", "_id": "54748"} -{"city": "KNAPP", "loc": [-92.075201, 44.954495], "pop": 935, "state": "WI", "_id": "54749"} -{"city": "MAIDEN ROCK", "loc": [-92.278212, 44.608599], "pop": 1503, "state": "WI", "_id": "54750"} -{"city": "MENOMONIE", "loc": [-91.92651, 44.87182], "pop": 21211, "state": "WI", "_id": "54751"} -{"city": "MERRILLAN", "loc": [-90.810496, 44.436938], "pop": 1317, "state": "WI", "_id": "54754"} -{"city": "MODENA", "loc": [-91.669576, 44.609853], "pop": 4907, "state": "WI", "_id": "54755"} -{"city": "NELSON", "loc": [-92.000742, 44.42947], "pop": 652, "state": "WI", "_id": "54756"} -{"city": "NEW AUBURN", "loc": [-91.519747, 45.23562], "pop": 2101, "state": "WI", "_id": "54757"} -{"city": "OSSEO", "loc": [-91.219148, 44.559923], "pop": 4347, "state": "WI", "_id": "54758"} -{"city": "PEPIN", "loc": [-92.139353, 44.458687], "pop": 1504, "state": "WI", "_id": "54759"} -{"city": "PLUM CITY", "loc": [-92.183725, 44.635979], "pop": 1050, "state": "WI", "_id": "54761"} -{"city": "PRAIRIE FARM", "loc": [-91.974205, 45.246183], "pop": 1061, "state": "WI", "_id": "54762"} -{"city": "RIDGELAND", "loc": [-91.879024, 45.186773], "pop": 863, "state": "WI", "_id": "54763"} -{"city": "SAND CREEK", "loc": [-91.701446, 45.170513], "pop": 501, "state": "WI", "_id": "54765"} -{"city": "SHELDON", "loc": [-90.914148, 45.318171], "pop": 1711, "state": "WI", "_id": "54766"} -{"city": "SPRING VALLEY", "loc": [-92.290599, 44.835569], "pop": 2746, "state": "WI", "_id": "54767"} -{"city": "STANLEY", "loc": [-90.938935, 44.968872], "pop": 3157, "state": "WI", "_id": "54768"} -{"city": "STOCKHOLM", "loc": [-92.228429, 44.500441], "pop": 434, "state": "WI", "_id": "54769"} -{"city": "STRUM", "loc": [-91.383271, 44.567356], "pop": 1560, "state": "WI", "_id": "54770"} -{"city": "THORP", "loc": [-90.802861, 44.957319], "pop": 4803, "state": "WI", "_id": "54771"} -{"city": "WHEELER", "loc": [-91.887055, 45.071856], "pop": 1094, "state": "WI", "_id": "54772"} -{"city": "WHITEHALL", "loc": [-91.302953, 44.382683], "pop": 3330, "state": "WI", "_id": "54773"} -{"city": "SPOONER", "loc": [-91.91555, 45.850468], "pop": 5384, "state": "WI", "_id": "54801"} -{"city": "ALMENA", "loc": [-92.002691, 45.419507], "pop": 3180, "state": "WI", "_id": "54805"} -{"city": "MOQUAH", "loc": [-90.876712, 46.577948], "pop": 12254, "state": "WI", "_id": "54806"} -{"city": "BALSAM LAKE", "loc": [-92.414954, 45.458845], "pop": 1765, "state": "WI", "_id": "54810"} -{"city": "BARRON", "loc": [-91.84996, 45.400535], "pop": 4049, "state": "WI", "_id": "54812"} -{"city": "BARRONETT", "loc": [-92.019595, 45.618995], "pop": 804, "state": "WI", "_id": "54813"} -{"city": "BAYFIELD", "loc": [-90.821681, 46.835359], "pop": 2375, "state": "WI", "_id": "54814"} -{"city": "BIRCHWOOD", "loc": [-91.577564, 45.640296], "pop": 1962, "state": "WI", "_id": "54817"} -{"city": "BRUCE", "loc": [-91.290622, 45.447923], "pop": 2654, "state": "WI", "_id": "54819"} -{"city": "BRULE", "loc": [-91.553862, 46.576318], "pop": 902, "state": "WI", "_id": "54820"} -{"city": "CABLE", "loc": [-91.224438, 46.217155], "pop": 1311, "state": "WI", "_id": "54821"} -{"city": "CAMERON", "loc": [-91.730992, 45.403836], "pop": 3314, "state": "WI", "_id": "54822"} -{"city": "CENTURIA", "loc": [-92.547741, 45.448369], "pop": 1791, "state": "WI", "_id": "54824"} -{"city": "COMSTOCK", "loc": [-92.085961, 45.491114], "pop": 473, "state": "WI", "_id": "54826"} -{"city": "CORNUCOPIA", "loc": [-91.097421, 46.837171], "pop": 242, "state": "WI", "_id": "54827"} -{"city": "NEW POST", "loc": [-91.327825, 45.809664], "pop": 377, "state": "WI", "_id": "54828"} -{"city": "CUMBERLAND", "loc": [-92.029709, 45.5403], "pop": 4229, "state": "WI", "_id": "54829"} -{"city": "DAIRYLAND", "loc": [-92.265995, 46.029681], "pop": 1855, "state": "WI", "_id": "54830"} -{"city": "DRUMMOND", "loc": [-91.285569, 46.311896], "pop": 329, "state": "WI", "_id": "54832"} -{"city": "EXELAND", "loc": [-91.223819, 45.675298], "pop": 939, "state": "WI", "_id": "54835"} -{"city": "FOXBORO", "loc": [-92.149254, 46.487511], "pop": 1528, "state": "WI", "_id": "54836"} -{"city": "CLAM FALLS", "loc": [-92.426482, 45.665314], "pop": 4011, "state": "WI", "_id": "54837"} -{"city": "GORDON", "loc": [-91.803301, 46.233603], "pop": 1088, "state": "WI", "_id": "54838"} -{"city": "GRAND VIEW", "loc": [-91.107465, 46.357682], "pop": 280, "state": "WI", "_id": "54839"} -{"city": "EVERGREEN", "loc": [-92.662685, 45.761312], "pop": 4008, "state": "WI", "_id": "54840"} -{"city": "NORTH WOODS BEAC", "loc": [-91.397659, 46.001404], "pop": 9339, "state": "WI", "_id": "54843"} -{"city": "HERBSTER", "loc": [-91.233193, 46.82128], "pop": 230, "state": "WI", "_id": "54844"} -{"city": "HERTEL", "loc": [-92.140419, 45.807561], "pop": 110, "state": "WI", "_id": "54845"} -{"city": "HIGH BRIDGE", "loc": [-90.73826, 46.379613], "pop": 536, "state": "WI", "_id": "54846"} -{"city": "IRON RIVER", "loc": [-91.421056, 46.578984], "pop": 1777, "state": "WI", "_id": "54847"} -{"city": "LADYSMITH", "loc": [-91.108804, 45.471941], "pop": 6737, "state": "WI", "_id": "54848"} -{"city": "LAKE NEBAGAMON", "loc": [-91.740243, 46.502347], "pop": 1824, "state": "WI", "_id": "54849"} -{"city": "LA POINTE", "loc": [-90.603622, 46.846675], "pop": 17, "state": "WI", "_id": "54850"} -{"city": "LUCK", "loc": [-92.416391, 45.556799], "pop": 2131, "state": "WI", "_id": "54853"} -{"city": "MAPLE", "loc": [-91.702015, 46.620591], "pop": 891, "state": "WI", "_id": "54854"} -{"city": "MARENGO", "loc": [-90.839991, 46.407112], "pop": 592, "state": "WI", "_id": "54855"} -{"city": "DELTA", "loc": [-91.057511, 46.457285], "pop": 1519, "state": "WI", "_id": "54856"} -{"city": "MILLTOWN", "loc": [-92.501739, 45.527082], "pop": 1570, "state": "WI", "_id": "54858"} -{"city": "MINONG", "loc": [-91.850628, 46.10445], "pop": 1451, "state": "WI", "_id": "54859"} -{"city": "OJIBWA", "loc": [-91.110922, 45.785351], "pop": 401, "state": "WI", "_id": "54862"} -{"city": "POPLAR", "loc": [-91.825345, 46.57708], "pop": 1064, "state": "WI", "_id": "54864"} -{"city": "PORT WING", "loc": [-91.392016, 46.76204], "pop": 544, "state": "WI", "_id": "54865"} -{"city": "RADISSON", "loc": [-91.22615, 45.773359], "pop": 509, "state": "WI", "_id": "54867"} -{"city": "CANTON", "loc": [-91.733544, 45.510466], "pop": 13996, "state": "WI", "_id": "54868"} -{"city": "SARONA", "loc": [-91.762103, 45.708532], "pop": 1201, "state": "WI", "_id": "54870"} -{"city": "SHELL LAKE", "loc": [-91.960606, 45.753598], "pop": 3450, "state": "WI", "_id": "54871"} -{"city": "SIREN", "loc": [-92.389151, 45.78214], "pop": 2495, "state": "WI", "_id": "54872"} -{"city": "BARNES", "loc": [-91.770154, 46.388821], "pop": 2443, "state": "WI", "_id": "54873"} -{"city": "WENTWORTH", "loc": [-91.945805, 46.588659], "pop": 3029, "state": "WI", "_id": "54874"} -{"city": "EARL", "loc": [-91.677734, 45.960384], "pop": 970, "state": "WI", "_id": "54875"} -{"city": "STONE LAKE", "loc": [-91.509291, 45.833733], "pop": 1662, "state": "WI", "_id": "54876"} -{"city": "SUPERIOR", "loc": [-92.091174, 46.701552], "pop": 29513, "state": "WI", "_id": "54880"} -{"city": "TREGO", "loc": [-91.858051, 45.951556], "pop": 1037, "state": "WI", "_id": "54888"} -{"city": "TURTLE LAKE", "loc": [-92.178142, 45.421349], "pop": 2048, "state": "WI", "_id": "54889"} -{"city": "WASHBURN", "loc": [-90.909516, 46.680595], "pop": 3177, "state": "WI", "_id": "54891"} -{"city": "WEBSTER", "loc": [-92.322722, 45.874001], "pop": 3012, "state": "WI", "_id": "54893"} -{"city": "WEYERHAEUSER", "loc": [-91.428944, 45.426347], "pop": 858, "state": "WI", "_id": "54895"} -{"city": "LORETTA", "loc": [-90.949591, 45.841419], "pop": 1385, "state": "WI", "_id": "54896"} -{"city": "OSHKOSH", "loc": [-88.543635, 44.021962], "pop": 57187, "state": "WI", "_id": "54901"} -{"city": "OSHKOSH", "loc": [-88.607015, 44.030356], "pop": 12608, "state": "WI", "_id": "54904"} -{"city": "ALMOND", "loc": [-89.358364, 44.289832], "pop": 2002, "state": "WI", "_id": "54909"} -{"city": "APPLETON", "loc": [-88.397649, 44.277325], "pop": 25970, "state": "WI", "_id": "54911"} -{"city": "APPLETON", "loc": [-88.432608, 44.270992], "pop": 23028, "state": "WI", "_id": "54914"} -{"city": "APPLETON", "loc": [-88.399902, 44.26351], "pop": 42119, "state": "WI", "_id": "54915"} -{"city": "BANCROFT", "loc": [-89.530438, 44.30648], "pop": 1368, "state": "WI", "_id": "54921"} -{"city": "BEAR CREEK", "loc": [-88.740375, 44.5348], "pop": 1488, "state": "WI", "_id": "54922"} -{"city": "BERLIN", "loc": [-88.949009, 43.976936], "pop": 8132, "state": "WI", "_id": "54923"} -{"city": "CAROLINE", "loc": [-88.888749, 44.727574], "pop": 293, "state": "WI", "_id": "54928"} -{"city": "CLINTONVILLE", "loc": [-88.742999, 44.636442], "pop": 8902, "state": "WI", "_id": "54929"} -{"city": "COLOMA", "loc": [-89.494332, 44.027462], "pop": 1364, "state": "WI", "_id": "54930"} -{"city": "ELDORADO", "loc": [-88.638827, 43.841471], "pop": 765, "state": "WI", "_id": "54932"} -{"city": "TAYCHEEDAH", "loc": [-88.429149, 43.770446], "pop": 37093, "state": "WI", "_id": "54935"} -{"city": "NORTH FOND DU LA", "loc": [-88.481264, 43.792633], "pop": 14665, "state": "WI", "_id": "54937"} -{"city": "FREMONT", "loc": [-88.844852, 44.237884], "pop": 3211, "state": "WI", "_id": "54940"} -{"city": "GREEN LAKE", "loc": [-88.96847, 43.844383], "pop": 2504, "state": "WI", "_id": "54941"} -{"city": "GREENVILLE", "loc": [-88.569579, 44.299747], "pop": 1722, "state": "WI", "_id": "54942"} -{"city": "HANCOCK", "loc": [-89.534448, 44.129743], "pop": 1214, "state": "WI", "_id": "54943"} -{"city": "HORTONVILLE", "loc": [-88.62813, 44.327306], "pop": 5602, "state": "WI", "_id": "54944"} -{"city": "IOLA", "loc": [-89.137311, 44.532183], "pop": 2589, "state": "WI", "_id": "54945"} -{"city": "KING", "loc": [-89.113739, 44.267332], "pop": 661, "state": "WI", "_id": "54946"} -{"city": "LARSEN", "loc": [-88.696305, 44.198421], "pop": 1863, "state": "WI", "_id": "54947"} -{"city": "LEOPOLIS", "loc": [-88.872223, 44.781188], "pop": 403, "state": "WI", "_id": "54948"} -{"city": "MANAWA", "loc": [-88.916444, 44.470148], "pop": 2749, "state": "WI", "_id": "54949"} -{"city": "MARION", "loc": [-88.876706, 44.679414], "pop": 3071, "state": "WI", "_id": "54950"} -{"city": "MENASHA", "loc": [-88.417536, 44.211164], "pop": 22505, "state": "WI", "_id": "54952"} -{"city": "NEENAH", "loc": [-88.479201, 44.181094], "pop": 34261, "state": "WI", "_id": "54956"} -{"city": "NESHKORO", "loc": [-89.233572, 43.898943], "pop": 2210, "state": "WI", "_id": "54960"} -{"city": "NEW LONDON", "loc": [-88.755428, 44.392847], "pop": 13037, "state": "WI", "_id": "54961"} -{"city": "OGDENSBURG", "loc": [-89.011851, 44.467803], "pop": 1509, "state": "WI", "_id": "54962"} -{"city": "OMRO", "loc": [-88.751933, 44.035678], "pop": 5678, "state": "WI", "_id": "54963"} -{"city": "PICKETT", "loc": [-88.713457, 43.900917], "pop": 518, "state": "WI", "_id": "54964"} -{"city": "PINE RIVER", "loc": [-89.040415, 44.160393], "pop": 2148, "state": "WI", "_id": "54965"} -{"city": "PLAINFIELD", "loc": [-89.455896, 44.209079], "pop": 2336, "state": "WI", "_id": "54966"} -{"city": "POY SIPPI", "loc": [-88.926835, 44.110069], "pop": 232, "state": "WI", "_id": "54967"} -{"city": "PRINCETON", "loc": [-89.124913, 43.843827], "pop": 3101, "state": "WI", "_id": "54968"} -{"city": "REDGRANITE", "loc": [-89.098125, 44.04381], "pop": 2093, "state": "WI", "_id": "54970"} -{"city": "RIPON", "loc": [-88.842433, 43.845402], "pop": 10694, "state": "WI", "_id": "54971"} -{"city": "ROSENDALE", "loc": [-88.641704, 43.788903], "pop": 2012, "state": "WI", "_id": "54974"} -{"city": "SCANDINAVIA", "loc": [-89.165042, 44.460436], "pop": 1097, "state": "WI", "_id": "54977"} -{"city": "TILLEDA", "loc": [-88.907498, 44.811812], "pop": 58, "state": "WI", "_id": "54978"} -{"city": "VAN DYNE", "loc": [-88.510008, 43.873399], "pop": 1403, "state": "WI", "_id": "54979"} -{"city": "WAUPACA", "loc": [-89.110143, 44.348101], "pop": 11716, "state": "WI", "_id": "54981"} -{"city": "WAUTOMA", "loc": [-89.266551, 44.065576], "pop": 6318, "state": "WI", "_id": "54982"} -{"city": "WEYAUWEGA", "loc": [-88.941047, 44.320973], "pop": 3886, "state": "WI", "_id": "54983"} -{"city": "WILD ROSE", "loc": [-89.195252, 44.183433], "pop": 2127, "state": "WI", "_id": "54984"} -{"city": "WINNECONNE", "loc": [-88.721384, 44.118704], "pop": 3887, "state": "WI", "_id": "54986"} -{"city": "CHEYENNE", "loc": [-104.796234, 41.143719], "pop": 33107, "state": "WY", "_id": "82001"} -{"city": "CHEYENNE", "loc": [-104.810745, 41.108433], "pop": 15050, "state": "WY", "_id": "82007"} -{"city": "CHEYENNE", "loc": [-104.802328, 41.183566], "pop": 22028, "state": "WY", "_id": "82009"} -{"city": "ALBIN", "loc": [-104.150542, 41.434237], "pop": 310, "state": "WY", "_id": "82050"} -{"city": "LARAMIE", "loc": [-105.819708, 41.562721], "pop": 22, "state": "WY", "_id": "82051"} -{"city": "BUFORD", "loc": [-105.469697, 41.142115], "pop": 97, "state": "WY", "_id": "82052"} -{"city": "BURNS", "loc": [-104.315521, 41.200297], "pop": 1303, "state": "WY", "_id": "82053"} -{"city": "CARPENTER", "loc": [-104.276514, 41.042819], "pop": 222, "state": "WY", "_id": "82054"} -{"city": "CENTENNIAL", "loc": [-105.99451, 41.339149], "pop": 446, "state": "WY", "_id": "82055"} -{"city": "82057", "loc": [-106.164358, 41.070928], "pop": 60, "state": "WY", "_id": "82057"} -{"city": "GARRETT", "loc": [-105.550534, 42.142015], "pop": 120, "state": "WY", "_id": "82058"} -{"city": "JELM", "loc": [-105.925727, 41.145723], "pop": 470, "state": "WY", "_id": "82063"} -{"city": "LARAMIE", "loc": [-105.581146, 41.312907], "pop": 29327, "state": "WY", "_id": "82070"} -{"city": "MC FADDEN", "loc": [-106.137861, 41.6327], "pop": 79, "state": "WY", "_id": "82080"} -{"city": "MERIDEN", "loc": [-104.286606, 41.54236], "pop": 40, "state": "WY", "_id": "82081"} -{"city": "PINE BLUFFS", "loc": [-104.066591, 41.178799], "pop": 1082, "state": "WY", "_id": "82082"} -{"city": "ROCK RIVER", "loc": [-105.974629, 41.746073], "pop": 236, "state": "WY", "_id": "82083"} -{"city": "TIE SIDING", "loc": [-105.446222, 41.052785], "pop": 19, "state": "WY", "_id": "82084"} -{"city": "FISHING BRIDGE", "loc": [-110.674366, 44.853913], "pop": 443, "state": "WY", "_id": "82190"} -{"city": "WHEATLAND", "loc": [-104.967852, 42.049467], "pop": 5952, "state": "WY", "_id": "82201"} -{"city": "CHUGWATER", "loc": [-104.817916, 41.748668], "pop": 295, "state": "WY", "_id": "82210"} -{"city": "FORT LARAMIE", "loc": [-104.522595, 42.213314], "pop": 325, "state": "WY", "_id": "82212"} -{"city": "GLENDO", "loc": [-105.000013, 42.500352], "pop": 471, "state": "WY", "_id": "82213"} -{"city": "GUERNSEY", "loc": [-104.751164, 42.265513], "pop": 1319, "state": "WY", "_id": "82214"} -{"city": "HARTVILLE", "loc": [-104.729564, 42.33339], "pop": 108, "state": "WY", "_id": "82215"} -{"city": "HAWK SPRINGS", "loc": [-104.329498, 41.741481], "pop": 125, "state": "WY", "_id": "82217"} -{"city": "JAY EM", "loc": [-104.311437, 42.357399], "pop": 399, "state": "WY", "_id": "82219"} -{"city": "KEELINE", "loc": [-104.720277, 42.839062], "pop": 209, "state": "WY", "_id": "82220"} -{"city": "LAGRANGE", "loc": [-104.19738, 41.642311], "pop": 413, "state": "WY", "_id": "82221"} -{"city": "LANCE CREEK", "loc": [-104.544958, 43.231636], "pop": 181, "state": "WY", "_id": "82222"} -{"city": "LINGLE", "loc": [-104.331992, 42.134624], "pop": 714, "state": "WY", "_id": "82223"} -{"city": "LOST SPRINGS", "loc": [-104.920901, 42.729835], "pop": 6, "state": "WY", "_id": "82224"} -{"city": "LUSK", "loc": [-104.465076, 42.765953], "pop": 1724, "state": "WY", "_id": "82225"} -{"city": "SHAWNEE", "loc": [-105.032447, 42.839464], "pop": 137, "state": "WY", "_id": "82229"} -{"city": "TORRINGTON", "loc": [-104.191662, 42.062377], "pop": 9575, "state": "WY", "_id": "82240"} -{"city": "VAN TASSELL", "loc": [-104.315073, 42.830004], "pop": 385, "state": "WY", "_id": "82242"} -{"city": "VETERAN", "loc": [-104.370899, 41.982091], "pop": 148, "state": "WY", "_id": "82243"} -{"city": "YODER", "loc": [-104.353507, 41.912018], "pop": 674, "state": "WY", "_id": "82244"} -{"city": "RAWLINS", "loc": [-107.234883, 41.795131], "pop": 9914, "state": "WY", "_id": "82301"} -{"city": "JEFFREY CITY", "loc": [-107.872422, 42.540201], "pop": 253, "state": "WY", "_id": "82310"} -{"city": "BAGGS", "loc": [-107.668733, 41.031191], "pop": 384, "state": "WY", "_id": "82321"} -{"city": "BAIROIL", "loc": [-107.563288, 42.232721], "pop": 236, "state": "WY", "_id": "82322"} -{"city": "DIXON", "loc": [-107.560354, 41.044631], "pop": 253, "state": "WY", "_id": "82323"} -{"city": "ENCAMPMENT", "loc": [-106.780285, 41.205353], "pop": 946, "state": "WY", "_id": "82325"} -{"city": "HANNA", "loc": [-106.528283, 41.87264], "pop": 1585, "state": "WY", "_id": "82327"} -{"city": "MEDICINE BOW", "loc": [-106.201228, 41.903002], "pop": 409, "state": "WY", "_id": "82329"} -{"city": "RYAN PARK", "loc": [-106.797538, 41.446293], "pop": 2462, "state": "WY", "_id": "82331"} -{"city": "SAVERY", "loc": [-107.42338, 41.039485], "pop": 97, "state": "WY", "_id": "82332"} -{"city": "SINCLAIR", "loc": [-107.109083, 41.779741], "pop": 530, "state": "WY", "_id": "82334"} -{"city": "WAMSUTTER", "loc": [-108.151674, 41.658278], "pop": 516, "state": "WY", "_id": "82336"} -{"city": "WORLAND", "loc": [-107.95626, 44.013796], "pop": 7693, "state": "WY", "_id": "82401"} -{"city": "BASIN", "loc": [-108.043787, 44.378765], "pop": 1580, "state": "WY", "_id": "82410"} -{"city": "BURLINGTON", "loc": [-108.432669, 44.444218], "pop": 435, "state": "WY", "_id": "82411"} -{"city": "CODY", "loc": [-109.075611, 44.523135], "pop": 11985, "state": "WY", "_id": "82414"} -{"city": "DEAVER", "loc": [-108.597948, 44.925695], "pop": 499, "state": "WY", "_id": "82421"} -{"city": "GREYBULL", "loc": [-108.079503, 44.491881], "pop": 2460, "state": "WY", "_id": "82426"} -{"city": "HYATTVILLE", "loc": [-107.605318, 44.250693], "pop": 97, "state": "WY", "_id": "82428"} -{"city": "LOVELL", "loc": [-108.414107, 44.833637], "pop": 4322, "state": "WY", "_id": "82431"} -{"city": "MANDERSON", "loc": [-107.953423, 44.311931], "pop": 657, "state": "WY", "_id": "82432"} -{"city": "MEETEETSE", "loc": [-108.950045, 44.196202], "pop": 1010, "state": "WY", "_id": "82433"} -{"city": "OTTO", "loc": [-108.304673, 44.405644], "pop": 120, "state": "WY", "_id": "82434"} -{"city": "POWELL", "loc": [-108.777322, 44.756077], "pop": 9608, "state": "WY", "_id": "82435"} -{"city": "SHELL", "loc": [-107.824333, 44.563649], "pop": 355, "state": "WY", "_id": "82441"} -{"city": "TEN SLEEP", "loc": [-107.415305, 43.997848], "pop": 695, "state": "WY", "_id": "82442"} -{"city": "GRASS CREEK", "loc": [-108.231297, 43.662152], "pop": 4809, "state": "WY", "_id": "82443"} -{"city": "WAPITI", "loc": [-109.432629, 44.47967], "pop": 214, "state": "WY", "_id": "82450"} -{"city": "GAS HILLS", "loc": [-108.411335, 43.045786], "pop": 15471, "state": "WY", "_id": "82501"} -{"city": "ARAPAHOE", "loc": [-108.494134, 42.967936], "pop": 605, "state": "WY", "_id": "82510"} -{"city": "CROWHEART", "loc": [-109.296043, 43.371569], "pop": 157, "state": "WY", "_id": "82512"} -{"city": "DUBOIS", "loc": [-109.649175, 43.545136], "pop": 1493, "state": "WY", "_id": "82513"} -{"city": "FORT WASHAKIE", "loc": [-108.896445, 43.004761], "pop": 1131, "state": "WY", "_id": "82514"} -{"city": "KINNEAR", "loc": [-108.615428, 43.131777], "pop": 420, "state": "WY", "_id": "82516"} -{"city": "ETHETE", "loc": [-108.738288, 42.859678], "pop": 11770, "state": "WY", "_id": "82520"} -{"city": "PAVILLION", "loc": [-108.604394, 43.198515], "pop": 1681, "state": "WY", "_id": "82523"} -{"city": "CASPER", "loc": [-106.316571, 42.845763], "pop": 21224, "state": "WY", "_id": "82601"} -{"city": "CASPER", "loc": [-106.389634, 42.826073], "pop": 24812, "state": "WY", "_id": "82604"} -{"city": "CASPER", "loc": [-106.280649, 42.840629], "pop": 12789, "state": "WY", "_id": "82609"} -{"city": "ALCOVA", "loc": [-106.610199, 42.568383], "pop": 10, "state": "WY", "_id": "82620"} -{"city": "ARMINTO", "loc": [-107.343611, 43.120883], "pop": 14, "state": "WY", "_id": "82630"} -{"city": "DOUGLAS", "loc": [-105.385484, 42.762558], "pop": 7502, "state": "WY", "_id": "82633"} -{"city": "EVANSVILLE", "loc": [-106.263886, 42.861384], "pop": 1621, "state": "WY", "_id": "82636"} -{"city": "GLENROCK", "loc": [-105.857911, 42.867495], "pop": 3483, "state": "WY", "_id": "82637"} -{"city": "KAYCEE", "loc": [-106.56323, 43.723625], "pop": 876, "state": "WY", "_id": "82639"} -{"city": "LYSITE", "loc": [-107.648781, 43.328417], "pop": 81, "state": "WY", "_id": "82642"} -{"city": "MIDWEST", "loc": [-106.266818, 43.410829], "pop": 756, "state": "WY", "_id": "82643"} -{"city": "SHOSHONI", "loc": [-108.100667, 43.245707], "pop": 600, "state": "WY", "_id": "82649"} -{"city": "NEWCASTLE", "loc": [-104.226205, 43.851098], "pop": 4833, "state": "WY", "_id": "82701"} -{"city": "ALADDIN", "loc": [-104.19314, 44.747331], "pop": 230, "state": "WY", "_id": "82710"} -{"city": "BEULAH", "loc": [-104.153095, 44.573575], "pop": 187, "state": "WY", "_id": "82712"} -{"city": "DEVILS TOWER", "loc": [-104.793638, 44.617067], "pop": 119, "state": "WY", "_id": "82714"} -{"city": "FOUR CORNERS", "loc": [-104.122897, 44.100747], "pop": 38, "state": "WY", "_id": "82715"} -{"city": "GILLETTE", "loc": [-105.497442, 44.282009], "pop": 25968, "state": "WY", "_id": "82716"} -{"city": "HULETT", "loc": [-104.617367, 44.735222], "pop": 1054, "state": "WY", "_id": "82720"} -{"city": "PINE HAVEN", "loc": [-104.905904, 44.299932], "pop": 1820, "state": "WY", "_id": "82721"} -{"city": "OSAGE", "loc": [-104.4226, 43.998982], "pop": 292, "state": "WY", "_id": "82723"} -{"city": "OSHOTO", "loc": [-104.937659, 44.583023], "pop": 57, "state": "WY", "_id": "82724"} -{"city": "RECLUSE", "loc": [-105.776005, 44.786149], "pop": 183, "state": "WY", "_id": "82725"} -{"city": "ROZET", "loc": [-105.245875, 44.305825], "pop": 900, "state": "WY", "_id": "82727"} -{"city": "SUNDANCE", "loc": [-104.383696, 44.405755], "pop": 1827, "state": "WY", "_id": "82729"} -{"city": "UPTON", "loc": [-104.635159, 44.089271], "pop": 1355, "state": "WY", "_id": "82730"} -{"city": "GILLETTE", "loc": [-105.373236, 44.835689], "pop": 187, "state": "WY", "_id": "82731"} -{"city": "WRIGHT", "loc": [-105.532327, 43.829349], "pop": 2132, "state": "WY", "_id": "82732"} -{"city": "SHERIDAN", "loc": [-106.964795, 44.78486], "pop": 20025, "state": "WY", "_id": "82801"} -{"city": "ARVADA", "loc": [-106.109191, 44.689876], "pop": 107, "state": "WY", "_id": "82831"} -{"city": "BANNER", "loc": [-106.87331, 44.590804], "pop": 983, "state": "WY", "_id": "82832"} -{"city": "BUFFALO", "loc": [-106.70726, 44.34847], "pop": 5269, "state": "WY", "_id": "82834"} -{"city": "CLEARMONT", "loc": [-106.458071, 44.66101], "pop": 350, "state": "WY", "_id": "82835"} -{"city": "DAYTON", "loc": [-107.302605, 44.877958], "pop": 986, "state": "WY", "_id": "82836"} -{"city": "PARKMAN", "loc": [-107.325393, 44.964965], "pop": 148, "state": "WY", "_id": "82838"} -{"city": "ACME", "loc": [-107.159833, 44.904789], "pop": 868, "state": "WY", "_id": "82839"} -{"city": "STORY", "loc": [-107.049229, 44.607169], "pop": 63, "state": "WY", "_id": "82842"} -{"city": "RANCHESTER", "loc": [-107.303429, 44.768228], "pop": 32, "state": "WY", "_id": "82844"} -{"city": "ROCK SPRINGS", "loc": [-109.230047, 41.605957], "pop": 23927, "state": "WY", "_id": "82901"} -{"city": "BONDURANT", "loc": [-110.335287, 43.223798], "pop": 116, "state": "WY", "_id": "82922"} -{"city": "BOULDER", "loc": [-109.540105, 42.688146], "pop": 112, "state": "WY", "_id": "82923"} -{"city": "CORA", "loc": [-109.915351, 43.139921], "pop": 30, "state": "WY", "_id": "82925"} -{"city": "EVANSTON", "loc": [-110.963067, 41.260947], "pop": 12577, "state": "WY", "_id": "82930"} -{"city": "FORT BRIDGER", "loc": [-110.347428, 41.28282], "pop": 3777, "state": "WY", "_id": "82933"} -{"city": "GREEN RIVER", "loc": [-109.471445, 41.51959], "pop": 13956, "state": "WY", "_id": "82935"} -{"city": "LONETREE", "loc": [-110.172862, 41.049144], "pop": 24, "state": "WY", "_id": "82936"} -{"city": "LYMAN", "loc": [-110.292629, 41.329136], "pop": 2327, "state": "WY", "_id": "82937"} -{"city": "MC KINNON", "loc": [-109.874536, 41.040898], "pop": 188, "state": "WY", "_id": "82938"} -{"city": "PINEDALE", "loc": [-109.856088, 42.854331], "pop": 2326, "state": "WY", "_id": "82941"} -{"city": "COLTER BAY", "loc": [-110.766277, 43.460734], "pop": 9078, "state": "WY", "_id": "83001"} -{"city": "KELLY", "loc": [-110.544186, 43.609361], "pop": 203, "state": "WY", "_id": "83011"} -{"city": "MOOSE", "loc": [-110.857493, 43.771201], "pop": 519, "state": "WY", "_id": "83012"} -{"city": "MORAN", "loc": [-110.329429, 43.881635], "pop": 191, "state": "WY", "_id": "83013"} -{"city": "WILSON", "loc": [-110.874199, 43.49922], "pop": 1099, "state": "WY", "_id": "83014"} -{"city": "KEMMERER", "loc": [-110.52834, 41.788661], "pop": 4258, "state": "WY", "_id": "83101"} -{"city": "AFTON", "loc": [-110.941976, 42.712829], "pop": 3201, "state": "WY", "_id": "83110"} -{"city": "AUBURN", "loc": [-110.994415, 42.805114], "pop": 488, "state": "WY", "_id": "83111"} -{"city": "BEDFORD", "loc": [-110.95556, 42.932336], "pop": 1177, "state": "WY", "_id": "83112"} -{"city": "MARBLETON", "loc": [-110.132954, 42.552059], "pop": 1861, "state": "WY", "_id": "83113"} -{"city": "COKEVILLE", "loc": [-110.916419, 42.057983], "pop": 905, "state": "WY", "_id": "83114"} -{"city": "DANIEL", "loc": [-110.133624, 42.917629], "pop": 398, "state": "WY", "_id": "83115"} -{"city": "ETNA", "loc": [-111.015996, 43.138606], "pop": 524, "state": "WY", "_id": "83118"} -{"city": "FREEDOM", "loc": [-111.029178, 43.017167], "pop": 212, "state": "WY", "_id": "83120"} -{"city": "GROVER", "loc": [-110.924392, 42.796472], "pop": 335, "state": "WY", "_id": "83122"} -{"city": "LA BARGE", "loc": [-110.210865, 42.24734], "pop": 606, "state": "WY", "_id": "83123"} -{"city": "SMOOT", "loc": [-110.922351, 42.619238], "pop": 414, "state": "WY", "_id": "83126"} -{"city": "THAYNE", "loc": [-111.011354, 42.933026], "pop": 505, "state": "WY", "_id": "83127"} \ No newline at end of file diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index a463689bd8..7e0ef51328 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -2444,9 +2444,8 @@ A `TypedAggregation`, just like an `Aggregation`, holds the instructions of the At runtime, field references get checked against the given input type, considering potential `@Field` annotations. [NOTE] ==== -Changed in 3.2 referencing none-xistent properties does no longer raise errors. To restore the previous behaviour use the `strictMapping` option of `AggregationOptions`. +Changed in 3.2 referencing non-existent properties does no longer raise errors. To restore the previous behaviour use the `strictMapping` option of `AggregationOptions`. ==== -+ * `AggregationDefinition` + An `AggregationDefinition` represents a MongoDB aggregation pipeline operation and describes the processing that should be performed in this aggregation step. Although you could manually create an `AggregationDefinition`, we recommend using the static factory methods provided by the `Aggregate` class to construct an `AggregateOperation`. diff --git a/src/main/resources/changelog.txt b/src/main/resources/changelog.txt index e7b34bdd63..3de91d4a95 100644 --- a/src/main/resources/changelog.txt +++ b/src/main/resources/changelog.txt @@ -1,6 +1,16 @@ Spring Data MongoDB Changelog ============================= +Changes in version 3.2.1 (2021-05-14) +------------------------------------- +* #3638 - Introduce template method for easier customization of fragments. +* #3632 - Fix bullet points in aggregations framework asciidoc. + + +Changes in version 3.1.9 (2021-05-14) +------------------------------------- + + Changes in version 3.2.0 (2021-04-14) ------------------------------------- * #3623 - `@Aggregation` repository query method causes `NullPointerException` when the result is empty. @@ -3429,6 +3439,8 @@ Repository + + diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 07cf1da6a0..e80acdca19 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data MongoDB 3.2 GA (2021.0.0) +Spring Data MongoDB 3.2.1 (2021.0.1) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -27,3 +27,4 @@ conditions of the subcomponent's license, as noted in the LICENSE file. +