目 錄java
1 內容 3mysql
2 Lucene簡介 3面試
2.1 全文檢索(Full-text Search) 3sql
2.1.1 定義 3數據庫
2.1.2 應用場景 3apache
2.2 Lucene實現全文檢索的流程 5編程
3 入門示例 5ide
3.1 需求 5工具
3.2 配置步驟說明 5學習
3.3 配置步驟 6
3.3.1 第一部分:搭建環境(建立項目,導入包) 6
3.3.2 第二部分:建立索引 7
3.3.3 第三部分:搜索索引 12
3.4 小結 14
4 分詞 14
4.1 重要性 14
4.2 分詞過程 14
4.3 分詞後索引庫結構 15
4.4 Luke客戶端鏈接索引庫 16
4.4.1 啓動方法 16
4.4.2 驗證分詞效果 17
5 Field域 17
5.1 域的屬性 17
5.1.1 三大屬性 17
5.1.2 特色 18
5.2 Field經常使用類型 18
5.3 改造入門示例中的域類型 19
5.3.1 分析 19
5.3.2 代碼修改 20
5.3.3 測試 21
6 索引庫維護 21
6.1 添加索引(文檔) 22
6.1.1 需求 22
6.1.2 代碼實現 22
6.2 刪除索引(文檔) 22
6.2.1 需求 22
6.2.2 代碼實現 22
6.2.3 清空索引庫 23
6.3 更新索引(文檔) 23
6.3.1 說明 23
6.3.2 代碼實現 23
7 搜索 24
7.1 建立查詢的兩種方法 24
7.1.1 使用Lucene提供Query子類 25
7.1.2 使用QueryParse解析查詢表達式 25
7.2 經常使用的Query子類搜索 25
7.2.1 TermQuery 25
7.2.2 NumericRangeQuery 27
7.2.3 BooleanQuery 28
7.3 經過QueryParser搜索 28
7.3.1 特色 28
7.3.2 語法 29
7.3.3 QueryParser 29
7.3.4 MultiFieldQueryParser 30
8 中文分詞器 31
8.1 什麼是中文分詞器 31
8.2 Lucene自帶的中文分詞器 31
8.2.1 StandardAnalyzer: 31
8.2.2 CJKAnalyzer 31
8.3 使用中文分詞器IKAnalyzer 31
8.3.1 使用luke測試IK中文分詞 31
8.3.2 改造代碼,使用IkAnalyzer作分詞器 32
9 思考:(面試題) 33
(1)Lucene概述
(2)什麼是全文檢索
(3)Lucene配置流程(重點)
(4)什麼是分詞(難點)
(5)Lucene域的屬性
(6)索引庫維護
(7)Lucene複雜搜索(重點)
(8)中文分詞器
Lucene是apache下的一個開源的全文檢索引擎工具包。
全文檢索就是先分詞建立索引,再執行搜索的過程。
分詞:就是將一段文字分紅一個個單詞
全文檢索就將一段文字分紅一個個單詞去查詢數據!!!
搜索引擎是一個基於全文檢索、能獨立運行、提供搜索服務的軟件系統。
思考:電商網站內,咱們都是經過輸入關鍵詞來搜索商品的。若是咱們根據關鍵詞,直接查詢數據庫,會有什麼後果?
答:咱們只能使用模糊搜索,來進行匹配,會致使不少數據匹配不到。因此,咱們必須使用全文檢索。
|
|
全文檢索的流程分爲兩大部分:索引流程、搜索流程。
索引流程:採集數據--->構建文檔對象--->建立索引(將文檔寫入索引庫)。
搜索流程:建立查詢--->執行搜索--->渲染搜索結果。
使用Lucene實現電商項目中圖書類商品的索引和搜索功能。
(1)搭建環境(先下載Lucene)
(2)建立索引庫
(3)搜索索引庫
前提:已經建立好了數據庫(直接導入book.sql文件)
|
Lucene是開發全文檢索功能的工具包,使用時從官方網站下載,並解壓。
官方網站:http://lucene.apache.org/
下載地址:http://archive.apache.org/dist/lucene/java/
下載版本:4.10.3(要求:jdk1.7及以上)
核心包lucene-core-4.10.3.jar(附經常使用API)
|
mysql5.1驅動包:mysql-connector-java-5.1.7-bin.jar 核心包:lucene-core-4.10.3.jar 分析器通用包:lucene-analyzers-common-4.10.3.jar 查詢解析器包:lucene-queryparser-4.10.3.jar |
項目結構以下:
|
步驟說明:
(1)採集數據
(2)將數據轉換成Lucene文檔
(3)將文檔寫入索引庫,建立索引
Lucene全文檢索,不是直接查詢數據庫,因此須要先將數據採集出來。
(1)建立Book類
public class Book { private Integer bookId; // 圖書ID private String name; // 圖書名稱 private Float price; // 圖書價格 private String pic; // 圖書圖片 private String description; // 圖書描述 // 補全get\set方法 } |
(2)建立一個BookDao類
package cn.gzsxt.lucene.dao;
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List;
import cn.gzsxt.lucene.pojo.Book;
public class BookDao { public List<Book> getAll() { // 數據庫連接 Connection connection = null; // 預編譯statement PreparedStatement preparedStatement = null; // 結果集 ResultSet resultSet = null; // 圖書列表 List<Book> list = new ArrayList<Book>(); try { // 加載數據庫驅動 Class.forName("com.mysql.jdbc.Driver"); // 鏈接數據庫 connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/lucene", "root", "gzsxt");
// SQL語句 String sql = "SELECT * FROM book"; // 建立preparedStatement preparedStatement = connection.prepareStatement(sql);
// 獲取結果集 resultSet = preparedStatement.executeQuery();
// 結果集解析 while (resultSet.next()) { Book book = new Book(); book.setBookId(resultSet.getInt("id")); book.setName(resultSet.getString("name")); book.setPrice(resultSet.getFloat("price")); book.setPic(resultSet.getString("pic")); book.setDescription(resultSet.getString("description")); list.add(book); } } catch (Exception e) { e.printStackTrace();
}finally {
if(null!=resultSet){ try { resultSet.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(null!=preparedStatement){ try { preparedStatement.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(null!=connection){ try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return list; } } |
(3)建立一個測試類BookDaoTest
package cn.gzsxt.lucene.test;
import java.util.List;
import org.junit.Test; import cn.gzsxt.lucene.dao.BookDao; import cn.gzsxt.lucene.pojo.Book;
public class BookDaoTest {
@Test public void getAll(){ BookDao dao = new BookDao(); List<Book> books = dao.getAll();
for (Book book : books) { System.out.println("圖書id:"+book.getBookId()+",圖書名稱:"+book.getName()); } } } |
(4)測試結果,採集數據成功
|
Lucene是使用文檔類型來封裝數據的,全部須要先將採集的數據轉換成文檔類型。其格式爲:
|
修改BookDao,新增一個方法,轉換數據
public List<Document> getDocuments(List<Book> books){ // Document對象集合 List<Document> docList = new ArrayList<Document>(); // Document對象 Document doc = null; for (Book book : books) { // 建立Document對象,同時要建立field對象 doc = new Document(); // 根據需求建立不一樣的Field Field id = new TextField("id", book.getBookId().toString(), Store.YES); Field name = new TextField("name", book.getName(), Store.YES); Field price = new TextField("price", book.getPrice().toString(),Store.YES); Field pic = new TextField("pic", book.getPic(), Store.YES); Field desc = new TextField("description", book.getDescription(), Store.YES); // 把域(Field)添加到文檔(Document)中 doc.add(id); doc.add(name); doc.add(price); doc.add(pic); doc.add(desc);
docList.add(doc); } return docList; } |
說明:Lucene是在將文檔寫入索引庫的過程當中,自動完成分詞、建立索引的。所以建立索引庫,從形式上看,就是將文檔寫入索引庫!
修改測試類,新增createIndex方法
@Test public void createIndex(){ try { BookDao dao = new BookDao();
// 分析文檔,對文檔中的field域進行分詞 Analyzer analyzer = new StandardAnalyzer(); // 建立索引 // 1) 建立索引庫目錄 Directory directory = FSDirectory.open(new File("F:\\lucene\\0719")); // 2) 建立IndexWriterConfig對象 IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, analyzer); // 3) 建立IndexWriter對象 IndexWriter writer = new IndexWriter(directory, cfg); // 4) 經過IndexWriter對象添加文檔對象(document) writer.addDocuments(dao.getDocuments(dao.getAll()));
// 5) 關閉IndexWriter writer.close(); System.out.println("建立索引庫成功");
} catch (Exception e) { e.printStackTrace(); } } |
測試結果,建立成功!!!
|
搜索的時候,須要指定搜索哪個域(也就是字段),而且,還要對搜索的關鍵詞作分詞處理。
修改測試類,新增searchDocumentByIndex方法
@Test public void searchDocumentByIndex(){ try { // 1、 建立查詢(Query對象) // 建立分析器 Analyzer analyzer = new StandardAnalyzer(); QueryParser queryParser = new QueryParser("name", analyzer); Query query = queryParser.parse("name:java教程"); // 2、 執行搜索 // a) 指定索引庫目錄 Directory directory = FSDirectory.open(new File("F:\\lucene\\0719")); // b) 建立IndexReader對象 IndexReader reader = DirectoryReader.open(directory); // c) 建立IndexSearcher對象 IndexSearcher searcher = new IndexSearcher(reader); // d) 經過IndexSearcher對象執行查詢索引庫,返回TopDocs對象 // 第一個參數:查詢對象 // 第二個參數:最大的n條記錄 TopDocs topDocs = searcher.search(query, 10); // e) 提取TopDocs對象中前n條記錄 ScoreDoc[] scoreDocs = topDocs.scoreDocs; System.out.println("查詢出文檔個數爲:" + topDocs.totalHits); for (ScoreDoc scoreDoc : scoreDocs) { // 文檔對象ID int docId = scoreDoc.doc; Document doc = searcher.doc(docId); // f) 輸出文檔內容 System.out.println("==============================="); System.out.println("文檔id:" + docId); System.out.println("圖書id:" + doc.get("id")); System.out.println("圖書name:" + doc.get("name")); System.out.println("圖書price:" + doc.get("price")); System.out.println("圖書pic:" + doc.get("pic")); System.out.println("圖書description:" + doc.get("description")); } // g) 關閉IndexReader reader.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } |
測試結果,很是成功!!!
|
Lucene全文檢索,確實能夠實現對關鍵詞作分詞、再執行搜索功能。而且結果更精確。
分詞是全文檢索的核心。
所謂的分詞,就是將一段文本,根據必定的規則,拆分紅一個一個詞。
Lucene是根據分析器實現分詞的。針對不一樣的語言提供了不一樣的分析器。而且提供了一個通用的標準分析器StandardAnalyzer
--說明:咱們經過分析StandardAnalyzer核心源碼來分析分詞過程
@Override protected TokenStreamComponents createComponents(final String fieldName, final Reader reader) { final StandardTokenizer src = new StandardTokenizer(getVersion(), reader); src.setMaxTokenLength(maxTokenLength); TokenStream tok = new StandardFilter(getVersion(), src); tok = new LowerCaseFilter(getVersion(), tok); tok = new StopFilter(getVersion(), tok, stopwords); return new TokenStreamComponents(src, tok) { @Override protected void setReader(final Reader reader) throws IOException { src.setMaxTokenLength(StandardAnalyzer.this.maxTokenLength); super.setReader(reader); } }; } |
對應Lucene分詞的過程,咱們能夠作以下總結:
(1)分詞的時候,是以域爲單位的。不一樣的域,相互獨立。
同一個域中,拆分出來相同的詞,視爲同一個詞(Term)
不一樣的域中,拆分出來相同的詞,不是同一個詞。
其中,Term是Lucene最小的語彙單元,不可再細分。
(2)分詞的時候經歷了一系列的過濾器。如大小寫轉換、去除停用詞等。
咱們這裏藉助前面的示例來講明
|
從上圖中,咱們發現:
(1)索引庫中有兩個區域:索引區、文檔區。
(2)文檔區存放的是文檔。Lucene給每個文檔自動加上一個文檔編號docID。
(3)索引區存放的是索引。注意:
索引是以域爲單位的,不一樣的域,彼此相互獨立。
索引是根據分詞規則建立出來的,根據索引就能找到對應的文檔。
Luke做爲Lucene工具包中的一個工具(http://www.getopt.org/luke/),能夠經過可視化界面,鏈接操做索引庫。
(1)雙擊start.bat啓動!
|
(2)鏈接索引庫
|
|
問題:咱們已經知道,Lucene是在寫入文檔時,完成分詞、索引的。那Lucene是怎麼知道的呢?
答:Lucene是根據文檔中的域的屬性,來肯定是否要分詞、建立索引的。因此,咱們必須搞清楚域有哪些屬性。
只有設置了分詞屬性爲true,lucene纔會對這個域進行分詞處理。
在實際的開發中,有一些字段是不須要分詞的,好比商品id,商品圖片等。
而有一些字段是必須分詞的,好比商品名稱,描述信息等。
只有設置了索引屬性爲true,lucene才爲這個域的Term詞建立索引。
在實際的開發中,有一些字段是不須要建立索引的,好比商品的圖片等。咱們只須要對參與搜索的字段作索引處理。
只有設置了存儲屬性爲true,在查找的時候,才能從文檔中獲取這個域的值。
在實際開發中,有一些字段是不須要存儲的。好比:商品的描述信息。
由於商品描述信息,一般都是大文本數據,讀的時候會形成巨大的IO開銷。而描述信息是不須要常常查詢的字段,這樣的話就白白浪費了cpu的資源了。
所以,像這種不須要常常查詢,又是大文本的字段,一般不會存儲到索引庫。
(1)三大屬性彼此獨立。
(2)一般分詞是爲了建立索引。
(3)不存儲這個域文本內容,也能夠對這個域先分詞、建立索引。
域的經常使用類型有不少,每個類都有本身默認的三大屬性。以下:
Field類 |
數據類型 |
Analyzed 是否分詞 |
Indexed 是否索引 |
Stored 是否存儲 |
StringField(FieldName, FieldValue,Store.YES)) |
字符串 |
N |
Y |
Y或N |
LongField(FieldName, FieldValue,Store.YES) |
Long型 |
Y |
Y |
Y或N |
FloatField(FieldName, FieldValue,Store.YES) |
Float型 |
Y |
Y |
Y或N |
StoredField(FieldName, FieldValue) |
重載方法,支持多種類型 |
N |
N |
Y |
TextField(FieldName, FieldValue, Store.NO) |
字符串 |
Y |
Y |
Y或N |
(1)圖書id:
是否分詞:不用分詞,由於不會根據商品id來搜索商品
是否索引:不索引,由於不須要根據圖書ID進行搜索
是否存儲:要存儲,由於查詢結果頁面須要使用id這個值。
(2)圖書名稱:
是否分詞:要分詞,由於要將圖書的名稱內容分詞索引,根據關鍵搜索圖書名稱抽取的詞。
是否索引:要索引。
是否存儲:要存儲。
(3)圖書價格:
是否分詞:要分詞,lucene對數字型的值只要有搜索需求的都要分詞和索引,由於lucene對數字型的內容要特殊分詞處理,本例子可能要根據價格範圍搜索, 須要分詞和索引。
是否索引:要索引
是否存儲:要存儲
(4)圖書圖片地址:
是否分詞:不分詞
是否索引:不索引
是否存儲:要存儲
(5)圖書描述:
是否分詞:要分詞
是否索引:要索引
是否存儲:由於圖書描述內容量大,不在查詢結果頁面直接顯示,不存儲。
不存儲是來不在lucene的索引文件中記錄,節省lucene的索引文件空間,若是要在詳情頁面顯示描述,思路:
從lucene中取出圖書的id,根據圖書的id查詢關係數據庫中book表獲得描述信息。
修改BookDao的getDocument方法
public List<Document> getDocuments(List<Book> books){ // Document對象集合 List<Document> docList = new ArrayList<Document>(); // Document對象 Document doc = null; for (Book book : books) { // 建立Document對象,同時要建立field對象 doc = new Document();
// 圖書ID // 參數:域名、域中存儲的內容、是否存儲 // 不分詞、索引、要存儲 // Field id = new TextField("id", book.getId().toString(),Store.YES); Field id = new StoredField("id", book.getBookId().toString()); // 圖書名稱 // 分詞、索引、存儲 Field name = new TextField("name", book.getName(),Store.YES); // 圖書價格 // 分詞、索引、存儲 Field price = new FloatField("price", book.getPrice(), Store.YES); // 圖書圖片 // 不分詞、不索引、要存儲 Field pic = new StoredField("pic", book.getPic()); // 圖書描述 // 分詞、索引、不存儲 Field desc = new TextField("description",book.getDescription(), Store.NO);
// 把域(Field)添加到文檔(Document)中 doc.add(id); doc.add(name); doc.add(price); doc.add(pic); doc.add(desc);
docList.add(doc); }
return docList; } |
(1)去索引庫目錄中,手動清空索引庫。
(2)從新建立索引庫。
(3)使用Luke驗證分詞、索引效果。
|
改形成功!!!
在第4節,咱們須要從新建立索引的時候,是去索引庫目錄下,手動刪除的。
而在實際的開發中,咱們可能壓根就不知道索引庫在哪,就算知道,咱們也不可能每次都去手動刪除,很是之麻煩!!!
因此,咱們必須學習如何維護索引庫,使用程序來操做索引庫。
須要注意的是,索引是與文檔緊密相連的,所以對索引的維護,實際上就是對文檔的增刪改。
數據庫中新上架了圖書,必須把這些圖書也添加到索引庫中,否則就搜不到該新上架的圖書了。
調用 indexWriter.addDocument(doc)添加索引。
參考入門示例中的建立索引。
某些圖書再也不出版銷售了,咱們須要從索引庫中移除該圖書。
@Test public void deleteIndex() throws Exception { // 1、指定索引庫目錄 Directory directory = FSDirectory.open(new File("F:\\lucene\\0719")); // 2、建立IndexWriterConfig IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, new StandardAnalyzer()); // 3、 建立IndexWriter IndexWriter writer = new IndexWriter(directory, cfg); // 4、經過IndexWriter來刪除索引 // 刪除指定索引 writer.deleteDocuments(new Term("name", "apache")); // 5、關閉IndexWriter writer.close();
System.out.println("刪除成功");
} |
@Test public void deleteIndex() throws Exception { // 1、指定索引庫目錄 Directory directory = FSDirectory.open(new File("F:\\lucene\\0719")); // 2、建立IndexWriterConfig IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, new StandardAnalyzer()); // 3、 建立IndexWriter IndexWriter writer = new IndexWriter(directory, cfg); // 4、經過IndexWriter來刪除索引 // 刪除指定索引 writer.deleteAll(); // 5、關閉IndexWriter writer.close();
System.out.println("清空索引庫成功");
} |
Lucene更新索引比較特殊,是先刪除知足條件的索引,再添加新的索引。
// 修改索引 @Test public void updateIndex() throws Exception { // 1、指定索引庫目錄 Directory directory = FSDirectory.open(new File("F:\\lucene\\0719")); // 2、建立IndexWriterConfig IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, new StandardAnalyzer()); // 3、 建立IndexWriter IndexWriter writer = new IndexWriter(directory, cfg); // 4、經過IndexWriter來修改索引 // a)、建立修改後的文檔對象 Document document = new Document();
// 文件名稱 Field filenameField = new StringField("name", "updateIndex", Store.YES); document.add(filenameField);
// 修改指定索引爲新的索引 writer.updateDocument(new Term("name", "apache"), document);
// 5、關閉IndexWriter writer.close();
System.out.println("更新成功"); } |
問題:咱們在入門示例中,已經知道Lucene是經過IndexSearcher對象,來執行搜索的。那咱們爲何還要繼續學習Lucene呢?
答:由於在實際的開發中,咱們的查詢的業務是相對複雜的,好比咱們在經過關鍵詞查找的時候,每每進行價格、商品類別的過濾。
而Lucene提供了一套查詢方案,供咱們實現複雜的查詢。
執行查詢以前,必須建立一個查詢Query查詢對象。
Query自身是一個抽象類,不能實例化,必須經過其它的方式來實現初始化。
在這裏,Lucene提供了兩種初始化Query查詢對象的方式。
Query是一個抽象類,lucene提供了不少查詢對象,好比TermQuery項精確查詢,NumericRangeQuery數字範圍查詢等。
使用TermQuery實例化
Query query = new TermQuery(new Term("name", "lucene")); |
QueryParser會將用戶輸入的查詢表達式解析成Query對象實例。以下代碼:
QueryParser queryParser = new QueryParser("name", new IKAnalyzer()); Query query = queryParser.parse("name:lucene"); |
特色:查詢的關鍵詞不會再作分詞處理,做爲總體來搜索。代碼以下:
/** * Query子類查詢之 TermQuery * * 特色:不會再對查詢的關鍵詞作分詞處理。 * * 須要:查詢書名與java教程相關書。 */ @Test public void queryByTermQuery(){ //1、獲取一個查詢對象 Query query = new TermQuery(new Term("name", "編程思想")); doSearch(query);
} private void doSearch(Query query) { try {
//2、建立一個查詢的執行對象 //指定索引庫的目錄 Directory d = FSDirectory.open(new File("F:\\lucene\\0719")); //建立流對象 IndexReader reader = DirectoryReader.open(d); //建立搜索執行對象 IndexSearcher searcher = new IndexSearcher(reader);
//3、執行搜索 TopDocs result = searcher.search(query, 10);
//4、提出結果集,獲取圖書的信息 int totalHits = result.totalHits; System.out.println("共查詢到"+totalHits+"條知足條件的數據!"); System.out.println("-----------------------------------------"); //提取圖書信息。 //score即相關度。即搜索的關鍵詞和 圖書名稱的相關度,用來作排序處理 ScoreDoc[] scoreDocs = result.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) { /** * scoreDoc.doc的返回值,是文檔的id, 即 將文檔寫入索引庫的時候,lucene自動給這份文檔作的一個編號。 * * 獲取到這個文檔id以後,便可以根據這個id,找到這份文檔。 */ int docId = scoreDoc.doc; System.out.println("文檔在索引庫中的編號:"+docId);
//從文檔中提取圖書的信息 Document doc = searcher.doc(docId); System.out.println("圖書id:"+doc.get("id")); System.out.println("圖書name:"+doc.get("name")); System.out.println("圖書price:"+doc.get("price")); System.out.println("圖書pic:"+doc.get("pic")); System.out.println("圖書description:"+doc.get("description")); System.out.println(); System.out.println("------------------------------------");
}
//關閉鏈接,釋放資源 if(null!=reader){ reader.close(); } } catch (Exception e) { e.printStackTrace(); } } |
指定數字範圍查詢.(建立field類型時,注意與之對應)
/** * Query子類查詢 之 NumricRangeQuery * 需求:查詢全部價格在[60,80)之間的書 * @param query */ @Test public void queryByNumricRangeQuery(){ /** * 第一個參數:要搜索的域 * 第二個參數:最小值 * 第三個參數:最大值 * 第四個參數:是否包含最小值 * 第五個參數:是否包含最大值 */ Query query = NumericRangeQuery.newFloatRange("price", 60.0f, 80.0f, true, false);
doSearch(query); } |
BooleanQuery,布爾查詢,實現組合條件查詢。
/** * Query子類查詢 之 BooelanQuery查詢 組合條件查詢 * * 需求:查詢書名包含java,而且價格區間在[60,80)之間的書。 */ @Test public void queryBooleanQuery(){ //1、要使用BooelanQuery查詢,首先要把單個建立出來,而後再經過BooelanQuery組合 Query price = NumericRangeQuery.newFloatRange("price", 60.0f, 80.0f, true, false); Query name = new TermQuery(new Term("name", "java"));
//2、建立BooleanQuery實例對象 BooleanQuery query = new BooleanQuery(); query.add(name, Occur.MUST_NOT); query.add(price, Occur.MUST); /** * MSUT 表示必須知足 對應的是 + * MSUT_NOT 必須不知足 應對的是 - * SHOULD 能夠知足也能夠不知足 沒有符號 * * SHOULD 與MUST、MUST_NOT組合的時候,SHOULD就沒有意義了。 */
doSearch(query); } |
對搜索的關鍵詞,作分詞處理。
域名:關鍵字 實例:name:java |
條件1 AND 條件2 條件1 OR 條件2 條件1 NOT 條件2 |
/** * 查詢解析器查詢 之 QueryParser查詢 */ @Test public void queryByQueryParser(){ try {
//1、加載分詞器 Analyzer analyzer = new StandardAnalyzer();
/** * 2、建立查詢解析器實例對象 * 第一個參數:默認搜索的域。 * 若是在搜索的時候,沒有特別指定搜索的域,則按照默認的域進行搜索 * 如何在搜索的時候指定搜索域呢? * 答:格式 域名:關鍵詞 即 name:java教程 * * 第二個參數:分詞器 ,對關鍵詞作分詞處理 */ QueryParser parser = new QueryParser("description", analyzer);
Query query = parser.parse("name:java教程");
doSearch(query);
} catch (Exception e) { e.printStackTrace(); } } |
經過MulitFieldQueryParse對多個域查詢。
/** * 查詢解析器查詢 之 MultiFieldQueryParser查詢 * * 特色:同時指定多個搜索域,而且對關鍵作分詞處理 */ @Test public void queryByMultiFieldQueryParser(){ try {
//1、定義多個搜索的 name、description String[] fields = {"name","description"}; //2、加載分詞器 Analyzer analyzer = new StandardAnalyzer();
//3、建立 MultiFieldQueryParser實例對象 MultiFieldQueryParser mParser = new MultiFieldQueryParser(fields, analyzer);
Query query = mParser.parse("lucene教程");
doSearch(query); } catch (Exception e) { e.printStackTrace(); } } |
學過英文的都知道,英文是以單詞爲單位的,單詞與單詞之間以空格或者逗號句號隔開。
而中文的語義比較特殊,很難像英文那樣,一個漢字一個漢字來劃分。
因此須要一個能自動識別中文語義的分詞器。
單字分詞:就是按照中文一個字一個字地進行分詞。如:「我愛中國」,
效果:「我」、「愛」、「中」、「國」。
二分法分詞:按兩個字進行切分。如:「我是中國人」,效果:「我是」、「是中」、「中國」「國人」。
上邊兩個分詞器沒法知足需求。
IKAnalyzer繼承Lucene的Analyzer抽象類,使用IKAnalyzer和Lucene自帶的分析器方法同樣,將Analyzer測試代碼改成IKAnalyzer測試中文分詞效果。
若是使用中文分詞器ik-analyzer,就在索引和搜索程序中使用一致的分詞器ik-analyzer。
(1)打開Luke,不要指定Lucene目錄。不然看不到效果
(2)在分詞器欄,手動輸入IkAnalyzer的全路徑
org.wltea.analyzer.lucene.IKAnalyzer
|
// 建立中文分詞器 Analyzer analyzer = new IKAnalyzer(); |
拓展詞庫的做用:在分詞的過程當中,保留定義的這些詞
1在src或其餘source目錄下創建本身的拓展詞庫,mydict.dic文件,例如:
|
2在src或其餘source目錄下創建本身的停用詞庫,ext_stopword.dic文件
停用詞的做用:在分詞的過程當中,分詞器會忽略這些詞。
3在src或其餘source目錄下創建IKAnalyzer.cfg.xml,內容以下(注意路徑對應):
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <comment>IK Analyzer 擴展配置</comment> <!-- 用戶能夠在這裏配置本身的擴展字典 --> <entry key="ext_dict">mydict.dic</entry> <!-- 用戶能夠在這裏配置本身的擴展停用詞字典 --> <entry key="ext_stopwords">ext_stopword.dic</entry> </properties>
|
若是想配置擴展詞和停用詞,就建立擴展詞的文件和停用詞的文件,文件的編碼要是utf-8。
注意:不要用記事本保存擴展詞文件和停用詞文件,那樣的話,格式中是含有bom的。
在一堆文件中,如何快速根據關鍵詞找出對應的文件?
思路:(1)使用全文檢索來解決問題
(2)數據源由數據庫變成一堆文件。
(3)從一堆文件中,讀出裏面的內容,轉成文檔,建立索引庫。
(4)建立索引庫以後,再根據關鍵詞搜索索引庫,找出文件的名稱。
問題:如何讀文件的內容?
答:txt文本,直接使用IO便可。
doc|docx 使用POI讀取內容。