Lucene

1、數據分類

一、結構化數據:有固定類型或者有固定長度的數據java

數據庫中的數據(mysql,oracle等), 元數據(就是windows中的數據)。數據庫中數據經過sql語句能夠搜索,元數據(windows中的)經過windows提供的搜索欄進行搜索mysql

二、非結構化數據:沒有固定類型和固定長度的數據web

world文檔中的數據, 郵件中的數據。Word文檔使用ctrl+F來搜索算法

三、順序掃描法sql

Ctrl+F中是使用的順序掃描法,拿到搜索的關鍵字,去文檔中,逐字匹配,直到找到和關鍵字一致的內容爲止數據庫

  優勢: 若是文檔中存在要找的關鍵字就必定能找到想要的內容apache

  缺點: 慢, 效率低windows

四、全文檢索算法(倒排索引算法)tomcat

將文件中的內容提取出來, 將文字拆封成一個一個的詞(分詞), 將這些詞組成索引(字典中的目錄), 搜索的時候先搜索索引,經過索引找文檔,這個過程就叫作全文檢索oracle

  分詞: 去掉停用詞(a, an, the ,的, 地, 得, 啊, 嗯 ,呵呵),由於搜索的時候搜索這些詞沒有意義,將句子拆分紅詞,去掉標點符號和空格

  優勢: 搜索速度快

  缺點: 由於建立的索引須要佔用磁盤空間,因此這個算法會使用掉更多的磁盤空間,這是用空間換時間

原理

至關於字典,分爲目錄和正文兩部分,查詢的時候經過先查目錄,而後經過目錄上標註的頁數去正文頁查找須要的內容

 

2、Lucene簡介

apache旗下的頂級項目,是一個全文檢索工具包,可使用它來構建全文檢索引擎系統,可是它不能獨立運(JDK要求1.7)

 

一、全文檢索引擎系統

  放在tomcat下能夠獨立運行,對外提供全文檢索服務

二、應用領域

(1)互聯網全文檢索引擎(好比百度,  谷歌,  必應)

(2)站內全文檢索引擎(淘寶, 京東搜索功能)

(3)優化數據庫查詢(由於數據庫中使用like關鍵字是全表掃描也就是順序掃描算法,查詢慢)

 

3、Lucene結構

Lucene包括索引和文檔(K-V)

一、索引(Index)

域名:詞  這樣的形式,它裏面有指針執行這個詞來源的文檔

索引庫:放索引的文件夾

詞元(Term):就是一個詞, 是Lucene中詞的最小單位

二、文檔(Document)

Document對象,一個Document中能夠有多個Field域對象,Field域對象中是key-value鍵值對的形式:有域名和域值

一個document就是數據庫表中的一條記錄, 一個Filed域對象就是數據庫表中的一行一列,這是一個通用的存儲結構

建立索引和全部時所用的分詞器必須一致

 

建立索引

用Luke可查看建立完成的索引

java -jar lukeall-4.10.3.jar

@Test
public void testIndexCreate() throws Exception{
    //建立文檔列表,保存多個Docuemnt
    List<Document> docList = new ArrayList<Document>();
    
    //指定文件所在目錄
    File dir = new File("E:\\searchsource"); 
    //循環文件夾取出文件
    for(File file : dir.listFiles()){
        //文件名稱
        String fileName = file.getName();
        //文件內容
        String fileContext = FileUtils.readFileToString(file);
        //文件大小
        Long fileSize = FileUtils.sizeOf(file);
        
        //文檔對象,文件系統中的一個文件就是一個Docuemnt對象
        Document doc = new Document();
        
        //第一個參數:域名
        //第二個參數:域值
        //第三個參數:是否存儲,是爲yes,不存儲爲no
        /*TextField nameFiled = new TextField("fileName", fileName, Store.YES);
        TextField contextFiled = new TextField("fileContext", fileContext, Store.YES);
        TextField sizeFiled = new TextField("fileSize", fileSize.toString(), Store.YES);*/
        
        //是否分詞:要,由於它要索引,而且它不是一個總體,分詞有意義
        //是否索引:要,由於要經過它來進行搜索
        //是否存儲:要,由於要直接在頁面上顯示
        TextField nameField = new TextField("fileName", fileName, Store.YES);
        
        //是否分詞: 要,由於要根據內容進行搜索,而且它分詞有意義
        //是否索引: 要,由於要根據它進行搜索
        //是否存儲: 能夠要也能夠不要,不存儲搜索完內容就提取不出來
        TextField contextField = new TextField("fileContext", fileContext, Store.NO);
        
        //是否分詞: 要, 由於數字要對比,搜索文檔的時候能夠搜大小, lunene內部對數字進行了分詞算法
        //是否索引: 要, 由於要根據大小進行搜索
        //是否存儲: 要, 由於要顯示文檔大小
        LongField sizeField = new LongField("fileSize", fileSize, Store.YES);
        
        //將全部的域都存入文檔中
        doc.add(nameField);
        doc.add(contextField);
        doc.add(sizeField);
        
        //將文檔存入文檔集合中
        docList.add(doc);
    }
    
    //建立分詞器,StandardAnalyzer標準分詞器,標準分詞器對英文分詞效果很好,對中文是單字分詞
    Analyzer analyzer = new IKAnalyzer();
    //指定索引和文檔存儲的目錄
    Directory directory = FSDirectory.open(new File("E:\\dic"));
    //建立寫對象的初始化對象
    IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
    //建立索引和文檔寫對象
    IndexWriter indexWriter = new IndexWriter(directory, config);
    
    //將文檔加入到索引和文檔的寫對象中
    for(Document doc : docList){
        indexWriter.addDocument(doc);
    }
    //提交
    indexWriter.commit();
    //關閉流
    indexWriter.close();
}

