View on GitHub

openEQUELLA

A digital repository providing a single platform to house your teaching and learning, research, media, and library content.

Querying repeated metadata nodes

In the previous tutorial we learned how to use the xpath function to extract data from the item xml.

We did a simply query which returned a single value, however XPath queries are much more powerful than that, they can be used return multiple parts of the xml.

A common pattern within EQUELLA is to collect multiple sets of data using the “Repeater” control, for example:

<xml>
 <item>
  <cars>
   <car>
    <make>Ford</make>
    <model>Laser</model>
   </car>
   <car>
    <make>Hyundai</make>
    <model>Accent</model>
   </car>
   <car>
    <make>Honda</make>
    <model>Accord Euro</model>
   </car>
  </cars>
 </item>
</xml>

What if we’d like to report on each car separately? Thankfully the native XML support of databases can easily handle this problem.

Selecting individual cars

Report on each car

SELECT i.id AS item_id, i.uuid, i.version, 
       i_name.text as item_name, be_name.text as collection_name,
       (xpath('make/text()', ix.car))[1]::text as make,
       (xpath('model/text()', ix.car))[1]::text as model
FROM item i 
INNER JOIN (select id, unnest(xpath('item/cars/car', xml::xml)) as car from item_xml) ix 
           on i.item_xml_id = ix.id
INNER JOIN base_entity be on be.id = i.item_definition_id
LEFT JOIN language_string i_name on i.name_id = i_name.bundle_id
LEFT JOIN language_string be_name on be.name_id = be_name.bundle_id
WHERE i.institution_id = CURRENT_INSTITUTION
  AND be.uuid = '20d5d5eb-af16-43b0-a42b-962c57fe87f2'
LIMIT 10

Let’s examine the inner query:

select id, unnest(xpath('item/cars/car', xml::xml)) as car from item_xml

The XPath “item/cars/car” is quite straight forward, it simply selects all the car elements. It’s the unnest() call which turns each <car> element from the returned array into it’s own row, naming the column “car”.

Now that we have rows with individual <car> elements, it’s a simple matter of extracting the data from the child nodes:

(xpath('make/text()', ix.car))[1]::text as make,

(xpath('model/text()', ix.car))[1]::text as model