Storing files with xquery in exist - xquery

Running the following xquery directly in eXide (Eval) it works fine, adding the XML files in MyFSdirectory into the MyCollectionPath:
xquery version "3.1";
let $selected_directory:= 'MyFSdirectory'
let $source-directory := $selected_directory
let $target-collection := 'MyCollectionPath'
return
xmldb:store-files-from-pattern($target-collection, $source-directory, '*.xml')
But when I add it to a function and call it from my app, the store-files-from-pattern is not doing the job (no errors are shown but files are not uploaded), the check point is printed in my screen, so the function is being called correctly. Any hints?
declare function app:upload_file($node as node(), $model as map(*)) {
let $selected_directory:= "MyFSdirectory"
let $source-directory := $selected_directory
let $target-collection := "MyCollectionPath"
return
<p>check point</p> |
xmldb:store-files-from-pattern($target-collection, $source-directory, '*.xml')
};

This sounds like a permissions issue. In other words, when you run the script in eXide, you're likely running as a user (e.g., "admin") with write permissions on the target collection, but in your application the script is likely running as a guest user without the required permission to write to the target collection.
To troubleshoot, add an expression calling xmldb:login() to your app:upload_file() function, supplying the credentials for the user you use in eXide.
If elevating privileges this way works, then the next step would be to consider setting appropriate permissions on the target collection or setting applying setuid or setgid on the module that writes to the database.

Related

Insert a document into a particular database MarkLogic

I'm setting up a database with roxy, which is different from the "documents" database. I want to insert a config file into that database.
I have this function in xquery ml-1.0
xquery version "1.0-ml";
declare namespace appsetup = "http://kittens.com.au/appsetup";
declare function appsetup:setup-day0($root,$content-db){
(: some values :)
let $m := map:new ()
let $_ := map:put ($m, "Kittens-Forever",xdmp:parse-dateTime("[Y0001]-[M01]-[D01]", "2999-12-31"))
let $_ := map:put ($m, "Kittens-Never", xdmp:parse-dateTime("[Y0001]-[M01]-[D01]", "1899-12-31"))
let $_ := map:put ($m, "Kittens-Load-Root", $root)
return xdmp:document-insert ("/ksys/smap", <s:map>{$m}</s:map>,
(xdmp:permission ("Kittens-role", "read"),
xdmp:permission ("Kittens-role", "update")) )
};
The xdmp:document-insert inserts the document in to the default "documents" database. I had a look around, and i couldn't find a way to say load this document into this database. I only found a way to insert the document into a particular forest with xdmp:document-load (https://docs.marklogic.com/xdmp:document-load).
Is there a way for me to say load this thing into this database, preferably just as a xquery parameter?
xdmp:document-insert does not insert into the Documents database by default. It inserts into the database defined in the application server configuration. The idea is that you have an application server that you use to interface with the database.
Some notes:
Use an app server configured for your database.
Or use the REST API that has a parameter for this
Or use HTTP rewrite rules to dynamically change the database based on a user, URI or query parameter
Or use xdmp:eval or its derivatives (spawn, invoke, invoke-function) which all have an option to define which database to use.
For your particular sample, I would suggest that you use xdmp:invoke-function.

MarkLogic 8 - XQuery - cts:search - Change database programmatically

How can I change the database that a cts:search function runs against programmatically?
Right now I'm in query console.
You will probably need to use xdmp:eval, which accepts an options argument, and in there you can specify the database:
xdmp:eval("cts:search(...)", (),
<options xmlns="xdmp:eval">
<database>{xdmp:database("otherdb")}</database>
</options>)
Although on the lowest level, xdmp:eval is really what happens, the most clean option which is probably easiest to write inline is xdmp:invoke-function- and even better may be to also use an anonymous function within it. This combination allows for natural use of existing variables. If you want to go further, then also look at xdmp:apply(to add more flexability)
Furthermore, in MarkLogic 8, there is a new transaction-type called update-auto-commit which also makes it nice and clean to invoke a function inline, while waiting for the results (no spawn) and have it in its own transaction. Used properly, then the results of an update/insert are even available in the calling code.
The code sample below applies cts:search against another database and
naturally uses the variables in the main code:
xquery version "1.0-ml";
declare namespace html = "http://www.w3.org/1999/xhtml";
let $query := cts:word-query("foo")
let $start := 1
let $end := 3
let $database-name := "your-other-database-name-here"
return
xdmp:invoke-function(
function() {
cts:search(doc(), $query)[$start to $end]
},
<options xmlns="xdmp:eval">
<database>{xdmp:database($database-name)}</database>
</options>)

