目錄

Lucene.net站內搜尋—1、SEO優化
Lucene.net站內搜尋—2、Lucene.Net簡介和分詞
Lucene.net站內搜尋—3、最簡單搜尋引擎程式碼
Lucene.net站內搜尋—4、搜尋引擎第一版技術儲備(簡單介紹Log4Net、生產者消費者模式)
Lucene.net站內搜尋—5、搜尋引擎第一版實現
Lucene.net站內搜尋—6、站內搜尋第二版

第二版功能需求

  • 自動完成
  • 熱門詞彙(SEO)
  • 顯示分詞、執行耗時
  • 分頁
  • 頁面美化

我們先假設用一張表來儲存使用者所有的關鍵字搜尋和次數,如下:

關鍵字名稱

搜尋次數

新四大名捕

3

新圓月彎刀

4

北京愛情故事

6

這樣實現的缺點:1、無法實現搜尋“最近7天的熱詞”2、效能差,因為update qCount=qCount+1 where Keyword=“新四大名捕”需要鎖定行。

搜尋記錄表用Guid。自動增長(標識列)是需要依賴於“上一次的值”,所以需要排隊,效能差;而Guid的生成則是獨立的,不需要依賴,所以效能好。

select top 10 Keyword,count(*) from T_SearchRecords where Keyword like '新四大名捕%' group by Keyword order by Count(*) desc

1、Like不一定會全表掃描。“%a%”會,而"a%"則不會。

2、group by肯定非常消耗資源。

因為對於每次搜尋都要:group by、order by、where like。所有的搜尋的group by、order by都是一樣的,沒必要重複,所以應該吧“group by、order by”結果快取起來。

因此進行資料的彙總結果插入:T_SearchRecordsSum(Keywords,SearchCount)表。每天彙總一次,這樣會有資料不及時的情況,但是並不是實時性非常強的,因此沒關係,大不了提高彙總的頻率。

Keywords  SearchCount

新四大名捕  5

新圓月彎刀  3

delete from T_SearchRecordsSum;
insert into T_SearchRecordsSum(Keywords,SearchCount)
select Keyword,count(*) from T_SearchRecords group by Keyword order by Count(*) desc;

最熱搜尋

兩張表,一張明細、一張彙總,定時把明細表的資料插入彙總表,查詢的時候到彙總表查詢。

由於明細表的主鍵不需要連續,而且用自動增長欄位會排隊、加鎖從而降低效率,因此主鍵用Guid。(為啥文章用自動增長?url短、好看。)

insert into T_KeyWordsRank(Id,KeyWords,SearchTimes)
select newid(), KeyWords,Count(*) from T_SearchDetails where DateDiff(day,SearchDateTime,GetDate())<=7 group by KeyWords

Group by效率低,寫SQLServer的是牛人,但不是神人

Quartz.Net

Quartz.Net是一個定時任務框架,可以實現異常靈活的定時任務,開發人員只要編寫少量的程式碼就可以實現“每隔1小時執行”、“每天22點執行”、“每月18日的下午執行8次”等各種定時任務。

Quartz.Net中的概念:計劃者(IScheduler)、工作(IJob)、觸發器(Trigger)。給計劃者一個工作,讓他在Trigger(什麼條件下做這件事)觸發的條件下執行這個工作

將要定時執行的任務的程式碼寫到實現IJob介面的Execute方法中即可,時間到來的時候Execute方法會被呼叫。

CrondTrigger是通過Crond表示式設定的觸發器,還有 SimpleTrigger等簡單的觸發器。可以通過TriggerUtils的MakeDailyTrigger、MakeHourlyTrigger等方法簡化呼叫。呼叫程式碼參考備註。

實現:用Quartz.Net完成定時搜尋資料彙總。

第一個使用者訪問我們的WebApplication的時候,Application_Start才執行。

