一、結構化數據:有固定類型或者有固定長度的數據java
數據庫中的數據(mysql,oracle等), 元數據(就是windows中的數據)。數據庫中數據經過sql語句能夠搜索,元數據(windows中的)經過windows提供的搜索欄進行搜索mysql
二、非結構化數據:沒有固定類型和固定長度的數據web
world文檔中的數據, 郵件中的數據。Word文檔使用ctrl+F來搜索算法
三、順序掃描法sql
Ctrl+F中是使用的順序掃描法,拿到搜索的關鍵字,去文檔中,逐字匹配,直到找到和關鍵字一致的內容爲止數據庫
優勢: 若是文檔中存在要找的關鍵字就必定能找到想要的內容apache
缺點: 慢, 效率低windows
四、全文檢索算法(倒排索引算法)tomcat
將文件中的內容提取出來, 將文字拆封成一個一個的詞(分詞), 將這些詞組成索引(字典中的目錄), 搜索的時候先搜索索引,經過索引找文檔,這個過程就叫作全文檢索oracle
分詞: 去掉停用詞(a, an, the ,的, 地, 得, 啊, 嗯 ,呵呵),由於搜索的時候搜索這些詞沒有意義,將句子拆分紅詞,去掉標點符號和空格
優勢: 搜索速度快
缺點: 由於建立的索引須要佔用磁盤空間,因此這個算法會使用掉更多的磁盤空間,這是用空間換時間
原理
至關於字典,分爲目錄和正文兩部分,查詢的時候經過先查目錄,而後經過目錄上標註的頁數去正文頁查找須要的內容
apache旗下的頂級項目,是一個全文檢索工具包,可使用它來構建全文檢索引擎系統,可是它不能獨立運(JDK要求1.7)
一、全文檢索引擎系統
放在tomcat下能夠獨立運行,對外提供全文檢索服務
二、應用領域
(1)互聯網全文檢索引擎(好比百度, 谷歌, 必應)
(2)站內全文檢索引擎(淘寶, 京東搜索功能)
(3)優化數據庫查詢(由於數據庫中使用like關鍵字是全表掃描也就是順序掃描算法,查詢慢)
Lucene包括索引和文檔(K-V)
域名:詞 這樣的形式,它裏面有指針執行這個詞來源的文檔
索引庫:放索引的文件夾
詞元(Term):就是一個詞, 是Lucene中詞的最小單位
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("============================================================"); } }
在文檔Document對象中的Field,爲K-V鍵值對
一、是否分詞
分詞的做用是爲了索引
須要分詞: 文件名稱, 文件內容
不須要分詞: 不須要索引的域不須要分詞,還有就是分詞後無心義的域不須要分詞,好比: id, 身份證號
二、是否索引
索引的的目的是爲了搜索
須要搜索的域就必定要建立索引,只有建立了索引才能被搜索出來
不須要搜索的域能夠不建立索引
須要索引: 文件名稱, 文件內容, id, 身份證號等
不須要索引: 好比圖片地址不須要建立索引, e:\\xxx.jpg,根據圖片地址搜索無心義
三、是否存儲
存儲的目的是爲了顯示
是否存儲看我的須要,存儲就是將內容放入Document文檔對象中保存出來,會額外佔用磁盤空間, 若是搜索的時候須要立刻顯示出來能夠放入document中也就是要存儲,
這樣查詢顯示速度快, 若是不是立刻馬上須要顯示出來,則不須要存儲,由於額外佔用磁盤空間不划算
四、域的類型
注意:Lucene底層的算法,錢數是要分詞的,由於要根據價錢進行對比
StandardAnalyzer:單字分詞:就是按照中文一個字一個字地進行分詞
CJKAnalyzer:二分法分詞:按兩個字進行切分
SmartChineseAnalyzer:對中文支持較好,但擴展性差,擴展詞庫,禁用詞庫和同義詞庫等很差處理
IK-analyzer
stopword.dic中止字典和擴展字典ext.dic,修改後保存爲UTF-8
導入IKAnalyzer2012FF_u1.jar
加配置文件ext.dic、IKAnalyzer.cfg.xml、stopword.dic
一、刪除文檔
能夠根據某個域的內容進行刪除,還能夠一次刪除全部
@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(); }
一、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("============================================================"); } }