Lucene系列:LuceneUtils之同步分页

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lucene系列:LuceneUtils之同步分页相关的知识,希望对你有一定的参考价值。


1、工具类LuceneUtils

LuceneUtils.java

package com.rk.lucene.utils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
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.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.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.junit.Test;

import com.rk.lucene.entity.Article;
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);
			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 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) 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, Article.class);
			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;
	}

	//测试
	public static void main(String[] args) throws Exception {
		Article article = new Article(1, "你好", "欢迎来到我的世界");
		Document document = javabean2document(article);
		
		Article a2 = (Article) document2javabean(document, Article.class);
		System.out.println(a2);
	}
}



2、entity->dao->service->action


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 "Article [编号=" + id + ", 标题=" + title + ", 内容=" + content + "]";
	}
	
}

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.pagination(page, "content", 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 javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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 {
		try {
			request.setCharacterEncoding("UTF-8");
			//获取关键字(此处并不完善,没有对关键字的有效性进行校验)
			String keyword = request.getParameter("keyword");
			String strPageNo = request.getParameter("pageNo");
			
			//只有当页码真实存在时,才进行查询
			if(strPageNo != null && !"".equals(strPageNo)){
				int curPage = Integer.parseInt(strPageNo);
				//调用业务层
				ArticleService service = new ArticleService();
				Page<Article> page = service.pagination(keyword, curPage, 2);
				//将Page对象绑定到request域对象中
				request.setAttribute("PAGE", page);
			}
			//将keywords变量绑定到request域对象中
			request.setAttribute("KEYWORD", keyword);
			//转发到list.jsp页面
			request.getRequestDispatcher("/index.jsp").forward(request, response);
		} 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>lucene01</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
  	<servlet-name>ArticleServlet</servlet-name>
  	<servlet-class>com.rk.lucene.action.ArticleServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>ArticleServlet</servlet-name>
  	<url-pattern>/article</url-pattern>
  </servlet-mapping>
</web-app>


3、前台index.jsp页面

<%@ page language="java" import="java.util.*" 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>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
  </head>
  <body>
	<!-- 输入区 -->
	<form action="${pageContext.request.contextPath}/article" method="post">
		<input type="hidden" name="pageNo"/>
		<table border="1" align="center" style="border-collapse:collapse;">
			<tr>
				<th>输入关键字</th>
				<td><input type="text" name="keyword" value="${requestScope.KEYWORD}" maxlength="10"/> </td>
				<td><input id="search" type="button" value="站内搜索"/> </td>
			</tr>
		</table>
		<script type="text/javascript">
			//去空格
			function trim(str){
				//先去左边的空格
				str = str.replace(/^s*/, "");
				//后去右边的空格
				str = str.replace(/\s*$/, "");
				//返回
				return str;
			}
			
			//定位“站内搜索”按钮,同时提供单击事件
			document.getElementById("search").onclick = function(){
				//定位表单
				var formElement = document.forms[0];
				//获取关键字
				var keyword = formElement.keyword.value;
				//去空格
				keyword = trim(keyword);
				//判断长度
				if(keyword.length == 0){
					//提示
					alert("你没有填写关键字");
				}
				else{
					//提交表单
					formElement.pageNo.value = 1;
					formElement.submit();
				}
			};
		</script>
	</form>
	<c:if test="${not empty requestScope.PAGE }">
	<!-- 显示区 -->
	<table border="2" align="center" width="70%" style="border-collapse:collapse;">
		<tr>
			<th>编号</th>
			<th>标题</th>
			<th>内容</th>
		</tr>
		<c:forEach var="article" items="${requestScope.PAGE.items}">
			<tr>
				<td>${article.id}</td>
				<td>${article.title}</td>
				<td>${article.content }</td>
			</tr>
		</c:forEach>
		<tr>
			<th colspan="3" align="center">
				<c:choose>
					<c:when test="${requestScope.PAGE.curPage <= 1 }">
						首页
					</c:when>
					<c:otherwise>
						<a onclick="fy(1)" style="cursor:hand;color:blue;text-decoration:underline;">首页</a>
					</c:otherwise>
				</c:choose>
				<c:choose>
					<c:when test="${requestScope.PAGE.curPage <= 1 }">
						上一页
					</c:when>
					<c:otherwise>
						<a onclick="fy(${requestScope.PAGE.curPage-1})" style="cursor:hand;color:blue;text-decoration:underline;">上一页</a>
					</c:otherwise>
				</c:choose>
				<c:choose>
					<c:when test="${requestScope.PAGE.curPage >= requestScope.PAGE.totalPages }">
						下一页
					</c:when>
					<c:otherwise>
						<a onclick="fy(${requestScope.PAGE.curPage+1})" style="cursor:hand;color:blue;text-decoration:underline;">下一页</a>
					</c:otherwise>
				</c:choose>
				<c:choose>
					<c:when test="${requestScope.PAGE.curPage >= requestScope.PAGE.totalPages }">
						末页
					</c:when>
					<c:otherwise>
						<a onclick="fy(${requestScope.PAGE.totalPages})" style="cursor:hand;color:blue;text-decoration:underline;">末页</a>
					</c:otherwise>
				</c:choose>
				&nbsp;&nbsp;&nbsp;&nbsp;
				共${requestScope.PAGE.totalPages}页,当前第${requestScope.PAGE.curPage}页
			</th>
		</tr>
	</table>
	
	<script type="text/javascript">
		function fy(curPage){
			//定位表单
			var formElement = document.forms[0];
			//修改当前页号
			formElement.pageNo.value = curPage;
			//提交表单
			formElement.submit();
		}
	</script>
	</c:if>
  </body>
</html>


技术分享


遇到的一个问题
Exception in thread "main" org.apache.lucene.queryParser.ParseException: Encountered "<EOF>" at line 1, column 0.

可能是代码写错了。


错误的代码:

Query query = queryParser.Query(keyword);

正确的代码:

Query query = queryParser.parse(keyword);







以上是关于Lucene系列:LuceneUtils之同步分页的主要内容,如果未能解决你的问题,请参考以下文章

Lucene系列:LuceneUtils之索引库优化

Lucene系列:(10)多条件搜索 QueryParser

Lucene系列:(11)异步分页

Lucene学习:分组统计

《Lucene由潜入深教程系列》之Lucene入门实例

剖析Elasticsearch集群系列之二:分布式的三个Ctranslog和Lucene段