How to find all mongoDB documents based on geolocation - mongodb

I'm using the react geosuggest package to save locations of my events in MongoDB. The package takes data from Google places and each time I store it, it gets stored like this:
"_id": "vvagbSbEginyrQGK8",
"name": "test3",
"googleLocation": {
"label": "Testaccio, Roma, Italia",
"placeId": "ChIJE47T5i5gLxMRhiCxCUAFq4Q",
"isFixture": false,
"gmaps": {
"address_components": [
{
"long_name": "Monte Testaccio",
"short_name": "Monte Testaccio",
"types": [
"establishment",
"natural_feature"
]
},
{
"long_name": "Rome",
"short_name": "Rome",
"types": [
"locality",
"political"
]
},
{
"long_name": "Rome",
"short_name": "Rome",
"types": [
"administrative_area_level_3",
"political"
]
},
{
"long_name": "Metropolitan City of Rome",
"short_name": "RM",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "Lazio",
"short_name": "Lazio",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "Italia",
"short_name": "IT",
"types": [
"country",
"political"
]
},
{
"long_name": "00153",
"short_name": "00153",
"types": [
"postal_code"
]
}
],
"formatted_address": "Monte Testaccio, 00153 Rome, Italia",
"geometry": {
"location": {},
"location_type": "APPROXIMATE",
"viewport": {
"f": {
"b": 41.87460301970851,
"f": 41.87730098029151
},
"b": {
"b": 12.474345019708494,
"f": 12.477042980291571
}
}
},
"place_id": "ChIJE47T5i5gLxMRhiCxCUAFq4Q",
"types": [
"establishment",
"natural_feature"
]
},
"location": {
"lat": 41.87595200000001,
"lng": 12.475693999999976
}
}
I've tried a lot of different queries with the mongodb near query, but I can't figure it out. Anyone know about a query that will for example find all documents within 10000 meter based on longitude and latitude that I send in.

According to the current version of mongodb, it has Gospatial query operators and you can achieve what you need after modifying the collection which contains the places as following:
First you should create "2dsphere" index for the collection, let's name it "places" as following:
db.places.createIndex( { loc : "2dsphere" } );
/*
the document should contain "loc" property as following:
{
loc : { type: "Point", coordinates: [ -73.88, 40.78 ] },
//+ the other needed properties.
}
*/
Then for a specific origin and distance range, you can apply the following query:
db.places.find(
{
loc:
{ $near :
{
$geometry: { type: "Point", coordinates: [ -73.9667, 40.78 ]
//the origin point.
},
$minDistance: 1000, //in meter
$maxDistance: 5000 //in meter
}
}
}
)
Note that the coordinates ordered as following [long,lat].
for more information you can check the mongodb document in the following link:
2Dshpere indexes
Geospatial operators

Related

$geoNear for a list embeded locations and return distance for all location in list

I have an user collection:
{
"name": "David",
"age": 20,
"addresses": [
{
"radius": 10000,
"location": {
"type": "Point",
"coordinates": [106.785299, 20.999999]
}
},
{
"radius": 30000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
}
}
]
}
Each user will have one or more address. I want to calculate the distance between these addresses with a point, then using calculated distance to compare with radius of each address. If distance < radius then keep address else remove address from addresses list. I am using below query:
db.collection.aggregrate(
{
"$geoNear": {
"near": {"type": "Point", "coordinates": [ 105.823620, 21.006047 ]},
"distanceField": "distance",
"key": "addresses.location"
}
}
)
But this query only return the distance of nearest address, like this:
{
"name": "David",
"age": 20,
"addresses": [
{
"radius": 10000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
}
},
{
"radius": 30000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
}
}
],
"distance": 110000 // <--- distance is added here, just for nearest addrest
}
My expected result:
{
"name": "David",
"age": 20,
"addresses": [
{
"radius": 10000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
},
"distance": 2000``// <------ add distance here for each addesss`
},
{
"radius": 30000,
"location": {
"type": "Point",
"coordinates": [105.785299, 20.979733]
},
"distance": 30000 // <------ add distance here for each addesss
}
]
}
So next stage I can compare distance with radius and keep proper adddress
Anybody know how to do it ? thanks
You need to store each address in an individual document:
{
"_id" : ObjectId("5ec77d127df107cd889d567d"),
"name" : "David",
"age" : 20,
"addresses" : {
"radius" : 10000,
"location" : {
"type" : "Point",
"coordinates" : [
105.785299,
20.979733
]
}
}
},
{
"_id" : ObjectId("5ec77f7843732e8f9a63bf67"),
"name" : "David",
"age" : 20,
"addresses" : {
"radius" : 30000,
"location" : {
"type" : "Point",
"coordinates" : [
105.795299,
20.989733
]
}
}
}
Now, we perform $geoNear and $group stages:
db.user.aggregate([
{
"$geoNear": {
"near": {
"type": "Point",
"coordinates": [
105.823620,
21.006047
]
},
"distanceField": "distance",
"key": "addresses.location"
}
},
{
"$group": {
"_id": "$name",
"name": {
"$first": "$name"
},
"age": {
"$first": "$age"
},
"addresses": {
"$push": {
"$mergeObjects": [
"$addresses",
{
"distance": "$distance"
}
]
}
}
}
}
])

