SSH + Lucene + 分頁 + 排序 + 高亮 模擬簡單新聞網站搜索引擎

  前兩天看到了一箇中國新聞網,這個網站的搜索form的action是

http://search.chinanews.com/search.do




便知道是struts1的產物,現在都用struts2了,所以給自己的任務是實現Struts2 SSH分頁瀏覽新聞、Lucene分頁高亮排序搜索新聞這個兩個功能。

 

    IDE使用的MyEclipse6.5,數據庫使用MySQL 5.0.37 , 另裝了Navicat for MySQL , jdk版本是6.0

    工程做完的效果圖如下,com.zly.indexManager中兩個類,分別創建索引和搜索索引,

    com.zly.test.entity中是使用的實體類,分別是NewsType(新聞類型),NewsItem(新聞具體條目),PageControl(分頁實體bean) , SearchResultBean(保存搜索結果的bean).

 

 

 

 

        瀏覽和搜索的前提是有據可查,沒有數據什麼都實現不了 , 我使用了Htmlparser通過抓取頁面信息的形式將新聞添加進數據庫 , 添加數據庫數據使用了hibernate3

        使用了Annotation的方式完成數據庫的映射。

         //NewsType(新聞類型)

Java代碼
package com.zly.test.entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "t_newsType")
public class NewsType implements Serializable {

	
	private static final long serialVersionUID = -5092622762025999122L;
	
	private Integer id;
	
	private String newsTypeName;
	@Column
	public String getNewsTypeName() {
		return newsTypeName;
	}

	public void setNewsTypeName(String newsTypeName) {
		this.newsTypeName = newsTypeName;
	}
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}
	
	
}

   //NewsItem(新聞具體條目)

 

Java代碼
package com.zly.test.entity;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = "t_newsItem")
public class NewsItem implements Serializable {

	
	private static final long serialVersionUID = -7532888546907495633L;
	
	private Integer id ; 
	
	private String newsTitle ;
	
	private String newsContent;
	
	private Date publishTime;
	
	private String resource;
	
	private NewsType type;
	
	private String editor;
	@Column
	public String getEditor() {
		return editor;
	}

	public void setEditor(String editor) {
		this.editor = editor;
	}
	@ManyToOne
	@JoinColumn(name = "t_newsType_id")
	public NewsType getType() {
		return type;
	}

	public void setType(NewsType type) {
		this.type = type;
	}
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}
	@Column
	public String getNewsTitle() {
		return newsTitle;
	}

	public void setNewsTitle(String newsTitle) {
		this.newsTitle = newsTitle;
	}
	
	
	@Temporal(value = TemporalType.TIMESTAMP)
	public Date getPublishTime() {
		return publishTime;
	}

	public void setPublishTime(Date publishTime) {
		this.publishTime = publishTime;
	}

	public String getResource() {
		return resource;
	}

	public void setResource(String resource) {
		this.resource = resource;
	}

	public String getNewsContent() {
		return newsContent;
	}

	public void setNewsContent(String newsContent) {
		this.newsContent = newsContent;
	}
	
	
}

    添加所有新聞類型的類放在了com.zly.test.newsFetch包下 , 具體代碼:

 

 

Java代碼
package com.zly.test.newsfetch;


import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.htmlparser.Parser;
import org.htmlparser.Tag;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.tags.Div;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.visitors.NodeVisitor;

import com.zly.test.entity.NewsItem;
import com.zly.test.entity.NewsType;

public class GetNews {
	public static void main(String[] args) throws Exception {
		//插入數據新聞類型
		//insertAllTypes();
		
		//插入所有新聞數據
		
		//國內新聞
		//insertNewsItems("http://www.chinanews.com.cn/scroll-news/gn/2009/05" ,"/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);
	}
}

 通過上面的代碼完成了所有的數據添加工作。

 

下面根據ssh的流程分別定製dao , manager , action

 

com.zly.test.dao包中是所有操作dao的抽象類和接口

 

我們直接看這些接口的實現

 

