4
votes

So i want to insert the function distint-nodes in the for clause (see which for below). I'm using BaseX for this purpose.

This is my code:

<autores>{
  for $a in doc("libros.xml")//libro
  return 
    <autor>
    <nombre>{
      for $b in $a/autor
      return concat($b/nombre,' ',$b/apellido)
    }
    </nombre>
    {
      for $c in doc("libros.xml")//libro
      where $c/autor = $a/autor
      return $c/titulo
    }
    </autor> 

  }
</autores>

I want to use this function in the first for, so it only returns me an unique instance of the <autor/> element:

for $b in distinct-nodes($a/autor)
      return concat($b/nombre,' ',$b/apellido)

But I get the following error (BaseX Query info):

Error: Stopped at G:/Pruebas XML/XML/xqueryLibros.xq, 6/31: [XPST0017] Unknown function: fn:distinct-nodes.

Why this function is unknown when it exists? Is there something I'm missing?

EDIT: My purpose is to get an unique instance of the element $a/autor where $a/autor/nombre and $a/autor/apellidos text values are the same

<autores>
  <autor>
    <nombre>W. Stevens</nombre>
    <titulo>TCP/IP Ilustrado</titulo>
    <titulo>Programación Avanzada en el entorno Unix</titulo>
  </autor>
  <autor>
    <nombre>W. Stevens</nombre>
    <titulo>TCP/IP Ilustrado</titulo>
    <titulo>Programación Avanzada en el entorno Unix</titulo>
  </autor>
  <autor>
    <nombre>Serge Abiteboul Peter Buneman Dan Suciu</nombre>
    <titulo>Datos en la Web</titulo>
  </autor>
  <autor>
    <nombre/>
  </autor>
</autores>
2
You say this function exists. Who told you that?Christian Grün
Then i missunderstood the difference between fn and functx, could be that?JD0001
…right, that’s it.Christian Grün

2 Answers

6
votes

There is no standard XQuery function fn:distinct-nodes(...), XQuery only knows fn:distinct-values(...).

The third-party XQuery function library functx knows a functx:dinstinct-nodes(...) function, which is again implemented as standard XQuery functions. The library can be downloaded and imported as a module with most XQuery implementations, as it only uses standard XQuery functions.

If all the <autor/> elements contains is the author's name, consider applying fn:distinct-values(...) instead and recreate the <autor/> elements afterwards.

For performance reasons, it might be reasonable to only extract the required functions if compile time increases too much (the library is rather large). Also be aware that some of the functions have much faster XQuery 3.0 counterparts, taking advantage of new language capabilities.

fn is the default XQuery function namespace, funct the one defined by the function library.

3
votes

Any path expression containing the "/" operator automatically eliminates duplicate nodes, so writing functx:distinct-nodes($a/autor) is completely redundant, it will always return exactly the same result as $a/autor.

But I suspect you misunderstood what functx:distinct-nodes() does. If you have the structure

<authors>
 <author>John Smith</author>
 <author>John Smith</author>
</authors>

then both authors/author and functx:distinct-nodes(authors/author) will return both the <author> elements. They are considered to be distinct because they are distinguishable (for example, one has preceding siblings and the other does not). If you want to treat them as duplicates then you need first to define exactly what you mean by duplicates (perhaps the definition you want is that they are deep-equal in the sense of the fn:deep-equal function) and then you need to adopt a different approach.

LATER:

In your edit to the question you have said what it means for two authors to be (non-)distinct: "... where $a/autor/nombre and $a/autor/apellidos text values are the same".

So it's best to think of this as a grouping problem: group the elements where $a/autor/nombre and $a/autor/apellidos are the same, then select one element from each group.

In XQuery 3.0 grouping is done using the "group by" clause of the FLWOR expression:

for $a in autor
group by $n := $a/nombre, $ap := $a/appellidos
return $a[1]

In XQuery 1.0 it's much more clumsy, you typically write something like

let $keys := distinct-values(autor/concat(nombre, '~', appellidos))
for $key in $keys
return /autor[concat(nombre, '~', appellidos) = $key][1]