MLS Search Parity · Lane A

Bring the Acquisto search experience to the new site

A step-by-step, reversible plan to reproduce the full acquistorealestate.com property-search bar — the transaction/status toggles, type tabs, location autocomplete, every quick-filter popover, and the four-tab Advanced Filters — on the PURE site, keeping the same layout and behavior, and reusing the search backend we already run.

Prepared by Lane A (Shattique) · 7/15/26 · sources: live site screenshots + client-dashboard-pure repo · target: pure-property-search.html + the appshell header PSB
01 Goal & scope 02 What we already have 03 Control inventory 04 Design & layout approach 05 Backend & data gaps 05b Legacy field map 06 Checkpoints 07 Risks & open questions

01Goal & scope

Match the live site's search UI and behavior on the PURE site, one reversible checkpoint at a time, without regressing the shell/global-header standard.

In scope

Guiding rule.

Reuse the existing PURE search backend and results page. This is mostly a UI-parity + filter-plumbing effort; the genuinely new work is the data fields several filters depend on. Keep the global header/rail standard intact — the header PSB is a shared component, changed only in pure-appshell.js.

02What we already have (reuse, don't rebuild)

The standalone pure-property-search.html is already strong; the header just hands off a ?tab=&q= URL to it.

CapabilityWhereStatus
Location autocomplete (addresses/cities/zips/subdivisions)mcp_search_suggestHave
Core search + paging + sortmcp_search_propertiesHave
Facets (ISD list, etc.)mcp_search_facetsHave
Gallery / Table / Map (Leaflet) views, detail modal, "Ask Poppy"results pageHave
Role-gating (public sees Active/Pending; pros see Sold/Off-Market)results pageHave
Header PSB chrome (Purchase/Lease, 4 type tabs, location chips, collapse)drawPSB() in pure-appshell.jsPartial

03Control inventory (from the screenshots)

Verified against the live listings table (1.52M rows) on 7/15/26.

Tags below reflect real columns + coverage, not assumptions. Top-half filters map to real typed columns now. The rest (garage, pool, waterfront, HVAC, stories, HOA detail, schools, 3D tour) are NOT absent — they live in listings.raw->'idx' + listing_meta and need promoting to indexed columns. Open House has its own open_houses table. Only the Agents/Brokerages directory is genuinely un-joined.

Have maps to an existing fieldNear field exists / small changeData needs ingestion first

Header bar

Purchase / Lease Have
Active / Sold Have
Residential / Commercial / Land / School Have
Location box: autocomplete + chips Near
Search My Location (geolocation + radius) Data
Reset Search Have
Listings updated N min ago Data
Search Have

Green pill popovers

Quick-filter popovers

Price Range (Min/Max + preset ladder + Any Price) Have
Beds & Baths (Any/1+…5+ + "use exact match") Near
MLS Number (text) Have
Number of Stories (Any/1/1+/2/2+) Data (stories 96% null; floor info trapped in levels)
Home Size Sq.Ft (Min/Max + presets) Have
Lot Size (Min/Max + acre presets) Have
Open House (Have Open House) Data

Advanced Filters — Property tab

Open House Data
Garage Spaces (Any/1+…5+) Data (no garage column)
ACQUISTO Listing (own-brokerage) Near (use org_id/source, not a featured flag)
Heating & Cooling Data
Must Have: Senior Community, 3D Tour Data
Days on ACQUISTO (8 DOM buckets) Near
Year Built (presets + Select Range) Near
Amenities: Pool, Waterfront Data

Advanced Filters — Neighborhood tab

HOA Type (None/Voluntary/Mandatory) Data
HOA Frequency (11 options) Data
HOA Annual Rate (Min/Max + presets) Data
HOA Name (text) Data
HOA Includes (14 chips) Data
Neighborhood Features (~35 chips) Data
Adult Community (Yes/No) Data

Advanced Filters — Schools & Brokerages/Agents tabs

04Design & layout approach

Keep the live site's layout; render it in PURE tokens so it still reads as PURE.

Placement decision (Shattique, 7/15/26) — supersedes CP1's in-page bar.

05Backend & data gaps

UI is cheap; these fields are the real work. Anything tagged Data above needs the column present + indexed and wired into mcp_search_properties/mcp_search_facets.

AreaNeedsOwner
RESO fields in raw.idx (present, not promoted)promote garage/pool/waterfront/heating/cooling/stories/HOA/schools from listings.raw->'idx' (or listing_meta) into indexed columns + add filter argsCode
Coverage / enrichmentRESO fields exist but are sparsely populated (e.g. pool/waterfront/senior); open-house is its own table open_houses (992 rows). Enrich feed for fuller coverageCode + feed
Freshness badgeexpose max(modified) so the bar can say "updated N ago"Code
Geolocation searchlat/lng + radius filter (PostGIS) behind Search-My-LocationCode
Agents/Brokerages tabagent/office directory joined to listings + contact search RPCCode
Save Searchsaved_searches table (owner, filter json, name) + save/list/delete, act-as awareCode + Lane A
DOM bucketsderive Coming Soon / Just Listed / Fresh … Super Stale from days-on-market thresholdsCode

05bLegacy field map (from acquisto-api-php)

Traced from get-search-properties.phpget_pro_by_search_parameter() / imp_build_school_quarry(). The legacy WordPress MLS table is esa_properties; each search control maps to a real column there. Kept here for future reference so ingestion targets are unambiguous.

Key finding (corrected 7/15).

The data was migrated — it lives in listings.raw->'idx' (full RESO field set: AttachedGarageYN, GarageSpaces, CarportSpaces, PoolFeatures, WaterfrontFeatures, WaterBodyName, Heating, AssociationYN/FeeFrequency/FeeIncludes, StoriesTotal, SeniorCommunityYN, VirtualTourURLBranded/Unbranded, school fields…) and in listing_meta (96.7M key/value rows). It was not promoted to typed, indexed top-level columns on the listings view. So every filter is queryable now via raw->'idx'->>'<ResoField>'; the real work is promote + index (extract keys into generated columns / expression indexes) plus per-field normalization — not sourcing data. Caveat: RESO coverage is partial (e.g. Heating ~50–78%, Pool/Waterfront sparse), so filters return fewer matches until the feed is enriched.

ControlLegacy paramesa_properties columnQuery patternIn PURE listings?
Statuss_statusmls_status codeIN (codes via get_p_status; public 19,24,106)text label, needs code map
Category (Resi/Comm/Land)p_typeproperty_type codeComm IN(10,11,12,68,70,73,96) · Land IN(26,27,28) · Resi = NOT IN thosetext, needs code map
Property Type popovers_sub_typeproperty_sub_type codeComm IN(34,53,62,75,76,78,79,82,87,88,89,97,99) · Land IN(52,91,103) · else Resitext, variant map
Prices_minprice / s_maxpricelist_price>= / <= (“Any Price” skips max)list_price
Beds (+ exact match)s_beds / s_exact_matchbedrooms_total= when exact, else >=beds
Baths (+ exact match)s_bathbathrooms_total= when exact, else >=baths
Number of Storiess_nstoriesnumber_of_stories text1→One · 1+→One and One Half · 2→Two · 2+→Three Or Morenot migrated (text lives in levels)
Home Size (Sq Ft)s_sqft_min / s_sqft_maxproperty_size>= / <=sqft
Lot Sizes_lotsize_min / s_lotsize_maxlot_area acres>= / <= (acres)lot_sqft is sqft, needs acre convert
MLS Numbers_mls_numberlisting_key=mls_number / listing_key
Days on ACQUISTOs_dayinacqcreated_atJust listed −2d · Super Fresh −7d · Fresh −30d · Regular −31..−90d · Stale ≤−91d · Super Stale ≤−180dlisted_at / created_at
Year Builts_ybuilt (+min/max)year_builtnewconstruction=curyear · 2010present ≥2010 · 2000present ≥2000 · before2000 ≤2000 · selectrange min/maxyear_built
Garage Spacess_parkinggarage_spaces>= Nin raw.idx / listing_meta — promote+index
Views (city/mtn/park/water)s_cityv / s_mountainv / s_parkv / s_watervproperty_viewLIKE '%city%' / '%mountain%' / '%park%' / '%lake|pond|river%'in raw.idx / listing_meta — promote+index
Pools_poolpool_private!= ''in raw.idx / listing_meta — promote+index
Spas_spaSpaYN!= ''in raw.idx / listing_meta — promote+index
Waterfronts_waterfrontwater_front!= ''in raw.idx / listing_meta — promote+index
Senior Communitys_communitySeniorCommunity!= ''in raw.idx / listing_meta — promote+index
Heatings_heatingHeatingYN!= ''in raw.idx / listing_meta — promote+index
Coolings_coolingCoolingYN!= ''in raw.idx / listing_meta — promote+index
Open Houses_openhouse / radioView=open_housesopen_house_status, open_house_type, open_house_end_timestatus='Active' AND type='Public' AND end_time > nowin raw.idx / listing_meta — promote+index
3D Tours_3dtourproperty_tour (/ VirtualTourURLBranded)!= ''in raw.idx / listing_meta — promote+index
ACQUISTO Listings_featuredfeatured= 1use org_id/source
School Districts_isdSchoolDistrict=school_district
Elementary / Middle / Highs_elementary_school / s_middle_school / s_high_schoolElementarySchool / MiddleSchool / HighSchool=in raw.idx / listing_meta — promote+index
HOA (type/freq/name/includes/features)hoa_types / hoa_frequencies / hoa_name / hoa_includes / hoa_featureAssociation* fields (mapping detail in search-function.php)— TBD from search-function.phpmissing — only association_name migrated

Notes. Source of truth: acquisto-api-php · acquistorealestate/allfunction/school-function.php (imp_build_school_quarry) + get-search-properties.php. Legacy stores mls_status/property_type/property_sub_type as numeric lookup codes (via get_p_status/get_p_type/get_p_sub_type in $alsetting); PURE's listings stores text labels, so migration must either bring the code columns or translate. The full HOA + neighborhood-feature SQL lives in the 663KB search-function.php and should be traced before building the Neighborhood tab.

06Checkpoints

One surface per turn, reversible, each deployed + logged before the next. Front-end-only CPs (1–4, 6) ship immediately against fields we have; data-dependent groups render disabled with a "pending data" note until CP8 lands.

1
Header bar parity — built & deployed on pure-property-search.html (Purchase/Lease + Active/Sold, 4 type tabs, location chips + mcp_search_suggest autocomplete, My location, Reset, Search, live freshness via mcp_listings_freshness). Status: DONE 7/15, but now to be RELOCATED per the 04 placement decision — the same controls move into the PURE appshell header search section and the in-page bar is removed (tracked as CP1b).
1b
Relocate search into the appshell header — move the CP1 bar (and the CP2/CP3 pill row: Status, Property Type, Price, Beds & Baths, MLS Number, Save Search) into the global header search section in pure-appshell.js, on all front-end pure-appshell pages (not admin); delete the standalone in-page omni bar; wire header ↔ results via shared search state.
2
Status & Property Type pills — the two green popovers with remove badges, wired to existing status + sub-type filters.
3
Quick-filter popovers — Price, Beds & Baths (+ exact match), MLS #, Stories, Home Size, Lot Size. All map to existing filters; Open House rendered disabled pending data.
4
Advanced Filters shell + Property tab — four-tab overlay; wire ACQUISTO-listing, garage, year-built presets, DOM buckets; stub Open House / HVAC / Must-Have / amenities with the pending-data flag.
5
Neighborhood tab — full HOA + features + adult-community UI; wire only what exists, rest disabled pending CP8.
6
Schools + Brokerages/Agents tabs — District/School selects from facets; Agents/Brokerages UI (side, sub-type, contact search) wired once the directory RPC exists.
7
Save Search + saved indicator — the green active-search controls with remove badges; needs saved_searches (act-as aware, per hats rules).
8
Backend & data — ingest/confirm the missing fields, add filter args to mcp_search_properties, facets, freshness timestamp, geolocation, DOM buckets. Largest; unlocks the disabled controls. Code
9
Appshell header PSB + QA — mirror the compact bar in the global header (single edit in pure-appshell.js), deep-link into results, full QA, register + park nothing.
Rollback.

Each CP is an additive commit to one file; the previous pure-property-search.html stays recoverable from git. No CP removes an existing capability. Data CP (8) is gated behind feature flags so disabled controls never break the bar.

07Risks & open questions


This is the plan of record for search parity. Recommended next step: a board epic (parent + CP1–9 children with effort tags) and a contract-first note for Code covering the field list, the extended mcp_search_properties signature, and saved_searches.

PURE MLS Technologies · Search Parity Implementation Plan · prepared by Lane A (Shattique) · 7/15/26