Geo-spatial radius query with multi-location docs, uniqueDocs (MongoDB 2.4)

670 views
Skip to first unread message

Marian Steinbach

unread,
Apr 8, 2013, 5:35:08 AM4/8/13
to mongod...@googlegroups.com
Hi!

I've been finding conflicting information on this.

I have documents with multiple "node" properties each and an index on "nodes.location". The location is a [lon, lat] tuple.

{
    _id: ...
    nodes: [
        {
            location: [7.2, 50.8],
            ...
        },
        {
            location: [7.3, 50.4],
            ...
        }
    ] 
}

With my query I want to get unique documents within a certain circle/radius around a lon/lat point. Which type of query do I have to use?

$near seems to have the problem that it cannot be combined with $uniqueDocuments = true and only returns a hard coded limit of 100 hits. And I also have the impression that $maxDistance doesn't have an effect. Or at least it's not using meters as a unit.

$geoWithin: I am trying this query:

db.locations.find({
'nodes.location': {
$geoWithin:
        {$center: [[7.2, 50.8], 1000]}
        }
    }
)

It seems as if this works in general, but the unit for the radius parameter is unclear. It's definitely not meters. I see the result getting smaller when I change it to some very small number like 0.1.

Also I'd like the result to be sorted by distance, but that's not a strong requirement. Additionally, I should note that I'm not talking about several hundrets of kilometers as a radius, but instead everything up to 1000 meters.

So what is the correct way to do a geospatial radius query with meters as a radius unit?

Thanks!

Marian

Derick Rethans

unread,
Apr 8, 2013, 6:07:52 AM4/8/13
to mongod...@googlegroups.com
On Mon, 8 Apr 2013, Marian Steinbach wrote:

> *$near* seems to have the problem that it cannot be combined with
> $uniqueDocuments = true and only returns a hard coded limit of 100
> hits. And I also have the impression that $maxDistance doesn't have an
> effect. Or at least it's not using meters as a unit.

Near should always only return unique docs.

> $geoWithin: I am trying this query:
>
> db.locations.find({
> 'nodes.location': {
> $geoWithin:
> {$center: [[7.2, 50.8], 1000]}
> }
> }
> )
>
> It seems as if this works in general, but the unit for the radius parameter
> is unclear. It's definitely not meters. I see the result getting smaller
> when I change it to some very small number like 0.1.

Providing you use MongoDB 2.4 and a "2dsphere" index, it's in meters. In
other cases it can be in radians, which you can convert that to KM by
dividing that by the radius of the Earth (6371) - or to meters, by
dividing by 6371000.

> Also I'd like the result to be sorted by distance, but that's not a
> strong requirement. Additionally, I should note that I'm not talking
> about several hundrets of kilometers as a radius, but instead
> everything up to 1000 meters.

$near should make order by distance by default. You're not quite saying
which MongoDB you use, and what index (types), so it's a bit hard to
give a more precise answer. I'd almost suggest you use something like
the following to do what you want with MongoDB 2.4 and the aggregation
framework:

maxDist = 500;
rEarth = 6371000;

res = db.poiConcat.aggregate( [ {
'$geoNear' : {
'near' : [ -0.122055, 51.514562 ],
'distanceField' : 'distance',
'distanceMultiplier' : rEarth,
'maxDistance' : maxDist / rEarth,
'spherical' : true,
'query' : {
'$or' : [
{ ts: 'amenity=pub' },
{ ts: 'amenity=bar' },
]
}
}
} ] )

Of course, you probably need to change the 'query'.

cheers,
Derick

--
{
website: [ "http://mongodb.org", "http://derickrethans.nl" ],
twitter: [ "@derickr", "@mongodb" ]
}

Marian Steinbach

unread,
Apr 8, 2013, 7:11:01 AM4/8/13
to mongod...@googlegroups.com
I am using MongoDb 2.4.1. 

I changed my index type from "2d" to "2dsphere". Unfortunately, I can't even look up the 2dsphere documentation. It's gone. http://docs.mongodb.org/manual/applications/2dsphere

The aggregate call you suggested, I adepted it to this, and it works fine!

maxDist = 1000;
rEarth = 6371000;
res = db.locations.aggregate( [ {
  '$geoNear' : {
    'near' : [6.9, 50.9],
    'distanceField' : 'distance',
    'distanceMultiplier' : rEarth,
    'maxDistance' : maxDist / rEarth,
    'spherical' : true
  }
} ] )

I wouldn't have tried aggregate for that by myself. Thanks!

I also adapted my first query.

db.locations.find({
'nodes.location': {
$geoWithin: {
$centerSphere: [[6.9, 50.9], 500]
}
}
})

here is what I get:

error: {
"$err" : "Spherical MaxDistance > PI. Are you sure you are using radians?",
"code" : 13461
}

-> Seems like in this case the radius is still radians, not meters.


Marian


Sam Millman

unread,
Apr 8, 2013, 7:38:29 AM4/8/13
to mongod...@googlegroups.com
The docs are here now: http://docs.mongodb.org/manual/core/2dsphere/ not sure why that page doesn't rewrite actually.


