Mongo compound index with geo location not working

1.5k views Asked by At

I am having hard time with something that supposed to be trivial....
I have the following profile document structure:

 {
   pid:"profileId",
   loc : {
       "lat" : 32.082156661684621,
       "lon" : 34.813229013156551,
       "locTime" : NumberLong(0)
         }
   age:29
 }

A common use-case in my app is to retrieve nearby profiles filtered by age.

   { "loc" : { "$near" : [ 32.08290052711715 , 34.80888522811172] , "$maxDistance" :    179.98560115190784}, "age" : { "$gte" : 0 , "$lte" : 33}}

So I have created the following compound index:

  { 'loc':2d , age:1}

And no matter what I do I can't make the query run with the created index (also tried with hint)
this is the generated explain for the query:

  { 
  "cursor" : "GeoSearchCursor" , 
  "isMultiKey" : false , 
  "n" : 4 , 
  "nscannedObjects" : 4 , 
  "nscanned" : 4 , 
  "nscannedObjectsAllPlans" : 4 , 
  "nscannedAllPlans" : 4 , 
  "scanAndOrder" : false , 
  "indexOnly" : false , 
  "nYields" : 0 , 
  "nChunkSkips" : 0 , 
  "millis" : 0 , 
  "indexBounds" : { } ,
  "allPlans" : [ { "cursor" : "GeoSearchCursor" , "n" : 4 , "nscannedObjects" : 4 , "nscanned" :    4 , "indexBounds" : { }
  }

I am using mongodb version 2.4.4.

What am I doing wrong? your answer is highly appreciated.

1

There are 1 answers

2
Ger Hartnett On

The explain output says "cursor" : "GeoSearchCursor". This indicates your query used a geospatial index.

See the following for details: http://docs.mongodb.org/manual/reference/method/cursor.explain/

2d indexes support a compound index with only one additional field, as a suffix of the 2d index field. http://docs.mongodb.org/manual/applications/geospatial-indexes

As @stennie mentioned in the comment on your question the problem might be the ordering of the coordinates. They should be ordered long, lat. If that doesn't work try storing the loc as an array with long first element, lat second.

Here is a worked example:

I created three profile objects with location as array and the locTime separate from loc.

> db.profile.find()
{ "_id" : ObjectId("52cd54f1c43bb3a468b9fd0d"), "loc" : [  -6,  50 ], "age" : 29, "pid" : "001", "locTime" : NumberLong(0) }
{ "_id" : ObjectId("52cd5507c43bb3a468b9fd0f"), "loc" : [  -6,  53 ], "age" : 30, "pid" : "002", "locTime" : NumberLong(1) }
{ "_id" : ObjectId("52cd5515c43bb3a468b9fd10"), "loc" : [  -1,  51 ], "age" : 31, "pid" : "003", "loctime" : NumberLong(2) }

Finding using large distance and age

> db.profile.find({ "loc" : { "$near" : [ -1, 50] , "$maxDistance" : 5}, "age" : { "$gte" : 0 , "$lte" : 33}})
{ "_id" : ObjectId("52cd5515c43bb3a468b9fd10"), "loc" : [  -1,  51 ], "age" : 31, "pid" : "003", "loctime" : NumberLong(2) }
{ "_id" : ObjectId("52cd54f1c43bb3a468b9fd0d"), "loc" : [  -6,  50 ], "age" : 29, "pid" : "001", "locTime" : NumberLong(0) }

The explain shows the index is being used:

> db.profile.find({ "loc" : { "$near" : [ -1, 50] , "$maxDistance" : 5}, "age" : { "$gte" : 0 , "$lte" : 33}}).explain()
{
    "cursor" : "GeoSearchCursor",
    "isMultiKey" : false,
    "n" : 2,
    "nscannedObjects" : 2,
    "nscanned" : 2,
    "nscannedObjectsAllPlans" : 2,
    "nscannedAllPlans" : 2,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {

    },
}

Narrow the distance with the same age range

> db.profile.find({ "loc" : { "$near" : [ -1, 50] , "$maxDistance" : 1}, "age" : { "$gte" : 0 , "$lte" : 33}})

Here is the explain, again the index is used:

> db.profile.find({ "loc" : { "$near" : [ -1, 50] , "$maxDistance" :     1}, "age" : { "$gte" : 0 , "$lte" : 33}}).explain()
{
    "cursor" : "GeoSearchCursor",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1,
    "nscanned" : 1,
    "nscannedObjectsAllPlans" : 1,
    "nscannedAllPlans" : 1,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {

    },
}

Here are the indexes:

> db.profile.getIndices()
[
    {
        "v" : 1,
        "key" : {
            "_id" : 1
        },
        "ns" : "test.profile",
        "name" : "_id_"
    },
    {
        "v" : 1,
        "key" : {
            "loc" : "2d",
            "age" : 1
        },
        "ns" : "test.profile",
        "name" : "loc_2d_age_1"
    }
]