How to iterate XQuery Path? - xquery

<Response xmlns="https://**********/token">
<rows>
<e>
<e>1,4507</e>
<e>1</e>
<e>2017-02-28T05:10:32.0606654Z</e>
<e>2017-02-28T05:10:32.2267838Z</e>
<e>Depart</e>
<e>Depart</e>
<e>acct: xxx on 10/2/2015</e>
<e>16.666131292069704</e>
<e null="true"/>
<e>Open</e>
<e null="true"/>
<e null="true"/>
<e null="true"/>
</e>
<e>
<e>1,4507</e>
<e>2</e>
<e>2017-02-28T05:10:32.0606654Z</e>
<e>2017-02-28T05:10:32.2267838Z</e>
<e>Depart</e>
<e>Depart</e>
<e>acct: xxx on 10/2/2015</e>
<e>16.666131292069704</e>
<e null="true"/>
<e>Open</e>
<e null="true"/>
<e null="true"/>
<e null="true"/>
</e>
</rows>
<skip>0</skip>
<take>126</take>
</Response>
The above is my xml response:
I wanted to verify each row that status is Open.
Open
declare namespace ns1='https://**********/token';
<Result>
for $x in //ns1:Response[1]/ns1:rows[1]
return data($x/ns1:e[1]/ns1:e[10]/text())
</Result>
but it is not returning the value.. It just gives the following in the expected result.
<Result>for $x in //ns1:Response[1]/ns1:rows[1]
return data($x/ns1:e[1]/ns1:e[2]/text())</Result>
Is it correct xquery expression? would be helpful if some check this?

You should wrap your XQuery expression in {} otherwise it will be treated as string literal :
declare namespace ns1='https://**********/token';
<Result>
{
for $x in //ns1:Response[1]/ns1:rows[1]
return data($x/ns1:e[1]/ns1:e[10]/text())
}
</Result>
demo
output :
<Result>Open</Result>
It isn't clear what is the expected output exactly though, since you've been using exact index on every path step which will cause the expression to match only one element at a time...