--
--
You received this message because you are subscribed to the Google
Groups "mongodb-user" group.
To post to this group, send email to mongod...@googlegroups.com
To unsubscribe from this group, send email to
mongodb-user...@googlegroups.com
See also the IRC channel -- freenode.net#mongodb
 
---
You received this message because you are subscribed to the Google Groups "mongodb-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mongodb-user...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Derick Rethans

unread,
Apr 8, 2013, 9:27:47 AM4/8/13
to mongod...@googlegroups.com
On Mon, 8 Apr 2013, Marian Steinbach wrote:

> I also adapted my first query.
>
> db.locations.find({
> 'nodes.location': {
> $geoWithin: {
> $centerSphere: [[6.9, 50.9], 500]
> }
> }
> })
>
> here is what I get:
>
> error: {
> "$err" : "Spherical MaxDistance > PI. Are you sure you are using radians?",
> "code" : 13461
> }
>
> -> Seems like in this case the radius is still radians, not meters.

The units depend on what the point is. If it's a GeoJSON point with the
default datum, the units are meters. If the point is an "old style"
point the units are in radians. The old style refers to a [lon, lat]
array, and GeoJSON to:
http://docs.mongodb.org/manual/core/2dsphere/#store-geojson-objects

cheers,
Derick

Marian Steinbach

unread,
Apr 8, 2013, 10:16:12 AM4/8/13
to mongod...@googlegroups.com
2013/4/8 Derick Rethans <der...@10gen.com>

...
The units depend on what the point is.  If it's a GeoJSON point with the
default datum, the units are meters.  If the point is an "old style"
point the units are in radians. The old style refers to a [lon, lat]
array, and GeoJSON to:
http://docs.mongodb.org/manual/core/2dsphere/#store-geojson-objects

...

Wow! This is against my intuition. If that's in the documentation, at least I didn't see it. Thank you very much!

Marian

Marian Steinbach

unread,
Apr 8, 2013, 10:40:53 AM4/8/13
to mongod...@googlegroups.com, mar...@sendung.de
Here is another gotcha. I adapted my import script (pymongo) to create GeoJSON Point entries. Now I get strange issues.

This is the representation of a dict my script wants to save to MongoDB:

{
    'name': u'L\xfctticher Stra\xdfe',
    'nodes': [{
        'location': {
            'coordinates': [6.9325423, 50.9374484],
            'type': 'Point'},
        'osmid': 21063145}],
    ...
}

I get the error

pymongo.errors.OperationFailure: location object expected, location array not in correct format

Can it be that MongoDB expects a certain attribute order (type first, coordinates second)? That wouldn't be the first time. Here is another one along these lines: https://jira.mongodb.org/browse/SERVER-1990

Unfortunately, Python doesn't maintain attribute order in dicts.

Or is anything else wrong?

Marian

Derick Rethans

unread,
Apr 8, 2013, 10:46:18 AM4/8/13
to mongod...@googlegroups.com, mar...@sendung.de
On Mon, 8 Apr 2013, Marian Steinbach wrote:

> Here is another gotcha. I adapted my import script (pymongo) to create
> GeoJSON Point entries. Now I get strange issues.
>
> This is the representation of a dict my script wants to save to MongoDB:
>
> {
> 'name': u'L\xfctticher Stra\xdfe',
> 'nodes': [{
> 'location': {
> 'coordinates': [6.9325423, 50.9374484],
> 'type': 'Point'},
> 'osmid': 21063145}],
> ...
> }
>
> I get the error
>
> pymongo.errors.OperationFailure: location object expected, location array
> not in correct format

What have you set indexes on? Also, are you importing OpenStreetMap
data? If so, i've a whole set of scripts and some other tools for that
already - drop me a line for that.

> Can it be that MongoDB expects a certain attribute order (type first,
> coordinates second)? That wouldn't be the first time. Here is another
> one along these lines: https://jira.mongodb.org/browse/SERVER-1990

It shouldn't do that. I've cc-ed Hari, who might know.

Marian Steinbach

unread,
Apr 8, 2013, 11:03:42 AM4/8/13
to mongod...@googlegroups.com, mar...@sendung.de
Am Montag, 8. April 2013 16:46:18 UTC+2 schrieb Derick Rethans:
On Mon, 8 Apr 2013, Marian Steinbach wrote:

What have you set indexes on?

Good question. I just found I still have the 2d index around:

[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "scrapearis.locations",
"name" : "_id_"
},
{
"v" : 1,
"key" : {
"osmid" : 1
},
"unique" : true,
"ns" : "scrapearis.locations",
"name" : "osmid_1"
},
{
"v" : 1,
"key" : {
"nodes.location" : "2d"
},
"ns" : "scrapearis.locations",
"name" : "nodes.location_2d"
},
{
"v" : 1,
"key" : {
"name" : 1
},
"ns" : "scrapearis.locations",
"name" : "name_1"
},
{
"v" : 1,
"key" : {
"nodes.location" : "2dsphere"
},
"ns" : "scrapearis.locations",
"name" : "nodes.location_2dsphere"
}
]

This reminds me that indexes are not deleted when db.collection.remove() is called. :(

So I deleted the old 2d index now and the import works fine :)

I still don't get the right distances and radius limitations though.
Reply all
Reply to author
Forward
0 new messages