索引搜索

@Test
public void testIndexSearch() throws Exception{
    
    //建立分詞器(建立索引和全部時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();
    //建立查詢對象,第一個參數:默認搜索域, 第二個參數:分詞器
    //默認搜索域做用:若是搜索語法中指定域名從指定域中搜索,若是搜索時只寫了查詢關鍵字,則從默認搜索域中進行搜索
    QueryParser queryParser = new QueryParser("fileContext", analyzer);
    //查詢語法=域名:搜索的關鍵字
    Query query = queryParser.parse("fileName:web");
    
    //指定索引和文檔的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文檔的讀取對象
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜索對象
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜索:第一個參數爲查詢語句對象, 第二個參數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(query, 5);
    //一共搜索到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜索結果對象中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;
    
    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //經過文檔ID從硬盤中讀取出對應的文檔
        Document document = indexReader.document(docID);
        //get域名能夠取出值 打印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("============================================================");
    }
    
}

 

4、域(Field)

在文檔Document對象中的Field,爲K-V鍵值對

一、是否分詞

分詞的做用是爲了索引

須要分詞: 文件名稱, 文件內容

不須要分詞: 不須要索引的域不須要分詞,還有就是分詞後無心義的域不須要分詞,好比: id, 身份證號

二、是否索引

索引的的目的是爲了搜索

須要搜索的域就必定要建立索引,只有建立了索引才能被搜索出來

不須要搜索的域能夠不建立索引

須要索引: 文件名稱, 文件內容, id, 身份證號等

不須要索引: 好比圖片地址不須要建立索引, e:\\xxx.jpg,根據圖片地址搜索無心義

三、是否存儲

存儲的目的是爲了顯示

是否存儲看我的須要,存儲就是將內容放入Document文檔對象中保存出來,會額外佔用磁盤空間, 若是搜索的時候須要立刻顯示出來能夠放入document中也就是要存儲,

這樣查詢顯示速度快, 若是不是立刻馬上須要顯示出來,則不須要存儲,由於額外佔用磁盤空間不划算

四、域的類型

注意:Lucene底層的算法,錢數是要分詞的,由於要根據價錢進行對比

 

5、中文分詞器

一、自帶

StandardAnalyzer:單字分詞:就是按照中文一個字一個字地進行分詞

CJKAnalyzer:二分法分詞:按兩個字進行切分

SmartChineseAnalyzer:對中文支持較好,但擴展性差,擴展詞庫,禁用詞庫和同義詞庫等很差處理

二、第三方

IK-analyzer

stopword.dic中止字典和擴展字典ext.dic,修改後保存爲UTF-8

導入IKAnalyzer2012FF_u1.jar

加配置文件ext.dic、IKAnalyzer.cfg.xml、stopword.dic

 

6、索引的維護

一、刪除文檔

能夠根據某個域的內容進行刪除,還能夠一次刪除全部

