Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB query : $near with aggregation

I have a collection that looks like that

{
    "_class" : "User",
    "_id" : "id1",
    "places" : [
            {
                    "_id" : "1",
                    "address" : "test1",
                    "location" : {
                            "latitude" : 1,
                            "longitude" : 1
                    }
            },
            {
                    "_id" : "2",
                    "address" : "test2",
                    "location" : {
                            "latitude" : 2,
                            "longitude" : 2
                    }
            },...
     ]
}

I am trying to retrieve every place of a user (in a range of 2 km). This query doesn't work :

db.users.ensureIndex({"places.location":"2d"})

db.users.aggregate([ 
   {$match : { "_id" : "id1" } }, 
   {$unwind : "$places"}, 
   {$project:{_id:0, places:1},
   {$match :
      {"places.location" :
             { $near :
                 { $geometry :
                    { type : "2d" ,
                      coordinates : [ -1 , -2 ]
                    }
                 },
                 $maxDistance : 2000
              } 
      }
    }])



Error: Printing Stack Trace
at printStackTrace (src/mongo/shell/utils.js:37:15)
at DBCollection.aggregate (src/mongo/shell/collection.js:897:9)
at (shell):1:10
JavaScript execution failed: aggregate failed: {
    "errmsg" : "exception: $near is not allowed inside of a $match aggregation expression",
    "code" : 16424,
    "ok" : 0

I don't know how to use $near with aggregation.

When i try

db.users.find(
                  {
                    "places.location" : 
                        { $near:
                            { $geometry :
                                { type : "2d" ,
                                  coordinates : [ -1 , -2 ] } 
                                },
                                $maxDistance : 2000
                        }
                    }

It returns me n times the given user (n = each place of the user), this is why i have decided to use aggregation, but i'm stuck.

Thx in advance

EDIT

I tried $geoNear :

db.users.aggregate(                      
{$geoNear : {
    near: [-1, 1],
    distanceField: "distance",
    query : {"_id" : "id1"},
    uniqueDocs: true,
    maxDistance : 2000  // In the specs, distance has to be in radians 
}})

and it returns me the whole user (with all the places, even the far ones).

So I need to rework the document before I search for the correct places.

  1. Retrieve the correct user (match)
  2. Unwind every places of the user
  3. then search every place in a range of 2 km (near/geonear)
  4. reshape the result (project)

The result I am expecting is something like :

"result" : [
        {
                "_id" : "1",
                "location" : {
                        "latitude" : 1,
                        "longitude" : 1
                }
        },
        {
                "_id" : "2",
                "location" : {
                        "latitude" : 2,
                        "longitude" : 2
                }
        }
]}

The problem is that I can't use other stages before $geoNear, my search parameters have to be specified in the "query" field. I get the following error :

$geoNear is only allowed as the first pipeline stage
like image 215
isy Avatar asked Feb 08 '14 18:02

isy


People also ask

Can we use count with aggregate function in MongoDB?

The MongoDB $count operator allows us to pass a document to the next phase of the aggregation pipeline that contains a count of the documents. There a couple of important things to note about this syntax: First, we invoke the $count operator and then specify the string.

Is aggregate faster than find in MongoDB?

Because of this, if you have a simple aggregation pipeline or one which does not cut down the data volume much it can often be quicker to use a find() and perform the aggregation client side.

What does $match do in MongoDB?

The MongoDB $match operator filters the documents to pass only those documents that match the specified condition(s) to the next pipeline stage.


1 Answers

You will have to use $geoNear, and only if you are using V2.4 onward

db.users.aggregate(   
    {$geoNear : {
        near: [-1, -2],
        distanceField: "distance",
        query : {"_id" : "id1"},
        uniqueDocs: true,
        maxDistance : 2000
}})

Edit : after edit to question

The bellow query will give you location and distance, but doesn't retrieve the internal (array element) _id

db.users.aggregate(   
  {$geoNear : {
    near: [-1, -2],
    distanceField: "distance",
    includeLocs: "location",
    query : {"_id" : "id1"},
    maxDistance : 2000
  }},
  {$project : {"location" : 1, "_id" : 0, "distance" : 1}} 
)

Note the addition of includeLocs and elimination of uniqueDocs: true

To also retrieve the internal _id, you will have to (following this example) unwind and conditionally project or so, but I don't think it worth it, unless you need the address instead of the _id

like image 134
Ori Dar Avatar answered Jan 01 '23 18:01

Ori Dar