目录

Lucene.net站内搜索—1、SEO优化
Lucene.net站内搜索—2、Lucene.Net简介和分词
Lucene.net站内搜索—3、最简单搜索引擎代码
Lucene.net站内搜索—4、搜索引擎第一版技术储备(简单介绍Log4Net、生产者消费者模式)
Lucene.net站内搜索—5、搜索引擎第一版实现
Lucene.net站内搜索—6、站内搜索第二版

  • 站内搜索模块:生产者、消费者,多线程。复习多线程,用多线程做一个winform的生产者、消费者的例子,有任务的时候(点按钮给整数)就处理任务,没任务的时候就每次扫描都说“还是没任务,睡会再看”,用Sleep模拟耗时操作,线程中操作UI线程的代码见备注。
  • 派一个人来管理索引库,想向索引库中写数据的地方都向这个人来发出请求。
  • 由于索引库同时只能有一个IndexWriter进行写,所以有一个消费者线程一直保持对IndexWriter写的状态,有新任务进入的时候对IndexWriter写入。如果IndexWriter一直保持打开状态的话,新添加的文档是不会被搜索到的,因此必须处理完队列中的任务后关闭writer,然后下次while循环扫描的时候判断如果队列汇总没有任务,则sleep5秒钟后再判断,防止不断判断给服务器cpu压力
  • IndexManager做成单例。维持一个任务的Queue,Thread thread = new Thread(ScanThread); thread.Start();启动一个线程,在ScanThread方法中不断遍历Queue ,当有新任务加入的时候把新任务加入索引库,当要删除文章的时候也是加入一个jobType == JobType.Delete的内容。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()
{
//如果停止,则不再无限循环
while (!isStopped)
{
Thread.Sleep();//休息5秒钟,尽可能多的累积任务
if (jobs.Count <= )
{
continue;//如果没任务继续睡
}
log.Debug("开始索引预处理");
string indexPath = SearchHelper.GetSearchIndexFullPath();
log.Debug("索引路径是:" + indexPath);
FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NativeFSLockFactory());
//判断索引目录是否已经存在
bool isUpdate = IndexReader.IndexExists(directory);
log.Debug("索引路径存在状态是" + 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())//转换为数组,避免读的时候不能修改的问题
{
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 <= )
{
log.Debug("id为"+threadId+"的帖子不存在!");
return;
}
string body = SearchHelper.GetThreadContent(threadId);//帖子内容
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 }//任务类型
}
}

多条件查询

我看了下淘宝,淘宝的站内搜索只实现了且条件:

或条件查询可以来看看百度,当然,百度是同时采用了且条件和或条件查询的:

在标题和正文中查找

PhraseQuery queryMsg = new PhraseQuery();

foreach (string word in CommonHelper.SplitWords(txtKW.Text))
{
queryMsg.Add(new Term("msg", word));
} queryMsg.SetSlop();
PhraseQuery queryTitle = new PhraseQuery(); foreach (string word in CommonHelper.SplitWords(txtKW.Text))
{
queryTitle.Add(new Term("title", word));
} queryTitle.SetSlop();
BooleanQuery query = new BooleanQuery();
query.Add(queryMsg, BooleanClause.Occur.SHOULD);
query.Add(queryTitle, BooleanClause.Occur.SHOULD);

BooleanQuery相当于盛放其他查询条件的容器,类似于div。第二个参数:Must为必须有,Must_Not为必须没有,Should为可以有

高亮显示

  • 高亮显示,只显示包含关键词的部分。参考盘古分词的文档。
  • 从网上、文档找来的代码不用细读每行代码,先把它拿过来运行通过再说。
  • 不用每次改代码都重启,在项目的属性页面的Web中选中“启用编辑并继续(Enable Edit and Continue)”
  • 把font控制颜色改成通过style控制颜色,原则“不要在正文中控制格式”。不推荐使用font、b、i、br等。
