Search code examples
xmlfull-text-searchxqueryxml-database

xquery: search across variable set of nodes


I am using BaseX XML Database. Consider an xml document in the database like so:

<entries>
<book-entry>
  <book>Book 1</book>
  <author>Author 1 ABC</author>
  <title>Title 1</title>
</book-entry>
<car-entry>
  <car>Car 1</car>
  <model>Model 1</model>
  <price>Price 1 ABC</price>
</car-entry>
</entries>

I am trying to perform a search with different options such as : search across books only, cars only, both books and cars.

I am trying to use an xml variable in my xquery to return search results based on the required search type.

Example variable values: - <types><type>book-entry</type></types> : search across book-entries only - <types><type>car-entry</type></types> : search across car-entries only - <types><type>book-entry</type><type>car-entry</type></types> : search across book-entries and car-entries

XQuery Sample:

declare variable $doc_name as xs:string external; (: name of xml document :)
declare variable $search_types as xs:anyAtomicType external; (: one of the example variable values shown above :)
declare variable $search_key as xs:string external; (: eg: ABC :)

for $entry in doc($doc_name)/entries[*[exists($search_types/types/type/text() = node-name(.)) and .//text() contains text $search_key]]
  return $entry

The above query returns both car and book entries which contain a text child node ABC although I pass <types><type>car-entry</type></types> to $search_types.

How do I restrict the search using an xml variable ? Is there a better way of doing this? Also, the xquery must return both cars and entries if the xml variable has child nodes of both the types.

Thanks, Sony


Solution

  • for $entry in doc($doc_name)/entries
             [*[exists($search_types/types/type/text() = node-name(.)) 
            and 
              .//text() contains text $search_key
               ]
             ]  return $entry
    

    Must be:

    for $entry in doc($doc_name)/entries/*
            [exists($search_types/types/type/text() = node-name(.)) 
           and 
             .//text() contains text $search_key]  
      return $entry
    

    Or, alternatively, this simple XPath expression may be used:

    /*/*[name() eq $vSearchTypes/types/type
       and
         .//text()[contains(., $vSearchKey)]
        ]
    

    Finally, this XQuery expression:

    let $vSearchTypes :=
      <types>
        <type>book-entry</type>
    </types>,
    
    $vSearchKey := 'ABC'
    
    return
      /*/*[name(.) eq $vSearchTypes/type
            and
              .//text()[contains(., $vSearchKey)]
             ]
    

    when applied on the provided XML document:

    <entries>
      <book-entry>
        <book>Book 1</book>
        <author>Author 1 ABC</author>
        <title>Title 1</title>
      </book-entry>
      <car-entry>
        <car>Car 1</car>
        <model>Model 1</model>
        <price>Price 1 ABC</price>
      </car-entry>
    </entries>
    

    produces the wanted, correct result:

    <book-entry>
        <book>Book 1</book>
        <author>Author 1 ABC</author>
        <title>Title 1</title>
      </book-entry>