1. 程式人生 > >Java進階學習第三十天(Lucene)

Java進階學習第三十天(Lucene)

一、Lucene概念

1、回顧索引
① 定義:索引是對資料庫表中一列或多列的值進行排序的一種結構
② 目的:加快對資料庫表中記錄的查詢
③ 特點:以空間換取時間,提高查詢速度快

2、什麼是Lucene
Lucene是apache軟體基金會發布的一個開放原始碼的全文檢索引擎工具包,由資深全文檢索專家Doug Cutting所撰寫,它是一個全文檢索引擎的架構,提供了完整的建立索引和查詢索引,以及部分文字分析的引擎,Lucene的目的是為軟體開發人員提供一個簡單易用的工具包,以方便在目標系統中實現全文檢索的功能,或者是以此為基礎建立起完整的全文檢索引擎,Lucene在全文檢索領域是一個經典的祖先,現在很多檢索引擎都是在其基礎上建立的,思想是相通的。

3、Lucene通常用在什麼地方
Lucece不能用在網際網路搜尋(即像百度那樣),只能用在網站內部的文字搜尋(即只能在CRM,RAX,ERP內部使用),但思想是相通的。

4、Lucene中存的什麼內容
Lucene中存的就是一系列的二進位制壓縮檔案和一些控制檔案,它們位於計算機的硬碟上,這些內容統稱為索引庫,索引庫有二部份組成:
① 原始記錄
存入到索引庫中的原始文字
② 詞彙表
按照一定的拆分策略(即分詞器)將原始記錄中的每個字元拆開後,存入一個供將來搜尋的表

5、為什麼網站內部有些地方要用Lucene來索搜,而不全用SQL來搜尋
① SQL只能針對資料庫表搜尋,不能直接針對硬碟上的文字搜尋
② SQL沒有相關度排名
③ SQL搜尋結果沒有關健字高亮顯示
④ SQL需要資料庫的支援,資料庫本身需要記憶體開銷較大
⑤ SQL搜尋有時較慢,尤其是資料庫不在本地時超慢

6、書寫程式碼使用Lucene的流程圖
① 建立索引庫:
◇ 建立JavaBean物件
◇ 建立Docment物件
◇ 將JavaBean物件所有的屬性值,均放到Document物件中去,屬性名可以和JavaBean相同或不同
◇ 建立IndexWriter物件
◇ 將Document物件通過IndexWriter物件寫入索引庫中
◇ 關閉IndexWriter物件
② 根據關鍵字查詢索引庫中的內容:
◇ 建立IndexSearcher物件
◇ 建立QueryParser物件
◇ 建立Query物件來封裝關鍵字
◇ 用IndexSearcher物件去索引庫中查詢符合條件的前100條記錄,不足100條記錄的以實際為準
◇ 獲取符合條件的編號
◇ 用indexSearcher物件去索引庫中查詢編號對應的Document物件
◇ 將Document物件中的所有屬性取出,再封裝回JavaBean物件中去,並加入到集合中儲存,以備將之用

二、Lucene入門

1、Lucene快速入門
① 建立javaweb工程,取名叫lucene-day01
② 匯入Lucene相關的jar包
lucene-core-3.0.2.jar【Lucene核心】
lucene-analyzers-3.0.2.jar【分詞器】
lucene-highlighter-3.0.2.jar【Lucene會將搜尋出來的字,高亮顯示,提示使用者】
lucene-memory-3.0.2.jar【索引庫優化策略】
③ 建立包結構
cn.itcast.javaee.lucene.entity
cn.itcast.javaee.lucene.firstapp
cn.itcast.javaee.lucene.secondapp
cn.itcast.javaee.lucene.crud
cn.itcast.javaee.lucene.fy
cn.itcast.javaee.lucene.utils
……
④ 建立JavaBean類

public class Article {
	private Integer id;//標題
	private String title;//標題
	private String content;//內容
	public Article(){}
	public Article(Integer id, String title, String content) {
		this.id = id;
		this.title = title;
		this.content = content;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
}

⑤ 建立FirstLucene.java類,編寫createIndexDB()和findIndexDB()二個業務方法

	//建立索引庫,將Article物件放入索引庫中的原始記錄表中,從而形成詞彙表
	@Test
	public void createIndexDB() throws Exception{
		//建立Article物件
		Article article = new Article(1,"培訓","傳智是一個Java培訓機構");
		//建立Document物件
		Document document = new Document();
		//將Article物件三個屬性值繫結到Document物件中
		/*
		*引數一:document物件的屬性名叫id,article的物件屬性名叫id,專案中提倡相同
		*引數二:document物件的id屬性值,與article的物件相同
		*引數三:是否將id屬性值由原始記錄錶轉存入詞彙表(Store.YES是存,Store.NO是不存)專案中提倡非id值存入詞彙表
		*引數四:是否將id屬性值進行分詞演算法(Index.ANALYZED表示會進行詞彙拆分,Index.NOT_ANALYZED表示不會拆分)專案中提倡非id值都進行詞彙拆分
		*/
		document.add(new Field("id",article.getId().toString(),Store.YES,Index.ANALYZED));
		document.add(new Field("title",article.getTitle(),Store.YES,Index.ANALYZED));
		document.add(new Field("content",article.getContent(),Store.YES,Index.ANALYZED));
		
		//建立indexWriter字元流物件
		/*
		*引數一:Lucene索引庫最終對應硬碟目錄
		*引數二:採用什麼策略將文字分詞,一個策略就是一個實現類
		*引數三:最多將文字拆分成多少個詞彙,LIMITED表示一萬個,即只取前一萬個詞彙,如果不足一萬以實際為準
		*/
		Directory directory = FSDirectory.open(new File("E:/LuceneDBDBDBDBDBDBDBDBDB"));
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
		MaxFieldLength maxFieldLength = MaxFieldLength.LIMITED;
		IndexWriter indexWriter = new IndexWriter(directory,analyzer,maxFieldLength);
		
		//將document物件寫入Lucene索引庫
		indexWriter.addDocument(document);
		//關閉indexWriter字元流物件
		indexWriter.close();
	}
     
