不同的属性名称

Distinct attribute names

我想使用 XQuery select 产品中每篇文章的特殊值。

我目前拥有的:

输入XML(摘录):

<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

这 return 向我发送了 product 下每篇文章的每个 attribute 元素,其中内容为“1”,其标识符为 EXAMPLE1 或 EXAMPLE2。 可能是,第 1 条中有与第 2 条中相同的 attribute 标识符(例如 EXAMPLE1)。

我得到的是:

<?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>

我试图在我的 for 循环周围添加一个不同的值,但这将 return 我只有 '1'。

我想要的是只获取每个属性一次:

<attribute identifier="EXAMPLE2" type="BOOLEAN">1</attribute>
<attribute identifier="EXAMPLE1" type="BOOLEAN">1</attribute>

听起来好像您想要的是为在内容为 1 的 attribute 元素中找到的 identifier 属性的每个不同值看到一个 attribute 元素。(或者,稍微更具挑战性,每组等效 attribute 元素对应一个 attribute 元素,其中等效由 deep-equals() 定义。)

distinct-values() 函数在这里对您没有帮助,因为它会将任何输入节点强制转换为简单值(此处为 1)。

如果匹配 identifier 属性就足够了

如果 identifier 属性足以在元素之间建立等价关系,那么像下面这样的东西就足够了(未测试):

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]

如果需要更一般的等价性检验

如果 @identifier 不足以为您的目的建立等效性,您将不得不做一些更复杂的事情;在一般情况下,一种方法是编写一个包含两个参数的函数(我将其称为 local:equivalent()),如果这两个参数对于您的目的是等效的,则 returns 为真。然后编写第二个函数来接受一系列项目并从序列中删除重复项(其中 'being a duplicate' 表示“在 local:equivalent() 上返回 true”。像这样的东西可能作为第一近似值(未测试):

(: 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)

那些对高阶函数感到满意的人会想更进一步,通过允许两个 local:dedup 函数接受一个提供等价的附加参数来消除对名为 local:equivalent 的函数的依赖功能。