SSH + Lucene + 分页 + 排序 + 高亮 模拟简单新闻网站搜索引擎
??? 前两天看到了一个中国新闻网,这个网站的搜索form的action是
?
??????? 浏览和搜索的前提是有据可查,没有数据什么都实现不了 , 我使用了Htmlparser通过抓取页面信息的形式将新闻添加进数据库 , 添加数据库数据使用了hibernate3
??????? 使用了Annotation的方式完成数据库的映射。
???????? //NewsType(新闻类型)
?
?通过上面的代码完成了所有的数据添加工作。
?
下面根据ssh的流程分别定制dao , manager , action
?
com.zly.test.dao包中是所有操作dao的抽象类和接口
?
我们直接看这些接口的实现
?
//NewsItemDaoHibernate? 新闻实体类dao
?
?
?
?
???只用到了一个?? NewsManagerImpl?
?
?
?
其中定一个两个map , 因为主页的查看分类新闻的url是采用的这种形式<a href="newsAction.action?category=china" target="_blank">国内</a>?? 名字为map的Map中保存信息如下
?
?
? key是?category后面的值 , value是两部分 , 被###分割开 , 前面的数值是所属新闻类别的id值, 后面的文字是其类别的文字。将其保存在map中,避免不停地查询数据库。
?
? 分页类PageControl的代码如下:
?
?
?? 下面是struts.xml中关于页面展示新闻的配置
?
??
??
?? NewsAction代码如下:
??
?
?
首页页面index.jsp,里面有几个分类超链接和搜索对话框
?
?
?
?
? 其表现形式如下:
新闻分页展示页面result.jsp代码如下:
? 显示效果如下:
?
?
?
其中点击具体超链接的效果图如下:
?
?
?
任务1 到此完成,新闻显示工作结束。下面是搜索引擎部分。
?
搜索的工具类放置在com.zly.indexManager包下面
?
说明,本程序使用了庖丁解牛中文分词,用户使用时需要中文字典,我的字典放在了c:\dic下面,使用庖丁还需要配置环境变量PAODING_DIC_HOME , 其值为c:\dic , (就是你的字典文件所在的目录)
?
代码如下:
?
创建索引类IndexCreateUtil
?
?
对索引进行搜索的代码如下:
?
??
?
?
分页action代码如下:
?
//searchResult.jsp代码如下:
?
??? 其运行结果如图所示(按标题搜索):
?
?
?
?
?
按内容搜索的运行结果如下:
?
?
?
至此,本小项目的所有功能完成,虽然没有多少难度,也不是什么高科技, 俺还是在google和javaeye上查了不少资料,总算是做完了,贴出来,与大家分享,也给新手学习提供资料。
?
所有的资源我都添加到了附件中,学过ssh的同学应该能够成功部署项目并运行。
?
其中NewsWithSearch.rar是工程文件夹,包含了所有的代码文件和jar包,加压完直接引到MyEclipse里就行,data.rar是所有的sql语句,插入到MySQL之前应先建立数据库mynews? ,???? dic.rar是庖丁解牛用到的字典文件,
解压成一个文件夹,并配置环境变量PAODING_DIC_HOME,其值就是你把它解压成的文件夹(例如c:\dic),最后如果你不想创建索引的话,可以把news.rar解压成一个文件夹,拷贝到c:\index\news下面。
?
<p>?</p>
<p>??????? 浏览和搜索的前提是有据可查,没有数据什么都实现不了 , 我使用了Htmlparser通过抓取页面信息的形式将新闻添加进数据库 , 添加数据库数据使用了hibernate3</p>
<p>??????? 使用了Annotation的方式完成数据库的映射。</p>
<p>???????? //NewsType(新闻类型)</p>
<pre name="code" ,"/news.shtml" ,1);
//国际新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/gj/2009/05" ,"/news.shtml" ,2);
//社会新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/sh/2009/05" ,"/news.shtml" ,3);
//港澳新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/ga/2009/05" ,"/news.shtml" ,4);
//台湾新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/tw/2009/05" ,"/news.shtml" ,5);
//华人新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/hr/2009/05" ,"/news.shtml" ,6);
//经济新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/cj/2009/05" ,"/news.shtml" ,7);
//文化新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/wh/2009/05" ,"/news.shtml" ,8);
//娱乐新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/yl/2009/05" ,"/news.shtml" ,9);
//体育新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/ty/2009/05" ,"/news.shtml" ,10);
//教育新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/edu/2009/05" ,"/news.shtml" ,11);
//健康新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/jk/2009/05" ,"/news.shtml" ,12);
//生活新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/life/2009/05" ,"/news.shtml" ,13);
//IT新闻
//insertNewsItems("http://www.chinanews.com.cn/scroll-news/it/2009/05" ,"/news.shtml" ,14);
}
public static void insertAllTypes() {
Configuration cfg = new AnnotationConfiguration().configure();
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
String str = new String("国内 国际 社会 港澳 台湾 华人 经济 文化 娱乐 体育 教育 健康 生活 IT");
String[] typesStr = str.split(" ");
NewsType[] types = new NewsType[typesStr.length];
session.beginTransaction();
for (int i = 0; i < typesStr.length; i++) {
types[i] = new NewsType();
types[i].setNewsTypeName(typesStr[i]);
session.save(types[i]);
}
session.getTransaction().commit();
session.close();
}
//处理5.1 - 5.5 所有的具体类型的新闻
public static void insertNewsItems(String before , String after , int type) throws Exception {
Configuration cfg = new AnnotationConfiguration().configure();
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
//得到当前新闻所属类别
NewsType newsType = (NewsType) session.get(NewsType.class, type);
final Set<NewsItem> set = new LinkedHashSet<NewsItem>();
//获取5月1日-5月5日的新闻
for (int i = 1; i <= 5; i++) {
String src = before;
if(i < 10) {
src = src + "0" + i;
}else {
src = src + i ;
}
src = src + after;
//使用htmlParser获取每一条新闻的超链接
Parser parser = new Parser(src);
parser.setEncoding("gb2312");
NodeList divList = parser.parse(new NodeClassFilter(Div.class));
for (int j = 0; j < divList.size(); j++) {
Div div = (Div) divList.elementAt(j);
String divClass = div.getAttribute("id");
//取得id为news_list的div
if(divClass != null && divClass.equals("news_list")) {
div.accept(new NodeVisitor() {
//遍历news_list里面的所有超链接
public void visitTag(Tag tag) {
if(tag instanceof LinkTag) {
String href = ((LinkTag)tag).getLink();
if(!href.startsWith("http")) {
href = "http://www.chinanews.com.cn" + href;
}
System.out.println(href);
//找到超链接,将这个超链接所在的页面进行处理,抓取新闻数据,并将其保存在Set中。
try{
insertOneNews(href , set);
}catch(Exception e) {
e.printStackTrace();
}
}
}
});
}
}
}
//获取新闻完成后,将所有NewsItem添加到数据库
session.beginTransaction();
for (NewsItem newsItem : set) {
newsItem.setType(newsType);
session.save(newsItem);
}
session.getTransaction().commit();
session.close();
}
//处理每一个具体的新闻超链接,得到NewsItem类
public static void insertOneNews(String src , Set<NewsItem> set) throws Exception {
Parser parser = new Parser(src);
parser.setEncoding("gb2312");
NodeList divList = parser.extractAllNodesThatMatch(new NodeClassFilter(Div.class));
NewsItem newsItem = new NewsItem();
String title = "";
Date parse = null;
String content = "";
String editor = "";
//遍历网页的div。将制定div里面的信息设置到NewsItem实体类中
for (int i = 0; i < divList.size(); i++) {
Div div = (Div) divList.elementAt(i);
String divString = div.getAttribute("class");
if(divString != null) {
//设置标题
if(divString.equals("left_bt")) {
title = div.toPlainTextString();
}
//设置发布时间
if(divString.equals("left_time")) {
String publishStr = "";
Pattern pattern = Pattern.compile("[\\d]{4}年[\\d]{2}月[\\d]{2}日 [\\d]{2}:[\\d]{2}");
Matcher matcher = pattern.matcher(div.toPlainTextString()) ;
if(matcher.find()) {
publishStr = matcher.group();
}
DateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm");
parse = format.parse(publishStr);
}
//设置正文内容
if(divString.equals("left_zw")) {
content = div.toHtml();
}
//设置记者名称
if(divString.equals("left_name")) {
editor = div.toPlainTextString().trim();
editor = editor.substring(editor.indexOf(":") + 1, editor.lastIndexOf("】"));
}
}
}
newsItem.setEditor(editor);
newsItem.setNewsContent(content);
newsItem.setNewsTitle(title);
newsItem.setPublishTime(parse);
//设置新闻来源
newsItem.setResource("最快新闻网");
//将新闻添加到set中
set.add(newsItem);
}
}
</pre>
<p>?</p>
<p>?通过上面的代码完成了所有的数据添加工作。</p>
<p>?</p>
<p>下面根据ssh的流程分别定制dao , manager , action</p>
<p>?</p>
<p>com.zly.test.dao包中是所有操作dao的抽象类和接口</p>
<p>?</p>
<p>我们直接看这些接口的实现</p>
<p>?</p>
<p>//NewsItemDaoHibernate? 新闻实体类dao</p>
<p>?</p>
<p>?</p>
<p>?</p>
<pre name="code" from NewsItem where type.id = '" + type + "'").setFirstResult(firstResult).setMaxResults(maxResult).list();
}
});
}
//得到数据总条数 , 以便计算总页数
public Long getItemCount(final int id) {
return (Long) this.getHibernateTemplate().execute(new HibernateCallback(){
public Object doInHibernate(Session session)
throws HibernateException, SQLException {
return session.createQuery("select count(*) from NewsItem where type.id = '" + id + "'").uniqueResult();
}
});
}
//显示新闻数据页面用到, 查询具体新闻属于哪个新闻类别
public int getNewsType(final int id) {
return (Integer) this.getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException, SQLException {
NewsItem newsItem = get(id);
NewsType type = newsItem.getType();
return type.getId();
}
});
}</pre>
<p>?</p>
<p>???只用到了一个?? NewsManagerImpl?</p>
<p>?</p>
<p>?</p>
<pre name="code" id="newsAction" scope="request">
<property name="newsManager" ref="newsManager"></property>
<property name="map" ref="map"></property>
<property name="map1" ref="map1"></property>
</bean></pre>
<p>?</p>
<p>其中定一个两个map , 因为主页的查看分类新闻的url是采用的这种形式<a href="newsAction.action?category=china" target="_blank">国内</a>?? 名字为map的Map中保存信息如下</p>
<p>?</p>
<pre name="code" value="1###国内新闻" />
<entry key="world" value="2###国际新闻" />
<entry key="society" value="3###社会新闻" />
<entry key="compatriot" value="4###港台新闻" />
<entry key="taiwan" value="5###台湾新闻" />
<entry key="huaren" value="6###华人新闻" />
<entry key="economic" value="7###经济新闻" />
<entry key="wenhua" value="8###文化新闻" />
<entry key="entertainment" value="9###娱乐新闻" />
<entry key="sports" value="10###体育新闻" />
<entry key="jiaoyu" value="11###教育新闻" />
<entry key="jiankang" value="12###健康新闻" />
<entry key="life" value="13###生活新闻" />
<entry key="keji" value="14###科技新闻" />
</map>
</constructor-arg>
</bean></pre>
<p>?</p>
<p>? key是?category后面的值 , value是两部分 , 被###分割开 , 前面的数值是所属新闻类别的id值, 后面的文字是其类别的文字。将其保存在map中,避免不停地查询数据库。</p>
<p>?</p>
<p>? 分页类PageControl的代码如下:</p>
<p>?</p>
<pre name="code" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<script language="javascript">
function jumping() {
document.pageForm.submit();
return true;
}
function gotoPage(pageNum) {
document.pageForm.jumpPage.value = pageNum ;
document.pageForm.submit();
return true;
}
</script>
</head>
<body>
<c:if test="${pageControl.maxPage != 1}">
<form method="post" action="pageAction.action" name="pageForm">
<table>
<tr>
<td>
每页${pageControl.rowsPerPage}行
</td>
<td>
共${pageControl.maxRowCount }行
</td>
<td>
第${pageControl.curPage }页
</td>
<td>
共${pageControl.maxPage }页
</td>
<td>
<c:choose>
<c:when test="${pageControl.curPage == 1}">
首页 上一页
</c:when>
<c:otherwise>
<A HREF="javascript:gotoPage(1)">首页</A>
<A HREF="javascript:gotoPage(${pageControl.curPage - 1} )">上一页</A>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${pageControl.curPage == pageControl.maxPage}">
下一页 尾页
</c:when>
<c:otherwise>
<A HREF="javascript:gotoPage(${pageControl.curPage + 1})">下一页</A>
<A HREF="javascript:gotoPage(${pageControl.maxPage })">尾页</A>
</c:otherwise>
</c:choose>
转到第
<SELECT name="jumpPage" onchange="jumping()">
<c:forEach var="i" begin="1" end="${pageControl.maxPage}" step="1">
<c:choose>
<c:when test="${i == pageControl.curPage}">
<OPTION selected value="${i}">${i }</OPTION>
</c:when>
<c:otherwise>
<OPTION value="${i}">${i}</OPTION>
</c:otherwise>
</c:choose>
</c:forEach>
</SELECT>
页
</td>
</tr>
</table>
</form>
</c:if>
</body>
</html>
</pre>
<p>?</p>
<p>?? 下面是struts.xml中关于页面展示新闻的配置</p>
<p>?</p>
<p>?</p>
<pre name="code" >
<result type="redirect">pageAction.action</result>
</action>
<action name="pageAction" method="pageAction">
<result>/result.jsp</result>
</action>
<action name="detailAction" method="showDetail">
<result>/detail.jsp</result>
</action></pre>
<p>?</p>
<p>?? NewsAction代码如下:</p>
<p>?</p>
<pre name="code" value="1###国内新闻" />这样的信息
private Map<String , String> map;
//保存<entry key="1" value="china###国内新闻" />这样的信息
private Map<String , String> map1;
public Map<String, String> getMap1() {
return map1;
}
public void setMap1(Map<String, String> map1) {
this.map1 = map1;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public String getJumpPage() {
return jumpPage;
}
public void setJumpPage(String jumpPage) {
this.jumpPage = jumpPage;
}
public NewsManager getNewsManager() {
return newsManager;
}
public void setNewsManager(NewsManager newsManager) {
this.newsManager = newsManager;
}
//处理newsAction.action?category=xxx
public String execute() {
//得到相应的 1###国内新闻
String typeIdInfo = map.get(category);
//这里得到的是新闻类别id
Integer typeId = Integer.parseInt(typeIdInfo.split("###")[0]);
//这里得到的是新闻类别字符串
String typeString = typeIdInfo.split("###")[1];
//将上面三个变量保存在session中,在jsp页面中显示
this.getSession().setAttribute("category", category);
this.getSession().setAttribute("typeString" , typeString);
this.getSession().setAttribute("id", typeId);
//跳转到pageAction中,处理分页
return SUCCESS;
}
public String pageAction() {
//取出新闻类别id
Integer id = (Integer) this.getSession().getAttribute("id");
//查看是不是第一次来分页,如果是,当前页设置成第一个
if(jumpPage == null) {
jumpPage = "1";
}
//从request范围内取出PageControl , 如为空,则第一次分页,创建pageControl对象
PageControl pageControl = (PageControl) this.getRequest().getAttribute("pageControl");
if(pageControl == null) {
pageControl = new PageControl();
//第一次的时候得到相应新闻类别所对应的记录的总数
pageControl.setMaxRowCount(newsManager.getItemCounts(id));
//计算一共多少页
pageControl.countMaxPage();
}
//设置jumpPage的值为当前页
pageControl.setCurPage(Integer.parseInt(jumpPage));
//计算出hibernate中firstResult的值
int firstResult = (pageControl.getCurPage() - 1) * pageControl.getRowsPerPage() + 1;
//设置hibernate中maxResult的值
int maxResult = pageControl.getRowsPerPage();
//利用hibernate得到当前页的数据,并保存在pageControl分页实体类中
List<NewsItem> userList = newsManager.getNewsItemByFirstResultAndMaxResult(firstResult, maxResult , id);
pageControl.setData(userList);
//将分页实体类保存在request范围内。
this.getRequest().setAttribute("pageControl", pageControl);
//将页面视图跳转到result.jsp
return SUCCESS;
}
public String showDetail() {
//得到url中的?id=xxx
String newsId = this.getRequest().getParameter("id");
int id = Integer.parseInt(newsId);
//使用hibernate得到具体id的新闻记录
NewsItem item = newsManager.getNewsById(id);
//得到id记录所对应的新闻类型的值 map1这种形式<entry key="1" value="china###国内新闻" />
int typeId = newsManager.getNewsType(id);
//得到china,添加到jsp页面的<a href="newsAction?category=">里面
String category = map1.get("" + typeId).split("###")[0];
//得到国内新闻,显示在jsp页面的多个位置
String typeString = map1.get("" + typeId).split("###")[1];
//将以上多个数据添加到request范围内,以便显示。
this.getRequest().setAttribute("news", item);
this.getRequest().setAttribute("category", category);
this.getRequest().setAttribute("typeString", typeString);
return SUCCESS;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
</pre>
<p>?</p>
<p>?</p>
<p>首页页面index.jsp,里面有几个分类超链接和搜索对话框</p>
<p>?</p>
<pre name="code" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="mainlink">
<span><a href="index.jsp">最快新闻网</a></span><br /><br />
<a href="newsAction.action?category=china" target="_blank">国内</a> | <a href="newsAction.action?category=world" target="_blank">国际</a> | <a href="newsAction.action?category=society" target="_blank">社会</a> | <a href="newsAction.action?category=compatriot" target="_blank">港澳</a> | <a href="newsAction.action?category=taiwan" target="_blank">台湾</a> | <a href="newsAction.action?category=huaren" target="_blank">华人</a> | <a href="newsAction.action?category=economic" target="_blank">经济</a> | <a href="newsAction.action?category=wenhua" target="_blank">文化</a> | <a href="newsAction.action?category=entertainment" target="_blank">娱乐</a> | <a href="newsAction.action?category=sports" target="_blank">体育</a> | <a href="newsAction.action?category=jiaoyu" target="_blank">教育</a> | <a href="newsAction.action?category=jiankang" target="_blank">健康</a> | <a href="newsAction.action?category=life" target="_blank">生活</a> | <a href="newsAction.action?category=keji" target="_blank">IT</a><br />
<form action="searchAction.action" method="post" target="_blank">
本站搜索:
<input type="text" name="searchParam" style="height:20px"/>
<select name="searchWhich">
<option value="title"/>标题
<option value="content"/>内容
</select>
<input type="submit" value="搜索" style="height:23px"/>
</form>
</div>
</body>
</html>
</pre>
<p>?</p>
<p>?</p>
<p>?</p>
<p>? 其表现形式如下:</p>
<p><img src="http://p13.freep.cn/p.aspx?u=v20_p13_p_0906242041205118_0.jpg" alt="SSH + Lucene + 分页 + 排序 + 高亮 模拟简略新闻网站搜索引擎"></p>
<p>新闻分页展示页面result.jsp代码如下:</p>
<pre name="code" contentType="text/html;charset=utf-8"
pageEncoding="utf-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>${typeString} -- 最快新闻网</title>
</head>
<body>
<jsp:include page="index.jsp"></jsp:include>
<div id="content">
<font color="red" size="5">${typeString}</font>
<br /><br /><br /><br />
<div id="newsListTitle" style="float:left;">
<c:forEach items="${pageControl.data}" var="news">
<div style="margin-top: 20px;">
<span>
<a href="detailAction.action?id=${news.id }">${news.newsTitle }</a>
</span>
<br />
</div>
</c:forEach>
</div>
<div id="newsListTime">
<c:forEach items="${pageControl.data}" var="news">
<div style="margin-top: 20px;">
<span>
<fmt:formatDate value="${news.publishTime}" pattern="MM-dd HH:mm"/>
</span>
<br />
</div>
</c:forEach>
</div>
<br /><br /><br /><br />
<%@ include file="page.jsp" %>
</div>
</body>
</html></pre>
<p>? 显示效果如下:</p>
<p>?</p>
<p>?<img src="http://p13.freep.cn/p.aspx?u=v20_p13_p_0906242059023617_0.gif" alt="SSH + Lucene + 分页 + 排序 + 高亮 模拟简略新闻网站搜索引擎"></p>
<p>?</p>
<p>其中点击具体超链接的效果图如下:</p>
<p>?</p>
<p>?</p>
<p><img src="http://p13.freep.cn/p.aspx?u=v20_p13_p_0906242102342358_0.jpg" alt="SSH + Lucene + 分页 + 排序 + 高亮 模拟简略新闻网站搜索引擎"></p>
<p>?</p>
<p>任务1 到此完成,新闻显示工作结束。下面是搜索引擎部分。</p>
<p>?</p>
<p>搜索的工具类放置在com.zly.indexManager包下面</p>
<p>?</p>
<p>说明,本程序使用了庖丁解牛中文分词,用户使用时需要中文字典,我的字典放在了c:\dic下面,使用庖丁还需要配置环境变量PAODING_DIC_HOME , 其值为c:\dic , (就是你的字典文件所在的目录)</p>
<p>?</p>
<p>代码如下:</p>
<p>?</p>
<p>创建索引类IndexCreateUtil</p>
<p>?</p>
<pre name="code" from NewsItem").list();
DateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
//对所有的新闻实体进行索引创建
for (NewsItem newsItem : list) {
//建立一个lucene文档
Document doc = new Document();
//得到新闻标题
String newsTitle = newsItem.getNewsTitle();
//得到新闻内容
String newsContent = newsItem.getNewsContent();
//得到新闻事件
String publishDate = format.format(newsItem.getPublishTime());
//得到新闻主键id
String id = newsItem.getId() + "";
//将新闻标题加入文档,因为要搜索和高亮,所以index是tokennized,TermVector是WITH_POSITIONS_OFFSETS
doc.add(new Field("title" , newsTitle , Field.Store.YES , Field.Index.TOKENIZED , Field.TermVector.WITH_POSITIONS_OFFSETS));
//利用htmlparser得到新闻内容html的纯文本
Parser parser = new Parser();
parser.setInputHTML(newsContent);
String strings = parser.parse(null).elementAt(0).toPlainTextString().trim();
//添加新闻内容至文档,与标题相似
doc.add(new Field("content" , strings , Field.Store.COMPRESS , Field.Index.TOKENIZED , Field.TermVector.WITH_POSITIONS_OFFSETS));
//添加时间至文档,因为要按照此字段降序排列排序,所以tokenzied,不用高亮所以TermVector是no就行了
doc.add(new Field("date" , publishDate , Field.Store.YES , Field.Index.TOKENIZED , Field.TermVector.NO));
//添加主键至文档,不分词,不高亮。
doc.add(new Field("id" , id , Field.Store.YES , Field.Index.NO , Field.TermVector.NO));
indexWriter.addDocument(doc);
}
//创建索引
indexWriter.optimize();
indexWriter.close();
//关闭session
session.close();
}
public static void main(String[] args) throws Exception {
IndexCreateUtil util = new IndexCreateUtil();
util.createIndexForNews();
}
}
</pre>
<p>?</p>
<p>对索引进行搜索的代码如下:</p>
<p>?</p>
<p>?</p>
<pre name="code" , true));
//创建list,将结果保存其中,以便在jsp页面中进行显示
List<SearchResultBean> list = new ArrayList<SearchResultBean>();
//模拟hibernate的serFirstResult和setMaxResult以便返回指定条目的结果
for (int i = firstResult - 1; i < firstResult + maxResult - 1; i++) {
Document doc = hits.doc(i);
//取得该条索引文档
SearchResultBean srb = new SearchResultBean();
//从中取出标题
String title = doc.get("title");
//从中取出内容
String content = doc.get("content");
//从中取出主键id
String id = doc.get("id");
//从中取出发布时间
String date = doc.get("date");
//高亮htmlFormatter对象
SimpleHTMLFormatter sHtmlF = new SimpleHTMLFormatter("<b><font color='red'>", "</font></b>");
//高亮对象
Highlighter highlighter = new Highlighter(sHtmlF,new QueryScorer(query));
//设置高亮附近的字数
highlighter.setTextFragmenter(new SimpleFragmenter(100));
//如果查询的是标题,进行处理
if(searchWhich.equals("title")) {
String bestFragment = highlighter.getBestFragment(analyzer,searchWhich,title);
//获得高亮后的标题内容
srb.setTitle(bestFragment);
//如果内容不足150个字,全部设置
if(content.length() < 150) {
srb.setContent(content);
}else {
//如果内容多于150个字,只取出前面150个字
srb.setContent(content.substring(0 , 150));
}
} else {
//如果查询的是内容字段
String bestFragment = highlighter.getBestFragment(analyzer,searchWhich,content);
//取得高亮内容并设置
srb.setContent(bestFragment);
//设置标题,全部设置
srb.setTitle(title);
}
//设置日期
srb.setDate(date);
//设置主键
srb.setId(id);
//添加到list中,以便在jsp页面上显示
list.add(srb);
}
return list;
}
//取得符合搜索条件的所有记录总数,以便分页 , 与上面方法类似
public int getResultCount(String searchWhich , String searchParam) throws Exception {
File indexFile = new File("c:/index/news");
IndexReader reader = IndexReader.open(indexFile);
Analyzer analyzer = new PaodingAnalyzer();
QueryParser parser = new QueryParser(searchWhich , analyzer);
IndexSearcher searcher = new IndexSearcher(reader);
Query query = parser.parse(searchParam);
Hits hits = searcher.search(query);
return hits.length();
}
}
</pre>
<p>?</p>
<p>?</p>
<p>分页action代码如下:</p>
<pre name="code" method="search">
<result>/searchResult.jsp</result>
</action></pre>
<p>?</p>
<p>//searchResult.jsp代码如下:</p>
<p>?</p>
<pre name="code" contentType="text/html;charset=utf-8"
pageEncoding="utf-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>${searchParam} 的搜查结果 -- 最快新闻网</title>
</head>
<body>
<jsp:include page="index.jsp"></jsp:include>
<div id="content">
<div id="searchResults" >
<c:forEach items="${pageControl.data}" var="result">
<div style="margin-top: 20px;">
<span>
<a href="detailAction.action?id=${result.id }">${result.title}</a><br />
${result.content }
<font color="green">http://localhost:8080/NewsWithSearch/detailAction.action?id=${result.id } ${result.date }</font>
</span>
<br />
</div>
</c:forEach>
</div>
<br /><br /><br /><br />
<%@ include file="searchPage.jsp" %>
</div>
</body>
</html></pre>
<p>??? 其运行结果如图所示(按标题搜索):</p>
<p>?</p>
<p>?</p>
<p>?</p>
<p><img src="http://p13.freep.cn/p.aspx?u=v20_p13_p_0906242258409633_0.jpg" alt="SSH + Lucene + 分页 + 排序 + 高亮 模拟简略新闻网站搜索引擎"></p>
<p>?</p>
<p>?</p>
<p>按内容搜索的运行结果如下:</p>
<p><img src="http://p13.freep.cn/p.aspx?u=v20_p13_p_0906242300302582_0.jpg" alt="SSH + Lucene + 分页 + 排序 + 高亮 模拟简略新闻网站搜索引擎"></p>
<p>?</p>
<p>?</p>
<p>?</p>
<p>至此,本小项目的所有功能完成,虽然没有多少难度,也不是什么高科技, 俺还是在google和javaeye上查了不少资料,总算是做完了,贴出来,与大家分享,也给新手学习提供资料。</p>
<p>?</p>
<p>所有的资源我都添加到了附件中,学过ssh的同学应该能够成功部署项目并运行。</p>
<p>?</p>
<p>其中NewsWithSearch.rar是工程文件夹,包含了所有的代码文件和jar包,加压完直接引到MyEclipse里就行,data.rar是所有的sql语句,插入到MySQL之前应先建立数据库mynews? ,???? dic.rar是庖丁解牛用到的字典文件,</p>
<p>解压成一个文件夹,并配置环境变量PAODING_DIC_HOME,其值就是你把它解压成的文件夹(例如c:\dic),最后如果你不想创建索引的话,可以把news.rar解压成一个文件夹,拷贝到c:\index\news下面。</p>
<p>?</p>
</div>
<p>?</p>at net.paoding.analysis.knife.PaodingMaker.setDicHomeProperties(PaodingMaker.java:308)
at net.paoding.analysis.knife.PaodingMaker.getDicHome(PaodingMaker.java:255)
at net.paoding.analysis.knife.PaodingMaker.loadProperties(PaodingMaker.java:187)
at net.paoding.analysis.knife.PaodingMaker.loadProperties(PaodingMaker.java:223)
at net.paoding.analysis.knife.PaodingMaker.loadProperties(PaodingMaker.java:223)
at net.paoding.analysis.knife.PaodingMaker.getProperties(PaodingMaker.java:129)
at net.paoding.analysis.analyzer.PaodingAnalyzer.init(PaodingAnalyzer.java:70)
at net.paoding.analysis.analyzer.PaodingAnalyzer.<init>(PaodingAnalyzer.java:59)
at net.paoding.analysis.analyzer.PaodingAnalyzer.<init>(PaodingAnalyzer.java:52)
at com.zly.indexManager.IndexCreateUtil.createIndexForNews(IndexCreateUtil.java:29)
at com.zly.indexManager.IndexCreateUtil.main(IndexCreateUtil.java:73)
这句应该是查询所有的吧.如果数据量大的话呢?然后载入内存?再用FOR循环出来? 25 楼 pricecome 2009-09-14 现在可以考虑用solr了,不用这么费劲lucene了
http://www.pricecome.com 26 楼 hudejunhit 2009-09-15 好厉害,相比之下,我真是太初级了 27 楼 foxlish 2009-11-01 我感觉写的挺好,挺适合我们新手研究和学习的!
我仔细看了一遍了,觉得代码和注释写的都不错! 28 楼 wangxyong2001 2010-01-28 不管怎么样 还是要感谢 lz的分享 29 楼 wulinshishen 2010-05-24 真该向楼主学习了啊 30 楼 rentianchou 2010-06-05 xuyao 写道晕,刚看第二行就看不下去了,谁告诉您有.do就是struts1?
嗯