Search code examples
xpathxquerycontainstokenizeconcatenation

Multiple contains() based on tokenised string


I want to match results from several partial terms search in a XQuery function

  • "joh do" matches "john doe"
  • "do jo" also matches "john doe"
  • etc.

With contains(), only "john do" or "joh" would match results.

$item[contains(., "john do")]

I'd like to do so...

$item[contains(., "joh") and contains(., "do")]

...no matter how many terms are in the search string.

I'm trying to use tokenize(), then a loop on it to create what I want

let $search := "john do"
let $terms := fn:tokenize($search, '\s')  
let $query := string-join(
    (for $t in $terms
     return 
       concat('contains(.,"', $t, '")')
     ), ' and '
)
return $query

The result of that loop is exactly as I expected it to be, but it has no effect on the XPATH query, as it were just text (and obviously concat() produce just text)

$item[$query] 

Did I miss something ? Is any function better than concat() for that example ?

Thanks for any help !


Solution

  • Your approach is simply not a good idea. You could do it that way, but I would strongly recommend you don't. What you are trying to do is construct a XPath/XQuery expression to evaluate it, i.e. you create your code. This is usually not a good idea.

    Instead, you can check the condition in a for loop instead of creating this query. Even better, XQuery and XPath (but only XPath 2.0) have quantified expression, which fit perfectly for your use case:

    for $item in $items
    let $search := "john do"
    let $terms := fn:tokenize($item, '\s')
    where every $term in $terms satisfies contains($search, $term)
    return $item
    

    This is hopefully easy to grasp, because it is very close to natural language: Every term has to satisfy a certain condition (in your case a contains())