前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >javaweb-Lucene-1-61

javaweb-Lucene-1-61

作者头像
全栈程序员站长
发布2021-05-19 17:29:02
7270
发布2021-05-19 17:29:02
举报

文章目录

简介

Lucene是一个基于Java开发全文检索工具包。 就是将不规范的文档的内容单词进行分割,建立单词-文档索引,这样查询某个单词内容时可以通过索引快速查找相关文档,内容 对于一些网站内部的内容检索有需要 这项技术其实有更成熟的封装,比如专门的服务器等,这里只是普及一下相关概念,后面会解释进行其他的基于lucene的上层封装的相关技术 工程:https://github.com/Jonekaka/javaweb-Lucene-1-61

1、什么是全文检索,如何实现全文检索

对于结构化数据,由于格式、长度、数据类型规范,例如数据库中的数据,查询简单速度也快 对于非结构化数据,格式,长度,数据类型都不规范,查询存在复杂难度 1.使用程序吧文档读取到内存中,然后匹配字符串。顺序扫描。非结构化数据查询速度较慢 2.先跟根据空格进行字符串拆分,得到一个单词列表,基于单词列表创建一个索引。 然后查询索引,根据单词和文档的对应关系找到文档列表。这个过程叫做全文检索。 索引:一个为了提高查询速度,创建某种数据结构的集合。 索引可以一次创建多次使用

全文检索的应用场景 1、搜索引擎 2、网站内搜索 3、电商搜索

2、Lucene实现全文检索的流程

1、创建索引 1)获得文档 原始文档:要基于那些数据来进行搜索,那么这些数据就是原始文档。 2)构建文档对象 对每个原始文档创建一个Document对象 每个document对象中包含多个域(field) 域中保存就是原始文档数据。 域的名称 域的值 每个文档都有一个唯一的编号,就是文档id 3)分析文档 就是分词的过程 1、根据空格进行字符串拆分,得到一个单词列表 2、把单词统一转换成小写。 3、去除标点符号 4、去除停用词 停用词:无意义的词,比如the,and, 每个关键词都封装成一个Term对象中。 Term中包含两部分内容: 关键词所在的域 关键词本身 不同的域中拆分出来的相同的关键词是不同的Term。 4)创建索引 基于关键词列表创建一个索引。保存到索引库中。 索引库中: 索引 document对象 关键词和文档的对应关系 通过词语找文档,这种索引的结构叫倒排索引结构。 因为一般方法是先找文档,再找单词,现在是先找单词,然后匹配文档 传统方法是根据文件找到该文件的内容,在文件内容中匹配搜索关键字,这种方法是顺序扫描方法,数据量大、搜索慢。倒排索引结构是根据内容(词语)找文档, 一个单词对应多个文档,内部存储为链表结构,记录着包含这个单词的文档id

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

2、查询索引 1)用户查询接口 例如:百度的搜索框 2)把关键词封装成一个查询对象 要查询的域 要搜索的关键词 3)执行查询 根据要查询的关键词到对应的域上进行搜索。 找到关键词,根据关键词找到 对应的文档 4)渲染结果 根据文档的id找到文档对象 对关键词进行高亮显示 分页处理 最终展示给用户看。

3、配置开发环境

创建索引

代码语言:javascript
复制
环境:
	需要下载Lucene
	http://lucene.apache.org/
	最低要求jdk1.8
工程搭建:
	创建一个java工程
	添加jar:
		lucene-analyzers-common-7.4.0.jar
		lucene-core-7.4.0.jar
		commons-io.jar

引入jar包

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
步骤:
	1、创建一个Director对象,指定索引库保存的位置。

	2、基于Directory对象创建一个IndexWriter对象

(—解释—:)【索引写入对象】 3、读取磁盘上的文件,对应每个文件创建一个文档对象。 4、向文档对象中添加域 (—解释—:)【域中包含文件属性,大小,id等】 5、把文档对象写入索引库,至此索引库创建好 6、关闭indexwriter对象

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

创建域的时候有很多的选项,选择lucene

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

按照上述流程进行代码编写 此处可以创建英文索引,stard中文索引ik

