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

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.

Related

oData v4 - ordering outer entity on property in related one-to-many entities

I have an oData model with a couple of one-to-many relationship, say person->addresses and person->driving-licences. I would like to be able to sort the result set based on properties in the address entity and driving licence entity. As there could be more than one address, I would initially select a single item from the addresses set, based on a property called IsPrimary. As there could be more that one driving licence, I would select the 'UK' driving licence. Is this possible?
I was hoping I could do something like:
/people?$expand=addresses($filter=isPrimary eq true),drivinglicences($filter=country eq 'UK')&$orderby=addresses/postcode,drivinglicences/active
Unfortunately I get the following error:
"The query specified in the URI is not valid. The parent value for a property access of a property 'isPrimary' is not a single value. Property access can only be applied to a single value."
Does anyone know if what I'm trying to do is supported by the spec? Or whether it is an issue with my query? Or whether it is an issue with the .NET library.
I'm using:
Microsoft.AspNet.OData - 7.2.3
Many Thanks.
What you see here is by design, or rather not supported by the specification, the error message even highlights the only type of expressions supported:
The query specified in the URI is not valid. The parent value for a property access of a property 'isPrimary' is not a single value. Property access can only be applied to a single value.
So the simplest solution is to modify the API either to include a Function bound to the people collection that applies the $filter or $order directly, or a Function that returns the data in a new shape, one that only has perhaps a singleton PrimaryAddress property. How you include driving license in this result is up to you, it could even be a parameter to the function, perhaps your people controller has a queryable function with this signature:
[EnableQuery]
public IHttpActionResult WithLicences(string countryCode)
However that is out of the scope of OPs question about specific syntax support
Although it seems like an important feature, we must remember that $select (Projection) and $filter are evaluated at different points in time, OData queries follow a similar execution sequence to SQL however the filter criteria and $orderby are evaluated separately, and the projection of the resultset is the last evaluation to be applied.
Due to $filter and $orderby being applied independently, neither concept is even aware of the other and as such neither can reference or assume to be applied before the other.
You can prove this by specifying a field in the $orderby and/or $filter that is not included in the $select, you can even reference singleton navigation fields that are not included in an $expand and the query will evaluate correctly.
The OData spec is similar to a law document, in that to properly understand and apply it we need to understand the original intent of the authors. We can get an initial understanding from the early listing of Addressing Entities
Addressing Entities describes functions that can be bound to collections or entities that return either a single entity or a collection of entities
By allowing special provision of custom functions to be applied the authors are encouraging API designers to provide natural extensions to their resource endpoints that can facilitate the execution of pre-determined queries that may be otherwise complex or problematic to express in pure OData query syntax.
In other words, we are encouraged to customise our APIs to make them easier for the end process to consume, and to guide the consuming developer to make the best use of the API, they shouldn't have to discover everything from first principals.
To achieve OPs type of query in pure SQL would still require either a nested lookup, CTE or self join... advanced syntax. In OData v4, the specification does not provide a syntax for targeting specific items within a collection for path expressions (of which $orderby derives from)
5.1.1.15 Path Expressions
Properties and navigation properties of the entity type of the set of resources that are addressed by the request URL can be used as operands or function parameters, as shown in the preceding examples.
Properties of complex properties can be used via the same syntax as in resource paths, i.e. by specifying the name of a complex property, followed by a forward slash (/) and the name of a property of the complex property, and so on,
Properties and navigation properties of entities related with a target cardinality 0..1 or 1 can be used by specifying the navigation property, followed by a forward slash (/) and the name of a property of the related entity, and so on.
If a complex property is null, or no entity is related (in case of target cardinality 0..1), its value, and the values of its components, are treated as null.
RE: I couldn't find anything explicit in the spec. :)
That is the very thing about the OData specification,the specification does not list what is not supported, only what should be supported. So by omission, if you cannot find a reference to how to do something, then that something is not required to be supported.
Introduction http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_Introduction ... This specification defines a set of recommended (but not required) rules for constructing URLs to identify the data and metadata exposed by an OData service as well as a set of reserved URL query string operators, which if accepted by an OData service, MUST be implemented as required by this document.
This has been on ongoing discussion held in may threads, recently https://stackoverflow.com/a/55324393/1690217
Many people complain that this is surely a fundamental feature of a data access platform, however it is important to respect the original intent of the OData platform and keep our APIs simple by providing customised endpoints to suit our business domain.

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.

How do I get the HTTP POST body in MarkLogic 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.

How to customize segment using query string or cookie in Adobe CQ5.6?

How to customize segment using query string or cookie in Adobe CQ5.6?
My requirement as follows:
I wanted to target querystring parameters in Segment which is added in my Experience, Teaser, Promotion, Voucher for owned site.
Thanks in advance.
You can define your own segment at outlined here: https://docs.adobe.com/docs/en/cq/5-6-1/administering/segmentation.html#Defining%20a%20New%20Segment
In step 6 where you edit the segment, you need to supply a JavaScript expression that will resolve to a boolean, true or false. You can put any JavaScript expression here that meets your need, so you can include script that will have a complex expression if you want. But rather than write a complex JavaScript expression here, it would be better to write JavaScript that exposes a function which returns true or false and then include that JavaScript function on your pages via a client library. Then, in the segment, you can simply invoke the function rather defined in your client library than try to write a complex expression.
As long as the JavaScript expression defined in the segment evaluates to true/false, the segment will be usable.
Without your own JavaScript you can easily fetch query string values of look at cookies. See these other questions for details on that:
What is the shortest function for reading a cookie by name in JavaScript?
How can I get query string values in JavaScript?

Can I declare a global variable in xquery in Marklogic Server?

I want a global variable which I can use in my different .xqy pages. Can I declare such a variable in xquery in Marklogic Server ?
You can declare a variable in any module. For instance, it is config.xqy.
declare variable $PRECISION as xs:integer := 4;
For using this variable you need to import this module in your work module.
import module namespace config = "http://your-namespace" at "config.xqy";
And refer to this variable:
$config:PRECISION
If your application is running on a single E-node, you can use server fields , which are sort of designed for this use case as well.
If you need values accessible across the server, there is a library in the Marklogic XQuery Commons for storing persistent key/value pairs:
https://github.com/marklogic/commons/blob/master/properties/properties.xqy
And you may have already considered this, but you could also just simply store the global data in a document on the database and access with doc() - or eval() if you need to get to it from a different database.
You have a few options. If you need a global constant variable, the config.xqy method mentions in #Andrew Orlov's answer is great because you avoid any locking from concurrent access to a properties.xml file.
If you need a variable that can be mutated across a cluster of nodes, the property.xqy example linked by #wst appears to use globally assigned namespaces to embed a retrievable key and value. Pretty clever. However, I'm not sure how much this is meant for heavy levels of change.
The E-node specific variable from #Eric Bloch is good, but please also be aware that it will not survive a system restart.
I'd be interested to know how these all compare performance-wise.

Resources