Reputation: 5460
With XQuery I want to select a special value from every article within a product.
What I currently have:
Input XML (extract):
<product type="product" id="2246091">
<product type="article">
<attribute identifier="EXAMPLE1" type="BOOLEAN">0</attribute>
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
</product>
<product type="article">
<attribute identifier="EXAMPLE1" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
</product>
<product type="article">
<attribute identifier="EXAMPLE1" type="BOOLEAN">0</attribute>
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
</product>
</product>
XQuery:
for $i in //product
[@type = 'product'
and @id = '2246091']
//attribute
[@type='BOOLEAN'
and @identifier= ('EXAMPLE1', 'EXAMPLE2') ]
where $i = '1'
return $i
This returns me every attribute
element from every article under a product
where the content is '1' and its identifier is EXAMPLE1 or EXAMPLE2.
It could be, that in article 1 there is the same attribute
identifier (e.g. EXAMPLE1) as in article 2.
What I get:
<?xml version="1.0" encoding="UTF-8"?>
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE1" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
I tried to add a distinct-values around my for
loop, but this will return me only '1'.
What I would like is to get every attribute only once:
<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE1" type="BOOLEAN">1</attribute>
Upvotes: 0
Views: 87
Reputation: 25034
It sounds as if what you want is to see one attribute
element for each distinct value of the identifier
attribute found among the attribute
elements whose content is 1. (Or, slightly more challengingly, one attribute
element for each set of equivalent attribute
elements, where equivalence is defined by deep-equals().)
The distinct-values() function isn't helping you here, because it coerces any input nodes into simple values (here, 1).
identifier
attribute sufficesIf the identifier
attribute suffices to establish equivalence among the elements, then something like the following should suffice (not tested):
let $ones := //product[@type = 'product'
and @id = '2246091']
//attribute[@type='BOOLEAN'
and @identifier =
('EXAMPLE1', 'EXAMPLE2') ],
$ids := distinct-values($ones/@identifier)
for $id in $ids
return ($ones[@identifier = $id])[1]
If @identifier
does not suffice to establish equivalence for your purposes, you will have to do something more complicated; in the general case one way to do it would be to write a function of two arguments (I'll call it local:equivalent()
) which returns true iff the two arguments are equivalent for your purposes. Then write a second function to accept a sequence of items and remove duplicates from the sequence (where 'being a duplicate' means 'returning true on local:equivalent()
). Something like this might work as a first approximation (not tested):
(: dedup#1: remove duplicates from a sequence :)
declare function local:dedup(
$items as item()*
) as xs:boolean {
local:dedup($items, ())
};
(: dedup#2: work through the input sequence one
by one, removing duplicates and accumulating
non-duplicates. Cost is n^2 / 2. :)
declare function local:dedup(
$in as item()*,
$out as item()*
) as xs:boolean {
if (empty($in))
then $out
else let $car := head($in)
return if (some $i in $in
satisfies
local:equivalent($i, $car))
then local:dedup(tail($in), $out)
else local:dedup(tail($in), ($car, $out))
};
(: equivalent#2: true iff arguments are equivalent :)
declare function local:equivalent(
$x, $y : item()
) as xs:boolean {
// determine application-specific equivalence
// however you like ...
deep-equal($x, $y)
};
(: Now do the work :)
let $ones := //product[@type = 'product'
and @id = '2246091']
//attribute[@type='BOOLEAN'
and @identifier =
('EXAMPLE1', 'EXAMPLE2') ]
return local:dedup($ones)
Those comfortable with higher-order functions will want to go a step further and remove the dependency on having a function named local:equivalent
by allowing both local:dedup
functions to accept an additional argument providing the equivalence function.
Upvotes: 2