代码语言:javascript
复制
@Test
    public void createIndex() throws Exception {
        //1、创建一个Director对象,指定索引库保存的位置。
        //把索引库保存在内存中,但是一关机内存就没了,所以不用
        //Directory directory = new RAMDirectory();
        //把索引库保存在磁盘
        Directory directory = FSDirectory.open(new File("C:\\temp\\index").toPath());
        //2、基于Directory对象创建一个IndexWriter对象
        IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //3、读取磁盘上的文件,对应每个文件创建一个文档对象。
        File dir = new File("C:\\A0.lucene2018\\05.参考资料\\searchsource");
        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
            //参数1:域的名称,参数2:域的内容,参数3:是否存储
            Field fieldName = new TextField("name", fileName, Field.Store.YES);
            //Field fieldPath = new TextField("path", filePath, Field.Store.YES);
            Field fieldPath = new StoredField("path", filePath);
            Field fieldContent = new TextField("content", fileContent, Field.Store.YES);
            //Field fieldSize = new TextField("size", fileSize + "", 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(fieldSize);
            document.add(fieldSizeValue);
            document.add(fieldSizeStore);
            //5、把文档对象写入索引库
            indexWriter.addDocument(document);
        }
        //6、关闭indexwriter对象
        indexWriter.close();
    }

如图目录中便包含了相应的索引数据

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

查看索引

使用luke查看索引库中的内容

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

target中有个支持的jar包,使用批处理命令luke可见 索引原文件为二进制文件 luke依赖jar1.9,低版本不行 找到索引库位置,添加

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

4个域

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

域中包含的关键词

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

简单查询

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

前面是域名,后面冒号内容,表示查询域名中的某个关键词

以文档为基准的查看

15个文档,每个文档中域中包含的内容,当时创建域时选择了保存,因此可见内容

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

代码实现查询

查询对象时所做的选择

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

类似于界面查询的 查询域中的内容

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

查询索引库 步骤: 1、创建一个Director对象,指定索引库的位置 2、创建一个IndexReader对象 3、创建一个IndexSearcher对象,构造方法中的参数indexReader对象。 4、创建一个Query对象,TermQuery,关键字查询 5、执行查询,得到一个TopDocs对象 6、取查询结果的总记录数 7、取文档列表 8、打印文档中的内容 9、关闭IndexReader对象

代码语言:javascript
复制
 @Test
    public void searchIndex() throws Exception {
        //1、创建一个Director对象,指定索引库的位置
        Directory directory = FSDirectory.open(new File("C:\\temp\\index").toPath());
        //2、创建一个IndexReader对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //3、创建一个IndexSearcher对象,构造方法中的参数indexReader对象。
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //4、创建一个Query对象,TermQuery
        Query query = new TermQuery(new Term("name", "spring"));
        //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;
            //根据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("-----------------分割线");
        }
        //9、关闭IndexReader对象
        indexReader.close();
    }

分析器的分析过程

指定分析器

对于文档的处理是交给分析器完成的,包括去除标点符号等等

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

使用的标准分析器

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

继承关系

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

java中无指针,实际为引用 查看分析器的分析效果 使用Analyzer对象的tokenStream方法返回一个TokenStream对象。词对象中包含了最终分词结果。 实现步骤: 1)创建一个Analyzer对象,StandardAnalyzer对象 2)使用分析器对象的tokenStream方法获得一个TokenStream对象 3)向TokenStream对象中设置一个引用,相当于数一个指针,指向某一个方向,取出相应内容 4)调用TokenStream对象的rest方法。如果不调用抛异常,分析完后指针位置不确定,复位指针位置为初始 5)使用while循环遍历TokenStream对象 6)关闭TokenStream对象 将文本提供,然后查看分析效果,即结果是否与预期一致,排除符号,空格拆分等

代码语言:javascript
复制
@Test
    public void testTokenStream() throws Exception {
        //1)创建一个Analyzer对象,StandardAnalyzer对象
//        Analyzer analyzer = new StandardAnalyzer();
        Analyzer analyzer = new IKAnalyzer();
        //2)使用分析器对象的tokenStream方法获得一个TokenStream对象
        //此处英文语句
        TokenStream tokenStream = analyzer.tokenStream("", "2017年12月14日 - learnEEELucene概述公安局Lucene是一款高性能的、可扩展的信息检索(IR)工具库。信息检索是指文档搜索、文档内信息搜索或者文档相关的元数据搜索等操作。");
        //3)向TokenStream对象中设置一个引用,相当于数一个指针
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
        //4)调用TokenStream对象的rest方法。如果不调用抛异常
        tokenStream.reset();
        //5)使用while循环遍历TokenStream对象
        while(tokenStream.incrementToken()) {
            System.out.println(charTermAttribute.toString());
        }
        //6)关闭TokenStream对象
        tokenStream.close();
    }

如果是英文,一个空格可以区分单词 但是中文只能拆分为一个个字,不靠谱 因此应该使用中文分析器 问题解决:添加jar

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

添加配置文件

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

常用词典,禁用词典,配置文件

IKAnalyze的使用方法 1)把IKAnalyzer的jar包添加到工程中 2)把配置文件和扩展词典添加到工程的classpath下 注意:扩展词典严禁使用windows记事本编辑保证扩展词典的编码格式是utf-8,windows的utf-8是utf-8+bom,可以使用notepad++编辑 扩展词典:添加一些新词, 停用词词典:无意义的词或者是敏感词汇,就是说不会为这些词汇创建索引

