使用到的jar包,分爲4部分: |
(1)beanutilsjavascript commons-beanutils-1.9.2.jarcss commons-collections-3.2.1.jarhtml commons-logging-1.1.1.jarjava (2)gsonjquery gson-2.6.2.jarweb (3)IK Analyzerapache IKAnalyzer3.2.0Stable.jarjson (4)lucene服務器 lucene-analyzers-3.0.2.jarapp lucene-core-3.0.2.jar lucene-highlighter-3.0.2.jar lucene-memory-3.0.2.jar |
一、LuceneUtils
LuceneUtils.java
package com.rk.lucene.utils; import java.io.File; import java.util.ArrayList; import java.util.List; import org.apache.commons.beanutils.BeanUtils; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.Field.Index; import org.apache.lucene.document.Field.Store; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.index.IndexWriter.MaxFieldLength; import org.apache.lucene.queryParser.MultiFieldQueryParser; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.highlight.Formatter; import org.apache.lucene.search.highlight.Fragmenter; import org.apache.lucene.search.highlight.Highlighter; import org.apache.lucene.search.highlight.QueryScorer; import org.apache.lucene.search.highlight.Scorer; import org.apache.lucene.search.highlight.SimpleFragmenter; import org.apache.lucene.search.highlight.SimpleHTMLFormatter; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.util.Version; import org.wltea.analyzer.lucene.IKAnalyzer; import com.rk.lucene.entity.Page; public class LuceneUtils { private static Directory directory; private static Version version; private static Analyzer analyzer; private static MaxFieldLength maxFieldLength; private static final String LUCENE_DIRECTORY= "D:/rk/indexDB"; static{ try { directory = FSDirectory.open(new File(LUCENE_DIRECTORY)); version = Version.LUCENE_30; //analyzer = new StandardAnalyzer(version); analyzer = new IKAnalyzer(); maxFieldLength = MaxFieldLength.LIMITED; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } //不讓外部new當前幫助類的對象 private LuceneUtils(){} public static <T> void pagination(Page<T> page,String field,String keyword,Class<T> clazz) throws Exception{ QueryParser queryParser = new QueryParser(getVersion(), field, getAnalyzer()); Query query = queryParser.parse(keyword); IndexSearcher indexSearcher = new IndexSearcher(getDirectory()); TopDocs topDocs = indexSearcher.search(query, 200); int totalHits = topDocs.totalHits; int curPage = page.getCurPage(); int pageSize = page.getPageSize(); int quotient = totalHits / pageSize; int remainder = totalHits % pageSize; int totalPages = remainder==0 ? quotient : quotient+1; int startIndex = (curPage-1) * pageSize; int stopIndex = Math.min(startIndex + pageSize, totalHits); List<T> list = page.getItems(); if(list == null){ list = new ArrayList<T>(); page.setItems(list); } list.clear(); for(int i=startIndex;i<stopIndex;i++){ ScoreDoc scoreDoc = topDocs.scoreDocs[i]; int docIndex = scoreDoc.doc; Document document = indexSearcher.doc(docIndex); T t = document2javabean(document, clazz); list.add(t); } page.setTotalPages(totalPages); page.setTotalItems(totalHits); indexSearcher.close(); } public static <T> void paginationWithHighlighter(Page<T> page,String keyword,Class<T> clazz) throws Exception{ QueryParser queryParser = new MultiFieldQueryParser(getVersion(), new String[]{"title","content"}, analyzer); Query query = queryParser.parse(keyword); IndexSearcher indexSearcher = new IndexSearcher(getDirectory()); TopDocs topDocs = indexSearcher.search(query, 200); int totalHits = topDocs.totalHits; int curPage = page.getCurPage(); int pageSize = page.getPageSize(); int quotient = totalHits / pageSize; int remainder = totalHits % pageSize; int totalPages = remainder==0 ? quotient : quotient+1; int startIndex = (curPage-1) * pageSize; int stopIndex = Math.min(startIndex + pageSize, totalHits); List<T> list = page.getItems(); if(list == null){ list = new ArrayList<T>(); page.setItems(list); } list.clear(); Formatter formatter = new SimpleHTMLFormatter("<font color='red'>", "</font>"); Scorer scorer = new QueryScorer(query); Highlighter titleHighlighter = new Highlighter(formatter, scorer); Highlighter contentHighlighter = new Highlighter(formatter, scorer); int MAX_CONTENT_LENGTH = 30; Fragmenter contentFragmenter = new SimpleFragmenter(MAX_CONTENT_LENGTH); contentHighlighter.setTextFragmenter(contentFragmenter); for(int i=startIndex;i<stopIndex;i++){ ScoreDoc scoreDoc = topDocs.scoreDocs[i]; int docIndex = scoreDoc.doc; Document document = indexSearcher.doc(docIndex); String titleValue = titleHighlighter.getBestFragment(LuceneUtils.getAnalyzer(), "title", document.get("title")); if(titleValue == null) titleValue = document.get("title"); String contentValue = contentHighlighter.getBestFragment(LuceneUtils.getAnalyzer(), "content", document.get("content")); if(contentValue == null) contentValue = document.get("content").substring(0, MAX_CONTENT_LENGTH); document.getField("title").setValue(titleValue); document.getField("content").setValue(contentValue); T t = document2javabean(document, clazz); list.add(t); } page.setTotalPages(totalPages); page.setTotalItems(totalHits); indexSearcher.close(); } public static <T> void add(T t) throws Exception{ Document document = javabean2document(t); IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength()); indexWriter.addDocument(document); indexWriter.close(); } public static <T> void addAll(List<T> list) throws Exception{ IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength()); for(T t : list){ Document doc = javabean2document(t); indexWriter.addDocument(doc); } indexWriter.close(); } public static <T> void update(String field,String value,T t) throws Exception{ Document document = javabean2document(t); IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength()); indexWriter.updateDocument(new Term(field,value), document); indexWriter.close(); } public static <T> void delete(String field,String value) throws Exception{ IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength()); indexWriter.deleteDocuments(new Term(field,value)); indexWriter.close(); } /** * 刪除全部記錄 */ public static void deleteAll() throws Exception { IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength()); indexWriter.deleteAll(); indexWriter.close(); } /** * 根據關鍵字進行搜索 */ public static <T> List<T> search(String field,String keyword,int topN,Class<T> clazz) throws Exception{ List<T> list = new ArrayList<T>(); QueryParser queryParser = new QueryParser(getVersion(), field, getAnalyzer()); Query query = queryParser.parse(keyword); IndexSearcher indexSearcher = new IndexSearcher(getDirectory()); TopDocs topDocs = indexSearcher.search(query, topN); for(int i=0;i<topDocs.scoreDocs.length;i++){ ScoreDoc scoreDoc = topDocs.scoreDocs[i]; int docIndex = scoreDoc.doc; System.out.println("文檔索引號" + docIndex + ",文檔得分:" + scoreDoc.score); Document document = indexSearcher.doc(docIndex); T entity = (T) document2javabean(document, clazz); list.add(entity); } indexSearcher.close(); return list; } /** * 打印List */ public static <T> void printList(List<T> list){ if(list != null && list.size()>0){ for(T t : list){ System.out.println(t); } } } //將JavaBean轉成Document對象 public static Document javabean2document(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 reflectField : reflectFields){ //反射 reflectField.setAccessible(true); //獲取字段名 String name = reflectField.getName(); //獲取字段值 String value = reflectField.get(obj).toString(); //加入到Document對象中去,這時javabean的屬性與document對象的屬性相同 document.add(new Field(name, value, Store.YES, Index.ANALYZED)); } //返回document對象 return document; } //將Document對象轉換成JavaBean對象 public static <T> T document2javabean(Document document,Class<T> clazz) throws Exception{ T obj = clazz.newInstance(); java.lang.reflect.Field[] reflectFields = clazz.getDeclaredFields(); for(java.lang.reflect.Field reflectField : reflectFields){ reflectField.setAccessible(true); String name = reflectField.getName(); String value = document.get(name); BeanUtils.setProperty(obj, name, value); } return obj; } public static Directory getDirectory() { return directory; } public static void setDirectory(Directory directory) { LuceneUtils.directory = directory; } public static Version getVersion() { return version; } public static void setVersion(Version version) { LuceneUtils.version = version; } public static Analyzer getAnalyzer() { return analyzer; } public static void setAnalyzer(Analyzer analyzer) { LuceneUtils.analyzer = analyzer; } public static MaxFieldLength getMaxFieldLength() { return maxFieldLength; } public static void setMaxFieldLength(MaxFieldLength maxFieldLength) { LuceneUtils.maxFieldLength = maxFieldLength; } }
Page.java
package com.rk.lucene.entity; import java.util.ArrayList; import java.util.List; public class Page<T> { private Integer totalItems; private Integer totalPages; private Integer pageSize; private Integer curPage; private List<T> items = new ArrayList<T>(); public Page() { } public Integer getTotalItems() { return totalItems; } public void setTotalItems(Integer totalItems) { this.totalItems = totalItems; } public Integer getTotalPages() { return totalPages; } public void setTotalPages(Integer totalPages) { this.totalPages = totalPages; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getCurPage() { return curPage; } public void setCurPage(Integer curPage) { this.curPage = curPage; } public List<T> getItems() { return items; } public void setItems(List<T> items) { this.items = items; } }
準備測試數據
package com.rk.lucene.g_search; import java.util.ArrayList; import java.util.List; import org.apache.lucene.document.Document; import org.apache.lucene.queryParser.MultiFieldQueryParser; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.junit.Test; import com.rk.lucene.entity.Article; import com.rk.lucene.utils.LuceneUtils; public class PrepareData { @Test public void testAdd() throws Exception{ List<Article> list = new ArrayList<Article>(); list.add(new Article(1, "疾風之刃", "《疾風之刃》是一款超動做3D動漫風網遊。做爲新一代動做遊戲,《疾風之刃》呈現出極致華麗的動做表演,精心打磨出的打擊感震撼人心。")); list.add(new Article(2, "月光疾風", "月光疾風,日本動漫《火影忍者》中的人物,比較我的主義,性格溫和。火之國木葉村的特別上忍,中忍考試正賽預選的考官,體質彷佛很很差,有着嚴重的黑眼圈、臉色蒼白且常常咳嗽,善用劍術。")); list.add(new Article(3, "疾風航班中文版下載", "《疾風航班》是一款優質的動做模擬遊戲。遊戲中包括亞歐美洲,乃至飛往太空的5條航線,共計50個按部就班的關卡,以及具備挑戰性的Expert級別評定,每一個關卡結束後還可進入商店對主角和飛機進...")); list.add(new Article(4, "八神疾風", "八神疾風(CV:植田佳奈)是日本動漫《魔法少女奈葉A's》首次登場的女角色。暗之書事件中心人物,時空管理局魔導師,擅長貝爾卡式廣域·遠程魔法。")); list.add(new Article(5, "逝去的疾風", "大戰中飛得最快的日本飛機,恐怕要數「疾風」戰鬥機了,它由中島飛機廠研製生產,制式型號爲: 四式單(座)戰(鬥機),代號キ-84(讀做 Ki-84)。")); list.add(new Article(6, "疾風劍豪 亞索", "亞索是一個百折不屈的男人,仍是一名身手敏捷的劍客,可以運用風的力量來斬殺敵人。這位曾經春風得意的戰士由於誣陷而身敗名裂,而且被迫捲入了一場使人絕望的生存之...")); list.add(new Article(7, "疾風知勁草", "疾風知勁草,謂在猛烈的大風中,可看出什麼樣的草是強勁的。比喻意志堅決,經得起考驗。出自《東觀漢記·王霸傳》:「上謂霸曰:‘潁川從我者皆逝,而子獨留,始驗疾風知勁草。...")); LuceneUtils.addAll(list); } }
二、entity->dao->service->action
2.一、entity層
Article.java
package com.rk.lucene.entity; 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; } @Override public String toString() { return "編號: " + id + "\n標題: " + title + "\n內容: " + content + "\n------------------------------------------------------------------\n"; } }
2.二、dao層
ArticleDao.java
package com.rk.lucene.dao; import com.rk.lucene.entity.Article; import com.rk.lucene.entity.Page; import com.rk.lucene.utils.LuceneUtils; public class ArticleDao { public void pagination(Page<Article> page,String keyword) throws Exception{ LuceneUtils.paginationWithHighlighter(page, keyword, Article.class); } }
ArticleService.java
package com.rk.lucene.service; import com.rk.lucene.dao.ArticleDao; import com.rk.lucene.entity.Article; import com.rk.lucene.entity.Page; public class ArticleService { private ArticleDao dao = new ArticleDao(); public Page<Article> pagination(String keyword,int curPage,int pageSize) throws Exception { Page<Article> page = new Page<Article>(); page.setCurPage(curPage); page.setPageSize(pageSize); dao.pagination(page, keyword); return page; } }
ArticleServlet.java
package com.rk.lucene.action; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.rk.lucene.entity.Article; import com.rk.lucene.entity.Page; import com.rk.lucene.service.ArticleService; public class ArticleServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Map<String,Object> map = new HashMap<String, Object>(); try { request.setCharacterEncoding("UTF-8"); //獲取關鍵字 String keyword = request.getParameter("keyword"); String strPageNo = request.getParameter("page"); String strRows = request.getParameter("rows"); //只有當頁碼真實存在時,才進行查詢 if(keyword != null && keyword.length()>0){ int curPage = Integer.parseInt(strPageNo); int rows = Integer.parseInt(strRows); //調用業務層 ArticleService service = new ArticleService(); Page<Article> page = service.pagination(keyword, curPage, rows); map.put("total", page.getTotalItems()); map.put("rows", page.getItems()); } else{ map.put("total", 0); map.put("rows", "[]"); } //以IO的流方式響應到DataGrid組件 Gson gson = new GsonBuilder().create(); String strJson = gson.toJson(map); response.setContentType("application/json;charset=UTF-8"); PrintWriter pw = response.getWriter(); pw.write(strJson); pw.flush(); pw.close(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } }
web.xml中註冊ArticleServlet
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>lucene02</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>article</servlet-name> <servlet-class>com.rk.lucene.action.ArticleServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>article</servlet-name> <url-pattern>/article</url-pattern> </servlet-mapping> </web-app>
三、前臺JSP
在頁面當中使用到了jQuery easyUI
將標註的文件和文件夾引入到Web項目中
index.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" type="text/css" href="${pageContext.request.contextPath}/js/easyui/themes/icon.css"></link> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/js/easyui/themes/default/easyui.css"></link> <!-- 引入js文件,有順序 --> <script type="text/javascript" src="${pageContext.request.contextPath}/js/easyui/jquery.min.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath}/js/easyui/jquery.easyui.min.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath}/js/easyui/locale/easyui-lang-zh_CN.js"></script> <script type="text/javascript"> $(document).ready(function(){ $('#dg').datagrid({ url:"${pageContext.request.contextPath}/article?time="+new Date().getTime(), columns:[[ {field:'id',title:'編號',width:30}, {field:'title',title:'標題',width:100}, {field:'content',title:'內容',width:100} ]], fitColumns:true, singleSelect:true, pagination:true, pageSize:2, pageList:[2,4] }); //定位「站內搜索」按鈕 $('#btnSearch').click(function(){ //獲取關鍵字 var keyword = $('#keyword').val(); //去空格 keyword = $.trim(keyword); if(keyword.length == 0){ //提示 alert("請輸入關鍵字!!!"); //清空文本框的內容 $('#keyword').val(""); //定位於輸入關鍵字文本框 $('#keyword').focus(); } else{ //異步發送請求到服務器 //load表示方法名 //"keyword"表示須要發送的的參數名,後臺收:request.getParameter("keyword") //keyword表示參數值 $('#dg').datagrid("load", { "keyword":keyword }); } }); }); </script> </head> <body> <!-- 輸入區 --> <form id="myFormId"> 輸入關鍵字: <input id="keyword" type="text" value=""/> <input id="btnSearch" type="button" value="站內搜索"/> </form> <!-- 顯示區 --> <table id="dg"></table> </body> </html>