Declare column in variable is not working in Optic query MarkLogic - xquery

I am trying to declare the column in a variable and want to call that variable inside my program.
Here is my optic query code which returns me - Column not found.
Please help to understand the problem in my query.
xquery version "1.0-ml";
import module namespace op="http://marklogic.com/optic" at "/MarkLogic/optic.xqy";
import module namespace ofn="http://marklogic.com/optic/expression/fn" at "/MarkLogic/optic/optic-fn.xqy";
import module namespace osql="http://marklogic.com/optic/expression/sql" at "/MarkLogic/optic/optic-sql.xqy";
declare option xdmp:mapping "false";
let $view := op:from-view("GTM2_Shipment", "Shipment_View")
let $count := op:view-col("Shipment_View", "Ancillary_QuotePrice")
return $view
=> op:where(op:and((
op:gt(op:col("$count"), 0)
))
)
=> op:group-by("transMode", op:count("QuotePrice", "$count"))
=> op:result()
here is the result I get -
[1.0-ml] SQL-NOCOLUMN: Column not found: $count
Stack Trace
At line 1 column 2:
In sem:execute(plan:sparql(" * { sample(transMode) AS transMode) count($count) AS QuotePrice) { FILTER ($count gt xs:integer("0") and fn:format-dateTime(xs:dateTime(BookingCreateDt), xs:string("[Y0001]-[M01]-[D01]")) gt sql:dateadd(xs:string("month"), xs:int("-6"), fn:format-dateTime(xs:dateTime("2022-06-30T10:42:50.2654743+01:00"), xs:string("[Y0001]-[M01]-[D01]")))) } GROUP BY transMode }"), (), ()
I have also tried to declare the column like this-
let $count := op:col("Ancillary_QuotePrice")
And it doesnot recognize the column from the view and gives the same error.

Once $count is defined as an op:view-col(), then just use it as a variable. See suggestions below:
op:gt(op:col("$count"), 0) ---change to--> op:gt($count, 0)
op:count("QuotePrice", "$count") ---change to--> op:count("QuotePrice", $count)

Related

Error in Optic query in MarkLogic - Invalid coercion: map:map

I am getting an invalid map error when trying to get the percentage of a record based on a certain condition to the total count of another record.
Can you please help to identify the issue here in my code.
The below code is trying to get the percentage of the count of all 6 months old AncillaryQuote price to the total no of Shipments References when the Ancillary quote price is greater than 0 . I am taking Booking Date to calculate the 6 months old date range.
Here is my optic query which I am trying -
xquery version "1.0-ml";
import module namespace op="http://marklogic.com/optic" at "/MarkLogic/optic.xqy";
import module namespace ofn="http://marklogic.com/optic/expression/fn" at "/MarkLogic/optic/optic-fn.xqy";
import module namespace osql="http://marklogic.com/optic/expression/sql" at "/MarkLogic/optic/optic-sql.xqy";
declare option xdmp:mapping "false";
let $view := op:from-view("GTM2_Shipment", "Shipment_View")
let $Var1 := op:view-col("Shipment_View", "Ancillary_QuotePrice")
let $Var2 := op:view-col("Shipment_View", "Shipment_Ref")
let $Var3 := op:view-col("Shipment_View", "BookingCreateDt")
return $view
=> op:group-by("transMode",(op:count("Var2", $Var2),op:count("Var1", $Var1)))
=> op:where(op:and((
op:gt($Var1, 0),op:gt(ofn:format-dateTime($Var3, '[Y0001]-[M01]-[D01]'),osql:dateadd('month',-6, ofn:format-dateTime(fn:current-dateTime(),'[Y0001]-[M01]-[D01]')))
))
)
=>op:select(op:as("multiply", op:divide(op:col("Var1"), op:col("Var2"))))
=>op:select($Var3,op:as("percentage", op:multiply(100, op:col("multiply"))))
=> op:result()
Here is the error I am getting -
[1.0-ml] XDMP-AS: (err:XPTY0004) $qualifier as xs:string? -- Invalid coercion: map:map(<map:map xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" .../>) as xs:string
Stack Trace
In /MarkLogic/optic.xqy on line 685
In xdmp:eval("xquery version "1.0-ml";
import module namespace o...", (), <options xmlns="xdmp:eval"><database>16857865358322067141</database>...</options>)
In xdmp:eval("xquery version "1.0-ml";
import module namespace o...", (), <options xmlns="xdmp:eval"><database>16857865358322067141</database>...</options>)
The error is telling you that the $qualifier parameter is a map and cannot be converted to xs:string.
If you look at the signature of the op:select function:
op:select(
$plan as map:map,
$columns as columnIdentifier*,
[$qualifier as xs:string?]
) as map:map
You will see that $qualifier is the third parameter.
When using the =>, the plan is being set as the first parameter for you. The columns for the op:select() are the second parameter. However, you need to provide those columns as a sequence (wrap with ()). Otherwise, it appears that you are specifying $Var3 as the columns to select, and then the percentage column as the $qualifier.
Change:
=> op:select($Var3, op:as("percentage", op:multiply(100, op:col("multiply"))))
to:
=> op:select( ($Var3,op:as("percentage", op:multiply(100, op:col("multiply")))) )

Error in MarkLogic Optic Query - to get percentage of occurrence of a record in a template view document

I am trying to find the percentage of a certain record based on a condition to the total no of that record . I am using optic query to get the result.
To be specific on the requirement :
Give a percentage of a certain record, when there is a condition, with the total number of that record available in the template view.
Here is my optic query which I am trying -
xquery version "1.0-ml";
import module namespace op="http://marklogic.com/optic" at "/MarkLogic/optic.xqy";
import module namespace ofn="http://marklogic.com/optic/expression/fn" at "/MarkLogic/optic/optic-fn.xqy";
import module namespace osql="http://marklogic.com/optic/expression/sql" at "/MarkLogic/optic/optic-sql.xqy";
declare option xdmp:mapping "false";
let $view := op:from-view("GTM2_Shipment", "Shipment_View")
let $Ancillary_QuotePrice := op:view-col("Shipment_View", "Ancillary_QuotePrice")
return $view
=> op:group-by((),(op:count("TotcountOfColumn", $Ancillary_QuotePrice),op:count("percentageCount", $Ancillary_QuotePrice)))
=> op:select(
op:as("CountOnCond", op:where(op:and((
op:gt($Ancillary_QuotePrice, 0),op:gt(ofn:format-dateTime(op:col('BookingCreateDt'), '[Y0001]-[M01]-[D01]'),osql:dateadd('month',-6, ofn:format-dateTime(fn:current-dateTime(),'[Y0001]-[M01]-[D01]')))
))
)
),
op:as("Percentage",op:divide(op:col("CountOnCond"),op:col("TotcountOfColumn")))
)
=> op:result()
It gives me this error -
1.0-ml] XDMP-TOOFEWARGS: (err:XPST0017) op:where(op:and((op:gt($countOfColumn, 0), op:gt(ofn:format-dateTime(op:col("BookingCreateDt"), "[Y0001]-[M01]-[D01]"), osql:dateadd("month", -6, ofn:format-dateTime(fn:current-dateTime(), "[Y0001]-[M01]-[D01]")))))) -- Too few args, expected 2 but got 1
Also please check if my query logic is correct or not.
xquery version "1.0-ml";
import module namespace op="http://marklogic.com/optic" at "/MarkLogic/optic.xqy";
import module namespace ofn="http://marklogic.com/optic/expression/fn" at "/MarkLogic/optic/optic-fn.xqy";
import module namespace osql="http://marklogic.com/optic/expression/sql" at "/MarkLogic/optic/optic-sql.xqy";
declare option xdmp:mapping "false";
let $view := op:from-view("GTM2_Shipment", "Shipment_View")
let $Var1 := op:view-col("Shipment_View", "Ancillary_QuotePrice")
let $Var2 := op:view-col("Shipment_View", "Shipment_Ref")
return $view
=> op:group-by((),(op:count("Var2", $Var2),op:count("Var1", $Var1)))
=>op:select(op:as("multiply", op:divide(op:col("Var1"), op:col("Var2"))))
=>op:select(op:as("percentageFinal", op:multiply(100, op:col("multiply"))))
=> op:result()
Above is the straightforward percentage found for Var1 with total of Var2.
I want the condition of the Var1 to be added in the code. Where can I add the where condition please.
op:where() expect the first parameter to be the $plan. Typically, when chaining calls from a plan using => it is set as that first parameter for you, and you are then providing the subsequent parameters starting from the second parameter with what is explicitly set in the function call.
So, when attempting to apply op:where() as the expression for op:as() to bind to:
op:as("CountOnCond", op:where(op:and((
op:gt($Ancillary_QuotePrice, 0),op:gt(ofn:format-dateTime(op:col('BookingCreateDt'), '[Y0001]-[M01]-[D01]'),osql:dateadd('month',-6, ofn:format-dateTime(fn:current-dateTime(),'[Y0001]-[M01]-[D01]')))
))
)
the op:and() parameter being specified is the one and only parameter being specified, but op:where() has two required parameters.
That is why it throws the XDMP-TOOFEWARGS error.

Handling undefined number of external variables

I'm using baseX in a REST environment and I'm quite stuck trying to run an .xq script with an undefined number of GET variables (could be 1 but could be 10)
I'd like to make my xq script generic about that and construct my query independently.
Is there a way to achieve that, playing with array or sending differently my variables, or I dunno how ?
here is my API call
http://basex:8984/rest/?run=WEB-INF/data/test.xq&$tag=p&value=sciences&tag2=p&value2=test&tag3=testdzq
here is my text.xq
declare variable $tag external;
declare variable $value external;
declare variable $tag2 external;
declare variable $value2 external;
<documents>
{for $doc in collection("testdb2")
where $doc//*[name() eq $tag]/text()[matches(., $value )]
and $doc//*[name() eq $tag2]/text()[matches(., $value2 )]
return <doc>{$doc//titleStmt/title/text()}</doc>
}
</documents>
Thanks !
found this here (see http-params function) https://www.balisage.net/Proceedings/vol18/print/Murray01/BalisageVol18-Murray01.html
(: BaseX example :)
(: In the controller ... :)
module namespace c = "http://balisage.net/ns/Bal2016murr0319/controller";
import module namespace request = "http://exquery.org/ns/request";
import module namespace item = "http://balisage.net/ns/Bal2016murr0319/views/item" at "views/item.xqm";
(:~ Returns a map containing the HTTP request parameters. :)
declare function c:http-params()
as map(*)
{
map:merge(
for $name in request:parameter-names()
let $value := request:parameter($name)
return map:entry($name, $value)
)
};
(:~ Calls the appropriate view, based on user input. :)
declare function c:get-view()
as element(html)
{
(: get HTTP request parameters :)
let $params := c:http-params()
return
if (map:get($params, "id")) then
(: the presence of "id" indicates that the user is requesting the item-level page for this unique identifier :)
(: call the item-level view :)
item:get-html($params)
else if ... (: call some other view :)
else if ... (: call some other view :)
else (: call the view for the home page ... :)
};

How to share markup snippets amongst functions in eXist-db?

I wonder whether there is a way how to share html code snippets in eXist-db. I have two different (more expected later) functions returning the same big html form for different results. It is annoying to maintain the same code when I change something in one of these. I have tried:
Saving it like html file and load it with doc() function (eXist complains it is not an xml file, it is binary.
Saving it like global variable into a separate module (eXist complains there is a problem with contexts). I don’t know how to pass such a variable without the namespace prefix.
Saving it like a function returning its own huge variable (eXist complains there is a problem with contexts).
What is the best practice?
UPDATE
Well, I have tried to put the snippet into a variable insinde a function loaded as a module. For me, it seems reasonable. However, I got an error when try to return that:
err:XPDY0002 Undefined context sequence for 'child::snip:snippet' [at line 62, column 13, source: /db/apps/karolinum-apps/modules/app.xql]
In function:a pp:book-search(node(), map, xs:string?) [34:9:/db/apps/karolinum-apps/modules/app.xql]
I am calling it like so:
declare function app:list-books($node as node(), $model as map(*)) {
for $resource in collection('/db/apps/karolinum-apps/data/mono')
let $author := $resource//tei:titleStmt/tei:author/text()
let $bookName := $resource//tei:titleStmt/tei:title/text()
let $bookUri := base-uri($resource)
let $imgPath := replace($bookUri, '[^/]*?$', '')
let $fileUri := ( '/exist/rest' || $bookUri )
let $fileName := replace($bookUri, '.*?/', '')
return
if ($resource//tei:titleStmt/tei:title)
then
snip:snippet
else ()
};
Any ideas, please?
UPDATE II
Here I have the function in the module:
module namespace snip = "http://46.28.111.241:8081/exist/db/apps/karolinum-apps/modules/snip";
declare function snip:snippet($node as node(), $model as map(*), $author as xs:string, $bookTitle as xs:string, $bookUri as xs:anyURI, $fileUri as xs:anyURI) as element()* {
let $snippet :=
(
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{$bookTitle} ({$author})</h3>
</div>
<div class="panel-body">
...
</div>
)
return $snippet
};
Here I am trying to call it:
declare function app:list-books($node as node(), $model as map(*)) {
for $resource in collection('/db/apps/karolinum-apps/data/mono')
let $author := $resource//tei:titleStmt/tei:author/text()
let $bookTitle := $resource//tei:titleStmt/tei:title/text()
let $bookUri := base-uri($resource)
let $fileUri := ('/exist/rest' || $bookUri)
let $fileName := replace($bookUri, '.*?/', '')
where not(util:is-binary-doc($bookUri))
order by $bookTitle, $author
return
snip:snippet($author, $bookTitle, $bookUri, $fileUri)
};
It throws:
err:XPST0017 error found while loading module app: Error while loading module app.xql: Function snip:snippet() is not defined in namespace 'http://46.28.111.241:8081/exist/db/apps/karolinum-apps/modules/snip' [at line 35, column 9]
When I tried to put the snippet into a variable, it was not possible to pass there those local variables used (it threw $fileUri is not set). Besides that I tried to change the returned type element()* but nothing helped.
All of your approaches should work. Let me address each one:
Is the HTML snippet well-formed XML? If so, save it as, e.g., form.xml or form.html (since by default eXist assumes files with the .html extension are well-formed; see mime-types.xml in your eXist installation folder) and refer to it with doc($path). If it is not well-formed, you can save it as form.txt and pull it in with util:binary-to-string(util:binary-doc($path)). Or make the HTML well-formed and use the first alternative.
This too is valid, so you must not be properly declaring or referring to the global variable. What is the exact error you are getting? Can you post a small example snippet that we could run to reproduce your results?
See #2.
I was very close. It was necessary to somehow pass parameters to the nested function and omit eXist’s typical $node as node(), $model as map(*) as arguments.
Templating function:
declare function app:list-books($node as node(), $model as map(*)) {
for $resource in collection('/db/apps/karolinum-apps/data/mono')
let $author := $resource//tei:titleStmt/tei:author/text()
let $bookTitle := $resource//tei:titleStmt/tei:title/text()
let $bookUri := base-uri($resource)
let $bookId := xs:integer(util:random() * 10000)
let $fileUri := ('/exist/rest' || $bookUri)
let $fileName := replace($bookUri, '.*?/', '')
where not(util:is-binary-doc($bookUri))
order by $bookTitle, $author
return
snip:snippet($author, $bookTitle, $bookUri, $bookId, $fileUri)
};
Snippet function:
declare function snip:snippet($author as xs:string, $bookTitle as xs:string, $bookUri as xs:anyURI, $bookId as xs:string, $fileUri as xs:anyURI) as element()* {
let $snippet :=
(
<div class="panel panel-default">
...
</div>
)
return $snippet
};

How to construct triples manually in Marklogic

I have been trying to insert triples in Marklogic using this query
xquery version "1.0-ml";
import module namespace sem = "http://marklogic.com/semantics"
at "/MarkLogic/semantics.xqy";
declare variable $TRIPLE as xs:string external ;
declare variable $GRAPHNAME as xs:string external ;
let $TRIPLE:="sem:triple(sem:iri('http://smartlogic.com/document#testForTriples.xml'),sem:iri('http://www.smartlogic.com/schemas/docinfo.rdf#type'),'document')"
let $GRAPHNAME :="sem:iri('testGraph')"
let $r :=
sem:graph-insert($GRAPHNAME, $TRIPLE)
return <result>{$r}</result>
Unfortunately, that returns a coercion error:
XDMP-AS: (err:XPTY0004) $graphname as sem:iri -- Invalid coercion: "sem:iri('testGraph')" as sem:iri
What am I doing wrong?
You should not put quotes around sem:triple and sem:iri, they are type cast functions:
xquery version "1.0-ml";
import module namespace sem = "http://marklogic.com/semantics"
at "/MarkLogic/semantics.xqy";
let $TRIPLE := sem:triple(sem:iri('http://smartlogic.com/document#testForTriples.xml'),sem:iri('http://www.smartlogic.com/schemas/docinfo.rdf#type'),'document')
let $GRAPHNAME := sem:iri('testGraph')
let $r := sem:graph-insert($GRAPHNAME, $TRIPLE)
return <result>{$r}</result>
If you are trying to create the triples dynamically, pass through a sem:triple or sem:iri objects from external, or pass through the string values, and cast them inside the code.
HTH!

Resources