How to dynamically create a search query based on a set of quoted strings in MarkLogic - xquery

I have the following query, where i want to form a string of values from a list and i want to use that comma separated string as an or-query but it does not give any result, however when i return just the concatenated string it gives the exact value needed for the query.
The query is as follows:
xquery version "1.0-ml";
declare namespace html = "http://www.w3.org/1999/xhtml";
declare variable $docURI as xs:string external ;
declare variable $orQuery as xs:string external ;
let $tags :=
<tags>
<tag>"credit"</tag>
<tag>"bank"</tag>
<tag>"private banking"</tag>
</tags>
let $docURI := "/2012-10-22_CSGN.VX_(Citi)_Credit_Suisse_(CSGN.VX)__Model_Update.61198869.xml"
let $orQuery := (string-join($tags/tag, ','))
for $x in cts:search(doc($docURI)/doc/Content/Section/Paragraph, cts:or-query(($orQuery)))
let $r := cts:highlight($x, cts:or-query($orQuery), <b>{$cts:text}</b>)
return <result>{$r}</result>
The exact query that i want to run is :
cts:search(doc($docURI)/doc/Content/Section/Paragraph, cts:or-query(("credit","bank","private banking")))
and when i do
return (string-join($tags/tag, ','))
it gives me exactly what i require
"credit","bank","private banking"
But why does it not return any result in or-query?

The string-join step should not need to be string-join. That passes in a literal string. In xQuery, sequences are your friend.
I think you want to do something like this:
let $tags-to-search := ($tags/tag/text()!replace(., '^"|"$', '') ) (: a sequence of tags :)
cts:search(doc($docURI)/doc/Content/Section/Paragraph, cts:word-query($tags-to-search))
cts:word-query is the default query used for parameter 2 of search if you pass in a string. cts:word query also returns matches for any items in a sequence if presented with that.
https://docs.marklogic.com/cts:word-query
EDIT: Added the replace step for the quotes as suggested by Abel. This is specific to the data as presented by the original question. The overall approach remains the same.

Maybe do you need something like this
let $orQuery := for $tag in $tags/tag return cts:word-query($tag)

I used fn:tokenize instead it worked perfectly for my usecase
its because i was trying to pass these arguments from java using XCC api and it would not return anything with string values
xquery version "1.0-ml";
declare namespace html = "http://www.w3.org/1999/xhtml";
declare variable $docURI as xs:string external ;
declare variable $orQuery as xs:string external ;
let $input := "credit,bank"
let $tokens := fn:tokenize($input, ",")
let $docURI := "2012-11-19 0005.HK (Citi) HSBC Holdings Plc (0005.HK)_ Model Update.61503613.pdf"
for $x in cts:search(fn:doc($docURI), cts:or-query(($tokens)))
let $r := cts:highlight($x, cts:or-query(($tokens)), <b>{$cts:text}</b>)
return <result>{$r}</result>

Related

How to convert string to XPATH in BaseX

