lucene超基础入门案例分析

―、什么是全文检索

1、数据的分类

​ 1.结构化数据

​ 格式固定、长度固定、数据类型固定

​ 例如数据库中的数据

​ 2.非结构化数据

​ word文档,pdf文档、邮件、html、xml

​ 格式不固定、长度不固定、数据类型不固定

2、数据的査词

​ 1.结构化数据的査词

​ SQL语句,査词结构化数据的方法。简单、速度快

​ 2.非结构化数据的査词

​ 从文本文件中找出包含spring单词的文件,

​ 1.目测

​ 2.使用程序把文档读取到内存中,然后匹配字符串。顺序扫描

​ 3.把非结构化数据变成结构化数据

​ 先根据空格进行字符拿拆分,得到一个单词列表.基于单词列表创建一个索引

​ 然后查询索引,根据单词和文档的对应关系找到文档列表,这个过程叫做全文检索

​ 索引: 一个为了提高査词速度.创建某种数据结构的集合

3、全文检索

​ 先创建索引然后查询索引的过程叫做全文检索

​ 索引一次创建可以多次使用,表现为每次查询速度很快.

二、 全文检索的应用场景

​ 1、 搜索引擎

​ 百度、360搜索、谷歌、搜狗

​ 2、 站内搜京

​ 论坛搜索、微博、文章搜索

​ 3、 电商搜索

​ 淘宝搜索、京东搜索

​ 4、 只要是有搜索的地方就可以使用全文检索技术

三、 什么是Lucene

​ Lucene是一个基于java开发全文检索工具包

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

1.创建索引

​ 1.获得文档

​ 原始文档:要基于那些数据来进行搜索.那么这些数据就是原始文档

​ 搜索引擎:使用爬虫获得原始文档

​ 站内搜索:数据库中的数据。

​ 案例:直接使用 io 流读取磁盘上的文件。

​ 2.构建文档对象

​ 对应每个原始文档创建一个 Document 对象

​ 每个Document对象中包含多个域( field )

​ 域中保存就是原始文档数据。

​ 域的名称

​ 域的值

​ 每个文档都有一个唯一的编号,就是文档id

​ 3.分析文档

​ 就是分词的过程

​ 1.根据空格进行字符串拆分.得到一个单词列表

​ 2.把单词统一转换成小写。

​ 3.去除标点符号

​ 4.去除停用词

​ 停用词:无意义的词

​ 每个关键词都封装成一个下 Term对象中。

​ Term中包含两部分内容:

​ 关键词所在的域

​ 关键词本者

​ 不同的域中拆分出来的相同的关键词是不同的 Term

​ 4.创建索引

​ 基于关键词列表创建一个索引.保存到索引库中。

​ 索引库中:

​ 索引

​ document 对象

​ 关键词和文档的对应关系

​ 通过词语找文档.这种索引的结构叫倒排索引结构。

2.查询索引

​ 1.用户查询接口

​ 用户输入查询条件的地方

​ 例如:百度的搜索框

​ 2.把关键词封装成一个查询对象

​ 要查询的域

​ 要搜索的关键词

​ 3.执行查询

​ 根据要查询的关键词到对应的域上进行搜索。

​ 找到关键词,根据关键词找到对应的文档

​ 4.渲染结果

​ 根据文档的 id 找到文档对象

​ 对关键词进行高亮显示

​ 分页处理

​ 最终展示给用户看。

五、入门程序

1 .创建索引

​ 环境:

​ 需要下载 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

​ 步骤:

​ 1. 创建一个Director对象.指定索引库保存的位置 。

​ 2. 基于 Directory 对象创建一个 IndexWriter对象

​ 3. 读取磁盘上的文件.对应每个文件创建一个文档对象。

​ 4. 向文档对象中添加域

