Lucene 入门

 Lucene 入门程序

需求


实现一个歌词搜索系统,通过关键字搜索,凡是文件名或文件内容包括关键字的文件都要找出来。

注意:该入门程序只对文本文件(.txt)搜索。

开发环境

Jdk1.7.0_72

开发工具:eclipse indigo

Lucene包:

lucene-core-4.10.3.jar

lucene-analyzers-common-4.10.3.jar

lucene-queryparser-4.10.3.jar

其它:

commons-io-2.4.jar

junit-4.9.jar

Index索引


3.1 创建数据源目录


创建数据源目录F:\develop\lucene\searchsource ,该目录存放要搜索的原始文件。


3.2 创建索引目录 


创建索引目录 F:\develop\lucene\indexdata,该目录存放创建的索引文件。


3.3 DocumentField


索引的目的是为了搜索,索引前要对搜索的内容创建成DocumentField



3.4 Document

Document文档是Lucene搜索的单位,最终要将文档中的数据展示给用户,文档中包括多个Field,在设计文档对象时,可以将一个Document对应关系数据库的一条记录,字段对应Field,但是要注意:Document结构属于NOSql(非关系),不同的Document包括不同的Field,建议将相同类型数据的Document保持Field一致,方便管理维护,避免将不同类型Field数据融合在一个Document中。

比如:

文件信息DocumentField包括:文件名称、文件类型、文件大小、文件路径、文件内容等,避免加入非文件信息Field


3.4.1 Field属性


Field是文档中的域,包括Field名和Field值两部分,一个文档可以包括多个FieldDocument只是Field的一个承载体,Field值即为要索引的内容,也是要搜索的内容。

1、 是否分析(tokenized)

是:将Field值分析出语汇单元即作分词处理,将词进行索引

比如:商品名称、商品简介等,这些内容用户要输入关键字搜索,由于搜索的内容格式大、内容多需要分析后将语汇单元索引。

否:不作分词处理

比如:商品id、订单号、身份证号等

2、 是否索引(indexed)

是:将Field分析后的词或整个Field值进行索引,只有索引方可搜索到。

比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。

否:不索引无法搜索到

比如:商品id、文件路径、图片路径等,不用作为查询条件的不用索引。

3、 是否存储(stored)

是:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取

比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。

否:不存储Field值,不存储的Field无法通过Document获取

比如:商品简介,内容较大不用存储。如果要向用户展示商品简介可以从系统的关系数据库中获取商品简介。

3.4.2 Field常用类型


下边列出了开发中常用 Filed类型,注意Field的属性,根据需求选择:

 


3.5 IndexWriterDirectory


IndexWriter是索引过程的核心组件,通过IndexWriter可以创建新索引、更新索引、删除索引操作。IndexWriter需要通过Directory对索引进行存储操作。

Directory描述了索引的存储位置,底层封装了I/O操作,负责对索引进行存储。它的子类常用的包括FSDirectory(在文件系统存储索引)、RAMDirectory(在内存存储索引)。


3.6 索引程序代码


分析:

确定文档的各各域的属性和类型:

文件名称:不分析、索引、存储,使用StringField方法

文件路径:不分析、不索引、存储,使用StoredField方法

文件大小:分析、索引、存储,使用LongField方法

文件内容:分析,索引,不存储,使用TextField方法

