lucene_全文检索技术

lucene

文章目录

  • lucene
    • 1 什么是全文检索
      • 1.1 数据的分类
      • 1.2 数据的查询
      • 1.3 全文检索
    • 2 全文检索的应用场景
    • 3 什么是Lucene
    • 4 Lucene实现全文检索的流程
      • 4.1 创建索引
      • 4.2 查询索引
        • 4.2.1 用户查询接口
        • 4.2.2 创建查询
        • 4.2.3 执行查询
        • 4.2.4 渲染结果
    • 5 配置开发环境
      • 5.1Lucene下载
      • 5.2 使用的jar包
    • 6 入门程序
      • 6.1 实现步骤
      • 6.2 代码实现
      • 6.3查询索引
        • 6.3.1实现步骤
    • 7 分析器
      • 7.1分析器的分词效果
      • 7.2 中文分析器
      • 7.3使用自定义分析器
    • 8 索引库的维护
      • 8.1 索引库的添加
        • 8.1.1 Field域的属性
        • 8.1.2 添加文档代码实现
      • 8.2 索引库删除
        • 8.2.1 删除全部
        • 8.2.2 指定查询条件删除
      • 8.3索引库的修改
    • 9 Lucene索引库查询
      • 9.1 TermQuery
      • 9.2 数值范围查询
      • 9.3 使用queryparser查询

1 什么是全文检索

在这里插入图片描述

1.1 数据的分类

1)结构化数据

  • 格式固定、长度固定、数据类型固定。
  • 例如数据库中的数据

2)非结构化数据

  • word文档、pdf文档、邮件、html、txt
  • 格式不固定、长度不固定、数据类型不固定。

1.2 数据的查询

1)结构化数据的查询

  • SQL语句,查询结构化数据的方法。简单、速度快。

2)非结构化数据的查询

  • 从文本文件中找出包含spring单词的文件。
    1、目测
    2、使用程序吧文档读取到内存中,然后匹配字符串。顺序扫描。
    3、把非结构化数据变成结构化数据
    先跟根据空格进行字符串拆分,得到一个单词列表,基于单词列表创建一个索引。
    然后查询索引,根据单词和文档的对应关系找到文档列表。这个过程叫做全文检索。

索引:一个为了提高查询速度,创建某种数据结构的集合。

1.3 全文检索

  • 先创建索引然后查询索引的过程叫做全文检索。
  • 索引一次创建可以多次使用。表现为每次查询速度很快。

在这里插入图片描述

2 全文检索的应用场景

1、搜索引擎
百度、360搜索、谷歌、搜狗
2、站内搜索
论坛搜索、微博、文章搜索
3、电商搜索
淘宝搜索、京东搜索
4、只要是有搜索的地方就可以使用全文检索技术。

3 什么是Lucene

Lucene是一个基于Java开发全文检索工具包。

4 Lucene实现全文检索的流程

在这里插入图片描述

4.1 创建索引

1)获得文档

  • 原始文档:要基于那些数据来进行搜索,那么这些数据就是原始文档。
  • 搜索引擎:使用爬虫获得原始文档
  • 站内搜索:数据库中的数据。
  • 案例:直接使用io流读取磁盘上的文件。

2)构建文档对象

  • 对应每个原始文档创建一个Document对象
  • 每个document对象中包含多个域(field)
  • 域中保存就是原始文档数据。
    - 域的名称
    - 域的值
  • 每个文档都有一个唯一的编号,就是文档id

在这里插入图片描述
3)分析文档
就是分词的过程

  • 1、根据空格进行字符串拆分,得到一个单词列表
  • 2、把单词统一转换成小写。
  • 3、去除标点符号
  • 4、去除停用词:停用词:无意义的词
  • 每个关键词都封装成一个Term对象中。

4)创建索引

在这里插入图片描述

  • 基于关键词列表创建一个索引。保存到索引库中。
  • 索引库中:索引,document对象,关键词和文档的对应关系
  • 通过词语找文档,这种索引的结构叫倒排索引结构。

注意:创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构。
传统方法是根据文件找到该文件的内容,在文件内容中匹配搜索关键字,这种方法是顺序扫描方法,数据量大、搜索慢。
在这里插入图片描述

4.2 查询索引

查询索引也是搜索的过程。搜索就是用户输入关键字,从索引(index)中进行搜索的过程。根据关键字搜索索引,根据索引找到对应的文档,从而找到要搜索的内容(这里指磁盘上的文件)。

