Creating a Geospatial Index on MongoDB - mongodb

I am using the Geospatial Feature of MongoDB and my typical document looks like:
{
"_id" : ObjectId("574ebe5f9985eb31a9bdbe39"),
"geoareaName" : "GEOAREA2",
"loc" : {
"type" : "Polygon",
"coordinates" : [
[
[
13.870663396091308,
3.481292724609375
],
[
13.875996314658366,
3.482236862182617
],
[
13.872746581997381,
3.484210968017578
],
[
13.871829982503106,
3.484961986541748
],
[
13.870663396091308,
3.481292724609375
]
]
]
},
"color" : "#ff0000",
"_version" : 4,
"active" : false
}
and then there are some point locations which I query using the $geoIntersects keyword and it works like a charm. However, to optimally use the geospatial feature of mongo I want to put indexes around the location.
Can someone please suggest what should be the correct approach for that.

Create a 2dsphere index on loc.
db.collection.createIndex( { loc : "2dsphere" } )
It's pretty basic but it works great!

Related

How do I query a GeometryCollection in Mongodb?

I am trying to run a mongoldb query on a GEOjson collection of points and polygons but I'm struggling. I am new to GEOjson and Mongodb and haven't been able to find any examples on how to do this via google search and S.O. search so I'm hoping someone can shed some light on my issue.
I have created a 2DSphere index on geometry.coordinates - not sure if that's correct or not.
My overall objective is to return all points within the current map view and add both the point and the polygon associated to the map. I am using Leaflet.js and understand how to add to the map, but I can't figure out the query on Mongodb to get the results.
Just looking to find out if my GEOjson is correct and maybe an example on how I would perform the search on my GeometryCollection and get the point using $geowithin and then get the associated polygon.
Here's an example of my GEOjson-
{
"_id" : ObjectId("5709722f4044ccc4b8291651"),
"type" : "Feature",
"geometry" : {
"type" : "GeometryCollection",
"geometries" : [
{
"type" : "Point",
"coordinates" : [
-105.960805584616,
39.4671896545181
]
},
{
"type" : "Polygon",
"coordinates" : [
[
[
-105.96110187372227,
39.463449804295806
],
[
-105.96602244670311,
39.462956063632134
],
[
-105.96577627486317,
39.46671592928652
],
[
-105.96553007974012,
39.470475771657675
],
[
-105.96047434857059,
39.47089761421853
],
[
-105.95541854755197,
39.471319247231634
],
[
-105.95579990086372,
39.46763141937862
],
[
-105.95618125417559,
39.463943568242485
],
[
-105.96110187372227,
39.463449804295806
]
]
]
}
]
},
"properties" : {
"processed" : "Yes"
}
}

Is there a way to query an embedded document in an embedded document?