代码如下:

  1. public class IndexTest {  
  2.   
  3.    
  4.   
  5. // 索引源,即源数据目录  
  6.   
  7. private static String searchSource = “F:\\develop\\lucene\\searchsource”;  
  8.   
  9.    
  10.   
  11. // 索引目标地址  
  12.   
  13. private static String indexFolder = “F:\\develop\\lucene\\indexdata”;  
  14.   
  15.    
  16.   
  17. @Test  
  18.   
  19. public void testCreateIndex() {  
  20.   
  21.    
  22.   
  23. try {  
  24.   
  25.    
  26.   
  27.    //从目录中读取文件内容并创建Document文档  
  28.   
  29. List<Document> docs = IndexUtils.file2Document(searchSource);  
  30.   
  31. //创建分析器,standardAnalyzer标准分析器  
  32.   
  33. Analyzer standardAnalyzer = new StandardAnalyzer();  
  34.   
  35. // 指定索引存储目录  
  36.   
  37. Directory directory = FSDirectory.open(new File(indexFolder));  
  38.   
  39. //创建索引操作配置对象  
  40.   
  41. IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_4_10_3,  
  42.   
  43. standardAnalyzer);  
  44.   
  45. // 定义索引操作对象indexWriter  
  46.   
  47. IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);  
  48.   
  49. // 遍历目录 下的文件生成的文档,调用indexWriter方法创建索引  
  50.   
  51. for (Document document : docs) {  
  52.   
  53. indexWriter.addDocument(document);  
  54.   
  55. }  
  56.   
  57. // 索引操作流关闭  
  58.   
  59. indexWriter.close();  
  60.   
  61.    
  62.   
  63. catch (IOException e) {  
  64.   
  65. e.printStackTrace();  
  66.   
  67. }  
  68.   
  69.    
  70.   
  71. }  
  72.   
  73. }  
public class IndexTest {



// 索引源,即源数据目录

private static String searchSource = "F:\\develop\\lucene\\searchsource";



// 索引目标地址

private static String indexFolder = "F:\\develop\\lucene\\indexdata";



@Test

public void testCreateIndex() {



try {



   //从目录中读取文件内容并创建Document文档

List<Document> docs = IndexUtils.file2Document(searchSource);

//创建分析器,standardAnalyzer标准分析器

Analyzer standardAnalyzer = new StandardAnalyzer();

// 指定索引存储目录

Directory directory = FSDirectory.open(new File(indexFolder));

//创建索引操作配置对象

IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_4_10_3,

standardAnalyzer);

// 定义索引操作对象indexWriter

IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);

// 遍历目录 下的文件生成的文档,调用indexWriter方法创建索引

for (Document document : docs) {

indexWriter.addDocument(document);

}

// 索引操作流关闭

indexWriter.close();



} catch (IOException e) {

e.printStackTrace();

}



}

}


IndexUtils.file2Document()方法定义如下:

  1. // 从文件创建Document  
  2.   
  3. public static List<Document> file2Document(String folderPath)  
  4.   
  5. throws IOException {  
  6.   
  7.    
  8.   
  9. List<Document> list = new ArrayList<Document>();  
  10.   
  11.    
  12.   
  13. File folder = new File(folderPath);  
  14.   
  15. if (!folder.isDirectory()) {  
  16.   
  17. return null;  
  18.   
  19. }  
  20.   
  21. // 获取目录 中的所有文件  
  22.   
  23. File[] files = folder.listFiles();  
  24.   
  25. for (File file : files) {  
  26.   
  27. //文件名称  
  28.   
  29. String fileName = file.getName();  
  30.   
  31. System.out.println(fileName);  
  32.   
  33. if (fileName.lastIndexOf(“.txt”) > 0) {  
  34.   
  35.    
  36.   
  37. // 文件内容  
  38.   
  39. String fileContent = FileUtils.readFileToString(file);  
  40.   
  41. //文件路径  
  42.   
  43. String filePath = file.getAbsolutePath();  
  44.   
  45. //文件大小  
  46.   
  47. long fileSize = FileUtils.sizeOf(file);  
  48.   
  49. //创建文档  
  50.   
  51. Document doc = new Document();  
  52.   
  53.    
  54.   
  55. //创建各各Field域  
  56.   
  57. //文件名  
  58.   
  59. Field field_fileName = new StringField(“fileName”, fileName, Store.YES);  
  60.   
  61. //文件内容  
  62.   
  63. Field field_fileContent = new TextField(“fileContent”, fileContent, Store.NO);  
  64.   
  65. //文件大小  
  66.   
  67. Field field_fileSize = new LongField(“fileSize”, fileSize, Store.YES);  
  68.   
  69. //文件路径  
  70.   
  71. Field field_filePath = new StoredField(“filePath”, filePath, Store.YES);  
  72.   
  73.    
  74.   
  75. //将各各Field添加到文档中  
  76.   
  77. doc.add(field_fileName);  
  78.   
  79. doc.add(field_fileContent);  
  80.   
  81. doc.add(field_fileSize);  
  82.   
  83. doc.add(field_filePath);  
  84.   
  85. list.add(doc);  
  86.   
  87. }  
  88.   
  89. }  
  90.   
  91.    
  92.   
  93. return list;  
  94.   
  95.    
  96.   
  97. }  
  98.   
  99.    
