从知道Hadoop起就听过Lucene的大名,但是一直没有抽出时间好好学习下,最近有了段空闲时间,决定把这些东西补一下,不求知根知底,但求大致了解。
Lucene的概要描述就不多复制了,总之使用它可以快速创建索引,并进行检索,是一个设计良好的框架。
Lucene的使用十分简单,网上下载Lucene包,导入工程基本上就可以使用了,我用的是Lucene4.2的包,下载下来解压后有以下这些目录:
简单的Lucene程序,只用引入core下面的lucene-core.jar以及analysis下面common里面的lucene-analyzers-common.jar就可以了。
Lucene设计的比较好,高级用户可以进行细致化的定制,但是如果初级用户(譬如我)使用的话,也可以很方面的使用,这里就把我的例子贴出来吧。
1 /** 2 * 针对某个文件夹下(不包括子文件夹)所有的txt文件建立索引,存放在该文件夹下的index文件夹中。 3 * 4 * @param rootPath 5 */ 6 public static void buildIndex(String rootPath) throws IOException { 7 //指定索引文件目录 8 Directory indexDir = FSDirectory.open(new File(rootPath + "/index")); 9 File dataDir = new File(rootPath); 10 File[] dataFiles = dataDir.listFiles(); 11 //构造分析器 12 Analyzer luceneAnalyzer = new StandardAnalyzer(Version.LUCENE_42); 13 //使用分析器构造索引生成器 14 IndexWriter indexWriter = new IndexWriter(indexDir, new IndexWriterConfig(Version.LUCENE_42, luceneAnalyzer)); 15 indexWriter.deleteAll(); 16 //开始生成索引 17 for (int i = 0; i < dataFiles.length; i++) { 18 if (dataFiles[i].isFile() && dataFiles[i].getName().endsWith(".txt")) { 19 Document document = new Document(); 20 //以文件名作为path域的内容,Field.Store.YES表示会将其存储到索引中,从而可以直接通过索引查询到内容 21 document.add(new StringField("path", dataFiles[i].getCanonicalPath(), Field.Store.YES)); 22 //以文件内容作为contents域的内容,Field.Store.NO表示不将其存储到索引中,这样的话,可以根据其关键字查询到索引,但是查询不到具体内容。 23 document.add(new TextField("contents", FileUtils.readFileToString(dataFiles[i], "GBK"), Field.Store.NO)); 24 indexWriter.addDocument(document); 25 } 26 } 27 //commit和close合二为一。 28 indexWriter.close(); 29 } 30 /** 31 * 根据索引进行检索 32 * 33 * @param indexDir 索引文件存放位置 34 * @param keyword 索引关键字 35 * @param numberOfRecords 需要查询出几条记录 36 */ 37 public static List<String> search(String indexDir, String keyword, int numberOfRecords) throws IOException { 38 //打开索引文件 39 Directory indexDirectory = FSDirectory.open(new File(indexDir)); 40 DirectoryReader ireader = DirectoryReader.open(indexDirectory); 41 //构造索引查询器 42 IndexSearcher isearcher = new IndexSearcher(ireader); 43 //构造查询语句 44 Query query = new TermQuery(new Term("contents", keyword)); 45 //进行查询 46 ScoreDoc[] hits = isearcher.search(query, null, numberOfRecords).scoreDocs; 47 List<String> result = new ArrayList<String>(); 48 //遍历查询结果 49 for (ScoreDoc scoreDoc : hits) 50 result.add(isearcher.doc(scoreDoc.doc).get("path")); 51 return result; 52 }
完成上面的代码后,就可以对对某个文件夹下的所有文件构建索引并进行查询了,经测试,对英文文件的索引是相当给力的。
这里啰嗦一下自己对Lucene原理的理解,Lucene建立索引的过程为:
1 对需要建立索引的内容进行分词,得到关键字。
2 对关键字进行加工,加工过程包括:过滤,过滤掉语气助词,如and、or、to等;大小写转化,因为人们可能希望查询"Computer"的时候,可以查询到"computer"、"COMPUTER"等相关词;词态转化,如希望在查询"want"的时候,可以查询到"wants","wanted"等等。
3 根据关键字建立索引,即记录某个关键字在哪些文档中出现过。
大致过程就是这样,更细节的优化还没有看,想着等将来需要深层次使用的时候再去看吧。
上面这些步骤完成后,就可以方面的进行查询了。
如果大家比较细心的话,就会发现我上面专门强调“对英文文件的索引是相当给力的”,也就说明对中文文件的索引相当不给力,这是因为英文分词相对比较容易,根据空白字符和标点符号基本上就可以分词了,但是中文分词比较有难度(可以百度“清明时节雨纷纷 断词”,然后自己体会),因此如果需要对中文文件建立索引时,需要使用其他的分词包,做的比较出色的有Lucene自带的SmartCN和IKAnalyzer。这两个分词包的使用也比较简单:
需要使用SmartCN时,引入SmartCN的包(在analysis下面的smartcn下),然后将上面代码的第12行改为:
1 Analyzer luceneAnalyzer = new SmartChineseAnalyzer(Version.LUCENE_42);
即可。
需要使用IKAnalyzer时,引入IKAnalyzer的包(网上自行搜索),然后将上面代码的第12行改为:
1 Analyzer luceneAnalyzer = new IKAnalyzer();
即可。
再次感谢面向对象这个伟大的发明!
以上就是我对Lucene当前的理解了,如果项目中真正用到Lucene时,再深入学习吧。
本文涉及代码已在Oschina的Git上共享了,链接为:http://git.oschina.net/xdxn/Test
参考文献:
开源中文分词框架分词效果对比smartcn与IKanalyzer