How to rename a document in MarkLogic?

I have simple task to do but unable to find the exact solutions for this.I have saved a file as abc.xml in MarkLogic.How can i rename the file as some example.xml using XQuery?
Code which I tried:
xquery version "1.0-ml";
xdmp:document-rename ("/aaa.xml","/final.xml");
This is showing an error.
There is no way, that I know of, to change the document URI of an existing document. The only way I can think of is to create a new document with the same content and the new URI, and delete the existing one, in the same transaction.
Where it gets tricky is to make sure to preserve the ownership, the permissions, all the properties, the property document, make sure that the old URI is not used anywhere to link to the existing document, etc.
But usually, the document URI is never really used. You should first considering whether you really need to rename the document, and why.
(Note that saying "this is showing an error" is rarely useful on SO or on mailing lists, if you do not show what the error is.)
Florent is correct, a true 'rename' is not possible, or perhaps not even meaningful. ( analogy - rename a file from one disk to another )
"Move" however is meaningful (copy then delete in a transaction).
Defining "Move" is use case dependent - i.e. what metatdata also needs to 'move' ? permissions? collections ? document properties ? inherited permissions ?
xmlsh (http://www.xmlsh.org) implements a 'rename' (http://www.xmlsh.org/MarkLogicRename) command for the marklogic extension which is really a 'move', with the implemenation borrowed from postings on markmail (http://markmail.org/)
The implementation is the following XQuery - it doesnt do everything you might want and it might do more then you want. YMMV
https://github.com/DALDEI/xmlsh/blob/master/extensions/marklogic/src/org/xmlsh/marklogic/resources/rename.xquery
( it was also written long ago - it is likely to benefit from improvement )
I have working example this works for me.
xquery version "1.0-ml";
declare function local:document-rename(
$old-uri as xs:string, $new-uri as xs:string)
as empty-sequence()
{
xdmp:document-delete($old-uri),
let $permissions := xdmp:document-get-permissions($old-uri)
let $collections := xdmp:document-get-collections($old-uri)
return xdmp:document-insert(
$new-uri, doc($old-uri),
if ($permissions) then $permissions
else xdmp:default-permissions(),
if ($collections) then $collections
else xdmp:default-collections(),
xdmp:document-get-quality($old-uri)
)
,
let $prop-ns := namespace-uri(<prop:properties/>)
let $properties :=
xdmp:document-properties($old-uri)/node()
[ namespace-uri(.) ne $prop-ns ]
return xdmp:document-set-properties($new-uri, $properties)
};
(: function call :)
local:document-rename ("/opt/backup/x.xml","y.xml");
MarkLogic has a tutorial up addressing file renaming (moving):
https://developer.marklogic.com/recipe/move-a-document/
Importantly, it uses the function xdmp:lock-for-update() to prevent modifications to the source file while it is being copied to the target location.
Also, if you are doing a batch renaming you'll want to make sure that each file URI you rename corresponds to a document in the database or you'll get runtime errors.

Not picking up modifications to an XML document in the database

I have only just started with MarkLogic and XQuery. I am having a really tough time in modifying the content of one of my XML documents. I just cannot seem to get a change to an element to pick up. Here's my process (I have had to take things back as basic as I could just to try and get it working):
In query console I have one tab open which queries for the contents of one XML doc:
xquery version "1.0-ml";
declare namespace html = "http://www.w3.org/1999/xhtml";
xdmp:document-get("C:/Users/Paul/Documents/MarkLogic/xml/ppl/ppl/jdbc_ppl_3790.xml")
This brings back the document as below
false
...
3790
Victoria Wilson
</ppl_name>
I now want to update the element using XQuery but it's just not happening. Here's the XQuery:
xquery version "1.0-ml";
declare namespace html = "http://www.w3.org/1999/xhtml";
let $docxml :=
xdmp:document-get("C:/Users/Paul/Documents/MarkLogic/xml/ppl/ppl/jdbc_ppl_3065.xml")/document/meta/ppl_name
return
for $node in $docxml/*
let $target := xdmp:document-get("C:/Users/Paul/Documents/MarkLogic/xml/ppl/ppl/jdbc_ppl_3790.xml")/document/meta/*[fn:name() = fn:name($node)]
return
xdmp:node-replace($target, $node)
I am basically looking to replace the ppl_name element in the target (3790) with the ppl_name element from the source (3065).
I run the XQuery - it completes without error (making me thing it has worked) - return value reads your query returned an empty sequence.
I then go back to the same tab as I used in step 1 and re-run the XQuery used in step 1. The doc (3790) comes back but it STILL has Victoria Wilson as the ppl_name.
The node returned by xdmp:document-get is an in-memory node from a document on the filesystem. It isn't coming from the database. You can't use xdmp:node-replace on in-memory nodes. That's only for database-resident nodes.
You can insert it using xdmp:document-insert. Then it's in the database, and you can access it using doc and update it using xdmp:node-replace. Or you can use in-memory operations to construct a new version with the changes you want.
See What are in memory elements in marklogic? for previous answers to a similar question, and more tips.
Here the node returned by xdmp:document-get is an in-memory node
If your working with in memory elements import the following module
import module namespace mem = "http://xqdev.com/in-mem-update" at "/MarkLogic/appservices/utils/in-mem-update.xqy";
Instead of using xdmp:node-replace you can use mem:node-replace(<x/>, <y/>)

How to dynamically reference nodes in xdmp:node-replace in MarkLogic?

In my function update-replace, I am trying to dynamically replace an XML node in one of my XML data source files in MarkLogic by calling xdmp:node-replace like below:
declare function update-lib:update-rec($doc as xs:string, $path as xs:string, $country as xs:string, $name as xs:string, $population as xs:integer, $latitude as xs:decimal, $longitude as xs:decimal) as document-node() {
(: read lock acquired :)
fn:doc($doc),
xdmp:node-replace(fn:doc($doc)/$path,
<city>
<country>{$country}</country>
<name>{$name}</name>
<population>{$population}</population>
<latitude>{$latitude}</latitude>
<longitude>{$longitude}</longitude>
</city>
),
(: after the following statement, txn ends and locks released :)
xdmp:commit()
};
The function takes 7 arguments with the 1st arg being path to the XML source file, the 2nd being path inside the XML file to the node to be updated and the rest corresponds to the child element values.
When I call xdmp:node-replace to update data, I encounter the following error:
500 Internal Server Error
XDMP-ARGTYPE: (err:XPTY0004) xdmp:node-replace("/cities/city[3961]",
JPMiyoshi56958)
-- arg1 is not of type node() ...
So I decided to have arg1 evaluated to ensure that node() gets passed as the 1st arg of node-replace:
xdmp:node-replace(xdmp:eval(fn:doc($doc)/$path),
<city>
<country>{$country}</country>
<name>{$name}</name>
<population>{$population}</population>
<latitude>{$latitude}</latitude>
<longitude>{$longitude}</longitude>
</city>
),
Now I receive the below error instead:
XDMP-UPEXTNODES:
xdmp:node-replace(fn:doc("/content/Users/Tako/Sites/MarkLogic/xml/worldcities/import/cities1000_02.xml")/cities/city[3961],
JPMiyoshi56958)
-- Cannot update external nodes ...
After a little googling, I've found this. It sounds like an issue with xdmp:eval and its context:
http://developer.marklogic.com/pipermail/general/2008-September/001753.html
I tried the workaround suggested here using fn:concat to have everything constructed as a string including xdmp:node-replace and evaluating the whole statement.
xdmp:eval(fn:concat('xdmp:node-replace(fn:doc("', $doc, '")', $path,
', ', '<city><country>',$country,'</country><name>',$name,'</name><population>',$population,'</population><latitude>',$latitude,'</latitude><longitude>',$longitude,'</longitude></city>', ')')),
The application sits and waits for a very long time before timing out when I tried this.
500 Internal Server Error
SVC-EXTIME:
xdmp:node-replace(fn:doc("/content/Users/Tako/Sites/MarkLogic/xml/worldcities/import/cities1000_17.xml")/cities/city[3961],
JPMiyoshi56958)
-- Time limit exceeded ...
All I want to do is to dynamically reference the XML file and the nodes to be updated and update the node with the info passed in. I must be overlooking something very fundamental or doing this completely wrong.
Could someone please shed light on this?
In your first solution you are applying a string value to each document node returned by fn:doc($doc). That way you only end up with the string value of $xpath itself. The second solution effectively takes the the value of $xpath too, and tries to evaluate that. That is likely to generate a lot of nodes that are potentially all being updated.
I am not entirely sure why you are getting XDMP-UPEXTNODES, and timeouts, but the following should do..
Replace:
fn:doc($doc)/$path
with:
xdmp:value(fn:concat("fn:doc($doc)", $path))
HTH!

Resources