1

My attempt to ask this before was apparently too convoluted, trying again! I am composing a search in Xquery. In one of the fields (title) it should be possible to enter multiple keywords. At the moment only ONE keyword works. When there is more than one there is the error ERROR XPTY0004: The actual cardinality for parameter 1 does not match the cardinality declared in the function's signature: concat($atomizable-values as xs:anyAtomicType?, ...) xs:string?. Expected cardinality: zero or one, got 2.

In my xquery I am trying to tokenize the keywords by \s and then match them individually. I think this method is probably false but I am not sure what other method to use. I am obviously a beginner!!

Here is the example XML to be searched:

<files>

<file>
<identifier>
    <institution>name1</institution>
    <idno>signature</idno>
</identifier>
<title>Math is fun</title>
</file>

<file>
<identifier>
    <institution>name1</institution>
    <idno>signature1</idno>
</identifier>
<title>philosophy of math</title>
</file>

<file>
<identifier>
    <institution>name2</institution>
    <idno>signature2</idno>
</identifier>
<title>i like cupcakes</title>
</file>

</files>

Here is the Xquery with example input 'math' for the search field title and 'name1' for the search field institution. This works, the search output are the titles 'math is fun' and 'philosophy of math'. What doesn't work is if you change the input ($title) to 'math fun'. Then you get the error message. The desired output is the title 'math is fun'.

xquery version "3.0";

let $institution := 'name1'
let $title := 'math' (:change to 'math fun' and doesn't work anymore, only a single word works:)


let $title-predicate := 
if ($title)
 then
    if (contains($title, '"'))
    then concat("[contains(lower-case(title), '", replace($title, '["]', ''), "')]")  (:This works fine:)
    else
    for $title2 in tokenize($title, '\s') (:HERE IS THE PROBLEM, this only works when the input is a single word, for instance 'math' not 'math fun':)
    return
    concat("[matches(lower-case(title), '", $title2, "')]")
else ()


let $institution-predicate := if ($institution) then concat('[lower-case(string-join(identifier/institution))', " = '", $institution, "']") else ()


let $eval-string := concat
("doc('/db/Unbenannt.xml')//file", 
$institution-predicate,
$title-predicate
)


let $records := util:eval($eval-string) 
let $test := count($records)
let $content :=

                        <inner_container>
                            <div>
                                <h2>Search Results</h2>

                              <ul>
                               {
                              for $record in $records
                              return
                                <li id="searchList">  
                                <span>{$record//institution/text()}</span> <br/>
                                <span>{$record//title/text()}</span>
                                </li>
                                }
                              </ul>
                            </div>
                            </inner_container>

return 
$content
Lissy
  • 61
  • 5

2 Answers2

1

You have to wrap your FLWOR expression with string-join():

string-join(
    for $title2 in tokenize($title, '\s')
    return
    concat("[matches(lower-case(title), '", $title2, "')]")
)
S. Dumont
  • 26
  • 3
  • Thanks! I tried sting-join but put it inside concat, not around the whole expression as you suggest. Your answer works perfectly. Such a simple solution to a (for me) tricky problem. – Lissy Oct 24 '16 at 14:21
0

If tokenize($title) returns a sequence of strings, then

for $title2 in tokenize($title, '\s')
return concat("[matches(lower-case(title), '", $title2, "')]")

will also return a sequence of strings

Therefore $title-predicate will be a sequence of strings, and you can't supply a sequence of strings as one of the arguments to concat().

So it's clear what's wrong, but fixing it requires a deeper understanding of your query than I have time to acquire.

I find it hard to believe that the approach of generating a query as a string and then doing dynamic evaluation of that query is really necessary.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Thanks for the comment. This is what I suspected. I guess I need to re-conceptualize the whole thing, but as it is my first time writing a search in xquery I'm not sure what other method to use. – Lissy Oct 22 '16 at 14:04