4.2.1 用户查询接口

全文检索系统提供用户搜索的界面供用户提交搜索的关键字,搜索完成展示搜索结果。
在这里插入图片描述

Lucene不提供制作用户搜索界面的功能,需要根据自己的需求开发搜索界面。

4.2.2 创建查询

用户输入查询关键字执行搜索之前需要先构建一个查询对象,查询对象中可以指定查询要搜索的Field文档域、查询关键字等,查询对象会生成具体的查询语法,
例如:
语法 “fileName:lucene”表示要搜索Field域的内容为“lucene”的文档

4.2.3 执行查询

搜索索引过程:
根据查询语法在倒排索引词典表中分别找出对应搜索词的索引,从而找到索引所链接的文档链表。
比如搜索语法为“fileName:lucene”表示搜索出fileName域中包含Lucene的文档。
搜索过程就是在索引上查找域为fileName,并且关键字为Lucene的term,并根据term找到文档id列表。

在这里插入图片描述

4.2.4 渲染结果

以一个友好的界面将查询结果展示给用户,用户根据搜索结果找自己想要的信息,为了帮助用户很快找到自己的结果,提供了很多展示的效果,比如搜索结果中将关键字高亮显示,百度提供的快照等。

在这里插入图片描述

5 配置开发环境

5.1Lucene下载

Lucene是开发全文检索功能的工具包,从官方网站下载lucene-7.4.0,并解压。

在这里插入图片描述

官方网站:http://lucene.apache.org/
版本:lucene-7.4.0
Jdk要求:1.8以上

5.2 使用的jar包

lucene-core-7.4.0.jar

在这里插入图片描述
在这里插入图片描述

6 入门程序

实现一个文件的搜索功能,通过关键字搜索文件,凡是文件名或文件内容包括关键字的文件都需要找出来。还可以根据中文词语进行查询,并且需要支持多个条件查询。
本案例中的原始内容就是磁盘上的文件,如下图:

在这里插入图片描述

6.1 实现步骤

第一步:创建一个java工程,并导入jar包。
第二步:创建一个indexwriter对象。

  • 1)指定索引库的存放位置Directory对象
  • 2)指定一个IndexWriterConfig对象。

第二步:创建document对象。
第三步:创建field对象,将field添加到document对象中。
第四步:使用indexwriter对象将document对象写入索引库,此过程进行索引创建。并将索引和document对象写入索引库。
第五步:关闭IndexWriter对象。

6.2 代码实现

//创建索引
@Test
public void createIndex() throws Exception {//指定索引库存放的路径//D:\temp\indexDirectory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());//索引库还可以存放到内存中//Directory directory = new RAMDirectory();//创建indexwriterCofig对象IndexWriterConfig config = new IndexWriterConfig();//创建indexwriter对象IndexWriter indexWriter = new IndexWriter(directory, config);//原始文档的路径File dir = new File("D:\\temp\\searchsource");for (File f : dir.listFiles()) {//文件名String fileName = f.getName();//文件内容String fileContent = FileUtils.readFileToString(f);//文件路径String filePath = f.getPath();//文件的大小long fileSize  = FileUtils.sizeOf(f);//创建文件名域//第一个参数:域的名称//第二个参数:域的内容//第三个参数:是否存储Field fileNameField = new TextField("filename", fileName, Field.Store.YES);//文件内容域Field fileContentField = new TextField("content", fileContent, Field.Store.YES);//文件路径域(不分析、不索引、只存储)Field filePathField = new TextField("path", filePath, Field.Store.YES);//文件大小域Field fileSizeField = new TextField("size", fileSize + "", Field.Store.YES);//创建document对象Document document = new Document();document.add(fileNameField);document.add(fileContentField);document.add(filePathField);document.add(fileSizeField);//创建索引,并写入索引库indexWriter.addDocument(document);}//关闭indexwriterindexWriter.close();
}

使用Luke工具查看索引文件
在这里插入图片描述

我们使用的luke的版本是luke-7.4.0,跟lucene的版本对应的。可以打开7.4.0版本的lucene创建的索引库。需要注意的是此版本的Luke是jdk9编译的,所以要想运行此工具还需要jdk9才可以。

6.3查询索引

6.3.1实现步骤