private static String highLight(string keyword,String content)
{
PanGu.HighLight.SimpleHTMLFormatter formatter = new PanGu.HighLight.SimpleHTMLFormatter("<font color='red'>", "</font>");
PanGu.HighLight.Highlighter highlighter = new PanGu.HighLight.Highlighter(formatter, new Segment());
highlighter.FragmentSize = ;
string msg = highlighter.GetBestFragment(keyword,content);
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段中,哪些需要配置:索引的路径,被索引的网站url,索引的时间间隔。
  • 读取string indexPath = ConfigurationManager.AppSettings["IndexPath"],使用ConfigurationManager添加引用System.Configuration
  • 使用request.MapPath或者Server.MapPath把相对于网站根路径的路径转换为绝对路径(不是转换为http://www.baidu.com/a.aspx,转换为c:/baidu_com/a.aspx)。在定时任务等不在Http线程中取HttpContext.Current得到的是null,因此在定时任务中不能用HttpContext.Current.Server.MapPath方法来转换,要用HostingEnvironment.MapPath,因此可以在其他地方也用HostingEnvironment.MapPath。
  • 修改Web.config会造成IIS重启,这样会立即加载新的任务

解决:地址无法发给好友

我们先看下淘宝的站内搜索:

细心看,我们会发现url是一连串的字符串,可以肯定这是采用了get请求的方式。

  • 用户没法把搜索结果页面发给好友,要用Get提交,这样才能得到搜索页面地址。如果采用Get方式的话,要删掉form的runat=server,变成HTML的form、method改为get,所有控件都要用HTML控件。因为只有去掉runat=server的form,才会完全去掉ViewState
  • 注意input不能只指定id,而应该指定name,否则不会出现在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不再有用,只能通过判断参数是否为空来判断是否是提交的页面。
  • 点搜索按钮以后如何显示搜索关键字:在aspx.cs中定义一个GetKeyWord方法,<input type="text" id="kw1" name="kw" value='<%=Request["kw"] %>'/>

只要有runat=server的form就会产生__VIEWSTATE等,所以去掉form的runat=server,这样除了Repeater等少数控件之外服务端控件都没法使用,只能使用html标签。这是为什么说“要求高的互联网项目不用服务端控件”。面试时候说:我在有的项目中没有用服务端控件的例子。

为了能让查询参数显示在地址栏中,方便传播地址,把form的method改为get;因为ViewState太长,所以影响美观,因此禁用ViewState;但是发现哪怕禁用ViewState,ViewState也没有完全消失;研究发现,只有去掉form的runat=server后才能完全干掉ViewState;但是,一旦去掉form的runat=server后几乎所有的WebForm控件都用不了(除了Repeater等少数几个和input无关的之外),只能用html控件,然后在Page_Load中进行响应。

Lucene.net站内搜索—5、搜索引擎第一版实现的更多相关文章

  1. Lucene.net站内搜索—4、搜索引擎第一版技术储备(简单介绍Log4Net、生产者消费者模式)

    目录 Lucene.net站内搜索—1.SEO优化 Lucene.net站内搜索—2.Lucene.Net简介和分词Lucene.net站内搜索—3.最简单搜索引擎代码Lucene.net站内搜索—4 ...

  2. Lucene.net站内搜索—3、最简单搜索引擎代码

    目录 Lucene.net站内搜索—1.SEO优化 Lucene.net站内搜索—2.Lucene.Net简介和分词Lucene.net站内搜索—3.最简单搜索引擎代码Lucene.net站内搜索—4 ...

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

    目录 Lucene.net站内搜索—1.SEO优化 Lucene.net站内搜索—2.Lucene.Net简介和分词Lucene.net站内搜索—3.最简单搜索引擎代码Lucene.net站内搜索—4 ...

  4. Lucene.net站内搜索—2、Lucene.Net简介和分词

    目录 Lucene.net站内搜索—1.SEO优化 Lucene.net站内搜索—2.Lucene.Net简介和分词Lucene.net站内搜索—3.最简单搜索引擎代码Lucene.net站内搜索—4 ...

  5. Lucene.net站内搜索—1、SEO优化

    目录 Lucene.net站内搜索—1.SEO优化 Lucene.net站内搜索—2.Lucene.Net简介和分词Lucene.net站内搜索—3.最简单搜索引擎代码Lucene.net站内搜索—4 ...

  6. Lucene.Net 站内搜索

    Lucene.Net 站内搜索 一  全文检索: like查询是全表扫描(为性能杀手)Lucene.Net搜索引擎,开源,而sql搜索引擎是收费的Lucene.Net只是一个全文检索开发包(只是帮我们 ...

  7. Lucene.net站内搜索-最简单搜索引擎代码

    Lucene.Net核心类简介 先运行写好的索引的代码,再向下讲解各个类的作用,不用背代码. (*)Directory表示索引文件(Lucene.net用来保存用户扔过来的数据的地方)保存的地方,是抽 ...

  8. 百度站内搜索https不可用切换api搜索,加上谷歌api站内搜索

    google推https几年了,百度开始宣传全面https,但是,百度站内搜索 自己的服务却不走https,接口报错.百度分享也是. 然后采用http://search.zhoulujun.cn/cs ...

  9. 基于lucene.net 和ICTCLAS2014的站内搜索的实现1

    Lucene.net是一个搜索引擎的框架,它自身并不能实现搜索.须要我们自己在当中实现索引的建立,索引的查找.全部这些都是依据它自身提供的API来实现.Lucene.net本身是基于java的,可是经 ...

随机推荐

  1. iOS-SDWebImage

    我之前写过一篇博客,介绍缓存处理的三种方式,其中最难,最麻烦,最占内存资源的还是图片缓存,最近做的项目有大量的图片处理,还是采用了SDWebImage来处理,但是发现之前封装好的代码报错了.研究发现, ...

  2. linux执行sh脚本文件命令

    linux执行sh脚本文件命令 很多时候需要多个命令来完成一项工作,而这个工作又常常是重复的,这个时候我们自然会想到将这些命令写成sh脚本,下次执行下这个脚本一切就都搞定了,下面就是发布代码的一个脚本 ...

  3. cordova填坑

    cordova填坑

  4. 打开android虚拟机时出现a repairable android virtual device

    打开android虚拟机时出现a repairable android virtual device,虚拟机可以打开但是一直处于开机状态,具体解决方案如下: 解决方案1:换个版本,不要选 CPU/AB ...

  5. 分析优秀的.NET 文档设计工具Vsdocman 7.1 软件保护技术

    Vsdocman是一个优秀的.NET源代码注释编写工具,方便的以GUI的方式设计.NET源代码的注释. 我们知道.NET源代码的注释是Xml格式的注释,在生成程序集时,只需用选中生成Xml注释,Vis ...

  6. Shader LOD

    设置:单个设置Shader.maximumLOD.全局设置Shader.globalMaximumLOD.QualitySettings里面的Maximum LODLevel 原理:小于指定值的sha ...

  7. .NET面试题解析(03)-string与字符串操作

      系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 字符串可以说是C#开发中最常用的类型了,也是对系统性能影响很关键的类型,熟练掌握字符串的操作非常重要. 常 ...

  8. nodejs生成多层目录和生成文件的通用方法

    /** *生成多层目录 * @param dir 多层目录 * @param split 分隔符,ex:'/' 对应的目录地址:'2015/10/10' * @param mode 目录权限(读写权限 ...

  9. Prim算法(三)之 Java详解

    前面分别通过C和C++实现了普里姆,本文介绍普里姆的Java实现. 目录 1. 普里姆算法介绍 2. 普里姆算法图解 3. 普里姆算法的代码说明 4. 普里姆算法的源码 转载请注明出处:http:// ...

  10. c#编码规范

    1  规范目的 --------------------- 3 2  适用范围 --------------------- 3 3  代码注释 --------------------- 3 3.1  ...