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,
|
limit = 10,
|
||||||
sortBy = 'createdAt',
|
sortBy = 'createdAt',
|
||||||
sortOrder = 'desc',
|
sortOrder = 'desc',
|
||||||
|
filters,
|
||||||
} = dto;
|
} = dto;
|
||||||
|
|
||||||
const skip = (page - 1) * limit;
|
const skip = (page - 1) * limit;
|
||||||
@@ -179,6 +180,82 @@ export class AgentsService {
|
|||||||
where.agentTypeId = agentTypeId;
|
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
|
// Build orderBy
|
||||||
const orderBy: Prisma.AgentProfileOrderByWithRelationInput = {};
|
const orderBy: Prisma.AgentProfileOrderByWithRelationInput = {};
|
||||||
if (sortBy === 'averageRating') {
|
if (sortBy === 'averageRating') {
|
||||||
|
|||||||
@@ -72,4 +72,12 @@ export class SearchAgentsDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
sortOrder?: 'asc' | 'desc' = 'desc';
|
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