第一步:创建一个Directory对象,也就是索引库存放的位置。
第二步:创建一个indexReader对象,需要指定Directory对象。
第三步:创建一个indexsearcher对象,需要指定IndexReader对象
第四步:创建一个TermQuery对象,指定查询的域和查询的关键词。
第五步:执行查询。
第六步:返回查询结果。遍历查询结果并输出。
第七步:关闭IndexReader对象

代码实现

//查询索引库
@Test
public void searchIndex() throws Exception {//指定索引库存放的路径//D:\temp\indexDirectory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());//创建indexReader对象IndexReader indexReader = DirectoryReader.open(directory);//创建indexsearcher对象IndexSearcher indexSearcher = new IndexSearcher(indexReader);//创建查询Query query = new TermQuery(new Term("filename", "apache"));//执行查询//第一个参数是查询对象,第二个参数是查询结果返回的最大值TopDocs topDocs = indexSearcher.search(query, 10);//查询结果的总条数System.out.println("查询结果的总条数:"+ topDocs.totalHits);//遍历查询结果//topDocs.scoreDocs存储了document对象的idfor (ScoreDoc scoreDoc : topDocs.scoreDocs) {//scoreDoc.doc属性就是document对象的id//根据document的id找到document对象Document document = indexSearcher.doc(scoreDoc.doc);System.out.println(document.get("filename"));//System.out.println(document.get("content"));System.out.println(document.get("path"));System.out.println(document.get("size"));System.out.println("-------------------------");}//关闭indexreader对象indexReader.close();
}

7 分析器

7.1分析器的分词效果

@Test
public void testTokenStream() throws Exception {//创建一个标准分析器对象Analyzer analyzer = new StandardAnalyzer();//获得tokenStream对象//第一个参数:域名,可以随便给一个//第二个参数:要分析的文本内容TokenStream tokenStream = analyzer.tokenStream("test", "The Spring Framework provides a comprehensive programming and configuration model.");//添加一个引用,可以获得每个关键词CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);//添加一个偏移量的引用,记录了关键词的开始位置以及结束位置OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);//将指针调整到列表的头部tokenStream.reset();//遍历关键词列表,通过incrementToken方法判断列表是否结束while(tokenStream.incrementToken()) {//关键词的起始位置System.out.println("start->" + offsetAttribute.startOffset());//取关键词System.out.println(charTermAttribute);//结束位置System.out.println("end->" + offsetAttribute.endOffset());}tokenStream.close();
}

7.2 中文分析器

7.2.1 Lucene自带中文分词器

  • StandardAnalyzer:
    单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”,
    效果:“我”、“爱”、“中”、“国”。
  • SmartChineseAnalyzer
    对中文支持较好,但扩展性差,扩展词库,禁用词库和同义词库等不好处理

7.2.2 IKAnalyzer

在这里插入图片描述

使用方法:
第一步:把jar包添加到工程中
第二步:把配置文件和扩展词典和停用词词典添加到classpath下

注意:hotword.dic和ext_stopword.dic文件的格式为UTF-8,注意是无BOM 的UTF-8 编码。
也就是说禁止使用windows记事本编辑扩展词典文件

使用EditPlus.exe保存为无BOM 的UTF-8 编码格式,如下图:

在这里插入图片描述

7.3使用自定义分析器

@Test
public void addDocument() throws Exception {//索引库存放路径Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());//创建一个indexwriter对象IndexWriter indexWriter = new IndexWriter(directory, config);
//...
}

8 索引库的维护

8.1 索引库的添加

8.1.1 Field域的属性

是否分析:是否对域的内容进行分词处理。前提是我们要对域的内容进行查询。
是否索引:将Field分析后的词或整个Field值进行索引,只有索引方可搜索到。
比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。
是否存储:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取
比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。

是否存储的标准:是否要将内容展示给用户

在这里插入图片描述

8.1.2 添加文档代码实现

//添加索引
@Test
public void addDocument() throws Exception {//索引库存放路径Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());//创建一个indexwriter对象IndexWriter indexWriter = new IndexWriter(directory, config);//创建一个Document对象Document document = new Document();//向document对象中添加域。//不同的document可以有不同的域,同一个document可以有相同的域。document.add(new TextField("filename", "新添加的文档", Field.Store.YES));document.add(new TextField("content", "新添加的文档的内容", Field.Store.NO));//LongPoint创建索引document.add(new LongPoint("size", 1000l));//StoreField存储数据document.add(new StoredField("size", 1000l));//不需要创建索引的就使用StoreField存储document.add(new StoredField("path", "d:/temp/1.txt"));//添加文档到索引库indexWriter.addDocument(document);//关闭indexwriterindexWriter.close();}

8.2 索引库删除

8.2.1 删除全部

//删除全部索引@Testpublic void deleteAllIndex() throws Exception {IndexWriter indexWriter = getIndexWriter();//删除全部索引indexWriter.deleteAll();//关闭indexwriterindexWriter.close();
}