​ 5. 把文档对象写入索引库

  1. 关闭 indexwriter 对象

    //创建索引
    @Test
    public void createIndex() throws Exception {
    
        //指定索引库存放的路径
        //D:\temp\index
        Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());
        //索引库还可以存放到内存中
        //Directory directory = new RAMDirectory();
        //创建indexwriterCofig对象
        IndexWriterConfig config = new IndexWriterConfig();
        //创建indexwriter对象
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //原始文档的路径
        File dir = new File("D:\\temp\\searchsource");
        for (File f : dir.listFiles()) {
            //文件名
            String fileName = f.getName();
            //文件内容
            String fileContent = FileUtils.readFileToString(f);
            //文件路径
            String filePath = f.getPath();
            //文件的大小
            long fileSize  = FileUtils.sizeOf(f);
            //创建文件名域
            //第一个参数:域的名称
            //第二个参数:域的内容
            //第三个参数:是否存储
            Field fileNameField = new TextField("filename", fileName, Field.Store.YES);
            //文件内容域
            Field fileContentField = new TextField("content", fileContent, Field.Store.YES);
            //文件路径域(不分析、不索引、只存储)
            Field filePathField = new TextField("path", filePath, Field.Store.YES);
            //文件大小域
            Field fileSizeField = new TextField("size", fileSize + "", Field.Store.YES);
    
            //创建document对象
            Document document = new Document();
            document.add(fileNameField);
            document.add(fileContentField);
            document.add(filePathField);
            document.add(fileSizeField);
            //创建索引,并写入索引库
            indexWriter.addDocument(document);
        }
        //关闭indexwriter
        indexWriter.close();
    }
    
    

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

在这里插入图片描述

3. 查询索引库

步骤:

​ 1. 创建一个 Directory 对象,指定索引库的位置

​ 2. 创建一个 IndexReader 对象

​ 3. 创建一个 IndexSearcher 对象.构造方法中的参数 indexReader 对象。

​ 4. 创建一个 Query对象,TermQuery

​ 5. 执行查询,得到一个 TopDocs 对象

​ 6. 取查询结果的总记录数

​ 7. 取文档列表

​ 8. 打印文档中的内容

  1. 关闭 indexReader 对象

    //查询索引库
    @Test
    public void searchIndex() throws Exception {
        //指定索引库存放的路径
        //D:\temp\index
        Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());
        //创建indexReader对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //创建indexsearcher对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //创建查询
        Query query = new TermQuery(new Term("filename", "apache"));
        //执行查询
        //第一个参数是查询对象,第二个参数是查询结果返回的最大值
        TopDocs topDocs = indexSearcher.search(query, 10);
        //查询结果的总条数
        System.out.println("查询结果的总条数:"+ topDocs.totalHits);
        //遍历查询结果
        //topDocs.scoreDocs存储了document对象的id
        for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
            //scoreDoc.doc属性就是document对象的id
            //根据document的id找到document对象
            Document document = indexSearcher.doc(scoreDoc.doc);
            System.out.println(document.get("filename"));
            //System.out.println(document.get("content"));
            System.out.println(document.get("path"));
            System.out.println(document.get("size"));
            System.out.println("-------------------------");
        }
        //关闭indexreader对象
        indexReader.close();
    }
    

六、分析器

​ 默认使用的数标准分析器 StandardAnalyzer

1. 查看分析器的分析效果

​ 使用 Analyzer 对象的 tokenStream 方法返回一个 TokenStream 对象。词对象中包含了最终分词结果。

​ 实现步骤:

​ 1. 创建一个Analyzer对象,StandardAnalyzer对象

​ 2. 使用分析器对象的 tokenStream 方法获得一个 TokenStream 对象

​ 3. 向 TokenStream 对象中设置一个引用,相当于数一个指针

​ 4. 调用 TokenStream 对象的 reset 方法。如果不调用抛异常