        //根據關鍵字從索引庫搜尋符合條件的內容
	@Test
	public void findIndexDB() throws Exception{
		List<Article> articleList = new ArrayList<Article>();
		String keywords = "傳";
		Directory directory = FSDirectory.open(new File("E:/LuceneDBDBDBDBDBDBDBDBDB"));
		Version version = Version.LUCENE_30;
		Analyzer analyzer = new StandardAnalyzer(version);
		
		//建立IndexSearcher字元流物件
		IndexSearcher indexSearcher = new IndexSearcher(directory);
		//建立查詢解析器物件
		/*
		*引數一:使用分詞器的版本,提倡使用該jar包中最高版本
		*引數二:針對document中那個物件進行搜尋
		*/
		QueryParser queryParser = new QueryParser(version,"content",analyzer);
		//建立查詢物件,封裝查詢關鍵字
		Query query = queryParser.parse(keywords);
		//根據關鍵詞去索引中的詞彙表中搜索
		/*
		*引數一:封裝關鍵字的查詢物件,其中QueryParser表示查詢解析器
		*引數二:如果根據關鍵字搜尋出來的詞彙太多,只取前100個,不足以實際為準
		*/
		TopDocs topDocs = indexSearcher.search(query,100);
		//迭代詞彙表中符合條件的編號
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			//取出封裝編號和分數的scoreDocs物件
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			//取出每一個編號
			int no = scoreDoc.doc;
			//根據編號去索引庫原始記錄表中查詢對應的document物件
			Document document = indexSearcher.doc(no);
			//獲取document物件中三個屬性值
			String id = document.get("id");
			String title = document.get("title");
			String content = document.get("content");
			//封裝到article物件中
			Article article = new Article(Integer.parseInt(id),title,content);
			//將article物件加入到list物件中儲存
			articleList.add(article);
		}
		//迭代結果集
		for(Article article : articleList){
			System.out.println(article.getId()+":"+article.getTitle()+":"+article.getContent());
		}
	}

2、建立LuceneUtil工具類,使用反射封裝通用的方法
(使用反射封裝:javabean物件轉document物件方法、document物件轉javabean物件方法)

public class LuceneUtil {
	//不讓外界new該工具類
	private LuceneUtil(){}
	
