← back to Homesonspec
Fix broken search: when a query doesn't resolve to a location (city/state/builder name), fall back to a free-text filter (city/community/builder/state) instead of silently returning the entire national catalog
ef19ec03793b0e514f84ab7e230675a4b636b475 · 2026-07-22 22:19:24 -0700 · Steve
Files touched
M packages/search/src/index.ts
Diff
commit ef19ec03793b0e514f84ab7e230675a4b636b475
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jul 22 22:19:24 2026 -0700
Fix broken search: when a query doesn't resolve to a location (city/state/builder name), fall back to a free-text filter (city/community/builder/state) instead of silently returning the entire national catalog
---
packages/search/src/index.ts | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/packages/search/src/index.ts b/packages/search/src/index.ts
index d61f5c6..173891d 100644
--- a/packages/search/src/index.ts
+++ b/packages/search/src/index.ts
@@ -13,6 +13,7 @@ import { bboxAround, type BBox } from "@spechomes/shared";
export interface SearchParams {
q?: string;
+ textQuery?: string; // set when q doesn't resolve to a location — free-text filter fallback
bbox?: BBox; // explicit map-move bbox skips location resolution
priceMin?: number;
priceMax?: number;
@@ -71,6 +72,31 @@ export async function resolveLocation(q: string): Promise<ResolvedLocation | nul
};
}
+const STATE_NAME_TO_CODE: Record<string, string> = {
+ alabama: "AL", alaska: "AK", arizona: "AZ", arkansas: "AR", california: "CA", colorado: "CO",
+ connecticut: "CT", delaware: "DE", florida: "FL", georgia: "GA", hawaii: "HI", idaho: "ID",
+ illinois: "IL", indiana: "IN", iowa: "IA", kansas: "KS", kentucky: "KY", louisiana: "LA",
+ maine: "ME", maryland: "MD", massachusetts: "MA", michigan: "MI", minnesota: "MN", mississippi: "MS",
+ missouri: "MO", montana: "MT", nebraska: "NE", nevada: "NV", "new hampshire": "NH", "new jersey": "NJ",
+ "new mexico": "NM", "new york": "NY", "north carolina": "NC", "north dakota": "ND", ohio: "OH",
+ oklahoma: "OK", oregon: "OR", pennsylvania: "PA", "rhode island": "RI", "south carolina": "SC",
+ "south dakota": "SD", tennessee: "TN", texas: "TX", utah: "UT", vermont: "VT", virginia: "VA",
+ washington: "WA", "west virginia": "WV", wisconsin: "WI", wyoming: "WY",
+};
+
+/** A query that didn't resolve to a location → match it against city/state/community/builder. */
+function textQueryClause(q: string): Prisma.InventoryHomeWhereInput {
+ const t = q.trim();
+ const stateCode = STATE_NAME_TO_CODE[t.toLowerCase()] ?? (/^[A-Za-z]{2}$/.test(t) ? t.toUpperCase() : null);
+ const or: Prisma.InventoryHomeWhereInput[] = [
+ { city: { contains: t, mode: "insensitive" } },
+ { community: { name: { contains: t, mode: "insensitive" } } },
+ { builder: { name: { contains: t, mode: "insensitive" } } },
+ ...(stateCode ? [{ state: stateCode }] : []),
+ ];
+ return { AND: [{ OR: or }] };
+}
+
export function buildWhere(params: SearchParams, bbox?: BBox): Prisma.InventoryHomeWhereInput {
const now = new Date();
const moveInCutoff =
@@ -82,6 +108,7 @@ export function buildWhere(params: SearchParams, bbox?: BBox): Prisma.InventoryH
status: "PUBLISHED",
isDemo: false, // HARD RULE: demonstration/synthetic inventory NEVER surfaces on the live site
freshness: { not: "INACTIVE" as const }, // Stage-3: deduped/retired records never surface (defense-in-depth over status)
+ ...(params.textQuery ? textQueryClause(params.textQuery) : {}),
...(bbox
? {
lat: { gte: bbox.minLat, lte: bbox.maxLat },
@@ -155,6 +182,9 @@ export async function runSearch(params: SearchParams): Promise<SearchResult> {
if (!bbox && params.q) {
location = await resolveLocation(params.q);
bbox = location?.bbox;
+ // Query didn't resolve to a place → fall back to free-text so we never
+ // silently drop the query and return the entire national catalog.
+ if (!bbox) params = { ...params, textQuery: params.q };
}
const where = buildWhere(params, bbox);
@@ -215,7 +245,10 @@ export async function runSearch(params: SearchParams): Promise<SearchResult> {
*/
export async function runFacets(params: SearchParams) {
let bbox = params.bbox;
- if (!bbox && params.q) bbox = (await resolveLocation(params.q))?.bbox;
+ if (!bbox && params.q) {
+ bbox = (await resolveLocation(params.q))?.bbox;
+ if (!bbox) params = { ...params, textQuery: params.q };
+ }
const without = (key: keyof SearchParams): Prisma.InventoryHomeWhereInput =>
buildWhere({ ...params, [key]: undefined }, bbox);
← 8231981 Design review fix: dedupe home detail Data-provenance table
·
back to Homesonspec
·
Lennar adapter: LENNAR_STATE_FILTER for targeted per-state c c642140 →