Geospatial Features of MongoDB
Introduction:
This blog demonstrates certain features of MongoDB for Geospatial record storage and manipulation. Spring Boot application is being used to connect to MongoDB and demonstrate these features.
Software Requirements for Windows:
MongoDB 3.6 : Install mongoDB 3.6 by following this link: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/
Data Preparation
Insert Data in MongoDB:
1. db.user.insertOne({
location: {
route: "Dhule Moghan Road1",
locality: "Ranmala",
area1: "Dhule",
area2: "Maharashtra",
country: "India",
postalCode: "424311",
locationCoord: [74.7774223, 20.833993]
}
})
2. db.user.insertOne({
location: {
route: "Tapola Road",
locality: "",
area1: "Satara",
area2: "Maharashtra",
country: "India",
postalCode: "412806",
locationCoord: [73.6688232421875, 17.905568811964685]
}
})
3. db.user.insertOne({
location: {
route: "Lane Number 4",
locality: "Aundh",
area1: "Pune",
area2: "Maharashtra",
country: "India",
postalCode: "411007",
locationCoord: [73.81301879882812, 18.55839092512608]
}
})
Geospatial features of MongoDB
1. Geospatial Data: In MongoDB , geospatial data can be stored as GeoJSON objects or as legacy coordinate pairs.
Using Legacy Coordinate Pairs: To specify data as legacy coordinate pairs, you can use either an array(preferred) or an embedded document.
-Specify via an array :
<field> : [<x>,<y>]
If specifying latitude and longitude coordinates, list the longitude first and then latitude. i.e.
<field> : [<longitude>,<latitude>]
§ Valid longitude values are between -180 and 180, both inclusive.
§ Valid latitude values are between -90 and 90(both inclusive).
For example,
location :{
locationCoord:[-73.856077,40.848447]
}
2. Geospatial Indexes:
2d indexes support queries that calculate geometries on a two-dimensional plane.
Creating GeoSpatial Index:
Create geospatial index in MongoDB using @GeoSpatialIndexed annotation supported by Spring Boot and index type as 2d.
Use a 2d index if:
§ Your database has legacy coordinate pairs from MongoDB 2.2 or earlier ,and
§ You do not intend to store any location data as GeoJSON objects.
Use 2dsphere index incase of GeoJSON objects.
@GeoSpatialIndexed(type=GeoSpatialIndexType.GEO_2D) annotation when applied at the field level to describe how to geoindex the field. When application is started, this index is created for field locationCoord.
Ýou can check this index in MongoDb by running command,
db.user.getIndexes()
This will return output as shown below,
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "user.user"
},
{
"v" : 2,
"key" : {
"location.locationCoord" : "2d" /index type
},
"name" : "location.locationCoord", //index name
"ns" : "user.user",
"bits" : 26,
"min" : -180,
"max" : 180
}
]
As you can see, we have two indexes – one of them is _id – which was created by default due to the @Id annotation and the second one is our locationCoord field.
Source code for Spring Boot, MongoDB example
Source code for the below application can be found here https://gitlab.com/vidyashree/mongodbGeoSpatialSearch
Spring Boot Application:
Spring boot application is used for this example. The following Maven dependencies are required,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Application.properties contain following line ,
spring.data.mongodb.host=mongo
Add ‘127.0.0.1 mongo’ line in C:\Windows\System32\drivers\etc\hosts file
Writing the application:
Start by creating a domain object to represent User with Location information.
Uses @Document to define a “collection name” when you save this object. In this case, when “User” object saves, it will save into “user” collection.
User Object is represented as:
package com.geospatial.search.demo.domain;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "user")
public class User {
@Id
ObjectId id;
Location location;//get and set properties
}
Location Object is represented as:
public class Location {
String route; //Dhule Moghan Road
String locality; //Ranmala
String area1; //Dhule String area2; //Maharashtra
String country; //India
String postalCode; //424311
@GeoSpatialIndexed(type= GeoSpatialIndexType.GEO_2D)
double[] locationCoord; //order should be <longitude,latitude> //get and set properties
}
Every User has automatically-generated id and Location.
Rest Controller:
package com.geospatial.search.demo.controller;
import com.geospatial.search.demo.domain.User;
import com.geospatial.search.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping(value="/users",method = RequestMethod.GET)
private List<User> getUsersByLocation(@RequestParam double longitude, @RequestParam double latitude, @RequestParam double distance) {
List<User> result = null;
result=userService.getUsersByLocation(longitude,latitude,distance);
return result;
}
}
Service Implementation:
package com.geospatial.search.demo.service;
import com.geospatial.search.demo.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
@Service
public class UserService implements UserInterface{
@Autowired
private MongoOperations mongoOperations;
@Override
public List<User> getUsersByLocation(double longitude,double latitude,double distance){
List<User> result = null;
List<AggregationOperation> list = new ArrayList<AggregationOperation>();
Point p = new Point(longitude, latitude);
NearQuery nearQuery = NearQuery.near(p, Metrics.KILOMETERS).maxDistance(distance);
list.add(Aggregation.geoNear(nearQuery, "distance"));
list.add(Aggregation.project("id", "location"));
TypedAggregation<User> agg = newAggregation(User.class, list);
result = mongoOperations.aggregate(agg, User.class).getMappedResults();
return result;
}
}
Description: Here, we used aggregation pipeline to search locations where documents enter a multi-stage pipeline that transforms the documents into an aggregated result. The most basic pipeline stages provide filters that operate like queries and document transformations that modify the form of output document.
Aggregation: Aggregation operation process data records and return computed results. Aggregation operations group values from multiple documents together, and can perform a variety of operations on the grouped data to return a single result.
Using a geospatial (2d) index ,you can query locations in two ways,
· Proximity:To determine a set of points near a point, or within a certain distance from point specified in geospatial queries.For example,
db.user.find( {"location.locationCoord": { $near: [73.85437774658203,18.46198844909668], $maxDistance: 10 } } ).pretty()
· Inclusion:To determine if any of the stored points were within a specified area,provided special MongoDB-specific operators like $box,$polygon in their queries.
We are using Proximity queries with $near and $maxDisatnce.The $maxDistance operator limits a $near query to return only those documents that fall within a maximum distance of a point.If you query for legacy coordinate pairs, specify $maxDistance in radians.
Starting Application:
· Start mongoDB 3.6.
· Run spring boot application which will automatically create database ‘user’ and collection ‘user’. It also creates index for field locationCoord.
· Insert data by importing userdata.json
C:\Program Files\MongoDB\Server\3.6\bin>mongoimport.exe --db user --collection user --file userdata.json OR
· Insert data manually,
db.user.insertOne({
location: {
route: "Survey No. 1",
locality: "Ranmala",
area1: "Banglore",
area2: "Karnataka",
country: "India",
postalCode: "121212",
locationCoord: [77.5945627, 12.9715987]
}
})
· Go to http://127.0.0.1:9000/users/?longitude=77.5945627&latitude=12.9715987&distance=280
Accessing the Rest API: As you can see, there is a ’/users’ endpoint available and already has longitude, latitude and distance as request parameters.
Now we started our application and go to ‘Postman’,
Set Method: GET
URI: http://127.0.0.1:9000/users/?longitude=74.7774223&latitude=20.833993&distance=280
Header: Accept-Application/Json
Which returns,
[
{
"id": "598ebff065ce79a3706730d0",
"location": {
"route": "Dhule Moghan Road1",
"locality": "Ranmala",
"area1": "Dhule",
"area2": "Maharashtra",
"country": "India",
"postalCode": "424311",
"locationCoord": [
74.7774223,
20.833993
]
}
},
{
"id": "598ee1ca65ce79a3706730d2",
"location": {
"route": "Lane Number 4",
"locality": "Aundh",
"area1": "Pune",
"area2": "Maharashtra",
"country": "India",
"postalCode": "411007",
"locationCoord": [
73.81301879882812,
18.55839092512608
]
}
}
]
If you pass all three parameters a ‘0’ as shown below,
URL:http://127.0.0.1:9000/users/?longitude=0&latitude=0&distance=0
Header:
-Accept: Application/JSON
The output will be,
[
{
"id": "598ec30865ce79a3706730d1",
"location": {
"route": "Tapola Road",
"locality": "",
"area1": "Satara",
"area2": "Maharashtra",
"country": "India",
"postalCode": "412806",
"locationCoord": [
73.6688232421875,
17.905568811964685
]
}
},
{
"id": "598ee1ca65ce79a3706730d2",
"location": {
"route": "Lane Number 4",
"locality": "Aundh",
"area1": "Pune",
"area2": "Maharashtra",
"country": "India",
"postalCode": "411007",
"locationCoord": [
73.81301879882812,
18.55839092512608
]
}
},
{
"id": "598ebff065ce79a3706730d0",
"location": {
"route": "Dhule Moghan Road1",
"locality": "Ranmala",
"area1": "Dhule",
"area2": "Maharashtra",
"country": "India",
"postalCode": "424311",
"locationCoord": [
74.7774223,
20.833993
]
}
}
]
Steps to export mongodb collection data as JSON file:
· Start command prompt as administrator.
· Cd to 'C:\Program Files\MongoDB\Server\3.6\bin' on Windows where 'mongoexport.exe' is located.
· Run following command:
mongoexport --db user --collection user --out userdata.json
You will find 'userdata.json' file in 'C:\Program Files\MongoDB\Server\3.6\bin' directory.