	private static Directory directory ;
	private static Analyzer analyzer ; 
	private static Version version; 
	private static MaxFieldLength maxFieldLength;
	static{
		try {
			directory = FSDirectory.open(new File("E:/LuceneDBDBDBDBDBDBDBDBDB"));
			version = Version.LUCENE_30;
			analyzer = new StandardAnalyzer(version);
			maxFieldLength = MaxFieldLength.LIMITED;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	public static Directory getDirectory() {
		return directory;
	}
	public static Analyzer getAnalyzer() {
		return analyzer;
	}
	public static Version getVersion() {
		return version;
	}
	public static MaxFieldLength getMaxFieldLength() {
		return maxFieldLength;
	}
	
	//javabean物件轉document物件
	public static Document javabean2documemt(Object obj) throws Exception{
		//建立document物件
		Document document = new Document();
		//獲取obj引用物件的位元組碼
		Class clazz = obj.getClass();
		//通過物件位元組碼獲取私有屬性
		java.lang.reflect.Field[] reflectFields = clazz.getDeclaredFields();
		//迭代
		for(java.lang.reflect.Field field : reflectFields){
			//強力反射
			field.setAccessible(true);
			//獲取屬性名
			String fieldName = field.getName();
			//手工拼接方法名
			String init = fieldName.substring(0,1).toUpperCase();
			String methodName = "get" + init + fieldName.substring(1);
			//獲取方法
			Method method = clazz.getDeclaredMethod(methodName,null);
			//執行方法
			String returnValue = method.invoke(obj,null).toString();
			//加入到document物件中,此時javabean屬性和document物件的屬性相同
			document.add(new Field(fieldName,returnValue,Store.YES,Index.ANALYZED));
		}
		//返回document物件
		return document;
	}
	
	//document物件轉javabean物件
	public static Object document2javabean(Document document,Class clazz) throws Exception{
		//通過位元組碼建立物件
		Object obj = clazz.newInstance();
		//通過位元組碼獲取私有屬性值
		java.lang.reflect.Field[] reflectFields = clazz.getDeclaredFields();
		for(java.lang.reflect.Field field : reflectFields){
			field.setAccessible(true);
			String fieldName = field.getName();
			String fieldValue = document.get(fieldName);
			//封裝到javabean對應的屬性中去,通過setXxx方法
			BeanUtils.setProperty(obj,fieldName,fieldValue);
		}	
		return obj;
	}
}

3、使用LuceneUtil工具類重構FirstLucene.java為SecondLucene.java

public class SecondLucene {
	@Test
	//建立索引庫
	public void createIndexDB() throws Exception{
		Article article = new Article(1,"Java培訓","傳智是一個Java培訓機構");
		Document document = LuceneUtil.javabean2documemt(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory(),LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.addDocument(document);
		indexWriter.close();
	}
	@Test
	//根據關鍵字從索引庫中查詢符合條件的資料
	public void findIndexDB() throws Exception{
		List<Article> articleList = new ArrayList<Article>();
		String keywords = "傳";
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,10);
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;
			Document document = indexSearcher.doc(no);
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		for(Article article : articleList){
			System.out.println(article.getId()+":"+article.getTitle()+":"+article.getContent());
		}
	}
}

4、使用LuceneUtil工具類,完成CURD操作

public class LuceneCURD {
	@Test
	public void addIndexDB() throws Exception{
		Article article = new Article(1,"培訓","傳智是一個Java培訓機構");
		Document document = LuceneUtil.javabean2documemt(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory(),LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.addDocument(document);//核心
		indexWriter.close();
	}
	@Test
	public void updateIndexDB() throws Exception{
		Integer id = 1;
		Article article = new Article(1,"培訓","廣州傳智是一個Java培訓機構");
		Document document = LuceneUtil.javabean2documemt(article);
		Term term = new Term("id",id.toString());
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory(),LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		/*
		*引數一:表示需要更新的document物件(id表示原來document的id屬性)
		*引數二:新的document物件
		*/
		indexWriter.updateDocument(term,document);
		indexWriter.close();
	}
	@Test
	public void deleteIndexDB() throws Exception{
		Integer id = 1;
		Term term = new Term("id",id.toString());
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory(),LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.deleteDocuments(term);
		indexWriter.close();
	}
	@Test
	public void deleteAllIndexDB() throws Exception{
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory(),LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.deleteAll();
		indexWriter.close();
	}
	@Test
	public void searchIndexDB() throws Exception{
		List<Article> articleList = new ArrayList<Article>();
		String keywords = "傳智";
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,10);
		for(int i = 0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];	
			int no = scoreDoc.doc;
			Document document = indexSearcher.doc(no);
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		for(Article article : articleList){
			System.out.println(article.getId()+":"+article.getTitle()+":"+article.getContent());
		}
	}
}

5、使用Jsp +Js + Jquery + Servlet + Lucene,完成同步分頁
① 建立ArticleDao.java類(持久層)

public class ArticleDao {
	//根據關鍵字獲取總記錄數
	public Integer getAllObjectNum(String keywords) throws Exception{
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,3);
		//返回符合條件的真實記錄數,不受3影響
		return topDocs.totalHits;
		//返回符合條件的真實記錄數,會受3影響
		//return topDocs.scoreDocs.Length;
	}
	
	//根據關鍵字批量查詢記錄(start:從第幾條記錄的索引號開始查詢,索引號從0開始;size:最多查詢幾條記錄,不滿足以實際為準)
	public List<Article> findAllObjectWithFY(String keywords,Integer start,Integer size) throws Exception{
		List<Article> articleList = new ArrayList<Article>();
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,100000000);
		int middle = Math.min(start+size,topDocs.totalHits);
		for(int i=start;i<middle;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;
			Document document = indexSearcher.doc(no);
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		return articleList;
	}
}

② 建立PageBean.java類

public class PageBean {
	private Integer allObjectNum;//總記錄數
	private Integer allPageNum;//總頁數
	private Integer currPageNum;//當前頁號
	private Integer perPageNum = 2;//每頁顯示記錄數,預設為2條
	private List<Article> articleList = new ArrayList<Article>();//該頁顯示的內容
	public PageBean(){}
	public Integer getAllObjectNum() {
		return allObjectNum;
	}
	public void setAllObjectNum(Integer allObjectNum) {
		this.allObjectNum = allObjectNum;
		if(this.allObjectNum % this.perPageNum == 0){
			this.allPageNum = this.allObjectNum / this.perPageNum;
		}else{
			this.allPageNum = this.allObjectNum / this.perPageNum + 1;
		}
	}
	public Integer getAllPageNum() {
		return allPageNum;
	}
	public void setAllPageNum(Integer allPageNum) {
		this.allPageNum = allPageNum;
	}
	public Integer getCurrPageNum() {
		return currPageNum;
	}
	public void setCurrPageNum(Integer currPageNum) {
		this.currPageNum = currPageNum;
	}
	public Integer getPerPageNum() {
		return perPageNum;
	}
	public void setPerPageNum(Integer perPageNum) {
		this.perPageNum = perPageNum;
	}
	public List<Article> getArticleList() {
		return articleList;
	}
	public void setArticleList(List<Article> articleList) {
		this.articleList = articleList;
	}
}

③ 建立ArticleService.java類(業務層)

public class ArticleService {
	private ArticleDao articleDao = new ArticleDao();
	//根據關鍵字和頁號查詢內容
	public PageBean fy(String keywords,Integer currPageNum) throws Exception{
		PageBean pageBean = new PageBean();
		//封裝當前頁號
		pageBean.setCurrPageNum(currPageNum);
		//封裝總記錄數
		Integer allObjectNum = articleDao.getAllObjectNum(keywords);
		//封裝總頁數
		Integer allPageNum = pageBean.setAllObjectNum(allObjectNum);
		//封裝內容
		Integer size = pageBean.getPerPageNum();
		Integer start = (pageBean.getCurrPageNum()-1) * size;
		List<Article> articleList = articleDao.findAllObjectWithFY(keywords,start,size);
		pageBean.setArticleList(articleList);
		return pageBean;
	}
}

④ 建立ArticleServlet.java類 (控制層)

public class ArticleServlet extends HttpServlet {
	public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
		try {
			request.setCharacterEncoding("UTF-8");
			Integer currPageNum = Integer.parseInt(request.getParameter("currPageNum"));//當前頁號
			String keywords = request.getParameter("keywords");//關鍵字
			//呼叫業務層
			ArticleService articleService = new ArticleService();
			PageBean pageBean = articleService.fy(keywords,currPageNum);
			//將pageBean變數繫結到request域物件中
			request.setAttribute("pageBean",pageBean);
			//將keywords繫結到request域物件中
			request.setAttribute("keywords",keywords);
			//轉發
			request.getRequestDispatcher("/list.jsp").forward(request,response);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

⑤ 匯入EasyUI相關的js包的目錄
⑥ 在WebRoot目錄下建立list.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  	<link rel="stylesheet" href="themes/default/easyui.css" type="text/css"></link>
    <link rel="stylesheet" href="themes/icon.css" type="text/css"></link>
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/jquery.easyui.min.js"></script>
    <script type="text/javascript" src="locale/easyui-lang-zh_CN.js"></script>
  </head>
  <body>
  	<!-- 輸入區 -->
	<form action="${pageContext.request.contextPath}/ArticleServlet?currPageNum=1" method="POST">
		輸入關健字:<input type="text" name="keywords" value="傳智" maxlength="4"/>
		<input type="button" value="提交"/>
	</form>
 	
 	<!-- 顯示區 -->
	<table border="2" align="center" width="70%">
		<tr>
			<th>編號</th>
			<th>標題</th>
			<th>內容</th>
		</tr>
		<c:forEach var="article" items="${pageBean.articleList}">
			<tr>
				<td>${article.id}</td>
				<td>${article.title}</td>
				<td>${article.content}</td>
			</tr>		
		</c:forEach>
	</table>

	<!-- 分頁元件區 -->
	<center>	
		<div id="pp" style="background:#efefef;border:1px solid #ccc;width:600px"></div> 
	</center>
	<script type="text/javascript">
		$("#pp").pagination({ 
			total:${pageBean.allObjectNum}, 
			pageSize:${pageBean.perPageNum},
			showPageList:false,
			showRefresh:false,
			pageNumber:${pageBean.currPageNum}
		}); 
		$("#pp").pagination({
			onSelectPage:function(pageNumber){
	$("form").attr("action","${pageContext.request.contextPath}/ArticleServlet?currPageNum="+pageNumber);
			$("form").submit();	
			}
		});
	</script>
	<script type="text/javascript">
 			$(":button").click(function(){
 			$("form").submit();	
 		});
 	</script>
  </body>
</html>

⑦ 在WebRoot目錄下建立list2.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>根據關鍵字分頁查詢所有資訊</title>
  </head>
  <body>
	<!-- 輸入區 -->
	<form action="${pageContext.request.contextPath}/ArticleServlet" method="POST">
		<input id="currPageNOID" type="hidden" name="currPageNO" value="1">
		<table border="2" align="center">
			<tr>
				<th>輸入關鍵字:</th>
				<th><input type="text" name="keywords" maxlength="4" value="${requestScope.keywords}"/></th>
				<th><input type="submit" value="站內搜尋"/></th>
			</tr>
		</table>
	</form>

	<!-- 輸出區 -->
	<table border="2" align="center" width="60%">
		<tr>
			<th>編號</th>
			<th>標題</th>
			<th>內容</th>
		</tr>
		<c:forEach var="article" items="${requestScope.pageBean.articleList}">
			<tr>
				<td>${article.id}</td>
				<td>${article.title}</td>
				<td>${article.content}</td>
			</tr>
		</c:forEach>
		<!-- 分頁條 -->
		<tr>
			<td colspan="3" align="center">
				<a onclick="fy(1)" style="text-decoration:none;cursor:hand">
					【首頁】
				</a>
				<c:choose>
					<c:when test="${requestScope.pageBean.currPageNO+1<=requestScope.pageBean.allPageNO}">
						<a onclick="fy(${requestScope.pageBean.currPageNO+1})" style="text-decoration:none;cursor:hand">
							【下一頁】
						</a>
					</c:when>
					<c:otherwise>
							下一頁
					</c:otherwise>
				</c:choose>
				<c:choose>
					<c:when test="${requestScope.pageBean.currPageNO-1>0}">
						<a onclick="fy(${requestScope.pageBean.currPageNO-1})" style="text-decoration:none;cursor:hand">
							【上一頁】
						</a>
					</c:when>
					<c:otherwise>
							上一頁
					</c:otherwise>
				</c:choose>
				<a onclick="fy(${requestScope.pageBean.allPageNO})" style="text-decoration:none;cursor:hand">
					【未頁】
				</a>
			</td>
		</tr>
	</table>

	<script type="text/javascript">
		function fy(currPageNO){
			document.getElementById("currPageNOID").value = currPageNO;
			document.forms[0].submit();
		}
	</script>
  </body>
</html>

三、索引庫優化

1、什麼是索引庫
索引庫是Lucene的重要的儲存結構,它包括二部份:原始記錄表和詞彙表
① 原始記錄表:存放的是原始記錄資訊,Lucene為存入的內容分配一個唯一的編號
② 詞彙表:存放的是經過分詞器拆分出來的詞彙和該詞彙在原始記錄表中的編號

2、為什麼要將索引庫進行優化
在預設情況下,向索引庫中增加一個Document物件時,索引庫自動會新增一個副檔名叫*.cfs的二進位制壓縮檔案,如果向索引庫中存Document物件過多,那麼*.cfs也會不斷增加,同時索引庫的容量也會不斷增加,影響索引庫的大小

3、索引庫優化方案
① 解決數量和大小問題
◇ 合併cfs檔案,合併後的cfs檔案是二進位制壓縮字元,能解決是的檔案大小和數量的問題
◇ 設定合併因子,自動合併cfs檔案,預設10個cfs檔案合併成一個cfs檔案
② 解決速度問題
使用RAMDirectory,類似於記憶體索引庫,能解決是的讀取索引庫檔案的速度問題,它能以空換時,提高速度快,但不能持久儲存,因此啟動時載入硬碟中的索引庫到記憶體中的索引庫,退出時將記憶體中的索引庫儲存到硬碟中的索引庫,且內容不能重複。

public class Article {
	private Integer id;//編號
	private String title;//標題
	private String content;//內容
	private Integer count;//字數
	public Article(){}
	public Article(Integer id, String title, String content, Integer count) {
		this.id = id;
		this.title = title;
		this.content = content;
		this.count = count;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public Integer getCount() {
		return count;
	}
	public void setCount(Integer count) {
		this.count = count;
	}
	@Override
	public String toString() {
		return "編號:" + id + "標題:" + title + "內容:" + content + "字數: " + count;
	}
}
public class ArticleDao {
	/**
	 * 增加document物件索引庫中
	 */
	@Test
	public void add() throws Exception{
		Article article = new Article(1,"培訓","傳智是一家it培訓機構",10);
		Document document = LuceneUtil.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory()	,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.addDocument(document);
		indexWriter.close();
	}
	/**
	 * 方法一:合併cfs檔案,合併後的cfs檔案是二進位制壓縮字元,能解決是的檔案大小和數量的問題
	 */
	@Test
	public void type1() throws Exception{
		Article article = new Article(1,"培訓","傳智是一家it培訓機構",10);
		Document document = LuceneUtil.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory()	,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.addDocument(document);
		//合併cfs文字
		indexWriter.optimize();
		indexWriter.close();
	}
	
	/**
	 * 方法二:設定合併因子,自動合併cfs檔案
	 */
	@Test
	public void type2() throws Exception{
		Article article = new Article(1,"培訓","傳智是一家it培訓機構",10);
		Document document = LuceneUtil.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory()	,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.addDocument(document);
		//設定合併因子,即滿足3個cfs文字一合併
		indexWriter.setMergeFactor(3);
		indexWriter.close();
	}
	/**
	 * 預設情況下,每10個cfs文字一合併
	 */
	@Test
	public void type3() throws Exception{
		Article article = new Article(1,"培訓","傳智是一家it培訓機構",10);
		Document document = LuceneUtil.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory()	,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.addDocument(document);
		//設定合併因子,即滿足10個cfs文字一合併
		//indexWriter.setMergeFactor(10);
		indexWriter.close();
	}
	
	/**
	 * 使用RAMDirectory,類似於記憶體索引庫,能解決是的讀取索引庫檔案的速度問題
	 */
	@Test
	public void type4() throws Exception{
		Article article = new Article(1,"培訓","傳智是一家it培訓機構",10);
		Document document = LuceneUtil.javabean2document(article);
		//硬碟索引庫
		Directory fsDirectory = FSDirectory.open(new File("E:/IndexDBDBDB"));
		//記憶體索引庫,因為硬碟索引庫的內容要同步到記憶體索引庫中
		Directory ramDirectory = new RAMDirectory(fsDirectory);	
		//指向硬碟索引庫的字元流,true表示如果記憶體索引庫中和硬碟索引庫中的相同的document物件時,先刪除硬碟索引庫中的document物件,
		//再將記憶體索引庫的document物件寫入硬碟索引庫中
		//反之是false,預設為false,這個boolean值寫在硬碟字元流的構造器
		IndexWriter fsIndexWriter = new IndexWriter(fsDirectory,LuceneUtil.getAnalyzer(),true,LuceneUtil.getMaxFieldLength());
		//指向記憶體索引庫的字元流
		IndexWriter ramIndexWriter = new IndexWriter(ramDirectory,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		//將document物件寫入記憶體索引庫
		ramIndexWriter.addDocument(document);
		ramIndexWriter.close();
		//將記憶體索引庫的所有document物件同步到硬碟索引庫中
		fsIndexWriter.addIndexesNoOptimize(ramDirectory);
		fsIndexWriter.close();
	}
	
	@Test
	public void findAll() throws Exception{
		String keywords = "家";
		List<Article> articleList = new ArrayList<Article>();
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,100);
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;
			Document document = indexSearcher.doc(no);
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		for(Article a : articleList){
			System.out.println(a);
		}	
	}
}

四、分詞器

1、什麼是分詞器
採用一種演算法,將中英文字中的字元拆分開來,形成詞彙,以待使用者輸入關健字後搜尋

2、為什麼要分詞器
因為使用者輸入的搜尋的內容是一段文字中的一個關健字,和原始表中的內容有差別,但作為搜尋引擎來講,又得將相關的內容搜尋出來,此時就得采用分詞器來最大限度匹配原始表中的內容

3、分詞器工作流程
① 按分詞器拆分出詞彙
② 去除停用詞和禁用詞
③ 如果有英文,把英文字母轉為小寫,即搜尋不分大小寫

4、測試Lucene內建和第三方分詞器的分詞效果

public class TestAnalyzer {
	private static void testAnalyzer(Analyzer analyzer, String text) throws Exception {
		System.out.println("當前使用的分詞器:" + analyzer.getClass());
		TokenStream tokenStream = analyzer.tokenStream("content",new StringReader(text));
		tokenStream.addAttribute(TermAttribute.class);
		while (tokenStream.incrementToken()) {
			TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
			System.out.println(termAttribute.term());
		}
	}
	
	public static void main(String[] args) throws Exception{
		//Lucene記憶體的分詞器
		//testAnalyzer(new StandardAnalyzer(LuceneUtil.getVersion()),"傳智播客說我們的首都是北京呀it");
		//testAnalyzer(new FrenchAnalyzer(LuceneUtil.getVersion()),"傳智播客說我們的首都是北京呀it");
		//testAnalyzer(new RussianAnalyzer(LuceneUtil.getVersion()),"傳智播客說我們的首都是北京呀it");
		//testAnalyzer(new ChineseAnalyzer(),"傳智播客說我們的首都是北京呀it");
		//testAnalyzer(new CJKAnalyzer(LuceneUtil.getVersion()),"傳智播客說我們的首都是北京呀it");
			
		//Lucene外接分詞器
		testAnalyzer(new IKAnalyzer(),"傳智播客說我們的首都是北京呀");
	}
}

6、使用第三方IKAnalyzer分詞器【中文首選】
需求:過濾掉上面例子中的“說”,“的”,“呀”,且將“傳智播客”看成一個整體 關健字
① 匯入IKAnalyzer分詞器核心jar包:IKAnalyzer3.2.0Stable.jar
② 將IKAnalyzer.cfg.xml和stopword.dic和xxx.dic檔案複製到MyEclipse的src目錄下,再進行配置,在配置時首行需要一個空行

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">  
<properties>  
	<comment>IK Analyzer 擴充套件配置</comment>
	
	<!-- 使用者可以在這裡配置自己的擴充套件字典 --> 
	<entry key="ext_dict">/mydict.dic</entry> 
	
	
	<!--使用者可以在這裡配置自己的擴充套件停止詞字典 -->
	<entry key="ext_stopwords">/surname.dic</entry> 
	
	
</properties>

五、搜尋結果高亮

在搜尋結果中,將與關健字相同的字元用紅色顯示

public class ArticleDao {
	//增加document物件索引庫中
	@Test
	public void add() throws Exception{
		Article article = new Article(1,"培訓","傳智是一家it培訓機構",10);
		Document document = LuceneUtil.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory()	,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.addDocument(document);
		indexWriter.close();
	}

	@Test
	public void findAll() throws Exception{
		String keywords = "培訓";
		List<Article> articleList = new ArrayList<Article>();
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,100);
		
		//以下程式碼對內容中含有關鍵字的字串高亮顯示
		//格式物件
		Formatter formatter = new SimpleHTMLFormatter("<font color='red'>","</font>");
		//關鍵字物件
		Scorer scorer = new QueryScorer(query);
		//高亮物件
		Highlighter highlighter = new Highlighter(formatter,scorer);
		
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;	
			
			//這時候,關鍵字還沒有高亮
			Document document = indexSearcher.doc(no);
			//設定關鍵字高亮
			String titleHighlighter = highlighter.getBestFragment(LuceneUtil.getAnalyzer(),"title",document.get("title"));
			String contentHighlighter = highlighter.getBestFragment(LuceneUtil.getAnalyzer(),"content",document.get("content"));
			//將高亮後的結果再次封裝到document物件中
			document.getField("title").setValue(titleHighlighter);
			document.getField("content").setValue(contentHighlighter);
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		for(Article a : articleList){
			System.out.println(a);
		}
	}
}

六、搜尋結果摘要

如果搜尋結果內容太多,我們只想顯示前幾個字元, 必須與高亮一起使用

	@Test
	public void findAll() throws Exception{
		String keywords = "培訓";
		List<Article> articleList = new ArrayList<Article>();
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,1000000);
		
		Formatter formatter = new SimpleHTMLFormatter("<font color='red'>","</font>");
		Scorer scorer = new QueryScorer(query);
		Highlighter highlighter = new Highlighter(formatter,scorer);
		
		Fragmenter fragmenter  = new SimpleFragmenter(4);
		highlighter.setTextFragmenter(fragmenter);
		
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;
			Document document = indexSearcher.doc(no);
			
			String highlighterContent = highlighter.getBestFragment(LuceneUtil.getAnalyzer(),"content",document.get("content"));
			document.getField("content").setValue(highlighterContent);
			
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		for(Article article : articleList){
			System.out.println(article);
		}
	}

七、搜尋結果排序

1、什麼是搜尋結果排序
搜尋結果是按某個或某些欄位高低排序來顯示的結果

2、影響網站排名的先後的有多種
① head/meta/
② 網頁的標籤整潔
③ 網頁執行速度
④ 採用div+css
……

3、Lucene中的顯示結果次序與相關度得分有關【ScoreDoc.score】
預設情況下,Lucene是按相關度得分排序的,得分高排在前,得分低排在後,如果相關度得分相同,按插入索引庫的先後次序排序

public class ArticleDao1 {
	@Test
	public void add() throws Exception{
		Article article = new Article(1,"培訓","傳智是一家it培訓機構",10);
		//Article article = new Article(2,"培訓","北大是一家it培訓機構",20);
		//Article article = new Article(3,"培訓","中大是一家華南地區it培訓機構",30);
		//Article article = new Article(4,"培訓","哈哈培訓機構是好的培訓",9);
		//Article article = new Article(5,"培訓","培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓",15);
		//Article article = new Article(6,"培訓","培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓培訓",35);

		Document document = LuceneUtil.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory()	,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		//人工設定該document的得分
		//document.setBoost(100F);
		indexWriter.addDocument(document);
		indexWriter.close();
	}
	
