feat: implement radio field value expansion for ordinal filter matching in agent search
This commit is contained in:
@@ -457,6 +457,43 @@ export class AgentsService {
|
||||
where.agentTypeId = agentTypeId;
|
||||
}
|
||||
|
||||
// Pre-compute RADIO option expansion map for filter matching.
|
||||
// For ordinal RADIO fields (e.g. years_in_business), selecting "2+" should
|
||||
// also match "5+" and "10+" — all options at or above the selected index.
|
||||
let radioExpansionMap: Map<string, string[]> | null = null;
|
||||
if (filters) {
|
||||
try {
|
||||
const pf: Record<string, string[]> = JSON.parse(filters);
|
||||
const slugs = Object.keys(pf).filter(k => pf[k]?.length > 0);
|
||||
if (slugs.length > 0) {
|
||||
const radioFields = await this.prisma.profileField.findMany({
|
||||
where: { slug: { in: slugs }, fieldType: 'RADIO' },
|
||||
select: { slug: true, options: true },
|
||||
});
|
||||
radioExpansionMap = new Map();
|
||||
for (const rf of radioFields) {
|
||||
if (!Array.isArray(rf.options)) continue;
|
||||
const optValues = (rf.options as Array<{ value: string }>).map(o => String(o.value));
|
||||
radioExpansionMap.set(rf.slug, optValues);
|
||||
}
|
||||
}
|
||||
} catch { /* invalid JSON — ignore */ }
|
||||
}
|
||||
|
||||
const expandRadioValuesFromMap = (slug: string, selected: string[]): string[] => {
|
||||
if (!radioExpansionMap) return selected;
|
||||
const optValues = radioExpansionMap.get(slug);
|
||||
if (!optValues) return selected;
|
||||
const expanded = new Set(selected);
|
||||
for (const sv of selected) {
|
||||
const idx = optValues.indexOf(sv);
|
||||
if (idx >= 0) {
|
||||
for (let i = idx; i < optValues.length; i++) expanded.add(optValues[i]);
|
||||
}
|
||||
}
|
||||
return [...expanded];
|
||||
};
|
||||
|
||||
// Process dynamic profile field filters
|
||||
if (filters) {
|
||||
try {
|
||||
@@ -464,12 +501,14 @@ export class AgentsService {
|
||||
const filterSlugs = Object.keys(parsedFilters);
|
||||
|
||||
if (filterSlugs.length > 0) {
|
||||
// Get field IDs for the filter slugs
|
||||
// Get field IDs + type + options for the filter slugs
|
||||
const fields = await this.prisma.profileField.findMany({
|
||||
where: { slug: { in: filterSlugs } },
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
fieldType: true,
|
||||
options: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -485,9 +524,10 @@ export class AgentsService {
|
||||
// Use AND logic between different filter slugs, OR logic within same slug
|
||||
const filterConditions: string[][] = [];
|
||||
|
||||
for (const [slug, selectedValues] of Object.entries(parsedFilters)) {
|
||||
for (const [slug, rawSelectedValues] of Object.entries(parsedFilters)) {
|
||||
const slugFields = slugToFields.get(slug);
|
||||
if (!slugFields || slugFields.length === 0 || !selectedValues || selectedValues.length === 0) continue;
|
||||
if (!slugFields || slugFields.length === 0 || !rawSelectedValues || rawSelectedValues.length === 0) continue;
|
||||
const selectedValues = expandRadioValuesFromMap(slug, rawSelectedValues);
|
||||
|
||||
// Search across ALL fields with this slug (e.g. loan_type in both Professional and Lender sections)
|
||||
const allMatchingAgentIds: string[] = [];
|
||||
@@ -673,7 +713,9 @@ export class AgentsService {
|
||||
if (filterKeys.length > 0) {
|
||||
let matchedFilters = 0;
|
||||
for (const filterSlug of filterKeys) {
|
||||
const selectedValues = parsedFilters[filterSlug];
|
||||
// Expand RADIO filters so "2+" also matches "5+", "10+", etc.
|
||||
const rawSelected = parsedFilters[filterSlug];
|
||||
const selectedValues = expandRadioValuesFromMap(filterSlug, rawSelected);
|
||||
const agentFieldValues = (agent.fieldValues || []) as any[];
|
||||
const fieldMatch = agentFieldValues.some((fv: any) => {
|
||||
if (fv.field?.slug !== filterSlug) return false;
|
||||
|
||||
Reference in New Issue
Block a user