//NewsItemDaoHibernate  新聞實體類dao

 

 

 

Java代碼
@SuppressWarnings("unchecked")
	//根據分頁得到具體頁的內容
	public List<com.zly.test.entity.NewsItem> getNewsItemByFirstResultAndMaxResult(
			final int firstResult, final int maxResult , final int type) {
		return (List<NewsItem>) this.getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				return session.createQuery(" 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();
			}
		});
	}

 

在applicationContext-action.xml中配置action類別

Java代碼
		<bean class="com.zly.test.action.NewsAction" id="newsAction" scope="request">
	 		<property name="newsManager" ref="newsManager"></property>
	 		<property name="map" ref="map"></property>
	 		<property name="map1" ref="map1"></property>
		</bean>

     

  key是?category後面的值 , value是兩部分 , 被###分割開 , 前面的數值是所屬新聞類別的id值, 後面的文字是其類別的文字。將其保存在map中,避免不停地查詢數據庫。

 

  分頁類PageControl的代碼如下:

 

Java代碼
package com.zly.test.entity;

import java.util.List;




public class PageControl {
	
	private int curPage ; //當前是第幾頁 
	
	private int maxPage ; //一共有多少頁 
	
	private Long maxRowCount ; //一共有多少行 
	
	private int rowsPerPage = 8 ; //每頁有多少行 

	private List<?> data;//每頁的User
	
	public int getCurPage() {
		return curPage;
	}

	public void setCurPage(int curPage) {
		this.curPage = curPage;
	}

	public int getMaxPage() {
		return maxPage;
	}

	public void setMaxPage(int maxPage) {
		this.maxPage = maxPage;
	}

	public List<?> getUserList() {
		return data;
	}

	public void setUserList(List<?> data) {
		this.data = data;
	}

	public Long getMaxRowCount() {
		return maxRowCount;
	}

	public void setMaxRowCount(Long amaxRowCountxRowCount) {
		this.maxRowCount = amaxRowCountxRowCount;
	}

	public int getRowsPerPage() {
		return rowsPerPage;
	}

	public void setRowsPerPage(int rowsPerPage) {
		this.rowsPerPage = rowsPerPage;
	}
	
	public void countMaxPage() {   //根據總行數計算總頁數  
	    if (this.maxRowCount % this.rowsPerPage ==0){
	       this.maxPage = (int) (this.maxRowCount/this.rowsPerPage);
	    }else{
	       this.maxPage = (int) (this.maxRowCount/this.rowsPerPage + 1);        
	    }
	}
	
}

 

  被許多頁所包含的page.jsp代碼如下:

 

 

Java代碼
<%@ page language="java"  contentType="text/html;charset=utf-8"
    pageEncoding="utf-8"%>
<%@ taglib prefix="c" 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>

   下面是struts.xml中關於頁面展示新聞的配置

 

 

Java代碼
			<action name="newsAction" class="newsAction" >
				<result type="redirect">pageAction.action</result>					
			</action>
			
			<action name="pageAction" class="newsAction" method="pageAction">
				<result>/result.jsp</result>
			</action>
			
			<action name="detailAction" class="newsAction" method="showDetail">
				<result>/detail.jsp</result>
			</action>

          

 

首頁頁面index.jsp,裏面有幾個分類超鏈接和搜索對話框

 

Java代碼
<%@ page language="java"  pageEncoding="utf-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  
  <head>
    <title>最快新聞網</title>
    <link href="css/main.css" 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>

 

 

  其表現形式如下:

 

新聞分頁展示頁面result.jsp代碼如下:

Html代碼
<%@ page language="java" 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>

  顯示效果如下:

 

 

 

其中點擊具體超鏈接的效果圖如下:

 

 

 

 

任務1 到此完成,新聞顯示工作結束。下面是搜索引擎部分。

 

搜索的工具類放置在com.zly.indexManager包下面

 

說明,本程序使用了庖丁解牛中文分詞,用戶使用時需要中文字典,我的字典放在了c:/dic下面,使用庖丁還需要配置環境變量PAODING_DIC_HOME , 其值爲c:/dic , (就是你的字典文件所在的目錄)

 

