feat: rank professionals by completed services + score, search by name/profession/city
- GET /professionals now accepts ?search= instead of ?city=/page/limit - Filters each word across profession, user name, and user city (AND of OR) - Counts completed services per professional via Prisma _count filter - Sorts: most completed services first, then highest average_score - Always returns top 7 results Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
74be21eb4e
commit
00a6f637f6
@@ -15,10 +15,8 @@ export class ProfessionalsController {
|
||||
|
||||
@Get()
|
||||
findAllActive(@Req() req) {
|
||||
const page = +(req.query.page || 1);
|
||||
const limit = +(req.query.limit || 20);
|
||||
const city = req.query.city as string | undefined;
|
||||
return this.pros.findAllActive(page, limit, city);
|
||||
const search = req.query.search as string | undefined;
|
||||
return this.pros.findAllActive(search);
|
||||
}
|
||||
|
||||
@Get('pending')
|
||||
|
||||
@@ -20,25 +20,43 @@ export class ProfessionalsService {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
async findAllActive(page = 1, limit = 20, city?: string) {
|
||||
const skip = (page - 1) * limit;
|
||||
async findAllActive(search?: string) {
|
||||
const where: any = { is_active: true };
|
||||
if (city) where.users = { city: { contains: city, mode: 'insensitive' } };
|
||||
const [data, total] = await Promise.all([
|
||||
this.prisma.professionals.findMany({
|
||||
where,
|
||||
skip,
|
||||
take: limit,
|
||||
include: {
|
||||
users: { select: { id: true, name: true, email: true, phone: true, picture: true, city: true } },
|
||||
schedules: true,
|
||||
specializations: true,
|
||||
payment_methods: true,
|
||||
},
|
||||
}),
|
||||
this.prisma.professionals.count({ where }),
|
||||
]);
|
||||
return { data, meta: { total, page, limit, totalPages: Math.ceil(total / limit) || 1 } };
|
||||
if (search && search.trim()) {
|
||||
// Each word must match at least one of: profession, user name, or city
|
||||
const words = search.trim().split(/\s+/).filter(w => w.length > 1);
|
||||
if (words.length > 0) {
|
||||
where.AND = words.map(word => ({
|
||||
OR: [
|
||||
{ profession: { contains: word, mode: 'insensitive' } },
|
||||
{ users: { name: { contains: word, mode: 'insensitive' } } },
|
||||
{ users: { city: { contains: word, mode: 'insensitive' } } },
|
||||
],
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const professionals = await this.prisma.professionals.findMany({
|
||||
where,
|
||||
include: {
|
||||
users: { select: { id: true, name: true, email: true, phone: true, picture: true, city: true } },
|
||||
schedules: true,
|
||||
specializations: true,
|
||||
payment_methods: true,
|
||||
_count: { select: { services: { where: { status: 'completed' as any } } } },
|
||||
},
|
||||
});
|
||||
|
||||
// Sort: most completed services first, then by average score
|
||||
const sorted = professionals
|
||||
.sort((a, b) => {
|
||||
const diff = (b._count?.services ?? 0) - (a._count?.services ?? 0);
|
||||
if (diff !== 0) return diff;
|
||||
return Number(b.average_score ?? 0) - Number(a.average_score ?? 0);
|
||||
})
|
||||
.slice(0, 7);
|
||||
|
||||
return { data: sorted, meta: { total: sorted.length, page: 1, limit: 7, totalPages: 1 } };
|
||||
}
|
||||
|
||||
async findById(id: string) {
|
||||
|
||||
Reference in New Issue
Block a user