此方法慎用!!

8.2.2 指定查询条件删除

//根据查询条件删除索引@Testpublic void deleteIndexByQuery() throws Exception {IndexWriter indexWriter = getIndexWriter();//创建一个查询条件Query query = new TermQuery(new Term("filename", "apache"));//根据查询条件删除indexWriter.deleteDocuments(query);//关闭indexwriterindexWriter.close();}

8.3索引库的修改

原理就是先删除后添加

//修改索引库
@Test
public void updateIndex() throws Exception {IndexWriter indexWriter = getIndexWriter();//创建一个Document对象Document document = new Document();//向document对象中添加域。//不同的document可以有不同的域,同一个document可以有相同的域。document.add(new TextField("filename", "要更新的文档", Field.Store.YES));document.add(new TextField("content", " Lucene 简介 Lucene 是一个基于 Java 的全文信息检索工具包," +"它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能。",Field.Store.YES));indexWriter.updateDocument(new Term("content", "java"), document);//关闭indexWriterindexWriter.close();
}

9 Lucene索引库查询

对要搜索的信息创建Query查询对象,Lucene会根据Query查询对象生成最终的查询语法,类似关系数据库Sql语法一样Lucene也有自己的查询语法,比如:“name:lucene”表示查询Field的name为“lucene”的文档信息。
可通过两种方法创建查询对象:
1)使用Lucene提供Query子类
2)使用QueryParse解析查询表达式

9.1 TermQuery

TermQuery,通过项查询,TermQuery不使用分析器所以建议匹配不分词的Field域查询,比如订单号、分类ID号等。
指定要查询的域和要查询的关键词。

//使用Termquery查询
@Test
public void testTermQuery() throws Exception {Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());IndexReader indexReader = DirectoryReader.open(directory);IndexSearcher indexSearcher = new IndexSearcher(indexReader);//创建查询对象Query query = new TermQuery(new Term("content", "lucene"));//执行查询TopDocs topDocs = indexSearcher.search(query, 10);//共查询到的document个数System.out.println("查询结果总数量:" + topDocs.totalHits);//遍历查询结果for (ScoreDoc scoreDoc : topDocs.scoreDocs) {Document document = indexSearcher.doc(scoreDoc.doc);System.out.println(document.get("filename"));//System.out.println(document.get("content"));System.out.println(document.get("path"));System.out.println(document.get("size"));}//关闭indexreaderindexSearcher.getIndexReader().close();
}

9.2 数值范围查询

@Test
public void testRangeQuery() throws Exception {IndexSearcher indexSearcher = getIndexSearcher();Query query = LongPoint.newRangeQuery("size", 0l, 10000l);printResult(query, indexSearcher);
}

9.3 使用queryparser查询

通过QueryParser也可以创建Query,QueryParser提供一个Parse方法,此方法可以直接根据查询语法来查询。Query对象执行的查询语法可通过System.out.println(query);查询。
需要使用到分析器。建议创建索引时使用的分析器和查询索引时使用的分析器要一致。
需要加入queryParser依赖的jar包。
在这里插入图片描述

@Test
public void testQueryParser() throws Exception {IndexSearcher indexSearcher = getIndexSearcher();//创建queryparser对象//第一个参数默认搜索的域//第二个参数就是分析器对象QueryParser queryParser = new QueryParser("content", new IKAnalyzer());Query query = queryParser.parse("Lucene是java开发的");//执行查询printResult(query, indexSearcher);
}private void printResult(Query query, IndexSearcher indexSearcher) throws Exception {//执行查询TopDocs topDocs = indexSearcher.search(query, 10);//共查询到的document个数System.out.println("查询结果总数量:" + topDocs.totalHits);//遍历查询结果for (ScoreDoc scoreDoc : topDocs.scoreDocs) {Document document = indexSearcher.doc(scoreDoc.doc);System.out.println(document.get("filename"));//System.out.println(document.get("content"));System.out.println(document.get("path"));System.out.println(document.get("size"));}//关闭indexreaderindexSearcher.getIndexReader().close();
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部