	@Test
	public void findAll() throws Exception{
		String keywords = "培訓";
		List<Article> articleList = new ArrayList<Article>();
		
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,100);
		
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;

			//獲取document物件的評分
			float score = scoreDoc.score;
			System.out.println("score=" + score);
			
			Document document = indexSearcher.doc(no);
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		for(Article a : articleList){
			System.out.println(a);
		}
	}
}

4、Lucene中根據單個或多個欄位排序

public class ArticleDao2 {
	@Test
	public void add() throws Exception{
		Article article = new Article(1,"培訓","傳智是一家it培訓機構",10);
		//Article article = new Article(2,"培訓","北大是一家it培訓機構",20);
		//Article article = new Article(3,"培訓","中大是一家it培訓機構",20);
		//Article article = new Article(4,"培訓","小大是一家it培訓機構",30);
		Document document = LuceneUtil.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory()	,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.addDocument(document);
		indexWriter.close();
	}
	
	@Test
	public void findAll() throws Exception{
		String keywords = "培訓";
		List<Article> articleList = new ArrayList<Article>();
		
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		
		//按得分度高低排序
		//TopDocs topDocs = indexSearcher.search(query,100);
		
		//建立排序物件
		//引數一:id表示依據document物件中的哪個欄位排序,例如:id
		//引數二:SortField.INT表示document物件中該欄位的型別,以常量方式書寫
		//引數三:true表示降序,類似於order by id desc;false表示升序,類似於order by id asc
		//Sort sort = new Sort(new SortField("id",SortField.INT,false));
		
		//按count欄位的降序排列,如果count欄位相同的話,再按id的升序排序
		Sort sort = new Sort(
				new SortField("count",SortField.INT,true),
				new SortField("id",SortField.INT,false));
		
		//sort表示排序的條件
		TopDocs topDocs = indexSearcher.search(query,null,100,sort);
		
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;
			Document document = indexSearcher.doc(no);
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		for(Article a : articleList){
			System.out.println(a);
		}
	}
}

在多欄位排序中,只有第一個欄位排序結果相同時,第二個欄位排序才有作用【提倡用數值型排序】

八、條件搜尋

1、什麼是條件搜尋
用關健字與指定的單列或多例進行匹配的搜尋

2、單欄位條件搜尋和多欄位條件搜尋【提倡多欄位】

public class ArticleDao {
	@Test
	public void add() throws Exception{
		Article article = new Article(1,"培訓","傳智是一家it培訓機構",10);
		//Article article = new Article(2,"培訓","北大是一家it培訓機構",20);
		//Article article = new Article(3,"培訓","中大是一家it培訓機構",20);
		//Article article = new Article(4,"培訓","小大是一家it培訓機構",30);
		Document document = LuceneUtil.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtil.getDirectory()	,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
		indexWriter.addDocument(document);
		indexWriter.close();
	}
	