//0 15 10 ? * *" :Fire at 10:15am every day
CronTrigger trigger = new CronTrigger("trigger1", "group1", "job1", "group1");
trigger.CronExpressionString = “ ? * *”; //每隔一段時間執行任務
IScheduler sched;
ISchedulerFactory sf = new StdSchedulerFactory();
sched = sf.GetScheduler();
JobDetail job = new JobDetail("job1", "group1", typeof(IndexJob));//IndexJob為實現了IJob介面的類 DateTime ts = TriggerUtils.GetNextGivenSecondDate(null, );//5秒後開始第一次執行 TimeSpan interval = TimeSpan.FromHours();//每隔1小時執行一次
Trigger trigger = new SimpleTrigger("trigger1", "group1", "job1", "group1", ts, null,
SimpleTrigger.RepeatIndefinitely, interval);//每若干小時執行一次,小時間隔由appsettings中的IndexIntervalHour引數指定
sched.AddJob(job, true);
sched.ScheduleJob(trigger);
sched.Start();

要關閉任務定時則需要sched.Shutdown(true)

搜尋記錄

搜尋建議、最新熱門搜尋都是基於已有的使用者搜尋記錄。SEO的歪門邪道:刷百度搜相關詞彙(SEO最終目的還是讓使用者找到我們的網站)。定期清除舊的歷史資料,防止資料庫太大

每次有使用者搜尋都把使用者的搜尋行為記錄下來,供熱門搜尋和搜尋建議用。三個欄位:搜尋時間、搜尋詞(一句話)、訪問者IP地址。因為自動增長欄位(標識列)有加鎖的機制,所以慢,這裡呢的Id也不會在邏輯中有,所以用Guid(不會加鎖)

使用三層,增加插入記錄的方法,在搜尋的時候插入記錄,IP地址:Request.UserHostAddress。

todo:為了避免groupby耗時,每小時做一次group by

自動完成

關於自動搜尋功能這裡我使用jquery ui 來實現,先下載AutoComplete.rar下載路徑:http://files.cnblogs.com/files/jiekzou/AutoComplete.rar

當然,你也可以在google搜尋“JQuery AutoComplete”,找到了JQueryUI庫中的AutoComplete元件。http://jqueryui.com/demos/autocomplete/

效果圖如下:

新建AutoComplete.aspx

要求伺服器返回搜尋建議詞彙的時候將詞彙以字串陣列的形式(JSon格式)返回給瀏覽器。

不要忘了引入jquery、jqueryui的js和css,注意順序。

autocomplete的source屬性指定自動完成資料的資料來源

生命週期的問題:在使用者敲入文字的時候,Autocomplete元件向ashx頁面發出ajax請求,並且將ashx返回的json格式的陣列顯示出來。(*)defer請求

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AutoComplete.aspx.cs" Inherits="BookShop.Web.AutoComplete" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<link href="/Css/themes/ui-lightness/jquery-ui-1.8.2.custom.css" rel="stylesheet" type="text/css" />
<script src="/js/jquery-1.4.2.js" type="text/javascript"></script>
<script src="/js/jquery-ui-1.8.2.custom.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () { $("#<%=txtSearch.ClientID%>").focus(function () { if ($(this).val() == "請輸入搜尋內容") { $(this).css("color", "black").val("");
}
}).blur(function () {
//游標離開
if ($(this).val() == "") {
$(this).css("color", "Gray").val("請輸入搜尋內容"); }
}); $("#<%=txtSearch.ClientID%>").autocomplete({
source: "/ashx/AutoComplete.ashx",
minLength: 1 }); });
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txtSearch" runat="server" Width="432px" style="color:grey">請輸入搜尋內容</asp:TextBox>
<asp:Button ID="btnSearch" runat="server" Text="Search" />
</div>
</form>
</body>
</html>

