ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

地图搜索看起来随机排序,其实没读相关性信息

2026/8/22 12:20:44 拓冰建站 浏览量
地图搜索看起来随机排序,其实没读相关性信息 我第一次把地图文字搜索接到页面上时做完最显眼的部分就停了输入固定关键词点按钮拿到一串地点名称按接口返回的先后渲染出来。列表出现的那一刻我还挺满意。可同事看了一眼就问“第一条为什么排在前面”我答不上来。更准确地说我当时并没有证据说明它为什么在前面。我只看到了一个数组顺手把数组下标写成了#1、#2、#3。页面看起来像有排序用户却只能猜排序规则。查询词固定为“华为”位置固定在北京中心点半径是 50000 米。哪怕这些条件都摆在那里列表依然可能让人感觉像一把纸条被风吹过落下来后恰好排成了现在的样子。这篇只复盘一件小事当搜索服务返回结果时怎样把结果项里已有的相关性线索留在页面上当服务没有返回时又怎样老老实实停在状态和错误上。它不讨论地点是不是“正确答案”也不讨论地图服务是否已经稳定可用。当前工程里真实服务仍取决于认证与运行环境不能拿一张列表的设计稿替它下结论。现象很简单误会也很容易发生最初页面只展示名称和地址时我会把第一个元素自然叫作“首条命中”。这句话听起来没有问题实际上悄悄多走了一步从“接口数组第一个元素”走到了“最值得信任的地点”。如果用户拿它去做门店跳转、地址回填后半句就不该由我替服务说出口。回到现有页面SearchEvidenceItem明确留了rank、siteId、name、address和可选的reliability。这组字段很克制。rank只说明该项在这次展示数组中的位置siteId让同名地点不至于混为一谈address提供人工辨认的文字reliability则是服务返回时可以显示的一条相关性线索。它们拼起来才让“为什么先看到这条”有了可以继续检查的入口。interface SearchEvidenceItem { rank: number; siteId: string; name: string; address: string; reliability?: number; } State totalCount: string 尚未返回; State searchItems: ArraySearchEvidenceItem [];我一开始的错误猜测是排序看着别扭多半是ForEach渲染时把元素弄乱了。这个怀疑并非完全荒唐列表 key 写得不好时确实会有视觉错位。但看回代码后展示用的是${item.rank}-${item.siteId}而rank又是在sites.forEach中按index 1生成的。页面没有另做排序也没有悄悄按名称、距离重新排列。它只是把服务返回的数组逐条装进searchItems再把这个数组显示出来。因此真正的空缺不是“排序代码错了”而是我没有把返回项里的reliability读出来也没有向使用者说明它只是线索。先把调用路径摆在桌面上runSearch()开始时先把页面写成“第 N 轮搜索中”再清空上一次的搜索错误。随后组装固定参数调用site.searchByText。返回成功时代码遍历response.sites而不是凭空生成样本每一项的item.reliability被原样放进SearchEvidenceItem。如果该字段没有提供页面通过reliabilityText显示“未提供”没有用 0、100 或所谓默认分去填空。是否点击执行真实 searchByTextsearchState 写入第 N 轮搜索中组装固定 query location radius调用 site.searchByText是否返回 response读取 response.sites逐项保存 rank siteId name address reliability写入 searchItems 与 totalCount列表显示位置和相关性线索清空 searchItems显示失败状态与真实错误这张图里最容易被忽略的是totalCount。我原本只关心页面展示了几条后来才发现这会制造另一个假象接口报告的总数与本页展示的五条并不是同一个概念。参数固定pageIndex: 1、pageSize: 5因此列表项数最多代表这一页被展示出来的数量totalCount是响应中单独返回的文字化记录。两者需要并排出现不能拿“页面只有五条”推断“地图只找到五个地方”也不能拿一个总数去替每一项解释先后次序。const response await site.searchByText(getContext(this), params); const sites response.sites ?? []; const items: ArraySearchEvidenceItem []; sites.forEach((item: site.Site, index: number) { items.push({ rank: index 1, siteId: item.siteId, name: item.name ?? 未提供名称, address: item.formatAddress ?? 未提供地址, reliability: item.reliability }); }); this.totalCount ${response.totalCount}; this.searchItems items;当我把这段顺着读完第二个猜测也被排除了不是页面在用某个神秘规则洗牌。页面既不计算相关性也不把它解释为距离更不承诺数值越大就必然更适合业务。它做的事是保留服务给出的字段并让结果列表不至于只有“第几条”这一种信息。相关性不是装饰性的紫色文字列表里那行reliability${this.reliabilityText(item.reliability)}看上去只是辅助信息。可在排查时它决定了我会不会把不确定的结果说得太满。假如某次真实响应给出了若干项我能记录固定查询词是什么、当前是第几轮、页面本轮展示多少项、响应声明的totalCount是多少以及每个地点是否携带reliability。这已经足够支持“排序有返回字段可供检查”这句话。它不支持“第一条一定离我最近”“第一条一定是用户要找的公司”“分数等于某个固定阈值就可以自动下单”。那些都需要额外业务规则、地点事实与运行材料当前页面没有写。反过来若字段是undefinedreliabilityText返回“未提供”。我以前总觉得这种文案不够漂亮想补一个 0 让卡片整齐。后来想明白了0 是一个数值结论未提供是信息没有到达。把后者改成前者会让排查者误以为服务明确给了最低分实际上代码只知道字段缺席。地图搜索里最危险的不是空白而是把空白涂成一个很像答案的数字。服务没有回来时别把页面做成会讲故事的人当前代码的失败分支很直白searchItems []totalCount 调用失败未返回searchState写第 N 轮搜索失败searchErrorMessage保存格式化后的真实错误。页面还把首条名称改为“调用失败未返回”并把相关性设回undefined。这是一种比“给你一组演示地点”更有用的失败表达因为它告诉我此刻没有结果可以解释。} catch (error) { this.searchRound nextRound; this.searchItems []; this.totalCount 调用失败未返回; this.searchState 第 ${nextRound} 轮搜索失败; this.searchErrorMessage formatRuntimeError(error as Error); this.evidenceState { query: this.evidenceState.query, resultName: 调用失败未返回, reliability: undefined, markerLongClickCount: this.evidenceState.markerLongClickCount, poiLongClickCount: this.evidenceState.poiLongClickCount, lastEvent: 第 ${nextRound} 轮搜索失败已保留真实错误 }; }我会把这里的页面复测分成两段。第一段不依赖外部结果只观察动作后的状态转换点击后是否先出现搜索中失败后是否清掉旧列表错误文本有没有留在searchErrorMessage。第二段需要认证、网络和真实 Map 服务响应才可以记录sites、totalCount与reliability的实际值。第一段通过不能冒充第二段已经完成第二段没有材料也不该反过来否定失败分支的页面处理。否是否是重置为尚未搜索确认 totalCount 为尚未返回点击搜索searchState 是否进入搜索中检查 runSearch 是否被按钮调用服务是否有真实响应检查 searchItems 为空检查 totalCount 和 searchErrorMessage记录为等待或错误状态核对展示数与 totalCount 分开记录核对每项 reliability 为实际值或未提供仅记录排序线索 不把首条写成地点事实这个流程看起来比“有列表就截图”麻烦一点却把问题拆开了。若searchState没进“搜索中”先查按钮和函数若进了搜索中又失败查错误文本与环境若有返回再查字段是否被保留。每一步都有落点至少不会因为最后空白就胡乱改样式。复测后我留下的三条判断第一列表顺序本身是一个现象不是理由。rank只记录这次展示的先后不替服务解释业务含义。第二reliability有值时可以作为检查线索未提供时就显示未提供。无论哪一种都不能从页面字段直接推导“这个地点是真的”或“这个地点一定适合用户”。第三searchItems的长度和totalCount是两种不同记录。前者是当前页实际装入并展示的项目后者是响应返回的总结果数。把它们混成一个数字后面无论做分页还是人工核对都会很别扭。我现在再看到“地图搜索看起来乱”的反馈不会马上讨论算法也不会急着给第一条加一个“推荐”角标。我会先问页面有没有显示能解释结果的原始线索再问这一轮到底有没有真实响应。若答案还是没有那就把状态和错误留在页面上等环境补齐。地图搜索最朴素的诚实是承认我们此刻只看到了什么也承认还没有看到什么。我还排除了三个看似合理的解释第一个是“固定北京中心点所以肯定按距离排”。页面确实构造了location: this.center也设置了radius: 50000但当前代码没有读取距离字段也没有在客户端比较坐标更没有sort。位置和半径是请求条件不能被我偷换成已证实的排序规则。即使用户感觉某一项“应该更近”那也只能成为进一步核对的理由不是页面已经解释了顺序的证据。第二个是“名称相同第一条自然就是目标”。恰恰相反同名是我更不敢直接采纳的原因。页面保留siteId与formatAddress说明名称不足以独立承担辨认任务。把名称、地址和 ID 同时摆出来目的不是把卡片做复杂而是让人工在返回真的存在时能区分两个名称相近的候选。这里没有地址确认按钮也没有业务表单回填因此我不能把任何一个siteId写成最终地点。第三个是“反正本页只展示五条相关性不显示也无所谓”。这会让诊断失去方向。五条是pageSize: 5的展示上限而非质量结论。没有reliability时我只能说这一页按返回数组显示了五项有该字段时我可以额外记录服务给出的线索。两种情况都比给用户一个没有解释的序号更清楚但谁也不能代替服务文档或实际运行材料解释分值算法。这些排除有个共同点不从请求参数、显示序号或单个字段跳到“排序已经被证明”。工程里常见的误会不是完全没有数据而是刚有一点数据就把它解释得比它本身更多。回到代码的好处是它会把我拉回事实边界当前页保存了哪些字段又明确没有保存哪些字段。状态区为什么不能被结果卡片遮住我还做过一次很容易误导人的操作先尝试搜索得到一个错误接着改了页面样式只留下结果卡片区域。这样一来空列表看上去和“没有符合条件的地点”一模一样。直到我把searchState与searchErrorMessage恢复到状态区才知道二者根本不是同一件事。searchState负责描述这轮请求处在什么阶段searchErrorMessage负责保留失败的原因文本。前者可能是“调用成功但 sites 为空”后者仍是“暂无错误”也可能前者是“搜索失败”后者有格式化的运行错误。若只保留一个“暂无结果”就会把成功空结果和调用失败混在一起。排查人会去修改关键词或半径真正的问题却可能是认证或网络。this.searchState 第 ${nextRound} 轮搜索中; this.searchErrorMessage 暂无错误; this.markOperation(开始第 ${nextRound} 轮固定条件搜索); // 成功后写入返回结果失败时清空列表并保留格式化错误。我现在复测时会先截状态区再看结果区。因为结果区只能告诉我“现在展示了什么”状态区才告诉我“为什么会是这个样子”。对于真实服务不可用的环境这个顺序尤其重要没有地点卡片不是文章的遗憾而是页面应当留下的真实状态。一次可复用的人工核对记录如果以后环境拿到了真实响应我不会只保存“首条名称”。我会在同一份记录里写下查询词、中心点、半径、语言、页码和 pageSize再写searchRound、totalCount、本页searchItems.length。每一项则记录 rank、name、siteId、address、reliability 是否提供。这样做不是为了制造一张漂亮表格而是避免下次有人换了参数后仍拿旧截图讨论排序。这里还要把时间维度放稳。当前页记录了lastTriggerTime它可帮助定位最近一次页面动作但并没有为每个结果项生成独立时间戳。若需要比较多轮实际响应应在外部测试记录中按轮次保存而不是让页面当前这一份状态替我保存历史。页面展示的是现场仪表盘不是历史仓库不承认这点后面最容易把上一轮结果错挂到下一轮。所以我的复测结论很窄页面在成功响应时能将数组位置、总数和reliability的提供情况显示出来响应失败时能清空项目并显示错误。至于排序是否符合某个业务预期、地点是否真实、分数具体意味着什么必须由合法服务响应、文档和人工判断共同补齐。把结论收窄不是退缩而是让下一次真正拿到数据时知道应该补哪一块。再往前走一步我会检查同一轮的页面文案是否自洽有项目时不应仍显示“尚未返回”失败时不应保留旧项目字段未提供时不应用一个数值冒充。这些不是排序算法检查却是排序解释进入页面之前最基本的卫生条件。状态干净后续的真实响应才有被正确阅读的可能。还有一点很容易漏response.sites ?? []把未提供的数组收成空数组并不自动说明“服务找到零个地点”。只有结合searchState、错误文本和实际响应空列表才有可解释的语境。把空数组当零命中是我过去最轻率的一种快捷判断。这也要求我在截图时保留状态摘要而不只截取列表区域否则一张空白图会把不同原因压成同一种沉默。本文依据当前MapSearchLongClickPage中的runSearch、searchItems与totalCount状态撰写。真实搜索排序、实际相关性值与地点结果仍需在合法认证、网络可用的运行环境中单独复核。