XQuery: Как узнать, есть ли дублеты? - PullRequest
2 голосов
/ 19 октября 2019

У меня есть XML-файл, есть <a> и <b> для каждого element

Я хочу написать запрос, используя XQuery для возврата True или False

есть элементс именем <element>.

каждый <element> содержит 2 элемента <a> и <b>.

Return False: если какой-либо из них <a> имеет то же значение, что и другое<a> в другом элементе && там <b> значения отличаются

в противном случае True: <a> значения различны в каждом элементе или есть сходство, но есть <b> значения отличаются

например

<root>
<element>
   <a>ttt</a>
   <b>tttsame</b>
</element>
<element>
   <a>ttt</a>
   <b>tttsame</b>
</element>
<element>
   <a/>
   <b>value</b>
</element>
<element>
   <a>rrr</a>
   <b>rrrvalue</b>
</element>
<element>
   <a>mmm</a>
   <b>rrrvalue</b>
</element>
<element>
   <a>mmm</a>
   <b>rrrvalue</b>
</element>
</root>

Этот должен быть в порядке, должен возвращать true

<root>
<element>
   <a>ttt</a>
   <b>ttt value</b>
</element>
<element>
   <a>ttt</a>
   <b>ttrdiff</b>
</element>
<element>
   <a/>
   <b>value</b>
</element>
<element>
   <a>mmm</a>
   <b>rrrvalue</b>
</element> 
</root>

не должен быть принят, потому что ttt имеет два разных значения, должен возвращать false

Ответы [ 3 ]

2 голосов
/ 19 октября 2019

Вы можете сгруппироваться по a, а затем проверить, существует ли более одной отдельной b в какой-либо группе, например с

not
(
    for $a-group in root/element
    group by $a := $a-group/a
    where tail(distinct-values($a-group/b))
    return $a-group
)

https://xqueryfiddle.liberty -development.net /6qM2e2r / 0 и https://xqueryfiddle.liberty -development.net / 6qM2e2r / 1 имеет две входные выборки.

Что касается того, как это работает, вопрос просит вернуть false, еслиесть любой <a>, имеющий то же значение, что и другой <a> в другом элементе && там * значение <b> отличается ".

Чтобы найти element элементов с тем же дочерним элементом a, мы можем group by $a := $a-group/a в выражении for $a-group in root/element. Различные или разные b значения в каждой группе a s с одним и тем же значением вычисляются с помощью distinct-values($a-group/b), если имеется хотя бы два разных значения b, тогда tail(distinct-values($a-group/b)) содержит хотя бы одно значение, в противном случаепустая последовательностьЭто работает так же, как в предложении XQuery 3 group by "В кортеже постгруппировки, созданном для данной группы, каждая переменная без группировки связана с последовательностью, содержащей объединенные значения этой переменной во всех кортежах предварительной группировки, которые были назначенык этой группе "(https://www.w3.org/TR/xquery-31/#id-group-by), так что после условия group by $a := $a-group/a переменная $a-group привязана к последовательности элементов element с тем же ключом группировки на основе дочернего элемента a.

Таким образом, полный for .. group by .. where .. return выбирает группы элементов element с одинаковым значением a, где есть как минимум два разных / отличных значения b.

Поскольку требуется«return false», если такие группы существуют, функция not() применяется для реализации этого условия, поскольку булево значение непустой последовательности равно true, а not(..) затем возвращает false, если есть какие-либо элементы, удовлетворяющие условию, выраженному вfor выбор.

2 голосов
/ 20 октября 2019

Простой XPath 2.0 :

empty(
        (for $parentA-Dubled in /*/*[a = following-sibling::*/a]
           return
             empty($parentA-Dubled/following-sibling::*
                                        [$parentA-Dubled/a eq a and $parentA-Dubled/b ne b])
        )
        [not(.)]
      )

XSLT 2.0 - проверка на основе:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="/">
    <xsl:value-of select=
    "empty(
            (for $parentA-Dubled in /*/*[a = following-sibling::*/a]
              return
                empty($parentA-Dubled/following-sibling::*
                                         [$parentA-Dubled/a eq a and $parentA-Dubled/b ne b])
            )
             [not(.)]
          )
     "/>
    </xsl:template>
</xsl:stylesheet>

Когдаэто преобразование применяется к любому XML-документу, оно оценивает выражение XPath и выводит результат этой оценки .

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

true

При применении ко второму предоставленному XML-документу снова получается нужный, правильный результат:

false

Объяснение :

Это подвыражение:

(for $parentA-Dubled in /*/*[a = following-sibling::*/a]
               return
                 empty($parentA-Dubled/following-sibling::*
                          [$parentA-Dubled/a eq a and $parentA-Dubled/b ne b])

вычисляет последовательность логических значений: true() / false()

true() возвращается, когда это правда:

empty($parentA-Dubled/following-sibling::*
                          [$parentA-Dubled/a eq a and $parentA-Dubled/b ne b])

Это означает, что true() возвращается для каждого случая, когда существует $parentA-Dubled/a, у которого нет другого a (потомок следующего брата $parentA-Dubled с тем же значением, что и $parentA-Dubled/a, нозначение его b родного брата отличается от значения $parentA-Dubled/b.

Подводя итог : true() возвращается, когда для всех a элементов с одинаковым значением их b братьев и сестер также имеют (все b с) одинаковое значение

Тогдакогда происходит возврат false()?

Возвращение false() означает, что empty() вернул false() - то есть существует хотя бы один случай из двух a элементов, которые имеютто же значение, но у их b родных элементов есть разные значения.

Таким образом, вышеприведенное подвыражение возвращает последовательность, такую ​​как:

true(), true(), true(), ..., true() - все значения true()

или

true(), true(), true(), ..., false), ..., true() - по крайней мере одно из значений равно false()

Первоначальная проблема требует, чтобы мы вернули true() в первом случае и вернулиfalse() во втором случае.

Это легко выразить как:

empty($booleanSequence[. eq false()]) - и это эквивалентно более короткому:

empty($booleanSequence[not(.)])

Теперь нам просто нужно заменить в вышеприведенном выражении $booleanSequence первое подвыражение, которое мы проанализировали выше:

(for $parentA-Dubled in /*/*[a = following-sibling::*/a]
               return
                 empty($parentA-Dubled/following-sibling::*
                          [$parentA-Dubled/a eq a and $parentA-Dubled/b ne b])

Таким образом, мыполучить полное выражение XPath, которое решает исходную проблему:

empty(
        (for $parentA-Dubled in /*/*[a = following-sibling::*/a]
           return
             empty($parentA-Dubled/following-sibling::*
                                        [$parentA-Dubled/a eq a and $parentA-Dubled/b ne b])
        )
        [not(.)]
      )
0 голосов
/ 19 октября 2019

Попробуйте этот код XQuery, чтобы получить только один отдельный элемент <a> (соответствующее значение <b> не указано; здесь выбран первый элемент):

let $file := doc("input.xml")/root,
    $vals := distinct-values($file/element/a) return
  <root>
    {for $i in $vals return $file/element[a=$i][1]}
  </root>

Егорезультат:

<root>
    <element>
        <a>ttt</a>
        <b>ttt value</b>
    </element>
    <element>
        <a/>
        <b>value</b>
    </element>
    <element>
        <a>rrr</a>
        <b>rrrvalue</b>
    </element>
    <element>
        <a>mmm</a>
        <b>rrrvalue</b>
    </element>
</root>
...