	@Test
	public void findAll() throws Exception{
		String keywords = "機構";
		List<Article> articleList = new ArrayList<Article>();
		
		//單欄位搜尋
		//QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"title",LuceneUtil.getAnalyzer());
		
		//多欄位搜尋,好處:搜尋的範圍大,最大限度匹配搜尋結果
		QueryParser queryParser = 
			new MultiFieldQueryParser(
					LuceneUtil.getVersion(),
					new String[]{"content","title"},
					LuceneUtil.getAnalyzer());
		
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		
		TopDocs topDocs = indexSearcher.search(query,100);
		
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;
			Document document = indexSearcher.doc(no);
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		for(Article a : articleList){
			System.out.println(a);
		}
	}
}

九、非同步分頁

1、用第三方工具類,將JavaBean、List、Map<String,Object>轉成JSON文字
① 首先匯入第三方jar包:
◇ commons-beanutils-1.7.0.jar
◇ commons-collections-3.1.jar
◇ commons-lang-2.5.jar
◇ commons-logging-1.1.1.jar
◇ ezmorph-1.0.3.jar
◇ json-lib-2.1-jdk15.jar

public class User {
	private Integer id;//編號
	private String name;//姓名
	private Integer sal;//薪水
	private String sex;//性別
	public User(){}
	public User(Integer id, String name, Integer sal, String sex) {
		this.id = id;
		this.name = name;
		this.sal = sal;
		this.sex = sex;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getSal() {
		return sal;
	}
	public void setSal(Integer sal) {
		this.sal = sal;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
}

② JavaBean->JSON

	@Test
	public void javabean2json(){
		User user = new User(1,"哈哈",7000,"男");
		JSONArray jsonArray = JSONArray.fromObject(user);
		String jsonJAVA = jsonArray.toString();
		System.out.println(jsonJAVA);
		//[{"id":1,"name":"哈哈","sal":7000}]
	}

③ List->JSON

	@Test
	public void list2json(){
		List<User> userList = new ArrayList<User>();
		userList.add(new User(1,"班長",7000,"男"));
		userList.add(new User(2,"班長老婆",8000,"男"));
		userList.add(new User(3,"班長小孩",20000,"男"));
		JSONArray jsonArray = JSONArray.fromObject(userList);
		String jsonJAVA = jsonArray.toString();
		System.out.println(jsonJAVA);
		//[{"id":1,"name":"班長","sal":7000},{"id":2,"name":"班長老婆","sal":8000},{"id":3,"name":"班長小孩","sal":20000}]
	}	

④ Map<String,Object>->JSON【重點】

	@Test
	public void map2json(){
		List<User> userList = new ArrayList<User>();
		userList.add(new User(1,"班長",7000,"男"));
		userList.add(new User(2,"班長老婆",8000,"女"));
		userList.add(new User(3,"班長小孩",20000,"男"));
		userList.add(new User(4,"班長小孩的小孩",40000,"男"));

		Map<String,Object> map = new HashMap<String,Object>();
		//total表示集合的長度
		map.put("total",userList.size());
		//rows表示集合內容
		map.put("rows",userList);
		
		JSONArray jsonArray = JSONArray.fromObject(map);
		String jsonJAVA = jsonArray.toString();
		System.out.println(jsonJAVA);
		
		jsonJAVA = jsonJAVA.substring(1,jsonJAVA.length()-1);
		System.out.println(jsonJAVA);
	}

2、用JSON文字動態建立DataGrid

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>DataGrid表格</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <!-- 引入css檔案,無順序 -->
    <link rel="stylesheet" href="themes/icon.css" type="text/css"></link>
    <link rel="stylesheet" href="themes/default/easyui.css" type="text/css"></link>
    
  	<!-- 引入js檔案,有順序 -->
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/jquery.easyui.min.js"></script>
  </head>
  <body>
	<!-- 
		data-options是easyui特有的屬性
		url表示載入json的路徑
		fitColumns=true表示等分寬度,要配合width:100這個屬性值(提倡)
		fitColumns=false表示不等分寬度,這時由width:100這個屬性值來決定寬度,預設為false
		singleSelect=true表示可以選擇表格中的一項
		singleSelect=false表示可以選擇表格中的多項,預設為false
	-->
	<table 
		style="width:400px;height:250px"
		border="2" 
		align="center" 
		class="easyui-datagrid"
		data-options="url:'data/datagrid_data.json',fitColumns:true,singleSelect:true">
		<thead>
			<tr>
				<th data-options="field:'id',width:50">編號</th>
				<th data-options="field:'name',width:50">姓名</th>
				<th data-options="field:'sal',width:50">薪水</th>
				<th data-options="field:'sex',width:50">性別</th>
			</tr>
		</thead>
	</table>
  </body>
</html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>DataGrid表格</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">

    <!-- 引入css檔案,無順序 -->
    <link rel="stylesheet" href="themes/icon.css" type="text/css"></link>
    <link rel="stylesheet" href="themes/default/easyui.css" type="text/css"></link>

  	<!-- 引入js檔案,有順序 -->
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/jquery.easyui.min.js"></script>
	<script type="text/javascript" src="js/easyui-lang-zh_CN.js"></script>
  </head>
  <body>
	<!-- 
		pagination表示分頁欄
		pageSize表示每個顯示多少條記錄
		pageList表示可供選擇的條數
	-->
	<table id="dg"></table>
	<script type="text/javascript">
		$("#dg").datagrid({
			url : "data/datagrid_data.json",
			columns :  [[    
					        	{field:'id',title:'編號',width:100},    
					        	{field:'name',title:'姓名',width:100},    
					        	{field:'sal',title:'薪水',width:100},
					        	{field:'sex',title:'性別',width:100}       
						]],
			fitColumns : true,
			singleSelect : true,
			pagination : true,
			pageSize : 2,
			pageList : [2]		    
		});
	</script>
  </body>
</html>

3、用Servlet返回JSON文字動態建立DataGrid

<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>用Servlet返回JSON文字動態建立DataGrid</title>
    <!-- 引入css檔案,無順序 -->
    <link rel="stylesheet" href="themes/icon.css" type="text/css"></link>
    <link rel="stylesheet" href="themes/default/easyui.css" type="text/css"></link>
  	<!-- 引入js檔案,有順序 -->
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/jquery.easyui.min.js"></script>
	<script type="text/javascript" src="js/easyui-lang-zh_CN.js"></script>
  </head>
  <body>

	<table id="dg"></table>
	<script type="text/javascript">
		$("#dg").datagrid({
			url : "${pageContext.request.contextPath}/UserServlet?time="+new Date().getTime(),
			columns :  [[    
					        	{field:'id',title:'編號',width:100},    
					        	{field:'name',title:'姓名',width:100},    
					        	{field:'sal',title:'薪水',width:100},
					        	{field:'sex',title:'性別',width:100}       
						]],
			fitColumns : true,
			singleSelect : true,
			pagination : true,
			pageSize : 2,
			pageList : [2,4]		    
		});
	</script>
  </body>
</html>

Servlet:

public class UserServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
		this.doPost(request,response);
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		
		//收集DataGrid向伺服器傳送的引數--page(當前頁號)
		String page = request.getParameter("page");
		System.out.println("page=" + page);
		//收集DataGrid向伺服器傳送的引數--rows(當前需要顯示的記錄數)
		String rows = request.getParameter("rows");
		System.out.println("rows=" + rows);
		
		List<User> userList = new ArrayList<User>();
		userList.add(new User(1,"哈哈",1000,"男"));
		userList.add(new User(2,"呵呵",2000,"男"));
		userList.add(new User(3,"嘻嘻",3000,"女"));
		userList.add(new User(4,"笨笨",4000,"男"));
		userList.add(new User(5,"聰聰",5000,"男"));
		userList.add(new User(6,"月月",6000,"女"));
		userList.add(new User(7,"花花",7000,"女"));
		
		Map<String,Object> map = new LinkedHashMap<String,Object>();
		map.put("total",userList.size());
		map.put("rows",userList);
		
		JSONArray jsonArray = JSONArray.fromObject(map);
		String jsonJAVA = jsonArray.toString();
		jsonJAVA = jsonJAVA.substring(1,jsonJAVA.length()-1);
		//以流的方式將JSON文字輸出到DateGrid元件中
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter pw = response.getWriter();
		pw.write(jsonJAVA);
		pw.flush();
		pw.close();
	}
}

4、使用Jsp +Js + Jquery + EasyUI + Servlet + Lucene,完成非同步分頁
① 建立ArticleDao.java類

public class ArticleDao {
	/**
	 * 根據關鍵字,獲取總記錄數
	 * @return 總記錄數
	 */
	public int getAllRecord(String keywords) throws Exception{
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,2);
		//返回符合條件的真實總記錄數,不受2的影響
		return topDocs.totalHits;
		//返回符合條件的總記錄數,受2的影響
		//return topDocs.scoreDocs.length;
	}
	/**
	 * 根據關鍵字,批量查詢記錄
	 * @param start 從第幾條記錄的索引號開始查詢,索引號從0開始
	 * @param size  最多查詢幾條記錄,不滿足最多數目時,以實際為準
	 * @return 集合
	 */
	public List<Article> findAll(String keywords,int start,int size) throws Exception{
		List<Article> articleList = new ArrayList<Article>();
		
		QueryParser queryParser = new QueryParser(LuceneUtil.getVersion(),"content",LuceneUtil.getAnalyzer());
		Query query = queryParser.parse(keywords);
		IndexSearcher indexSearcher = new IndexSearcher(LuceneUtil.getDirectory());
		TopDocs topDocs = indexSearcher.search(query,100);
		//小技巧
		int middle = Math.min(start+size,topDocs.totalHits);
		for(int i=start;i<middle;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;
			Document document = indexSearcher.doc(no);
			Article article = (Article) LuceneUtil.document2javabean(document,Article.class);
			articleList.add(article);
		}
		return articleList;
	}
}

