How to perform a 'join' with a sub element in XQuery - xquery

Im not sure how to word what I am trying to do, but I am trying to get all touples of FID, BID, and Something. Consider the following XML:
<FOO>
<FID>f1</FID>
<NAME>f1</NAME>
<BAR>
<BID>b1</BID>
<SOMETHING>15</SOMETHING>
</BAR>
<BAR>
<BID>b2</BID>
<SOMETHING>25</SOMETHING>
</BAR>
</FOO>
<FOO>
<FID>f2</FID>
<NAME>f2</NAME>
<BAR>
<BID>b1</BID>
<SOMETHING>35</SOMETHING>
</BAR>
<BAR>
<BID>b3</BID>
<SOMETHING>0</SOMETHING>
</BAR>
</FOO>
What I need is:
b1 f1 15
b1 f2 35
b2 f1 25
b3 f2 0
Anyone know the syntax that I would use?
I tried:
for $foo in /root/FOO
for $bar in /root/FOO/BAR
let $fid := $foo/FID/text() where $foo/BAR/BID/text()=$bar/BID/text()
let $bid := $foo/BAR/BID/text() where $foo/BAR/BID/text()=$bar/BID/text()
let $something := $foo/BAR/SOMETHING/text() where $foo/BAR/BID/text()=$bar/BID/text()

