3

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

sony
  • 1,557
  • 10
  • 36
  • 50

2 Answers2

2
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>
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • I passed an xml node value "book-entry" to the parameter $search_types. But, the query you suggested still returns both book-entry and car-entry rather than just car-entry as expected. I am using an and condition so am wondering why the first part of the and condition is ignored - Sony – sony May 23 '11 at 03:28
  • @sony: I added to my answer a simple XPath expression that I tested to work correctly. Unfortunately, I am not familiar with XQuery Full Text. – Dimitre Novatchev May 23 '11 at 03:47
  • 1
    @sony: I also added a complete XQuery expression that when applied on your XML document produces the correct result -- do have a look. – Dimitre Novatchev May 23 '11 at 04:02
1

for your Question 1 - u could try with fn:data() to escape all user input values from Xml specific thing.

for your Question 2 - if your using any Xml database try to leverage Database Search APIs than Xpath. if not you need to come up with some defined abstract xquery search layer which can form the XPath grammer rather than following through xpath directly.

kadalamittai
  • 2,076
  • 1
  • 16
  • 19