How do I get the HTTP POST body in MarkLogic XQuery? - xquery

I'm trying to use the exist-db request:get-data() method to get the post data of a request. However, I'm getting the error:
XDMP-UNDFUN: (err:XPST0017) Undefined function request:get-data()
I did declare the namespace in my header. I don't understand why I still can't use request:get-data() or any of the other request: functions
declare namespace request="http://exist-db.org/xquery/request";
declare option exist:serialize "method=xml media-type=text/xml indent=yes";
let $post-data := request:get-data()
return $post-data

I think you're looking for xdmp:get-request-body.

Sam pointed you to the function you need, but I wanted to respond to another part of your question:
I did declare the namespace in my header. I don't understand why I still can't use request:get-data() or any of the other request: functions
Each XQuery processing engine implements standard functions, but there is other functionality needed that is not defined by the standard. For MarkLogic, you'll use standard functions with the fn: prefix.
Each XQuery engine then defines additional functions that will be needed. For Exist DB, some of those are in the "http://exist-db.org/xquery/request" namespace, while MarkLogic uses "http://marklogic.com/xdmp" for a lot of its extension functions.
When you're looking for the MarkLogic equivalent of an Exist DB-specific function, search on http://docs.marklogic.com -- start with the function name, and if that doesn't work, search for the terms that describe what you're trying to do.

Related

Call db-specific function from jOOQ

In the new fts5 virtual tables in SQLite there is a ranking function bm25. What is the best way to call it from jOOQ? Is there any way to introduce this function as Java/Kotlin function or should it be just a string literal?
What I do currently is
.orderBy(field("bm25(books_fts)"))
but indeed this approach is error-prone. I can make a typo in any part and will have an error only in runtime.
Is there any better way?
Instead of using the plain SQL template directly in your query, better create a custom library for these purposes, e.g.
fun bm25(t: Table<*>): Field<Double> = field("bm25({0})", SQLDataType.DOUBLE, t)
Now you can pretend this is a natively supported function in jOOQ and use it in a type safe way everywhere.

Enforcing flow annotations for function parameters

How do I configure Flow to require type annotations for function parameters?
Specifically, I'd like Flow to show an error in the following code because params is not typed:
Types are inferred from usage. If this is an exported function then I recommend you use the types-first mode if you haven't already which enforces all module boundaries to have their types defined.
https://medium.com/flow-type/types-first-a-scalable-new-architecture-for-flow-3d8c7ba1d4eb
export function findOne(param) {
}
This will now throw an error and you must define the types of your function in your codebase.
You can achieve this by using eslint-plugin-flowtype and configuring require-parameter-type rule
Requires that all function parameters have type annotations

Can someone explain how the external variable works in XQuery?

I see an XML node when I log $variable, but I'm not sure how $variable received the node and assume it has something to do with external.
declare variable $variable as element() external;
Declaring an external variable in a query means that the calling application must supply a value for the variable when it invokes the query. The way in which this is done depends on the API offered by your chosen XQuery processor.

Nested property expansion in SoapUI

Short version: How do I do nested property expansion in an XQuery in SoapUI Pro 5, where the outer property is a reference to the ResponseAsXML of a previous test step, and the inner property comes from a properties file?
Example:
My test steps look like this:
Call AddCustomer, which also adds an email address, and returns customerId.
Use property transfer to store the customerId in a properties file called EmailProperties.
Call AddEmailToCustomer, which adds a second email to the same customer and returns a new emailId.
Call GetEmailsForCustomer, which returns both emails for the customer.
The REST endpoints for step 3 and 4 look like this:
POST/GET http://myEndpoint.com/customers/{customerId}/emails
When trying to verify step 4, I wrote an XQuery that loops through the Response (since we can't guarantee what order the emails will be in the response) to find the email added in step 3. In my XQuery, if I hardcode the customerId in the namespace like this it works fine:
for $email in //emails
where $email/id/text()='${AddEmailToCustomer#ResponseAsXml#declare namespace ns1='http://myEndpoint.com/customers/1234/emails'; //ns1:Response[1]/ns1:id[1]}'
But if I try to use the customerId from the properties file like this:
for $email in //emails
where $email/id/text()='${AddEmailToCustomer#ResponseAsXml#declare namespace ns1='http://myEndpoint.com/customers/${#EmailProperties#customerId}/emails'; //ns1:Response[1]/ns1:id[1]}'
I get an error about how it can't find the expected child nodes:
...Exception:org.custommonkey.xmlunit.Diff[different] Expected presence of child nodes to be 'true' but was 'false'...
How can I get this to work?
You can use wildcards for namespaces to greatly simplify your XPaths:
where $email/id/text()='${AddEmailToCustomer#ResponseAsXml#//*:Response[1]/*:id[1]}'
As #SiKing recommends you can use wildcards for namespaces to simplify XPath, this is enough if you don't care about check the namespace (which is the most usual behavior). However I think that you put the namespace in a property in order to check possible different namespaces due you can try declaring your XQuery Match assertion as follows:
declare namespace ns1='http://myEndpoint.com/customers/${#EmailProperties#customerId}/emails';
<XQueryResult>
{
for $email in //ns1:emails
where $email/id/text()='${AddEmailToCustomer#Response#//ns1:Response[1]/ns1:id[1]}'
return $email
}
</XQueryResult>
Please note that if EmailProperties is a property testStep then you must use ${EmailProperties#customerId} instead of ${#EmailProperties#customerId}, also in this case I prefer to use #Response instead of #ResponseAsXml.
Hope this helps,

How to set the default / base collection of a XQJ Connection

I want to query the eXist db via the XQJ API. Therefore I have an XQuery file which I want to apply to several different documents inside different collections of the database.
My question is, how can I set the path to the document/collection? I want to apply the XQuery only on specific documents/collections and canĀ“t set the path directly in the XQuery document (with the doc('path/to/doc') function ), because I want to apply the same XQuery to several files with different paths.
I could bind a Java variable to an Xquery expression but there has to be a more elegant way to achieve this task with the XQJ API. Hope you can help me out a little bit.
If you know the specific document URIs up-front, you could bind a bunch of URIs as a xs:string sequence, where you can then iterate through them in your XQuery expression, performing operations against each URI as necessary.
declare variable $uris as xs:string* external;
for $uri in $uris
return (
(: perform operation against $uri :)
)
Or if you know the collection uri,
declare variable $collection-uri as xs:string external;
for $document-node in fn:collection($collection-uri)
return (
(: perform operation against document-node() :)
)
In a future version of eXist XQJ API, you will be able to set a collection as the default context, which goes against both the XQJ standard AND the XQuery 1.0 / 3.0 standards but other users appear to be wanting this functionality quite badly.

Resources