Hello Cracks ;-)
In the code below I encounter the problem, that I have to define the same xPath function variable $organizations twice and I do not understand why. Running the code with the newest versions of BaseX or with Saxon EE, I get a runtime error when I leave the second definition of $organizations out.
Any explanation? Thanks in advance.
Happy Holidays Always, Leo
xquery version "3.1" encoding "UTF-8"; declare variable $doc := doc("factbook.xml");
element Memberships { let $membershipPotentcy := function($orgs){if (count($orgs)le 0) then 'none' else if (count($orgs) le 5) then 'few' else 'many'} let $organizations := function($country){$doc//organization[members/@country = $country/@id]/@abbrev/string(.)} for $countries in $doc//country group by $mp:= $membershipPotentcy($organizations($countries)) order by $mp return element {$mp} { for $country in $countries let $organizations := function($country){$doc//organization[members/@country = $country/@id]/@abbrev/string(.)} order by $country/@name return <country>{$country/@name, $organizations($country)}</country> }}
On 12/19/2022 10:43 PM, Leo Studer wrote:
Hello Cracks ;-)
In the code below I encounter the problem, that I have to define the same xPath function variable $organizations twice and I do not understand why. Running the code with the newest versions of BaseX or with Saxon EE, I get a runtime error when I leave the second definition of $organizations out.
Any explanation? Thanks in advance.
Happy Holidays Always, Leo
xquery version "3.1" encoding "UTF-8"; declare variable $doc := doc("factbook.xml");
*element**Memberships*{ *let**$membershipPotentcy*:=*function*(*$orgs*){*if*(/count/(*$orgs*)le0) *then*'none' *else**if*(/count/(*$orgs*) le5) *then*'few' *else*'many'} *let**$organizations*:=*function*(*$country*){*$doc*//*organization*[*members*/*/@country/*=*$country*/*/@id/*]/*/@abbrev/*//string/(.)} *for**$countries**in**$doc*//*country* *group by**$mp*:=*$membershipPotentcy*(*$organizations*(*$countries*)) *order by**$mp* *return* *element*{*$mp*} { *for**$country**in**$countries* *let**$organizations*:=*function*(*$country*){*$doc*//*organization*[*members*/*/@country/*=*$country*/*/@id/*]/*/@abbrev/*//string/(.)} *order by**$country*/*/@name/* *return* <country>{*$country*/*/@name/*,*$organizations*(*$country*)}</country> }}
Which runtime error do you get? I guess the group by binds your let variable for each country in the group so perhaps
head($organizations)($country)
would suffice without redefining the variable.
It would be an odd way to solve it, I would prefer to have a
declare function local:organizations($country)
declared before.
On Mon, Dec 19, 2022 at 10:43:25PM +0100, Leo Studer scripsit:
In the code below I encounter the problem, that I have to define the same xPath function variable $organizations twice and I do not understand why.
In line 10, you've used a let clause to bind a variable of type function to the name organizations. That's in context a specific FLOWR expression.
The expression in the return clause of that FLOWR expression returns an element constructor where the contents expression contains another FLOWR expression. The first definition on line 10 will be out of scope in there.
You could define the function with scope for the module:
declare function local:getOrganizations($in as element(country)) as xs:string* { let $thisCountry as xs:string := $in/@id/string(); return $doc//organization[members/@country = $thisCountry]/@abbrev/string(.) };
And then use it in both places with the single definition.
On 12/19/2022 10:58 PM, Graydon wrote:
On Mon, Dec 19, 2022 at 10:43:25PM +0100, Leo Studer scripsit:
In the code below I encounter the problem, that I have to define the same xPath function variable $organizations twice and I do not understand why.
In line 10, you've used a let clause to bind a variable of type function to the name organizations. That's in context a specific FLOWR expression.
The expression in the return clause of that FLOWR expression returns an element constructor where the contents expression contains another FLOWR expression. The first definition on line 10 will be out of scope in there.
I don't think so, the variable is not out of scope, it is bound for each item in the group to that function, meaning that value in the return clause of the group is a sequence of functions and that way
head($organizations)($country)
would work.
You could define the function with scope for the module:
declare function local:getOrganizations($in as element(country)) as xs:string* { let $thisCountry as xs:string := $in/@id/string(); return $doc//organization[members/@country = $thisCountry]/@abbrev/string(.) };
That is certainly the cleaner way.
I think the reason is a different one - it is related to the precise semantics of the group by clause. The group by clause maps each group of input tuples with equal grouping key(s) to a single output tuple, in which- the grouping variable(s) is/are bound to the key values (as you would expect)- all other variables are bound to the concatenated sequence of values bound to the variable name in the members of the group (which may cause surprises) So when a group has, say, 3 members, the variable $organisations is not any more bound to a single function item, but to a sequence of 3 function items (which are identical). Of course, a sequence of more than one function item cannot be called - only a single function item can be called. In order to check that this is the correct interpretation of the issue, you can remove the second assignment to $organisations, and append to the invocation the predicate [1] - then it works, as then you have again a single function item: <country>{$country/@name, $organizations[1]($country)}</country>
One way to handle this in a more straightforward way is to shift the function item into an additional, outer FLWOR level at the beginning. While we are at it, let's shift both function items: let $organizations := function($country){ $doc//organization[members/@country = $country/@id]/@abbrev/string(.)}let $membershipPotentcy := function($orgs){ if (count($orgs)le 0) then 'none' else if (count($orgs) le 5) then 'few' else 'many'}return element Memberships { for $countries in $doc//country group by $mp:= $membershipPotentcy($organizations($countries)) order by $mp return element {$mp} { for $country in $countries return <country>{$country/@name, $organizations($country)}</country> }}
Am Montag, 19. Dezember 2022 um 22:58:30 MEZ hat Graydon graydonish@gmail.com Folgendes geschrieben:
On Mon, Dec 19, 2022 at 10:43:25PM +0100, Leo Studer scripsit:
In the code below I encounter the problem, that I have to define the same xPath function variable $organizations twice and I do not understand why.
In line 10, you've used a let clause to bind a variable of type function to the name organizations. That's in context a specific FLOWR expression.
The expression in the return clause of that FLOWR expression returns an element constructor where the contents expression contains another FLOWR expression. The first definition on line 10 will be out of scope in there.
You could define the function with scope for the module:
declare function local:getOrganizations($in as element(country)) as xs:string* { let $thisCountry as xs:string := $in/@id/string(); return $doc//organization[members/@country = $thisCountry]/@abbrev/string(.) };
And then use it in both places with the single definition.
Thank you, Hans-Jürgen, for shedding really insightful light (if there is such a thing) on this aspect of XQuery grouping, which I use gropingly (pun intended) without fully understanding it.
On 19.12.2022 23:19, Hans-Juergen Rennau wrote:
I think the reason is a different one - it is related to the precise semantics of the group by clause.
The group by clause maps each group of input tuples with equal grouping key(s) to a single output tuple, in which
- the grouping variable(s) is/are bound to the key values (as you would
expect)
- all other variables are bound to the concatenated sequence of values
bound to the variable name in the members of the group (which may cause surprises)
So when a group has, say, 3 members, the variable $organisations is not any more bound to a single function item, but to a sequence of 3 function items (which are identical). Of course, a sequence of more than one function item cannot be called - only a single function item can be called. In order to check that this is the correct interpretation of the issue, you can remove the second assignment to $organisations, and append to the invocation the predicate [1] - then it works, as then you have again a single function item:
<country>{$country/@name, $organizations[1]($country)}</country>
One way to handle this in a more straightforward way is to shift the function item into an additional, outer FLWOR level at the beginning. While we are at it, let's shift both function items:
let $organizations := function($country){ $doc//organization[members/@country = $country/@id]/@abbrev/string(.)} let $membershipPotentcy := function($orgs){ if (count($orgs)le 0) then 'none' else if (count($orgs) le 5) then 'few' else 'many'} return element Memberships { for $countries in $doc//country group by $mp:= $membershipPotentcy($organizations($countries)) order by $mp return element {$mp} { for $country in $countries return <country>{$country/@name, $organizations($country)}</country> }}
Am Montag, 19. Dezember 2022 um 22:58:30 MEZ hat Graydon graydonish@gmail.com Folgendes geschrieben:
On Mon, Dec 19, 2022 at 10:43:25PM +0100, Leo Studer scripsit:
In the code below I encounter the problem, that I have to define the
same xPath function variable $organizations twice and I do not understand why.
In line 10, you've used a let clause to bind a variable of type function to the name organizations. That's in context a specific FLOWR expression.
The expression in the return clause of that FLOWR expression returns an element constructor where the contents expression contains another FLOWR expression. The first definition on line 10 will be out of scope in there.
You could define the function with scope for the module:
declare function local:getOrganizations($in as element(country)) as xs:string* { let $thisCountry as xs:string := $in/@id/string(); return $doc//organization[members/@country = $thisCountry]/@abbrev/string(.) };
And then use it in both places with the single definition.
-- Graydon Saunders | graydonish@gmail.com mailto:graydonish@gmail.com Þæs oferéode, ðisses swá mæg. -- Deor ("That passed, so may this.")
Thank you for your kind words, Gerrit! Until a few weeks ago, my use of grouping was a groping one also in my case - until I learned from the spec that the FLWOR mechanics are only fully understood if we regard the clauses as a pipeline of stations, each one transforming an input tuple stream into an output tuple stream - that's the key. And that's at first weird, as we are used to think in terms of "assignments", "loops", sorting, grouping, filtering, counting devices. But when thinking of the different clauses as just variants of a single idea - "tuple stream transformer" - everything is decomposed into tiny bits - the details of how a particular kind of clause maps its input tuples to output tuples. No interaction whatsoever between the clauses, apart from output tuple streams becoming input tuple streams. Suddenly it is not amazing any more that you can have any number of any kind of clauses, e.g. several where clauses, and that the clauses can occur at any position within the expression (except for the constraint that the first clause must be let or for). And so, for example, I had always kept my fingers crossed when adding an order by after a group by. Now I understand that once the group by has done its thing and produced an output tuple stream according to its rules, you have a tuple stream without any memory of what has happened at earlier clauses. Am Montag, 19. Dezember 2022 um 23:48:45 MEZ hat Imsieke, Gerrit, le-tex gerrit.imsieke@le-tex.de Folgendes geschrieben:
Thank you, Hans-Jürgen, for shedding really insightful light (if there is such a thing) on this aspect of XQuery grouping, which I use gropingly (pun intended) without fully understanding it.
On 19.12.2022 23:19, Hans-Juergen Rennau wrote:
I think the reason is a different one - it is related to the precise semantics of the group by clause.
The group by clause maps each group of input tuples with equal grouping key(s) to a single output tuple, in which
- the grouping variable(s) is/are bound to the key values (as you would
expect)
- all other variables are bound to the concatenated sequence of values
bound to the variable name in the members of the group (which may cause surprises)
So when a group has, say, 3 members, the variable $organisations is not any more bound to a single function item, but to a sequence of 3 function items (which are identical). Of course, a sequence of more than one function item cannot be called - only a single function item can be called. In order to check that this is the correct interpretation of the issue, you can remove the second assignment to $organisations, and append to the invocation the predicate [1] - then it works, as then you have again a single function item:
<country>{$country/@name, $organizations[1]($country)}</country>
One way to handle this in a more straightforward way is to shift the function item into an additional, outer FLWOR level at the beginning. While we are at it, let's shift both function items:
let $organizations := function($country){ $doc//organization[members/@country = $country/@id]/@abbrev/string(.)} let $membershipPotentcy := function($orgs){ if (count($orgs)le 0) then 'none' else if (count($orgs) le 5) then 'few' else 'many'} return element Memberships { for $countries in $doc//country group by $mp:= $membershipPotentcy($organizations($countries)) order by $mp return element {$mp} { for $country in $countries return <country>{$country/@name, $organizations($country)}</country> }}
Am Montag, 19. Dezember 2022 um 22:58:30 MEZ hat Graydon graydonish@gmail.com Folgendes geschrieben:
On Mon, Dec 19, 2022 at 10:43:25PM +0100, Leo Studer scripsit:
> In the code below I encounter the problem, that I have to define the same xPath function variable $organizations twice and I do not understand why.
In line 10, you've used a let clause to bind a variable of type function to the name organizations. That's in context a specific FLOWR expression.
The expression in the return clause of that FLOWR expression returns an element constructor where the contents expression contains another FLOWR expression. The first definition on line 10 will be out of scope in there.
You could define the function with scope for the module:
declare function local:getOrganizations($in as element(country)) as xs:string* { let $thisCountry as xs:string := $in/@id/string(); return $doc//organization[members/@country = $thisCountry]/@abbrev/string(.) };
And then use it in both places with the single definition.
-- Graydon Saunders | graydonish@gmail.com mailto:graydonish@gmail.com Þæs oferéode, ðisses swá mæg. -- Deor ("That passed, so may this.")
PS. Pardon - a correction. The first clause may also be a sliding window or a tumbling window. Am Dienstag, 20. Dezember 2022 um 08:44:02 MEZ hat Hans-Juergen Rennau hrennau@yahoo.de Folgendes geschrieben:
Thank you for your kind words, Gerrit! Until a few weeks ago, my use of grouping was a groping one also in my case - until I learned from the spec that the FLWOR mechanics are only fully understood if we regard the clauses as a pipeline of stations, each one transforming an input tuple stream into an output tuple stream - that's the key. And that's at first weird, as we are used to think in terms of "assignments", "loops", sorting, grouping, filtering, counting devices. But when thinking of the different clauses as just variants of a single idea - "tuple stream transformer" - everything is decomposed into tiny bits - the details of how a particular kind of clause maps its input tuples to output tuples. No interaction whatsoever between the clauses, apart from output tuple streams becoming input tuple streams. Suddenly it is not amazing any more that you can have any number of any kind of clauses, e.g. several where clauses, and that the clauses can occur at any position within the expression (except for the constraint that the first clause must be let or for). And so, for example, I had always kept my fingers crossed when adding an order by after a group by. Now I understand that once the group by has done its thing and produced an output tuple stream according to its rules, you have a tuple stream without any memory of what has happened at earlier clauses. Am Montag, 19. Dezember 2022 um 23:48:45 MEZ hat Imsieke, Gerrit, le-tex gerrit.imsieke@le-tex.de Folgendes geschrieben:
Thank you, Hans-Jürgen, for shedding really insightful light (if there is such a thing) on this aspect of XQuery grouping, which I use gropingly (pun intended) without fully understanding it.
On 19.12.2022 23:19, Hans-Juergen Rennau wrote:
I think the reason is a different one - it is related to the precise semantics of the group by clause.
The group by clause maps each group of input tuples with equal grouping key(s) to a single output tuple, in which
- the grouping variable(s) is/are bound to the key values (as you would
expect)
- all other variables are bound to the concatenated sequence of values
bound to the variable name in the members of the group (which may cause surprises)
So when a group has, say, 3 members, the variable $organisations is not any more bound to a single function item, but to a sequence of 3 function items (which are identical). Of course, a sequence of more than one function item cannot be called - only a single function item can be called. In order to check that this is the correct interpretation of the issue, you can remove the second assignment to $organisations, and append to the invocation the predicate [1] - then it works, as then you have again a single function item:
<country>{$country/@name, $organizations[1]($country)}</country>
One way to handle this in a more straightforward way is to shift the function item into an additional, outer FLWOR level at the beginning. While we are at it, let's shift both function items:
let $organizations := function($country){ $doc//organization[members/@country = $country/@id]/@abbrev/string(.)} let $membershipPotentcy := function($orgs){ if (count($orgs)le 0) then 'none' else if (count($orgs) le 5) then 'few' else 'many'} return element Memberships { for $countries in $doc//country group by $mp:= $membershipPotentcy($organizations($countries)) order by $mp return element {$mp} { for $country in $countries return <country>{$country/@name, $organizations($country)}</country> }}
Am Montag, 19. Dezember 2022 um 22:58:30 MEZ hat Graydon graydonish@gmail.com Folgendes geschrieben:
On Mon, Dec 19, 2022 at 10:43:25PM +0100, Leo Studer scripsit:
> In the code below I encounter the problem, that I have to define the same xPath function variable $organizations twice and I do not understand why.
In line 10, you've used a let clause to bind a variable of type function to the name organizations. That's in context a specific FLOWR expression.
The expression in the return clause of that FLOWR expression returns an element constructor where the contents expression contains another FLOWR expression. The first definition on line 10 will be out of scope in there.
You could define the function with scope for the module:
declare function local:getOrganizations($in as element(country)) as xs:string* { let $thisCountry as xs:string := $in/@id/string(); return $doc//organization[members/@country = $thisCountry]/@abbrev/string(.) };
And then use it in both places with the single definition.
-- Graydon Saunders | graydonish@gmail.com mailto:graydonish@gmail.com Þæs oferéode, ðisses swá mæg. -- Deor ("That passed, so may this.")
Hans-Juergen, thanks for this insight, that sheds even more light for me into the grouping with xquery...
On 20 Dec 2022, at 08:42, Hans-Juergen Rennau hrennau@yahoo.de wrote:
Thank you for your kind words, Gerrit! Until a few weeks ago, my use of grouping was a groping one also in my case - until I learned from the spec that the FLWOR mechanics are only fully understood if we regard the clauses as a pipeline of stations, each one transforming an input tuple stream into an output tuple stream - that's the key. And that's at first weird, as we are used to think in terms of "assignments", "loops", sorting, grouping, filtering, counting devices. But when thinking of the different clauses as just variants of a single idea - "tuple stream transformer" - everything is decomposed into tiny bits - the details of how a particular kind of clause maps its input tuples to output tuples. No interaction whatsoever between the clauses, apart from output tuple streams becoming input tuple streams. Suddenly it is not amazing any more that you can have any number of any kind of clauses, e.g. several where clauses, and that the clauses can occur at any position within the expression (except for the constraint that the first clause must be let or for). And so, for example, I had always kept my fingers crossed when adding an order by after a group by. Now I understand that once the group by has done its thing and produced an output tuple stream according to its rules, you have a tuple stream without any memory of what has happened at earlier clauses.
Am Montag, 19. Dezember 2022 um 23:48:45 MEZ hat Imsieke, Gerrit, le-tex gerrit.imsieke@le-tex.de Folgendes geschrieben:
Thank you, Hans-Jürgen, for shedding really insightful light (if there is such a thing) on this aspect of XQuery grouping, which I use gropingly (pun intended) without fully understanding it.
On 19.12.2022 23:19, Hans-Juergen Rennau wrote:
I think the reason is a different one - it is related to the precise semantics of the group by clause.
The group by clause maps each group of input tuples with equal grouping key(s) to a single output tuple, in which
- the grouping variable(s) is/are bound to the key values (as you would
expect)
- all other variables are bound to the concatenated sequence of values
bound to the variable name in the members of the group (which may cause surprises)
So when a group has, say, 3 members, the variable $organisations is not any more bound to a single function item, but to a sequence of 3 function items (which are identical). Of course, a sequence of more than one function item cannot be called - only a single function item can be called. In order to check that this is the correct interpretation of the issue, you can remove the second assignment to $organisations, and append to the invocation the predicate [1] - then it works, as then you have again a single function item:
<country>{$country/@name, $organizations[1]($country)}</country>
One way to handle this in a more straightforward way is to shift the function item into an additional, outer FLWOR level at the beginning. While we are at it, let's shift both function items:
let $organizations := function($country){ $doc//organization[members/@country = $country/@id]/@abbrev/string(.)} let $membershipPotentcy := function($orgs){ if (count($orgs)le 0) then 'none' else if (count($orgs) le 5) then 'few' else 'many'} return element Memberships { for $countries in $doc//country group by $mp:= $membershipPotentcy($organizations($countries)) order by $mp return element {$mp} { for $country in $countries return <country>{$country/@name, $organizations($country)}</country> }}
Am Montag, 19. Dezember 2022 um 22:58:30 MEZ hat Graydon <graydonish@gmail.com mailto:graydonish@gmail.com> Folgendes geschrieben:
On Mon, Dec 19, 2022 at 10:43:25PM +0100, Leo Studer scripsit:
In the code below I encounter the problem, that I have to define the
same xPath function variable $organizations twice and I do not understand why.
In line 10, you've used a let clause to bind a variable of type function to the name organizations. That's in context a specific FLOWR expression.
The expression in the return clause of that FLOWR expression returns an element constructor where the contents expression contains another FLOWR expression. The first definition on line 10 will be out of scope in there.
You could define the function with scope for the module:
declare function local:getOrganizations($in as element(country)) as xs:string* { let $thisCountry as xs:string := $in/@id/string(); return $doc//organization[members/@country = $thisCountry]/@abbrev/string(.) };
And then use it in both places with the single definition.
-- Graydon Saunders | graydonish@gmail.com mailto:graydonish@gmail.com <mailto:graydonish@gmail.com mailto:graydonish@gmail.com> Þæs oferéode, ðisses swá mæg. -- Deor ("That passed, so may this.")
basex-talk@mailman.uni-konstanz.de