If you just want to know that every row has e[10] = "Open" then you can do:
<Result>{empty(/ns1:Rows/*/*[10][.!="Open"])}</Result>
(Your question title talks of iterating. That's procedural thinking. In XQuery, just like in SQL, you generally operate on sets of items directly: you don't need to iterate over them to process them one at a time.)

Related

Xquery in Marklogic to return datetime less than 1 month from the current date

Delete documents less than 30 days in Marklogic Xquery
I was searching over the internet to find how to return documents less than 1 month old from the current date and i found the above link. Since there are no range indexes for the element i am using
let $currentdt := fn:current-dateTime()
let $amonthAgo := $currentdt- xs:yearMonthDuration("P1M")
cts:search(doc(),
cts:element-query(xs:QName("myDateTimeField"), cts:true-query())
)[.//myDateTimeField[xs:dateTime(.) eq $amonthAgo]]
returns nothing.
I have sample values such as
2021-05-18T06:32:34.761729-04:00
2021-05-15T06:32:34.761729-04:00
2021-03-19T06:32:34.761729-04:00
2021-06-15T06:32:34.761729-04:00
TIA!
Aside from a missing return for the FLWOR expression, you are asking for all matches that are exactly one month old, because you are using eq in the comparison. If you want to find documents that are younger than one month, you want their date to be after the point in time exactly one month ago (i.e., $amonthAgo). You can correct and simplify the predicate to xs:dateTime(myDateTimeField) gt $amonthAgo. Here is a stand-alone example that works for me:
declare context item := document {
<doc>
<e id="1">
<myDateTimeField>2021-05-18T06:32:34.761729-04:00</myDateTimeField>
</e>
<e id="2">
<myDateTimeField>2021-05-15T06:32:34.761729-04:00</myDateTimeField>
</e>
<e id="3">
<myDateTimeField>2021-03-19T06:32:34.761729-04:00</myDateTimeField>
</e>
<e id="4">
<myDateTimeField>2021-06-15T06:32:34.761729-04:00</myDateTimeField>
</e>
</doc>
};
let $currentdt := fn:current-dateTime()
let $amonthAgo := $currentdt - xs:yearMonthDuration("P1M")
return //e[xs:dateTime(myDateTimeField) gt $amonthAgo]
Result (on 2021-06-19):
<e id="4">
<myDateTimeField>2021-06-15T06:32:34.761729-04:00</myDateTimeField>
</e>

Gremlin connected components prints one by one

I'm newbie to the gremlin QL, My requirement to generate the connected components on huge graph. I tried the below query but it's printing as a group of values but I need to print one by one.
Connected components Query:
g.V().emit(cyclicPath().or().not(both())).repeat(both()).until(cyclicPath()).path().aggregate("p").unfold().dedup().map(__.as("v").select("p").unfold().filter(unfold().where(eq("v"))).unfold().dedup().order().by(id).fold()).dedup()
[v[89826185]]
[v[89826188], v[89826189], v[89826190], v[89826191], v[89826192], v[89826193], v[89826194]]
[v[89826195], v[89826196], v[89826198]]
I need to print the values like below way.
min-id of group(list) to each element of the group(list).
Ex:
89826188 89826189
89826188 89826190
89826188 89826191
89826188 89826192
89826188 89826193
89826188 89826194
89826188 89826188 (self)
You could do that in your application's code. Doing it at the query level will only blow up the result size, but here you go:
g.V().
emit(cyclicPath().or().not(both())).
repeat(both()).
until(cyclicPath()).
path().aggregate("p").
unfold().dedup().
map(__.as("v").select("p").unfold().
filter(unfold().where(eq("v"))).
unfold().dedup().
order().
by(id).
fold()).
dedup().as("list").
unfold().
map(union(select("list").
by(limit(local, 1)),
identity()).
id().fold())
It's basically the same query, I only added the final map() step to reformat the result.

XQuery Match assertion on SOAPUI

I have a soap testStep in SOAPUI with an XQuery match.
The XML (simplified) look as follows:
<root>
<element>
<a>a</a>
<b>b</b>
<c>c</c>
<d>d</d>
</element>
</root>
I want to make an XQuery to get all child nodes from <element> removing a child element depending on his node name. My XQuery looks like:
for $x in //root/element/element()
return
if (name($x) != 'a') then $x
else ""
I expect the next result:
<b>b</b>
<c>c</c>
<d>d</d>
I think that my XQuery is correct, I tested with an XQuery online evaluator and looks ok, you can try with the follow link
However when I use this expression in a XQuery Match assertion in SOAPUI I get the following message: More than one match in current response. How can achieve this with SOAPUI?
Thanks,
Doing some tries finally I found the solution, the way to do this XQuery in SOAPUI is specifying a root node in the XQuery expression i.e:
<MyResult>
{
for $x in //root/element/element()
return
if (name($x) != 'a') then $x
else ""
}
</MyResult>

how to change the XML structure using XQuery

I have a XML file containing Employees Name and the Job done by them.
The structure of the XML file is -
<Employee>AAA#A#B#C#D</Employee>
<Employee>BBB#A#B#C#D</Employee>
<Employee>CCC#A#B#C#D</Employee>
<Employee>DDD#A#B#C#D</Employee>
There are thousands of records and I have to change structure to -
<Employee>
<Name>AAA</Name>
<Jobs>
<Job>A</Job>
<Job>B</Job>
<Job>C</Job>
<Job>D</Job>
</Jobs>
</Employee>
How to get this done using XQuery in BaseX ?
3 XQuery functions, substring-before, substring-after and tokenize are used to get
the required output.
substring-before is used to get the Name.
Similarly, the substring-after is used to get the Job portion.
Then the tokenize function, is used to split the Jobs.
let $data :=
<E>
<Employee>AAA#A#B#C#D</Employee>
<Employee>BBB#A#B#C#D</Employee>
<Employee>CCC#A#B#C#D</Employee>
<Employee>DDD#A#B#C#D</Employee>
</E>
for $x in $data/Employee
return
<Employee>
{<Name>{substring-before($x,"#")}</Name>}
{<Jobs>{
for $tag in tokenize(substring-after($x,"#"),'#')
return
<Job>{$tag}</Job>
}</Jobs>
}</Employee>
HTH...
Tokenizing the string is probably easier and faster. tokenize($string, $pattern) splits $string using the regular expression $pattern, head($seq) returns the first value of a sequence and tail($seq) all but the first. You could also use positional predicates of course, but these functions are easier to read.
for $employee in //Employee
let $tokens := tokenize($employee, '[##]')
return element Employee {
element Name { head($tokens) },
element Jobs {
for $job in tail($tokens)
return element Job { $job }
}
}

Find the total number of child elements?

<?xml version="1.0" encoding="UTF-8"?>
<root>
<author>
<name>A</name>
<book>Book1</book>
<book>Book2</book>
</author>
<author>
<name>B</name>
<age>45</age>
<book>Book3</book>
</author>
</root>
How do I write a XQuery to display the total number of books by an author?
One approach is:
let $max-books = max(/root/author/count(book))
return /root/author[count(book) = $max-books]
which will return all authors who have authored a maximum number of books.
As a one liner, this can be simplified to:
/root/author[count(book) = max(/root/author/count(book))]
Another way to do this is:
(for $author in /root/author
order by count($author/book) descending
return $author/name)[1]
which will return an author with the maximum number of books.
#Fox: should you not tag this question with "homework"? ;-)
#Oliver Hallam: IMHO, #Fox wants to list each author together with the amount of books by that author and not the author with the highest amount of books.
Your first query
let $max-books = max(/root/author/count(book))
return /root/author[count(book) = $max-books]
Contains a syntax error. You should use ":=" instead of only "=". Furthermore
#Fox: to find the solution, have a look at FLWOR expressions. You can use the "for" part to select each of the book nodes with an XPath expression and bind each node to a $book variable. Then use the "let" part to define two variables ($authorName and $amountOfBooks) using the $book as "starting point". Last, use the "return" part to define the output format you need for the resulting XML.

Resources