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
Kilometer - 6371
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