新建一般處理程式:AutoComplete.ashx

    public class AutoComplete : IHttpHandler
{ public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
string term = context.Request.QueryString["term"];
//BLL.BookManager bll = new BookShop.BLL.BookManager();
List<Book> bookList = new List<Book>();//bll.GetBooks(term); bookList.Add(new Book {Id=,ISBN="",PublishDate=DateTime.Now,Title="bad",UnitPrice=10.00m });
bookList.Add(new Book { Id = , ISBN = "", PublishDate = DateTime.Now, Title = "Anple", UnitPrice = 10.00m });
bookList.Add(new Book { Id = , ISBN = "", PublishDate = DateTime.Now, Title = "Acccd", UnitPrice = 10.00m });
bookList.Add(new Book { Id = , ISBN = "", PublishDate = DateTime.Now, Title = "bcccd", UnitPrice = 10.00m }); List<string> list = new List<string>();
for (int i = ; i < bookList.Count; i++)
{
if (bookList[i].Title.ToLower().LastIndexOf(term.ToLower())==)
{
list.Add(bookList[i].Title);
}
}
//序列化物件
System.Web.Script.Serialization.JavaScriptSerializer js = new System.Web.Script.Serialization.JavaScriptSerializer();
context.Response.Write (js.Serialize(list.ToArray())); } public bool IsReusable
{
get
{
return false;
}
}
}

熱門詞彙

在搜尋框下方顯示搜尋次數最多的語句,語句新增超連結,使用者可以點選語句的超連結快捷的開始搜尋。用ObjectDataSource和Repeater控制元件。

在搜尋框下方顯示熱門詞彙有利於SEO,方便著名的搜尋引擎收錄網站的搜尋結果頁面,因為搜尋引擎只認超連結。很多站內搜尋都有熱門詞彙就是這麼回事。

熱門詞彙是所有訪問者每次訪問頁面的時候都要顯示的,所以需要快取。這個是快取的一個很好的例子,面試的時候問到快取的問題舉這個例子就很好。

顯示資料庫中所有搜尋次數最多的語句,好嗎?不好,容易形成馬太效應。只取一週之內的最熱門。

在經常需要進行檢索的欄位上新增索引,可以提高檢索速度。

<div class="rp_5equalcol"><asp:HyperLink Text='<%# Eval("KeyWord")%>' NavigateUrl='<%#EvalSearchURL(Eval("KeyWord").ToString()) %>' runat="server"></asp:HyperLink></div>
private string GetPageAbsolutePath()
{
string pageurl = this.AppRelativeVirtualPath;
return this.ResolveUrl(pageurl);
} public string EvalSearchURL(string kw)
{
string pageurl = GetPageAbsolutePath();
return string.Format("{0}?kw={1}", pageurl,HttpUtility.UrlEncode(kw));
}

搜尋結果分頁

開發站內搜尋的分頁功能。

能夠SEO的無重新整理分頁。

這裡你可以使用現有的第三方分頁控制元件,也可以自己寫一個或自己改造一個,注意使用ajax無重新整理分頁。

Lucene.net高階

這裡我只做簡單介紹,有興趣的朋友可以自己去研究下。

(*) IndexSearcher是Searcher類的一個子類,Searcher類還有其他子類,MultiSearcher(在多個索引上搜索),ParallelMultiSearcher(在多個索引上並行搜尋,速度更快),做更大的搜尋引擎會用到。

(*)可以對檢索過程過濾,可以實現評分大於0.8的才顯示搜尋結果,在搜尋出的資料太多的情況下調整。搜尋排序也是預設根據評分。
(*)除了PhraseQuery,還有BooleanQuery(檢索包含“網路”或者“志願者”;搜尋標題或者正文中包含“搞笑電影”的資源)、FuzzyQuery(糾錯匹配,使用者輸入“Chjna”時候也能匹配“China”,需要提供額外的糾錯資料)、WildcardQuery (萬用字元檢索,包含“搞笑*電影”)等  #mailContentContainer .txt {height:auto;}