Xquery - Count elements - xquery

My XML schema is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE buildings[
<!ELEMENT buildings (building*)>
<!ELEMENT building (department+, name, type+, year)>
<!ATTLIST building id ID #REQUIRED>
<!ELEMENT department (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT year (#PCDATA)>
]>
<buildings>
<building id='B1'>
<department>CSE</department>
<department>EE</department>
<name>Hoch</name>
<type>Teaching</type>
<year>1900</year>
</building>
<building id='B2'>
<department>CSE</department>
<name>HSC</name>
<type>Library</type>
<year>1900</year>
</building>
<building id='B3'>
<department>EE</department>
<name>Hoch</name>
<type>Teaching</type>
<year>1990</year>
</building>
<building id='B4'>
<department>Arts</department>
<name>NSC</name>
<type>Laboratory</type>
<year>1900</year>
</building>
</buildings>
I want to count departments which have buildings made in 1900. I want my output as:
CSE 2
EE 1
Arts 1
I have tried a few things and this the best I got. I am new to Xquery.
let $depts := doc("buildings.xml")/buildings/building/department
let $dept := doc("buildings.xml")/buildings/building[year="1900"]/department
for $x in distinct-values($dept)
let $count := count($x=(data($depts)))
return ($x, $count)
And the result I am getting is:
CSE 1 EE 1 Arts 1
What changes should I make to my query in order to get the result.
TIA.

XQuery since 2014 supports a group by clause you can use:
for $dept at $pos in //building[year = '1900']/department
group by $key := $dept
order by $pos[1]
return ($key || ' ' || count($dept))
https://xqueryfiddle.liberty-development.net/gWcDMen
If you don't have XQuery 3 support you can use
let $dept := //building[year = '1900']/department
for $dist-dept in distinct-values($dept)
return
concat($dist-dept, ' ', count($dept[. = $dist-dept]))
https://xqueryfiddle.liberty-development.net/gWcDMen/1

Related

map single source schema element to multiple element with attribute

I have a source schema as follows:
Users
Id
Name
Department
and destination schema as follows:
Employee
Name
Number
number_type(attribute)
I need to do the following mapping
Name ---> Name
Id ---> Number (number_type = "Id")
Department_no --> Number (number_type = "dept")
I need to map both Id and department number i.e. 2 elements to 1 element i.e. Number in dest schema but for both attribute value should be different.
Input
<Users>
<Id>123</Id>
<Name>abc</Name>
<Department_no>456</Department_no>
</Users>
Output:
<Employee>
<Name>abc</Name>
<Number number_type = "Id">123</Number>
<Number number_type = "dept">456</Number>
</Employee>
How can I achieve in BizTalk or what could be the inline XSLT for the same?
You nearly have it
Name ---> Name
Id ---> Number
Department --> Number
Then also link both Id & Department to a looping functoid that goes to Number
Plus to do the attributes there are some things you can try such as
Id ---> number_type
Department --> number_type
But click on the links select Copy Name instead of the Copy Text value.
From an input
<ns0:Root xmlns:ns0="http://Scratch.SO55049939Input">
<Users>
<Id>Id_0</Id>
<Name>Name_0</Name>
<Department>Department_0</Department>
</Users>
</ns0:Root>
You will get output
<ns0:Root xmlns:ns0="http://Scratch.SO55049939output">
<Employee>
<Name>Name_0</Name>
<Number number_type="Id">Id_0</Number>
<Number number_type="Department">Department_0</Number>
</Employee>
</ns0:Root>

Best way by pass parameter in service layer

I have a Student info table with column Name, Sex,Class, Course, Grade.. etc
I would like to implement a filter, require search by name, by class,by course,by grade ....
I using mybatis by querying the DB.. the query is very simple. But I have to write multiple query to implement these search function.
Select * FROM TB where name =#{name} ,
Select * FROM TB where class =#{class},
Select * FROM TB where course =#{course},
Select * FROM TB where grade =#{grade}
....
In service layer,
Multiple functions need to implement.
List<Student> getStudentByName(String name);
List<Student> getStudentByCourse(String course);
... bla bla ...
Question:
is that any simple way to implement this requirement?
what I expected is :
in #service layer, a function can handle all the pass parameter,
like:
List search( someParam );
I come out with an idea is using in mybatis.
SELECT * FROM TB WHERE 1 =1
<if name!=null>
AND name = #{name}
</if>
I'm not sure if this is a good practice for approaching.
yes it can
you can do that . refer the below syntax in xml :
SELECT * FROM TB WHERE 1 = 1
<if name != null and name!= ""'>
AND name = #{name}
</if>
<if class != null and class!= ""'>
AND class = #{class}
</if>
<if course != null and course!= ""'>
AND course = #{course}
</if>
<if grade != null and grade != ""'>
AND grade = #{grade}
</if>

Xquery: Counting the number of occurrences of a term in each record within a set of records

Given a set of xml records and a set of terms $terms . The terms in $terms sequence are extracted from the set of records. I want to count the number of occurrences of each term in each paragraph record. I used the following code to do so:
for $record in /rec:Record
for $term in $terms
return xdmp:unquote(concat('<info>',string(count(lower-case($record/rec:paragraph )[. = lower-case($term)])), '</info>'))
For each term in each record i got 0 count:
Example: $term:='Mathematics', $record/rec:paragraph:='Mathematics is the study of topics such as quantity'
I want the number of occurances of the term Mathematics in $record/rec:paragraph
Any idea of what caused this result? Is there any other way to count the number of occurrences of each of the terms in each paragraph.
Use tokenize() to split up the input string into word tokens. Then the counting itself is trivial. For example:
let $text := 'Mathematics is the study of topics such as quantity'
let $myterms := 'mathematics'
let $wds := tokenize($text, '\s+')
for $t in $myterms
return <term name="{$t}">{count($wds[lower-case(.)=lower-case($t)])}</term>
Returns this:
<term nm="mathematics">1</term>

XQuery get distinct values after tokenize?

I want to get the distinct countries of all mountains, but sometimes a mountain will be in more than one country as indicated by having multiple country codes in a string like this:
<mountain id="mount-Kangchendzonga" country="NEP IND"></mountain>
I can get all the distinct strings associated with a country using
let $mts := doc("mondial.xml")/mondial//mountain
let $countries := distinct-values(data($mts/#country))
But this isn't quite correct because if I had one mountain with country="NEP IND" and another with country="NEP" these would be recognized as distinct.
let $countries := distinct-values(concat(' ', data($mts/#country)))
let $countries := distinct-values(tokenize(data($mts/#country), "\s+"))
Is there a way I could first split up a string of a country by white space, and then get the distinct values of these? I have tried using distinct-values on concatenated and tokenized data like I showed above, but both result in errors with the compiler.
This is one possible way to combine tokenize() and distinct-values() to get the distinct country names :
let $all-countries :=
for $c in $mts/#country
return tokenize($c, "\s+")
let $distinct-countries := distinct-values($all-countries)
xpathtester.com demo
Or in XQuery 3.1, as suggested in comment below :
($mts/#country ! tokenize(., '\s+')) => distinct-values()

XQuery - to insert splitted nodes data into another node

<?xml version="1.0" encoding="UTF-8"?>
<Data>
<A><DelInfo>123-20150308-345</DelInfo><OrderNo>11</OrderNo></A>
<A><DelInfo>1204-20150308-355</DelInfo><OrderNo>15</OrderNo></A>
<A><DelInfo>153-20150408-343</DelInfo><OrderNo>10</OrderNo></A>
<A><DelInfo>44345-20150308-341</DelInfo><OrderNo>21</OrderNo></A>
<A><DelInfo>153-20150204-245</DelInfo><OrderNo>1</OrderNo></A>
<A><DelInfo>423-20150311-445</DelInfo><OrderNo>13</OrderNo></A>
..........
</Data>
I receive following XML. The DelInfo node contains a combination of
EmpId, Delivery Date and Receipt No. The OrderNo node contains the
order number wrt the Delivery Information.
The XML is stored in BaseX and I need following report to be generated from the
above XML.
<A><DelInfo>123-20150308-345</DelInfo><OrderNo>11</OrderNo><Report>20150308 - 11</Report></A>
.....
In other word, I want to insert an additional node Report with Date and Order No.
Any idea?
Replace yourdoc with your document name.
for $x in doc('yourdoc')//A
let $d := substring-before(substring-after($x/DelInfo, "-"), "-")
let $o := $x/OrderNo/text()
let $i := <C>{concat($d, " - ", $o)}</C>
return
insert node $i after $x/OrderNo
The inner substring-after() will return the string after the first -. Then, the substring-before() will return the string before the -. This way you will get the Date portion.

Resources