1、介绍lucene的功能以及建立索引、搜索单词、搜索词语和搜索句子四个示例实现

Elasticsearch 系列文章

1、介绍lucene的功能以及建立索引、搜索单词、搜索词语和搜索句子四个示例实现
2、Elasticsearch7.6.1基本介绍、2种部署方式及验证、head插件安装、分词器安装及验证
3、Elasticsearch7.6.1信息搜索示例(索引操作、数据操作-添加、删除、导入等、数据搜索及分页)
4、Elasticsearch7.6.1 Java api操作ES(CRUD、两种分页方式、高亮显示)和Elasticsearch SQL详细示例
5、Elasticsearch7.6.1 filebeat介绍及收集kafka日志到es示例
6、Elasticsearch7.6.1、logstash、kibana介绍及综合示例(ELK、grok插件)
7、Elasticsearch7.6.1收集nginx日志及监测指标示例
8、Elasticsearch7.6.1收集mysql慢查询日志及监控
9、Elasticsearch7.6.1 ES与HDFS相互转存数据-ES-Hadoop



本文简单的介绍了 lucene的功能以及四个示例(建立索引、搜索单词、搜索词语和搜索句子),是Elasticsearch的引导篇。
本文分为2个部分,即介绍lucene和使用示例。

一、Lucene全文检索库

1、全文检索

  • 结构化数据与非结构化数据
    结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等
    非结构化数据:指不定长或无固定格式的数据,如邮件,word文档等磁盘上的文件
  • 搜索结构化数据和非结构化数据
    使用SQL语言专门搜索结构化的数据
    使用ES/Lucene/Solor建立倒排索引,根据关键字就可以搜索一些非结构化的数据
  • 全文检索
    全文检索是指通过一个程序扫描文本中的每一个单词,针对单词建立索引,并保存该单词在文本中的位置、以及出现的次数。用户查询时,通过之前建立好的索引来查询,将索引中单词对应的文本位置、出现的次数返回给用户,因为有了具体文本的位置,所以就可以将具体内容读取出来了,类似于通过字典中的检索字表查字的过程。

2、Lucene简介

Lucene是一种高性能的全文检索库,在2000年开源,最初由Doug Cutting(道格·卡丁)开发
Lucene是Apache的一个顶级开源项目,是一个全文检索引擎工具包。但Lucene不是一个完整的全文检索引擎,它只是提供一个基本的全文检索的架构,还提供了一些基本的文本分词库
Lucene是一个简单易用的工具包,可以方便的实现全文检索的功能

二、示例

通过搜索一个关键字就能够找到哪些文章包含了这些关键字。例如:搜索「hadoop」,就能找到hadoop相关的文章。

1、准备测试数据

自行准备测试数据即可

2、建立索引库

实现步骤

  • 构建分词器(StandardAnalyzer)
  • 构建文档写入器配置(IndexWriterConfig)
  • 构建文档写入器(IndexWriter,注意:需要使用Paths来)
  • 读取所有文件构建文档
  • 文档中添加字段
  • 写入文档
  • 关闭写入器

数据结构

3、实现

1)、maven 依赖

 <!-- lucene核心类库 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>8.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>8.4.0</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

2)、java 实现

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.FSDirectory;


/**
 * 通过这个类来读取文章的文本文件,建立索引
 * 
 * @author chenw
 *
 */
public class BuildArticleIndex {
    public static void main(String[] args) throws IOException {
        // 1. 构建分词器(StandardAnalyzer)
        Analyzer standardAnalyzer = new StandardAnalyzer();

        // 2. 构建文档写入器配置(IndexWriterConfig)
        IndexWriterConfig writerConfig = new IndexWriterConfig(standardAnalyzer);

        // 3. 构建文档写入器(IndexWriter,注意:需要使用Paths来)
        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(Paths.get("D:\\workspace\\es\\lucene\\index")), writerConfig);

        // 4. 读取所有文件构建文档
        // 读取data目录中的所有文件
        File dataDir = new File("D:\\workspace\\es\\lucene\\data");
        File[] fileArray = dataDir.listFiles();

        // 迭代所有的文本文件,读取文件并建立索引
        for (File file : fileArray) {
            // 5. 文档中添加字段
            // 字段名 类型 说明
            // file_name TextFiled 文件名字段,需要在索引文档中保存文件名内容
            // content TextFiled 内容字段,只需要能被检索,但无需在文档中保存
            // path StoredFiled 路径字段,无需被检索,只需要在文档中保存即可

            // 在Lucene中都是以Document的形式来存储内容的
            // Lucene在添加文档的时候就会自动建立索引
            Document doc = new Document();
            // 如果需要建立索引,就用TextField,如果不需要就使用StoredField
            doc.add(new TextField("file_name", file.getName(), Field.Store.YES));
            doc.add(new TextField("content", FileUtils.readFileToString(file), Field.Store.NO));
            doc.add(new StoredField("path", file.getAbsolutePath()));

            // 6. 写入文档
            indexWriter.addDocument(doc);
        }

