全文检索技术Lucene

                                   ##什么是全文检索?

百度百科: 全文数据库是全文检索系统的主要构成部分。所谓全文数据库是将一个完整的信息源的全部内容转化为计算机可以识别、处理的信息单元而形成的数据集合。全文数据库不仅存储了信息,而且还有对全文数据进行词、字、段落等更深层次的编辑、加工的功能,而且所有全文数据库无一不是海量信息数据库。

                                   ## 数据类型

在我们开发中通常分为两种数据类型,结构化/非结构化

结构化数据:结构化数据指的是在我们使用之前已经定义好的、固定长度大小、固定结构的数据类型,例如:数据库中的字段,在我们使用数据库之前就会对该表的字段进行定义成字符类型,或者数值类型,一旦定义就必须按照该结构进行存储;

非结构化数据:非结构化数据则指的是存储在我们磁盘中的文件,例如doc文档、邮件等等,我们并不能固定该数据必须要求指定的类型、大小;

非结构化数据通常使用两种查询方法

1)顺序扫描:所谓顺序扫描就是对整个文件从头到尾挨个挨个进行查找,类似于Windows中的全盘查找,查询效率很慢;

2)全文检索(倒排索引):这也是本文所要讲的重点技术,全文检索会对非结构化数据先进行分词提取,并将分词将内容重新组织对应一个索引使之变的有结构化,然后对有结构化的数据进行匹配搜索,从而达到较快的效果;
例如:文本中包含"武汉加油,中国加油",会将该内容分成“武汉”、“加油”、“中国”对应1、2、3索引,而该内容就直接与索引相关联,我们搜索"武汉"则会根据1索引进行查找包含1索引的内容;

                                   ## 应用场景

全文检索技术通常应用在对大量数据进行查找的模块中,例如:淘宝、京东的商品搜索,百度、谷歌的搜索引擎,都是在海量数据中查找匹配的内容;

有朋友可能会好奇,我用数据库SQL也能实现搜索呀,为什么偏偏要引入检索技术不是多此一举吗?

SQL实现搜索:select * from 表名 where name like ‘%xx%’;

分析以上SQL:使用like关键字确实能实现搜索技术,而且相当方便、简单,但是在不建索引的情况下相当于是对所有数据进行匹配,并且like关键字只能实现指定字段包含匹配的固定字符,如果用户误输一个字符,匹配的结果会千差万别,像淘宝、京东的大型应用以亿为单位的数据量,使用like关键字查询的速度也就可想而知;

扫描二维码关注公众号,回复: 9840715 查看本文章
                                  ## Lucene

Lucene是Apache旗下的一款使用Java开发的开源全文检索工具库,可以理解为全文检索技术的底层API,Lucene实现了强大、准确、高效的搜索算法,市场上主流的搜索引擎Solr、ElasticSearch都是基于Lucene开发的,所以学习搜索技术,Lucene是必会的基础;

学习Lucene之前先了解Lucene中几个对象的概念:
1)Directory:目录对象,用于指定索引库的存储位置;
2)Document:文档对象,用于获取原始数据,例如数据库表中某一列数据;
3)Faield:域对象,用于获取原始数据某一个字段的数据,例如数据库表中某一列的一个字段信息;
4)Analyzer:分词器对象,用于使用指定规则对原始内容进行分词,例如"Java天下第一"分为"Java"、“天下”、“第一"或"J”、“a”、“v”、“a”、“天”、“下”、“第”、"一"等不同规则;

废话不多说,先整个入门程序详细的介绍下Lucene的Api,先创建一个maven项目:
在这里插入图片描述
依赖文件:

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- lucene核心库 -->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-core</artifactId>
        <version>7.4.0</version>
    </dependency>
    <!--IK中文分词器-->
    <dependency>
        <groupId>com.jianggujin</groupId>
        <artifactId>IKAnalyzer-lucene</artifactId>
        <version>7.0.0</version>
    </dependency>

创建索引流程图:在这里插入图片描述

创建索引代码:

@Test
public void createIndex() throws Exception {
//1、创建Director对象,指定索引库存储的位置。
//也可以把索引库保存在内存中
//Directory directory = new RAMDirectory();

    //指定索引库保存在磁盘
    Directory directory = FSDirectory.open(new File("C:\\temp\\index1").toPath());
    //2、基于Directory对象创建IndexWriterConfig以及IndexWriter对象
    IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
    IndexWriter indexWriter = new IndexWriter(directory, config);
    //3、定义需要存储文档的内容
    int id[]={1,2,3};
    String context[]={"Java天下第一","关注Java资料社区","更多干货等你学习"};
    for (int i=0;i<id.length;i++){
        //4、创建Field
        //参数1:域的名称,参数2:域的内容,参数3:是否存储
        //Field idFaield = new IntPoint("id", id[i]);
        //由于IntPoint默认不存储,可以通过StoredField配合使用才能存储
        Field idFields = new StoredField("id", id[i]);
        Field contextFaield = new TextField("context", context[i], Field.Store.YES);
        //5、创建文档对象
        Document document = new Document();
        //向文档对象中添加域

        document.add(contextFaield);
        document.add(idFields);
        //6、把文档对象写入索引库
        indexWriter.addDocument(document);
    }
    //6、关闭indexwriter对象
    indexWriter.close();

}

