XQuery - Matching by Paths or Attributes
- Last Updated: April 14, 2026
- 3 minute read
- MarkLogic Server
- Version 11.0
- Documentation
This next example shows how protected paths can be used with fn:contains() and fn:matches(). The example uses the same roles from the previous example, adding a new role (els-role-3).
First unprotect the protected paths from the previous example:
(: unprotect the protected paths -> run against the Security database :)
xquery version "1.0-ml";
import module namespace sec = "http://marklogic.com/xdmp/security"
at "/MarkLogic/security.xqy";
sec:unprotect-path("secret", ()),
sec:unprotect-path("top-secret", ())
Note: Adding or unprotecting protected paths will trigger reindexing. After unprotecting elements, you must wait for reindexing to finish.
Create a new role els-role-3 and add els-user-3 to the role. See Create Roles and Create Users and Assign Roles for details.
Add a new document with permissions to the Documents database:
(: run this against the Documents database :)
xquery version "1.0-ml";
xdmp:document-insert(
"attributes.xml", <root>
<title>Document Title</title>
<summary>Summary of document contents</summary>
<executive-summary>Executive summary of contents
<info attr="EU">Only role with "EU" attribute can read this summary </info>
<info attr="UK">Only role with "UK" attribute can read this summary </info>
<info attr="US">Only role with "US" attribute can read this summary </info>
</executive-summary>
<content>Contents of document
Unclassified content
<notes>
<info attr="EU">Only role with "EU" attribute can read this content</info>
<info attr="UK">Only role with "UK" attribute can read this content</info>
<info attr="US">Only role with "US" attribute can read this content</info>
</notes>
</content>
</root>,
(xdmp:permission("els-role-1", "read"), xdmp:permission("els-role-2", "read"), xdmp:permission("els-role-3", "read"),
xdmp:permission("els-role-1", "update"), xdmp:permission("els-role-2", "update"), xdmp:permission("els-role-3", "update")))
Add the new protected paths with permissions for roles to the Security database:
(: add new protected paths -> run against the Security database :)
xquery version "1.0-ml";
import module namespace sec="http://marklogic.com/xdmp/security"
at "/MarkLogic/security.xqy";
sec:protect-path("//info[fn:matches(@attr, 'US')]", (), (xdmp:permission("els-role-1", "read"))),
sec:protect-path("//info[fn:matches(@attr, 'UK')]", (), (xdmp:permission("els-role-2", "read"),
xdmp:permission("els-role-3", "read"))),
sec:protect-path("//info[fn:matches(@attr, 'EU')]", (), (xdmp:permission("els-role-3", "read")))
=>
Returns three numbers representing the protected paths
Note: Adding, unprotecting, or changing permissions on protected paths will trigger reindexing.
Notice that the protected paths include attributes in the document elements. Also note that els-role-3 has permissions for two protected paths (@attr, ‘UK’ and @attr, ‘EU’).
Run this next query, similar to the previous queries, this time looking for the attributes.xml document. First query in the context of els-user-1 who has a role that can see the “US” attribute:
(: run this against the Documents database :)
xdmp:eval('fn:doc("attributes.xml")',(),
<options xmlns="xdmp:eval">
<user-id>{xdmp:user("els-user-1")}</user-id>
</options>
)
=>
<?xml version="1.0" encoding="UTF-8"?>
<root>
<title>Document Title</title>
<summary>Summary of document contents</summary>
<executive-summary>Executive summary of contents
<info attr="US">Only role having "US" attribute can read this summary</info>
</executive-summary>
<content>Contents of document
Unclassified content
<notes>
<info attr="US">Only role having "US" attribute can read this content
</info>
</notes>
</content>
</root>
Next modify the query to run in the context of els-user-2, who has a role that can see the “UK” attribute:
(: run this against the Documents database :)
xdmp:eval('fn:doc("attributes.xml")',(),
<options xmlns="xdmp:eval">
<user-id>{xdmp:user("els-user-2")}</user-id>
</options>
)
=>
<?xml version="1.0" encoding="UTF-8"?>
<root>
<title>Document Title</title>
<summary>Summary of document contents</summary>
<executive-summary>Executive summary of contents
<info attr="UK">Only role having "UK" attribute can read this summary
</info>
</executive-summary>
<content>Contents of document
Unclassified content
<notes>
<info attr="UK">Only role having "UK" attribute can read this content</info>
</notes>
</content>
</root>
And finally modify the query to run in the context of els-user-3:
(: run this against the Documents database :)
xdmp:eval('fn:doc("attributes.xml")',(),
<options xmlns="xdmp:eval">
<user-id>{xdmp:user("els-user-3")}</user-id>
</options>
)
=>
<?xml version="1.0" encoding="UTF-8"?>
<root>
<title>Document Title</title>
<summary>Summary of document contents</summary>
<executive-summary>Executive summary of contents
<info attr="EU">Only role having "EU" attribute can read this summary
</info>
<info attr="UK">Only role having "UK" attribute can read this summary
</info>
</executive-summary>
<content>Contents of document
Unclassified content
<notes>
<info attr="EU">Only role having "EU" attribute can read this content
</info>
<info attr="UK">Only role having "UK" attribute can read this content
</info>
</notes>
</content>
</root>
The els-user-3 has protected path permissions on both elements with the “EU” info attribute and the elements with the “UK” info attribute, so the els-user-3 can see both elements. If you are getting different results, check to be sure that you created an els-role-3 and added the els-user-3 to that role.
Note: If you run the query in the context of the admin user, you will be able to see the entire document because the query is using fn:doc.