Lucene(全文检索)

什么是全文检索

在我们生活中的数据可分为结构化数据和非结构化数据

结构化数据

格式固定、长度固定、数据类型固定。
如数据库中的数据,
查询方法:SQL语句。
特点:简单、速度快。

非结构化数据

格式不固定、长度不固定、数据类型不固定。
如word文档、txt、图片、XML、HTML、pdf文档、邮件、图像和音频/视频信息等
查询方法
(1)顺序扫描法(Serial Scanning)
所谓顺序扫描,比如要找内容包含某一个字符串的文件,就是一个文档一个文档的找,对于每一个文档,从头找到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着找下一个文件,直到扫描完所有的文件。如利用windows的搜索也可以搜索文件内容,只是相当的慢。
(2)全文检索(Full-text Search)
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引。
这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。
虽然创建索引的过程也是非常耗时的,但是索引一旦创建就可以多次使用,全文检索主要处理的是查询,所以耗时间创建索引是值得的。

全文检索大体分两个过程,索引创建(Indexing)和搜索索引(Search)。
索引创建:提取结构化和非结构化数据信息,创建索引。
搜索索引:根据查询请求搜索创建的索引,然后返回查询结果。

什么是Lucene

Lucene是apache下的一个开放源代码的全文检索引擎工具包。提供了完整的查询引擎和索引引擎,部分文本分析引擎。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能。

Lucene实现全文检索的流程

创建索引

获得文档
原始文档是指要索引和搜索的内容。原始内容包括互联网上的网页、数据库中的数据、磁盘上的文件等。
构建文档对象
获取原始内容的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档中包括一个一个的域(Field),域中存储内容。每个Document可以有多个Field,不同的Document可以有不同的Field,同一个Document可以有相同的Field(域名和域值都相同)
分析文档
将原始内容创建为包含域(Field)的文档(document),需要再对域中的内容进行分析,分析的过程是经过对原始文档提取单词、将字母转为小写、去除标点符号、去除停用词等过程生成最终的语汇单元,可以将语汇单元理解为一个一个的单词。每个单词叫做一个Term,不同的域中拆分出来的相同的单词是不同的term。term中包含两部分一部分是文档的域名,另一部分是单词的内容。
创建索引
对所有文档分析得出的语汇单元进行索引,索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到Document(文档)。
创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构。
倒排索引结构是根据内容(词语)找文档,如下图:
在这里插入图片描述
倒排索引结构也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它的规模较小,而文档集合较大。

查询索引

查询索引也是搜索的过程。搜索就是用户输入关键字,从索引(index)中进行搜索的过程。根据关键字搜索索引,根据索引找到对应的文档,从而找到要搜索的内容。
创建查询
用户输入查询关键字执行搜索之前需要先构建一个查询对象,查询对象中可以指定查询要搜索的Field文档域、查询关键字等,查询对象会生成具体的查询语法
执行查询
根据查询语法在倒排索引词典表中分别找出对应搜索词的索引,从而找到索引所链接的文档链表。

案例

案例说明

对于磁盘上某一个目录下的所有文本文件进行全文检索,通过关键字搜索文件,凡是文件名或文件内容包括关键字的文件都需要找出来。还可以根据中文词语进行查询。

环境搭建

创建maven工程,导入相关jar包坐标

<packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>7.4.0</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>7.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-queryparser</artifactId>
            <version>7.4.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.jianggujin/IKAnalyzer-lucene -->
        <dependency>
            <groupId>com.jianggujin</groupId>
            <artifactId>IKAnalyzer-lucene</artifactId>
            <version>8.0.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

resources目录下创建IKAnalyzer.cfg.xml(名字固定)
可以配置用户自定义扩展词典和停用词典

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">  
<properties>  
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 -->
	<entry key="ext_dict">hotword.dic;</entry>
	<!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_stopwords">stopword.dic;</entry> 
</properties>

同时在此目录下创建hotword.dic和stopword.dic文件,分别存放扩展词和停用词

创建索引

public void CreateIndex() throws Exception {
        //创建一个Director对象,指定索引库保存在磁盘上
        Directory directory = FSDirectory.open(new File("此处填写索引库保存的目录名").toPath());
  		// 配置IndexWriter使用中文分析器
        IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
        //创建一个IndexWriter对象
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //读取磁盘上的文件,对应每个文件创建一个文档对象。
        File dir = new File("此处填写需要全文检索的文件所在目录名");
        File[] files = dir.listFiles();
        for (File f :files) 
        {
            //文件名
            String fileName = f.getName();
            //文件的路径
            String filePath = f.getPath();
            //文件的内容
            String fileContent = FileUtils.readFileToString(f, "utf-8");
            //文件的大小
            long fileSize = FileUtils.sizeOf(f);
            //创建Field(域对象)
            Field fieldName = new TextField("name", fileName, Field.Store.YES);
            Field fieldPath = new StoredField("path", filePath);
            Field fieldContent = new TextField("content", fileContent, Field.Store.YES);
            Field fieldSizeValue = new LongPoint("size", fileSize);
            Field fieldSizeStore = new StoredField("size", fileSize);
            //创建文档对象
            Document document = new Document();
            //向文档对象中添加域
            document.add(fieldName);
            document.add(fieldPath);
            document.add(fieldContent);
            document.add(fieldSizeValue);
            document.add(fieldSizeStore);
            //把文档对象写入索引库
            indexWriter.addDocument(document);
        }
        //关闭indexwriter对象
        indexWriter.close();
    }

查询索引