I have a weird mongodb document, but still need to query on it. Is it possible?
For example: I need every player within a certain radius.
{
"_id" : ObjectId("55d89c63c746230c200c528e"),
"speler_id" : 12,
"naam" : "Arjen Robben",
"seconds" : [
[
{
"locatie" : [
8.7173307286181370,
33.2784843816214250
],
"timestamp" : ISODate("1970-01-01T19:00:01.000Z")
},
{
"locatie" : [
-45.8853075448968970,
138.1526615469845800
],
"timestamp" : ISODate("1970-01-01T19:00:02.000Z")
},
{
"locatie" : [
80.5503710377444690,
10.0500048843973580
],
"timestamp" : ISODate("1970-01-01T19:00:03.000Z")
}
]
]
}
Well you can always use $geoWithin with $center or $centerSphere ( depending on whether these are global geometry coordinates or just a flat plane, for distance caluation purposes ) after processing with $unwind in the aggregation framework:
db.collection.aggregate([
{ "$unwind": "$seconds" },
{ "$unwind": "$seconds" },
{ "$match": {
"seconds.locatie": {
"$geoWithin": {
"$centerSphere": [
[
8.7173307286181370,
33.2784843816214250
],
100
]
}
}
}}
])
Which on the presented data would return:
{
"_id" : ObjectId("55d89c63c746230c200c528e"),
"speler_id" : 12,
"naam" : "Arjen Robben",
"seconds" : {
"locatie" : [
8.717330728618137,
33.278484381621425
],
"timestamp" : ISODate("1970-01-01T19:00:01Z")
}
}
{
"_id" : ObjectId("55d89c63c746230c200c528e"),
"speler_id" : 12,
"naam" : "Arjen Robben",
"seconds" : {
"locatie" : [
80.55037103774447,
10.050004884397358
],
"timestamp" : ISODate("1970-01-01T19:00:03Z")
}
}
Since $geoWithin does not "require" a geospatial index, then this is fine to use at later aggregation stages than the initial match. The $centerSphere in this case defines a point to query from and the "radius" extending from that point. This is just really a geometery "shortcut" as you can alternately provdide a GeoJSON polygon of your own definition.
But it's not really great. And mostly because it will not be able to use an index and therefore is pretty much brute force over the whole collection, and in that you cannot do nice things like return the distance from the queried point, like you can do with $geoNear.
Therefore while you can do things like this, most geoSpatial queries with MongoDB are best left to keeping that location data at the top level of the document, rather than embedded within arrays. So such modelling usually means having separate collection objects rather than embedded ones for the best results.
If you want an aggregated array in your response, then it is better to do this in aggregation after the intial geospatial query is made.

determine which predefined graphical region a user is in using mongodb

using mongo, I would like to compare a user's geo coordinates to a collection of documents each representing a defined geographical boundary. This is what i have thought to do, but cant get working. Other suggestions to get the same result are also welcome.
A document from the collection looks like this (those are manhattan coordinates, if you draw a line in sequence from one coordinate to the next, it wraps manhattan)
{
"__v" : 1,
"_id" : ObjectId("53220803a74a16da9c729202"),
"name" : "Manhattan",
"boundary" : {
"type" : "Polygon",
"coordinates" : [
[
[
-74.01237999999999,
40.69967
],
[
-73.97895,
40.71076
],
[
-73.97169,
40.73049
],
[
-73.92828,
40.79605
],
[
-73.95547999999999,
40.82635
],
[
-74.00901,
40.75431
],
[
-74.01961,
40.7059
]
]
]
}
}
i then use the following query to make my check
db.collection.find( { boundary :
{ $geoIntersects :
{ $geometry :
{ type : "Point" ,
coordinates : [ [user_long, user_lat] ]
} } } } ).exec(function(err, city){
console.log(err, "error");
res.send({agent_count : city});
});

$and with $nearSphere in mongodb

