关于Lucene以及索引和搜索的流程

上一篇 / 下一篇  2012-06-26 09:11:12 / 个人分类:杂谈

Lucene的普及和成功的背后是因为它的简单。51Testing软件测试网T'q[D&V-u_

  因此,你不需要深入理解Lucene的信息索引和检索工作方面的知识就可以开始使用。

h@(}&B(\0

.|Y:YL/`+lox5E0  Lucene提供了简单但是强大的核心API去实现全文索引和检索,你只需要掌握少数的类就能将Lucene整合到应用中。

u"O6A$C3EXGIt0

!_7x;w5^8{:r0|'D4]0   刚接触Lucene的人可能会误认为Lucene是一个文件搜索工具、网络爬虫、或者网页搜索引擎。实际上Lucene是一个软件库,而不是一个全功能 的搜索应用程序。它涉及全文索引和搜索,而且做得非常好。Lucene可以让你的应用程序隐藏起复杂的索引和搜索背后的操作,而使用简单的API处理特定 的问题领域和业务规则。你可以想象Lucene就是像一个层,你的应用就在层的上面。

KuE1X\?1Z!L051Testing软件测试网,fp9OXM5l

  Lucene允许你添加索引和搜索功能到应用程序中。Lucene不关心数据的来源,Lucene可以索引和搜索任何可以转换成文本格式的数据。这意味着你可以用Lucene索引和搜索数据:远程web服务器上的网页、存储在本地文件系统的文档、简单的文本文件、Microsoft Word文档、HTML或PDF文件,或者其他任何可以从中提取文本信息的格式文件。51Testing软件测试网b Vje6~?:j

x*L%U9TX-kT#q0  所有搜索引擎的核心就是索引的概念:把原始数据处理成一个高效的交叉引用查找,以便快速检索。让我们看看快速高效的索引和搜索过程。51Testing软件测试网.G'Fzb)Dn1L

51Testing软件测试网j\?(I)qC6U||

  1、索引是什么,为什么它这么重要?51Testing软件测试网AFe7iZR3b

ro2L` [G1y2Q&qm v0   假如你需要搜索大量的文件,你希望找到那些包含某个单词或词组的文件。你将如何去写一个程序实现这个功能?一个做法就是按顺序扫描每一个文件,搜索是否 包含给定的单词或词组。但是这样的做法有很多缺陷的,其中最明显的就是在大量的文件存在的情况下,速度是令人无法接受的。这种情况下,索引产生了。为了搜 索大量的文本,你首先要对这些文本以特定的结构存储,这种存储结构可以让你迅速的搜索,消除慢的顺序扫描的过程。这种存储结构就叫索引,将文本转换成特定 结构存储的过程,就叫建立索引。

[I3d2{n d0

K(G m&g ?0  索引作为一种数据结构,允许你快速随机的访问存储在里面的词。类似于字典的目录,某个词对应到某一页,查找的时候直接定位到那一页,速度就非常快,不用一页一页的翻去查找。Lucene的索引是一种专门设计的数据结构,通常作为一组索引文件存储在文件系统上。51Testing软件测试网Acoa^V

51Testing软件测试网;d}XF,U3`2u&xs

  2、什么是搜索?

eQ({5V3^4d:W)N%H*g0

)Q,?j'X pulVY0   在索引中搜索关键词,找到包含关键词的文档的过程就是搜索。搜索质量通常使用准确度和召回率来描述。所谓召回率是指一次搜索结果集合中符合用户要求的数 目与和用户查询相关的总数之比,而准确率是指一次搜索结果集合中符合用户要求的数目与该次搜索结果总数之比。我们也需要考虑其他有关搜索的因素,比如速度 和快速搜索大量文本的能力,单个和多项查询、 短语查询、 通配符、 结果的排名和排序的支持也很重要。51Testing软件测试网qQ5e"i3eL"u

{}Y6u6{&FOF)v0  3、Lucene in Action

q.scN3^;r-e@V051Testing软件测试网V(T,yo?+W5P,h

  假如我们需要索引和搜索存储在一个目录下的文件。

"b%B~1R/C051Testing软件测试网3~ k tH?P+OE9H

  在我们使用Lucene进行搜索之前,我们需要先建立索引。使用的Lucene的版本是3.6。51Testing软件测试网`WaaXwY

&|G3w*v"oel0  3.1 建立索引

sG:_;l;p)BA-~3Oy0

N3mo;n$J5v5P0  1)创建存放索引的目录Directory

O"qCf$O0

\\0Q!bR*{0  2)创建索引器配置管理类IndexWriterConfig

N[M%_W@6z@7]051Testing软件测试网2L.D*e*j CrKu5p.E

  3)使用索引目录和配置管理类创建索引器

K\ l5q#Qjz0

7jRi#i%w0  4)使用索引器将Document写到索引文件中51Testing软件测试网 Y7}*n0Ku5w%QV1{A

#Q0z M(U`V}0  索引器类:

Jq~y/I0
  1. /** 
  2.  * 索引器 
  3.  * @author Luxh 
  4.  */ 
  5. public class Indexer {  
  6.        
  7.        
  8.     /** 
  9.      * 建立索引 
  10.      * @param filePath 需要建立索引的文件的存放路径 
  11.      * @throws IOException 
  12.      */ 
  13.     public static void createIndex(String filePath) throws IOException {  
  14.            
  15.         //在当前路径下创建一个叫indexDir的目录 
  16.         File indexDir = new File("./indexDir");  
  17.            
  18.         //创建索引目录 
  19.         Directory directory = FSDirectory.open(indexDir);  
  20.            
  21.         //创建一个分词器 
  22.         Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);  
  23.            
  24.         //创建索引配置器 
  25.         IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36,analyzer);  
  26.            
  27.            
  28.         LogMergePolicy mergePolicy = new LogByteSizeMergePolicy();  
  29.            
  30.         //设置segment添加文档(Document)时的合并频率 
  31.         //值较小,建立索引的速度就较慢 
  32.         //值较大,建立索引的速度就较快,>10适合批量建立索引 
  33.         mergePolicy.setMergeFactor(50);  
  34.            
  35.         //设置segment最大合并文档(Document)数 
  36.         //值较小有利于追加索引的速度 
  37.         //值较大,适合批量建立索引和更快的搜索 
  38.         mergePolicy.setMaxMergeDocs(5000);  
  39.            
  40.         //启用复合式索引文件格式,合并多个segment 
  41.         mergePolicy.setUseCompoundFile(true);  
  42.            
  43.            
  44.         indexWriterConfig.setMergePolicy(mergePolicy);  
  45.            
  46.         //设置索引的打开模式 
  47.         indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);  
  48.            
  49.         //创建索引器 
  50.         IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);  
  51.            
  52.            
  53.         File fileDir = new File(filePath);  
  54.         for(File file : fileDir.listFiles()) {  
  55.             //Document是Lucene的文档结构,需要索引的对象都要转换为Document 
  56.             Document document = new Document();  
  57.                
  58.             //文件名,可查询,分词,存储到索引库记录中 
  59.             document.add(new Field("name",getFileName(file),Store.YES,Index.ANALYZED));  
  60.                
  61.             //文件路径,可查询,不分词,存储到索引库记录中 
  62.             document.add(new Field("path",file.getAbsolutePath(),Store.YES,Index.NOT_ANALYZED));  
  63.                
  64.             //大文本内容,可查询,不存储,实际上可根据文件路径去找到真正的文本内容 
  65.             //document.add(new Field("content",new FileReader(file))); 
  66.                
  67.             //小文本内容,可以存储到索引记录库 
  68.             document.add(new Field("content",getFileContent(file),Store.YES,Index.ANALYZED));  
  69.                
  70.             //把文档添加到索引库 
  71.             indexWriter.addDocument(document);  
  72.                
  73.         }  
  74.            
  75.         //提交索引到磁盘上的索引库,关闭索引器 
  76.         indexWriter.close();  
  77.    
  78.     }  
  79.        
  80.     /** 
  81.      * 获取文件名 
  82.      */ 
  83.     public static String getFileName(File file) {  
  84.         String fileName = "";  
  85.         if(file != null) {  
  86.             fileName = file.getName().substring(0, file.getName().lastIndexOf("."));  
  87.         }  
  88.         return fileName;  
  89.     }  
  90.        
  91.     /** 
  92.      * 获取文本 
  93.      * @param file 
  94.      */ 
  95.     public static String getFileContent(File file) {  
  96.         FileReader fr = null;  
  97.         BufferedReader br = null;  
  98.         String content = "";  
  99.         try {  
  100.             fr = new FileReader(file);  
  101.             br = new BufferedReader(fr);  
  102.             StringBuffer sb = new StringBuffer();  
  103.             String line = br.readLine();  
  104.             while(null != line){  
  105.                 sb.append(line);  
  106.                 line = br.readLine();  
  107.             }  
  108.             content = sb.toString();  
  109.         }catch(Exception e) {  
  110.             e.printStackTrace();  
  111.         }finally {  
  112.             try {  
  113.                 if(fr != null)  
  114.                     fr.close();  
  115.                 if(br != null)  
  116.                     br.close();  
  117.             } catch (IOException e) {  
  118.                 e.printStackTrace();  
  119.             }  
  120.         }  
  121.         return content;  
  122.            
  123.     }  
  124.    
  125.        
  126. }
 IndexWriter:索引器,负责创建和维护一条索引。51Testing软件测试网j;N"y ]&A

  在Lucene3.6版本,只推荐使用一个构造方法IndexWriter(Directory d,IndexWriterConfig conf),其他的构造方法都已经过时。所有关于IndexWriter的配置都是通过IndexWriterConfig来进行管理。

