java实现站内搜索转自http://blog.csdn.net/liunian02050328/article/details/8220379

来源:互联网 发布:js鼠标移入事件 编辑:程序博客网 时间:2024/04/30 11:24
1.站内搜索
在以往的网站建设,企业系统的搭建过程中,因为信息比较简单,比较少,站内搜索可能不是必要的选项,而今,时代的发展, 信息量的增大,网站逻辑的复杂,企业自身对信息架构、管理、发布的需求,以及用户对信息的组织、查询、可寻性的要求越来越高,于是站内搜索出现了。
2.和别的搜索引擎的区别
谷歌、百度、搜搜等通用搜索引擎都会免费开放站内搜索功能,以嵌入网页代码的形式保持与搜索引擎机器人的沟通、爬取,其弊病有如下三种:
  1、这些通用搜索引擎不能及时、全部抓取网站最新页面内容。这对电子商务网站信息(如价格、活动有效时间等)经常更新的特点来说是致命的,用户查到的可能是过时信息.
  2、既然是所谓通用引擎,其对搜索结果的展示也是通用的,没有差异性的。其不能按照商城自身业务逻辑去做排序、过滤、展示是其最大的弱项
  3.通用搜索,无法提供热词,搜索推荐词,关联词等功能,还需要二次开发,增加工作量复杂度.
  因此,站内搜索的出现也是有其具体原因和需求的,在搜索的精确度和效果上击败了通用搜索引擎。 
3.站内搜索的优点
              能够很快的得到用户所要检索的信息,这样给用户提供了很大的方便
4.站内搜索的技术流程
  • 第一步 提取原料:抓取网站页面或格式化数据。
  • 第二步 把原料归类:建立索引,把关键字和页面一一对应上,分类放好(想象一下老式图书馆里的归档管理方法就能形象理解索引了)
  • 第三步 听用户要上啥菜:响应用户的搜索需要,对用户输入的关键词进行分解,从索引中找到符合该关键词的所有相关网页。
  • 第四步  摆盘上桌:对搜索结果页面进行排序,将页面标题、url、摘要等信息呈现给用户。
一步步细说:
第一步:提取原料:抓取网站页面或格式化数据。
抓取页面是使用一种叫蜘蛛的程序,一个网站中成千上万个页面是通过什么关联起来的?url。蜘蛛就是通过一个页面的url找到另一个页面再找到另一个页面,把所有能链到的页面遍历一边,记录下来。
通用搜索引擎(google baidu)的蜘蛛 很复杂,因为一个页面上可能有很多个url,每个url又关联着无数页面,整个网络像一棵树,假设首页是第一层,首页上的url关联的页面是第二层,第二层页面上url关联的页面是第三层……
蜘蛛抓取的顺序就会很重要,是沿着一个url一直爬下去,还是先爬完一层再爬一层;加密或需要用户登录后才能访问的内容怎么抓取;pdf、rar及多媒体文件怎么抓取,都有讲究。不同的抓取处理在有效性、效率和对 被爬网站的资源占用 上有很大差异。
当然站内搜索没有那么麻烦,一般是技术人员根据希望被纳入搜索的内容数据库 生成一份格式化的 xml文档,让蜘蛛直接抓取就行了。但是站内搜索在提抓取页面更新索引时,有一个指标比较高,那就是抓取的更新频率。
比如对于电商类网站,某些商品,特价、抢货啦,可能刚发布出来5分钟就卖完了,或者改价格了。但是用户听说促销啦,来网站上一搜,搜不到,或者搜到了点进去发现价格不对,用户就会不舒服。 这就是蜘蛛抓取页面的更新频率过低导致的。要解决这个问题并平衡性能与资源占用之间的矛盾,需要多种算法组合优化。
通用搜索 比如google、twitter在做这方面的努力,也就是做成实时搜索。但是站内搜索服务还鲜有尝试者,霍炬余晟最近在做针对电商类的优化。目前可以做到即时更新,也就是发布后1分钟内就可以被搜索到。
第二步:把原料归类:建立索引。
建立索引这一步 集中了搜索引擎 的两大核心技术难点:索引结构和中文分词。
如果按照正常人的思维,索引应该是这样建立的:
  • 把每篇文章存在文章索引表里(假定我们叫它doc索引),然后解析出该文章中有多少关键字,把关键字存在一个表里(我们叫它keyword索引)
  • doc索引的大致结构就是:docID  | doc标题/内容 | doc的url及其它信息 | doc中每个keywordID 。
  • 当用户输入关键字搜索的时,先找到关键字对应的keywordID, 然后查找到有哪些doc里包含做这个keywordID。