代碼如下:

 

創建索引類IndexCreateUtil

 

Java代碼
package com.zly.indexManager;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;

import net.paoding.analysis.analyzer.PaodingAnalyzer;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.Session;
import org.htmlparser.Parser;

import com.zly.test.entity.NewsItem;

public class IndexCreateUtil {
	
	@SuppressWarnings("unchecked")
	public void createIndexForNews() throws Exception {
		//存放索引的文件夾
		File indexFile = new File("c:/index/news");
		//使用了庖丁解牛分詞器
		Analyzer analyzer = new PaodingAnalyzer();
		//使用索引文件夾,庖丁解牛分詞器創建IndexWriter
		IndexWriter indexWriter = new IndexWriter(indexFile , analyzer , true);
		//從數據庫中讀取出所有的新聞記錄以便進行索引的創建
		Configuration cfg = new AnnotationConfiguration().configure();
		SessionFactory factory = cfg.buildSessionFactory();
		Session session = factory.openSession();
		List<NewsItem> list = session.createQuery(" 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();
	}
}

對索引進行搜索的代碼如下:

 

 

Java代碼
package com.zly.indexManager;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import net.paoding.analysis.analyzer.PaodingAnalyzer;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;

import com.zly.test.entity.SearchResultBean;

public class IndexSearchUtil {
	