$y`7IG*v9zu(x0

|8F4BL'}I0  IndexWriterConfig:索引器配置类,管理所有有关索引器的配置。只有一个构造方法 IndexWriterConfig(Version matchVersion,Analyzer analyzer),构造方法中的参数matchVersion是Lucene的版本,analyzer是分词器。

%Q"VpW4\G2F\'p2b051Testing软件测试网4Xc rg l3o1Het

  接下来我们运行索引器创建索引。51Testing软件测试网k` vaA@

51Testing软件测试网 eF"gH*o#Tz

51Testing软件测试网 ?,z,dZ"q r|#uK

  1. public class TestIndexer {  
  2.        
  3.     /** 
  4.      * 创建索引 
  5.      * @throws IOException 
  6.      */ 
  7.     @Test 
  8.     public void testCreateIndex() throws IOException{  
  9.         //存放需要建立索引的文件的目录路径 
  10.         String filePath = "./fileDir";  
  11.         //调用索引器的创建索引方法 
  12.         Indexer.createIndex(filePath);  
  13.     }  
  14. }
51Testing软件测试网'L6~z~)f8iX

  这样我们就对当前路径下fileDir中的文件创建了索引。51Testing软件测试网_!O V8S,^cb~

6j%L5H j bV `g0  3.2 执行搜索51Testing软件测试网7oOQ0HWn FE+XX

51Testing软件测试网3^+j*xw3q9d7X!gH

  在Lucene中搜索像建立索引一样简单、快速。现在,我们建立一个搜索器,搜索包含特定文本的文件。51Testing软件测试网 HK3W,u8dXGPgd3[z/qk

51Testing软件测试网3B,Lm.w$JJ7N \

  1)使用QueryParser将查询的关键词解析成Lucene的查询对象Query。创建QueryParser的时候我们需要用到分词器,这个分词器要和前面创建索引的时候使用的分词器一致。51Testing软件测试网-M*f$mbh }O*~*B

51Testing软件测试网As%XF7d.a

  2)使用FSDirectory打开索引所在的目录。51Testing软件测试网zc |e'p*Ma1R

51Testing软件测试网"Q:j1I m Qy

  3)使用IndexReader读取索引目录和使用IndexSearcher进行搜索。

mOW"C%nY&R9E*y051Testing软件测试网1q;ZE#B6\

  4)返回搜索结果对象TopDocs。TopDocs包含搜索到结果总数和结果的集合ScoreDocs数组51Testing软件测试网2n%`{,y;A&zJ\

ezR#r(\!q0  5)遍历结果的集合ScoreDocs数组,根据每一个ScoreDoc的文档编号获取Document

1F0\4A2Ev0Kop0

jx{7UQ0  看看搜索器的代码:51Testing软件测试网x \2O)c} P1^ln