② 建立Page.java類

public class Page {
	private Integer currPageNO;//當前頁號OK
	private Integer perPageSize = 2;//每頁顯示記錄數,預設為2條OK
	private Integer allRecordNO;//總記錄數OK
	private Integer allPageNO;//總頁數OK
	private List<Article> articleList = new ArrayList<Article>();//內容OK
	public Page(){}
	public Integer getCurrPageNO() {
		return currPageNO;
	}
	public void setCurrPageNO(Integer currPageNO) {
		this.currPageNO = currPageNO;
	}
	public Integer getPerPageSize() {
		return perPageSize;
	}
	public void setPerPageSize(Integer perPageSize) {
		this.perPageSize = perPageSize;
	}
	public Integer getAllRecordNO() {
		return allRecordNO;
	}
	public void setAllRecordNO(Integer allRecordNO) {
		this.allRecordNO = allRecordNO;
	}
	public Integer getAllPageNO() {
		return allPageNO;
	}
	public void setAllPageNO(Integer allPageNO) {
		this.allPageNO = allPageNO;
	}
	public List<Article> getArticleList() {
		return articleList;
	}
	public void setArticleList(List<Article> articleList) {
		this.articleList = articleList;
	}
}

③ 建立ArticleService.java類

public class ArticleService {
	//持久層
	private ArticleDao articleDao = new ArticleDao();
	/**
	 * 根據關鍵字和頁號,查詢內容
	 */
	public Page show(String keywords,int currPageNO) throws Exception{
		Page page = new Page();
		
		//封裝當前頁號
		page.setCurrPageNO(currPageNO);
		
		//封裝總記錄數
		int allRecordNO = articleDao.getAllRecord(keywords);
		page.setAllRecordNO(allRecordNO);
		
		//封裝總頁數
		int allPageNO = 0;
		if(page.getAllRecordNO() % page.getPerPageSize() == 0){
			allPageNO = page.getAllRecordNO() / page.getPerPageSize();
		}else{
			allPageNO = page.getAllRecordNO() / page.getPerPageSize() + 1;
		}
		page.setAllPageNO(allPageNO);
		
		//封裝內容
		int size = page.getPerPageSize();
		int start = (page.getCurrPageNO()-1) * size;
		List<Article> articleList = articleDao.findAll(keywords,start,size);
		page.setArticleList(articleList);
		return page;
	}
}

