至于那些概念什么的,我就不多说了,大家可以参考以前的旧教程来了解Lucene的体系结构和基本原理。大致说一下Lucene就是通过创建索引这个类似书目录那样的东西来提升查询效率的一个框架,所谓索引我理解就是将文档等数据源的不同组成部分的指示标志,索引会指引使用者快速找到这些数据源不同组成部分。
多了不说了,先看代码吧,首先是Indexer类,用来创建索引:
public class Indexer { private IndexWriter writer;//这个类用来写入索引 //下面这个类是FileFilter的实现类,用来过滤符合条件的文档。 private static class TextFilesFilter implements FileFilter{ @Override public boolean accept(File pathname) { // TODO Auto-generated method stub return pathname.getName().toLowerCase().endsWith(".txt"); } } //构造方法,用来传入索引存放路径 public Indexer(String indexDir) throws IOException{ Directory dir=FSDirectory.open(Paths.get(indexDir)); IndexWriterConfig config=new IndexWriterConfig(new StandardAnalyzer()); config.setOpenMode(OpenMode.CREATE_OR_APPEND); writer=new IndexWriter(dir,config); } //关闭indexWriter public void close() throws IOException{ writer.close(); } //这个方法是遍历文件夹下所有文件并选择符合条件文件写入索引的方法,返回写入的文档总数 public int index(String dataDir,FileFilter filter) throws IOException{ File[] files=new File(dataDir).listFiles(); for(File file:files){ if(!file.isDirectory() && !file.isHidden() && file.exists() && file.canRead() && (filter==null) || filter.accept(file)){ indexFile(file); } } return writer.numDocs(); } //这个方法是写入索引的方法,将生成的document对象写入到索引中 private void indexFile(File file) throws IOException{ System.out.println("indexing..."+file.getCanonicalPath()); Document doc=getDocument(file); writer.addDocument(doc); } //这个方法是生成Document对象的方法,Document对象就是对文档各个属性的封装 protected Document getDocument(File file) throws IOException{ Document doc=new Document(); doc.add(new Field("contents",new FileReader(file),TextField.TYPE_NOT_STORED)); doc.add(new Field("filename",file.getName(),TextField.TYPE_STORED)); doc.add(new Field("fullpath",file.getCanonicalPath(),TextField.TYPE_STORED)); return doc; } public static void main(String[] args) throws IOException { String indexDir="C:/lucene_index"; String dataDir="C:/lucene_data"; long start=System.currentTimeMillis(); Indexer indexer=new Indexer(indexDir); int numberIndexed=indexer.index(dataDir, new TextFilesFilter()); indexer.close(); long end=System.currentTimeMillis(); System.out.println(numberIndexed); System.out.println(end-start); } }
我说一下几个需要注意的地方。
Directory dir=FSDirectory.open(Paths.get(indexDir));这个方法是获取索引存放路径的方法,早期版本的Lucene在open方法中用new File或new一个输入流的方式获取路径,目前这里采用的是nio的方式获取文件夹路径,效率较高。
IndexWriterConfig config=new IndexWriterConfig(new StandardAnalyzer());
config.setOpenMode(OpenMode.CREATE_OR_APPEND);
writer=new IndexWriter(dir,config);
这里IndexWriterConfig对象是IndexWriter的配置对象,有很多配置。第二行的openMode是索引路径打开的方式,这里选择的是创建或追加内容。
然后就是初始化一个IndexWriter,这里与早期版本也有所不同,将配置对象传入构造方法。
doc.add(new Field("contents",new FileReader(file),TextField.TYPE_NOT_STORED));
doc.add(new Field("filename",file.getName(),TextField.TYPE_STORED));
这是为Document对象添加属性的方法,与早期版本的也有所不同,第一个是将文件内容用输入流的方式写入,其中Field对象第三个参数是TextField.TYPE_NOT_STORED,我的理解就是索引中不存储这个流以及流读取的内容本身
然后看一下IndexSearcher就是搜索索引的类:
public class Searcher { //这个方法是搜索索引的方法,传入索引路径和查询表达式 public static void search(String indexDir,String query) throws IOException, ParseException{ Directory dir=FSDirectory.open(Paths.get(indexDir)); IndexSearcher searcher=new IndexSearcher(DirectoryReader.open(dir)); QueryParser parser=new QueryParser("contents",new StandardAnalyzer()); Query q=parser.parse(query); long start=System.currentTimeMillis(); TopDocs hits=searcher.search(q, 10); long end=System.currentTimeMillis(); System.out.println(hits.totalHits); System.out.println(end-start); for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc=searcher.doc(scoreDoc.doc); System.out.println(doc.get("fullpath")); } } public static void main(String[] args) throws IOException, ParseException { String indexDir="C:/lucene_index"; String query="netty"; search(indexDir, query); } }
这里需要注意的是这些:
IndexSearcher searcher=new IndexSearcher(DirectoryReader.open(dir));这个IndexSearcher初始化的过程也和早期版本有所不同,看源码构造方法参数是IndexReader这个抽象类的子类,我根据源码找到了DirectoryReader这个子类比较合适,用open方法获得了索引路径,然后IndexSearcher的构造方法传入这个IndexReader子类。
QueryParser parser=new QueryParser("contents",new StandardAnalyzer());
Query q=parser.parse(query);
这里就是解析查询语句以及要查询的Document属性,换句话说就是在哪儿查?查什么?怎么查?
TopDocs hits=searcher.search(q, 10);
这个方法查询出前10个符合要求的文档。
for(ScoreDoc scoreDoc:hits.scoreDocs){
Document doc=searcher.doc(scoreDoc.doc);
System.out.println(doc.get("fullpath"));
}
这里就是遍历每个查询到的文档,以及打印出每个查询到的文档的fullpath属性。
基本写入读取索引程序就这些。
欢迎加入qq群204058911,群主是Lucene和Solr方面的大牛,著有Solr实战这一本经典教科书。