zkt25/sk1/backend/routes/services.js

91 lines
2.3 KiB
JavaScript

import express from "express";
import { User } from "../models/User.js";
const router = express.Router();
router.get("/:field", async (req, res) => {
const { field } = req.params;
const lat = parseFloat(req.query.lat);
const lon = parseFloat(req.query.lon);
if (isNaN(lat) || isNaN(lon)) {
return res.status(400).json({ error: "Must provide lat & lon" });
}
const rawRadius = parseFloat(req.query.radius);
const radiusKm = isNaN(rawRadius) || rawRadius < 0 ? 50 : rawRadius;
const maxDistance = radiusKm * 1000;
const page = Math.max(1, parseInt(req.query.page) || 1);
const limit = 6; // now 6 per page
const skip = (page - 1) * limit;
const sortBy = req.query.sortBy === "rate" ? "rate" : "distance";
const order = req.query.order === "desc" ? -1 : 1;
try {
// pipeline to get paged docs
const pipeline = [
{
$geoNear: {
near: { type: "Point", coordinates: [lon, lat] },
distanceField: "distanceMeters",
maxDistance,
spherical: true,
},
},
{
$match: {
"profile.field": field,
"profile.published": true,
},
},
{
$sort:
sortBy === "rate"
? { "profile.rate": order }
: { distanceMeters: order },
},
{ $skip: skip },
{ $limit: limit },
];
// pipeline to count total matching
const countPipeline = [
{
$geoNear: {
near: { type: "Point", coordinates: [lon, lat] },
distanceField: "distanceMeters",
maxDistance,
spherical: true,
},
},
{
$match: {
"profile.field": field,
"profile.published": true,
},
},
{ $count: "count" },
];
const [docs, countRes] = await Promise.all([
User.aggregate(pipeline),
User.aggregate(countPipeline),
]);
const total = countRes[0]?.count || 0;
const totalPages = Math.ceil(total / limit);
const pros = docs.map((u) => ({
_id: u._id,
profile: u.profile,
distanceKm: u.distanceMeters / 1000,
}));
return res.json({ pros, page, totalPages });
} catch (err) {
return res.status(500).json({ error: err.message });
}
});
export default router;