之前我们已经将索引的创建基本说明了,下面我们看搜索的相关方法(工程lucene_searcher01)
一、精确搜素TermQuery
这里我们想给出相关代码:
SearcherUtil.java
package cn.itcast.searcher;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
public class SearcherUtil {
private Directory directory;
private IndexReader reader;
private String[] ids = { "1", "2", "3", "4", "5", "6" };
private String[] emails = { "aa@qq.com", "bb@sina.edu", "cc@yahu.org",
"ss@sina.com", "dd@gmail.com", "ee@163.com" };
private String[] content = { "welcom to visited the space,I like football",
"hello boy, i like someone", "come on baby", "first blood",
"I like football,I like football",
"my girlfriend is so beatiful, every body like game" };
private int[] attaches = { 2, 5, 6, 5, 8, 4 };
private String[] names = { "tom", "jack", "goudan", "alibaba", "jerry", "kitty" };
private Date[] dates = null;
private Map<String, Float> scores = new HashMap<String, Float>();// 新建一个Map,用来存储权值
public SearcherUtil(){
directory = new RAMDirectory();
setDates();
index();
}
public IndexSearcher getSearcher(){
try {
if (reader == null) {
reader = IndexReader.open(directory);
}else{
IndexReader tr = IndexReader.openIfChanged(reader);
if(tr != null){
reader.close();
reader = tr;
}
}
return new IndexSearcher(reader);
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//精确匹配查询
public void searchByTerm(String field, String name, int num){
IndexSearcher searcher = getSearcher();
Query query = new TermQuery(new Term(field, name));
try {
TopDocs tds = searcher.search(query, num);//TopDocs封装搜索结果以及 ScoreDoc 的总数
System.out.println("总共查询了: " + tds.totalHits);
for(ScoreDoc sd : tds.scoreDocs){//ScoreDoc提供对TopDocs中每条搜索结果的访问接口
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
}
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void index() {
IndexWriter writer = null;
try {
writer = new IndexWriter(directory, new IndexWriterConfig(
Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)));
// 此方法可将索引全部清空
writer.deleteAll();
Document document = null;
for (int i = 0; i < ids.length; i++) {
document = new Document();
// id需要存储,不需要加权、分词,email也需要存储,但不需要分词,有时候也需要加权
// 对于内容,我们不需要存储和加权,但需要分词。而名字需要存储,不需要分词和加权
// 这里我们先不对整型数据进行索引,后面再说
document.add(new Field("id", ids[i], Field.Store.YES,
Field.Index.NOT_ANALYZED_NO_NORMS));
document.add(new Field("email", emails[i], Field.Store.YES,
Field.Index.NOT_ANALYZED));
document.add(new Field("content", content[i], Field.Store.NO,
Field.Index.ANALYZED));
document.add(new Field("name", names[i], Field.Store.YES,
Field.Index.NOT_ANALYZED_NO_NORMS));
// 为数字添加索引,第三个参数设置为true表示默认索引
document.add(new NumericField("attach", Field.Store.YES, true)
.setIntValue(attaches[i]));
// 为日期添加索引
document.add(new NumericField("date", Field.Store.YES, true)
.setLongValue(dates[i].getTime()));
String et = emails[i].substring(emails[i].lastIndexOf("@") + 1);
// 加入权值
if (scores.containsKey(et)) {
document.setBoost(scores.get(et));
} else {
document.setBoost(0.5f);
}
writer.addDocument(document);
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 设置日期
private void setDates() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
dates = new Date[ids.length];
dates[0] = sdf.parse("2015-02-15");
dates[1] = sdf.parse("2015-03-01");
dates[2] = sdf.parse("2015-05-18");
dates[3] = sdf.parse("2015-09-05");
dates[4] = sdf.parse("2015-12-15");
dates[5] = sdf.parse("2015-08-29");
} catch (ParseException e) {
e.printStackTrace();
}
}
}
TestSearch.java
package cn.lucene.test;
import org.junit.Before;
import org.junit.Test;
import cn.itcast.searcher.SearcherUtil;
public class TestSearch {
private SearcherUtil util;
@Before
public void init(){
util = new SearcherUtil();
}
@Test
public void searchByTerm(){
//util.searchByTerm("name", "Jack", 3);
util.searchByTerm("content", "like", 3);//注意:查询到的总记录数和我们想要显示的记录数没有关系
}
}
说明:因为这里我们将索引存放在内存中,所以当程序运行完也就没有了,所以不能使用之前的类创建索引,这里我们将创建索引的方法写在本类中,这里我们使用
Query query = new TermQuery(new Term(field, name));
进行精确搜索。注意:查询到的总记录数和我们想要显示的记录数没有关系。
二、范围查询 TermRangeQuery
public void searchByTermRang(String field, String start, String end, int num){
IndexSearcher searcher = getSearcher();
//最后两个参数表示起始和结尾是开区间还是闭区间,为什么我使用true或者false都无效?都是前闭后开
Query query = new TermRangeQuery(field, start, end, true, true);
try {
TopDocs tds = searcher.search(query, num);
System.out.println("总共查询了: " + tds.totalHits);
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
}
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
测试:
@Test
public void searchByTermRang(){
//util.searchByTermRang("id", "1", "3", 10);
//util.searchByTermRang("attach", "1", "3", 10);数字查询不到
util.searchByTermRang("name", "a", "j", 10);//注意:这里的范围区分大小写
}
说明:这里是范围查询,我们使用
Query query = new TermRangeQuery(field, start, end, true, true);
进行查询,给出相关的域和范围区间即可。但是这里的区间不知道试验时不起作用。但是这种范围查询对数字无效。
三、数字范围查询 NumericRangeQuery
public void searchByNumricRange(String field, int start, int end, int num){
IndexSearcher searcher = getSearcher();
//这里前后都是闭区间,这里设置最后两个参数却有有效了
Query query = NumericRangeQuery.newIntRange(field, start, end, true, true);
try {
TopDocs tds = searcher.search(query, num);
System.out.println("总共查询了: " + tds.totalHits);
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
}
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
测试:
@Test
public void searchByNumricRange(){
util.searchByNumricRange("attach", 2, 3, 10);
}
说明:这里我们使用
Query query = NumericRangeQuery.newIntRange(field, start, end, true, true);
进行查询。
四、前缀搜索 PrefixQuery
public void searchByPrefix(String field, String value, int num){
IndexSearcher searcher = getSearcher();
Query query = new PrefixQuery(new Term(field, value));//value的值通过前缀匹配
try {
TopDocs tds = searcher.search(query, num);
System.out.println("总共查询了: " + tds.totalHits);
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
}
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
测试:
@Test
public void searchByPrefix(){
//util.searchByPrefix("name", "j", 10);
util.searchByPrefix("content", "s", 10);
}
说明:这里我们使用
Query query = new PrefixQuery(new Term(field, value));
其中value就是前缀值。
五、通配符搜索 WildcardQuery
public void searchByWildCard(String field, String value, int num){
IndexSearcher searcher = getSearcher();
Query query = new WildcardQuery(new Term(field, value));
try {
TopDocs tds = searcher.search(query, num);
System.out.println("总共查询了: " + tds.totalHits);
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
}
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
测试:
@Test
public void searchByWildCard(){
//*表示任何字符,?表示一个字符
//util.searchByWildCard("name", "j?", 10);//搜不出来
util.searchByWildCard("name", "j*", 10);
}
说明:这里我们使用
Query query = new WildcardQuery(new Term(field, value));
其中我们使用?表示任意一个字符,而使用*号表示任意多个字符。
六、多条件查询 BooleanQuery
public void searchByBoolean(int num){
IndexSearcher searcher = getSearcher();
BooleanQuery query = new BooleanQuery();
//下面的查询表示名字为tom同时内容中有like的索引
query.add(new TermQuery(new Term("name", "tom")), Occur.MUST);//最后的参数表示条件必须有,如果是MUST_NOT表示必须没有
query.add(new TermQuery(new Term("content", "like")), Occur.MUST);//如果是SHOULD表示可有可无
try {
TopDocs tds = searcher.search(query, num);
System.out.println("总共查询了: " + tds.totalHits);
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
}
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
测试:
@Test
public void searchByBoolean(){
util.searchByBoolean(10);
}
说明:这里我们使用
BooleanQuery query = new BooleanQuery();
//下面的查询表示名字为tom同时内容中有like的索引
query.add(new TermQuery(new Term("name", "tom")), Occur.MUST);
query.add(new TermQuery(new Term("content", "like")), Occur.MUST);
这里Occur.MUST表示必须要有,Occur.MUST_NOT表示必须没有,Occur.SHOULD表示可有可无。这样我们就将多个条件组合起来了。
七、短语查询 PhraseQuery
public void searchByPhrase(int num){
IndexSearcher searcher = getSearcher();
PhraseQuery query = new PhraseQuery();
//比如I like football,我们查询I football则表示中间有一跳(一空),参数值就表示跳数
query.setSlop(1);
query.add(new Term("content", "i"));//注意:这里的参数值要小写,但是也会将大写的查出来
query.add(new Term("content", "football"));
try {
TopDocs tds = searcher.search(query, num);
System.out.println("总共查询了: " + tds.totalHits);
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
}
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
测试:
@Test
public void searchByPhrase(){
util.searchByPhrase(10);
}
说明:这里我们使用
PhraseQuery query = new PhraseQuery();
query.setSlop(1);
query.add(new Term("content", "i"));
query.add(new Term("content", "football"));
这里我们在给值的时候需要给小写,但是会将小写和大小匹配的索引都查询出来。当然这种查询很消耗资源,同时对中文搜索作用不大。
八、模糊查询 FuzzyQuery
public void searchByFuzzy(int num){
IndexSearcher searcher = getSearcher();
//这里我们看到名字虽然写错了,但是能够查出来,默认可以匹配一个字符出错的情况,这里设置匹配力(相似度)为0.5<=1,距离为2
FuzzyQuery query = new FuzzyQuery(new Term("name", "tome"), 0.5f, 2);
try {
TopDocs tds = searcher.search(query, num);
System.out.println("总共查询了: " + tds.totalHits);
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
}
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
测试:
@Test
public void searchByFuzzy(){
util.searchByFuzzy(10);
}
说明:这里我们使用
FuzzyQuery query = new FuzzyQuery(new Term("name", "tome"), 0.5f, 2);
进行查询,模糊查询和通配符查询不一样,可以理解为容错查询,如果不给出后面两个参数,则默认可以允许一个字符出错,当然这可以使用后面两个参数进行设定。倒数第二个参数是匹配力,此值小于等于1.0,默认为1.0。值越小匹配越小,最后一个参数表示可以出现错误的字符个数。此方法用的不多,了解即可。
九、QueryParser
public void searchByQueryParser(Query query, int num){
IndexSearcher searcher = getSearcher();
try {
TopDocs tds = searcher.search(query, num);
System.out.println("总共查询了: " + tds.totalHits);
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + doc.get("id")
+ ",权值:"+ doc.getBoost()
+ ",名字:" + doc.get("name")
+ ",邮箱:" + doc.get("email")
+ ",附件条数:" +doc.get("attach")
+ ",日期:" + doc.get("date"));
}
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
测试:
@Test
public void searchByQueryParser() throws ParseException{
//1、创建QueryParser对象,默认搜索域为content
QueryParser parser = new QueryParser(Version.LUCENE_35, "content", new StandardAnalyzer(Version.LUCENE_35));
parser.setDefaultOperator(Operator.AND);//改变默认空格操作符,但是分开写还是或操作符
parser.setAllowLeadingWildcard(true);//开启前缀为通配符的查询,默认关闭,效率较低
//搜索content中包含like的
Query query = parser.parse("like");//空格和分开默认表示或
//query = parser.parse("I football");//这就是要查有I或者football的
query = parser.parse("I AND football");//这就是要查有I同时有football的
//改变搜索域为name为tom的
query = parser.parse("name:like");
query = parser.parse("name:j*");
query = parser.parse("email:*@sina.com");//这种效率较低,默认是关闭的,但是可以打开
//这里表示不能有like而必须有football,中间要有空格,加减要放在条件前面
query = parser.parse("- name:tom + football");
query = parser.parse("id:[1 TO 3]");//注意是闭区间,TO必须是大写
query = parser.parse("id:{1 TO 3}");//注意是开区间,不能是半开半闭
query = parser.parse("\"I like football\"");//完全匹配"I like football"
query = parser.parse("\"I football\"~1");//表示I和football之间有一个空白单词的
util.searchByQueryParser(query, 10);
}
说明:此类会将我们传入的参数进行处理之后再进行查询,首先我们构造一个QueryParser对象,此对象中第二个参数(此处是content)表示默认搜索的域当然可以改变。当我们使用
query = parser.parse("I football");
进行查询时会将中间的空格默认为“或”,也就是有"I"或者有"football"的索引,当然我们可以通过
parser.setDefaultOperator(Operator.AND);
将默认的“或”设置为“与”,此时就表示查询既有"I"也有"football"的索引,当然我们一般不改,而使用"AND"来进行条件“与”操作。而这里通配符查询由于效率较低,默认是不允许放在首位的,当然我们可以使用
parser.setAllowLeadingWildcard(true);
让其允许。可以查询的组合还有很多,下面给出:
| 条件 | 含义 |
|---|---|
mike |
默认域包含mike
|
mike john、mike OR john
|
默认域包含mike或者john
|
+ mike + address:zhaotong、mike AND address:zhaotong
|
默认域包含mike,同时address是zhaotong的 |
id:2 |
id域为2 |
address:Kunming – desc:she、address:Kunming AND NOT desc:she
|
address是Kunming并且desc不是she的 |
(mike OR john) AND address:zhaotong |
默认域是mike或者john并且address是zhaotong
|
desc:”she like” |
desc域是she like
|
j* |
默认域是j开头 |
johe~ |
模糊搜索johe
|
id:[1 TO 3] |
id从1到3 |
十、分页搜索
在做分页查询的时候我们需要很多文件,不然看不出效果。这里我们在路径E:/myeclipse/Lucene/somefile/中准备了一些文件,如果文件不够,我们可以使用下面的方法进行拷贝:
TestSearch.java
@Test
public void testCopuFiles(){
File file = new File("E:/myeclipse/Lucene/somefile/");
for(File f : file.listFiles()){
String destFileName = FilenameUtils.getFullPath(f.getAbsolutePath())
+ FilenameUtils.getBaseName(f.getName()) + ".py";
try {
FileUtils.copyFile(f, new File(destFileName));
} catch (IOException e) {
e.printStackTrace();
}
}
}
当然这个方法中我们需要用到依赖包commons-io.jar。之后我们不能再使用之前的索引创建方法了,因为之前创建的索引保存在内存中,这里我们需要保存在硬盘上。于是我们给出创建索引的类,其实在之前我们已经写过了,这里作为复习:
FileIndexUtil.java
package cn.itcast.searcher;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Version;
public class FileIndexUtil {
private static Directory directory = null;
static {
try {
directory = FSDirectory.open(new File(
"E:/myeclipse/Lucene/index"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static Directory getDirectory() {
return directory;
}
// 创建索引
public static void index(boolean hasNew) {
IndexWriter writer = null;
try {
writer = new IndexWriter(directory, new IndexWriterConfig(
Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)));
if (hasNew) {
writer.deleteAll();//如果我们要新建索引,那么将之前创建的删除
}
File file = new File("E:/myeclipse/Lucene/somefile");
Document document = null;
for (File f : file.listFiles()) {
document = new Document();
document.add(new Field("content", new FileReader(f)));
document.add(new Field("filename", f.getName(),
Field.Store.YES, Field.Index.NOT_ANALYZED));
document.add(new Field("path", f.getAbsolutePath(),
Field.Store.YES, Field.Index.NOT_ANALYZED));
document.add(new NumericField("date", Field.Store.YES, true)
.setLongValue(f.lastModified()));
// 最后我们将字节数转换成kb
document.add(new NumericField("size", Field.Store.YES, true)
.setIntValue((int) (f.length() / 1024)));
writer.addDocument(document);
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
下面我们看分页查询的方法,当然这里我们同时给出不使用分页查询的方法作为比较:
// 分页查询
public void searchPage(String query, int pageIndex, int pageSize) {
try {
Directory dir = FileIndexUtil.getDirectory();
IndexSearcher searcher = getSearcher(dir);
QueryParser parser = new QueryParser(Version.LUCENE_35, "content",
new StandardAnalyzer(Version.LUCENE_35));
Query q = parser.parse(query);
TopDocs tds = searcher.search(q, 100);//TopDocs封装搜索结果以及 ScoreDoc 的总数
ScoreDoc[] sds = tds.scoreDocs;//ScoreDoc提供对TopDocs中每条搜索结果的访问接口
int start = (pageIndex - 1) * pageSize;
int end = pageIndex * pageSize;
for(int i = start; i < end; i++ ){
Document doc = searcher.doc(sds[i].doc);
System.out.println("id号:" + sds[i].doc + ",路径:" + doc.get("path") + ",名称:" + doc.get("filename"));
}
searcher.close();
} catch (org.apache.lucene.queryParser.ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//不加分页的查询
public void searchNoPage(String query) {
try {
Directory dir = FileIndexUtil.getDirectory();
IndexSearcher searcher = getSearcher(dir);
QueryParser parser = new QueryParser(Version.LUCENE_35, "content",
new StandardAnalyzer(Version.LUCENE_35));
Query q = parser.parse(query);
TopDocs tds = searcher.search(q, 100);//TopDocs封装搜索结果以及 ScoreDoc 的总数
ScoreDoc[] sds = tds.scoreDocs;//ScoreDoc提供对TopDocs中每条搜索结果的访问接口
for(int i = 0; i < sds.length; i++ ){
Document doc = searcher.doc(sds[i].doc);
System.out.println("id号:" + sds[i].doc + ",路径:" + doc.get("path") + ",名称:" + doc.get("filename"));
}
searcher.close();
} catch (org.apache.lucene.queryParser.ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
测试:
@Test
public void searchPage01(){
util.searchPage("java", 1, 10);
System.out.println("*****************");
util.searchPage("java", 2, 10);
System.out.println("*****************");
util.searchNoPage("java");
}
从这里我们从结果中可以看出分页查询的效果。但是虽然是分页查询,但是其实每次都将给定的查询条数的索引全部查询出来了,效率有点低。下面看第二种分页查询方式:
public void searchPageByAfter(String query, int pageIndex, int pageSize){
try {
Directory dir = FileIndexUtil.getDirectory();
IndexSearcher searcher = getSearcher(dir);
QueryParser parser = new QueryParser(Version.LUCENE_35, "content",
new StandardAnalyzer(Version.LUCENE_35));
Query q = parser.parse(query);
TopDocs tds = searcher.search(q, 100);//TopDocs封装搜索结果以及 ScoreDoc 的总数
ScoreDoc[] sds = tds.scoreDocs;//ScoreDoc提供对TopDocs中每条搜索结果的访问接口
int last = (pageIndex - 1) * pageSize - 1;
tds = searcher.searchAfter(sds[last], q, 10);//表示从第十条索引开始
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + sd.doc + ",路径:" + doc.get("path") + ",名称:" + doc.get("filename"));
}
searcher.close();
} catch (org.apache.lucene.queryParser.ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
说明:这里
int last = (pageIndex - 1) * pageSize - 1;
tds = searcher.searchAfter(sds[last], q, 10);//表示从第十条索引开始
表示我们从前一页的最后一条索引的后一条索引开始显示。测试方法这里省略了,和之前的类似。但是我们看到我们给出的查询条数依然是100,也就是说我们依然是查询出了100条数据(如果有100条数据的话),这样其实和第一种分页查询在效率上相差不大。下面我们进行改进:
private ScoreDoc getLaScoreDoc(int pageIndex, int pageSize, Query query, IndexSearcher searcher) throws IOException{
if(pageIndex == 1){
return null;
}
//这里我们不能像mysql中那样每次都只取pageSize条数据,这里第一次取pageSize条,第二次取2*pageSize条,只能这样
int num = pageSize * (pageIndex - 1);
TopDocs tds = searcher.search(query, num);
return tds.scoreDocs[num - 1];
}
public void searchPageByAfter01(String query, int pageIndex, int pageSize){
try {
Directory dir = FileIndexUtil.getDirectory();
IndexSearcher searcher = getSearcher(dir);
QueryParser parser = new QueryParser(Version.LUCENE_35, "content",
new StandardAnalyzer(Version.LUCENE_35));
Query q = parser.parse(query);
ScoreDoc lastSd = getLaScoreDoc(pageIndex, pageSize, q, searcher);
TopDocs tds = searcher.searchAfter(lastSd, q, pageSize);
ScoreDoc[] sds = tds.scoreDocs;//ScoreDoc提供对TopDocs中每条搜索结果的访问接口
for(ScoreDoc sd : tds.scoreDocs){
Document doc = searcher.doc(sd.doc);
System.out.println("id号:" + sd.doc + ",路径:" + doc.get("path") + ",名称:" + doc.get("filename"));
}
searcher.close();
} catch (org.apache.lucene.queryParser.ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
说明:这里我们先通过每页显示的条数和要显示的页码将显示的起始位置的索引和需要显示的总条数计算出来(在方法getLaScoreDoc中)。这里注意,如果我们显示条数为10,而页码为2,那么要显示的数据第一条就是10,而这里的num为19。因为我们不能想数据库那样只查询出给定的条数,必须将给定的所有条数都查询出来,这样或多或少可以提高一定的效率。当然此方法在3.5版本之后才提供。