④ 建立ArticleServlet.java類

public class ArticleServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
		this.doPost(request,response);
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
		try {
			request.setCharacterEncoding("UTF-8");
			//獲取關鍵字
			String keywords = request.getParameter("keywords");//培訓
			if(keywords == null || keywords.trim().length()==0){
				keywords = "培訓";//預設值
			}
			
			//獲取當前頁號
			String temp = request.getParameter("page");//核心
			if(temp == null || temp.trim().length()==0){
				temp = "1";//預設值
			}
				
			//呼叫業務層
			ArticleService articleService = new ArticleService(); 
			Page page = articleService.show(keywords,Integer.parseInt(temp));
			
			//構造Map物件
			Map<String,Object> map = new LinkedHashMap<String,Object>();
			map.put("total",page.getAllRecordNO());
			map.put("rows",page.getArticleList());
			
			//第三方工具將Map轉成JSON
			JSONArray jsonArray = JSONArray.fromObject(map);
			String jsonJAVA = jsonArray.toString();
			
			//去掉二邊的[]符號
			jsonJAVA = jsonJAVA.substring(1,jsonJAVA.length()-1);
			
			//以IO的流方式響應到DataGrid元件
			response.setContentType("text/html;charset=UTF-8");
			PrintWriter pw = response.getWriter();
			pw.write(jsonJAVA);
			pw.flush();
			pw.close();
			
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
}

