Xquery For Complex payload - xquery

I have the input request like below
<Input>
<BIKey></BIKey>
<BusinessObjects>
<BusinessObject>
<BusinessIdentifiers>
<BusinessIdentifier>
<BKey>BuCode</BKey>
<BValue>CDC</BValue>
</BusinessIdentifier>
<BusinessIdentifier>
<BKey>BuType</BKey>
<BValue>123</BValue>
</BusinessIdentifier>
<BusinessIdentifier>
<BKey>CsmNo</BKey>
<BValue>857895</BValue>
</BusinessIdentifier>
</BusinessIdentifiers>
<BusinessAttributes>
<BusinessAttribute>
<BKey>Version</BKey>
<BValue>1</BValue>
</BusinessAttribute>
<BusinessAttribute>
<BKey>date</BKey>
<BValue>2018-06-28</BValue>
</BusinessAttribute>
</BusinessAttributes>
</BusinessObject>
<BusinessObject>
<BusinessIdentifiers>
<BusinessIdentifier>
<BKey>BuCode</BKey>
<BValue>CDC</BValue>
</BusinessIdentifier>
<BusinessIdentifier>
<BKey>BuType</BKey>
<BValue>123</BValue>
</BusinessIdentifier>
<BusinessIdentifier>
<BKey>CsmNo</BKey>
<BValue>34567</BValue>
</BusinessIdentifier>
</BusinessIdentifiers>
<BusinessAttributes>
<BusinessAttribute>
<BKey>Version</BKey>
<BValue>1</BValue>
</BusinessAttribute>
<BusinessAttribute>
<BKey>date</BKey>
<BValue>2018-06-28</BValue>
</BusinessAttribute>
</BusinessAttributes>
</BusinessObject>
</BusinessObjects>
</Input>
and i would like to have the <BIKey> value should be all the values of <BValue> in for each <BusinessObject> separated by ':'
for the above example, the <BIKey> Values should be populated like below
<BIKey>CDC:123:857895:1:2018-06-28</BIKey>
<BIKey>CDC:123:34567:1:2018-06-28</BIKey>
i have tried like below
<BIKey>
{
string-join(
for $bo in Input/BusinessObjects/BusinessObject return string-join($bo/BusinessIdentifiers/BusinessIdentifier/BValue, '|'),
':'
)
}
</BIKey>
But i am not meeting the exact requiremnt. Kindly suggest how to proceed.
Thanks

