feat: Implement dynamic filtering for agent search based on profile fields.
This commit is contained in:
@@ -141,6 +141,7 @@ export class AgentsService {
|
||||
limit = 10,
|
||||
sortBy = 'createdAt',
|
||||
sortOrder = 'desc',
|
||||
filters,
|
||||
} = dto;
|
||||
|
||||
const skip = (page - 1) * limit;
|
||||
@@ -179,6 +180,82 @@ export class AgentsService {
|
||||
where.agentTypeId = agentTypeId;
|
||||
}
|
||||
|
||||
// Process dynamic profile field filters
|
||||
if (filters) {
|
||||
try {
|
||||
const parsedFilters: Record<string, string[]> = JSON.parse(filters);
|
||||
const filterSlugs = Object.keys(parsedFilters);
|
||||
|
||||
if (filterSlugs.length > 0) {
|
||||
// Get field IDs for the filter slugs
|
||||
const fields = await this.prisma.profileField.findMany({
|
||||
where: { slug: { in: filterSlugs } },
|
||||
select: { id: true, slug: true },
|
||||
});
|
||||
|
||||
const slugToFieldId = new Map(fields.map((f) => [f.slug, f.id]));
|
||||
|
||||
// For each filter, find agents that have ANY of the selected values
|
||||
// Use AND logic between different fields
|
||||
const filterConditions: string[][] = [];
|
||||
|
||||
for (const [slug, selectedValues] of Object.entries(parsedFilters)) {
|
||||
const fieldId = slugToFieldId.get(slug);
|
||||
if (!fieldId || !selectedValues || selectedValues.length === 0) continue;
|
||||
|
||||
// Find agentProfileIds where jsonValue contains ANY of the selected values
|
||||
const matchingValues = await this.prisma.agentProfileFieldValue.findMany({
|
||||
where: {
|
||||
fieldId,
|
||||
// jsonValue contains any of the selected values (for MULTI_SELECT, CHECKBOX_GROUP)
|
||||
// We need to check if the jsonValue array contains any of the selected values
|
||||
},
|
||||
select: {
|
||||
agentProfileId: true,
|
||||
jsonValue: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Filter in memory - check if jsonValue array contains any selected value
|
||||
const matchingAgentIds = matchingValues
|
||||
.filter((fv) => {
|
||||
const jsonVal = fv.jsonValue as string[] | null;
|
||||
if (!jsonVal || !Array.isArray(jsonVal)) return false;
|
||||
return selectedValues.some((sv) => jsonVal.includes(sv));
|
||||
})
|
||||
.map((fv) => fv.agentProfileId);
|
||||
|
||||
filterConditions.push(matchingAgentIds);
|
||||
}
|
||||
|
||||
// AND all filter conditions - find intersection of all matching agent IDs
|
||||
if (filterConditions.length > 0) {
|
||||
const intersection = filterConditions.reduce((acc, curr) => {
|
||||
const currSet = new Set(curr);
|
||||
return acc.filter((id) => currSet.has(id));
|
||||
});
|
||||
|
||||
// If no agents match all filters, return empty result
|
||||
if (intersection.length === 0) {
|
||||
return {
|
||||
data: [],
|
||||
meta: {
|
||||
total: 0,
|
||||
page,
|
||||
limit,
|
||||
totalPages: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
where.id = { in: intersection };
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Invalid JSON, ignore filters
|
||||
}
|
||||
}
|
||||
|
||||
// Build orderBy
|
||||
const orderBy: Prisma.AgentProfileOrderByWithRelationInput = {};
|
||||
if (sortBy === 'averageRating') {
|
||||
|
||||
@@ -72,4 +72,12 @@ export class SearchAgentsDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sortOrder?: 'asc' | 'desc' = 'desc';
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Dynamic profile field filters as JSON string (fieldSlug: [selectedValues])',
|
||||
example: '{"client_specialization":["first_time_buyers","investors"]}',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
filters?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user