This is a simple example for a basic full-text expression:
<syntaxhighlight pre lang="'xquery"'>
"This is YOUR World" contains text "your world"
</syntaxhighlightpre>
It yields {{Code|true}}, because the search string is ''tokenized'' before it is compared with the tokenized input string. In the tokenization process, several normalizations take place. Many of those steps can hardly be simulated with plain XQuery: as an example, upper/lower case and diacritics (umlauts, accents, etc.) are removed and an optional, language-dependent stemming algorithm is applied. Beside that, special characters such as whitespaces and punctuation marks will be ignored. Thus, this query also yields true:
<syntaxhighlight pre lang="'xquery"'>
"Well... Done!" contains text "well, done"
</syntaxhighlightpre>
The {{Code|occurs}} keyword comes into play when more than one occurrence of a token is to be found:
<syntaxhighlight pre lang="'xquery"'>
"one and two and three" contains text "and" occurs at least 2 times
</syntaxhighlightpre>
Various range modifiers are available: {{Code|exactly}}, {{Code|at least}}, {{Code|at most}}, and {{Code|from ... to ...}}.
In the given example, curly braces are used to combine multiple keywords:
<syntaxhighlight pre lang="'xquery"'>
for $country in doc('factbook')//country
where $country//religions[text() contains text { 'Sunni', 'Shia' } any]
return $country/name
</syntaxhighlightpre>
The query will output the names of all countries with a religion element containing {{Code|sunni}} or {{Code|shia}}. The {{Code|any}} keyword is optional; it can be replaced with:
The keywords {{Code|ftand}}, {{Code|ftor}} and {{Code|ftnot}} can also be used to combine multiple query terms. The following query yields the same result as the last one does:
<syntaxhighlight pre lang="'xquery"'>
doc('factbook')//country[descendant::religions contains text 'sunni' ftor 'shia']/name
</syntaxhighlightpre>
The keywords {{Code|not in}} are special: they are used to find tokens which are not part of a longer token sequence:
<syntaxhighlight pre lang="'xquery"'>
for $text in ("New York", "new conditions")
return $text contains text "New" not in "New York"
</syntaxhighlightpre>
Due to the complex data model of the XQuery Full Text spec, the usage of {{Code|ftand}} may lead to a high memory consumption. If you should encounter problems, simply use the {{Code|all}} keyword:
<syntaxhighlight pre lang="'xquery"'>
doc('factbook')//country[descendant::religions contains text { 'Christian', 'Jewish'} all]/name
</syntaxhighlightpre>
==Positional Filters==
A popular retrieval operation is to filter texts by the distance of the searched words. In this query…
<syntaxhighlight pre lang="'xquery"'>
<xml>
<text>There is some reason why ...</text>
<text>The reason why some people ...</text>
</xml>//text[. contains text { "some", "reason" } all ordered distance at most 3 words]
</syntaxhighlightpre>
…the two first texts will be returned as result, because there are at most three words between {{Code|some}} and {{Code|reason}}. Additionally, the {{Code|ordered}} keyword ensures that the words are found in the specified order, which is why the third text is excluded. Note that {{Code|all}} is required here to guarantee that only those hits will be accepted that contain all searched words.
The {{Code|window}} keyword is related: it accepts those texts in which all keyword occur within the specified number of tokens. Can you guess what is returned by the following query?
<syntaxhighlight pre lang="'xquery"'>
("A C D", "A B C D E")[. contains text { "A", "E" } all window 3 words]
</syntaxhighlightpre>
Sometimes it is interesting to only select texts in which all searched terms occur in the {{Code|same sentence}} or {{Code|paragraph}} (you can even filter for {{Code|different}} sentences/paragraphs). This is obviously not the case in the following example:
<syntaxhighlight pre lang="'xquery"'>
'Mary told me, “I will survive!”.' contains text { 'will', 'told' } all words same sentence
</syntaxhighlightpre>
By the way: In some examples above, the {{Code|words}} unit was used, but {{Code|sentences}} and {{Code|paragraphs}} would have been valid alternatives.
* If {{Code|case}} is insensitive, no distinction is made between characters in upper and lower case. By default, the option is {{Code|insensitive}}; it can also be set to {{Code|sensitive}}:
<syntaxhighlight pre lang="'xquery"'>
"Respect Upper Case" contains text "Upper" using case sensitive
</syntaxhighlightpre>
* If {{Code|diacritics}} is insensitive, characters with and without diacritics (umlauts, characters with accents) are declared as identical. By default, the option is {{Code|insensitive}}; it can also be set to {{Code|sensitive}}:
<syntaxhighlight pre lang="'xquery"'>
"'Äpfel' will not be found..." contains text "Apfel" using diacritics sensitive
</syntaxhighlightpre>
* If {{Code|stemming}} is activated, words are shortened to a base form by a language-specific stemmer:
<syntaxhighlight pre lang="'xquery"'>
"catch" contains text "catches" using stemming
</syntaxhighlightpre>
* With the {{Code|stop words}} option, a list of words can be defined that will be ignored when tokenizing a string. This is particularly helpful if the full-text index takes too much space (a standard stopword list for English texts is provided in the directory {{Code|etc/stopwords.txt}} in the full distributions of BaseX, and available online at http://files.basex.org/etc/stopwords.txt):
<syntaxhighlight pre lang="'xquery"'>
"You and me" contains text "you or me" using stop words ("and", "or"),
"You and me" contains text "you or me" using stop words at "http://files.basex.org/etc/stopwords.txt"
</syntaxhighlightpre>
* Related terms such as synonyms can be found with the sophisticated [[#Thesaurus|Thesaurus]] option.
* <code>.{min,max}</code> matches ''min''–''max'' number of characters.
<syntaxhighlight pre lang="'xquery"'>
"This may be interesting in the year 2000" contains text { "interest.*", "2.{3,3}" } using wildcards
</syntaxhighlightpre>
This was a quick introduction to XQuery Full Text; you are invited to explore the numerous other features of the language!
A list of all language codes that are available on your system can be retrieved as follows:
<syntaxhighlight pre lang="'xquery"'>
declare namespace locale = "java:java.util.Locale";
distinct-values(locale:getAvailableLocales() ! locale:getLanguage(.))
</syntaxhighlightpre>
By default, unless the languages codes <code>ja</code>, <code>ar</code>, <code>ko</code>, <code>th</code>, or <code>zh</code> are specified, a tokenizer for Western texts is used:
The following two queries, which both return <code>true</code>, demonstrate that stemming depends on the selected language:
<syntaxhighlight pre lang="'xquery"'>
"Indexing" contains text "index" using stemming,
"häuser" contains text "haus" using stemming using language "German"
</syntaxhighlightpre>
==Scoring==
The scoring model of BaseX takes into consideration the number of found terms, their frequency in a text, and the length of a text. The shorter the input text is, the higher scores will be:
<syntaxhighlight pre lang="'xquery"'>
(: Score values: 1 0.62 0.45 :)
for $text in ("A", "A B", "A B C")
order by $score descending
return <hit score='{ format-number($score, "0.00") }'>{ $text }</hit>
</syntaxhighlightpre>
This simple approach has proven to consistently deliver good results, in particular when little is known about the structure of the queried XML documents.
Scoring values can be further processed to compute custom values:
<syntaxhighlight pre lang="'xquery"'>
let $terms := ('a', 'b')
let $scores := ft:score($terms ! ('a b c' contains text { . }))
return avg($scores)
</syntaxhighlightpre>
Please note that scoring propagation was removed with Scoring is supported within full-text expressions, by {{MarkFunction|Full-Text|Version 9.5ft:search}}. The following expressions will now yield , and by simple predicate tests that can be rewritten to {{CodeFunction|Full-Text|0ft:search}}: <syntaxhighlight lang="xquery">for $n score $s in db:open('factbook')//religions[text() contains text 'orthodox']return $s,
<pre lang='xquery'>
let $string := 'a b'
return ft:score($string contains text 'a' and $string contains text ftand 'b')</syntaxhighlight>,
Scoring is still supported within full-text expressions and by {{Function|Full-Text|ft:search}}:
<syntaxhighlight lang="xquery">
for $n score $s in ft:search('factbook', 'orthodox')
order by $s descendingreturn $s|| ': ' || $n,
let for $n score $string s in db:= get('a bfactbook'return ft:score)//text($string )[. contains text 'aorthodox' ftand ]order by $s descendingreturn $s || 'b: ')|| $n</syntaxhighlightpre> The reason for removing the scoring propagation was that the storage of scoring values required additional memory, even if scoring is not required.
==Thesaurus==
BaseX supports One or more thesaurus files can be specified in a full-text queries using thesauri, but it does not provide a default thesaurusexpression. This is why queries such asThe following query returns {{Code|false}}:
<syntaxhighlight pre lang="'xquery"'>'computershardware' contains text 'hardwarecomputers'
using thesaurus default
</syntaxhighlightpre>
will return <code>false</code>. However, if the If a thesaurus is specified, then the result will be <code>true</code>:employed…
<syntaxhighlight pre lang="xml"><thesaurus xmlns="http://www.w3.org/2007/xqftts/thesaurus"> <entry> <term>computers</term> <synonym> <term>hardware</term> <relationship>NT</relationship> </synonym> </entry></thesaurus></pre> …the result will be {{Code|true}}: <pre lang='xquery"'>'hardware' contains text 'computers' using thesaurus at 'thesaurus.xml'</pre> Thesaurus files must comply with the [https://dev.w3.org/2007/xpath-full-text-10-test-suite/TestSuiteStagingArea/TestSources/thesaurus.xsd XSD Schema] of the XQFT Test Suite (but the namespace can be omitted). Apart from the relationship defined in [https://www.iso.org/standard/7776.html ISO 2788] (NT: narrower team, RT: related term, etc.), custom relationships can be used. The type of relationship and the level depth can be specified as well: <pre lang='xquery'>(: BT: find broader terms; NT means narrower term :)
'computers' contains text 'hardware'
using thesaurus at 'XQFTTS_1_0_4/TestSources/usability2x.xml'relationship 'BT' from 1 to 10 levels</syntaxhighlightpre>
The format of the thesaurus files must More details can be the same as the format of the thesauri provided by found in the [https://devwww.w3.org/2007TR/xpath-full-text-10-test-suite XQuery and XPath Full Text 1.0 Test Suite]. It is an XML with structure defined by an [https://dev.w3.org/2007/xpath-full-text-10-test-suite/TestSuiteStagingArea/TestSources/thesaurus.xsd XSD Schema#ftthesaurusoption specification].
==Fuzzy Querying==
'''Document 'doc.xml'''':
<syntaxhighlight pre lang="xml">
<doc>
<a>house</a>
<a>haus</a>
</doc>
</syntaxhighlightpre>
'''Query:'''
<syntaxhighlight pre lang="'xquery"'>
//a[text() contains text 'house' using fuzzy]
</syntaxhighlightpre>
'''Result:'''
<syntaxhighlight pre lang="xml">
<a>house</a>
<a>hous</a>
</syntaxhighlightpre> Fuzzy search is based on the Levenshtein distance. The maximum number of allowed errors is calculated by dividing the token length of a specified query term by 4. The query above yields two results as there is no error between the query term “house” and the text node “house”, and one error between “house” and “hous”.
Fuzzy search is based on the Levenshtein distance. The maximum number of allowed errors is calculated by dividing the token length of a specified query term by 4, preserving a minimum of 1 errors. A static error distance user-defined value can be set by adjusting adjusted globally via the {{Option|LSERROR}} option (defaultor via an additional argument: <code>SET LSERROR 0</code>). The query above yields two results as there is no error between the query term “house” and the text node “house”, and one error between “house” and “hous”.
Fuzzy search is also supported by the full-<pre lang='xquery'>//a[text index.() contains text 'house' using fuzzy 3 errors]</pre>
=Mixed Content=
When working with so-called narrative XML documents, such as HTML, [https://tei-c.org/ TEI], or [https://docbook.org/ DocBook] documents, you typically have ''mixed content'', i.e., elements containing a mix of text and markup, such as:
<syntaxhighlight pre lang="xml">
<p>This is only an illustrative <hi>example</hi>, not a <q>real</q> text.</p>
</syntaxhighlightpre>
Since the logical flow of the text is not interrupted by the child elements, you will typically want to search across elements, so that the above paragraph would match a search for “real text”. For more examples, see [https://www.w3.org/TR/xpath-full-text-10-use-cases/#Across XQuery and XPath Full Text 1.0 Use Cases].
To enable this kind of searches, it is recommendable to:
* Turn off Keep ''whitespace choppingstripping'' turned off when importing XML documents. This can be done by setting ensuring that {{Option|CHOPSTRIPWS}} to <code>OFF</code>is disabled. This can also be done in the GUI if a new database is created (''Database'' → ''New…'' → ''Parsing'' → ''Chop Strip Whitespaces'').* Turn off Keep automatic indentation by assigning <code>turned off. Ensure that the [[Serialization|serialization parameter]] {{Code|indent=no</code> }} is set to the {{OptionCode|SERIALIZERno}} option.
A query such as <code>//p[. contains text 'real text']</code> will then match the example paragraph above. However, the full-text index will '''not''' be used in this query, so it may take a long time. The full-text index would be used for the query <code>//p[text() contains text 'real text']</code>, but this query will not find the example paragraph, because the matching text is split over two text nodes.
Note that the node structure is ignored by the full-text tokenizer: The {{Code|contains text}} expression applies all full-text operations to the ''string value'' of its left operand. As a consequence, the <code>{{Function|Full-Text|ft:mark</code> }} and <code>{{Function|Full-Text|ft:extract</code> }} functions (see [[Full-Text Module|Full-Text Functions]]) will only yield useful results if they are applied to single text nodes, as the following example demonstrates:
<syntaxhighlight pre lang="'xquery"'>
(: Structure is ignored; no highlighting: :)
ft:mark(//p[. contains text 'real'])
(: Single text nodes are addressed: results will be highlighted: :)
ft:mark(//p[.//text() contains text 'real'])
</syntaxhighlightpre>
BaseX does '''not''' support the ''ignore option'' (<code>without content</code>) of the [https://www.w3.org/TR/xpath-full-text-10/#ftignoreoption W3C XQuery Full Text 1.0] Recommendation. If you want to ignore descendant element content, such as footnotes or other material that does not belong to the same logical text flow, you can build a second database from and exclude all information you do not want to search avoid searching for. See the following example (visit [[XQuery Update]] to learn more about updates):
<syntaxhighlight pre lang="'xquery"'>let $docs := db:openget('docs')
return db:create(
'index-db',
map { 'ftindex': true() }
)
</syntaxhighlightpre>
=Functions=
* If a default collation is specified, it applies to all collation-dependent string operations in the query. The following expression yields <code>true</code>:
<syntaxhighlight pre lang="'xquery"'>
declare default collation 'http://basex.org/collation?lang=de;strength=secondary';
'Straße' = 'Strasse'
</syntaxhighlightpre>
* Collations can also be specified in {{Code|order by}} and {{Code|group by}} clauses of FLWOR expressions. This query returns {{Code|à plutôt! bonjour!}}:
<syntaxhighlight pre lang="'xquery"'>
for $w in ("bonjour!", "à plutôt!") order by $w collation "?lang=fr" return $w
</syntaxhighlightpre>
* Various string function exists that take an optional collation as argument: The following functions give us {{Code|a}} and {{Code|1 2 3}} as results:
<syntaxhighlight pre lang="'xquery"'><nowiki>
distinct-values(("a", "á", "à"), "?lang=it-IT;strength=primary"),
index-of(("a", "á", "à"), "a", "?lang=it-IT;strength=primary")
</nowiki></syntaxhighlightpre>
If the [http://site.icu-project.org/download ICU Library] is added to the classpath, the full [https://www.w3.org/TR/xpath-functions-31/#uca-collations Unicode Collation Algorithm] features become available:
<syntaxhighlight pre lang="'xquery"'>
(: returns 0 (both strings are compared as equal) :)
compare('a-b', 'ab', 'http://www.w3.org/2013/collation/UCA?alternate=shifted')
</syntaxhighlightpre>
=Changelog=
; Version 9.6
* Updated: [[#Fuzzy_Querying|Fuzzy Querying]]: Specify Levenshtein error
; Version 9.5:
* Removed: Scoring propagation.
; Version 9.2:
* Added: Arabic stemmer.
; Version 8.0:
* Updated: [[#Scoring|Scores]] will be propagated by the {{Code|and}} and {{Code|or}} expressions and in predicates.
; Version 7.7:
* Added: [[#Collations|Collations]] support.
; Version 7.3:
* Removed: Trie index, which was specialized on wildcard queries. The fuzzy index now supports both wildcard and fuzzy queries.
* Removed: TF/IDF scoring was discarded in favor of the internal scoring model.