⑤ 匯入EasyUI相關的js包的目錄
⑥ 在WebRoot目錄下建立list.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>使用Jsp +Js + Jquery + EasyUI + Servlet + Lucene,完成分頁</title>
    <!-- 引入css檔案,無順序 -->
    <link rel="stylesheet" href="themes/icon.css" type="text/css"></link>
    <link rel="stylesheet" href="themes/default/easyui.css" type="text/css"></link>
  	<!-- 引入js檔案,有順序 -->
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/jquery.easyui.min.js"></script>
	<script type="text/javascript" src="js/easyui-lang-zh_CN.js"></script>
  </head>
  <body>

	<!-- 輸入區 -->
	<form id="myformID">
		輸入關鍵字:<input type="text" value="" id="keywordID"/>
		<input type="button" value="站內搜尋" id="findID"/>
	</form>
	<script type="text/javascript">
		//定位"站內搜尋"按鈕
		$("#findID").click(function(){
			//獲取關鍵字
			var keyword = $("#keywordID").val();
			//去空格
			keyword = $.trim(keyword);
			//判斷
			if(keyword.length == 0){
				//提示
				alert("請輸入關鍵字!!!");
				//清空文字框的內容
				$("#keywordID").val("");
				//定位於輸入關鍵字文字框
				$("#keywordID").focus();
			}else{
				//非同步傳送請求到伺服器
				//load表示方法名
				//"keywords"表示需要傳送的的引數名,後臺收:request.getParameter("keywords")
				//keyword表示引數值
				$("#dg").datagrid("load",{    
				    "keywords" : keyword
				});  
			}
		});
	</script>

	<!-- 顯示區 -->
	<table id="dg"></table>
	<script type="text/javascript">
		$("#dg").datagrid({
			url : "${pageContext.request.contextPath}/ArticleServlet?time="+new Date().getTime(),
			columns :  [[    
					        	{field:'id',title:'編號',width:100},    
					        	{field:'title',title:'標題',width:100},    
					        	{field:'content',title:'內容',width:100}
						]],
			fitColumns : true,
			singleSelect : true,
			pagination : true,
			pageSize : 2,
			pageList : [2]		    
		});
	</script>
  </body>
</html>