Lucene全文检索基础
0赞我提倡“少理论,少模型”,“多实践”,“多应用”的学习态度,今天带来的是关于站内搜索的基础使用,目前检索这个领域有非常多优秀的框架了,但是身为一个全文检索领域的经典祖先,我们还是需要了解和掌握其精华的。本文主要内容有Lucene简介、索引库的建立、关键字搜索、检索分页、网站排名优化、分词、搜索结果高亮等。要求掌握其基本开发流程,并可以使用servlet+easyUI+lucene+jsp+js等技术做一个简易的站内搜索的功能模块。
一、简介
1.1 简介
Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包,由资深全文检索专家Doug Cutting所撰写,它是一个全文检索引擎的架构,提供了完整的创建索引和查询索引,以及部分文本分析的引擎,Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎,Lucene在全文检索领域是一个经典的祖先,现在很多检索引擎都是在其基础上创建的,思想是相通的。
即:Lucene是根据关健字来搜索的文本搜索工具,只能在某个网站内部搜索文本内容,不能跨网站搜索。
Lucene只能进行站内搜索。需要有索引库。
1.2 Lucene中存放的内容
Lucene中存的就是一系列的二进制压缩文件和一些控制文件,它们位于计算机的硬盘上,
这些内容统称为索引库,索引库有二部份组成:
(1)原始记录
存入到索引库中的原始文本,例如:传智是一家IT培训机构
(2)词汇表
按照一定的拆分策略(即分词器)将原始记录中的每个字符拆开后,存入一个供将来搜索的表
1.3 传统SQL的缺点
(1)SQL只能针对数据库表存储,不能直接针对硬盘上的文本搜索
(2)SQL没有相关度排名
(3)SQL搜索结果没有关健字高亮显示
(4)SQL需要数据库的支持,数据库本身需要内存开销较大,例如:Oracle
(5)SQL搜索有时较慢,尤其是数据库不在本地时,超慢,例如:Oracle
二、创建索引库
2.1 索引库创建
创建索引库:
1) 创建JavaBean对象
2) 创建Docment对象
3) 将JavaBean对象所有的属性值,均放到Document对象中去,属性名可以和JavaBean相同或不同
4) 创建IndexWriter对象
5) 将Document对象通过IndexWriter对象写入索引库中
6) 关闭IndexWriter对象
开发步骤:
1、新建一个javaweb工程,
导入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【索引库优化策略】
然后可以写javaBean对象,Article.java
1 2 3 | private Integer id; //索引号 private String title; //标题 private String content; //内容 |
get ,set方法,还有toString一下,有参和无参构造函数都要加上。
2、在demo1中邪一个方法,createIndexDB()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public void createIndexDB() throws IOException{ //创建article对象 Article at= new Article( 1 , "lucene" , "Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包" ); //创建Document对象 Document doc= new Document(); //将article对象中的3个属性分别绑定到document对象中 /* * * 参数1:document对象中的属性名叫xid,article对象中的属性名叫id * 参数2:document对象中的xid的值,与article对象中相同 * 参数3:是否将xid属性值存入词汇表,YES表示会存入,NO表示不会存入 * 参数4:是否将xid属性值进行分词,ANALYZED表示会进行词汇拆分 ,NOT_ANALYZED不会进行拆分 * * 项目中提倡非id值进行词汇拆分,提倡非id值存入词汇表 */ doc.add( new Field( "xid" ,at.getId().toString(),Store.YES,Index.ANALYZED)); doc.add( new Field( "xtitle" ,at.getTitle().toString(),Store.YES,Index.ANALYZED)); doc.add( new Field( "xcontent" ,at.getContent().toString(),Store.YES,Index.ANALYZED)); //将document对象写入lucene索引库 /* * 参数1:lucene索引库最终对应于硬盘中的目录 * 参数2:将文本拆分的策略,一个策略就是一个具体的实现类 * 参数3:最多将文本拆分的词汇,LIMITED表示1万个,只取前1万个 * */ Directory d=FSDirectory.open( new File( "D:/IndexDB" )); Analyzer a= new StandardAnalyzer(Version.LUCENE_30); MaxFieldLength mfl=MaxFieldLength.LIMITED; IndexWriter iw= new IndexWriter(d, a, mfl); iw.addDocument(doc); //关闭字符流对象 iw.close(); } |
2.2工具类创建
我们发现以上方法创建索引比较麻烦,所以我们可以封装一下,新建一个工具类LuceneUtil.java,在这个类中把以下四个内容进行封装,各自取get,set方法。
1 2 3 4 | private static Directory d; //索引库存放的目录 private static Version version; //版本号 private static Analyzer analyzer; //词汇拆分的策略 private static MaxFieldLength mfl; //最大分词数 |
用静态块来防止外界new该帮助类;
1 2 3 4 5 6 7 8 9 10 11 12 13 | //防止外界new该帮助类 static { try { d=FSDirectory.open( new File( "D:/IndexDB" )); version=Version.LUCENE_30; analyzer= new StandardAnalyzer(version); mfl=MaxFieldLength.LIMITED; } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } |
将需要的对象进行封装:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | //将javabean转成document对象 public static Document javabean2Document(Object obj) throws Exception{ 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 methodName= "get" +name.substring( 0 , 1 ).toUpperCase()+name.substring( 1 ); //获取方法 Method method=clazz.getMethod(methodName, null ); //执行方法 String value=method.invoke(obj, null ).toString(); //加入到document对象中去 document.add( new Field(name,value,Store.YES,Index.ANALYZED)); } //返回document对象 return document; } //将document转成avabean对象 public static Object document2Javabean(Document document, Class clazz) throws InstantiationException, IllegalAccessException, InvocationTargetException{ Object obj=clazz.newInstance(); java.lang.reflect.Field[] reflectFields = clazz.getDeclaredFields(); for (java.lang.reflect.Field field : reflectFields){ field.setAccessible( true ); String name= field.getName(); String value = document.get(name); BeanUtils.setProperty(obj,name,value); } return obj; } |
2.3 使用工具类对索引库的增删改
以上步骤都完成后,就可以直接用工具类来对索引库进行操作了
创建单个索引库
1 2 3 4 5 6 7 8 9 10 | @Test //创建索引库 public void createIndexDB() throws Exception{ Article at= new Article( 1 , "lucene" , "Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包" ); //创建Document对象 Document doc=LuceneUtil.javabean2Document(at); IndexWriter iw= new IndexWriter(LuceneUtil.getD(), LuceneUtil.getAnalyzer(), LuceneUtil.getMfl()) ; iw.addDocument(doc); iw.close(); } |
创建多个索引对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Test public void addAll() throws Exception{ IndexWriter iw= new IndexWriter(LuceneUtil.getD(), LuceneUtil.getAnalyzer(), LuceneUtil.getMfl()); Article at1= new Article( 1 , "lucene" , "Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包" ); Document document1=LuceneUtil.javabean2Document(at1); iw.addDocument(document1); Article at2= new Article( 2 , "lucene" , "Lucene是根据关健字来搜索的文本搜索工具" ); Document document2=LuceneUtil.javabean2Document(at2); iw.addDocument(document2); Article at3= new Article( 3 , "lucene" , "Lucene在全文检索领域是一个经典的祖先,现在很多检索引擎都是在其基础上创建的,思想是相通的" ); Document document3=LuceneUtil.javabean2Document(at3); iw.addDocument(document3); Article at4= new Article( 4 , "lucene" , "Lucene它是一个全文检索引擎的架构,提供了完整的创建索引和查询索引" ); Document document4=LuceneUtil.javabean2Document(at4); iw.addDocument(document4); iw.close(); } |
修改索引、
1 2 3 4 5 6 7 8 9 | @Test public void update() throws Exception{ Article newArticle= new Article( 4 , "lucene" , "4是一个全文检索引擎的架构,提供了完整的创建索引和查询索引" ); Document document=LuceneUtil.javabean2Document(newArticle); IndexWriter iw= new IndexWriter(LuceneUtil.getD(), LuceneUtil.getAnalyzer(), LuceneUtil.getMfl()); iw.updateDocument( new Term( "id" , "4" ), document); iw.close(); } |
查询:
1 2 3 4 | @Test public void findAll() throws Exception{ String keywords= "是" ; List |
根据关键字查询索引库中的内容: 1) 创建IndexSearcher对象 2) 创建QueryParser对象 3) 创建Query对象来封装关键字 4) 用IndexSearcher对象去索引库中查询符合条件的前100条记录,不足100条记录的以实际为准 5) 获取符合条件的编号 6) 用indexSearcher对象去索引库中查询编号对应的Document对象 7) 将Document对象中的所有属性取出,再封装回JavaBean对象中去,并加入到集合中保存,以备将之用 传统的搜索方式: 单字段: 多字段 采用一种算法,将中英文本中的字符拆分开来,形成词汇,以待用户输入关健字后搜索 使用分词器的原因 分词器工作流程 导入IKAnalyzer分词器核心jar包,IKAnalyzer3.2.0Stable.jar 将IKAnalyzer.cfg.xml和stopword.dic和mydict.dic文件复制到MyEclipse的src目录下, 再进行配置,在配置时,首行需要一个空行。 mydict.dic内容如下:首行空出来: stopword.dic内容如下,首行空出来 //设置合并因子,即满足3个cfs文件合并 iw.addDocument(doc); iw.setMergeFactor(3); 索引库 内存索引库 硬盘索引库 Lucene中的显示结果次序与相关度得分有关 ScoreDoc.score; 默认情况下,Lucene是按相关度得分排序的,得分高排在前,得分低排在后 如果相关度得分相同,按插入索引库的先后次序排序。 强行提高得分 doc.setBoost(100F); 搜索结果是按某个或某些字段高低排序来显示的结果. 按单个字段排序 按多个字段排序 同之前的Artucle.java 同时还要一个分页的bean,进行封装 新建ArticleDao.java 新建一个ArticleService.java ,对分页进行处理 使用一个servlet来对数据进行控制。doGet和doPost方法中进行处理。 我们使用一个jsp来书写界面。先把我们需要的js包,jquery,easyui相关的jar导入到项目的lib目录中,然后在这个jsp文件中引入我们所需要的文件。命名为list.jsp. 在这里就暂时假设你已经学会使用easyui了,如果对使用easyui还有什么问题的,欢迎查看我的下一篇文章。 源码下载地址:https://github.com/sdksdk0/lucene (基础部分,前两章节) https://github.com/sdksdk0/lucene2 (应用部分,后四章节的内容) 小结:这里对于lucene的使用都是比较基础的部分,重要的要是学会基本的编码流程,开发思想,学以致用,做一个有思想有深度的人,不要变成一个只会敲代码的机器。 http://xianhuo.dzsc.com/A
删除@Test
public
void
delete()
throws
LockObtainFailedException, IOException{
IndexWriter iw=
new
IndexWriter(LuceneUtil.getD(), LuceneUtil.getAnalyzer(), LuceneUtil.getMfl());
iw.deleteDocuments(
new
Term(
"id"
,
"2"
));
iw.close();
}
public
void
deleteAll()
throws
CorruptIndexException, LockObtainFailedException, IOException{
IndexWriter indexWriter =
new
IndexWriter(LuceneUtil.getD(),LuceneUtil.getAnalyzer(),LuceneUtil.getMfl());
indexWriter.deleteAll();
indexWriter.close();
}
三、关键字搜索
/**
* 根据关键字从索引库中搜索符合条件的记录
*
* @throws IOException
* @throws ParseException
*
*/
@Test
public
void
findIndexDB()
throws
IOException, ParseException{
//搜索"源"这个字
String keyword=
"apache"
;
List
使用工具类:其实就是前面那一节里面的查询。//根据关键字查询
@Test
public
void
findIndexDB()
throws
Exception {
String keywords=
"是"
;
List
单字段和多字段的搜索
<code>QueryParser queryParser =
new
QueryParser(LuceneUtil.getVersion(),
"content"
,LuceneUtil.getAnalyzer());
</code>
<code>QueryParser queryParser =
new
MultiF</code>
搜索结果高亮显示
<code>
//格式对象
Formatter formatter =
new
SimpleHTMLFormatter(
"<font color="
red
">"
,
"</font>"
);
//关键字对象
Scorer scorer =
new
QueryScorer(query);
//高亮对象
Highlighter highlighter =
new
Highlighter(formatter,scorer);
</code>
四、分词器
<code> 因为用户输入的搜索的内容是一段文本中的一个关健字,和原始表中的内容有差别,
但作为搜索引擎来讲,又得将相关的内容搜索出来,此时就得采用分词器来最大限度
匹配原始表中的内容
</code>
<code> 步一:按分词器拆分出词汇
步二:去除停用词和禁用词
步三:如果有英文,把英文字母转为小写,即搜索不分大小写
</code>
//测试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 {
testAnalyzer(
new
StandardAnalyzer(LuceneUtil.getVersion()),
"采用一种算法aa,将中英文本中的字符拆分开来,形成词汇,以待用户输入关健字后搜索"
);
testAnalyzer(
new
FrenchAnalyzer(LuceneUtil.getVersion()),
"采用一种算法aa,将中英文本中的字符拆分开来,形成词汇,以待用户输入关健字后搜索"
);
testAnalyzer(
new
CJKAnalyzer(LuceneUtil.getVersion()),
"采用一种算法aa,将中英文本中的字符拆分开来,形成词汇,以待用户输入关健字后搜索"
);
testAnalyzer(
new
IKAnalyzer(),
"指令汇科技实业呀"
);
}
}
IK分词器
testAnalyzer(
new
IKAnalyzer(),
"指令汇科技实业呀"
);
<!--?xml version=
"1.0"
encoding=
"UTF-8"
?-->
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!-- 用户可以在这里配置自己的扩展字典 -->
<entry key=
"ext_dict"
>/mydict.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典 -->
<entry key=
"ext_stopwords"
>/surname.dic</entry>
</properties>
指令汇科技
哦
嗯
呀 五、搜索排名优化
5.1 索引库优化
//合并.cfs文件,解决文件的大小和数量问题
@Test
public
void
type1()
throws
Exception{
Article at=
new
Article(
1
,
"lucene"
,
"Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包"
,
10
);
//创建Document对象
Document doc=LuceneUtil.javabean2Document(at);
IndexWriter iw=
new
IndexWriter(LuceneUtil.getD(),LuceneUtil.getAnalyzer(),LuceneUtil.getMfl());
iw.optimize();
iw.close();
}
//合并.cfs文件,解决文件的大小和数量问题
@Test
public
void
type2()
throws
Exception{
Article at=
new
Article(
2
,
"lucene"
,
"Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包"
,
10
);
//创建Document对象
Document doc=LuceneUtil.javabean2Document(at);
IndexWriter iw=
new
IndexWriter(LuceneUtil.getD(),LuceneUtil.getAnalyzer(),LuceneUtil.getMfl());
//设置合并因子,即满足3个cfs文件合并
iw.addDocument(doc);
iw.setMergeFactor(
3
);
iw.close();
}*/
//合并.cfs文件,解决文件的大小和数量问题
@Test
public
void
type4()
throws
Exception{
Article at=
new
Article(
2
,
"lucene"
,
"Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包"
,
10
);
Document doc=LuceneUtil.javabean2Document(at);
//硬盘索引库
Directory fsDirectory=FSDirectory.open(
new
File(
"D:/IndexDB"
));
//内存索引库
Directory ramDirectory=
new
RAMDirectory(fsDirectory);
//指向硬盘索引库的字符流,true表示如果内存索引库中和硬盘索引库中有系统的document对象时,先删除硬盘索引库的值
IndexWriter fsIw=
new
IndexWriter(fsDirectory,LuceneUtil.getAnalyzer(),
true
,LuceneUtil.getMfl());
//指向内存索引库的字符流
IndexWriter ramIw=
new
IndexWriter(ramDirectory,LuceneUtil.getAnalyzer(),LuceneUtil.getMfl());
//将document对象写入内存索引库
ramIw.addDocument(doc);
ramIw.close();
//将内存索引库中所有的document对象同步到硬盘索引中
fsIw.addIndexesNoOptimize(ramDirectory);
fsIw.close();
}
5.2 相关度得分排序
//增加document对象索引库中
@Test
public
void
add()
throws
Exception{
//Article at=new Article(1, "lucene","Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包",10);
//Article at=new Article(2, "lucene2","Lucene2是apache软件基金会发布的一个开放源代码的全文检索引擎工具包",20);
Article at=
new
Article(
3
,
"新lucene3"
,
"Lucene3是apache软件基金会发布的一个开放源代码的全文检索引擎工具包"
,
30
);
//Article at=new Article(4, "lucene4","Lucene4是是软件基金会发布是时的一个开放源是代码的全文是检索引擎是工具包",30);
//Article at=new Article(5, "lucene5","是是是是是是是是是是是是是是是是是是是是是是是是是是是是是是是是是是是是是是",30);
//创建Document对象
Document doc=LuceneUtil.javabean2Document(at);
IndexWriter iw=
new
IndexWriter(LuceneUtil.getD(),LuceneUtil.getAnalyzer(),LuceneUtil.getMfl());
doc.setBoost(20F);
iw.addDocument(doc);
iw.close();
}
<code>
//获取document对象的评分
float
score=sd.score;
</code>
5.3 字段排序
//创建排序的条件
/*
* 参数1:id表示依据document对象中的那个字段排序,里如id
* 参数2:sortField.INT表示document对象中该字段的类型,
* 参数3:true表示降序,类似于order by
*
*
*/
Sort sort=
new
Sort(
new
SortField(
"id"
,SortField.INT,
true
));
TopDocs td=is.search(query,
null
,
100
,sort);
//创建排序的条件
/*
* 参数1:id表示依据document对象中的那个字段排序,里如id
* 参数2:sortField.INT表示document对象中该字段的类型,
* 参数3:true表示降序,类似于order by
*
*
*/
Sort sort=
new
Sort(
new
SortField(
"count"
,SortField.INT,
true
),
new
SortField(
"count"
,SortField.INT,
true
));
TopDocs td=is.search(query,
null
,
100
,sort);
六、分页(实现站内搜索功能)
6.1 Bean
private
Integer id;
//索引号
private
String title;
//标题
private
String content;
//内容
get ,set方法,还有toString一下,有参和无参构造函数都要加上。private
Integer currPageNO;
//当前页号
private
Integer perPageSize =
3
;
//每页显示记录数,默认为2条
private
Integer allRecordNO;
//总记录数OK
private
Integer allPageNO;
//总页数OK
private
List
6.2 持久层
//根据关键字,获取总记录数,返回总的记录数
public
int
getAllRecord(String keywords)
throws
Exception{
List<Article> atl=
new
ArrayList<Article>();
QueryParser queryParser=
new
QueryParser(LuceneUtil.getVersion(),
"content"
,LuceneUtil.getAnalyzer());
Query query=queryParser.parse(keywords);
IndexSearcher is=
new
IndexSearcher(LuceneUtil.getD());
TopDocs td=is.search(query,
5
);
//返回符合条件的真实总记录数,不受search(query,5)中定义的5的影响
//return td.totalHits; //17条
return
td.scoreDocs.length;
//5条
}
//根据关键字,批量查询记录
/*
* start:从第几条记录的索引号开始查询,索引号从0开始
* size:最多查询几条记录,不满足最多数目时,以实际为准
* 返回集合
*/
public
List<Article> findAll(String keywords,
int
start,
int
size)
throws
Exception{
List<Article> atl=
new
ArrayList<Article>();
QueryParser queryParser=
new
QueryParser(LuceneUtil.getVersion(),
"content"
,LuceneUtil.getAnalyzer());
Query query=queryParser.parse(keywords);
IndexSearcher is=
new
IndexSearcher(LuceneUtil.getD());
TopDocs td=is.search(query,
100
);
int
middle=Math.min(start+size,td.totalHits);
for
(
int
i=start;i<middle;i++){
ScoreDoc sd=td.scoreDocs[i];
int
no=sd.doc;
Document document=is.doc(no);
Article at=(Article) LuceneUtil.document2Javabean(document, Article.
class
);
atl.add(at);
}
return
atl;
}
6.3、业务层
private
ArticleDao articleDao=
new
ArticleDao();
//根据关键字和页号,查询内容
public
PageBean show(String keywords,
int
currPageNO)
throws
Exception{
PageBean page=
new
PageBean();
//封装当前页号
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
6.4、控制器
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"
);
try
{
//获取关键字
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();
PageBean page=articleService.show(keywords,Integer.parseInt(temp));
//构造map对象
Map<string,object> map=
new
LinkedHashMap<string, object=
""
>();
map.put(
"total"
, page.getAllPageNO());
map.put(
"rows"
, page.getArticleList());
//将map转成json
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();
}
catch
(Exception e) {
e.printStackTrace();
throw
new
RuntimeException(e);
}
}</string,></string,object>
6.5 视图(界面处理)
<!-- 引入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>
界面效果如下。<!-- 输入区 -->
<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}/servlet/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>