索引库维护

常用域解析

案例中使用的都是文本域,这代表存入的都是字符串 然而假如索引文档大小等,如果进行大小检索,数字就需要不同的域存储以便进行更多操作

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

内容是否存储不影响查询 StringField(FieldName, FieldValue,Store.YES))不分词查询,具有完整意义的词,没必要分开,比如身份证号,名字 这里指文件路径作为整体存在

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

LongPoint(String name, long… point)不存储,仅仅作为运算使用,存储可以用StoredField(FieldName, FieldValue) 可以取同样的名字,来保证既可以索引,可以存储

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

重新生成索引,进行查看

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

仍然可以索引,但是无法查看内容了 path,size都是无存储类型,因此这里不可见 因为大小被store存储了,看文档可以看出来

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

1、添加文档

代码语言:javascript
复制
 private IndexWriter indexWriter;
    @Before
    public void init() throws Exception {
        //创建一个IndexWriter对象,需要使用IKAnalyzer作为分析器
        indexWriter =
                new IndexWriter(FSDirectory.open(new File("C:\\temp\\index").toPath()),
                        new IndexWriterConfig(new IKAnalyzer()));
    }
    @Test
    public void addDocument() throws Exception {
        //创建一个IndexWriter对象,需要使用IKAnalyzer作为分析器
        IndexWriter indexWriter =
                new IndexWriter(FSDirectory.open(new File("C:\\temp\\index").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", "c:/temp/helo"));
        // 把文档写入索引库
        indexWriter.addDocument(document);
        //关闭索引库
        indexWriter.close();
    }

2、删除文档 1)删除全部,代价太高,一般不经常重建

代码语言:javascript
复制
@Test
    public void deleteAllDocument() throws Exception {
        //删除全部文档
        indexWriter.deleteAll();
        //关闭索引库
        indexWriter.close();
    }
代码语言:javascript
复制
2)根据查询、关键词删除文档
代码语言:javascript
复制
@Test
    public void deleteDocumentByQuery() throws Exception {
        indexWriter.deleteDocuments(new Term("name", "apache"));
        indexWriter.close();
    }

3、修改文档 修改的原理是先删除后添加,被替代的域就不存在了 不同的文本对象中可以有不同的域

代码语言:javascript
复制
@Test
    public void updateDocument() throws Exception {
        //创建一个新的文档对象
        Document document = new Document();
        //向文档对象中添加域
        document.add(new TextField("name", "更新之后的文档", Field.Store.YES));
        document.add(new TextField("name1", "更新之后的文档2", Field.Store.YES));
        document.add(new TextField("name2", "更新之后的文档3", Field.Store.YES));
        //更新操作
        indexWriter.updateDocument(new Term("name", "spring"), document);
        //关闭索引库
        indexWriter.close();
    }

索引库查询

1、使用Query的子类 1)TermQuery 根据关键词进行查询。 需要指定要查询的域及要查询的关键词。就像上面的案例 2)RangeQuery 范围查询

代码语言:javascript
复制
private IndexReader indexReader;
    private IndexSearcher indexSearcher;
    @Before
    public void init() throws Exception {
        indexReader = DirectoryReader.open(FSDirectory.open(new File("C:\\temp\\index").toPath()));
        indexSearcher = new IndexSearcher(indexReader);
    }
    @Test
    public void testRangeQuery() throws Exception {
        //创建一个Query对象
        Query query = LongPoint.newRangeQuery("size", 0l, 100l);
        printResult(query);
    }
    private void printResult(Query query) throws Exception {
        //执行查询
        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();
    }

2、使用QueryPaser进行查询 可以对要查询的内容先分词,然后基于分词的结果进行查询。 添加一个jar包 lucene-queryparser-7.4.0.jar

代码语言:javascript
复制
 @Test
    public void testQueryParser() throws Exception {
        //创建一个QueryPaser对象,两个参数
        QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
        //参数1:默认搜索域,参数2:分析器对象
        //使用QueryPaser对象创建一个Query对象
        Query query = queryParser.parse("lucene是一个Java开发的全文检索工具包");
        //执行查询
        printResult(query);
    }

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/100228.html原文链接:

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 简介
  • 1、什么是全文检索,如何实现全文检索
  • 2、Lucene实现全文检索的流程
  • 3、配置开发环境
    • 创建索引
      • 查看索引
        • 简单查询
        • 以文档为基准的查看
      • 代码实现查询
      • 分析器的分析过程
        • 指定分析器
        • 索引库维护
          • 常用域解析
          • 索引库查询
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
          http://www.vxiaotou.com