XQuery函数返回值异常:仅显示部分最远国家问题排查
排查XQuery BFS函数只返回单个最远国家的问题
我明白你遇到的问题了——用BFS递归函数找尼加拉瓜的最远邻国时,本该返回加拿大、阿根廷等多个国家,结果只出了加拿大,但单独查阿根廷却正常。咱们来一步步拆解你的代码问题,然后修复它。
首先先贴出你的原始代码方便对照:
declare variable $mondial := doc("D:/mondial.xml")/mondial; declare variable $country := $mondial/country; declare function local:reachable-far($curr) { if ($curr = ()) then () else ( local:reachable-bfsl($curr, $curr, $curr, $curr, 0) ) }; declare function local:reachable-bfsl($queue, $seen, $lastoflevel, $currlevel, $depth) { (:return current level if stack is empty (should never happen):) if (empty($queue)) then (<row>{$currlevel} <depth>{$depth}</depth></row>) else ( let $curr := $queue[1] let $neighbors-all := $curr/border/@country let $neighbors-code := $neighbors-all[not(.=$seen/@car_code)] let $neighbors := $country[@car_code = $neighbors-code] (:if current country is not the last of level, continue with current level:) return if ($curr/@car_code != $lastoflevel/@car_code) then ( local:reachable-bfsl(($queue[position()>1], $neighbors), ($seen, $neighbors), $lastoflevel, $currlevel union $curr, $depth) ) (:if current country is the last of level:) else ( (:current contury has searchable neighbors or stack is not empty after popping:) if (not(empty($neighbors)) or not(empty($queue[position()>1]))) then ( (:clear current level, continue next level, update last of level to last in stack, depth++:) local:reachable-bfsl(($queue[position()>1], $neighbors), ($seen, $neighbors), ($queue[position()>1], $neighbors)[last()], (), $depth + 1) ) (:current country does not have searchable neighbors and stack is empty after popping:) else ( <row>{trace($currlevel union $curr)} <depth>{$depth}</depth></row> ) ) ) }; let $return-list := local:reachable-far($country[name="Nicaragua"]) return $return-list/country/name
问题根源分析
你的代码核心问题出在当前层级节点的收集逻辑上:
- 当处理到当前层级的最后一个节点时,你把
$currlevel重置成了空序列(),这导致下一层级的节点完全没有被累积。 - 最后返回结果时,
$currlevel union $curr只能拿到最后一个被处理的节点,而不是所有同深度的最远节点——加拿大刚好是最后被处理的那个,所以只显示了它。 - 另外,
$currlevel union $curr用union其实没必要,因为每个节点只会被处理一次,直接用序列拼接($currlevel, $curr)更合适。
修复后的代码
我调整了参数和逻辑,专门加了参数来跟踪当前层级的所有节点,以及记录所有最大深度的节点:
declare variable $mondial := doc("D:/mondial.xml")/mondial; declare variable $country := $mondial/country; declare function local:reachable-far($start-country) { // 初始化BFS:队列是起始国家,已访问集合是起始国,当前层级最后一个节点是起始国,当前层级为空,深度0,最大深度节点为空 local:reachable-bfsl($start-country, $start-country, $start-country, (), 0, ()) }; declare function local:reachable-bfsl($queue, $visited, $last-in-level, $current-level-nodes, $current-depth, $max-depth-nodes) { // 队列为空时,返回所有最远的节点 if (empty($queue)) then $max-depth-nodes else let $current-node := $queue[1] // 找到当前节点未访问过的邻国 let $unvisited-neighbor-codes := $current-node/border/@country[not(. = $visited/@car_code)] let $unvisited-neighbors := $country[@car_code = $unvisited-neighbor-codes] // 更新已访问集合和队列 let $updated-visited := ($visited, $unvisited-neighbors) let $updated-queue := ($queue[position() > 1], $unvisited-neighbors) // 把当前节点加入当前层级集合 let $updated-current-level := ($current-level-nodes, $current-node) return if ($current-node/@car_code != $last-in-level/@car_code) then // 还没到当前层级末尾,继续处理队列,累积当前层级节点 local:reachable-bfsl($updated-queue, $updated-visited, $last-in-level, $updated-current-level, $current-depth, $max-depth-nodes) else // 到达当前层级末尾,判断是否有下一层级 if (not(empty($unvisited-neighbors)) or not(empty($queue[position() > 1]))) then // 下一层级的最后一个节点是更新后队列的最后一个元素 let $next-last-in-level := $updated-queue[last()] // 更新最大深度节点:如果当前深度比之前的最大深度大,就替换成当前层级的所有节点 let $new-max-nodes := if ($current-depth > count($max-depth-nodes) div count($country[1])) then $updated-current-level else $max-depth-nodes // 进入下一层级,重置当前层级集合,深度+1 local:reachable-bfsl($updated-queue, $updated-visited, $next-last-in-level, (), $current-depth + 1, $new-max-nodes) else // 没有下一层级了,当前层级就是最远的,返回所有节点 $updated-current-level }; // 查询尼加拉瓜的最远国家 let $furthest-countries := local:reachable-far($country[name="Nicaragua"]) return $furthest-countries/name
关键修改说明
- 新增
$current-level-nodes参数:专门用来累积当前层级的所有节点,不会在层级切换时被清空,确保同深度的节点都被收集。 - 新增
$max-depth-nodes参数:跟踪所有达到最大深度的节点,避免只返回最后一个处理的节点。 - 层级切换逻辑优化:当处理完当前层级最后一个节点时,先判断当前深度是否是最大的,再更新最大深度节点集合,然后才进入下一层级。
- 移除了不必要的
union操作:用序列拼接($a, $b)代替,更高效且符合BFS节点收集的逻辑。
现在你再运行这个修改后的代码,查询尼加拉瓜应该就能得到加拿大、阿根廷等所有最远国家了,单独查询阿根廷也能正常工作。
内容的提问来源于stack exchange,提问作者Vamdinade
相关产品推荐
相关产品推荐

