XPath 3.0: выбор узлов по условию (поиск максимального количества дочерних узлов по дате)

Данный XML:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <c cid="c0" v="100">
        <d did="c00" v="0" dt="2016-01-01" />
        <d did="c01" v="0" dt="2016-02-03" />
        <d did="c02" v="0" dt="2016-01-15" />
    </c>
    <c cid="c1" v="100">
        <d did="c10" v="1" dt="2016-01-01" />
        <d did="c11" v="0" dt="2015-03-03" />
        <d did="c12" v="0" dt="2015-04-15" />
    </c>
</root>

Я должен найти XPath 2.0 или 3.0, который вернет мне список c узлы по выражению: найти максимум d узел этого c дети от dt атрибут (разобрать как date), и если его v значение атрибута "0", вернуть c узел.

Я ожидаю получить один c узел (с @cid="c0") в результате, потому что /root/c[cid="c1"]/d[@dt="2016-01-01"]/@v не равно 0 (равно 1).

Я застрял на этом XPath софар:

/root/c[d[max(xs:date(@dt))]/@v="0"]

Это дает мне ошибку (я использую Oxygen XML Editor, XPath 3.0):

Ошибка XPath из-за: Эффективное логическое значение не определено для последовательности, начинающейся с атомарного значения, отличного от логического, числа, строки или URI

Я полагаю, это связано с неправильным max функционировать, но не может заставить его работать.

ОБНОВЛЕНИЕ:

  1. Предположим, что c узлы будут иметь d дети с уникальным максимальным значением их dt атрибутов.
  2. Я также попробовал XPath 3.0 выражение:

    /root/c[d[@dt=max(.//@dt/xs:date(.))]/@v="0"]

но он вернул мне все узлы (неправильный результат).

2 ответа

Решение

Вот короче и, вероятно, более эффективный (максимум за <c> элемент рассчитывается только один раз) XPath 2.0 однострочно:

/*/c[max(d/xs:date(@dt)) = d[@v eq '0']/@dt]

Наслаждайтесь!

В XQuery запрос довольно прост и может быть написан так, чтобы вычислять максимальную дату только один раз за c узел:

for $c in /root/c
let $max := max($c/d/@dt/xs:date(.))
where $c/d[@dt = $max]/@v = '0'
return $c

В XPath вы можете написать запрос, чтобы (хотя бы концептуально) вычислить его один раз за d и надеемся, что результат либо оптимизируется процессором, либо все равно будет достаточно быстрым:

/root/c[d[@dt = max(../d/@dt/xs:date(.))]/@v = '0']

Вы также можете использовать let выражение XPath для восстановления явного предварительного вычисления:

/root/c[
  let $max := max(d/@dt/xs:date(.))
  return d[@dt = $max]/@v = '0'
]
Другие вопросы по тегам