Click Below to subscribe

How to Find Nearest Location Using Latitude and Longitude In Objection.js 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 objection orm for finding nearby locations.

DB Structure - users table

models/connection.js

const Knex = require('knex');
const { Model } = require('objection');
const config = require('../config/database');

const knex = new Knex({
    client: 'mysql',
    connection: {
        host: config.host,
        user: config.username,
        password: config.password,
        database: config.database
    },
    useNullAsDefault: true
});

Model.knex(knex);

module.exports = { knex };

const { raw } = require('objection');
const User = require('../models/user.model');

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

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

const haversine = `(
    6371 * acos(
        cos(radians(${latitude}))
        * cos(radians(latitude))
        * cos(radians(longitude) - radians(${longitude}))
        + sin(radians(${latitude})) * sin(radians(latitude))
    )
)`;

const users = await User.query()
    .select(
        'id',
        raw(`${haversine} as distance`)
    )
    .where('status', 1)
    .orderBy('distance', 'ASC')
    .having('distance', '<=', distance)
    .limit(5); 

Note:- In case of miles just replace the 6371 with 3959 in the haversine formula.

Miles - 3959 

Kilometer6371

you can also round the distance in query. 

const users = await User.query()
    .select(
        'id',
        raw(`round(${haversine}, 2) as distance`)
    )
    .where('status', 1)
    .orderBy('distance', 'ASC')
    .having('distance', '<=', distance)
    .limit(5); 

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

models/user.model.js

const { Model } = require('objection');
const { raw } = require('objection');

class User extends Model {
    static get tableName() {
        return 'users';
    }

    static modifiers = { 
        filterDistance(query, latitude, longitude, distance, unit = 'km') {
            const constant = unit == 'km' ? 6371 : 3959;
            const haversine = `(
                ${constant} * acos(
                    cos(radians(${latitude}))
                    * cos(radians(latitude))
                    * cos(radians(longitude) - radians(${longitude}))
                    + sin(radians(${latitude})) * sin(radians(latitude))
                )
            )`;

            query.select(raw(`${haversine} as distance`))
                .having('distance', '<=', distance);
        }
    } 
}

module.exports = User;

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 scope.

const users = await User.query()
    .modify('filterDistance', latitude, longitude, distance)
    .select('id')
    .where('status', 1)
    .orderBy('distance', 'ASC') 
    .limit(5); 

In the case of miles.

const users = await User.query()
    .modify('filterDistance', latitude, longitude, distance, 'm')
    .select('id')
    .where('status', 1)
    .orderBy('distance', 'ASC') 
    .limit(5); 

Checkout my full objection-geo example.

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

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

Thanks.

Leave Your Comment