// 从文件创建Document

public static List<Document> file2Document(String folderPath)

throws IOException {



List<Document> list = new ArrayList<Document>();



File folder = new File(folderPath);

if (!folder.isDirectory()) {

return null;

}

// 获取目录 中的所有文件

File[] files = folder.listFiles();

for (File file : files) {

//文件名称

String fileName = file.getName();

System.out.println(fileName);

if (fileName.lastIndexOf(".txt") > 0) {



// 文件内容

String fileContent = FileUtils.readFileToString(file);

//文件路径

String filePath = file.getAbsolutePath();

//文件大小

long fileSize = FileUtils.sizeOf(file);

//创建文档

Document doc = new Document();



//创建各各Field域

//文件名

Field field_fileName = new StringField("fileName", fileName, Store.YES);

//文件内容

Field field_fileContent = new TextField("fileContent", fileContent, Store.NO);

//文件大小

Field field_fileSize = new LongField("fileSize", fileSize, Store.YES);

//文件路径

Field field_filePath = new StoredField("filePath", filePath, Store.YES);



//将各各Field添加到文档中

doc.add(field_fileName);

doc.add(field_fileContent);

doc.add(field_fileSize);

doc.add(field_filePath);

list.add(doc);

}

}



return list;



}

 


4 Search搜索

4.1 索引结构

Lucene的索引结构为倒排索引,倒排文件或倒排索引是指索引对象是文档或者文档集合中的单词等,用来存储这些单词在一个文档或者一组文档中的存储位置,是对文档或者文档集合的一种最常用的索引机制。

 

Lucene索引index由若干段(segment)组成,每一段由若干的文档(document)组成,每一个文档由若干的域(field)组成,每一个域由若干的项(term)组成。

项是最小的索引单位,如果Field进行分析,可能会分析出多个语汇单元(词),每个词就是一个Term项,如果Field不进行分析,整个Field就是一个Term项。

项直接代表了一个字符串以及其在文件中的位置、出现次数等信息。域将项和文档进行关联。

4.2 使用Luke查看索引