@Test
public void testIndexDel() throws Exception{
    //建立分詞器,StandardAnalyzer標準分詞器,標準分詞器對英文分詞效果很好,對中文是單字分詞
    Analyzer analyzer = new IKAnalyzer();
    //指定索引和文檔存儲的目錄
    Directory directory = FSDirectory.open(new File("E:\\dic"));
    //建立寫對象的初始化對象
    IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
    //建立索引和文檔寫對象
    IndexWriter indexWriter = new IndexWriter(directory, config);
    
    //刪除全部
    //indexWriter.deleteAll();
    
    //根據名稱進行刪除
    //Term詞元,就是一個詞, 第一個參數:域名, 第二個參數:要刪除含有此關鍵詞的數據
    indexWriter.deleteDocuments(new Term("fileName", "apache"));
    
    //提交
    indexWriter.commit();
    //關閉
    indexWriter.close();
}

二、更新文檔

更新就是按照傳入的Term進行搜索,若是找到結果那麼刪除,將更新的內容從新生成一個Document對象

若是沒有搜索到結果,那麼將更新的內容直接添加一個新的Document對象

/**
 * 更新就是按照傳入的Term進行搜索,若是找到結果那麼刪除,將更新的內容從新生成一個Document對象
 * 若是沒有搜索到結果,那麼將更新的內容直接添加一個新的Document對象
 * @throws Exception
 */
@Test
public void testIndexUpdate() throws Exception{
    //建立分詞器,StandardAnalyzer標準分詞器,標準分詞器對英文分詞效果很好,對中文是單字分詞
    Analyzer analyzer = new IKAnalyzer();
    //指定索引和文檔存儲的目錄
    Directory directory = FSDirectory.open(new File("E:\\dic"));
    //建立寫對象的初始化對象
    IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
    //建立索引和文檔寫對象
    IndexWriter indexWriter = new IndexWriter(directory, config);
    
    
    //根據文件名稱進行更新
    Term term = new Term("fileName", "web");
    //更新的對象
    Document doc = new Document();
    doc.add(new TextField("fileName", "xxxxxx", Store.YES));
    doc.add(new TextField("fileContext", "think in java xxxxxxx", Store.NO));
    doc.add(new LongField("fileSize", 100L, Store.YES));
    
    //更新
    indexWriter.updateDocument(term, doc);
    
    //提交
    indexWriter.commit();
    //關閉
    indexWriter.close();
}

 

7、搜索(查詢)

一、TermQuery:根據詞進行搜索(只能從文本中進行搜索)

@Test
public void testIndexTermQuery() throws Exception{
    //建立分詞器(建立索引和全部時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();
    
    //建立詞元:就是詞,   
    Term term = new Term("fileName", "apache");
    //使用TermQuery查詢,根據term對象進行查詢
    TermQuery termQuery = new TermQuery(term);
    
    //指定索引和文檔的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文檔的讀取對象
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜索對象
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜索:第一個參數爲查詢語句對象, 第二個參數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(termQuery, 5);
    //一共搜索到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜索結果對象中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;
    
    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //經過文檔ID從硬盤中讀取出對應的文檔
        Document document = indexReader.document(docID);
        //get域名能夠取出值 打印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("============================================================");
    }
}

二、QueryParser:根據域名進行搜索,能夠設置默認搜索域,推薦使用. (只能從文本中進行搜索)

//建立查詢對象,第一個參數:默認搜索域, 第二個參數:分詞器
//默認搜索域做用:若是搜索語法中指定域名從指定域中搜索,若是搜索時只寫了查詢關鍵字,則從默認搜索域中進行搜索
QueryParser queryParser = new QueryParser("fileContext", analyzer);
//查詢語法=域名:搜索的關鍵字
Query query = queryParser.parse("fileName:web");

三、NumericRangeQuery:從數值範圍進行搜索

@Test
public void testNumericRangeQuery() throws Exception{
    //建立分詞器(建立索引和全部時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();
    
    //根據數字範圍查詢
    //查詢文件大小,大於100 小於1000的文章
    //第一個參數:域名      第二個參數:最小值,  第三個參數:最大值, 第四個參數:是否包含最小值,   第五個參數:是否包含最大值
    Query query = NumericRangeQuery.newLongRange("fileSize", 100L, 1000L, true, true);        
    
    //指定索引和文檔的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文檔的讀取對象
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜索對象
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜索:第一個參數爲查詢語句對象, 第二個參數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(query, 5);
    //一共搜索到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜索結果對象中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;
    
    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //經過文檔ID從硬盤中讀取出對應的文檔
        Document document = indexReader.document(docID);
        //get域名能夠取出值 打印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("============================================================");
    }
}

