Click Below to subscribe

How to Find Nearest Location Using Latitude and Longitude In Mongoose 2022

Sometimes you need to find the nearest location using latitude and longitude within a radius.

In this article, we gonna learn how to use mongoose orm to find nearby locations.

We need to use GeoJSON objects for geospatial queries.

Ref:- https://mongoosejs.com/docs/geojson.html

Point Schema:- The most simple structure in GeoJSON is a point. 

{
    "type" : "Point",
    "coordinates" : [-122.5, 37.7]
}

Note:- that longitude comes first in a GeoJSON coordinate array, not latitude.

models/user.model.js

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema(
    {
        name: String,
        status: {
            type: Boolean,
            default: true
        },
        location: {
            type: { 
                type: String, 
                enum: ['Point'] 
            },
            coordinates: { 
                type: [Number] 
            }
        }
    },
    {
        timestamps: true
    }
);

UserSchema.index({ location: "2dsphere" }); 

const User = mongoose.model("User", UserSchema);

module.exports = User;

Insert Dummy Users

await User.insertMany([
    {
        name: "User 1",
        location: {
            type: 'Point',
            coordinates: [79.821602, 28.626137]
        }
    },
    {
        name: "User 2",
        location: {
            type: 'Point',
            coordinates: [79.821091, 28.625484]
        }
    },
    {
        name: "User 3",
        location: {
            type: 'Point',
            coordinates: [79.817924, 28.625294]
        }
    },
    {
        name: "User 4",
        location: {
            type: 'Point',
            coordinates: [79.814636, 28.625181]
        }
    },
    {
        name: "User 5",
        location: {
            type: 'Point',
            coordinates: [79.810135, 28.625044]
        }
    },
    {
        name: "User 6",
        location: {
            type: 'Point',
            coordinates: [79.808296, 28.625019]
        }
    } 
]);

Here is an example for finding nearby 5 users within 1 km.

Example using aggregate()

const latitude = 28.626137;
const longitude = 79.821602;
const distance = 1;
const unitValue = 1000;

const users = await User.aggregate([
    {
        $geoNear: {
            near: {
                type: 'Point',
                coordinates: [longitude, latitude]
            },
            query: { 
                status: true 
            },
            maxDistance: distance * unitValue,
            distanceField: 'distance',
            distanceMultiplier: 1 / unitValue
        }
    },
    {
        $project: {
            _id: 1, 
            distance: 1
        }
    },
    {
        $sort: {
            distance: 1
        }
    },
    { $limit: 5 }
]);

Note:- Inside the query object you can pass your conditions. in the above example, I am searching only active users. you can also remove this query object.

If your collection has multiple 2d and/or multiple 2dsphere indexes, you must use the key option to specify the indexed field path to use.

$geoNear: {
    near: {
        type: 'Point',
        coordinates: [longitude, latitude]
    },
    query: { 
        status: true 
    },
    maxDistance: distance * unitValue,
    distanceField: 'distance',
    distanceMultiplier: 1 / unitValue,
    key: 'location'
}

Note:- In the case of miles just replace unitValue 1000 with 1609.3

Miles - 1609.3

Kilometer - 1000

you can also round the distance in query. 

{
    $project: {
        _id: 1, 
        distance: { 
            $round: ["$distance", 2] 
        }
    }
}

Example using static function

We can also make it reusable by defining this as a static function inside the model.

UserSchema.static('findByDistance', function(longitude, latitude, distance, unit = 'km') { 
    const unitValue = unit == "km" ? 1000 : 1609.3;
    return this.aggregate([
        {
            $geoNear: {
                near: {
                    type: 'Point',
                    coordinates: [longitude, latitude]
                },
                query: { status: true },
                maxDistance: distance * unitValue, 
                distanceField: 'distance',
                distanceMultiplier: 1 / unitValue
            }
        },
        {
            $project: {
                _id: 1, 
                distance: 1
            }
        },
        {
            $sort: {
                distance: 1
            }
        },
        { $limit: 5 }
    ]); 
});

Note:- I have passed km as the default unit. so you don't need to pass in case of kilometer calculation.

Now in the controller, you can call this function.

const users = await User.findByDistance(longitude, latitude, distance);

In the case of miles.

const users = await User.findByDistance(longitude, latitude, distance, 'm');

Example using find()

const latitude = 28.626137;
const longitude = 79.821602;
const distance = 1;
const unitValue = 1000;

const users = await User.find({
    location: {
        $near: {
            $maxDistance: distance * unitValue, // distance in meters
            $geometry: {
                type: 'Point',
                coordinates: [longitude, latitude]
            }
        }
    }
})
.select('_id')
return res.json(users)

Note:- $near by default sorts documents by distance.

Checkout my full mongoose-geo example.

https://github.com/ultimateakash/mongoose-geo

If you facing any issues. don't hesitate to comment below. I will be happy to help you.

Thanks.

Leave Your Comment