咱这搞技术活儿的吧,得会用Google/Baidu!
这哥俩儿在我们的成长过程中,起着举足轻重的作用,马大队长(马士兵)时刻提醒我们有不懂的首先问问Google/Baidu。输入我们感兴趣的话题直接回车就可以获取到互联网上所有的信息,那么多的信息量,却在毫秒级的时间里返回,不无感慨一下它的强大。当然,搜索之属于产生正是由于互联网越来越庞大的信息量。
一应用系统,随着时间的推移,数据量越来越大,系统性能是否经受得住考验?!这是个我们必须考虑的问题。我们知道,通过SQL的like模糊查询数据匹配时是巨慢的,我想你肯定有收到过用户为系统的低效而抱怨的信息,你优化SQL还是达不到预想的速度,基于此,我们有必要研究一下搜索引擎。
lucene是一个高性能的、可扩展的信息检索工具库。我们可以把它融入到我们所开发的应用程序中来增加索引和搜索功能。
说说索引和搜索的关系吧:
索引操作是所有搜索引擎的核心概念:为了进行快速查找,就需要把数据处理成一种高效的、可交叉引用的查找表格式。
我们知道,为了提高数据库的数据检索性能,我们通常会对相应的列做索引,然后通过搜索找到相应的索引,再通过索引找到相对应的数据,这样查询速度会提高很多。实际上,我们仔细思考这一种策略,确实是有一定的道理的。
搜索是一个在索引中查找关键字的过程。你原来需要在巨多的数据中搜索你想要的数据,现在只要在你建立的少量索引文件中检索,这高效不言而喻。
越来越有点意思了对吧,所以,Lucene应用实例,Come on(以下代码示例均来自Lucene in action):
创建索引文件(对扩展名为txt的文件进行索引),示例代码如下:
private IndexWriter writer; /** * 指定索引目录的构造函数 * */ public Indexer(String indexDir) throws IOException{ Directory dir = FSDirectory.open(new File(indexDir)); writer = new IndexWriter(dir,new StandardAnalyzer(Version.LUCENE_30), true, IndexWriter.MaxFieldLength.UNLIMITED);//1) } //关闭IndexWriter public void close() throws CorruptIndexException, IOException{ if(null!=writer){ writer.close(); } } /** * 索引 * @param dataDir 指定需要索引的数据路径 * @param filter 进行索引的文件类型 * @return int 索引的文件数 * */ public int index(String dataDir,FileFilter filter) throws IOException{ File[] files = new File(dataDir).listFiles(); for(File f:files){ if(f.isDirectory()){ index(f.getAbsolutePath(),filter); }else if(!f.isHidden()&& f.exists()&& f.canRead()&&(filter==null||filter.accept(f))){ indexFile(f); } } return writer.numRamDocs();//Return number of documents indexed } /** * 索引指定的文件 * */ private void indexFile(File f) throws IOException{ System.out.println("Indexing "+f.getCanonicalPath()); Document doc = getDocument(f); writer.addDocument(doc);//Add document to Lucene index } /** * 指定可索引的文件类型(只索引后缀名为.txt文件) * */ private static class TextFilesFilter implements FileFilter{ public boolean accept(File path){ return path.getName().toLowerCase().endsWith(".txt");//Index .txt files only,using FileFilter } } /** * 返回代表索引集合的文档 * */ protected Document getDocument(File f) throws IOException{ Document doc = new Document();//2) doc.add(new Field("contents",new FileReader(f)));//Index file content(not store) doc.add(new Field("filename",f.getName(), Field.Store.YES,Field.Index.NOT_ANALYZED));//Index filename doc.add(new Field("fullpath",f.getCanonicalPath(), Field.Store.YES,Field.Index.NOT_ANALYZED));//Index file full path return doc; }
现对上段代码的主要部分进行简单的说明如下:
1)初始化IndexWriter。IndexWriter是索引过程中的核心组件,该类用于创建一个新的索引或者打开一个已经存在的,并增加、删除或更新文档中的索引。因为你要有存储索引的地方啊,所以参数需要一个存储索引的目录。该参数目录通过
Directory dir = FSDirectory.open(new File(indexDir));
返回。org.apache.lucene.store.Directory有两个直接子类:FSDirectory和RAMDirectory。分别代表硬盘目录以及内存目录,这两个具体的含义以后再说,因为我们需要索引的文件存储在硬盘上,所以使用FSDirectory返回代表目录的实例。
2)实例化一个Document对象,该对象代表了一些域(Field)的集合。每个域都对应一段可能在搜索过程中被查询的数据。Lucene只能处理java.lang.String、java.io.Reader以及一些基本数据类型(如int或者float)。如果要处理其它诸如Microsoft的word、excel或者html、PDF等文件类型时,则需要使用到一些其它的分析器来进行索引操作,而不只是我们上文提到的最基础的StandardAnalyzer。
索引搜索,代码示例如下:
public static void search(String indexDir,String q) throws IOException, ParseException{ Directory dir = FSDirectory.open(new File(indexDir));//Open index IndexSearcher is = new IndexSearcher(dir); QueryParser parser = new QueryParser(Version.LUCENE_30,//Parse query "contents", new StandardAnalyzer( Version.LUCENE_30)); Query query = parser.parse(q);//Search index long start = System.currentTimeMillis(); TopDocs hits = is.search(query,10); long end = System.currentTimeMillis(); System.err.println("Found "+hits.totalHits+//Write search stats " document(s) (in "+(end-start)+ " milliseconds) that matched query '"+ q+"':"); // Note that the TopDocs object contains only references to the underlying documents. // In other words, instead of being loaded immediately upon search, matches are loaded // from the index in a lazy fashion—only when requested with the Index- // Searcher.doc(int) call. // 换言之,搜索并不立即加载,当调用IndexSearcher.doc(int)时返回。 // That call returns a Document object from which we can then // retrieve individual field values. for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc = is.doc(scoreDoc.doc);//Retrieve matching document System.out.println(doc.get("fullpath"));//Display filename System.out.println(doc.get("filename")); System.out.println(doc.get("contents")); } is.close();//Close IndexerSearcher }
在搜索索引之前,我们必须得知道索引在哪儿吧,所以通过
Directory dir = FSDirectory.open(new File(indexDir));//Open index
打开索引。然后实例化IndexSearcher对象,通过QueryParser对象解析查询字符并返回一个Query对象。再调用
TopDocs hits = is.search(query,10);
查询前10的文件集合。最后循环获取,非常的简单易懂。
具体的输出结果我就不再浪费时间打字了,源码可参见附件。
当然了,这只是对所有的文本文件做了一个Lucene的简单入门介绍,希望对有兴趣学习lucene的朋友提供一个入门介绍!如有不足的地方,望指出大家共同学习和进步!