4

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>
JD0001
  • 131
  • 3
  • 11

2 Answers2

6

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.

Jens Erat
  • 37,523
  • 16
  • 80
  • 96
  • Thanks. This is the case where our teachers only shows a bit of a language and they dont do it properly or dont explain us a **** . Sorry for my mistakes and sorry for the people who works with this language, also sorry for my english. – JD0001 Mar 27 '17 at 18:45
  • 1
    Don't worry, XQuery (and the whole concept of functional programming) is not easy to get started with, but the effort's worth it. – Jens Erat Mar 27 '17 at 19:17
3

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]
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • I think my problem is that i have to apply some function to return distinct mayor element $a/autor, to achieve the next code, `return concat($b/nombre,' ',$b/apellido)` only returns the text of different instances of the autor. As far as i know the `for` clause iterates between each `$a/autor` elements, so applying `fn:distinct-values` inside the `fn:concat` wont work as i wish – JD0001 Mar 28 '17 at 09:31
  • Thanks. This was so helpful for me, the way to treat the same elements as a group was the key for me – JD0001 Mar 28 '17 at 17:45