I have some running code that used the "propitiatory" basex communication method over TCP/IP. The application was calling basex "client" side (from an Ant task) so this was all fine.
Being used to the SQL paradigm of "preparing" an SQL statement then executing it with different "host variable" values which gives a massive performance advantage over constantly preparing an SQL statement with different literal values plugged in, I chose the same approach for my original XQuery application.
String partDelete = "declare namespace xyz="" + BaseXML.NAMESPACE_URL + "";\n" + "declare variable $contributor external;\n" + "delete node /xyz:driver[@name="" + driver.getDriverName() + ""]/xyz:part[@contributor=$contributor]";
This statement is executed multiple times from within a loop to delete many "parts", before each execution I perform :- pd.bind("$contributor", contributor); pd.execute()
Now we are trying to move the code from being client side to server side, the server is using RESTful interfaces for all external interactions so we are migrating the BaseX interaction to REST too.
I reasoned that if I created a partdelete.xq file on the server that I could then perform a rest request passing in the values of driver and contributor emulating the "host variable" approach.
I created partdelete.xq
module namespace xyz = 'http://my.basex.server.net/xyz';
declare variable $driver external; declare variable $contributor external; delete node /zss:driver[@name="$driver"]/zss:part[@contributor=$contributor]
Then (using postman) performed a GET request on
//http:my.basex.server.ne:8984/rest/ TESTDB?run=partdelete.xq&$driver=TESTDRVR&$contributor=LINUX
During my experiments I have managed to get many different error messages, but never got even close to making this work.
Questions: o Am I fooling myself that creating a server side .xq file is going to perform better than building a completed query on the client and sending that via REST without "variables"? o Is there a way to perform the equivalent "prepare/execute" method from the client without creating a .xq file on the server? o Can anyone suggest how I can get the server side .xq file to work?
I also have a query which is executed before the delete to obtain the list of parts to delete. I was unable to make this work either. The .xq file looked like this
module namespace xyz = 'http://my.basex.server.net/xyz';
declare variable $driver external; declare variable $contributor external; for $p in /xyz:driver[@name="$driver"]/xyz:part, $o in $p/xyz:part-outputs/xyz:part-output where $p/@contributor/data() = $contributor return data($o/@type)||"/"||data($p/@class)||"/"||data($p/@hostname)
The query works fine from the client side, and since this is only executed once it's no big deal to prefill all the values and dispense with the variables and send it over REST. I could (I suppose) alternatively use the <variable element as indicated in the BaseX documentation, but it's unclear how that would offer any performance advantage. Being a lover of consistency I would like to move both queries over to the BaseX server side.
Examples of some of the errors I managed to produce ... [XPST0081] No namespace declared for 'xyz:driver' [XPST0003] Library modules cannot be evaluated [XPDY0002] root(): no context value bound.
Any help, advice or suggestions greatly appreciated. Mike
Hi Mike,
o Am I fooling myself that creating a server side .xq file is going to perform better than building a completed query on the client and sending that via REST without "variables"?
Indeed it’s always recommendable to bind variables to a static expression instead of concatenating query strings (no matter if you are using our client bindings or REST). In terms of performance, there shouldn’t be any difference in BaseX, but your APIs will definitely be more robust and less prone to errors in the long term. Moreover, there’s no chance of query injection if you pass on dynamic user values.
o Is there a way to perform the equivalent "prepare/execute" method from the client without creating a .xq file on the server?
The usual way to go would be to pass on variables via the POST method and the <variable> elements. I see you have already found this alternative in our documentation.
o Can anyone suggest how I can get the server side .xq file to work?
First I thought “yeah, should be trivial”, then I noticed that I also stumbled upon the "no context found" error message. This is what I did:
URL: http://localhost:8984/rest/db?run=test.xq&driver=abc&contributor=def
XQUERY: declare namespace zss = 'your-zss-namespace-uri'; declare variable $driver external; declare variable $contributor external; delete node /zss:driver[@name=$driver]/zss:part[@contributor=$contributor]
To get this work, I had to include the database in the query:
URL: http://localhost:8984/rest?run=test.xq&driver=abc&contributor=def
XQUERY (in the server webapp directory): declare namespace zss = 'your-zss-namespace-uri'; declare variable $driver external; declare variable $contributor external; delete node db:open($db) /zss:driver[@name=$driver]/zss:part[@contributor=$contributor]
The database could obviously be passed on as query parameter as well.
I’ll send you another mail once I have found out why it’s not possible anymore to access the currently opened database inside your query.
However, I’d like to add another question to your three:
o Are there better ways to do this?
Yes: use RESTXQ :) It may take some more time to understand how it’s working, but it will allow you to specify much nicer APIs than REST.
Here is how your previous example would look like with RESTXQ:
URL (use the DELETE Method to delete data...): http://localhost:8984/db/abc/def
XQUERY (in the server webapp directory):
declare namespace zss = 'your-zss-namespace-uri'; declare %updating %rest:path("{$database}/{$driver}/{$contributor}") %rest:method("DELETE") function local:delete( $database as xs:string, $driver as xs:string, $contributor as xs:string ) { delete node db:open($database) /zss:driver[@name=$driver] /zss:part[@contributor=$contributor] }; ()
Hope this helps Christian
Hi Mike,
I’ll send you another mail once I have found out why it’s not possible anymore to access the currently opened database inside your query.
Here we are… I was surprised to see that the specified database was not considered so far in combination with the 'run' option. This has now changed with the latest snapshot [1]. I assume we never noticed this because we are working with RESTXQ almost exclusively.
All the best Christian
[1] http://files.basex.org/releases/latest/
However, I’d like to add another question to your three:
o Are there better ways to do this?
Yes: use RESTXQ :) It may take some more time to understand how it’s working, but it will allow you to specify much nicer APIs than REST.
Here is how your previous example would look like with RESTXQ:
URL (use the DELETE Method to delete data...): http://localhost:8984/db/abc/def
XQUERY (in the server webapp directory):
declare namespace zss = 'your-zss-namespace-uri'; declare %updating %rest:path("{$database}/{$driver}/{$contributor}") %rest:method("DELETE") function local:delete( $database as xs:string, $driver as xs:string, $contributor as xs:string ) { delete node db:open($database) /zss:driver[@name=$driver] /zss:part[@contributor=$contributor] }; ()
Hope this helps Christian
Christian,
Thank you for your carefully considered and helpful suggestions, I will try them out first thing on Monday. Regarding RESTXQ, I had only really started to appreciate RESTXQ recently, while I was trying to get this server side query to run and I discovered that the admin functions (dba) were based on it. I will certainly look at RESTXQ too, potentially for also other "user interaction / reporting" aspects of our application, however, would like to see both methods work for completeness :-)
Mike
On Sat, Aug 6, 2016 at 1:12 AM, Christian Grün christian.gruen@gmail.com wrote:
Hi Mike,
o Am I fooling myself that creating a server side .xq file is going to perform better than building a completed query on the client and sending that via REST without "variables"?
Indeed it’s always recommendable to bind variables to a static expression instead of concatenating query strings (no matter if you are using our client bindings or REST). In terms of performance, there shouldn’t be any difference in BaseX, but your APIs will definitely be more robust and less prone to errors in the long term. Moreover, there’s no chance of query injection if you pass on dynamic user values.
o Is there a way to perform the equivalent "prepare/execute" method from the client without creating a .xq file on the server?
The usual way to go would be to pass on variables via the POST method and the <variable> elements. I see you have already found this alternative in our documentation.
o Can anyone suggest how I can get the server side .xq file to work?
First I thought “yeah, should be trivial”, then I noticed that I also stumbled upon the "no context found" error message. This is what I did:
URL: http://localhost:8984/rest/db?run=test.xq&driver=abc&contributor=def
XQUERY: declare namespace zss = 'your-zss-namespace-uri'; declare variable $driver external; declare variable $contributor external; delete node /zss:driver[@name=$driver]/zss:part[@contributor=$contributor]
To get this work, I had to include the database in the query:
URL: http://localhost:8984/rest?run=test.xq&driver=abc&contributor=def
XQUERY (in the server webapp directory): declare namespace zss = 'your-zss-namespace-uri'; declare variable $driver external; declare variable $contributor external; delete node db:open($db) /zss:driver[@name=$driver]/zss:part[@contributor=$contributor]
The database could obviously be passed on as query parameter as well.
I’ll send you another mail once I have found out why it’s not possible anymore to access the currently opened database inside your query.
However, I’d like to add another question to your three:
o Are there better ways to do this?
Yes: use RESTXQ :) It may take some more time to understand how it’s working, but it will allow you to specify much nicer APIs than REST.
Here is how your previous example would look like with RESTXQ:
URL (use the DELETE Method to delete data...): http://localhost:8984/db/abc/def
XQUERY (in the server webapp directory):
declare namespace zss = 'your-zss-namespace-uri'; declare %updating %rest:path("{$database}/{$driver}/{$contributor}") %rest:method("DELETE") function local:delete( $database as xs:string, $driver as xs:string, $contributor as xs:string ) { delete node db:open($database) /zss:driver[@name=$driver] /zss:part[@contributor=$contributor] }; ()
Hope this helps Christian
basex-talk@mailman.uni-konstanz.de