​ 5. 使用 while 循环遍历 TokenStream 对象

  1. 关闭TokenStream对象。

    //查看标准分析器的分词效果
    @Test
    public void testTokenStream() throws Exception {
        //创建一个标准分析器对象
        Analyzer analyzer = new StandardAnalyzer();
        //获得tokenStream对象
        //第一个参数:域名,可以随便给一个
        //第二个参数:要分析的文本内容
        TokenStream tokenStream = analyzer.tokenStream("test", "The Spring Framework provides a comprehensive programming and configuration model.");
        //添加一个引用,可以获得每个关键词
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
        //添加一个偏移量的引用,记录了关键词的开始位置以及结束位置
        OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
        //将指针调整到列表的头部
        tokenStream.reset();
        //遍历关键词列表,通过incrementToken方法判断列表是否结束
        while(tokenStream.incrementToken()) {
            //关键词的起始位置
            System.out.println("start->" + offsetAttribute.startOffset());
            //取关键词
            System.out.println(charTermAttribute);
            //结束位置
            System.out.println("end->" + offsetAttribute.endOffset());
        }
        tokenStream.close();
    }
    

2. IKAnalyzer 的便用方法(中文分析器)

​ 1.把 IKAnalyzer 的 jar 包添加到工程中

​ 2.把配置文件和扩展词典添加到工程的 classpath 下

​ 注意:扩展词典严禁使用weindows记事本编辑保证扩展词典的编码格式是utf-8

​ 扩展词典:添加一些新词

​ 停用词词典:无意义的词或者是敏感词汇

@Test
public void addDocument() throws Exception {
    //索引库存放路径
    Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());
    IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
    //创建一个indexwriter对象
    IndexWriter indexWriter = new IndexWriter(directory, config);
//...
}

七、索引库维护

  1. Field域的属性

    是否分析:是否对域的内容进行分词处理。前提是我们要对域的内容进行查询。

    是否索引:将Field分析后的词或整个Field值进行索引,只有索引方可搜索到。

    比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。

    是否存储:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取

    比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。

    是否存储的标准:是否要将内容展示给用户

    Field类数据类型Analyzed 是否分析Indexed 是否索引Stored 是否存储说明
    StringField(FieldName, FieldValue,Store.YES))字符串NYY或N这个Field用来构建一个字符串Field,但是不会进行分析,会将整个串存储在索引中,比如(订单号,姓名等) 是否存储在文档中用Store.YES或Store.NO决定
    LongPoint(String name, long… point)Long型YYN可以使用LongPoint、IntPoint等类型存储数值类型的数据。让数值类型可以进行索引。但是不能存储数据,如果想存储数据还需要使用StoredField。
    StoredField(FieldName, FieldValue)重载方法,支持多种类型NNY这个Field用来构建不同类型Field 不分析,不索引,但要Field存储在文档中
    TextField(FieldName, FieldValue, Store.NO) 或 TextField(FieldName, reader)字符串 或 流YYY或N如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略.
  2. 添加文档

    //添加索引
    @Test
    public void addDocument() throws Exception {
        //索引库存放路径
        Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());
        IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
        //创建一个indexwriter对象
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //创建一个Document对象
        Document document = new Document();
        //向document对象中添加域。
        //不同的document可以有不同的域,同一个document可以有相同的域。
        document.add(new TextField("filename", "新添加的文档", Field.Store.YES));
        document.add(new TextField("content", "新添加的文档的内容", Field.Store.NO));
        //LongPoint创建索引
        document.add(new LongPoint("size", 1000l));
        //StoreField存储数据
        document.add(new StoredField("size", 1000l));
        //不需要创建索引的就使用StoreField存储
        document.add(new StoredField("path", "d:/temp/1.txt"));
        //添加文档到索引库
        indexWriter.addDocument(document);
        //关闭indexwriter
        indexWriter.close();
    }
    

  3. 删除文档

  4. 删除全部 (说明:将索引目录的索引信息全部删除,直接彻底删除,无法恢复)

    //删除全部索引
    	@Test
    	public void deleteAllIndex() throws Exception {
    		IndexWriter indexWriter = getIndexWriter();
    		//删除全部索引
    		indexWriter.deleteAll();
    		//关闭indexwriter
    		indexWriter.close();
    	}
    
  5. 根据查询、关键词删除文档 ,

    //根据查询条件删除索引
    	@Test
    	public void deleteIndexByQuery() throws Exception {
    		IndexWriter indexWriter = getIndexWriter();
    		//创建一个查询条件
    		Query query = new TermQuery(new Term("filename", "apache"));
    		//根据查询条件删除
    		indexWriter.deleteDocuments(query);
    		//关闭indexwriter
    		indexWriter.close();
    	}
    