Stuck on simple jolt transformation

So I have this json retrieved from the Google Maps API and I just want to get longitude and latitude. I am looking to use the jolt template to extract just the information that I need.
{
"results": [
{
"address_components": [
{
"long_name": "1115",
"short_name": "1115",
"types": [
"street_number"
]
},
{
"long_name": "West Idaho Avenue",
"short_name": "W Idaho Ave",
"types": [
"route"
]
},
{
"long_name": "Ontario",
"short_name": "Ontario",
"types": [
"locality",
"political"
]
},
{
"long_name": "Malheur County",
"short_name": "Malheur County",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "Oregon",
"short_name": "OR",
"types": [
"administrative_area_level_1",
"political"
]
},
{`enter code here`
"long_name": "United States",
"short_name": "US",
"types": [
"country",
"political"
]
},
{
"long_name": "97914",
"short_name": "97914",
"types": [
"postal_code"
]
},
{
"long_name": "2146",
"short_name": "2146",
"types": [
"postal_code_suffix"
]
}
],
"formatted_address": "1115 W Idaho Ave, Ontario, OR 97914, USA",
"geometry": {
"location": {
"lat": 44.0294445,
"lng": -116.9776502
},
"location_type": "ROOFTOP",
"viewport": {
"northeast": {
"lat": 44.03079348029149,
"lng": -116.9763012197085
},
"southwest": {
"lat": 44.02809551970849,
"lng": -116.9789991802915
}
}
},
"partial_match": true,
"place_id": "ChIJP3C3Z6uPr1QRUDkcSIXzx5g",
"types": [
"establishment",
"point_of_interest",
"school"
]
}
],
"status": "OK"
}
So this is the jolt spec that I am using:
[
{
"operation": "shift",
"spec": {
"results": {
"*": {
"geometry": {
"location": {
"lat": "employees[&1].firstName",
"lng": "employees[&1].lastName"
}
}
}
}
}
}
]
I would like to retrieve a json that looks like this:
{
"data" : [
{
"lng": "-116.9763012197085",
"lat": "44.0294445"
} ]
}
But I keep getting null... Any help would be appreciated thanks
Your original spec wasn't working because "lat": "employees[&1].firstName" should be "lat": "employees[&3].firstName".
In this case &1 evaluated to the word "location". &3 gets you up the tree to the index of the input results array, which is what I think you meant.
Shift maintains a stack as it doing its transform, the & wildcard lets you grab previously matched values from the stack / up the tree.
From where "lat" is in the spec, it is 4 levels up the stack 0,1,2,3 to get to the index of the results array, that was matched by the *.
Spec
[
{
"operation": "shift",
"spec": {
"results": {
"*": {
"geometry": {
"location": {
"lat": "data[&3].lat",
"lng": "data[&3].lng"
}
}
}
}
}
}
]

Extracting data from HTTP Response - Angular 5