If you'd wanted to order by the <FID/> elements first, it'd be as easy as looping over the <FOO/>s, for each of them over its <BAR/>s and dumping a string each time:
for $foo in /root/FOO
for $bar in /BAR
return string-join(($foo/FID, $bar/BID, $bar/SOMETHING), ' ')
For grouping by <BID/>s, you have to loop over those first and collect the other information relatively:
for $bar in //BAR
order by $bar/BID
return string-join(($bar/BID, $bar/../FID, $bar/SOMETHING), ' ')
A small remark (as I wasn't able to run your code without major cleanup): $foo and $FOO are not the same, XQuery is case sensitive. Furthermore, you're missing a return clause.

Related

XQuery how to count with "where" condition

I'm just starting to learn XQuery and I want that it shows me the number of festivals with genre (genero) is the same as "metal". I can't get the total number of them, only separately.
Xquery
for $b in //festival
where $b/#genero="Metal"
return <prueba>{count ($b/#genero="Metal"), $b//nombre}</prueba>
XML
<Festivales>
<festival genero="Metal">
<informacion>
<nombre>Resurrection Fest</nombre>
<fecha_inicio>2020-07-01</fecha_inicio>
<fecha_fin>2020-07-04</fecha_fin>
</festival>
<festival genero="Rock-Heavy Metal">
<informacion>
<nombre>Rock the Night</nombre>
<fecha_inicio>2020-06-26</fecha_inicio>
<fecha_fin>2020-06-27</fecha_fin>
</festival>
<festival genero="Hardcore">
<informacion>
<nombre>Ieperfest</nombre>
<fecha_inicio>2020-07-03</fecha_inicio>
<fecha_fin>2020-07-05</fecha_fin>
</informacion>
</festival>
<festival genero="Metal">
<informacion>
<nombre>Download UK</nombre>
<fecha_inicio>2020-06-12</fecha_inicio>
<fecha_fin>2020-06-14</fecha_fin>
</informacion>
</festival>
</Festivales>
Result
<prueba>1<nombre>Resurrection Fest</nombre>
</prueba>
<prueba>1<nombre>Hellfest</nombre>
</prueba>
<prueba>1<nombre>Download UK</nombre>
</prueba>
Thanks!
for $b in //festival[#genero="Metal"]
let $n := $b/informacion/nombre/text()
return
<prueba>
{
<cnt>{count(//festival[#genero="Metal"]/informacion/nombre[. = $n])}</cnt>
, $b/informacion/nombre
}
</prueba>

Return multiple nested dictionaries from Tcl

I have a Tcl proc that creates two dictionaries from a large file. It is something like this:
...
...
proc makeCircuitData {spiceNetlist} {
#read the spiceNetlist file line by line
# create a dict with multilevel nesting called elementMap that will have the following structure:
# elementMap key1 key2 value12
# elementMap keyA keyB valueAB
# and so on
# ... some other code here ...
# create another dict with multilevel nesting called cktElementAttr that will have the following structure:
# cktElementAttr resistor leftVoltageNode1 rightVoltageNode1 resValue11
# cktElementAttr resistor leftVoltageNode2 rightVoltageNode2 resValue12
# cktElementAttr inductor leftVoltageNode2 rightVoltageNode2 indValue11
# cktElementAttr inductor leftVoltageNode2 rightVoltageNode2 indValue12
# cktElementAttr capacitor leftVoltageNode2 rightVoltageNode2 capValue11
# ... so on...
}
I want to return these two nested dictionaries:
cktElementAttr and elementMap from the above types of procedures as these two dictionaries get used by other parts of my program.
What is the recommended way to return two dictionaries from Tcl procs?
Thanks.
This should work:
return [list $cktElementAttr $elementMap]
Then, at the caller, you can assign the return value to a list:
set theDictionaries [makeCircuitData ...]
or assign them to different variables:
lassign [makeCircuitData ...] cEltAttr elmMap
In Tcl 8.4 or older (which are obsolete!), you can (ab)use foreach to do the job of lassign:
foreach {cEltAttr elmMap} [makeCircuitData ...] break
Documentation:
break,
foreach,
lassign,
list,
return,
set

jq select elements with array not containing string

Now, this is somewhat similar to jq: select only an array which contains element A but not element B but it somehow doesn't work for me (which is likely my fault)... ;-)
So here's what we have:
[ {
"employeeType": "student",
"cn": "dc8aff1",
"uid": "dc8aff1",
"ou": [
"4210910",
"4210910 #Abg",
"4210910 Abgang",
"4240115",
"4240115 5",
"4240115 5\/5"
]
},
{
"employeeType": "student",
"cn": "160f656",
"uid": "160f656",
"ou": [
"4210910",
"4210910 3",
"4210910 3a"
] } ]
I'd like to select all elements where ou does not contain a specific string, say "4210910 3a" or - which would be even better - where ou does not contain any member of a given list of strings.
When it comes to possibly changing inputs, you should make it a parameter to your filter, rather than hardcoding it in. Also, using contains might not work for you in general. It runs the filter recursively so even substrings will match which might not be preferred.
For example:
["10", "20", "30", "40", "50"] | contains(["0"])
is true
I would write it like this:
$ jq --argjson ex '["4210910 3a"]' 'map(select(all(.ou[]; $ex[]!=.)))' input.json
This response addresses the case where .ou is an array and we are given another array of forbidden strings.
For clarity, let's define a filter, intersectq(a;b), that will return true iff the arrays have an element in common:
def intersectq(a;b):
any(a[]; . as $x | any( b[]; . == $x) );
This is effectively a loop-within-a-loop, but because of the semantics of any/2, the computation will stop once a match has been found.(*)
Assuming $ex is the list of exceptions, then the filter we could use to solve the problem would be:
map(select(intersectq(.ou; $ex) | not))
For example, we could use an invocation along the lines suggested by Jeff:
$ jq --argjson ex '["4210910 3a"]' -f myfilter.jq input.json
Now you might ask: why use the any-within-any double loop rather than .[]-within-all double loop? The answer is efficiency, as can be seen using debug:
$ jq -n '[1,2,3] as $a | [1,1] as $b | all( $a[]; ($b[] | debug) != .)'
["DEBUG:",1]
["DEBUG:",1]
false
$ jq -n '[1,2,3] as $a | [1,1] as $b | all( $a[]; . as $x | all( $b[]; debug | $x != .))'
["DEBUG:",1]
false
(*) Footnote
Of course intersectq/2 as defined here is still O(m*n) and thus inefficient, but the main point of this post is to highlight the drawback of the .[]-within-all double loop.
Here is a solution that checks the .ou member of each element of the input using foreach and contains.
["4210910 3a"] as $list # adjust as necessary
| .[]
| foreach $list[] as $e (
.; .; if .ou | contains([$e]) then . else empty end
)
EDIT: I now realize a filter of the form foreach E as $X (.; .; R) can almost always be rewritten as E as $X | R so the above is really just
["4210910 3a"] as $list
| .[]
| $list[] as $e
| if .ou | contains([$e]) then . else empty end

xQuery category nesting issue

I have a problem nesting the result tags in each other the right way.
The result should look like this:
aimed result
<categoryA>
<position>...</position>
<position>...</position>
...
</categoryA>
<categoryB>
<position>...</position>
<position>...</position>
...
</categoryB>
currently I have only managed to get the right results for the positions, the categoryA and B are 1 hierarchic layer higher than the positions. the positions should be nested in the categories. The categories can be referenced by let $y := $d/Bilanz/Aktiva/* (respectively $d$d/Bilanz/Aktiva/LangfristigesVermoegen and $d$d/Bilanz/Aktiva/KurzfristigesVermoegen).
Here is my query:
query
let $d := doc('http://etutor.dke.uni-linz.ac.at/etutor/XML?id=5001')/Bilanzen
let $a02 := $d/Bilanz[#jahr='2002']/Aktiva/*
let $a03 := $d/Bilanz[#jahr='2003']/Aktiva/*
for $n02 in $a02//* , $n03 in $a03//*
(:
where name($n02) = name($n03)
where node-name($n02) = node-name($n03)
:)
where name($n02) = name($n03)
return <position name="{node-name($n02)}">
<j2002>{data($n02/#summe)}</j2002>
<j2003>{data($n03/#summe)}</j2003>
<diff>{data($n03/#summe) - data($n02/#summe)}</diff>
</position>
xml
<Bilanzen>
<Bilanz jahr="2002">
<Aktiva>
<LangfristigesVermoegen>
<Sachanlagen summe="1486575.8"/>
<ImmateriellesVermoegen summe="67767.2"/>
<AssoziierteUnternehmen summe="190826.3"/>
<AndereBeteiligungen summe="507692.7"/>
<Uebrige summe="92916.4"/>
</LangfristigesVermoegen>
<KurzfristigesVermoegen>
<Vorraete summe="78830.9"/>
<Forderungen summe="198210.3"/>
<Finanzmittel summe="181102.0"/>
</KurzfristigesVermoegen>
</Aktiva>
<Passiva>
<Eigenkapital>
<Grundkapital summe="91072.4"/>
<Kapitalruecklagen summe="186789.5"/>
<Gewinnruecklagen summe="798176.2"/>
<Bewertungsruecklagen summe="-34922.4"/>
<Waehrungsumrechnung summe="0"/>
<EigeneAktien summe="0"/>
</Eigenkapital>
<AnteileGesellschafter summe="23613.1"/>
<LangfristigeVerb>
<Finanzverbindlichkeiten summe="680007.1"/>
<Steuern summe="36555.8"/>
<Rueckstellungen summe="429286.1"/>
<Baukostenzuschuesse summe="169246.0"/>
<Uebrige summe="36166.9"/>
</LangfristigeVerb>
<KurzfristigeVerb>
<Finanzverbindlichkeiten summe="14614.6"/>
<Steuern summe="65247.6"/>
<Lieferanten summe="94939.2"/>
<Rueckstellungen summe="123664.8"/>
<Uebrige summe="89464.8"/>
</KurzfristigeVerb>
</Passiva>
</Bilanz>
<Bilanz jahr="2003">
<Aktiva>
<LangfristigesVermoegen>
<Sachanlagen summe="1590313.7"/>
<ImmateriellesVermoegen summe="69693.2"/>
<AssoziierteUnternehmen summe="198224.7"/>
<AndereBeteiligungen summe="418489.3"/>
<Uebrige summe="104566.7"/>
</LangfristigesVermoegen>
<KurzfristigesVermoegen>
<Vorraete summe="20609.8"/>
<Forderungen summe="289458.5"/>
<Finanzmittel summe="302445.9"/>
</KurzfristigesVermoegen>
</Aktiva>
<Passiva>
<Eigenkapital>
<Grundkapital summe="91072.4"/>
<Kapitalruecklagen summe="186789.5"/>
<Gewinnruecklagen summe="875723.4"/>
<Bewertungsruecklagen summe="-15459.5"/>
<Waehrungsumrechnung summe="-633.7"/>
<EigeneAktien summe="0"/>
</Eigenkapital>
<AnteileGesellschafter summe="22669.8"/>
<LangfristigeVerb>
<Finanzverbindlichkeiten summe="733990.2"/>
<Steuern summe="68156.8"/>
<Rueckstellungen summe="395997.2"/>
<Baukostenzuschuesse summe="177338.5"/>
<Uebrige summe="38064.9"/>
</LangfristigeVerb>
<KurzfristigeVerb>
<Finanzverbindlichkeiten summe="6634.7"/>
<Steuern summe="97119.1"/>
<Lieferanten summe="89606.0"/>
<Rueckstellungen summe="128237.5"/>
<Uebrige summe="98495.2"/>
</KurzfristigeVerb>
</Passiva>
</Bilanz>
</Bilanzen>
I would really appreciate some help, i have no clue at all. Thank you.
If I understand you correctly, you want the information about LangfristigesVermoegen (and its children) to be grouped in the output under element categoryA, and the information about Kurzfristigesvermoegen to be grouped under categoryB.
So you will want first of all to do something to generate the categoryA and categoryB elements. For example,
let $d := doc(...)/Bilanzen
return (
<categoryA>{ ... children of category A here ... }</categoryA>,
<categoryB>{ ... children of category B here ... }</categoryB>
)
The positions in each category can be generated using code similar to what you've now got, except that instead of iterating over
for $n02 in $a02//* , $n03 in $a03//*
you will need to iterate over $a02[self::LangfristigesVermoegen]/* for category A, and over $a02[self::KurzfristigesVermoegen]/* for category B (and similarly, of course, for $n02 and $n03).
If the set of categories is not static and you just want to group things in the output using the same grouping elements present in the input, then you'll want an outer structure something like this:
for $assetclass1 in $anno2002/*
let $assetclass2 := $anno2003/*[name() = name($assetclass1)]
return
(element {name($assetclass1)} {
for $old in $assetclass1/*,
$new in $assetclass2/*
where name($old) eq name($new)
return <position name="{node-name($old)}">
<j2002>{data($old/#summe)}</j2002>
<j2003>{data($new/#summe)}</j2003>
<diff>{data($new/#summe) - data($old/#summe)}</diff>
</position>
})

how to group result in xquery

Sorry for the title, I didnt know what's the best title for this question
I'm having difficulties with XQUERY. The result is not what I expected
here's the xml
<Equipes>
<Equipe>
<equipeId>1</equipeId>
<equipeNom>Equipe A</equipeNom>
<JoueurEquipe>
<dateDebut>2011-01-01</dateDebut>
<dateFin>2013-01-01</dateFin>
<numero>1</numero>
<joueurId>1</joueurId>
</JoueurEquipe>
<JoueurEquipe>
<dateDebut>2010-01-01</dateDebut>
<dateFin>2012-01-01</dateFin>
<numero>2</numero>
<joueurId>2</joueurId>
</JoueurEquipe>
</Equipe>
<Equipe>
<equipeId>2</equipeId>
<equipeNom>Equipe B</equipeNom>
<JoueurEquipe>
<dateDebut>2009-01-01</dateDebut>
<dateFin>2012-01-01</dateFin>
<numero>1</numero>
<joueurId>3</joueurId>
</JoueurEquipe>
<JoueurEquipe>
<dateDebut>2010-01-01</dateDebut>
<dateFin>2014-01-01</dateFin>
<numero>2</numero>
<joueurId>4</joueurId>
</JoueurEquipe>
</Equipe>
</Equipes>
and here's the query
for $b in doc("ligue.xml")/ligue/Equipes
return
<Equipes>
{
$b/Equipe/equipeId,
$b/Equipe/equipeNom
}
</Equipes>
the result is
<equipes>
<equipeId>1</equipeId>
<equipeId>2</equipeId>
<equipeNom>Equipe A</equipeNom>
<equipeNom>Equipe B</equipeNom>
</equipes>
what I need is
<equipes>
<equipeId>1</equipeId>
<equipeNom>Equipe A</equipeNom>
<equipeId>2</equipeId>
<equipeNom>Equipe B</equipeNom>
</equipes>
I don't know what i'm missing
Thank you
Iterate over the things you actually want your output to iterate over:
<Equipes>{
for $b in doc("ligue.xml")/ligue/Equipes/Equipe[equipeId][equipeNom]
return ($b/equipeId, $b/equipeNom)
}</Equipes>
Now, let's explore why your prior query behaved as it did. This:
for $b in doc("ligue.xml")/ligue/Equipes
return
<Equipes>
{
$b/Equipe/equipeId,
$b/Equipe/equipeNom
}
</Equipes>
...finds the Equipes element, of which there's only one (making the for loop useless). It then searches for $b/Equipe/equipeId and $b/Equipe/equipeNom, both of which evaluate to lists containing multiple elements, and concatenates those two lists to get the output.

Resources