I have a collection having from and to point locations. Now I wish to find documents which have both, to and from locations nearby the given source and destinations.
Here's the setup:
collection: db.t2.find():
{
"_id" : ObjectId("5..4"),
"uid" : "sdrr",
"valid_upto": 122334,
"loc" : {
"from" : {
"type" : "Point",
"coordinates" : [ 77.206672, 28.543347 ]
},
"to" : {
"type" : "Point",
"coordinates" : [ 77.1997687, 28.5567278 ]
}
}
}
Indices: db.t2.getIndices():
{
"v" : 1,
"name" : "_id_",
"key" : {
"_id" : 1
},
"ns" : "mydb.t2"
},
{
"v" : 1,
"name" : "uid_1_loc.from_2dsphere_loc.to_2dsphere_valid_upto_1",
"key" : {
"uid" : 1,
"loc.from" : "2dsphere",
"loc.to" : "2dsphere",
"valid_upto" : 1
},
"ns" : "mydb.t2"
}
Single queries for either to or from work good with the current settings give nice results. However, when I use to and from together in a single query with $and clause:
db.t2.find({
"$and" : [
{
"loc.from" : {
"$nearSphere" : [ 77.5454589,28.4621213 ],
"$maxDistance" : 0.18
}
},
{
"loc.to" : {
"$nearSphere" : [ 77.206672, 28.543347 ],
"$maxDistance" : 0.18
}
}
]
})
it throws the following error:
error: {
"$err" : "can't find any special indices: 2d (needs index), 2dsphere (needs index), for: { $and: [ { loc.from: { $nearSphere: [ 77.5454589, 28.4621213 ], $maxDistance: 0.18 } }, { loc.to: { $nearSphere: [ 77.206672, 28.543347 ], $maxDistance: 0.18 } } ] }",
"code" : 13038
}
I suppose the data has been indexed as evident from getIndices(), but still its unable to find indices! Where is the problem then and how can I fix it to have effect of a $and-ed operation?
The error appears to be present from a MongoDB 2.4 version where there indeed was a bug that would not allow a $near type of query within and $and operation that accessed another field.
But your particular problem here is that you just cannot do this.
The code and comments to test this can be vied on GitHub but essentially:
// There can only be one NEAR. If there is a NEAR, it must be either the root or the root
// must be an AND and its child must be a NEAR.
size_t numGeoNear = countNodes(root, MatchExpression::GEO_NEAR);
if (numGeoNear > 1) {
return Status(ErrorCodes::BadValue, "Too many geoNear expressions");
}
So that is an error that would be emitted from MongoDB 2.6 you tried to do this.
A brief look at all the surrounding code within the method will show you that "geo" queries are not alone in this and the other "special" index type of "text" is included in the same rules.
Part of the reason for this is the $meta "scoring" that is required, as in this case is $maxDistance. There really is no valid way to combine or discern which value would actually apply in combined results such as this.
On a bit more of a technical note, the other issue is with being able to "intersect" indexes in a query such as this. The required fuzzy matching makes this a very different prospect to something like the basic "Btree" index intersection.
For now at least, your best approach is to perform each query by itself and manually "union/intersect" your results in code, with of course your own tagging as to which results are for your origin and which are for your destination.
This was a known issue in version 2.4 and prior of MongoDB, fixed in version 2.5.5:
https://jira.mongodb.org/browse/SERVER-4572
Core ServerSERVER-4572 Geospatial index cannot be used in $and
criteria of a query?
Should be fixed as of 2.6 - if you're running 2.4 or previous I'd upgrade, if you're running 2.6.X I'd report it as a bug.

mongodb 2.4.9 $geoWithin query on very simple dataset returning no results. Why?

Here is the output from my mongodb shell of a very simple example of a $geoWithin query. As you can see, I have only a single GeoJson Polygon in my collection, and each of its coordinates lies within the described $box. Furthermore, the GeoJson seems valid, as the 2dsphere index was created without error.
> db.Townships.find()
{ "_id" : ObjectId("5310f13c9f3a313af872530c"), "geometry" : { "type" : "Polygon", "coordinates" : [ [ [ -96.74084500000001, 36.99911500000002 ], [ -96.74975600000002, 36.99916100000001 ], [ -96.74953099999998, 36.99916000000002 ], [ -96.74084500000001, 36.99911500000002 ] ] ] }, "type" : "Feature" }
> db.Townships.ensureIndex( { "geometry" : "2dsphere"})
> db.Townships.find( { "geometry" : { $geoWithin : { "$box" : [[-97, 36], [-96, 37]] } } } ).count()
0
Thanks for any advice.
From documentation:
The $box operator specifies a rectangle for a geospatial $geoWithin query. The query returns documents that are within the bounds of the rectangle, according to their point-based location data. The $box operator returns documents based on grid coordinates and does not query for GeoJSON shapes.
If you insert this document...
db.Townships.insert(
{ "geometry" : [ -96.74084500000001, 36.99911500000002 ],
"type" : "Feature"
})
...your query will found it (but without index support).