XQuery: how to properly append , in for loop - xquery

So I have xml data like this:
<PhoneNumber>213-512-7457</PhoneNumber>
<PhoneNumber>213-512-7465</PhoneNumber>
and with this XQuery:
<PhoneNumberList>
{
for $phone in $c//PhoneNumber
let $phoneStr := ""
return concat($phoneStr, $phone)
}
</PhoneNumberList>
I get:
<PhoneNumberList>213-512-7457213-512-7465</PhoneNumberList>
But I actually want:
<PhoneNumberList>213-512-7457, 213-512-7465</PhoneNumberList>
Could someone shed some light on how to do this?

<PhoneNumberList>
{
string-join($c//PhoneNumber, ", ")
}
</PhoneNumberList>

There seems to be a lot of confusion with variables in XQuery. A let expression creates a new variable each time it is evaluated, so the "procedural" approaches below will not work.
Whilst the string-join solution is the best in your case, the correct way to write this "manually" is with a recursive function:
declare function local:join-numbers($numbers)
{
concat($numbers[1], ", ", local:join-numbers(substring($numbers,2)))
};
<PhoneNumberList>
{
local:joinNumbers($c//PhoneNumber)
}
</PhoneNumberList>

Ok, something like this should return the first element as-is and the rest with prepended ", "
<PhoneNumberList>
{
for $phone in $c//PhoneNumber[0]
return $phone
for $phone in $c//PhoneNumber[position()>0]
return concat(", ", $phone)
}
</PhoneNumberList>

What about this?
let $phoneStr := ""
<PhoneNumberList>
{
for $phone in $c//PhoneNumber
let $result = concat($phoneStr, $phone)
let $phoneStr = ", "
return $result
}
</PhoneNumberList>

Related

XQuery - Enclosing sequence of elements under a single node

I'm using xquery with BaseX to attempt to retrieve information from JMeter test cases (.jmx files) in a format I desire.
This is the code I'm actually running (inside BaseX GUI):
let $rlist := db:list("JMeter")
for $resource in $rlist
let $rcontent := db:open("JMeter", $resource)
let $ret :=
<HttpRequest>
{
for $item in $rcontent/jmeterTestPlan//HTTPSamplerProxy
return
(<method>
{
$item/stringProp[attribute()/string() = "HTTPSampler.method"]/text()
}
</method>,
<path>
{
let $parse := $item/stringProp[attribute()/string() = "HTTPSampler.path"]/text()
let $parse := fn:replace($parse, "\?[^/]*", "")
let $parse := fn:replace($parse, "\$[^/]*", "\${}")
let $parse := fn:replace($parse, "[0-9]+", "\${}")
return $parse
}
</path>,
<resource>
{
$resource
}
</resource>)
}
</HttpRequest>
return
<HttpRequests>
{
$ret
}
</HttpRequests>
The resulting xml looks like this:
<HttpRequests>
<HttpRequest>
<method>POST</method>
<path>/config</path>
<resource>CentralConfiguration/Requests/addConfiguration.jmx</resource>
</HttpRequest>
</HttpRequests>
<HttpRequests>
<HttpRequest>
<method>POST</method>
<path>/propertyType</path>
<resource>CentralConfiguration/Requests/addPropertyType.jmx</resource>
</HttpRequest>
</HttpRequests>
...
This is what I expect the result to look like:
<HttpRequests>
<HttpRequest>
<method>POST</method>
<path>/config</path>
<resource>CentralConfiguration/Requests/addConfiguration.jmx</resource>
</HttpRequest>
<HttpRequest>
<method>POST</method>
<path>/propertyType</path>
<resource>CentralConfiguration/Requests/addPropertyType.jmx</resource>
</HttpRequest>
...
</HttpRequests>
I'd much appreciate help in formatting my xquery to get the expected result, thanks
The problem was solved by placing the outside for loop inside the first label.

XQuery transform string into a datetime (exist-db)

I am using XQuery 3.0 for exist-db, and am trying to make the string $value a dateTime object. This is what I've got, but it's not working:
let $value := '"2001-10-18T08:47:00"'
if ($key = 'start_time')
then
element { $key } { xs:dateTime(string(replace($value, '"', ''))) }
It's saying: illegal lexical form for date-time-like value ''. Any ideas?
I'm unable to reproduce the error you're getting with that code - which is incomplete (lacks return and else clauses, doesn't define $key). But paring those issues away, your code runs fine for me, as follows:
let $value := '"2001-10-18T08:47:00"'
return
xs:dateTime(replace($value, '"', ''))
The result:
2001-10-18T08:47:00

XQuery with if condition in for loop

I have written xquery to return results in normal way.
let $results := //data:data
return
<result>
{
for $i in $results
return
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
}
</result>
Now, I have to filter out the results in for loop with some condition like
(pseudo logic)
if id = 'abc' and status ="closed"
then skip the row
else add row.
I have tried several ways. but could not run the query..
Try this:
<result>
{
for $i in //data:data
where fn:not($i/DATA:ID = 'abc' and $i/#status = "closed")
return
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
}
</result>
Note that the XPath //data:data may have a lot of work to do, but that's a separate matter.
You Can also use if condition instead of where
<result>
{
for $i in //data:data
return
if($i/DATA:ID != 'abc' and $i/#status != "closed")
then
(
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
)
else ()
}
</result>

How can I use XQuery using a for loop inside another for loop?

When parsing any XML like below, in order to fetch the PATH/TEST_PATH and ALIAS,
<TEST TEST_VERSION="14">
<TEST_QUERY_LIST PROP="0">
<TEST_QUERY_FILE>"C:/Files/File.txt"</TEST_QUERY_FILE>
</TEST_QUERY_LIST>
<TEST_OUTPUT>
<FILE PATH="C:/Files/File2.txt" TEST_TYPE_ID_REF="1">
<USED_BY TEST_PATH="C:/Files/File2.txt" ALIAS="thisData"/>
<USED_BY TEST_PATH="C:/Files/File3.txt" ALIAS="thatData"/>
</FILE>
<FILE PATH="C:/Files/File1.txt" TEST_TYPE_ID_REF="1">
<USED_BY TEST_PATH="C:/Files/File5.txt" ALIAS="Data"/>
</FILE>
</TEST_OUTPUT>
</TEST>
I write a query like
.....setQuery(
"for $x in doc($xmlByteArry)//TEST/TEST_OUTPUT/FILE"
" return if($x/#TEST_TYPE_ID_REF = 1) "
" then ($x/USED_BY/#TEST_PATH, $x/USED_BY/#ALIAS, $x/#PATH ) "
" else ()");
But the problem is that the output is random, ie: I can not associate the outputs into a relation.
I would be happy to get the output where PATH, TEST_PATH and ALIAS are related
Something like ::
"C:/Files/File2.txt", "C:/Files/File2.txt", "thisData"
"C:/Files/File2.txt", "C:/Files/File3.txt", "thatData"
"C:/Files/File1.txt", "C:/Files/File5.txt", "Data"
After some thought I try to modify it like this
.....setQuery(
"for $x in doc($xmlByteArry)//TEST/TEST_OUTPUT/FILE"
" for $y in $x/USED_BY "
" return if($x/#TEST_TYPE_ID_REF = 1) "
" then concat ($y/#TEST_PATH, ':::', $y/#ALIAS, ':::', $x/#PATH ) "
" else ()");
I get an exception when this query is evaluated. Any hint why it fails to recognize the XML ? I use the following code (C++/Qt)
QString getParsedString()
{
QByteArray xmlByteArry;
QBuffer device(&xmlByteArry);
device.setData(XmlTree().toUtf8());
device.open(QIODevice::ReadOnly);
QXmlQuery query;
query.bindVariable("xmlByteArry", &device);
// query.setQuery("for $x in doc($xmlByteArry)//TEST/TEST_OUTPUT/FILE"
// " return if($x/#TEST_TYPE_ID_REF = 1) "
// " then ($x/USED_BY/#TEST_PATH,':::',$x/USED_BY/#ALIAS,':::',$x/#PATH) "
// " else ()");
query.setQuery("for $x in doc($xmlByteArry)//TEST/TEST_OUTPUT/FILE"
" for $y in $x/USED_BY "
" return if($x/#TEST_TYPE_ID_REF = 1) "
" then concat($y/#TEST_PATH,':::',$y/#ALIAS,':::',$x/#PATH)"
" else ()");
QString values;
if(query.isValid())
{
QXmlResultItems result;
query.evaluateTo(&result);
QXmlItem XmlItem(result.next());
while (!XmlItem.isNull())
{
//Code to parse the Internal ones
if(XmlItem.isAtomicValue())
{
//I hope some values will be atomic like ALIAS and TEST_PATH
values.append(XmlItem.toAtomicValue().toString());
}
else if (XmlItem.isNode())
{
//I hope to get the FILE Node and USED_BY node but I dont see anything in the following stringValue.
QXmlNodeModelIndex Index = XmlItem.toNodeModelIndex();
values.append(Index.stringValue());
}
XmlItem = result.next();
}
}
return values;
}

having trouble incorporating css into perl cgi

the code below is a cgi file and I am having problems displaying the image and style from an external css file. The code is in lines 18-28 and I'm not sure what I am doing wrong. I would appreciate any help.
#!/usr/bin/perl -w
use strict;
use DBI;
use CGI;
use CGI::Carp('fatalsToBrowser');
my $query = new CGI;
print $query->header();
my $my_database = "TrypSnoDB";
my $localhost = "localhost";
my $dsn = "DBI:mysql:$my_database:$localhost";
my $db_user_name = "adrian";
my $db_password = "temp_pass";
my $dbh = DBI->connect("DBI:mysql:database=TrypSnoDB;host=localhost;mysql_socket=/private/software/mysql/mysql.sock","adrian","temp_pass", {'RaiseError' => 1});
print "<html>\n";
print "<head>\n";
print "<title>Welcome to the T. Brucei snoRNA Database</title>\n";
print "<link type='text/css' rel='stylesheet' href='/public_html/style.css'>\n";
print "</head>\n";
print "<body>\n";
print "<h1>Trypanosomatid snoRNA Database</h1>\n";
print "<img class='my_images' src='/public_html/tb_pic1.png'>\n";
print "</body>\n";
print "</html>\n";
if ($query->param('submit1')){
my $orig_sno = $query->param('snorna1');
my $family = $query->param('family1');
my $query_type = $query->param('target_option1');
my $target = $query->param('target_name1');
if ($orig_sno eq "Trypanosoma brucei") {
$orig_sno = 1;
}
elsif ($orig_sno eq "Leishmania major") {
$orig_sno = 7;
}
elsif ($orig_sno eq "ALL") {
$orig_sno = "1 or ST.org_id=7";
}
if ($family eq "ALL") {
$family = "'C/D' or ST.family='H/ACA'";
}
else {
$family = "'$family'";
}
if ($target ne "ALL") {
$family = "$family and T.target_name='$target'";
}
my($db_query,$common_tar,$exp_ver_sno,$exp_ver_tar,$total);
$db_query = "SELECT ST.sno_name,T.target_name,T.location,T.base_pair,SM.annotated_seq FROM sno_Table ST,sno_Modifications SM,Targets T WHERE ST.sno_id=SM.sno_id and SM.mod_id=T.target_id and (ST.org_id=$orig_sno) and (ST.family=$family)";
$common_tar="and T.target_id in(SELECT T.target_id FROM sno_Table ST,sno_Modifications SM,Targets T WHERE ST.sno_id=SM.sno_id and SM.mod_id=T.target_id group by T.target_id having count(*)=2) order by T.location desc";
$exp_ver_sno="and ST.exper_ver='Y'";
$exp_ver_tar = "and T.exp_ver='Y'";
if ($query_type eq "snoRNAs with common targets") {
$db_query=$db_query.$common_tar;
}
elsif ($query_type eq "Experimentally verified snoRNAs") {
$db_query=$db_query.$exp_ver_sno;
}
elsif ($query_type eq "snoRNAs with experimentally verified targets") {
$db_query=$db_query.$exp_ver_tar;
}
elsif ($query_type eq "ALL"){
$db_query=$db_query;
}
my $sth = $dbh->prepare($db_query);
$sth->execute();
my$total = $sth->rows;
print "<table border=1>\n
<tr>
<th>snoRNA</th>\n
<th>Target Name</th>\n
<th>Target Location</th>\n
<th>Target Base Pair</th>\n
<th>Annotated Sequence</th>\n
</tr>\n";
while (my#row = $sth->fetchrow_array()){
my$sno_name = $row[0];
my$tar_name = $row[1];
my$tar_loc = $row[2];
my$tar_bp = $row[3];
my$annotated_seq = $row[4];
print "<tr>\n<td>$sno_name</td><td>$tar_name</td><td>$tar_loc</td><td>$tar_bp</td><td>$annotated_seq</td></tr>\n";
}
print "<tr>
<th>TOTAL</th>\n
<th>$total</th>\n
</tr>\n";
print "</table>";
}
Your problem is almost certainly that you have the wrong URL for the CSS file. You can confirm that by looking in the web server error log and seeing if there is a 404 record for the CSS request.
Unfortunately I can't tell you what the correct URL is as I have no idea how your web server is configured.
There are a couple of other issues that you might want to address though:
The HTML that you generate is invalid. You print the tags outside of the and tags. Printing raw HTML within your Perl program is a terrible idea - it's far too easy to make the kinds of errors that you have here. You would be far better advised to use a templating engine (I recommend the Template Toolkit).
Your database queries are prone to SQL injection attacks. Please switch to using bind variables before someone trashes your server.
this is in response to Dave Cross comment regarding the SQL statement building. To convert the statement build to use bindings appears to be fairly straightforward in order to prevent an SQL injection.
To use placeholder bindings I think the OP only needs to replace the variables $orig_sno and $family in the $db_query variable with the ? character. like so:
$db_query = "SELECT ST.sno_name,T.target_name,T.location,T.base_pair,SM.annotated_seq
FROM sno_Table ST,sno_Modifications SM,Targets T WHERE ST.sno_id=SM.sno_id and
SM.mod_id=T.target_id and (ST.org_id=?) and (ST.family=?)"; # one line
...
my $sth = $dbh->prepare($db_query);
$sth->execute($orig_sno, $family);
However as the $family variable is built possibly out of a previous conditional, a further variable $target is also in play.
if ($family eq "ALL") {
$family = "'C/D' or ST.family='H/ACA'";
}
else {
$family = "'$family'";
}
if ($target ne "ALL") {
$family = "$family and T.target_name='$target'";
}
Will the placeholder handle this interpolated variable? Or would the $target variable also require its own placeholder?
And is this all that would be required to do to deter would be SQL injection attacks in this case?
solved. If the $target variable did require its own place holder, a few adjustments to the conditionals would do the trick.
else {
$family = "'$family'";
}
# removed - if $target ne ALL conditonal
my($db_query,$common_tar,$exp_ver_sno,$exp_ver_tar,$total);
$db_query = "SELECT ... and (ST.org_id=?) and (ST.family=?)";
if ($target ne "ALL") {
$db_query =~ s/\)$//;
$db_query .= ' and T.target_name=?)';
}
$common_tar="and T.target_id ... ";
...
my $sth = $dbh->prepare($db_query);
if ($target ne 'ALL'){
$sth->execute($orig_sno, $family, $target);
else{
$sth->execute($orig_sno, $family);
}

Resources