这种思路很符合逻辑,但是不好意思,在效率上几乎是不可行的。
因为在查找哪些doc里包含这个keyword的过程相当于 哗哗哗狂翻一本书来找里面的一个词。网站的doc索引条目动辄上万上十万,要是同时查找多个关键字,相当于多次狂翻一本十几万页的书,你说是不是累死了。
于是 就有一种更符合 程序运行方式的 索引建立方法。这就是倒排索引,也叫反向索引。 而上文中提到的符合正常人思维的叫正排索引。
倒排索引中“倒”的含义是指把doc索引和keyword索引的关联次序颠倒过来。在建立索引的时候,先建一张keyword表,结构是:“keywordID  | 关键字 | 存在哪篇doc中的哪个地方 ”
“存在哪篇doc中的哪个地方” 这个信息怎么表示?
通过一种叫映射的方法。通俗地举例:“北京”这个词出现在 id为0011文章的第2段第5行第3个字,可以表达为一个字符串 0010p2l5f3,所以“北京”这个词在keyword表里是可能这样写
“ k001 | 北京 | 0010p2l5f3 , 0010p5l1f9, 0012p1l2f6……”
这是一个平面的结构,实际程序中当然不会这么简单处理,这样效率还是太低。会处理成一个有层次的结构,比如第一层只存docID  “k001 | 北京 | 0010,0010,0012……” ,第二层再存是属于哪一段哪一行等。
这样做的好处是 可以在第一层实现一次归并。因为搜索结果页面最先需要列出的只是那篇文章里包含哪个关键字,不需要具体位置,所以,当北京这个词在0010文章中多次时,第一层索引可以归并为为 “北京| 0010,0012 “ 这样结构又精简了。
索引的结构及存储方法对 搜索速度起致命的影响。
分词: 中文分词技术是中日韩语专属的一个的高难度课题,研究了十几年了。而英文每个单词之间都有空格,没这个麻烦。
比如 “作家长平时常翻阅这本书” 这句话 人可以识别“ 作家 长平 时常 翻阅 这本 书”。但是计算机可能就分成了“ 作 家长 平时 常 翻阅 这本 书”。计算机不认得“长平’ 这个人名,导致分词错误,用户搜索长平的时候就得不到这条结果。
再例如,当用户输入 “和服”搜索时,出来第一屏都是 “产品和服务”,“化妆和服装自己搞定”,用户是不是很郁闷。
所以分词技术 对于 搜索的准确有效性 起关键作用。
基础的分词方法是机械切分,也叫二元切分。以2个字为一个单位进行切分,不进行判断,比如 中华人民共和国 -> 中华 华人 人民 民共 共和 和国
在此基础上进化出了双向最大切分,就是把一句话切成最小词单元,正向切分一次,再反向切分一次,比较下哪个更合理,再通过复杂算法识别出有效关键字。
更先进的方法是在机械切分的基础上使用合理的词库,地名、品牌名、机构、简称等需要词库。而不同行业如金融类、计算机类、商品类都有不同的专业词库。
还有基于人工智能和统计概率的分词算法,但是对于站内搜索这个量级的都不适用。
对于站内搜索而且,除了好的分词算法,更重要的是词库添加和统计功能。网站管理员可以根据用户搜索行为的统计分析 手动向词库内添加新词。
第三步:听用户要上啥菜:响应用户的搜索需要
  • 用户输入的搜索条件可能是一句话,所以对用户搜索请求的解析也要用到分词技术。如果搜 “吉野家沙拉” 和 “吉野家 沙拉” 会得出不一样的搜索结果,就是比较差的搜索引擎了。
  • 用户输入的关键字是对词库的有利补充。比如搜全聚德的人多了,全聚德显然是一个有效的专业词汇。
  • 多个搜索关键字之间存在逻辑运算关系。逻辑运算。。不要怕,搞设计的人应该都知道布尔运算。。不知道?总知道反选、多选、选区交集吧。这就是逻辑运算中 非运算(not)、  或运算(or)、 与运算(and)。