I am trying to extract lat and lng values form the resulting JSON, from HTTP response -
lat: any;
lng: any;
geoResult: any;
public getGeoCoordinates(store) {
let apiUrl = '/assets/GoogleGeoCoordinates.json';
this.http.get(apiUrl).subscribe( resp => {
this.geoResult = resp;
});
}
from above method, I am trying to capture the values as follows
if (resp['status'] == 'OK' && resp['results']['length'] > 0) {
this.lat = resp['results'][0]['geometry']['location']['lat'];
this.lng = resp['results'][0]['geometry']['location']['lng'];
}
if I use alert(), like alert('Latitude : ' + resp['results'][0]['geometry']['location']['lat']);
alert is showing the value.
If I store that value in 'this.lat' variable, I am getting 'undefined'
can someone help me, to understand to get the values from the json HTTP response
JSON file content - GoogleGeoCoordinates.json
{
"results": [
{
"address_components": [
{
"long_name": "120",
"short_name": "120",
"types": [
"street_number"
]
},
{
"long_name": "West 56th Street",
"short_name": "W 56th St",
"types": [
"route"
]
},
{
"long_name": "Manhattan",
"short_name": "Manhattan",
"types": [
"political",
"sublocality",
"sublocality_level_1"
]
},
{
"long_name": "New York",
"short_name": "New York",
"types": [
"locality",
"political"
]
},
{
"long_name": "New York County",
"short_name": "New York County",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "New York",
"short_name": "NY",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "United States",
"short_name": "US",
"types": [
"country",
"political"
]
},
{
"long_name": "10019",
"short_name": "10019",
"types": [
"postal_code"
]
}
],
"formatted_address": "120 W 56th St, New York, NY 10019, USA",
"geometry": {
"location": {
"lat": 40.7640254,
"lng": -73.97896
},
"location_type": "ROOFTOP",
"viewport": {
"northeast": {
"lat": 40.7653743802915,
"lng": -73.97761101970849
},
"southwest": {
"lat": 40.7626764197085,
"lng": -73.98030898029151
}
}
},
"place_id": "ChIJE4YK3PlYwokRybTACneYny4",
"types": [
"street_address"
]
}
],
"status": "OK"
}
I have assigned the values outside the 'subscribe', and it seems started working.
working code as follows -
public getGeoCoordinates(store) {
let apiUrl = '/assets/GoogleGeoCoordinates.json';
this.http.get(apiUrl).subscribe( resp => {
this.geoResult = resp;
});
this.lat = this.geoResult['results'][0]['geometry']['location']['lat'];
this.lng = this.geoResult['results'][0]['geometry']['location']['lng'];
}

MongoDB Query aggreate/group by and geometry/location/geoNear query

I am trying to fetch the documents with geometry within a certain location, however only want to return a single document per UUID. For this project, in most cases, there are many documents for each UUID that match the $near selector, hence we get many documents with the same UUID.
Can anyone assist with completing the below query so it only returns a single document per uuid (most recent "date")?
db.device.find(
{
location:
{ $near :
{
$geometry: { type: "Point", coordinates: [ -73.9667, 40.78 ] },
$minDistance: 1000,
}
}
}
)
Here's an example of the collection:
{
"_id":ObjectId("5a4f1ff0fc6ded723265e6b0"),
"uuid":"user1",
"date": "2018-01-20 11:58:29.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
},
{
"_id":ObjectId("5a62a245ce689f68245450a7"),
"uuid":"user2",
"date": "2018-01-20 11:58:07.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
},
{
"_id":ObjectId("5a62a20fce689f7a14648c62"),
"uuid":"user1",
"date": "2018-01-20 11:58:39.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
},
{
"_id":ObjectId("5a62a205ce689f7039203923"),
"uuid":"user1",
"date": "2018-01-20 11:58:49.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
},
{
"_id":ObjectId("5a62a277ce689f703a3eacb3"),
"uuid":"user2",
"date": "2018-01-20 11:58:59.000",
"location":{
"type": "Point",
"coordinates":[
//remove for demo sake
]
}
}
When performing this kind of heavier operations, you can switch to using an aggregation pipeline.
Using this input:
{
"uuid": "user1",
"date": "2018-01-20 11:58:29.000",
"location": { "type": "Point", "coordinates":[-0.17818, 51.15609] }
},
{
"uuid": "user2",
"date": "2018-01-20 11:58:07.000",
"location": { "type": "Point", "coordinates":[2.35944, 48.72528] }
},
{
"uuid": "user1",
"date": "2018-01-20 11:58:39.000",
"location": { "type": "Point", "coordinates": [1.45414, 43.61132] }
},
{
"uuid": "user1",
"date": "2018-01-20 11:58:49.000",
"location": { "type": "Point", "coordinates":[-6.24889, 53.33306] }
},
{
"uuid": "user2",
"date": "2018-01-20 11:58:59.000",
"location": { "type": "Point", "coordinates":[-3.68248, 40.47184] }
}
Using this index:
db.device.createIndex( { location : "2dsphere" } )
This pipeline should perform what you want:
db.device.aggregate([
{ $match: { location: { $geoWithin: { $centerSphere: [ [ -0.17818, 51.15609 ], 0.1232135647961246 ] } } } },
{ $sort: { "date": -1 } },
{ $group: { _id: { uuid: "$uuid" }, users: { $push: { "uuid": "$uuid", "date": "$date", "location": "$location" } } } },
{ $project: { user: { $arrayElemAt: ["$users", 0] } } }
])
I first adapted the find/$near operator to an aggregation equivalent ($geoWithin/$centerSphere). It matches locations within 0.123 radians (488 kms (0.123*3963.2)).
I then directly sort by date, this way when documents will then be grouped by user, I will be able to easily select the first per user.
I then group by user.
And finally for each user, as I have a value produced by $group which is an array of the user documents (sorted), I just extract the first item of the array with $arrayElemAt.
This produces:
{
"_id" : { "uuid" : "user2" },
"user": {
"uuid": "user2",
"date": "2018-01-20 11:58:07.000",
"location": { "type": "Point", "coordinates": [ 2.35944, 48.72528 ] }
}
}
{
"_id": { "uuid" : "user1" },
"user": {
"uuid": "user1",
"date": "2018-01-20 11:58:49.000",
"location": { "type": "Point", "coordinates": [ -6.24889, 53.33306 ] }
}
}