Luke作为Lucene工具包中的一个工具(http://www.getopt.org/luke/),用于查询、修改lucene的索引文件。

打开Luke方法:

cmd运行:java  -jar lukeall-4.10.3.jar

如果需要加载第三方分词器,如果需要加载第三方分词器,需通过java.ext.dirs加载jar:

可简单的将第三方分词器和lukeall放在一块儿,cmd下运行:。

java -Djava.ext.dirs=. -jar lukeall-4.10.3.jar

 

4.3 确定索引目录


搜索是要从索引中查找,确定索引目录即上边创建的索引目录 F:\develop\lucene\indexdata

4.4 IndexSearcherIndexReader


通过IndexSearcher执行搜索,构建IndexSearcher需要IndexReader读取索引目录,如下图:

 

代码如下:

  1. // 指定 目录  
  2.   
  3. File folder = new File(indexFolder);  
  4.   
  5. Directory directory = FSDirectory.open(folder);  
  6.   
  7. // indexReader  
  8.   
  9. IndexReader indexReader = DirectoryReader.open(directory);  
  10.   
  11. // 定义indexSearcher  
  12.   
  13. IndexSearcher indexSearcher = new IndexSearcher(indexReader);  
  14.   
  15.    
// 指定 目录

File folder = new File(indexFolder);

Directory directory = FSDirectory.open(folder);

// indexReader

IndexReader indexReader = DirectoryReader.open(directory);

// 定义indexSearcher

IndexSearcher indexSearcher = new IndexSearcher(indexReader);

 

需要注意:

Indexreader打开需要耗费很大的系统资源,建议使用一个IndexReader,如果索引进行添加、修改或删除需要打开新的Reader才可以搜索到。

IndexSearcher搜索方法如下:

 


4.5 搜索程序代码

  1. public class SearchTest {  
  2.   
  3. // 索引目录地址  
  4.   
  5. private static String indexFolder = “F:\\develop\\lucene\\indexdata”;  
  6.   
  7.    
  8.   
  9. //查询方法  
  10.   
  11. @Test  
  12.   
  13. public void testTermQuery() throws IOException {  
  14.   
  15.    
  16.   
  17. // 创建查询对象,根据文件名称域搜索匹配文件名称的文档  
  18.   
  19. Query query = new TermQuery(new Term(“fileName”“springmvc_test.txt”));  
  20.   
  21.    
  22.   
  23. // 指定索引目录  
  24.   
  25. Directory directory = FSDirectory.open(new File(indexFolder));  
  26.   
  27.    
  28.   
  29. // 定义IndexReader  
  30.   
  31. IndexReader reader = DirectoryReader.open(directory);  
  32.   
  33. // 创建indexSearcher  
  34.   
  35. IndexSearcher indexSearcher = new IndexSearcher(reader);  
  36.   
  37. // 执行搜索  
  38.   
  39. TopDocs topDocs = indexSearcher.search(query, 100);  
  40.   
  41. // 提取搜索结果  
  42.   
  43. ScoreDoc[] scoreDocs = topDocs.scoreDocs;  
  44.   
  45.    
  46.   
  47. System.out.println(”共搜索到总记录数:” + topDocs.totalHits);  
  48.   
  49.    
  50.   
  51. for (ScoreDoc scoreDoc : scoreDocs) {  
  52.   
  53. // 文档id  
  54.   
  55. int docID = scoreDoc.doc;  
  56.   
  57. // 得到文档  
  58.   
  59. Document doc = indexSearcher.doc(docID);  
  60.   
  61. // 输出 文件内容  
  62.   
  63. IndexUtils.printDocumentOfFile(doc);  
  64.   
  65. }  
  66.   
  67.    
  68.   
  69. }  
public class SearchTest {

// 索引目录地址

private static String indexFolder = "F:\\develop\\lucene\\indexdata";



//查询方法

@Test

public void testTermQuery() throws IOException {



// 创建查询对象,根据文件名称域搜索匹配文件名称的文档

Query query = new TermQuery(new Term("fileName", "springmvc_test.txt"));



// 指定索引目录

Directory directory = FSDirectory.open(new File(indexFolder));



// 定义IndexReader

IndexReader reader = DirectoryReader.open(directory);

// 创建indexSearcher

IndexSearcher indexSearcher = new IndexSearcher(reader);

// 执行搜索

TopDocs topDocs = indexSearcher.search(query, 100);

// 提取搜索结果

ScoreDoc[] scoreDocs = topDocs.scoreDocs;



System.out.println("共搜索到总记录数:" + topDocs.totalHits);



for (ScoreDoc scoreDoc : scoreDocs) {

// 文档id

int docID = scoreDoc.doc;

// 得到文档

Document doc = indexSearcher.doc(docID);

// 输出 文件内容

IndexUtils.printDocumentOfFile(doc);

}



}


IndexUtils.printDocumentOfFile()方法:

  1. //从文档中读取文件内容  
  2.   
  3. public static void printDocumentOfFile(Document doc){  
  4.   
  5. System.out.println(”——————————”);  
  6.   
  7. System.out.println(”文件名称 =” + doc.get(“fileName”));  
  8.   
  9. System.out.println(”文件大小 =” + doc.get(“fileSize”));  
  10.   
  11. System.out.println(”文件内容 =” + doc.get(“fileContent”));  
  12.   
  13. }  
//从文档中读取文件内容

public static void printDocumentOfFile(Document doc){

System.out.println("------------------------------");

System.out.println("文件名称 =" + doc.get("fileName"));

System.out.println("文件大小 =" + doc.get("fileSize"));

System.out.println("文件内容 =" + doc.get("fileContent"));

}


4.6 TopDocs

Lucene搜索结果可通过TopDocs遍历,TopDocs类提供了少量的属性,如下:

 


注意:

Search方法需要指定匹配记录数量nindexSearcher.search(query, n)

TopDocs.totalHits:是匹配索引库中所有记录的数量

TopDocs.scoreDocs:匹配相关度高的前边记录数组,scoreDocs的长度小于等于search方法指定的参数n

猜你喜欢

转载自blog.csdn.net/qq_33384065/article/details/79805659