用户的搜索条件是“美国 金融危机”如果采用 或运算,则文章中只要包含了 美国 或 金融危机 这两个词中的一个,都有可能被列为搜索结果。 如果采用 与运算,则只有同时包含了 美国 和金融危机这两个词的文章 采会被列进搜索结果。
所有搜索引擎都应该在输入搜索条件时,支持逻辑运算符。
对于通用搜索引擎,一般 多个关键词之间的空格 就默认代表了是 与运算(and) 的关系。可以通过输入逻辑运算符 来完成其它搜索需求。比如 可以使用 “哈希 or Hash” 来搜索更多关于哈希算法的信息中英文都有, 也可以使用 “小李飞刀—电视剧” (减号)来搜索除电视剧外的小李飞刀的信息。
对于站内搜索,1 没有通用搜索那么大的数据量 2 比搜索引擎专业性更强。所以站内搜索 多个关键字之间的空格 默认代表的是 或运算 的关系。但是会在呈现结果的排序上做文章,通过多种算法计算出相关性最高的文章排在前面,相关性弱的排在后面。这样可以帮助用户发掘到更多 关联性内容,结果呈现也更人性化。这是通常定义下的 站内全文检索 的一个重要特征。也是区别于数据库搜索的技术优势。
响应用户搜索条件的时候 还有字段匹配及权重的问题,一篇doc 可能有标题、摘要、正文、tag、作者等多字段信息存在doc索引库里。Keyword是出现在标题、摘要还是正文中时,权重是不一样的。
第四步 摆盘上桌:对搜索结果页面进行排序,
琢磨过SEO的同学一定知道,所谓搜索引擎优化 1是让蜘蛛能抓取自己网站上更多的页面2 让自己网站的页面在搜索结果里能排得更靠前。
这就要研究搜索引擎的排序算法。对于各个通用搜索引擎,排序算法是许多人的关注核心,每次权重调整都会带来巨大震荡。通用搜索引擎都是在基于相关性排序上在加上各自的算法,如Google的专利pagerank就是通过页面之间的互链来判断页面的价值高低,再加上链接引用页面的PR值、是否在一个分类等 各种其它指标。
但是站内搜索,用互链这种方式来判定显然不靠谱,所以主要还是通过优化相关性的算法,计算keyword和DOC之间的关系,例如 keyword 在doc中出现的密度,词频, doc是否和 keyword 属于同一语义类别,doc的长度属性(短的doc应该降权之类的)等判定 keyword的搜索结果中,哪些doc更重要更有价值。
多个关键字的搜索条件,让算法更复杂,如何对多个关键字进行比较、两者的结果如何合并,两者的结果顺序如何穿插重排。
最后还要利用算法来优化 结果排序的速度和稳定性。
由此 才能得到站内搜索的相关度排序结果。
5.站内搜索的实现
首先看下一个数据库的表关系图
在一个网站中可能会有很多的信息类型,若是每个类型板块设定一个数据库表可能会很麻烦。如上图所示,这样设计可以动态的增加板块,并且可以减少数据库的压力,
其中数据类型的主键是信息表的外间,这样就形成了一对多的关系,也就是说可能是一个信息类型中包含很多条信息。
若是我们想在网站中查找到自己所需要的信息,例如,查找一级标题,二级标题,或者内容中的关键字来找出文章,我们该如何实现呢?
我们首先想到的问题是模糊匹配的原则,这样会有两种方式我们可以解决,一种是通过数据库的方式来实现, 一种是通过java提供的match来显示。
下面是我的通过数据库的方式来实现的, 个人认为这样实现比较直观易懂
我们知道通过like语句,提供模糊查询的操作,如下
select *  from msg where title like '%gjz%' or titlesec like '%gjz%' or contents like '%gjz%'
这样我们可以根据后台传过来的关键字(gjz) 来进行查询,最后将结果返回给用户。
我是用java做的网站,用的是ssh框架,下面我大体介绍下我个人的做法:
在daoImpl中写出要查询的方法语句:
//根据关键字搜索
public List<Msg> listSSMsg(String gjz) {// TODO Auto-generated method stubreturn this.getHibernateTemplate().find("from Msg where title like '%"+gjz+"%' or titlesec like '%"+gjz+"%' or contents like '%"+gjz+"%'");}
然后再action中的代码如下:
//根据关键字搜索
private String gjz;private List<Msg> listBygjz;public String listMsgBygjz()throws Exception{try {listBygjz=this.msgService.listSSMsg(gjz);System.out.println("符合搜索要求的条数:"+listBygjz.size());} catch (Exception e) {// TODO: handle exceptione.printStackTrace();return INPUT;}return SUCCESS;}
我们是通过页面传过来gjz(关键字)这个变量来确定你查询的条件的
下面我们来看下页面
<form action="<%=basePath%>msg/ss.action" method="post"><td class="STYLE1"><div align="center"><input name="gjz" type="text" size="20" onfocus="this.value=''"></div></td><td class="STYLE1"><div align="center"><input style="cursor: pointer;" name="" type="button" id="tijiao"value="搜索" onclick="this.blur();submit();"></div></td></form>

其中ss.action是我们要执行搜索的action,用的方法是上面action中讲的listMsgBygjz
页面我们在鼠标离开text的时候获取文本框中的内容,然后点击按钮执行的是将从text中得到的数据传到后台, 然后通过后台的一系列操作然后将结果返回给用户