Lucene&Solr(之一)-全文索引、入门程序

时间:2021-07-11 20:56:55


什么是全文检索

1、生活中的数据类型分成: a、结构化数据:类型固定、格式固定、有限长度的数据。如数据库中的数据 b、非结构化数据:格式不固定、长度不固定的数据。如txt文档、word文档和excel文档、pdf等。
2、数据查询: 结构化数据查询:一般使用sql进行查询( Structure Query Language )。 非结构化数据查询:
a、通过顺序检索,一个一个看…… b、自己写程序,通过IO流读取,匹配字符串。 c、把非结构化数据变成结构化数据。 如:将一段英文文本根据空格拆分,得到单词列表,记录下单词和文本的关系。 查询时只需要查询单词列表,根据单词和文件的对应关系找到文件。基于单词列表创建索引提高查询速度。 这个过程就叫全文检索。
3、全文检索: 这种先建立索引,再对索引进行搜索的过程就叫全文检索。一次创建多次查询。

Lucene实现全文检索的流程

Lucene可以实现全文检索,Lucene是apache旗下一个开源的全文检索引擎工具包。
全文检索的应用场景: 1、搜索引擎:百度一下等 2、电商搜索:淘宝、天猫等 3、论坛搜索:论坛、微博等

Lucene实现全文检索的流程

1、创建索引

原始文档:基于哪些内容进行搜索。 如果是文件搜索,要查询的文件就是原始文档; 搜索引擎:整个互联网的网页内容 电商搜索:所有商品数据
a、获得原始文档
如果是文件:使用IO流读取文件内容
搜索引擎:使用爬虫抓取网页 在internet上采集信息的软件通常称为爬虫或蜘蛛,也成网络机器人。爬取互联网的网页,原理其实是模拟浏览器。 Heritrix:java开发的开源的爬虫,用户可以自己使用它从网上抓取资源,良好的可扩展性,方便用户自定逻辑。
Nutch:apache旗下爬虫程序。lucene&solr都是apache旗下的。
jsoup:java的解析html工具包,可通过DOM、CSS以及类似于JQuery的操作方法取出和操作数据。

电商网站:查询数据库。
b、创建文档对象。每个原始文档对应一个文档对象Document对象。 向Document中添加field,field叫做域。每个field中保存原始文档的一个属性。 每个Document可以有多个field,不同的Document可以有不同的field,同一个Document可以有相同的Field(域名和域值都相同) 每个文档都有唯一id。
c、分析文档内容 1、先对文档内容根据空格进行字符串拆分,得到一个单词列表。 2、去除标点符号,去除停用词,转换成小写。 最终得到一个词汇单元流(理解为一个一个的单词)。 基于词汇单元流创建索引。索引是一个提高查询速度的数据结构。
d、创建索引库 把索引和文档对象写入磁盘。 索引部分、文档部分、关键字和文档之间的对应关系。
每个单词叫做term,term包括两部分内容,一个关键字和关键字所在的域。 如文件名中包含apache和文件内容中包含的apache是不同的term。

2、查询索引


a、用户查询接口 用户输入查询条件的地方。如百度搜索框
b、接收查询条件 查询对象中包含要搜索的域和要搜索的内容。 lucene基本查询语法: 域的名称:关键字---fileName:lucene
c、查询索引 查询索引库中的索引部分,在某个域上查询关键字。找到关键字得到文档id列表。
d、渲染结果 1、根据id获取文档对象。 2、从文档的field中取内容,展示给用户。 3、关键字高亮显示、相关度排序、分页等效果。

入门程序

1、jar包 lucene核心core文件夹下jar、analyzer-common包、common-io包
2、创建索引库的实现步骤: 1)创建java工程 2)导入lucene相关包
3)创建Directory对象,索引库保存的目录 4)创建IndexWriterConfig对象,参数1:lucene的版本号,参数2:分析器对象,使用标准分析器(英文文本) 5)创建IndexWriter对象,传入IndexWriterConfig对象 6)使用IO流,读取文件信息 7)对应每个文件创建一个Document对象 8)创建field对象,向文档对象中添加域 9)把文档对象写入索引库 10)关闭流
代码实现:
@Test
public void createIndex() throws Exception{
// 3)创建Directory对象,索引库保存的目录
//Directory directory = new RAMDirectory();// 索引库存放位置:内存
Directory directory = FSDirectory.open(new File("F:\\index"));
// 4)创建IndexWriterConfig对象,参数1:lucene的版本号,参数2:分析器对象,使用标准分析器(英文文本)
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
// 5)创建IndexWriter对象,传入IndexWriterConfig对象
IndexWriter writer = new IndexWriter(directory, config);

File file = new File("E:\\就业\\课程\\Lucene&Solr\\00.参考资料\\searchsource");
for (File f : file.listFiles()) {
// 6)使用IO流,读取文件信息
String fileName = f.getName();
String fileContent = FileUtils.readFileToString(f);
String filePath = f.getPath();
long fileSize = FileUtils.sizeOf(f);
// 7)对应每个文件创建一个Document对象
Document document = new Document();
// 8)创建field对象,向文档对象中添加域
// 参数1:域的名称,参数2:域的内容,参数3:是否存储
Field field1 = new TextField("name", fileName, Store.YES);
Field field2 = new TextField("content", fileContent, Store.YES);
Field field3 = new TextField("path", filePath, Store.YES);
Field field4 = new TextField("size", fileSize + "", Store.YES);
document.add(field1);
document.add(field2);
document.add(field3);
document.add(field4);
// 9)把文档对象写入索引库
writer.addDocument(document);
}
// 10)关闭流
writer.close();
}



