Lucene.net站內搜索—6、站內搜索第二版


目錄

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 = “0 15 10 ? * *”; 

//每隔一段時間執行任務
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);//5秒后開始第一次運行

TimeSpan interval = TimeSpan.FromHours(1);//每隔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=1,ISBN="01",PublishDate=DateTime.Now,Title="bad",UnitPrice=10.00m });
            bookList.Add(new Book { Id = 2, ISBN = "02", PublishDate = DateTime.Now, Title = "Anple", UnitPrice = 10.00m });
            bookList.Add(new Book { Id = 3, ISBN = "03", PublishDate = DateTime.Now, Title = "Acccd", UnitPrice = 10.00m });
            bookList.Add(new Book { Id = 4, ISBN = "04", PublishDate = DateTime.Now, Title = "bcccd", UnitPrice = 10.00m });

            List<string> list = new List<string>();
            for (int i = 0; i < bookList.Count; i++)
            {
                if (bookList[i].Title.ToLower().LastIndexOf(term.ToLower())==0)
                {
                    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;}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM