Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: geoNear command. #55

Closed
RdlP opened this issue Oct 14, 2013 · 14 comments
Closed

Question: geoNear command. #55

RdlP opened this issue Oct 14, 2013 · 14 comments

Comments

@RdlP
Copy link

RdlP commented Oct 14, 2013

Hello, mongodb has a command called geoNear (http://docs.mongodb.org/manual/reference/command/geoNear/#dbcmd.geoNear). How can I run this command with this project?

@jenssegers
Copy link
Contributor

There is no command support at this moment, but you can get the original MongoDB object with the following method:

$mongodb = DB::getMongoDB();

@RdlP
Copy link
Author

RdlP commented Oct 15, 2013

Ok, thanks very much.

A last question, what's object return DB::getMongoDB()? MongoClient MongoDB MongoCollection?

@jenssegers
Copy link
Contributor

It returns the MongoDB object. getMongoClient() returns the MongoClient object.

https://github.com/jenssegers/Laravel-MongoDB#raw-expressions

@duro
Copy link

duro commented Nov 19, 2013

@RdlP: Any chance you got this to work and could provide an example of how you ran a geoNear command?

@RdlP
Copy link
Author

RdlP commented Nov 19, 2013

First yo got a mongodb instance

$mongodb = DB::getMongoDB();

and then you run the command.

$r = $mongodb->command(array( 'geoNear' => "tiendas", // Put here the collection 'near' => array( 'type' => "Point", 'coordinates' => array(doubleval($lng), doubleval($lat))), 'spherical' => true, 'maxDistance' => 100, ));

Finally in $r you obtain the result.

I hope that this help you.

@duro
Copy link

duro commented Nov 19, 2013

Thanks! This is perfect.

@mikebronner
Copy link

RdIP, duro, thanks for your examples above. I have a similar situation, however, I want to get the distance between two points in an already complex query.

db.stores.aggregate([{$geoNear: {near: [-118.09771, 33.89244], "distanceField": "distance", "maxDistance":100, "spherical": true}}])

The above query works great stand-alone, but how can I integrate this into a query like this:

        $stores = Store::with('employees')
            ->where('_items', 'elemMatch', ['value' => ['$gte' => $minValue, '$lte' => $maxValue]])
            ->whereType('Bookstore')
            ->whereIn('status', ['Closed', 'Open', 'Active', 'Planned'])
            ->orderBy('_items.title')
            ->aggregate('$geoNear', ['near' => [-118.09771, 33.89244], "distanceField" => "distance", "spherical" => true])
            ->get();

I tried to add the aggregate as I thought it might work, but I get an error, (Array to string conversion) which indicates that I am using it wrong.

Any suggestions?

@RdlP
Copy link
Author

RdlP commented Jul 29, 2014

Can you post the exactly error?.

Month ago I don't use this project but maybe I could help you.

@mikebronner
Copy link

Hi RdIP, thanks for your response. That is the exact error. I believe that I am using aggregate() incorrectly. The signature for the aggregate function is
aggregate($function, array $colums)
I'm guessing that the location array for 'near' is causing the issue.

My goal is to determine the distance of each item in the query result to a given set of coordinates. How would you best do that?

@mikebronner
Copy link

Making some progress -- I think I have a query that works in mongo, now to see if it can be rewritten in Moloquent?

db.stores.aggregate([
{$geoNear: {near: [-118.09771, 33.89244], distanceField: "distance", spherical: true}},
{"$match":
    {"_books":{"$elemMatch":{"soldDate":{"$gte":<date>,"$lte":<date>}}}}}], [])

Any ideas how to write this using Laravel-MongoDB? :)
Or also how would I write this as a raw query and get it into my models?

@jmshelby
Copy link

Since $geoNear is a command, and not an operator, I haven't found a built in way yet to get it into the models easily (since raw queries don't accept commands, it's a different type of call).

However, it shouldn't be too hard to add some way of combining the command calling functionality of Mongo, with this ORM, would just have to figure out a way to handle aggregates, and where to put the additional diagnostic information that comes with a mongo command call (like distance from the near point).

Something like this:

        $point = array(
            'type' => "Point",
            'coordinates' => array($long, $lat),
        );

        $db = \DB::getMongoDB();

        $r = $db->command(array(
            'geoNear' => 'stores',
            'near' => $point,
            'spherical' => true,
        ));

        $stores = Store::hydrate( some_function_to_pull_out_records($r) );
        $stores->some_function_to_add_data( some_function_to_pull_out_diagostic_data($r) );

You could possibly add this as an abstract function to your base Eloquent model (or a php trait to be used on your Geo Centric Model).

Or if you want to be really cool, then you can build this functionality into the query builder classes. In the end you just need to make sure $db->command() is being called, instead of collection->find() (or aggregate(), or distinct())

@mikebronner
Copy link

Wow, heavy stuff. Thanks for your thoughts ... will think this over. Right now I'm making progress using the raw aggregate queries returning array results -- focus is on getting it working first, improving it is secondary. :)

@madmanlear
Copy link

Has anyone gotten this working cleanly? Iterating through an array doesn't really seem sustainable.

@khoatran
Copy link

Hi Mike,

I think firstly you should make the test of the geo query on a flat collection - then the next step for aggregate later.

In my case, I have a collection called poi (Point of Interest). Inside this collection, there is a field called 'geo_point'. To make it works, I do below steps:

1/ Do the 2dsphere index for the field 'geo_point'. To do that with this package, I use below command:

Schema::collection('poi', function ($collection) {
                $collection->index(['geo_point' => '2dsphere']);
            });

2/ When doing the query (eg: query poi locations that nears to a specific geo point), I just use the below code:

       /** @var \Jenssegers\Mongodb\Query\Builder $queryBuilder */
        $queryBuilder = DB::collection('poi');

        $result = $queryBuilder->where('geo_point', 'near', [
            '$geometry' =>  [
                'type' => 'Point',
                'coordinates' => [floatval(106.637056), floatval(10.825903)]
            ],
            '$maxDistance' => 10000,
        ])->get();

If you want to use query raw, then:

$result = $queryBuilder->whereRaw([
                    'geo_point'=> [
                        '$near' => [
                            '$geometry' =>  [
                                'type' => 'Point',
                                'coordinates' => [floatval(106.637056), floatval(10.825903)]
                            ],
                            '$maxDistance' => 10000,
                        ]
                    ]
                ]
        )->get();

Also, when you insert the data to the geo location field which is indexed, you must follow the specification of MongoDB. If we do the indexing by 2dsphere, the location data must be a geometry point.

I made a function to help me convenient to create the location data format as below:

public static function createLocationData($lat, $lng) {
        return [
            'type' => 'Point',
            'coordinates' => ['lng' => floatval($lng), 'lat' => floatval($lat)]
        ];
    }

If you follow above steps and can make it works with the example for the flat structure, I think it's easier for you to debug and check on the aggregate function later.

Hope this help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants