我们先看完整的代码:
package cn.zhao.cms.column;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.MultiFields;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.queryparser.classic.QueryParser.Operator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.BytesRef;
public class TestLucene {
public static void main(String[] args) throws Exception{
createIndex();
searchIndex();
}
//获取某个域分词后的terms
private static void searchIndex() throws IOException, ParseException,
InvalidTokenOffsetsException {
Directory directory = FSDirectory.open(Paths.get("D:\\lucene\\index"));
IndexReader ir=DirectoryReader.open(directory);
//获取某个域分词后的terms
Terms terms = MultiFields.getTerms(ir, "content");
TermsEnum iterator = terms.iterator();
BytesRef bRef = null;
int rank=1;
while ((bRef=iterator.next())!=null) {
String oneTerm=new String(bRef.bytes, bRef.offset, bRef.length,Charset.forName("utf-8"));
//docFreq:当前这个term在几个document里面出现了
System.out.println("第"+rank+"个term是:"+oneTerm+",共在:"+iterator.docFreq()
+"个文档出现,该term在所有文档中共出现:"
+ir.totalTermFreq(new Term("content", bRef))+"次");
rank++;
}
//可以看做是jdbc里面的connection
IndexSearcher isearcher = new IndexSearcher(ir);
//Analyzer analyzer=new StandardAnalyzer();
SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();
//可以看成是sql里面的PreparedStatement,下面就像是 content=?的意思一样
QueryParser parser = new QueryParser("content", analyzer);
parser.setDefaultOperator(Operator.AND);
parser.setAllowLeadingWildcard(true);//设置通配符能在第一位
//query对象就像一个最终生成的sql语句,下面就像是ps.setString("xxx")一样
//能不能搜索到,要看分词器是否把content里面的内容切出了"窃窃私语"这个term单元,英文分词器
//可能会切出"窃","私","语",而中文分词器可能就会切出"窃窃私语"来
Query query = parser.parse("窃窃私语");
//可能有很多个文档含有上边的搜索词,这里10代表只取前10条
TopDocs tds = isearcher.search(query,10);
ScoreDoc[] sds = tds.scoreDocs;
int length = sds.length;
System.out.println("共搜索到"+length+"个文档含有设置的关键词");
for (int i = 0; i < length; i++) {
Document hitDoc = isearcher.doc(sds[i].doc);
System.out.println("得到设置的id域:"+hitDoc.get("docurl"));
System.out.println("得到设置的docurl域"+hitDoc.get("docurl"));
String content = hitDoc.get("content");
System.out.println("内容:"+content);
}
ir.close();
directory.close();
}
//创建索引
private static void createIndex() throws IOException {
//指定存放lucene生成的索引的目录,可以把这个目录看做一张表,lucene的所有索引都存放在这个表中
Directory directory = FSDirectory.open(Paths.get("D:\\lucene\\index"));
//使用默认的英文分词器
// Analyzer analyzer=new StandardAnalyzer();
//使用对中文支持更好的分词器
SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();
IndexWriterConfig conf=new IndexWriterConfig(analyzer);
IndexWriter iwriter =new IndexWriter(directory, conf);
//每次创建先删除原来的已经生成的索引
iwriter.deleteAll();
//deleteAll只是放进了lucene索引回收站,这里还要从回收站也删除掉
iwriter.forceMergeDeletes();
//这里我索引了D:\\lucene\\data目录下的所有文件
File dirFile=new File("D:\\lucene\\data");
File[] listFiles =dirFile.listFiles();
for(int i=0;i<listFiles.length;i++){
File file=listFiles[i];
//Document可以看做表的一条记录,记录自然有多个field,可以看做表里的字段
//field在lucene里叫域,一个域有n(>=1)个terms,这个terms没法类比关系型数据库里面的概念了,
//你可以理解每个字段的内容按一定的切分规则(分词器)切分成了若干单元,每个单元就叫做term
Document document=new Document();
document.add(new StringField("id", 0+"", Field.Store.YES));
document.add(new StringField("docurl", file.getAbsolutePath(), Field.Store.YES));
TextField textField = new TextField("content", FileUtils.readFileToString(file), Field.Store.NO);
document.add(textField);
//按指定的分词规则analyzer写入存放索引的目录D:\\lucene\\index
iwriter.addDocument(document);
}
System.out.println("实际索引多少个文档:"+iwriter.numDocs());
System.out.println("最大索引多少个文档:"+iwriter.maxDoc());
//最后记得关闭资源
iwriter.close();
directory.close();
}
}
代码说明:
1.创建索引和检索使用的分词器必须前后一致,要使用SmartChineseAnalyzer就都使用SmartChineseAnalyzer。
2.Operator.AND表示要求含有整个"窃窃私语"这个词语,如果改为Operator.OR,那么即便是分词器分的结果中没有完整的“窃窃私语”这个词,比如StandardAnalyzer只有“窃”"私"“语”单个字,但是因为设置的是OR,即便你设置要查询的关键字是“窃窃私语”,因为含有单字,也会被命中。我们先看下上面程序的运行结果:
3.为什么获取到的内容是null?
因为我们设置了new TextField("content", FileUtils.readFileToString(file), Field.Store.NO);, Field.Store.NO表示lucene不存储实际内容,只存储内容的索引,如果设置为 Field.Store.NO,获取实际内容是什么的时候,是没办法获取到的,只能通过docurl再读进来,但是虽然实际内容通过lucene不能直接获得,因为存储了实际内容的索引,所以我们可以检索。
4.我们更换要搜索的词为“href”,即 修改程序代码为:Query query = parser.parse("href"); 执行结果如下:
5.好,这就是lucene检索的代码。数据源我就不贴出来了,太多了,大家随便找几个文本试试就行了。