marklogicmarklogic-9schematron

Calling external API and library functions from Schematron in MarkLogic


With proper <sch:ns/> declared for cts and dohickey, I can call MarkLogic API functions such as cts:* from compiled Schematron as follows (which is awesome, by the way):

<sch:rule context="dohickey:thingummy">
    <sch:let name="this-id" value="dohickey:meta/dohickey:id/string()"/>
    <sch:let name="known-ids" value="cts:element-values(xs:QName('dohickey:id'))"/>
    <sch:report test="$this-id = $known-ids">Warning: This id is known, and that's a business error</sch:report>
</sch:rule>

(Please ignore that the $known-ids variable would be optimized to lock down on a single value in real life, instead of returning them all---for the sake of the example)

Now I'd like to include custom XQuery modules in my Schematron in the same way that I can call cts:* in the example above, and in the same way I can call XQuery library functions when working with raw XSLT and MarkLogic XSLT/XQuery integration by defining, e.g., an xsl:stylesheet/@extension-element-prefixes value, followed by an appropriate <xdmp:import-module/>.

Example: I'd like to do the following:

<sch:rule context="dohickey:thingummy">
    <sch:let name="this-id" value="dohickey:meta/dohickey:id/string()"/>
    <sch:let name="is-flagged-for-revision" value="mycustommodule:is-flagged-for-revision($this-id)"/>
    <sch:report test="$is-flagged-for-revision">Warning: This id has been flagged for revision</sch:report>
</sch:rule>

Question: is there currently a way to get MarkLogic's schematron:put compilation behavior to include custom XQuery module imports, analogous to MarkLogic's XSLT <xdmp:import-module/> support? Or should I just implement a wrapper that transforms the schematron:put compiled result to include what I need so it's in the compiled XSLT?


Solution

  • As you mentioned yourself, you can use the <xdmp:import-module> instruction to import an XQuery library module into an XSLT stylesheet in MarkLogic. Once you have imported the module, any functions defined in that module are available to that stylesheet.

    It is possible to incorporate foreign vocabularies in your Schematron schema. You can use that to include an xdmp:import-module into the compiled schema. However, you need to set the allow-foreign parameter to true when you compile the Schematron. The default is false.

    Within your Schematron schema, you first need to declare the namespace(s) using sch:ns, so that the prefixes are recognized:

    <sch:ns prefix="search" uri="http://marklogic.com/appservices/search"/>
    <sch:ns prefix="xdmp" uri="http://marklogic.com/xdmp"/>
    

    You add your foreign markup, such as xdmp:import-module to import an XQuery library module. Put the import at the root-level of you schema:

    <xdmp:import-module namespace="http://marklogic.com/appservices/search" 
      href="/MarkLogic/appservices/search/search.xqy"/>
    

    You can use the imported library function(s) within your Schematron directly using the prefix defined earlier:

    <sch:rule context="test">
      <sch:let name="estimate" value="search:estimate(search:parse(@term))"/>
      <sch:assert test="$estimate gt 0" diagnostics="d1">At least one doc should be found</sch:assert>
    </sch:rule>
    

    Just make sure that when you compile the Schematron schema into an XSLT, and load it into your modules database using schematron:put(), you set the allow-foreign option to true() as follows:

    xquery version "1.0-ml"; 
    import module namespace schematron = "http://marklogic.com/xdmp/schematron" 
      at "/MarkLogic/schematron/schematron.xqy";
    
    let $params := map:map()
      => map:with('allow-foreign', fn:true())
    return
      schematron:put("/mySchema.sch", $params)