        // 7. 关闭写入器
        indexWriter.close();

    }
}

运行完上述程序后的输出如下图
在这里插入图片描述

4、查询-单字搜索

该示例只能搜索单字,如果想搜索多字,则需要使用相关分词器

1)、实现步骤

使用DirectoryReader.open构建索引读取器
构建索引查询器(IndexSearcher)
构建词条(Term)和词条查询(TermQuery)
执行查询,获取文档
遍历打印文档(可以使用IndexSearch.doc根据文档ID获取到文档)
关键索引读取器

2)、代码

import java.io.IOException;
import java.nio.file.Paths;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;

public class KeywordSearch {

    public static void main(String[] args) throws IOException {
        // 1. 使用DirectoryReader.open构建索引读取器
        DirectoryReader reader = DirectoryReader.open(FSDirectory.open(Paths.get("D:\\workspace\\es\\lucene\\index")));
        // 2. 构建索引查询器(IndexSearcher)
        // 用来搜索关键字的
        IndexSearcher indexSearcher = new IndexSearcher(reader);

        // 3. 构建词条(Term)和词条查询(TermQuery)
        TermQuery termQuery = new TermQuery(new Term("content", "他"));

        // 4. 执行查询,获取文档
        TopDocs topDocs = indexSearcher.search(termQuery, 50);

        // 5. 遍历打印文档(可以使用IndexSearch.doc根据文档ID获取到文档)
        ScoreDoc[] scoreDocArray = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocArray) {
            // 在Lucene中,每一个文档都有一个唯一ID
            // 根据唯一ID就可以获取到文档
            Document document = indexSearcher.doc(scoreDoc.doc);
            // 获取文档中的字段
            System.out.println("-------------");
            System.out.println("文件名:" + document.get("file_name"));
            System.out.println("文件路径:" + document.get("path"));
            //content是否有内容,取决于创建索引时是否存储
            System.out.println("文件内容:" + document.get("content"));
        }

        // 6. 关闭索引读取器
        reader.close();
    }

}

运行结果
-------------
文件名:飞翔的精灵.txt
文件路径:D:\workspace\es\lucene\data\飞翔的精灵.txt
文件内容:null
-------------
文件名:一辈子陪伴.txt
文件路径:D:\workspace\es\lucene\data\一辈子陪伴.txt
文件内容:null
-------------
文件名:永远的坐票.txt
文件路径:D:\workspace\es\lucene\data\永远的坐票.txt
文件内容:null
-------------
文件名:大度也是一种美德.txt
文件路径:D:\workspace\es\lucene\data\大度也是一种美德.txt
文件内容:null

5、查询-词语搜索

1)、分词器与中文分词器

分词器是指将一段文本,分割成为一个个的词语的动作。例如:按照停用词进行分隔(的、地、啊、吧、标点符号等)。之前在代码中使用的分词器是Lucene中自带的分词器。这个分词器对中文很不友好,只是将一个一个字分出来,所以,就会从后出现上面的问题——无法搜索词语。
所以,基于该背景,我们需要使用跟适合中文的分词器。中文分词器也有不少,例如:

  • Jieba分词器

  • IK分词器

  • 庖丁分词器

  • Smarkcn分词器
    等等。此处使用比较好用的IK分词器来进行分词。

  • IK介绍
    IK已经实现好了Lucene的分词器:https://github.com/wks/ik-analyzer
    IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始,IKAnalyzer已经推出了3个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。新版本的 IKAnalyzer3.0则发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。

  • IKAnalyzer3.0特性
    采用了特有的“正向迭代最细粒度切分算法“,支持细粒度和最大词长两种切分模式;具有83万字/秒(1600KB/S)的高速处理能力。
    采用了多子处理器分析模式,支持:英文字母、数字、中文词汇等分词处理,兼容韩文、日文字符
    优化的词典存储,更小的内存占用。支持用户词典扩展定义
    针对Lucene全文检索优化的查询分析器IKQueryParser;引入简单搜索表达式,采用歧义分析算法优化查询关键字的搜索排列组合,能极大的提高Lucene检索的命中率。

2)、实现步骤

把之前生成的索引文件删除,然后将之前使用的StandardAnalyzer修改为IKAnalyzer。然后重新生成索引。

  • 构建分词器(IKAnalyzer)
  • 构建文档写入器配置(IndexWriterConfig)
  • 构建文档写入器(IndexWriter,注意:需要使用Paths来)
  • 读取所有文件构建文档
  • 文档中添加字段
  • 写入文档
  • 关闭写入器
    在这里插入图片描述

3)、maven 依赖