	public List<SearchResultBean> getSearchResult(String searchWhich , String searchParam , int firstResult , int maxResult) throws Exception{
		//索引所在文件夾
		File indexFile = new File("c:/index/news");
		//讀取索引的indexReader
		IndexReader reader = IndexReader.open(indexFile);
		//庖丁解牛分詞器
		Analyzer analyzer = new PaodingAnalyzer();
		//指定對content還是title進行查詢
		QueryParser parser = new QueryParser(searchWhich , analyzer);
		//創建indexSearcher
		IndexSearcher searcher  = new IndexSearcher(reader);
		//對用戶的輸入進行查詢
		Query query = parser.parse(searchParam);
		//根據date字段進行排序,得到查詢結果
		Hits hits = searcher.search(query , new Sort("date" , 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();
	}
}

 

分頁action代碼如下:

Java代碼
package com.zly.test.action;

import java.util.List;

import com.zly.indexManager.IndexSearchUtil;
import com.zly.test.entity.PageControl;
import com.zly.test.entity.SearchResultBean;

public class SearchAction extends BaseAction {

	
	private static final long serialVersionUID = -2387037924517370511L;
	//查詢索引實體類
	private IndexSearchUtil indexSearcher;
	//對應搜索字段是標題還是內容
	private String searchWhich;
	//對應用戶輸入的搜索內容
	private String searchParam;
	//對應分頁跳轉到的頁面
	private String jumpPage;
	
	public String getJumpPage() {
		return jumpPage;
	}

	public void setJumpPage(String jumpPage) {
		this.jumpPage = jumpPage;
	}

	public String getSearchWhich() {
		return searchWhich;
	}

	public void setSearchWhich(String searchWhich) {
		this.searchWhich = searchWhich;
	}

	public String getSearchParam() {
		return searchParam;
	}

	public void setSearchParam(String searchParam) {
		this.searchParam = searchParam;
	}

	public String search() throws Exception {
		//如果爲空,說明第一次進入分頁
		if(jumpPage == null) {
			jumpPage = "1";
		}
		//從request範圍內取得pageControl對象
		PageControl pageControl  = (PageControl) this.getRequest().getAttribute("pageControl");
		//如果爲空,則是第一次分頁,創建分頁對象,並且設置總的記錄條數,以便設置最大頁數 
		if(pageControl == null) {
			pageControl = new PageControl();
			pageControl.setMaxRowCount((long)indexSearcher.getResultCount(searchWhich, searchParam));
			pageControl.countMaxPage();
		}
		//設置當前頁
		pageControl.setCurPage(Integer.parseInt(jumpPage));
		//計算firstResult
		int firstResult = (pageControl.getCurPage() - 1) * pageControl.getRowsPerPage() + 1;
		//計算從當前條數算還有多少條記錄
		long left = pageControl.getMaxRowCount() - firstResult;
		int maxResult = -1;
		//如果剩餘的記錄數不如每頁顯示數,就設置maxResult爲剩餘條數
		if(left < pageControl.getRowsPerPage()) {
			maxResult = Integer.valueOf(left + "");
		//如果剩餘記錄數大於每頁顯示頁數,就設置maxResult爲每頁條數
		}else {
			maxResult = pageControl.getRowsPerPage(); 
		}
		//取得查詢結果集
		List<SearchResultBean> userList = indexSearcher.getSearchResult(searchWhich, searchParam, firstResult, maxResult);
		//設置爲pageControl
		pageControl.setData(userList);
		//將pageControl設置到request範圍,以便在jsp現實結果
		this.getRequest().setAttribute("pageControl", pageControl);
		//將searchWhich和searchParam設置到request範圍,以便添加到分頁jsp的form裏面的hidden表單域,以便下次分頁時,能夠將值提交過來
		this.getRequest().setAttribute("searchWhich", searchWhich);
		this.getRequest().setAttribute("searchParam", searchParam);
		//跳轉到分頁視圖
		return SUCCESS;
		
	}

	public IndexSearchUtil getIndexSearcher() {
		return indexSearcher;
	}

	public void setIndexSearcher(IndexSearchUtil indexSearcher) {
		this.indexSearcher = indexSearcher;
	}
	
}

 

搜索的action在struts.xml中設置如下:

 

Xml代碼

            <action name="searchAction" class="searchAction" method="search">
                <result>/searchResult.jsp</result>
            </action>

//searchResult.jsp代碼如下:

 

Html代碼
<%@ page language="java" 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>

    其運行結果如圖所示(按標題搜索):

 

 

 

 

 

 

按內容搜索的運行結果如下:

 

 

 

 

至此,本小項目的所有功能完成,雖然沒有多少難度,也不是什麼高科技, 俺還是在google和javaeye上查了不少資料,總算是做完了,貼出來,與大家分享,也給新手學習提供資料。

 

所有的資源我都添加到了附件中,學過ssh的同學應該能夠成功部署項目並運行。

 

其中NewsWithSearch.rar是工程文件夾,包含了所有的代碼文件和jar包,加壓完直接引到MyEclipse裏就 行,data.rar是所有的sql語句,插入到MySQL之前應先建立數據庫mynews  ,     dic.rar是庖丁解牛用到的字典文件,

解壓成一個文件夾,並配置環境變量PAODING_DIC_HOME,其值就是你把它解壓成的文件夾(例如c:/dic),最後如果你不想創建索引的話,可以把news.rar解壓成一個文件夾,拷貝到c:/index/news下面。

 

以上是那位牛兄的代碼,實在太多了,機械的粘貼複製也是挺累的。不過堅持就是勝利嗎,終於複製完了。但可能會有篡行或複製錯的地方,請多諒解。

 

源代碼下載:

SSH + Lucene + 分頁 + 排序 + 高亮 模擬簡單新聞網站搜索引擎--NewsWithSearch.part3
SSH + Lucene + 分頁 + 排序 + 高亮 模擬簡單新聞網站搜索引擎--NewsWithSearch.part2
SSH + Lucene + 分頁 + 排序 + 高亮 模擬簡單新聞網站搜索引擎--NewsWithSearch.part1
SSH + Lucene + 分頁 + 排序 + 高亮 模擬簡單新聞網站搜索引擎--news.part2
SSH + Lucene + 分頁 + 排序 + 高亮 模擬簡單新聞網站搜索引擎--news.part1
SSH + Lucene + 分頁 + 排序 + 高亮 模擬簡單新聞網站搜索引擎--dic
原出處請看:http://www.javaeye.com/post/1068335


 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章