Scenario
I was recently working on a problem where I needed to query messages based on a certain physical location.
To achieve that, a Haversine formula implementation is used to calculate the distance between two points using its latitude and longitude. However, we can solve this problem by query a schema using geospatial's query feature from Mongoose.
Approach
First, we need to declare a MessageSchema and what field should be used as 2dSphere. Because I'm using TypeScript with Node, I have to define a document type in order to instantiate the model properly.
// message.model.ts
import mongoose from "mongoose";
export type MessageDocument = mongoose.Document & {
name: string;
text: string;
location: {
type: { type: String };
coordinates: Array;
};
};
const MessageSchema = new mongoose.Schema(
{
name: String,
text: String,
location: {
type: { type: String },
coordinates: Array,
},
},
{
timestamps: {},
}
);
MessageSchema.index({ location: "2dsphere" });
export default mongoose.model<MessageDocument>("Message", MessageSchema);Once the model is defined, we can then query Message objects by latitude and longitude via the $maxDistance and $geometry MongoDB operators, passing the longitude and latitude as method parameters to the service:
// message.service.ts
import Message from "../schemas/MessageSchema";
interface IRequest {
latt: number;
long: number;
}
class MessageService {
private messageSchema;
constructor(private messageSchema) {}
async run({ latt, long }:IRequest): Promise<Message[]> {
const messages = await this.messageSchema.find({
location: {
$near: {
$maxDistance: 15000,
$geometry: {
type: "Point",
coordinates: [long, latt],
},
},
},
});
return messages;
}
}Now, MessageService is able to query messages by latitude and longitude instead of doing error-prone and compute-intensive math with the Haversine formula.
Takeaways
My takeaway from this is: don't re-invent the wheels. Use tools that are going to make you solve problems faster.