How can i convert string into XPATH, below is the code
let $ti := "item/title"
let $tiValue := "Welcome to America"
return db:open('test')/*[ $tiValue = $ti]/base-uri()
Here is one way to solve it:
let $ti := "item/title"
let $tiValue := "Welcome to America"
let $input := db:open('test')
let $steps := tokenize($ti, '/')
let $process-step := function($input, $step) { $input/*[name() = $step] }
let $output := fold-left($input, $steps, $process-step)
let $test := $output[. = $tiValue]
return $test/base-uri()
The path string is split into single steps (item, title). With fold-left, all child nodes of the current input (initially db:open('test')) will be matched against the current step (initially, item). The result will be used as new input and matched against the next step (title), and so on. Finally, only those nodes with $tiValue as text value will be returned.
Your question is very unclear - the basic problem is that you've shown us some code that doesn't do what you want, and you're asking us to work out what you want by guessing what was going on in your head when you wrote the incorrect code.
I suspect -- I may be wrong -- that you were hoping this might somehow give you the result of
db:open('test')/*[item/title = $ti]/base-uri()
and presumably $ti might hold different path expressions on different occasions.
XQuery 3.0/3.1 doesn't have any standard way to evaluate an XPath expression supplied dynamically as a string (unless you count the rather devious approach of using fn:transform() to invoke an XSLT transformation that uses the xsl:evaluate instruction).
BaseX however has an query:eval() function that will do the job for you. See https://docs.basex.org/wiki/XQuery_Module

Is it possible to pass a uri parameter with xi:include in exist-db?

I have an exist-db .xql page where I am wrapping three other .xql pages via xi:include:
(:==========
Declare namespaces
==========:)
declare namespace mwjl = "http://minorworksoflydgate.net/mwjl";
declare namespace m = "http://minorworksofjohnlydgate.net/model";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare namespace xi = "http://www.w3.org/2001/XInclude";
(:==========
Declare global variables to path
==========:)
declare variable $exist:root as xs:string :=
request:get-parameter("exist:root", "xmldb:exist:///db/apps");
declare variable $exist:controller as xs:string :=
request:get-parameter("exist:controller", "/mwjl-app");
declare variable $path-to-data as xs:string :=
$exist:root || $exist:controller || '/data';
declare variable $docPath as xs:string := "paratext/Lydgate_Main.xml";
(:===
Declare variable
===:)
declare variable $work := doc("/db/apps/mwjl-app/data/" || $docPath);
declare variable $TEI as element(tei:TEI)+ := $work/tei:TEI;
<m:page>
<xi:include href="/db/apps/mwjl-app/modules/header.xql"/>
<xi:include href="/db/apps/mwjl-app/modules/body.xql"/>
<xi:include href="/db/apps/mwjl-app/modules/footer.xql"/>
</m:page>
I would like to pass the variable $docPath to the three xi:include's, but when I format the xi:include as follows (for the footer, for example): <xi:include href="/db/apps/mwjl-app/modules/footer.xql?docPath={$docPath}"/>it ceases to produce results. If put a request:get-parameter statement on the target .xqls and include a fallback (so something like declare variable $docPath as xs:string := request:get-parameter('docPath','fallback') on the target xqls) it generates the fallback fine, so it's obvious to me that the function works. I'm just not forming my href correctly in the xi:include statements in the requesting code. But everything I've read suggests this should be how it's formed and I cannot for the life of me figure out where I'm going wrong. Am I mistaken that this is possible in eXist-db, and if I'm not how do I format the xi:include statement?
I would suggest applying the fn:encode-for-uri() function to the $docPath variable:
<xi:include href="/db/apps/mwjl-app/modules/footer.xql?docPath={encode-for-uri($docPath)}"/>
This function encodes any reserved characters in the supplied string by replacing them with their percent-encoded form. So paratext/Lydgate_Main.xml becomes paratext%2FLydgate_Main.xml.
If this doesn't do the trick (sorry, I haven't tested it), I'd suggest adding logging to the target queries, e.g.:
util:log("INFO", "docPath: " || request:get-parameter('docPath', 'no docPath parameter received'))
... which will be visible in your exist.log file when you next call your main query. (And then, if you could add a comment to this reply with, I'll update my suggestion accordingly.)

pad the string with whitespaces to make it of certain length in xquery osb 12 c

I want to pad a string with whitespaces to make it of certain length in XQuery on the OSB platform.
I tried string-join and concat, but none of them pad whitespaces as they consider them as empty string.
Sample input:
<root-element xmlns="">
<string-to-pad>abc</string-to-pad>
</root-element>
**Expected output:**
<root-element>
<paddedString>abc </paddedString>
</root-element>
Yes not much to say without a code sample. This is how the functx library, solves your problem in XQuery. Either import it as a module (its uri is stable), or google the function name.
declare namespace functx = "http://www.functx.com";
declare function functx:pad-string-to-length
( $stringToPad as xs:string? ,
$padChar as xs:string ,
$length as xs:integer ) as xs:string {
substring(
string-join (
($stringToPad, for $i in (1 to $length) return $padChar)
,'')
,1,$length)
} ;
see this fiddle: http://xqueryfiddle.liberty-development.net/jyyiVhe/2
Will generate the desired output but Oracle Jdev will not display it with proper spacing.

Access variable from within itself in XQuery

I'm wondering whether in XQuery it is possible to access some elements in a variable from within the variable itself.
For instance, if you have a variable with several numbers and you want to sum them all up inside the variable itself. Can you do that with only one variable? Consider something like this:
let $my_variable :=
<my_variable_root>
<number>5</number>
<number>10</number>
<sum>{sum (??)}</sum>
</my_variable_root>
return $my_variable
Can you put some XPath expression inside sum() to access the value of the preceding number elements? I've tried $my_variable//number/number(text()), //number/number(text()), and preceding-sibling::number/number(text()) - but nothing worked for me.
You cannot do that. The variable is not created, till everything in it is constructed.
But you can have temporary variables in the variable
Like
let $my_variable :=
<my_variable_root>{
let $numbers := (
<number>5</number>,
<number>10</number>
)
return ($numbers, <sum>{sum ($numbers)}</sum>)
} </my_variable_root>
Or (XQuery 3):
let $my_variable :=
<my_variable_root>{
let $numbers := (5,10)
return (
$numbers ! <number>{.}</number>,
<sum>{sum ($numbers)}</sum>)
} </my_variable_root>
This is not possible, neither by using the variable name (it is not defined yet), nor using the preceding-sibling axis (no context item bound).
Construct the variable's contents in a flwor-expression instead:
let $my_variable :=
let $numbers := (
<number>5</number>,
<number>10</number>
)
return
<my_variable_root>
{ $numbers }
<sum>{ sum( $numbers) }</sum>
</my_variable_root>
return $my_variable
If you have similar patterns multiple times, consider writing a function; using XQuery Update might also be an alternative (but does not seem to be the most reasonable one to me, both in terms of readability and probably performance).

xquery replace issue in Oracle Service Bus

I'm trying to replace two backslashes with a single one within Oracle Service Bus xquery transformation with the replace function:
let $str := replace($srcStr, "\\\\", "\\"), where $srcStr holds the value "^\\d{1,4}$"
But for some reason this does not work. The result is stil "^\\d{1,4}$"
When I'm calling the same function in e.g. Altova XmlSpy this works fine: replace("^\\d{1,4}$", "\\\\", "\\") results in ^\d{1,4}
Does anybody have an idea why OSB does not match the backslashes in the source string? What could be a workaround?
This is a bug.
You can write custom regexp to workaround this bug.
declare function xf:replace_test($e as element()) as xs:string {
let $str := replace("junk (\)\ junk", ".*\\.*", "\$1")
return $str
};
declare variable $e as element() external;
xf:replace_test($e)`

Resources