XQuery — извлечение отдельных элементов по атрибуту

У меня есть XML-документ, который содержит список клиентов, их заказов и позиций в этих заказах. Я хочу получить список, содержащий различные значения клиентов. Идентификатор клиента является атрибутом «Имя». Пример XML:

<customerunion>
  <Customer Name="c2">
    <Order OrderNumber="o1">
      <Item Description="xyz">i10</Item>
    </Order>
  </Customer>

 <Customer Name="c3">
    <Order OrderNumber="o1">
      <Item Description="qwe">i11</Item>
      <Item Description="mnb">i12</Item>
    </Order>
    <Order OrderNumber="o2">
      <Item Description="cfg">i5</Item>
    </Order>
 </Customer>

 <Customer Name="c1">
    <Order OrderNumber="o1">
      <Item Description="abc">i1</Item>
      <Item Description="def">i2</Item>
    </Order>
 </Customer>

 <Customer Name="c4">
    <Order OrderNumber="o1">
      <Item Description="abc">i1</Item>
    </Order>
    <Order OrderNumber="o2">
      <Item Description="def">i2</Item>
    </Order>
 </Customer>

 <Customer Name="c2">
    <Order OrderNumber="o1">
      <Item Description="milk"/>
 </Order>
 </Customer>

 <Customer Name="c10">
    <Order OrderNumber="o1">
      <Item Description="chips greek yogort avacado"/>
    </Order>
 </Customer>

 <Customer Name="c4">
    <Order OrderNumber="o1">
      <Item Description="tea bags cheese"/>
    </Order>
 </Customer>

 <Customer Name="c11">
    <Order OrderNumber="o1">
      <Item Description=" milk sushi Notebook "/>
    </Order>
 </Customer>

 <Customer Name="c11">
    <Order OrderNumber="o2">
      <Item Description="grape tomato"/>
    </Order>
 </Customer>

 <Customer Name="c12">
    <Order OrderNumber="o1">
      <Item Description="bread"/>
    </Order>
 </Customer>
</customerunion>

и ниже результат, который я хочу получить:

<customerlib>
  <Customer Name="c1">
    <Order OrderNumber="o1">
      <Item Description="abc">i1</Item>
      <Item Description="def">i2</Item>
    </Order>
  </Customer>

  <Customer Name="c2">
    <Order OrderNumber="o1">
      <Item Description="xyz">i10</Item>
    </Order>
  </Customer>

  <Customer Name="c3">
    <Order OrderNumber="o1">
      <Item Description="qwe">i11</Item>
      <Item Description="mnb">i12</Item>
    </Order>
    <Order OrderNumber="o2">
      <Item Description="cfg">i5</Item>
    </Order>
  </Customer>
  <Customer Name="c4">
    <Order OrderNumber="o1">
      <Item Description="abc">i1</Item>
    </Order>
    <Order OrderNumber="o2">
      <Item Description="def">i2</Item>
    </Order>
  </Customer>
  <Customer Name="c10">
    <Order OrderNumber="o1">
      <Item Description="chips">i1</Item>
      <Item Description="greek yogort">i2</Item>
      <Item Description="avacado">i3</Item>
    </Order>
  </Customer>

  <Customer Name="c11">
    <Order OrderNumber="o1">
      <Item Description="milk">i1</Item>
      <Item Description="sushi">i2</Item>
      <Item Description="Notebook">i3</Item>
    </Order>
    <Order OrderNumber="o2">
      <Item Description="grape tomato">i1</Item>
    </Order>
  </Customer>

  <Customer Name="c12">
    <Order OrderNumber="o1">
      <Item Description="bread">i1</Item>
    </Order>
  </Customer>
</customerlib>

Я использовал следующий XQuery:

let $customer := doc("customerunion.xml")/customerunion/Customer
let $allcustomername := doc("customerunion.xml")/Customer/@Name
for $uniquecustomer in distinct-values($customer/@Name)
let $order := distinct-values($customer[Name=$uniquecustomer]/Order)
let $item := distinct-values($customer[Name=$uniquecustomer]/Order/Item)
where $uniquecustomer = $allcustomername
return 
 <customerlib>
    {$uniquecustomer/..}
    {
      for $o in $order
      return $o
    }
    {
      for $i in $item
      return $i
    }
 </customerlib>

Однако я не получил результатов от выполнения запроса. Можете ли вы помочь мне изменить XQuery, чтобы получить желаемый результат?


person Aaron    schedule 30.09.2014    source источник
comment
Во-первых, ваша первая строка XQuery имеет /customerunion/, тогда как XML имеет <customersunion>: в середине имени элемента есть s. Ваше второе утверждение let полностью исключает /customersunion/. По этим причинам ваши первые две переменные ничего не содержат.   -  person LarsH    schedule 30.09.2014
comment
Пожалуйста, выполните базовую отладку, прежде чем задавать вопрос. Запустите только первую строку (и верните результаты), если все в порядке, перейдите к следующей строке (или блоку логического кода, или любой другой разумной базовой единице).   -  person Jens Erat    schedule 30.09.2014
comment
Чтобы добавить к справедливому комментарию Йенса, вам может быть интересно узнать о функции fn:trace(), которую вы можете использовать для отладки.   -  person dirkk    schedule 30.09.2014
comment
Спасибо всем за помощь.   -  person Aaron    schedule 01.10.2014


Ответы (1)


Если я вас правильно понял, мне кажется, что вы просите сгруппировать все Заказы в отдельных Клиентов?

Если это так, в eXist-db вы можете использовать выражение group by из XQuery 3.0. Таким образом, следующий запрос должен работать для вас:

<customerlib>
{
    let $customers := doc("customerunion.xml")
    return
        for $customer in $customers/customersunion/Customer
        group by $customer-name := $customer/@Name
        order by substring-after($customer-name, "c") cast as xs:integer
        return
            <Customer Name="{$customer-name}">
            {
                $customers/customersunion/Customer[@Name eq $customer-name]/Order
            }
            </Customer>
}
</customerlib>

Я также добавил выражение order by, так как результаты, которые вы показали, были упорядочены. Однако это работает на основе того факта, что ваши имена клиентов всегда имеют формат cN, где N — некоторое число. Если это не так, вам нужно будет изменить или удалить эту строку из запроса.

person adamretter    schedule 30.09.2014