四、BooleanQuery:組合查詢,能夠設置組合條件,not and or.從多個域中進行查詢

  must至關於and關鍵字,而且

  should,至關於or關鍵字或者

  must_not至關於not關鍵字, 非

注意:單獨使用must_not  或者 獨自使用must_not沒有任何意義

@Test
public void testBooleanQuery() throws Exception{
    //建立分詞器(建立索引和全部時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();
    
    //布爾查詢,就是能夠根據多個條件組合進行查詢
    //文件名稱包含apache的,而且文件大小大於等於100 小於等於1000字節的文章
    BooleanQuery query = new BooleanQuery();
    
    //根據數字範圍查詢
    //查詢文件大小,大於100 小於1000的文章
    //第一個參數:域名      第二個參數:最小值,  第三個參數:最大值, 第四個參數:是否包含最小值,   第五個參數:是否包含最大值
    Query numericQuery = NumericRangeQuery.newLongRange("fileSize", 100L, 1000L, true, true);
    
    //建立詞元:就是詞,   
    Term term = new Term("fileName", "apache");
    //使用TermQuery查詢,根據term對象進行查詢
    TermQuery termQuery = new TermQuery(term);
    
    //Occur是邏輯條件
    //must至關於and關鍵字,是而且的意思
    //should,至關於or關鍵字或者的意思
    //must_not至關於not關鍵字, 非的意思
    //注意:單獨使用must_not  或者 獨自使用must_not沒有任何意義
    query.add(termQuery, Occur.MUST);
    query.add(numericQuery, Occur.MUST);
    
    //指定索引和文檔的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文檔的讀取對象
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜索對象
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜索:第一個參數爲查詢語句對象, 第二個參數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(query, 5);
    //一共搜索到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜索結果對象中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;
    
    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //經過文檔ID從硬盤中讀取出對應的文檔
        Document document = indexReader.document(docID);
        //get域名能夠取出值 打印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("============================================================");
    }
}

五、MatchAllDocsQuery:查詢出全部文檔

@Test
public void testMathAllQuery() throws Exception{
    //建立分詞器(建立索引和全部時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();
    
    //查詢全部文檔
    MatchAllDocsQuery query = new MatchAllDocsQuery();
    
    //指定索引和文檔的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文檔的讀取對象
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜索對象
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜索:第一個參數爲查詢語句對象, 第二個參數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(query, 5);
    //一共搜索到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜索結果對象中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;
    
    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //經過文檔ID從硬盤中讀取出對應的文檔
        Document document = indexReader.document(docID);
        //get域名能夠取出值 打印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("============================================================");
    }
}

六、MultiFieldQueryParser:能夠從多個域中進行查詢,只有這些域中有關鍵詞的存在就查詢出來

@Test
public void testMultiFieldQueryParser() throws Exception{
    //建立分詞器(建立索引和全部時所用的分詞器必須一致)
    Analyzer analyzer = new IKAnalyzer();
    
    String [] fields = {"fileName","fileContext"};
    //從文件名稱和文件內容中查詢,只有含有apache的就查出來
    MultiFieldQueryParser multiQuery = new MultiFieldQueryParser(fields, analyzer);
    //輸入須要搜索的關鍵字
    Query query = multiQuery.parse("apache");
    
    //指定索引和文檔的目錄
    Directory dir = FSDirectory.open(new File("E:\\dic"));
    //索引和文檔的讀取對象
    IndexReader indexReader = IndexReader.open(dir);
    //建立索引的搜索對象
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //搜索:第一個參數爲查詢語句對象, 第二個參數:指定顯示多少條
    TopDocs topdocs = indexSearcher.search(query, 5);
    //一共搜索到多少條記錄
    System.out.println("=====count=====" + topdocs.totalHits);
    //從搜索結果對象中獲取結果集
    ScoreDoc[] scoreDocs = topdocs.scoreDocs;
    
    for(ScoreDoc scoreDoc : scoreDocs){
        //獲取docID
        int docID = scoreDoc.doc;
        //經過文檔ID從硬盤中讀取出對應的文檔
        Document document = indexReader.document(docID);
        //get域名能夠取出值 打印
        System.out.println("fileName:" + document.get("fileName"));
        System.out.println("fileSize:" + document.get("fileSize"));
        System.out.println("============================================================");
    }
}
相關文章
相關標籤/搜索