3、查询索引的实现步骤: 1)打开索引库,以读的方式打开。创建IndexReader对象,需要创建Document对象 2)创建IndexSearcher对象,传入IndexReader对象。 3)创建一个查询对象,Query的子类TermQuery,传入两个参数:要搜索的域和要查询的内容 4)执行查询,得到一个文档id列表 5)遍历列表根据文档的id取文档对象 6)从域中取内容,并打印 7)关闭流
代码实现:
@Test
public void searchIndex() throws Exception {
// 1)打开索引库,以读的方式打开。创建IndexReader对象,需要创建Document对象
Directory directory = FSDirectory.open(new File("F:\\index"));
IndexReader reader = DirectoryReader.open(directory);
// 2)创建IndexSearcher对象,传入IndexReader对象。
IndexSearcher searcher = new IndexSearcher(reader);
// 3)创建一个查询对象,Query的子类TermQuery,传入两个参数:要搜索的域和要查询的内容
Query query = new TermQuery(new Term("name", "apache"));
// 4)执行查询,得到一个文档id列表
//参数1:查询对象,参数2:查询结果返回的最大记录数,返回最多10个
TopDocs topDocs = searcher.search(query, 10);
// 查询的总记录数
System.err.println("本次查询结果总记录数:" + topDocs.totalHits);
// 5)遍历列表根据文档的id取文档对象
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
// 去文档id
int id = scoreDoc.doc;
// 根据id去文档对象
Document document = searcher.doc(id);
// 6)从域中取内容,并打印
System.out.println("==========================================================================================");
System.out.println(document.get("name"));
System.out.println(document.get("path"));
System.out.println(document.get("size"));
//System.out.println(document.get("content"));
}
// 7)关闭流
reader.close();
}

TopDocs

lucene搜索结果可通过TopDocs遍历,提供属性: totalHits :匹配搜索条件的总记录数
scoreDocs :相关度高的顶部匹配记录,长度<=search方法指定的n。



分析器

在使用分析器之前,需要先查看分析器的分词效果。 每个分析器对象,都有个tokenStream的方法,返回一个TokenStream对象,通过查看TokenStream对象中的内容,分词效果。 使用方法: 1)创建Analyzer对象 2)调用Analyzer的tokenStream,得到TokenStream对象,参数就是待分析的文本。 3)设置一个引用,通过这个引用查看分词效果 4)调用TokenStream的reset方法。初始化这个引用 5)循环遍历TokenStream打印结果。
代码实现:
@Test
public void testTokenStream() throws Exception {
// 创建一个标准分词器
Analyzer analyzer = new StandardAnalyzer();
// 获取TokenStream对象
TokenStream tokenStream = analyzer.tokenStream("此处写域名", "The Spring Framework provides a comprehensive programming and configuration model.");
// 3)设置一个引用,通过这个引用查看分词效果
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
// 4)调用TokenStream的reset方法。初始化这个引用
tokenStream.reset();
// 5)循环遍历TokenStream打印结果。
while(tokenStream.incrementToken()) {
System.out.println(charTermAttribute.toString());
}
}


中文分析器

推荐第三方: 这里使用 IKAnalyzer 1、添加jar包到工程中。 2、第三方的配置文件、扩展词典文件、停用字词典文件配置到classpath下
注意:必须保证词典文件编码格式为 utf-8
代码实现:
@Test
public void testTokenStream() throws Exception {
Analyzer analyzer = null;
// 创建一个标准分词器
//analyzer = new StandardAnalyzer();
// 二分法分词
//analyzer = new CJKAnalyzer();
//扩展性差
//analyzer = new SmartChineseAnalyzer();
// 第三方 IK-analyzer
analyzer = new IKAnalyzer();

// 获取TokenStream对象
String str = "Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,";
TokenStream tokenStream = analyzer.tokenStream("此处写域名", str);
// 3)设置一个引用,通过这个引用查看分词效果
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
// 4)调用TokenStream的reset方法。初始化这个引用
tokenStream.reset();
// 5)循环遍历TokenStream打印结果。
while(tokenStream.incrementToken()) {
System.out.println(charTermAttribute.toString());
}
}