API解释:
Directory:目录对象,用于指定索引库存储位置
FSDirectory:Directory的子类,表示指定存储地址将索引库存储在磁盘中
RAMDirectory:Directory的子类,表示将索引库存储在内存中

IndexWriterConfig:索引写出配置对象,参数用于配置指定规则分词器对象;
IndexWriter:索引写出对象,用于将文档通过addDocument方法写入索引库,参数基于Directory、IndexWriterConfig;
Field:域对象,用于获取原始数据某一个字段信息最后向文档中存储,比如"标题"、“正文内容”;

在这里插入图片描述
还有更多方法,大家可以自行了解;还有更多方法,大家可以自行了解

                               这里引出三个问题:

什么时候需要分词?
什么时候需要索引?
如果需要用到这个字段匹配,就必须要创建索引;
什么时候需要分词?
首先这个字段值应该属于用户搜索匹配的值,再判读该值是否属于可分割值,例如订单号就属于不可分割字段不建议使用;
什么时候需要存储?
如果该字段要求要显示到最终结果中,那么就需要被存储;

Document:文档对象,相当于一个需要进行索引的单元,可以是文本、字符串、数据库记录,用于获取所有域的集合,通过add方法将Faield域存入,也可以使用removeField方法删除;

Analyzer:分词器对象,用于对文档内容指定规则创建分词,这里我使用的第三方分词器IKAnalyzer,这是一款开源中文分词器,采用了特有的"正向迭代最细粒度切分算法",支持细粒度和智能分词两种切分模式,需要引入相关依赖才能使用;

如果不使用第三方分词器,Lucene默认也有自带的分词器,通常会使用standarAnalyzer,但是对中文分词效果并不是很好;
在这里插入图片描述
创建结果:
在这里插入图片描述
查看索引文件具体值需要使用luke工具才能查看,我在文章底部会打包给大家;
在这里插入图片描述

查询索引方法:
@Test
public void searchIndex() throws Exception
//1、创建一个Director对象,指定索引库的位置
Directory directory = FSDirectory.open(new File(“C:\temp\index1”).toPath());
//2、创建一个IndexReader对象
IndexReader indexReader = DirectoryReader.open(directory);
//3、创建一个IndexSearcher对象,构造方法中的参数indexReader对象。
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
QueryParser queryParser = new QueryParser(“context”, new IKAnalyzer());
Query query = queryParser.parse(“Java关注资料社区”);
//5、执行查询,得到一个TopDocs对象
//参数1:查询对象 参数2:查询结果返回的最大记录数
TopDocs topDocs = indexSearcher.search(query, 10);
//6、取查询结果的总记录数
System.out.println(“查询总记录数:” + topDocs.totalHits);
//7、取文档列表
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//8、打印文档中的内容
for (ScoreDoc doc :scoreDocs) {
//取文档id
int docId = doc.doc;
//获取匹配精准度
float score=doc.score;
//根据id取文档对象
Document document = indexSearcher.doc(docId);
System.out.println(document.get(“id”));
System.out.println(document.get(“context”));
System.out.println(“匹配准确度”+score);
System.out.println("-----------------");
}
//9、关闭IndexReader对象
indexReader.close();
}

IndexReader:索引读取对象,基于Directory对象创建,往索引库中读取数据;

IndexSearcher:索引搜索对象,基于读取对象创建,可以通过search方法查询指定条件及指定结果条数;

Query:索引查询条件对象,Lucene会根据该对象生成的查询条件以及返回结果数在索引库进行筛选,可以通过Query子类或QueryParse查询解析器两种方式创建查询对象;

TermQuery:单词条搜索,不会使用分词器 ;

//Java为一个词条,所以结果能搜索到
Query query = new TermQuery(new Term(“context”, “java”));
//关注java资料社区为多个词条组成,返回结果为空
Query query = new TermQuery(new Term(“context”, “关注java资料社区”));

IntPoint.newRangeQuery: 根据数字范围搜索,返回Query对象;
//返回指定id为1-2范围内的数据
Query query = IntPoint.newRangeQuery(“id”,1,2);

QueryParser:查询解析器,通过传入需要匹配的域名及分词器(分词器必须与创建索引时一致),最后通过parse方法传入具体匹配的值返回Query对象;
QueryParser queryParser = new QueryParser(“context”, new IKAnalyzer());
Query query = queryParser.parse(“Java关注资料社区”);

TopDocs:该类是一个简单的容器指针,指针指向前N个排名的结果,用于接收查询结果的文档,该类包含三个重要属性:
scoreDocs[]:符合条件排名后的文档查询结果集,其中包含doc(文档ID),score(分数),shardIndex(碎片索引);
talHits:匹配总命中率次数;
maxScore:最高分数,如果没有则返回NaN;

删除索引方法:
@Test
public void delIndex() throws Exception {
Directory directory = FSDirectory.open(new File(“C:\temp\index1”).toPath());
IndexWriterConfig config = new IndexWriterConfig(new StandardAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory,config);
//创建一个删除条件,条件字段不能为分词字段
Query query = new TermQuery(new Term(“id”, “1”));
//删除全部索引
//indexWriter.deleteAll();
//根据条件删除
indexWriter.deleteDocuments(query);
indexWriter.commit();
//关闭indexwriter
indexWriter.close();
}
更新索引:先根据条件删除后添加;

发布了0 篇原创文章 · 获赞 0 · 访问量 159

猜你喜欢

转载自blog.csdn.net/qq_41490913/article/details/104882491