public void SearchIndex() throws Exception {
        //创建一个Director对象,指定索引库的位置
        Directory directory = FSDirectory.open(new File("此处填写你的索引库位置").toPath());
        //创建一个IndexReader对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //创建一个IndexSearcher对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //创建一个Query对象TermQuery,指定查询的域和查询的关键词。
        //Term 参数一:想在查找的域 如在content里查找
        //Term 参数二:想要匹配的内容 如"java" 
        Query query = new TermQuery(new Term("", ""));
        //执行查询,得到一个TopDocs对象
        //query:查询对象 
        //10:查询结果返回的最大记录数
        TopDocs topDocs = indexSearcher.search(query, 10);
        //取查询结果的总记录数
        System.out.println("查询总记录数:" + topDocs.totalHits);
        //取文档列表
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        //打印文档中的内容
        for (ScoreDoc doc: scoreDocs) 
        {
            //拿到文档id
            int docId = doc.doc;
            //根据id取文档对象
            Document document = indexSearcher.doc(docId);
            System.out.println(document.get("name"));
            System.out.println(document.get("path"));
            System.out.println(document.get("size"));
            System.out.println(document.get("content"));
            System.out.println("-----------------");
        }
        //关闭IndexReader对象
        indexReader.close();
    }

数值范围查询

public void RangeQuery() throws Exception {
		//创建一个IndexReader对象
        IndexReader indexReader = DirectoryReader.open(FSDirectory.open(new File("此处填写你的索引库位置").toPath()));
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //创建一个Query对象 查询文件大小为1字节到2048字节的文件
        Query query = LongPoint.newRangeQuery("size", 1, 2048);
        //执行查询
        TopDocs topDocs = indexSearcher.search(query, 10);
        System.out.println("总记录数:" + topDocs.totalHits);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc doc:scoreDocs){
            //取文档id
            int docId = doc.doc;
            //根据id取文档对象
            Document document = indexSearcher.doc(docId);
            System.out.println(document.get("name"));
            System.out.println(document.get("path"));
            System.out.println(document.get("size"));
            System.out.println(document.get("content"));
            System.out.println("-----------------");
        }
        indexReader.close();
    }

queryparser查询

通过QueryParser也可以创建Query,QueryParser提供一个Parse方法,此方法可以直接根据查询语法来查询。此方法会将输入语句解析为多个词项,根据这些词项全文检索
queryParser创建时需要使用到分析器。建议创建索引时使用的分析器和查询索引时使用的分析器要一致

public void testQueryParser() throws Exception {
		//创建一个IndexReader对象
        IndexReader indexReader = DirectoryReader.open(FSDirectory.open(new File("此处填写你的索引库位置").toPath()));
		IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //创建一个QueryPaser对象,两个参数
        //参数1:默认搜索域,参数2:分析器对象
        QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
        //使用QueryPaser对象创建一个Query对象
        Query query = queryParser.parse("输入想要检索的语句");
        System.out.println(query);
        //执行查询
		TopDocs topDocs = indexSearcher.search(query, 10);
        System.out.println("总记录数:" + topDocs.totalHits);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc doc:scoreDocs){
            //取文档id
            int docId = doc.doc;
            //根据id取文档对象
            Document document = indexSearcher.doc(docId);
            System.out.println(document.get("name"));
            System.out.println(document.get("path"));
            System.out.println(document.get("size"));
            System.out.println(document.get("content"));
            System.out.println("-----------------");
        }
        indexReader.close();
    }

索引库的维护

索引库的添加

public void addDocument() throws Exception {
        //创建一个IndexWriter对象,需要使用IKAnalyzer作为分析器
        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("此处填写索引库保存的目录名").toPath()), new IndexWriterConfig(new IKAnalyzer()));
        //创建一个Document对象
        Document document = new Document();
        //向document对象中添加域
        document.add(new TextField("name", "新增文件名", Field.Store.YES));
        document.add(new TextField("content", "新增内容", Field.Store.NO));
        document.add(new StoredField("path", "此处填写新文件路径"));
        document.add(new LongPoint("size",1024));
        document.add(new StoredField("size",1024));
        // 把文档写入索引库
        indexWriter.addDocument(document);
        //关闭索引库
        indexWriter.close();
    }

索引库删除
删除全部

public void deleteAllDocument() throws Exception {
		//创建一个IndexWriter对象,需要使用IKAnalyzer作为分析器
        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("此处填写索引库保存的目录名").toPath()), new IndexWriterConfig(new IKAnalyzer()));
        //删除全部文档
        indexWriter.deleteAll();
        //关闭索引库
        indexWriter.close();
    }

指定查询条件删除

public void deleteDocumentByQuery() throws Exception {
		//创建一个IndexWriter对象,需要使用IKAnalyzer作为分析器
        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("此处填写索引库保存的目录名").toPath()), new IndexWriterConfig(new IKAnalyzer()));
        indexWriter.deleteDocuments(new Term("查询域名", "查询关键词"));
        indexWriter.close();
    }

索引库修改
先删除后添加

public void updateDocument() throws Exception {
		//创建一个IndexWriter对象,需要使用IKAnalyzer作为分析器
        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("此处填写索引库保存的目录名").toPath()), new IndexWriterConfig(new IKAnalyzer()));
        //创建一个新的文档对象
        Document document = new Document();
        //向文档对象中添加域
        document.add(new TextField("name", "更新文档", Field.Store.YES));
        //更新操作
        indexWriter.updateDocument(new Term("查询域名", "查询关键词"), document);
        //关闭索引库
        indexWriter.close();
    }

今天的分享就到这里了,希望大家能够有所收获,欢迎关注。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值