51Testing软件测试网 g4L;DDs6F

51Testing软件测试网Q/D%uDM

  1. /** 
  2.  * 搜索器 
  3.  * @author Luxh 
  4.  */ 
  5. public class Searcher {  
  6.        
  7.     /** 
  8.      * 搜索 
  9.      * @param keyWord 搜索的关键词 
  10.      * @param indexDir  索引目录所在路径 
  11.      * @throws ParseException 
  12.      * @throws IOException 
  13.      * @return List<Document> 
  14.      */ 
  15.     public static List<Document> search(String keyWord,String indexDirPath) throws ParseException, IOException {  
  16.            
  17.         String[] fields = {"name","content"};  
  18.            
  19.         //创建一个分词器,和创建索引时用的分词器要一致 
  20.         Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);  
  21.            
  22.         //创建查询解析器 
  23.         QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_36,fields,analyzer);  
  24.            
  25.         //将查询关键词解析成Lucene的Query对象 
  26.         Query query = queryParser.parse(keyWord);  
  27.            
  28.         //打开索引目录 
  29.         File indexDir = new File(indexDirPath);  
  30.         Directory directory = FSDirectory.open(indexDir);  
  31.            
  32.         //获取访问索引的接口,进行搜索 
  33.         IndexReader indexReader  = IndexReader.open(directory);  
  34.         IndexSearcher indexSearcher = new IndexSearcher(indexReader);  
  35.            
  36.         //TopDocs 搜索返回的结果 
  37.         TopDocs topDocs = indexSearcher.search(query, 100);//只返回前100条记录 
  38.            
  39.         int totalCount = topDocs.totalHits; // 搜索结果总数量 
  40.         System.out.println("搜索到的结果总数量为:" + totalCount);  
  41.            
  42.         ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 搜索的结果集合 
  43.            
  44.         List<Document> docs = new ArrayList<Document>();  
  45.            
  46.         for(ScoreDoc scoreDoc : scoreDocs) {  
  47.             //文档编号 
  48.             int docID = scoreDoc.doc;  
  49.             //根据文档编号获取文档 
  50.             Document doc = indexSearcher.doc(docID);  
  51.             docs.add(doc);  
  52.         }  
  53.         indexReader.close();  
  54.         indexSearcher.close();  
  55.         return docs;  
  56.     }  
  57. }

S&}/N)h0z;py;Y0  接下来我们运行搜索器:

G.i)Vo)C&X(l)ra@Ut051Testing软件测试网'UBT+S2A;a i6~j

51Testing软件测试网%W2Z ^0e)G

  1. public class TestSearcher {  
  2.        
  3.     /** 
  4.      * 搜索 
  5.      */ 
  6.     @Test 
  7.     public void testSearch() throws IOException, ParseException{  
  8.         //搜索关键词 
  9.         String keyWord = "Java";  
  10.         //索引目录路径 
  11.         String indexDirPath = "./indexDir";  
  12.         //调用搜索器进行搜索 
  13.         List<Document> docs = Searcher.search(keyWord, indexDirPath);  
  14.         for(Document doc : docs) {  
  15.             System.out.println("文件名 : "+doc.get("name"));  
  16.             System.out.println("路径 : "+doc.get("path"));  
  17.             System.out.println("内容 : "+doc.get("content"));  
  18.         }  
  19.     }  
  20.        
  21.        
  22. }

yt'EUX m4w2g0  如果有包含关键词的文件,就会被搜索出来了。51Testing软件测试网7V'Y0Z#? \9U sQ


TAG:

 

评分:0

我来说两句

Open Toolbar