XQuery/Lucene 搜尋
您希望對一個或多個 XML 文件執行全文關鍵詞搜尋。這可以透過使用 Lucene 索引擴充套件到 eXist 來完成。
Apache Lucene 全文搜尋框架作為全文索引新增到 eXist 1.4 中,取代了以前的原生全文索引。新的 Lucene 全文搜尋框架比 eXist 的傳統全文索引更快、更可配置且功能更豐富。它也將成為 W3C 的 XQuery 全文擴充套件實現的基礎。
eXist 將一個獨特的節點 ID 與 XML 文件中的每個節點關聯。此節點 ID 用作 Lucene 索引檔案中的 Lucene 文件 ID,即每個 XML 節點都成為一個 Lucene 文件。這意味著您可以高度自定義關鍵詞匹配到文件中每個節點的搜尋權重。例如,標題中關鍵詞的匹配得分可能高於文件正文中的匹配得分。這意味著在大量文件中檢索文件標題的搜尋命中更有可能在搜尋結果中排名第一。這意味著您的搜尋將具有比不保留文件結構的搜尋系統更高的精確率和召回率。
以下是關於如何使用 Lucene 的 eXist 文件
eXist 支援完整的 Lucene 查詢解析器語法(除了“欄位搜尋”)。
<test>
<p n="1">this paragraph tests the things made up by
<name>ron</name>.</p>
<p n="2">this paragraph tests the other issues made up by
<name>edward</name>.</p>
</test>
為了對該文件執行 Lucene 索引的全文搜尋,我們需要建立一個索引配置檔案,collection.xconf,描述應索引哪些元素和屬性,以及索引的各種詳細資訊。
<collection xmlns="http://exist-db.org/collection-config/1.0">
<index>
<!-- Enable the legacy full text index for comparison with Lucene -->
<fulltext default="all" attributes="no"/>
<!-- Lucene index is configured below -->
<lucene>
<analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer"/>
<analyzer id="ws" class="org.apache.lucene.analysis.WhitespaceAnalyzer"/>
<text match="//test"/>
</lucene>
</index>
</collection>
註釋
- 如果您的測試資料儲存在 db/test 中,則應將 collection.xconf 儲存到 db/system/config/db/test 中。索引配置檔案始終儲存在 system/config/db 內部的目錄結構中,該目錄結構與 db 的目錄結構同構。
- 建立或更新此索引配置檔案後,您需要重新索引資料。您可以使用基於 Java 的 eXist 管理客戶端來執行此操作,選擇測試集合並選擇“重新索引集合”,或者使用xmldb:reindex()函式,在 eXide 或 XQuery 沙箱中提供 xmldb:reindex('/db/test')。
- 雖然傳統全文索引對於基於 Lucene 的搜尋不是必需的,但在此示例配置中已明確啟用它,以便指出 Lucene 和傳統搜尋函式/運算子之間的表達相似性(即 Lucene 的 ft:query() 與傳統全文索引的 &=、|=、near()、text:match-all()、text:match-any())。
您可以為單個元素或屬性名稱 (qname="...") 或節點路徑 (match="...") 定義 Lucene 索引。
如果在 qname 上定義索引,例如 <text qname="test"/>,則僅在 <test> 上建立索引。傳遞給 Lucene 的是 <test> 的字串值,其中包括其所有後代文字節點的文字。使用此類索引,無法搜尋 <test> 下的節點,例如 <p> 或 <name>,因為此類節點都已摺疊。如果要能夠查詢後代節點,則應為其設定其他索引,例如 <text qname="p"/> 或 <text qname="name"/>。
如果在節點路徑上定義索引,如上所示使用 <text match="//test"/>,則 <test> 下的節點結構將保留在索引中,並且您仍然可以查詢後代節點,例如 <p> 或 <name>。這可以看作是在 <test> 下的所有元素上建立索引的快捷方式。請注意,根據文件,此功能“可能會發生變化”[1]。
在決定使用哪種方法時,您應該考慮文件的哪些部分將作為全文查詢的上下文。在考慮具體搜尋場景時,最好決定將其範圍縮小或擴大。
eXist 可以處理以兩種查詢語法表達的 Lucene 搜尋,即 Lucene 的標準查詢語法和特定於 eXist 的 XML 語法。在本節中,介紹了標準查詢語法。這是使用者可以在搜尋欄位中輸入的語法。
在當前上下文中搜索“Ron”將表示為 [ft:query(., 'ron')]。第一個引數儲存要搜尋的節點,此處為“.”,當前上下文節點。第二個引數提供查詢字串,此處僅為單詞“ron”。
ft:query() 函式允許使用 Lucene 萬用字元。
“?”可用於單個字元,“*”可用於零個、一個或多個字元:“edward”可以透過“ed?ard”和“e*d”找到。Lucene 標準查詢語法不允許“*”和“?”出現在單詞的開頭。但是,在 eXist 中,可以向查詢新增一個選項以允許在搜尋中使用前導萬用字元;請參閱eXist Lucene 文件。
模糊搜尋,在單詞末尾使用“~”可以檢索“ron”透過“don~”。可以透過附加 0.0f 到 1.0f 之間的一個數字來量化模糊度,從而可以透過 [ft:query(., 'don~0.6')] 檢索“ron”,但不能透過 [ft:query(., 'don~0.7')] 檢索。模糊度是基於 Levenshtein 距離或編輯距離演算法。[2]。預設值為 0.5。
可以使用布林運算子“AND”和“OR”,具有預期的語義。這有一個變體表示法:[ft:query(., 'edward AND ron')] 也可以寫成 [ft:query(., '+edward +ron')]。[ft:query(., '+edward ron')] 將要求“edward”存在,但不要求“ron”存在。“NOT”也可以使用:[ft:query(., 'edward NOT ron')] 查詢不帶“ron”的“edward”。“NOT”也可以用“-”表示:[ft:query(., '+edward -ron')]。運算子可以用括號分組,如 [ft:query(., '(edward OR ron) NOT things')]。
可以透過將短語放在引號中來搜尋短語:[ft:query(., '"other issues"')]。
eXist 中不支援使用 Lucene 標準查詢語法的查詢中的欄位、鄰近搜尋、範圍搜尋、提升和轉義保留字元。可以在索引期間進行提升:eXist Lucene 文件。
由於我們已將<test>元素索引為路徑,因此索引包含後代節點,對巢狀元素的查詢也會返回命中結果。
collection('/db/test')/test/p/name[ft:query(., 'edward')]
collection('/db/test')/test/p[ft:query(name, 'edward')]
如果我們使用<text qname="test"/>將qname test索引,我們將無法做到這一點。
上面collection.conf檔案中啟用的標準Lucene分析器(使用<analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer"/>),應用了Lucene預設的英語停用詞列表,並從索引中刪除了以下單詞:a、an、and、are、as、at、be、but、by、for、if、in、into、is、it、no、not、of、on、or、such、that、the、their、then、there、these、they、this、to、was、will、with。
如果要自定義停用詞列表,請指定一個分析器,該分析器包含您希望應用的停用詞列表(停用詞之間用換行符分隔)的檔案系統上的絕對位置,然後重新索引集合。
<analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer">
<param name="stopwords" type="java.io.File" value="/tmp/stop.txt"/>
</analyzer>
如果希望所有單詞都可搜尋,可以將stop.txt留空或省略對stop.txt的引用。
<analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer">
<param name="stopwords" type="java.io.File"/>
</analyzer>
進行這些更改後,重新啟動eXist並重新索引。
Lucene為每個匹配項分配一個相關性分數或排名。某個單詞在文件中出現的頻率越高,分數就越高。eXist會保留此分數,並且可以透過score函式訪問,該函式返回一個小數。
for $m in collection('/db/test')//p[ft:query(., 'tests ron')]
let $score := ft:score($m)
order by $score descending
return
<hit score="{$score}">{$m}</hit>
分數越高,命中結果的相關性就越高。
可以設定配置檔案,以便對文件中特定元素應用更高的搜尋權重。例如,在書籍標題中匹配關鍵字的排名將高於在書籍正文中匹配關鍵字的排名。
以下查詢是等效的(使用的索引除外)。
要使用新的Lucene查詢函式表達“匹配任何”(|=)傳統風格的全文查詢
collection('/db/test')//p[. |= 'tests edward']
可以使用以下方法
collection('/db/test')//p[ft:query(.,
<query>
<bool>
<term occur="should">tests</term>
<term occur="should">edward</term>
</bool>
</query>)]
要使用新的Lucene查詢函式表達“匹配所有”(&=)傳統風格的全文查詢
collection('/db/test')//p[. &= 'tests edward']
可以使用以下方法
collection('/db/test')//p[ft:query(.,
<query>
<bool>
<term occur="must">tests</term>
<term occur="must">edward</term>
</bool>
</query>)]
要使用新的Lucene查詢函式表達“匹配無”(not + |=)傳統風格的全文查詢
collection('/db/test')//p[not(. |= 'issues edward')]
可以使用以下方法
collection('/db/test')//p[not(ft:query(.,
<query>
<bool>
<term occur="should">issues</term>
<term occur="should">edward</term>
</bool>
</query>))]
請注意,最後一個不能表示為
collection('/db/test')//p[ft:query(.,
<query>
<bool>
<term occur="not">issues</term>
<term occur="not">edward</term>
</bool>
</query>)]
因為Lucene的NOT運算子不能單獨使用,必須存在“正”搜尋詞。
以下查詢是等效的,可以透過將它們作為此XQuery程式碼片段中$query的值來測試莎士比亞示例(隨eXist一起提供)。
declare option exist:serialize "highlight-matches=both";
let $query := 'query'
return //SPEECH[ft:query(., $query)]
| 搜尋型別 | Lucene語法 | XML語法 |
|---|---|---|
| '原子',匹配任何術語 | fillet snake |
<query>
<bool>
<term>fillet</term>
<term>snake</term>
</bool>
</query> |
| '原子',匹配所有術語 | +fillet +snake |
<query>
<bool>
<term occur="must">fillet</term>
<term occur="must">snake</term>
</bool>
</query> |
| '原子',僅匹配某些術語 | -fillet +snake |
<query>
<bool>
<term occur="not">fillet</term>
<term occur="must">snake</term>
</bool>
</query> |
| '原子',帶萬用字元 | +fillet +sn*e |
<query>
<bool>
<term occur="must">fillet</term>
<wildcard occur="must">sn*e</wildcard>
</bool>
</query> |
| '原子',帶正則表示式 | <query>
<bool>
<term occur="must">fillet</term>
<regex occur="must">sn.*e</regex>
</bool>
</query> |
|
| 短語搜尋 | "fillet snake" |
<query> <phrase>fillet snake</phrase> </query> <query> <near>fillet snake</near> </query> |
| 鄰近搜尋 | "fillet snake"~1 |
<query>
<near slop="3">
<term>fillet</term>
<term>snake</term>
</near>
</query> |
| 鄰近搜尋,無序 | <query>
<near slop="1" ordered="no">
<term>snake</term>
<term>fillet</term>
</near>
</query> |
|
| 模糊搜尋,無相似度引數 | snake~ |
<query> <fuzzy>snake</fuzzy> </query> |
| 模糊搜尋,帶相似度引數 | snake~0.3 |
<query> <fuzzy min-similarity="0.3">snake</fuzzy> </query> |
請注意上表中的空白!在標準Lucene語法中,您無法表達
- 正則表示式:這是eXist的XML查詢語法的獨特功能,透過<regex>元素實現。
- 鄰近搜尋詞的順序:這是eXist的XML查詢語法的獨特功能,透過<near>上的@ordered屬性實現。
最後,一個更復雜的情況,其中布林運算子被分組以覆蓋預設的優先順序規則。
| 搜尋型別 | Lucene語法 | XML語法 |
|---|---|---|
| 布林搜尋運算子組 | (fillet OR malice) AND snake |
<query>
<bool>
<bool occur="must">
<term occur="should">fillet</term>
<term occur="should">malice</term>
</bool>
<term occur="must">snake</term>
</bool>
</query> |
請注意如何
- 標準Lucene語法中的分組可以用XML語法中的巢狀來表達。
- 對於巢狀的<bool>運算子,也可以指定@occur屬性。
請注意,如果在字串中包含萬用字元,則必須使用<wildcard>元素將字串括起來。
以下
//SPEECH[ft:query(., 'fennny sna*')]
等效於
xquery version "1.0";
let $query :=
<query>
<term>fen</term>
<wildcard>sna*</wildcard>
</query>
return
//SPEECH[ft:query(., $query)]
- eXist Lucene XML語法 Ron Van den Branden的部落格文章