Knowledgebase: MarkLogic Server
XPath not() with path fields
22 November 2016 07:01 PM

Introduction

Fields are a great way of restricting what parts of your documents to search, based on XML element QNames or JSON propertyNamesFields are extremely useful when you have content in one or more elements or JSON properties that you want to query simply and efficiently as a single unit. But can you use field names you've created with XPath's fn:not ()? In other words, given a field name "test-field-name" can you do something like fn:not(//test-field-name)? Unfortunately, you can not, as the server will return an XDMP-UNINDEXABLEPATH error. There is, however, a workaround.

Workaround

The workaround is to create two fields, then to query across those two fields using cts:not-in-query ( http://docs.marklogic.com/cts:not-in-query), Consider two documents:

Document 1

xdmp:document-insert(
  "/test/fields-001.xml",
  <doc>
      <content>
          <courtcase>
              <metadata>
                  <docinfo>
                      <hier>
                          <hierlev>
                              <heading>
                                  <title>1900</title>
                              </heading>
                              <hierlev>
                                  <heading>
                                      <title>Volume 10</title>
                                  </heading>
                                  <hierlev>
                                      <heading>
                                          <title>test title - (1900) 10 Ch.D. 900</title>
                                      </heading>
                                  </hierlev>
                              </hierlev>
                          </hierlev>
                      </hier>
                  </docinfo>
              </metadata>
          </courtcase>
      </content>
</doc> ,
xdmp:default-permissions(),
("test", "fields")
)

Document 2

xdmp:document-insert(
  "/test/fields-002.xml",
<doc>
    <content>
        <courtcase>
              <metadata>
                     <docinfo>
                            <hier>
                                  <hierlev>
                                       <heading>
                                              <title>1879</title>
                                        </heading>
                                        <hierlev>
                                            <heading>
                                                  <title>John had a little lamb</title>
                                            </heading>
                                            <hierlev>
                                                  <heading>
                                                        <title>Mary had a little lamb</title>
                                                  </heading>
                                              </hierlev>
                                         </hierlev>
                                   </hierlev>
                               </hier>
                          </docinfo>
                     </metadata>
                </courtcase>
            </content>
</doc> ,
xdmp:default-permissions(),
("test", "fields")
)

Say you're interested in three different paths:

1) All titles, Which Should be defined as fn:collection()//heading/title

2) Titles with lower-level titles, Which Should be defined as fn:collection()//hierlev[.//hierlev/heading/title]/heading/title

3) Titles with NO lower-level titles, Which Should be defined as fn:collection()//hierlev[fn:not(.//hierlev/heading/title)]/heading/title

Unfortunately, while we can express #3 in full XPath, we can not express #3 in the subset of XPath used to describe path fields. However, you can emulate #3 by defining fields corresponding to #1 & #2, then combining them in a cts:not-in-query.

Create the path fields
  1. All titles

  Create a Path Field with name "titles-all" path "//heading/title"

  1. Titles with lower-level titles

  Create a Path Field with name "titles-with-lower-level titles," path "//hierlev[.//hierlev/heading/title]/heading/title"

Emulate the XPath you want by combining these two newly created path fields in a cts: not-in-query ()

for $doc in cts:search(

  fn:collection("fields"),

  cts:not-in-query(

    cts:field-word-query(

      "titles-all",

      $term

      ) ,

    cts:field-word-query(

      "titles-with-lower-level-titles",

      $term

      )

    )

  )

return

  xdmp:node-uri($doc)

(0 vote(s))
Helpful
Not helpful

Comments (0)