3. 修改文档

​ 修改的原理是先删除后添加

//修改索引库
@Test
public void updateIndex() throws Exception {
    IndexWriter indexWriter = getIndexWriter();
    //创建一个Document对象
    Document document = new Document();
    //向document对象中添加域。
    //不同的document可以有不同的域,同一个document可以有相同的域。
    document.add(new TextField("filename", "要更新的文档", Field.Store.YES));
    document.add(new TextField("content", " Lucene 简介 Lucene 是一个基于 Java 的全文信息检索工具包," +
                                                       "它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能。",
                Field.Store.YES));
    indexWriter.updateDocument(new Term("content", "java"), document);
    //关闭indexWriter
    indexWriter.close();
}

八、索引库查询

1. 使用 Query 的子类

​ 1. TermQuery

​ 根据关键词进行查询。

​ 需要指定要查询的域及要查询的关键词

//使用Termquery查询
@Test
public void testTermQuery() throws Exception {
    Directory directory = FSDirectory.open(new File("D:\\temp\\index").toPath());
    IndexReader indexReader = DirectoryReader.open(directory);
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    
    //创建查询对象
    Query query = new TermQuery(new Term("content", "lucene"));
    //执行查询
    TopDocs topDocs = indexSearcher.search(query, 10);
    //共查询到的document个数
    System.out.println("查询结果总数量:" + topDocs.totalHits);
    //遍历查询结果
    for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
        Document document = indexSearcher.doc(scoreDoc.doc);
        System.out.println(document.get("filename"));
        //System.out.println(document.get("content"));
        System.out.println(document.get("path"));
        System.out.println(document.get("size"));
    }
    //关闭indexreader
    indexSearcher.getIndexReader().close();
}

​ 2. RangeQuery

​ 范围查询

@Test
public void testRangeQuery() throws Exception {
    IndexSearcher indexSearcher = getIndexSearcher();
    Query query = LongPoint.newRangeQuery("size", 0l, 10000l);
    printResult(query, indexSearcher);
}

2. 使用 QueryPaser 解析查询表达式

  1. 可以对要查询的内容先分词,然后基于分词的结果进行查询。
  2. 添加一个jar包

​ lucene-queryparser-7.4.0.jar

@Test
public void testQueryParser() throws Exception {
    IndexSearcher indexSearcher = getIndexSearcher();
    //创建queryparser对象
    //第一个参数默认搜索的域
    //第二个参数就是分析器对象
    QueryParser queryParser = new QueryParser("content", new IKAnalyzer());
    Query query = queryParser.parse("Lucene是java开发的");
    //执行查询
    printResult(query, indexSearcher);
}
private void printResult(Query query, IndexSearcher indexSearcher) throws Exception {
    //执行查询
    TopDocs topDocs = indexSearcher.search(query, 10);
    //共查询到的document个数
    System.out.println("查询结果总数量:" + topDocs.totalHits);
    //遍历查询结果
    for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
        Document document = indexSearcher.doc(scoreDoc.doc);
        System.out.println(document.get("filename"));
        //System.out.println(document.get("content"));
        System.out.println(document.get("path"));
        System.out.println(document.get("size"));
    }
    //关闭indexreader
    indexSearcher.getIndexReader().close();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值