Looks like this is what you want :
for $bo in Input/BusinessObjects/BusinessObject return <BIKey>{string-join($bo//BValue, ':')}</BIKey>
You can try it here.
Your XQuery code had the following problems :
you created a single <BIKey> while you expected one per BusinessObject => move the <BIKey> tag in the return of the FLWOR instead of outside it
you had a string-join using | as a separator
you only joined the BValues inside BusinessIdentifiers, but you also wanted those inside BusinessAttributes

Related

Exist-db Add node with XQUERY

I have a listPers.xml (TEI List containing persons, obviously ) . I want to write a function to update the listPers.xml
My function looks like this:
declare function app:addPerson($node as node(), $model as map(*)) {
let $person := "<person xml:id=""><persName><forename>Albert</forename><surname>Test</surname></persName></person>"
let $list := doc(concat($config:app-root, '/resources/listPers_test.xml'))
return
update insert $person into $list//tei:listPerson
};
And the listPerson.xml
looks more or less like a typical list with person-entries
I have a tei:header (here omitted) followed by
<text>
<body>
<listPerson xml:id="person">
<person xml:id="abbadie_jacques">
<persName ref="http://d-nb.info/gnd/100002307">
<forename>Jacques</forename>
<surname>Abbadie</surname>
</persName>
<note>Prediger der französisch-reformierten Gemeinde in <rs type="place" ref="#berlin">Berlin</rs>
</note>
</person>
</body>
</text>
</TEI>
(sorry for ruining indentions, it's just an excerpt )
I do not get an error, which means that my app:addPerson should be fine, right?
I want the listPers_test to look like this:
<text>
<body>
<listPerson xml:id="person">
<person xml:id="abbadie_jacques">
<persName ref="http://d-nb.info/gnd/100002307">
<forename>Jacques</forename>
<surname>Abbadie</surname>
</persName>
<note>Prediger der französisch-reformierten Gemeinde in <rs type="place" ref="#berlin">Berlin</rs>
</note>
</person>
<!-- here comes the output that I wish to have :-) -->
<person xml:id=""><persName><forename>Albert</forename><surname>Test</surname></persName></person>
</body>
</text>
</TEI>
In the long run, I aim for an html-form that allows users to input names etc., where ids are generated using sth like
to-lowercase(concat($surname, "_", $forename));
But I will not get into my questions regarding forms and xquery, as I have barely done a quick Google-trip regarding html forms and xquery!
Can anyone hint me at why I do not get the listPers_test.xml file updated with the second value? :-)
All the best and thanks in advance to everyone,
K
Alright, I have a solution for anyone interested in it:
My first snippet $person:= ... contains a STRING, not an element.Changing the line
let $person := "<person xml:id=""><persName><forename>Albert</forename><surname>Test</surname></persName></person>"
to this one actually solves the issue:
let $person := <tei:person xml:id=""><persName><forename>Albert</forename><surname>Test</surname></persName></tei:person>

How to match space in MarkLogic using CTS functions?

I need to search those elements who have space " " in their attributes.
For example:
<unit href="http:xxxx/unit/2 ">
Suppose above code have space in the last for href attribute.
I have done this using FLOWER query. But I need this to be done using CTS functions. Please suggest.
For FLOWER query I have tried this:
let $x := (
for $d in doc()
order by $d//id
return
for $attribute in data($d//#href)
return
if (fn:contains($attribute," ")) then
<td>{(concat( "id = " , $d//id) ,", data =", $attribute)}</td>
else ()
)
return <tr>{$x}</tr>
This is working fine.
For CTS I have tried
let $query :=
cts:element-attribute-value-query(xs:QName("methodology"),
xs:QName("href"),
xs:string(" "),
"wildcarded")
let $search := cts:search(doc(), $query)
return fn:count($search)
Your query is looking for " " to be the entirety of the value of the attribute. If you want to look for attributes that contain a space, then you need to use wildcards. However, since there is no indexing of whitespace except for exact value queries (which are by definition not wildcarded), you are not going to get a lot of index support for that query, so you'll need to run this as a filtered search (which you have in your code above) with a lot of false positives.
You may be better off creating a string range index on the attribute and doing value-match on that.

goquery insert newline after text found

I am using "github.com/PuerkitoBio/goquery" to parse numbers from inside 'value' tag in the html document like below
<tab>
<value>1,2,3</value>
<value>2,4,6</value>
<value>5,6,7</value>
</tab>
and what I got with code snippet below is 1,2,32,4,65,6,7 so without newline which is not what I want . I need multiple 3 'values'(to append each of them later to slice) not one
func parseGoQuery(b io.Reader) {
doc, err := goquery.NewDocumentFromReader(b)
fmt.Println(doc.Find("tab").Find("value").Text())
}
try this:
doc.Find("tab").Find("value").Each(func(_ int, value *goquery.Selection) {
fmt.Println(value.Text())
})
The above code iterates over all value elements, and then print the text of each element in one line, which is exactly what you want.

XQuery "flattening" an element

I am extracting data from an XML file and I need to extract a delimited list of sub-elements. I have the following:
for $record in //record
let $person := $record/person/names
return concat($record/#uid/string()
,",", $record/#category/string()
,",", $person/first_name
,",", $person/last_name
,",", $record/details/citizenships
,"
")
The element "citizenships" contains sub-elements called "citizenship" and as the query stands it sticks them all together in one string, e.g. "UKFrance". I need to keep them in one string but separate them, e.g. "UK|France".
Thanks in advance for any help!
fn:string-join($arg1 as xs:string*, $arg2 as xs:string) is what you're looking for here.
In your currently desired usage, that would look something like the following:
fn:string-join($record/details/citizenships/citizenship, "|")
Testing outside your document, with:
fn:string-join(("UK", "France"), "|")
...returns:
UK|France
Notably, ("UK", "France") is a sequence of strings, just as a query returning multiple citizenships would likewise be a sequence (the entries in which will be evaluated for their string value when passed to fn:string-join(), which is typed as taking a sequence of strings for its first argument).
Consider the following (simplified) query:
declare context item := document { <root>
<record uid="1">
<person>
<citizenships>
<citizenship>France</citizenship>
<citizenship>UK</citizenship>
</citizenships>
</person>
</record>
</root> };
for $record in //record
return concat(fn:string-join($record//citizenship, "|"), "
")
...and its output:
France|UK

XQuery equality of attributes in node and subnodes

I have this piece of XML-code:
<player name="John" points="50">
<game points="5">Beans</game>
<game points="40">Cucumbers</game>
<game points="50">Tomatos</game>
</player>
What I want to do is to get this piece, but only with those games where number of points (attribute of "game") is equal to points which is attribute of "player".
Thus, considering above example, I should get next XML-piece:
<player name="John" points="50">
<game points="50">Tomatos</game>
</player>
I write following XQuery:
for $a in doc("ex.xml")
where $a/xs:int(#points)=$a/game/xs:int(#points)
return $a
But I don't get any result. Could you please help me?
You cannot modify/filter a subtree by selecting parts of it (if not using XQuery Update). You will have to reconstruct the XML instead.
element player {
/player/#*,
/player/game[#points=/player/#points]
}
The first line creates a new element, the second line adds the attributes again, and the third line all games that fulfill the points condition.
If you've got multiple players in a document which you need to loop over, the code would look like that:
for $player in /player
return element player {
$player/#*,
$player/game[#points=$player/#points]
}
Now, we do not start all queries at the root level, but use the $player as context instead.
Using XQuery Update (if supported by your XQuery processor), you could also do something like this (actually not changing the original document, but only a copy):
copy $result := .
modify delete node $result//game[../#points != #points]
return $result

Resources