您的位置:首页 > 编程语言 > Java开发

全文检索-Lucene简介

2018-03-20 20:20 471 查看

全文检索的引入

全文数据的搜索通常我们采取以下两种方式:顺序扫描和全文检索。
       在了解顺序扫描和全文检索之前,我们先要了解几个概念:
    结构化数据:指具有“固定格式”或“有限长度”的数据,如数据库,元数据等;
    非结构化数据:指不定长或无固定格式的数据,如邮件,word文档等;
    半结构化数据,如XML,HTML等,当根据需要可按结构化数据来处理,也可抽取出纯文本按非结构化数据来处理。
顺序扫描:所谓顺序扫描,比如要找内容包含某一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。比如Window自带的搜索,在数据库中扫描不带索引文本字段等。
全文检索:从非结构化数据中提取出的然后重新组织的信息,我们称之索引。即为文本数据建立类似字典目录,从而提高检索速度。全文检索还有一层含义,将返回数据按照相关度的大小返回。
 
从上文我们可以看出,采用顺序扫描可以达到我们检索全文数据的效果,但是当数据量较大时,我们的检索效率将变得非常的低,严重影响的使用体验。因此我们引入了全文检索的方式来进行,已达到提高搜索效率的需求。
 

全文检索的工作流程



全文检索的工作流程如图所示,全文检索主要执行两个操作,①是收集数据,收集到的数据经过一定的处理,建立自己的索引库;②就是处理用户的搜索请求,因为在①中已经按照一定的规则建立了自己的索引库,此时搜索引擎经过一定的检索方式,将数据反馈给用户的搜索请求。

全文检索的工作原理

       全文检索之所以能够提高检索效率,关键就在于它对索引库的创建,那么接下来我们就讲一下它的工作原理。





在①中我们准备了三条收集的数据,②我们将收集到的数据通过词法分析将数据分成一个个的单词,在分词完毕后还需剔除掉一些语气助词等没有语义的词语,第二步我们通常是找与收集的数据的语言相对应的分词器完成这步操作,③统一单词的大小写和使用时态,④将单词进行排序,⑤将单词进行合并,保留单词所在数据的编号。
 
经过以上的几步操作,我们将非结构化数据就能转换成有序的结构化的索引库,有序的结构化数据能够大幅度的提升我们的检索效率。
 

Lucene的基本API操作(java)

       Lucene就是针对以上的操作需求开发并封装的执行程序,尽管它从诞生到现在已经很多年了,但是,现在搭建的许多的全文检索框架仍然是基于Lucene进行的封装,包括现在使用较多的ElasticSearch、Solr、Katta等。
       在操作Lucene的Api之前,我们需要导入以下的jar包:
      


各个jar包简介
       IKAnalyzer:针对中文分词的包
       common:通用的解析数据的包(分词等功能,拆分英文还行)
       smartcn:lucene自带的解析中文的包(效果不好,推荐解析中文采用IKAnalyzer)
       core:lucene核心包
       highlighter:控制关键字高亮的包
       queries:检索使用的包
       queryparser:检索解析使用的包
 

索引库的简单创建

public void testWriteDoc() throws Exception {
 
              //目录对象(索引库相关的数据存放的地方)
              Directorydirectory =FSDirectory.open(Paths.get("E:/workspace/lucene-hello/helloIndex"));
              //词法分析器对象(标准词法分析器,只对英文有效)
              Analyzeranalyzer = new StandardAnalyzer();
              //配置对象
              IndexWriterConfigconf = new IndexWriterConfig(analyzer);
 
              /*
               * 配置索引库的生成策略,主要有以下三种创建方式
               *   1、CREATE:每次都重新创建索引库
               *   2、CREATE_OR_APPEND:当索引库存在时直接在该索引库追加数据,否则重新创建
               *   3、APPEND:在当前指定索引库追加数据
               */
              conf.setOpenMode(OpenMode.CREATE);
 
              //获取核心对象IndexWritter
              IndexWriterindexWriter = new IndexWriter(directory, conf);
              System.out.println(indexWriter);
 
              //通过IndexWritter的addDocument方法完成文档的添加
              //模拟添加第一条数据
              Documentdocument1 = new Document();
              //增加多个文档的字段及其值
              IndexableFieldfield = new TextField("docId", "doc1", Store.YES);
              document1.add(field);
              document1.add(newTextField("content", doc1, Store.YES));
              //完成文档的添加和索引的创建
              indexWriter.addDocument(document1);
 
              //模拟添加第二条数据
              Documentdocument2 = new Document();
              document2.add(newTextField("docId", "doc2", Store.YES));
              document2.add(newTextField("content", doc2, Store.YES));
              indexWriter.addDocument(document2);
 
              //模拟添加第三条数据
              Documentdocument3 = new Document();
              document3.add(newTextField("docId", "doc3", Store.YES));
              document3.add(newTextField("content", doc3, Store.YES));
              indexWriter.addDocument(document3);
 
              //
9e23
提交修改到索引库和关闭核心写对象
              indexWriter.commit();
              indexWriter.close();
 
              //查询所有数据,查看是否将数据添加成功
              testSearch("*:*");
       }
 

简单的检索测试

       privatevoid testSearch(String queryStr) throws Exception {
              //核心目录对象
              Directorydirectory = FSDirectory.open(Paths.get("E:/workspace/lucene-hello/helloIndex"));
              //核心索引文件的读对象
              IndexReaderreader = DirectoryReader.open(directory);
              //核心对象IndexSearcher的创建
              IndexSearcherindexSearcher = new IndexSearcher(reader);
 
              //在文档的哪个字段上查询
              Stringfield = "content";
              //搜索的时候的词法分析器
              Analyzeranalyer = new StandardAnalyzer();
              QueryParserqueryParser = new QueryParser(field, analyer);
              //通过查询分析器直接解析查询内容生成
              Queryquery = queryParser.parse(queryStr);
 
              //通过query对象指定的条件,最终返回最相关(相关度最高)的前n个文档
              TopDocstopDocs = indexSearcher.search(query, 5);
              //通过结果封装对象topDocs获取返回的具体结果
              //获取最高的相关度的值
              floatmaxScore = topDocs.getMaxScore();
              //查询的相关的文档总数
              inttotalHits = topDocs.totalHits;
              //获取具体文档数据
              ScoreDoc[]scoreDocs = topDocs.scoreDocs;
              //遍历查看数据
              for(ScoreDoc scoreDoc : scoreDocs) {
                     //相关度
                     floatscore = scoreDoc.score;
                     //文档的ID
                     intdocumentId = scoreDoc.doc;
                     Documentdocument = indexSearcher.doc(documentId);
 
                     //获取字段的内容
                     StringdocId = document.get("docId");
                     Stringcontent = document.get("content");
 
                     System.out.println("id="+ documentId + ",score=" + score + ",docId=" + docId +",content=" + content);
              }
       }

以上内容纯属个人见解,如有疏漏,欢迎指正(xlzwhq@163.com)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  全文检索 Java