XQuery в BaseX: возвращает значение avg из результата отображения

75
8

Я пытаюсь управлять выходом карты: получить функцию в BaseX.

Файл сопоставления выглядит следующим образом:

 <around>
<point NR="51151">161</point>
<point NR="31252">82</point>
<point NR="54321">323</point>
<point NR="54321">319</point>
<point NR="54321">327</point>
</around>

и представляет собой некоторые географические точки с номером в качестве атрибута (NR) и расстояние от определенной точки поиска в качестве значения. Есть несколько точек, которые присутствуют более одного раза (как точка 54321 в приведенном выше примере). Это касается содержания, так как это "ветки" точки с тем же номером.

То, что я сделал, - это среднее от расстояний, если я ищу эти NR.

Мой запрос:

 let $c :=
<around>
<point NR="51151">161</point>
<point NR="31252">82</point>
<point NR="54321">323</point>
<point NR="54321">319</point>
<point NR="54321">327</point>
</around>
for $r in $c//point
let $m := map { data($r/@NR) := $r/text() }
return
if ( map:contains($m, '54321' ) ) then
avg(map:get($m, '54321'))
else
()

... возвращает 323 319 327, поэтому игнорирует "avg", хотя в BaseX отсутствует сообщение об ошибке синтаксиса.

Как я могу достичь вышеуказанного? Спасибо заранее!

спросил(а) 2021-01-19T21:07:30+03:00 2 месяца, 3 недели назад
1
Решение
75

Карта сопоставляет один ключ с одним значением. То, что вы на самом деле делаете здесь, - это создание одной одномерной карты $m для каждой итерации цикла for. Ваш код в точности эквивалентен этому:

for $r in $c/point
return if ($r/@NR eq '54321') then avg($r/text()) else ()

Если вы ищете только один ключ, я рекомендую вам не использовать карту вообще и просто сделать это, что намного проще и быстрее:

return avg($c/point[@NR='54321'])

Даже если вы просматриваете несколько ключей, наивная реализация, использующая group by или distinct-values() может быть быстрее, чем карта, если BaseX имеет новый индекс атрибута в запрашиваемом документе.

Тем не менее, чтобы использовать карту, вам нужно создать одну новую карту, используя map:new и group by

let $c :=
<around>
<point NR="51151">161</point>
<point NR="31252">82</point>
<point NR="54321">323</point>
<point NR="54321">319</point>
<point NR="54321">327</point>
</around>
let $m := map:new(
for $r in $c/point
let $key := $r/@NR, $value := $r/text()
group by $key
return map {$key := $value}
)
return avg($m('54321'))

Вы также можете сделать это, используя функцию сокращения (функции fold-) вместо группового:

let $c :=
<around>
<point NR="51151">161</point>
<point NR="31252">82</point>
<point NR="54321">323</point>
<point NR="54321">319</point>
<point NR="54321">327</point>
</around>
let $reducer := function($points as map(*), $point as item()) {
map:new((
$points,
map { $point/@NR := ($points($point/@NR), $point/text())}
))
}
let $m := fold-left($reducer, map{}, $c/point)
return avg($m('54321'))

Помните, что в производственном коде вам нужно создать карту один раз, а затем использовать свой список ключевых поисков на нем, чтобы иметь любую надежду на получение выгоды от использования карты.

ответил(а) 2021-01-19T21:07:30+03:00 2 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема