Как изменить выводимый XML из рекурсивной функции в XQuery? - PullRequest
0 голосов
/ 15 ноября 2018

Я пытаюсь рекурсивно сгенерировать XML-документ стран, граничащих друг с другом в базе данных mondial. В документе должно быть указано, сколько границ вам нужно будет пересечь, чтобы добраться до страны, и общее количество пересеченных стран.

XML должен выглядеть так:

    <from_swe_cross>
        <cross num="1" sum="2">
            <country>Finland</country>
            <country>Norway</country>
        </cross>
        <cross num="2" sum="3">
            <country>Russia</country>
        </cross>

Я знаю, как составить список стран, в которые вы можете поехать, но не знаю, как правильно его отформатировать, и знаю, сколько было сделано пересечений границы или общее количество пересеченных стран.

Мой код сейчас:

declare function local:crossing($stack, $seen, $level, $sum) {
  if(empty($stack)) then $seen
  else (
    let $country := $stack[1]
    let $neighbors :=
        for $code in $country/border/@country[not(. = $seen/@car_code)]
        return $country/../country[@car_code = $code]
    return local:crossing(($neighbors, $stack[position() > 1]), ($seen, $neighbors), $level + 1, $sum + count($neighbors))
  )
};

<from_swe_cross>{
  local:crossing(db:open('mondial')//country[@car_code = 'S'], db:open('mondial')//country[@car_code = 'S'], 1, 0)/name
}
</from_swe_cross>

Но в этом списке перечислены только те страны, в которые вы можете перейти, например:

<from_swe_cross>
  <name>Albania</name>
  <name>Greece</name>
  <name>Macedonia</name>
  <name>Serbia</name>
  <name>Montenegro</name>
  ...
</from_swe_cross>

Как мне сгенерировать правильный XML?

1 Ответ

0 голосов
/ 15 ноября 2018

Одним из решений является использование параметра накопления, в котором вы собираете последовательность результатов, начиная с пустой последовательности:

declare function local:crossing($stack, $seen, $level, $sum, $out) {
  if(empty($stack)) then $out
  else (
    let $country := $stack[1]
    let $neighbors :=
        for $code in $country/border/@country[not(. = $seen/@car_code)]
        return $country/../country[@car_code = $code]
    let $sum := $sum + count($neighbors)
    return local:crossing(
      ($neighbors, $stack[position() > 1]),
      ($seen, $neighbors),
      $level + 1,
      $sum,
      ($out, <cross num="{$level}" sum="{$sum}">{$neighbors/name}</cross>)
    )
  )
};
<from_swe_cross>{
  local:crossing(
    db:open('mondial')//country[@car_code = 'S'],
    db:open('mondial')//country[@car_code = 'S'],
    1, 0, ()
  )
}
</from_swe_cross>

Результат выглядит так же, как показано выше:

<from_swe_cross>
  <cross num="1" sum="2">
    <name>Finland</name>
    <name>Norway</name>
  </cross>
  <cross num="2" sum="3">
    <name>Russia</name>
  </cross>
  <cross num="3" sum="15">
    <name>Belarus</name>
    <name>Latvia</name>
    <name>Lithuania</name>
    <name>Poland</name>
    <name>Ukraine</name>
    <name>Estonia</name>
    <name>China</name>
    ...

Шаги, не находящие новых адресатов, отображаются как пустые cross элементы.Если вы хотите, вы можете подавить их.

  ...
  <cross num="4" sum="15"/>
  <cross num="5" sum="15"/>
  <cross num="6" sum="15"/>
  <cross num="7" sum="18">
    <name>Czech Republic</name>
    <name>Germany</name>
    <name>Slovakia</name>
  </cross>
  ...
...