Lucene.net站内搜索—5、搜索引擎第一版实现
目錄
Lucene.net站內(nèi)搜索—1、SEO優(yōu)化
Lucene.net站內(nèi)搜索—2、Lucene.Net簡介和分詞
Lucene.net站內(nèi)搜索—3、最簡單搜索引擎代碼
Lucene.net站內(nèi)搜索—4、搜索引擎第一版技術儲備(簡單介紹Log4Net、生產(chǎn)者消費者模式)
Lucene.net站內(nèi)搜索—5、搜索引擎第一版實現(xiàn)
Lucene.net站內(nèi)搜索—6、站內(nèi)搜索第二版
- ?站內(nèi)搜索模塊:生產(chǎn)者、消費者,多線程。復習多線程,用多線程做一個winform的生產(chǎn)者、消費者的例子,有任務的時候(點按鈕給整數(shù))就處理任務,沒任務的時候就每次掃描都說“還是沒任務,睡會再看”,用Sleep模擬耗時操作,線程中操作UI線程的代碼見備注。
- ?派一個人來管理索引庫,想向索引庫中寫數(shù)據(jù)的地方都向這個人來發(fā)出請求。
- ?由于索引庫同時只能有一個IndexWriter進行寫,所以有一個消費者線程一直保持對IndexWriter寫的狀態(tài),有新任務進入的時候?qū)ndexWriter寫入。如果IndexWriter一直保持打開狀態(tài)的話,新添加的文檔是不會被搜索到的,因此必須處理完隊列中的任務后關閉writer,然后下次while循環(huán)掃描的時候判斷如果隊列匯總沒有任務,則sleep5秒鐘后再判斷,防止不斷判斷給服務器cpu壓力
- ?IndexManager做成單例。維持一個任務的Queue,Thread thread = new Thread(ScanThread); thread.Start();啟動一個線程,在ScanThread方法中不斷遍歷Queue ,當有新任務加入的時候把新任務加入索引庫,當要刪除文章的時候也是加入一個jobType == JobType.Delete的內(nèi)容。UpdateDocument
- 文章的更新和刪除。
1、線程訪問UI線程:
ParameterizedThreadStart threadStart = (obj) =>{txtLog.AppendText(obj + "\n");};txtLog.Invoke(threadStart, item);詳細代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Threading; using log4net; using System.Configuration; using System.Web.Hosting; using RuPeng.Utils; using RuPengSite.DataTier.DataSetThreadTableAdapters; using System.Text; using Lucene.Net.Store; using Lucene.Net.Index; using System.IO; using Lucene.Net.Analysis.PanGu; using Lucene.Net.Documents; namespace RuPengSite.Search {public class IndexManager{public readonly static IndexManager Instance = new IndexManager();private HashSet<IndexJobItem> jobs = new HashSet<IndexJobItem>();//任務的集合private bool isStopped;//任務是否停止private static ILog log = LogManager.GetLogger(typeof(IndexManager));private IndexManager(){ }//啟動任務public void Start(){isStopped = false;Thread thread = new Thread(ScanThread);thread.Start(); }//停止任務public void Stop(){isStopped = true;}/// <summary>/// 掃描線程/// </summary>private void ScanThread(){//如果停止,則不再無限循環(huán)while (!isStopped){Thread.Sleep(5000);//休息5秒鐘,盡可能多的累積任務if (jobs.Count <= 0){continue;//如果沒任務繼續(xù)睡 }log.Debug("開始索引預處理");string indexPath = SearchHelper.GetSearchIndexFullPath();log.Debug("索引路徑是:" + indexPath);FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NativeFSLockFactory());//判斷索引目錄是否已經(jīng)存在bool isUpdate = IndexReader.IndexExists(directory);log.Debug("索引路徑存在狀態(tài)是" + isUpdate);if (isUpdate){//如果索引目錄被鎖定(比如索引過程中程序異常退出),則首先解鎖if (IndexWriter.IsLocked(directory)){log.Debug("開始解鎖索引路徑");IndexWriter.Unlock(directory);}}IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);try{ProcessJobItems(directory, writer);}finally{log.Debug("開始關閉reader、writer");writer.Close();directory.Close();log.Debug("完成關閉reader、writer");} }}/// <summary>/// 處理隊列中的任務/// </summary>/// <param name="directory"></param>/// <param name="writer"></param>private void ProcessJobItems(FSDirectory directory, IndexWriter writer){log.Debug("開始處理隊列中的"+jobs.Count+"個任務");foreach (var jobItem in jobs.ToArray())//轉(zhuǎn)換為數(shù)組,避免讀的時候不能修改的問題 {try{ProcessJobItem(writer, jobItem);jobs.Remove(jobItem);//將處理完成的任務移除 }catch (Exception ex){log.Error("對任務進行處理失敗" + jobItem, ex);} }log.Debug("隊列中的任務處理完畢");}private static void ProcessJobItem(IndexWriter writer, IndexJobItem jobItem){long threadId = jobItem.ThreadId;JobType jobType = jobItem.ItemType;string url = SearchHelper.GetThreadUrl(threadId);if (jobType == JobType.Delete)//判斷任務的類型 {log.Debug("將帖子從索引中移除,threadId=" + threadId);writer.DeleteDocuments(new Term(SearchHelper.URL, url));//刪除舊的收錄 }else if (jobType == JobType.Add){writer.DeleteDocuments(new Term(SearchHelper.URL, url));//刪除舊的收錄var threads = new rp_threadsTableAdapter().GetDataById(threadId);if (threads.Count <= 0){log.Debug("id為"+threadId+"的帖子不存在!");return;}string body = SearchHelper.GetThreadContent(threadId);//帖子內(nèi)容string title = threads.Single().Subject;//主題Document document = new Document();document.Add(new Field(SearchHelper.URL, url, Field.Store.YES, Field.Index.NOT_ANALYZED));document.Add(new Field(SearchHelper.TITLE, title, Field.Store.YES, Field.Index.NOT_ANALYZED));document.Add(new Field(SearchHelper.BODY, body, Field.Store.YES, Field.Index.ANALYZED,Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));writer.AddDocument(document);log.Debug("索引帖子" + threadId+"完成");}else{throw new Exception("錯誤的jobType:" + jobType);}}/// <summary>/// 添加帖子的索引任務/// </summary>/// <param name="threadId"></param>public void AddThread(long threadId){IndexJobItem jobItem = new IndexJobItem() { ItemType=JobType.Add,ThreadId=threadId};jobs.Add(jobItem);}/// <summary>/// 移除帖子的索引任務/// </summary>/// <param name="threadId"></param>public void DeleteThread(long threadId){IndexJobItem jobItem = new IndexJobItem() { ItemType = JobType.Delete, ThreadId = threadId };jobs.Add(jobItem);}class IndexJobItem{public JobType ItemType { get; set; }public long ThreadId { get; set; }public override bool Equals(object obj){IndexJobItem item = obj as IndexJobItem;if (item == null){return false;}return this.ItemType==item.ItemType&&this.ThreadId==item.ThreadId;}public override int GetHashCode(){return ToString().GetHashCode();}public override string ToString(){return ItemType+":"+ThreadId;}}enum JobType {Delete,Add }//任務類型 } }
多條件查詢
我看了下淘寶,淘寶的站內(nèi)搜索只實現(xiàn)了且條件:
或條件查詢可以來看看百度,當然,百度是同時采用了且條件和或條件查詢的:
在標題和正文中查找
PhraseQuery queryMsg = new PhraseQuery();foreach (string word in CommonHelper.SplitWords(txtKW.Text)){queryMsg.Add(new Term("msg", word));}queryMsg.SetSlop(100); PhraseQuery queryTitle = new PhraseQuery();foreach (string word in CommonHelper.SplitWords(txtKW.Text)){queryTitle.Add(new Term("title", word));}queryTitle.SetSlop(100); BooleanQuery query = new BooleanQuery();query.Add(queryMsg, BooleanClause.Occur.SHOULD); query.Add(queryTitle, BooleanClause.Occur.SHOULD);
BooleanQuery相當于盛放其他查詢條件的容器,類似于div。第二個參數(shù):Must為必須有,Must_Not為必須沒有,Should為可以有
高亮顯示
- 高亮顯示,只顯示包含關鍵詞的部分。參考盤古分詞的文檔。
- 從網(wǎng)上、文檔找來的代碼不用細讀每行代碼,先把它拿過來運行通過再說。
- 不用每次改代碼都重啟,在項目的屬性頁面的Web中選中“啟用編輯并繼續(xù)(Enable Edit and Continue)”
- 把font控制顏色改成通過style控制顏色,原則“不要在正文中控制格式”。不推薦使用font、b、i、br等。
if (string.IsNullOrEmpty(msg)){return content;}else{return msg;}}String hightlightTitle = highLight(keyword, title);String hightlightBody = HttpUtility.HtmlEncode(body);//防止XSS攻擊hightlightBody = highLight(keyword, hightlightBody);
路徑可配置化
- 連接配置信息放到Web.Config的ConnectionStrings段中,而普通的自定義配置則可以寫到AppSettings段中,哪些需要配置:索引的路徑,被索引的網(wǎng)站url,索引的時間間隔。
- 讀取string indexPath = ConfigurationManager.AppSettings["IndexPath"],使用ConfigurationManager添加引用System.Configuration
- 使用request.MapPath或者Server.MapPath把相對于網(wǎng)站根路徑的路徑轉(zhuǎn)換為絕對路徑(不是轉(zhuǎn)換為http://www.baidu.com/a.aspx,轉(zhuǎn)換為c:/baidu_com/a.aspx)。在定時任務等不在Http線程中取HttpContext.Current得到的是null,因此在定時任務中不能用HttpContext.Current.Server.MapPath方法來轉(zhuǎn)換,要用HostingEnvironment.MapPath,因此可以在其他地方也用HostingEnvironment.MapPath。
- 修改Web.config會造成IIS重啟,這樣會立即加載新的任務
解決:地址無法發(fā)給好友
我們先看下淘寶的站內(nèi)搜索:
細心看,我們會發(fā)現(xiàn)url是一連串的字符串,可以肯定這是采用了get請求的方式。
- 用戶沒法把搜索結(jié)果頁面發(fā)給好友,要用Get提交,這樣才能得到搜索頁面地址。如果采用Get方式的話,要刪掉form的runat=server,變成HTML的form、method改為get,所有控件都要用HTML控件。因為只有去掉runat=server的form,才會完全去掉ViewState
- 注意input不能只指定id,而應該指定name,否則不會出現(xiàn)在querystring中。Id是供Javascript用的,name是供querystring/Request用的。對于type=submit的input來說,只有被點擊的input的name、value才會被提交給服務器。
- method改為get
- 1、要刪掉form的runat=server。(唯一去掉viewstate的方法)
- 2、所有除了DataBound控件(比如GridView、Repeater等)都要用HTML控件。Repeater、ObjectDataSource之類控件不需要runat=server的form也可以,但是VS總是提示,去源代碼視圖拖放、讓他生成再手動刪掉。
- 3、控件注意要給表單name屬性賦值。
- 4、在后臺Page_Load代碼中進行響應
- 5、IsPostBack不再有用,只能通過判斷參數(shù)是否為空來判斷是否是提交的頁面。
- 點搜索按鈕以后如何顯示搜索關鍵字:在aspx.cs中定義一個GetKeyWord方法,<input type="text" id="kw1" name="kw" value='<%=Request["kw"] %>'/>
只要有runat=server的form就會產(chǎn)生__VIEWSTATE等,所以去掉form的runat=server,這樣除了Repeater等少數(shù)控件之外服務端控件都沒法使用,只能使用html標簽。這是為什么說“要求高的互聯(lián)網(wǎng)項目不用服務端控件”。面試時候說:我在有的項目中沒有用服務端控件的例子。
為了能讓查詢參數(shù)顯示在地址欄中,方便傳播地址,把form的method改為get;因為ViewState太長,所以影響美觀,因此禁用ViewState;但是發(fā)現(xiàn)哪怕禁用ViewState,ViewState也沒有完全消失;研究發(fā)現(xiàn),只有去掉form的runat=server后才能完全干掉ViewState;但是,一旦去掉form的runat=server后幾乎所有的WebForm控件都用不了(除了Repeater等少數(shù)幾個和input無關的之外),只能用html控件,然后在Page_Load中進行響應。
轉(zhuǎn)載于:https://www.cnblogs.com/jiekzou/p/4381649.html
總結(jié)
以上是生活随笔為你收集整理的Lucene.net站内搜索—5、搜索引擎第一版实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 树——平衡二叉树插入和查找的JAVA实现
- 下一篇: 机房合作(一):我怎样做组长(敢于承担责