Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return distance with MongoDB and Mongoose on geospatial search?

I'm using Mongoose 3.5.3 with Node.js 0.8.8 and I was surprised to see that distance from a given point is not being returned in the results. Is it possible to allow for distance to be returned? It must have it because my results seem to be ordered by distance as one would expect.

exports.nearBuilding = function (req, res, next) {
    var area = {
        center: [parseFloat(req.params.longitude), parseFloat(req.params.latitude)],
        radius: parseFloat(req.params.distance) / 3963.192 };

    var query = Building.find().where('coords').within.centerSphere(area);

    query.exec(function (error, docs) {
        var records = {'records': docs};
        if (error) {
            process.stderr.write(error);
            res.send(error, 500);
        }
        if (req.params.callback !== null) {
            res.contentType = 'application/javascript';
        }
        res.send(records);
        return next();
    });
};
like image 401
occasl Avatar asked Dec 09 '22 16:12

occasl


1 Answers

You can use the geoNear function which returns distance:

Building.collection.geoNear(longitude, latitude, {maxDistance: radius }, cb);

Here are the API options:

/**
 * Execute the geoNear command to search for items in the collection
 *
 * Options
 *  - **num** {Number}, max number of results to return.
 *  - **maxDistance** {Number}, include results up to maxDistance from the point.
 *  - **distanceMultiplier** {Number}, include a value to multiply the distances with allowing for range conversions.
 *  - **query** {Object}, filter the results by a query.
 *  - **spherical** {Boolean, default:false}, perform query using a spherical model.
 *  - **uniqueDocs** {Boolean, default:false}, the closest location in a document to the center of the search region will always be returned MongoDB > 2.X.
 *  - **includeLocs** {Boolean, default:false}, include the location data fields in the top level of the results MongoDB > 2.X.
 *  - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST).
 *
 * @param {Number} x point to search on the x axis, ensure the indexes are ordered in the same order.
 * @param {Number} y point to search on the y axis, ensure the indexes are ordered in the same order.
 * @param {Objects} [options] options for the map reduce job.
 * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the geoNear method or null if an error occured.
 * @return {null}
 * @api public

In your case you would do something like this:

       exports.nearBuilding = function (req, res, next) {

        var query = Building.collection.geoNear(parseFloat(req.params.longitude), parseFloat(req.params.latitude), { distance: parseFloat(req.params.distance) / 3963.192}, function (error, docs) {

            if (error) {
                process.stderr.write(error);
                res.send(error, 500);
            }
            if (req.params.callback !== null) {
                res.contentType = 'application/javascript';
            }
            // Docs are turned as an array of objects that contain distance (dis) and the object (obj). 
            // Let's concatenate that into something cleaner - i.e. the distance as part of object

            var results = []
            docs.forEach(function(doc) {
                doc.obj.distance = doc.dis;
                results.push(doc.obj);
            });
            var records = {'records': results};      

            res.send(records);
            return next();
        });
    };
like image 118
cyberwombat Avatar answered Jan 23 '23 04:01

cyberwombat