Elasticsearch indexing of Twitter bounding box not recognized as a geo_shape

I'm trying to create an Elasticsearch mapping for Twitter's Place geo bounding_box array and I can't get Elasticsearch to index it as a geo bounding box. In my app, I will be getting the raw JSON from Twitter4j, however the bounding box does not close the bounding box, so for the purpose of this test, I edited the json and closed it. I'm using Elastic cloud (ES v5) and the Rest API and then visualizing with Kibana.
Here is the mapping I'm trying to use. I've tried several variations with and without a "properties" block and it doesn't work. With this mapping, I am successfully able to PUT the mapping, but when I POST the document, Kibana recognizes the array as an unknown field type.
The Point coordinates field is indexed as a geopoint just fine, it's the bounding box that does not.
Here is my mapping:
PUT /testgeo
{
"mappings": {
"tweet": {
"_all": {
"enabled": false
},
"properties": {
"created_at": {
"type": "date",
"format": "EEE MMM dd HH:mm:ss Z YYYY||strict_date_optional_time||epoch_millis"
},
"coordinates": {
"properties": {
"coordinates": {
"type": "geo_point",
"ignore_malformed": true
}
}
},
"place": {
"properties": {
"bounding_box": {
"type": "geo_shape",
"tree": "quadtree",
"precision": "1m"
}
}
}
}
}
}
}
Here is the snippet of the document I am trying to POST (NOTE: I manually added the 5th array element to close the bounding box).
POST /testgeo/tweet/1
{
...
"coordinates": {
"type": "point",
"coordinates": [
0.78055556,
51.97222222
]
},
"place": {
"id": "0c31a1a5b970086e",
"url": "https:\/\/api.twitter.com\/1.1\/geo\/id\/0c31a1a5b970086e.json",
"place_type": "city",
"name": "Bures",
"full_name": "Bures, England",
"country_code": "GB",
"country": "United Kingdom",
"bounding_box": {
"type": "polygon",
"coordinates": [
[
[
0.773779,
51.96971
],
[
0.773779,
51.976437
],
[
0.781794,
51.976437
],
[
0.781794,
51.96971
],
[
0.773779,
51.96971
]
]
]
},
"attributes": {
}
},
If anyone can identify the reason for this and correct it, I would be most appreciative.
NOTE 1:: I tried using the mapping and document examples from Elastic's geo_shape documentation page and Kibana again showed the location field as unknown type.
PUT /testgeo
{
"mappings": {
"tweet": {
"_all": {
"enabled": false
},
"properties": {
"location": {
"type": "geo_shape",
"tree": "quadtree",
"precision": "1m"
}
}
}
}
}
POST /testgeo/tweet/1
{
"location" : {
"type" : "polygon",
"coordinates" : [
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
]
}
}
Turns out that Kibana simply does reflect the type for GeoShape's. When doing a geo query, however, Elasticsearch returns correct results.
For example:
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_shape": {
"place.bounding_box": {
"shape": {
"type": "polygon",
"coordinates": [
[
[
0.773779,
51.96971
],
[
0.773779,
51.976437
],
[
0.781794,
51.976437
],
[
0.781794,
51.96971
],
[
0.773779,
51.96971
]
]
]
},
"relation": "within"
}
}
}
}
}
}
Even though you seem to have found a solution to your problem I just wanted to say there is a fix now for this issue by using the coerce option in the mapping for geo_shape like so:
"properties": {
"bounding_box": {
"type": "geo_shape",
"tree": "quadtree",
"precision": "1m",
"coerce": true
}
}
Also see:
https://github.com/elastic/elasticsearch/pull/11161