XQuery - Enclosing sequence of elements under a single node - xquery

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.

Related

Need to compare array in Marklogic with xquery

I need to compare array in MarkLogic with Xquery .
Query parameters:
{
"list": {
"bookNo": 13,
"BookArray":[20,21,22,23,24,25]
}
}
Sample Data:
{
"no":01'
"arrayList"[20,25]
}
{
"no":02'
"arrayList"[20,27]
}
{
"no":03'
"arrayList"[20,23,25]
}
Output:
"no":01
"no":03
I need to return "no" where all values from arraylist should be match with bookArray.
Ok. You do not explain if the actual data is in the system or not. So I did an example as if it is all in memory.
I chose to keep the sample in the MarkLogic JSON representation which has some oddities like number-nodes and array-nodes under the hood. To make it more readable if you dig into it, i used fn:data() to get less verbose. In all reality, if this was an in-memory operation and I could not use Javascript, then I would have converted the JSON structures to maps.
Here is a sample to help you explore. I cleaned up the JSON to be valid and for my sample wrapped the three samples in a single array.
xquery version "1.0-ml";
let $param-as-json := xdmp:unquote('{
"list": {
"bookNo": 13,
"BookArray":[20,21,22,23,24,25]
}
}')
let $list-as-json := xdmp:unquote('[
{
"no":"01",
"arrayList":[20,25]
},
{
"no":"02",
"arrayList":[20,27]
},
{
"no":"03",
"arrayList":[20,23,25]
}
]')
let $my-list := fn:data($param-as-json//BookArray)
return for $item in $list-as-json/*
let $local-list := fn:data($item//arrayList)
let $intersection := fn:data($item//arrayList)[.=$my-list]
where fn:deep-equal($intersection, $local-list)
return $item/no
Result:
01
03

Getting the JSON output in an object format for a single record instead of an array using XQuery

I am getting JSON output from an API in an array format(for multiple records) and in object format for a single record. Instead consumer wants to maintain one format - as an array for single record also. Please let me know if there is a way to display the JSON output in array format irrespective of single/multiple records using XQuery
I have tried the below XQuery:
<Response>
<totalSize>{totalSize/number()}</totalSize>
<done>{done/text()}</done>
<nextRecordsUrl>{nextRecordsUrl/text()}</nextRecordsUrl>
{
let $input:=/root/records
for $i in $input
return
<records>
<Product1>{$i/Product_Lookup_BI__c/text()}</Product1>
<EventLastModifiedDate>{$i/LastModifiedDate/text()}</EventLastModifiedDate>
<Venue>{$i/Venue_vod__r/(fn:concat(fn:concat(fn:concat(fn:concat(fn:concat(fn:concat(BI_Country_Code__c/text(),'-'),State_Province_vod__c/text()),'-'),City_vod__c/text()),'-'),Address_vod__c/text()))}</Venue>
{
let $a:=$i/EM_Event_Team_Member_vod__r/records
for $y in $a
return
<User_records>
<AttendeeLastModifiedDate>{$y/LastModifiedDate/text()}</AttendeeLastModifiedDate>
<EmployeeName>{$y/Team_Member_vod__r/Name/text()}</EmployeeName>
<EmployeeID>{$y/Team_Member_vod__r/BIDS_ID_BI__c/text()}</EmployeeID>
</User_records>
}
</records>
}
</Response>
Actual Output from the above XQuery:
{
"Response": {
"totalSize": 1,
"done": true,
"nextRecordsUrl": "",
"records": {
"Product1": "12345",
"EventLastModifiedDate": "2021-11-10T01:30:55.000+0000",
"Venue": "UK",
"User_records": {
"AttendeeLastModifiedDate": "2021-11-08T02:55:03.000+0000",
"EmployeeName": "Ish",
"EmployeeID": "00002113152"
}
}
}
}
Expected Output:
The Output should be in an array format for "records" & "user_records"
{
"Response":{
"totalSize":1,
"done":true,
"nextRecordsUrl":"",
"records":[
{
"Product1":"12345",
"EventLastModifiedDate":"2021-11-10T01:30:55.000+0000",
"Venue":"UK",
"User_records":[
{
"AttendeeLastModifiedDate":"2021-11-08T02:55:03.000+0000",
"EmployeeName":"Ish",
"EmployeeID":"00002113152"
}
]
}
]
}
}
Try:
<User_records xmlns:json="http://www.json.org" json:array="true">
<AttendeeLastModifiedDate>{$y/LastModifiedDate/text()}</AttendeeLastModifiedDate>
<EmployeeName>{$y/Team_Member_vod__r/Name/text()}</EmployeeName>
<EmployeeID>{$y/Team_Member_vod__r/BIDS_ID_BI__c/text()}</EmployeeID>
</User_records>
I would do the same for <records> as well. This example works in eXist-db. The JSON namespace may be different in your environment.
Here is what I ran in eXide:
xquery version "3.1";
declare option exist:serialize "method=json indent=yes media-type=application/json";
<Response>
<totalSize>5</totalSize>
<done>yes</done>
<nextRecordsUrl>abc</nextRecordsUrl>
<User_records xmlns:json="http://www.json.org" json:array="true">
<AttendeeLastModifiedDate>123</AttendeeLastModifiedDate>
<EmployeeName>456</EmployeeName>
<EmployeeID>789</EmployeeID>
</User_records>
</Response>

Import data using mlcp, a csv file and transform parse date

I have a csv file. I've managed import these data into MarkLogic using mlcp which then created a xml file in MarkLogic.
Now in csv I have this format "6/29/2013 5:00:00 PM" random in one of the column. How do I use xquery and probably node-replace as a transform function to convert this date into a different format such as "2013-06-29" as MarkLogic default date format?
Any help is appreciated...
I have created transform.xqy and install it on Modules in MLogic. I'm
thinking about using "xdmp:node-replace" to replace the date with expected
format. Or should I go thorugh the csv column by column (How to do?) and
use "castable as xs:dateTime" to determine date value or not. Yet, even
just printing out the content value/uri, always giving me error.
xquery version "1.0-ml";
module namespace example = "http://test.com/example";
(: If the input document is XML, insert #NEWATTR, with the value
: specified in the input parameter. If the input document is not
: XML, leave it as-is.
:)
declare function example:transform(
$content as map:map,
$context as map:map
) as map:map*
{
let $the-doc-uri := map:get($content, "uri")
let $the-doc := map:get($content, "value")
return
trace($the-doc, 'The value of doc is: ')
};
The MarkLogic documentation contains a full example of an MLCP transform:
https://docs.marklogic.com/guide/mlcp/import#id_65640
It shows this example, which adds an attribute to the XML content:
declare function example:transform(
$content as map:map,
$context as map:map
) as map:map*
{
let $attr-value :=
(map:get($context, "transform_param"), "UNDEFINED")[1]
let $the-doc := map:get($content, "value")
return
if (fn:empty($the-doc/element()))
then $content
else
let $root := $the-doc/*
return (
map:put($content, "value",
document {
$root/preceding-sibling::node(),
element {fn:name($root)} {
attribute { fn:QName("", "NEWATTR") } {$attr-value},
$root/#*,
$root/node()
},
$root/following-sibling::node()
}
), $content
)
};
Keep in mind you are supposed to update the "value" property of the $content map:map, and return $content to get your transformation result added to the database. I suggest using a (potentially recursive) typeswitch to identify element nodes, and then adjusting their value accordingly..
HTH!
finally did it.
The thing is I must use mem:node-replace because it is on the fly, on memory. While xdmp:node-replace is when the data is already on MarkLogic.
The rest is as expected I must use format-date and xdmp:parse-dateTime to get date format as required.
Here is some snippets
xquery version "1.0-ml";
module namespace ns_transform = "this_is_my_namespace";
import module namespace mem = "http://xqdev.com/in-mem-update" at "/MarkLogic/appservices/utils/in-mem-update.xqy";
declare variable $ns := "this_is_my_namespace";
declare function ns_transform:transform(
$content as map:map,
$context as map:map
) as map:map*
{
let $doc := map:get($content, "value")
let $format_in := "[M]/[D]/[Y0001] [h01]:[m01]:[s01] [P]"
let $format_out := "[Y0001]-[M01]-[D01]"
let $old_date := $doc/*:root_doc/*:date/text()
let $new_date := format-date(xs:date(xdmp:parse-dateTime($format_in, $old_date)), $format_out)
let $new_doc := mem:node-replace($doc/*:root_doc/*:date,element {fn:QName($ns, "date")}{$new_date})
let $_ := map:put($content, "value", $new_doc)
return $content
};

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>

XQuery: how to properly append , in for loop

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>

Resources