<!-- lucene核心类库 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>8.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>8.4.0</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>com.jianggujin</groupId>
            <artifactId>IKAnalyzer-lucene</artifactId>
            <version>8.0.0</version>
        </dependency>

4)、代码

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

/**
 * 通过这个类来读取文章的文本文件,建立索引
 * 
 * @author chenw
 *
 */
public class BuildArticleIndex {
    public static void main(String[] args) throws IOException {
        // 1. 构建分词器(StandardAnalyzer)
        Analyzer standardAnalyzer = new IKAnalyzer();

        // 2. 构建文档写入器配置(IndexWriterConfig)
        IndexWriterConfig writerConfig = new IndexWriterConfig(standardAnalyzer);

        // 3. 构建文档写入器(IndexWriter,注意:需要使用Paths来)
        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(Paths.get("D:\\workspace\\es\\lucene\\index")), writerConfig);

        // 4. 读取所有文件构建文档
        // 读取data目录中的所有文件
        File dataDir = new File("D:\\workspace\\es\\lucene\\data");
        File[] fileArray = dataDir.listFiles();

        // 迭代所有的文本文件,读取文件并建立索引
        for (File file : fileArray) {
            // 5. 文档中添加字段
            // 字段名 类型 说明
            // file_name TextFiled 文件名字段,需要在索引文档中保存文件名内容
            // content TextFiled 内容字段,只需要能被检索,但无需在文档中保存
            // path StoredFiled 路径字段,无需被检索,只需要在文档中保存即可

            // 在Lucene中都是以Document的形式来存储内容的
            // Lucene在添加文档的时候就会自动建立索引
            Document doc = new Document();
            // 如果需要建立索引,就用TextField,如果不需要就使用StoredField
            doc.add(new TextField("file_name", file.getName(), Field.Store.YES));
            doc.add(new TextField("content", FileUtils.readFileToString(file), Field.Store.NO));
            doc.add(new StoredField("path", file.getAbsolutePath()));

            // 6. 写入文档
            indexWriter.addDocument(doc);
        }

        // 7. 关闭写入器
        indexWriter.close();

    }
}

5)、验证

采用单字搜索的例子,只是将搜索的字变成一个词语。运行结果如下:

-------------
文件名:永远的坐票.txt
文件路径:D:\workspace\es\lucene\data\永远的坐票.txt
文件内容:null
-------------
文件名:一辈子陪伴.txt
文件路径:D:\workspace\es\lucene\data\一辈子陪伴.txt
文件内容:null

6、句子搜索

要实现搜索句子,是将句子进行分词后,再进行搜索。我们需要使用QueryParser类来实现。通过QueryParser可以指定分词器对要搜索的句子进行分词。

import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.nio.file.Paths;
import java.text.ParseException;

/**
 * @author chenw
 *
 */
public class SentenceSearch {

    public static void main(String[] args) throws IOException, ParseException, org.apache.lucene.queryparser.classic.ParseException {
        // 1. 构建索引读取器
        IndexReader indexReader = DirectoryReader.open(FSDirectory.open(Paths.get("D:\\workspace\\es\\lucene\\index")));

        // 2. 构建索引查询器
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);

        // 3. 执行查询,获取文档
        QueryParser queryParser = new QueryParser("content", new IKAnalyzer());

        TopDocs topDocs = indexSearcher.search(queryParser.parse("人生是一条河"), 50);
        ScoreDoc[] scoreDocArrary = topDocs.scoreDocs;

        // 4. 遍历打印文档
        for (ScoreDoc scoreDoc : scoreDocArrary) {
            int docId = scoreDoc.doc;
            Document document = indexSearcher.doc(docId);

            System.out.println("文件名:" + document.get("file_name") + " 路径:" + document.get("path"));
        }

        indexReader.close();

    }

}
运行结果:

加载扩展词典:ext.dic
加载扩展停止词典:stopword.dic
文件名:月台.txt 路径:D:\workspace\es\lucene\data\月台.txt
文件名:永远的坐票.txt 路径:D:\workspace\es\lucene\data\永远的坐票.txt
文件名:大度也是一种美德.txt 路径:D:\workspace\es\lucene\data\大度也是一种美德.txt
文件名:一辈子陪伴.txt 路径:D:\workspace\es\lucene\data\一辈子陪伴.txt
文件名:飞翔的精灵.txt 路径:D:\workspace\es\lucene\data\飞翔的精灵.txt

4、倒排索引结构

倒排索引是一种建立索引的方法。是全文检索系统中常用的数据结构。通过倒排索引,就是根据单词快速获取包含这个单词的文档列表。倒排索引通常由两个部分组成:单词词典、文档。
在这里插入图片描述
以上,简单的介绍了 lucene的功能以及四个示例(建立索引、搜索单词、搜索词语和搜索句子)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一瓢一瓢的饮 alanchanchn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值