您好,登錄后才能下訂單哦!
數據量大,要求高的時候,數據庫內容很多,數據庫搜索的時候對數據庫服務器壓力大的時候,請用全文檢索-Lucene框架
搜索的數據是什么?
文本(important)
多媒體
搜索的方式是什么?
不處理語義
搜索含有指定詞匯的文章
應用范圍?
網頁搜索,貼吧搜索,文檔搜索等
全文檢索的要求?
搜索速度要快
結果要準確
搜出多個結果的時候,把最匹配的結果放在前面,相關度排序
不區分大小寫
lucene.apache.org
apache提供了tomcat/struts/beanutils/dbUtils/..
在大量數據之中,Lucene如何實現快速檢索功能呢?
百度搜索---用戶請求發給百度服務器---返回結果
│
很多爬蟲從互聯網中抓了數據組織特定(快速搜索)格式放到百度服務器中的索引庫
│
Lucene管理索引庫,對外提供搜索功能
索引庫存放一堆二進制數據,可以吧索引庫理解為數據庫
如何建立索引庫的目錄?
網頁,文件,在java中表現的都是一個對象,普通javabean,用Map<String, Object>表示一個javabean里面的所有信息,比如Map<name,value>
核心包lucene-core-3.0.1.jar
特定功能包
分詞器 lucene-analyzers-3.0.1.jar
高亮關鍵字 lucene-highlighter-3.0.1.jar
高亮功能依賴的包 lucene-memory-3.0.1.jar
快速操作模式 shift + Alt +A
索引庫有兩個區
1.目錄區-分詞器劃分關鍵字,存儲(關鍵字與n個文章)對應關系
2.數據區-存儲document
doc.add(new Field("id", idStr, Store.YES, Index.ANALYZED));
doc.add(new Field("title", article.getTitle(), Store.YES, Index.ANALYZED));
doc.add(new Field("content", article.getContent(), Store.NO, Index.ANALYZED));
Store參數
用于指定某Field的原始值是否存到數據庫中
YES - 存儲,取出的Document中就有這個字段的值
NO - 不存儲,取出的Document中就沒有這個字段的值
Index參數
用于指定是否把某個Field中的文本值更新到目錄區中
NO - 不更新到目錄區中,不能按此字段搜索
ANALYZED - 先把字段文本值分詞處理,把分詞后的結果更新到目錄中
NOT_ANALYZED - 不分詞,直接把Field的文本值當作一個詞更新到目錄中,應用場景:作者、日期、數字、url、文件地址
Store | Index | 應用場景 |
YES | ANALYZED | 能搜索 能顯示 |
YES | NO | 不按一個字段搜索,但是顯示的時候,會顯示出這個字段。比如作者,不按作者搜索,但是顯示文章的時候會顯示作者。將一些數據存儲在索引庫里,可以直接拿出來用,不用再向數據庫要數據了,一次查詢顯示所有數據,效率高,比如百度快照。如果這個數據特別大,那就考慮不把內容存到索引庫里面了。 |
NO | ANALYZED | 能按這個字段搜索,能找到這個字段對應的數據編號,但是不顯示這個字段內容。比如電子書,能按照內容搜索,但是在結果頁面不顯示電子數內容,只顯示電子書標題、作者 |
NO | NOT_ANALYZED | 不允許 |
1.把Article轉為Document
Document doc = new Document();
doc.add(new Field("id", idStr, Store.YES, Index.ANALYZED));
2.添加到索引庫中
IndexWriter indexWriter =
new IndexWriter(directory, analyzer, MaxFieldLength.UNLIMITED);
indexWriter.addDocument(doc);
做兩件事情(1)把Document存到數據區中,這個時候會自動分配一個內部編號
(2)把一個field值(分詞或者不分詞)更新到目錄中。
搜索過程
1,把查詢字符串轉為Query對象(默認只從title中查詢)
QueryParser queryParser = new QueryParser(Version.LUCENE_30, "title", analyzer);
Query query = queryParser.parse(queryString);
2,執行查詢,得到中間結果 搜索目錄
IndexSearcher indexSearcher = new IndexSearcher(directory); // 指定所用的索引庫
TopDocs topDocs = indexSearcher.search(query, 100); // 最多返回前n條結果
int count = topDocs.totalHits;//關聯總條數
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
3,處理結果 根據docId取出真正的Document數據
搜索的時候 也要分詞,分成關鍵字去匹配目錄里面的關鍵字
分詞器
分詞規則
建立索引 和 搜索 都使用同一個分詞規則
MaxFieldLength
有限制10000 default
無限制 max
建立索引目錄的時候,只處理字段前多少個詞
web應用增刪改數據庫和索引庫會引發哪些問題?
數據庫和索引庫都有文章的信息,1.浪費存儲空間2.狀態同步問題
1.是否浪費?
索引庫存放數據的原則:能被搜索的數據,比如數據庫里有作者表,文章表,索引庫只存了文章表,根據作者搜索,可以搜索出作者寫的文章,而搜索不出作者的具體信息。重復的數據只是需要被搜索出來的數據。
一次搜索顯示所有數據,減輕數據庫壓力。
實現全文搜索必須要索引庫,為了不存儲重復信息,是否能取消數據庫,把數據全部存到索引庫中?
不行!數據庫的一些功能,索引庫無法實現,比如事務管理。
2.保證索引庫的狀態與數據源的狀態一致的方案:
(1)方案一:當對數據庫做增刪改的操作時,就同時對索引庫也做增刪改的操作會出現什么問題?在一個別人已經存在的一個程序上,不讓你修改他們的程序,但是我要增加一個搜索功能,怎么辦?比如百度去對別人網頁的數據做組織成一個搜索。
(2)方案二:自己不能控制數據源的時候,定時從數據源中抓取數據 -- 爬蟲的概念。定時重建索引庫,(或者定時與數據源做同步的操作),有時候做同步操作還沒有重建索引庫快呢,除非索引庫忒別大。忒別大的時候就是利用爬蟲分析網絡上的網頁是否有最新數據,比如MD5摘要一下網頁中的內容,(MD5摘要的內容是不可逆的),兩個不同字符串做MD5摘要是不同,對比MD5摘要,不一樣就更新索引庫,幾百萬的數據重建是比較快的。
垂直搜索:對專業的事情有更細致的分析,比如淘寶搜索商品,能根據價格搜索特點商品。
根據數據源的不同,采用不同方案
1.數據源是數據庫 使用方案一或者方案二
2.數據源是網頁 使用方案二,流行垂直搜索
3.數據源是文件 采用JNI技術:在java里面調用C或者C++程序,及時獲取操作系統的信息。使用方案二。
eg:利用struts2做 貼吧的增刪改查
ArticleAction{
add() {
form-->article; dao.save(article);..//保存到數據庫
indexDao.save(article)//保存到索引庫
}
delete() {
id = getparam(“id”); dao.delete(id);..//從數據庫里刪除
//從索引庫中刪除
}
modify() {
article= getById(id); form-->articel; dao.update(article);//更新到數據庫
//更新到索引庫
}
}
對索引庫的增刪改查
IndexDao{
save(article);
delete(id);
update(article);
search(str)
...
}
article 的id也要存到索引庫里,必須不分詞的存入,這是唯一標識符,可以準確鎖定article,比如可以根據id查找刪除索引庫里article。
int 類型轉化為string類型使用lucene里面的方法,存放的int類型的2進制類型。直接用toString會將一個4字節的數據變成一個十幾個字節的數據,浪費空間,不好排序。
Ctrl + T 查看繼承關系
測試
初始化IndexWriter 如果沒有關閉,正在被使用,可能內存上的緩存就沒有刷新到硬盤上面,沒有釋放資源,web應用程序需要反正application監聽器里面,在應用程序啟動時創建indexwriter對象,應用程序退出之前關閉indexwriter對象。java程序,虛擬機退出之前關閉eindexWriter對象。指定一段代碼,在jvm退出之前執行。
indexDir文件夾里面很多文件,有的已經被標記為刪除文件,為什么不直接刪除,而是標記呢,這是應為這個文件夾很大,頻繁修改增刪這個文件夾可能帶來效率低問題,什么時候才刪除這些標記文件呢,lucene不忙的時候,才進行合并或者刪除。這些被標記刪除的文件怎么可以不被查出來呢?先查出所有數據,再過濾掉del文件。優化之后,就沒有了del文件了==合并文件,幾個小文件合并成一個大文件,減少io操作。
優化 就是 合并文件,合并幾個小文件成一個大文件
什么時候合并文件呢
批量操作,每天重新創建索引庫,抓了更多文件之后合并成一個大文件放入索引庫里面,當擴展名相同的文件達到一定數量之后,默認10條,最小2條,再合并==自動優化,
@Test
public void test() throws Exception {
LuceneUtils.getIndexWriter().optimize();
}
// 自動合并文件
@Test
public void testAuto() throws Exception {
// 配置當小文件的數量達到多少個后就自動合并為一個大文件,默認為10,最小為2.
LuceneUtils.getIndexWriter().setMergeFactor(5);
// 建立索引
Article article = new Article();
article.setId(1);
article.setTitle("準備Lucene的開發環境");
article.setContent("如果信息檢索系統在用戶發出了檢索請求后再去互聯網上找答案,根本無法在有限的時間內返回結果。");
new ArticleIndexDao().save(article);
}
爬蟲:功能,抓網頁。
把一個網站上面所有頁面下載下來?
方案:
0.初始條件:首頁
1.下載網頁得到網頁的內容
2.獲取其中所有超鏈接
3.去掉已經下載過的鏈接和棧外的鏈接
4.循環處理每一個有效的超鏈接 回到 1 沒新的鏈接出現就停止循環
技術:
如何下載一個網站的URL - UrlConnection(Socket)
Http協議 發送請求 響應實體內容
1.下載網頁
public static String downLoad(String urlString){
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream();
//in 流中的數據就是網頁內容
2.獲取所有的超鏈接
Dom + XPath
3.去掉已經下載過的鏈接和棧外的鏈接
把下載過的鏈接全部放到數據庫里面,或者一個集合里面,新的鏈接和集合中的鏈接進行比較,看是否包含。
如果要做到更好一點:出現網絡問題,好多個線程去訪問,總有幾個線程訪問不了,可以進行3次請求鏈接。
多個線程不斷的從任務隊列里面拿任務
任務隊列的(需要完成的任務)放到數據庫里面,停電也沒有關系。多線程從數據庫里查詢和刪除任務。隊列形式解決了遞歸的內存溢出,停電無斷點,不能使用多線程的問題。
在java里面用LinkedList表示隊列
addFirst() removeLast()
addLast() removeFirst()
使用多種模式:使用架構,mvc,繼承
類變多了,關系變復雜了
1.適用更復雜,要求嚴格的情況
2.代碼可讀性好
3.結構合理,方便修改
4.方便擴展,可維護性強
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。