一、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>