Java全文检索Lucene急速入门知识

简介: Java全文检索Lucene急速入门知识

Java全文检索Lucene急速入门知识

1、流程分析

image.png

1、绿色表示索引过程,对要搜索的原始内容进行索引构建一个索引库,索引过程包括:确定原始内容即要搜索的内容 ---> 采集文档 ---> 创建文档 ---> 分析文档 ---> 索引文档。

2、红色表示搜索过程,从索引库中搜索内容,搜索过程包括:用户通过搜索界面 ---> 创建查询 ---> 执行搜索,从索引库搜索 ---> 渲染搜索结果

2、生成索引看库

2.1、获得原始文档

原始文档,简单地说就是我们准备搜索的内容。可以是互联网上的网页、数据库中的数据、磁盘上的文件等。

2.2、创建文档对象

在创建索引前,我们需要将原始内容创建成文档(Document),一个文档中会包括很多的的域(Field),然后在域中存储原始文档的一些内容。 每一个文件可以当成一个document,Document下面可以包含多个Field(域),域由两部分组成:域的名称和值,可以理解为以key和value的形式存放数据。其实还有一个类型,用来表示域中存放数据的类型,这个不用我们考虑,可以在调用方法时作区分。比如我们现在以磁盘上面的文件为例,那么每个文件下可以包含四个域:name(名称)、path(路径)、context(内容)、size(文件大小)。如下所示:

名称
name Helloworld.java
path f://Helloworld.java
context public class HelloWorld...
size 4562

注意:每个文档都有一个唯一的编号,就是文档id。

Field域还有三个属性,如下所示:

  • 是否分析:是否对域的内容进行分词处理。前提是我们要对域的内容进行查询。
  • 是否索引:将Field分析后的词或整个Field值进行索引,只有索引的内容才能搜索到。比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。
  • 是否存储:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。

总结下来就是,是否查询这些内容决定了是否进行分析,时候进行搜索决定了是否索引,是否眼展示内容决定了是否进行存储。

附上一张表,用来展示各个方法对这三个属性的条件。

image.png


2.3、分析文档

说白了这就是一个分词的操作,就是挑选出关键词,去除没用的。比如根据空格进行字符串的拆分得到关键词列表、将关键词中的字母转为小写、去除标点符号、去除停用词(即需要过滤掉的词汇)等。

原文档内容:Lucene is a Java full-text search engine. Lucene is not a complete application, but rather a code library and API that can easily be used to add search capabilities to applications.

分析后得到的语汇单元:lucene、java、full、search、engine。。。。

分析得到的每个关键词封装成一个Term对象,Term中包含两部分:一部分是关键词所在的域名,另一部分是关键词的内容。不同的域中拆分出来的相同的关键词是不同的term。

2.4、创建索引

说了那么多,我们先进行导包操作,需要导入下面这些包,按照从左往右的顺序依次为:文件输入输出、汉语词法分析、Lucene包。

commons-io、IK-Analyzer-1.0-SNAPSHOT、lucene-analyzers-common-7.4.0、lucene-core-7.4.0、lucene-queryparser-7.4.0

接着来看一下创建索引的方法:

public void createIndex() throws Exception {
        //1、创建一个Director对象,指定索引库保存的位置。
        //把索引库保存在内存中
        //Directory directory = new RAMDirectory();
        //把索引库保存在磁盘
        Directory directory = FSDirectory.open(new File("F:\\index").toPath());
        //2、基于Directory对象创建一个IndexWriter对象
        IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //3、读取磁盘上的文件,对应每个文件创建一个文档对象。
        File dir = new File("F:\\texts");
        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 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(fieldSize);
            document.add(fieldSizeValue);
            document.add(fieldSizeStore);
            //5、把文档对象写入索引库
            indexWriter.addDocument(document);
        }
        //6、关闭indexwriter对象
        indexWriter.close();
    }

3、查询索引

3.1、关键词查询

代码实现上上面几乎相同,最基本就是根据关键词查询内容,如下所示:

public void searchIndex() throws Exception {
        //1、创建一个Director对象,指定索引库的位置
        Directory directory = FSDirectory.open(new File("F:\\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"));
        }
        //9、关闭IndexReader对象
        indexReader.close();
    }

3.2、语句查询

当然我们也可以用文本,例如一句话去查询,去查询哪些文件里面包含这句话。那么代码该怎么操作呢?

private void printResult(Query query) throws Exception {
    IndexReader indexReader = 
        DirectoryReader.open(FSDirectory.open(new File("F:\\index").toPath()));
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //创建一个QueryPaser对象,两个参数。参数1:默认搜索域,参数2:分析器对象
    QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
    //使用QueryPaser对象创建一个Query对象
    Query query = queryParser.parse("lucene是一个Java开发的全文检索工具包");
    //执行查询,查询出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"));
    }
    indexReader.close();
}

3.1、范围查询

我们也可以根据范围进行查询,如下所示:

//创建一个Query对象,查询size的范围,最小值01,最大值1001
Query query = LongPoint.newRangeQuery("size", 0l, 100l);
printResult(query);

4、维护索引

4.1、添加索引

public void addDocument() throws Exception {
        //创建一个IndexWriter对象,需要使用IKAnalyzer作为分析器
        IndexWriter indexWriter =
                new IndexWriter(FSDirectory.open(new File("F:\\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", "新添加的文件路径"));
        // 把文档写入索引库
        indexWriter.addDocument(document);
        //关闭索引库
        indexWriter.close();
    }

4.2、删除索引

public void deleteAllDocument() throws Exception {
    //删除全部文档
    indexWriter.deleteAll();
    //关闭索引库
    indexWriter.close();
 }
//根据条件删除索引
public void deleteDocumentByQuery() throws Exception {
    indexWriter.deleteDocuments(new Term("name", "apache"));
    indexWriter.close();
}

4.3、更新索引

原理就是先删除后添加。

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();
}


讲了这么多,大家是不是感觉很流弊的样子,但是要告诉大家一点,实际开发过程中,一般不会使用Lucene进行开发,我们会选择Elasticsearch之类的检索引擎,这个底层也是Lucene实现的,大家可以先了解一下Lucene,后面我们再聊一下ES~

目录
相关文章
|
4天前
|
算法 Java C++
刷题两个月,从入门到字节跳动offer丨GitHub标星16k+,美团Java面试题
刷题两个月,从入门到字节跳动offer丨GitHub标星16k+,美团Java面试题
|
5天前
|
算法 Java Python
保姆级Java入门练习教程,附代码讲解,小白零基础入门必备
保姆级Java入门练习教程,附代码讲解,小白零基础入门必备
|
6天前
|
SQL Java 关系型数据库
零基础轻松入门Java数据库连接(JDBC)
零基础轻松入门Java数据库连接(JDBC)
14 0
|
6天前
|
存储 安全 算法
Java一分钟之-Java集合框架入门:List接口与ArrayList
【5月更文挑战第10天】本文介绍了Java集合框架中的`List`接口和`ArrayList`实现类。`List`是有序集合,支持元素重复并能按索引访问。核心方法包括添加、删除、获取和设置元素。`ArrayList`基于动态数组,提供高效随机访问和自动扩容,但非线程安全。文章讨论了三个常见问题:索引越界、遍历时修改集合和并发修改,并给出避免策略。通过示例代码展示了基本操作和安全遍历删除。理解并正确使用`List`和`ArrayList`能提升程序效率和稳定性。
12 0
|
6天前
|
Java API 开发工具
java与Android开发入门指南
java与Android开发入门指南
16 0
|
6天前
|
Java
Java一分钟之-类与对象:面向对象编程入门
【5月更文挑战第8天】本文为Java面向对象编程的入门指南,介绍了类与对象的基础概念、常见问题及规避策略。文章通过代码示例展示了如何定义类,包括访问修饰符的适当使用、构造器的设计以及方法的封装。同时,讨论了对象创建与使用时可能遇到的内存泄漏、空指针异常和数据不一致等问题,并提供了相应的解决建议。学习OOP需注重理论与实践相结合,不断编写和优化代码。
31 1
|
6天前
|
Java 编译器 对象存储
java一分钟之Java入门:认识JDK与JVM
【5月更文挑战第7天】本文介绍了Java编程的基础——JDK和JVM。JDK是包含编译器、运行时环境、类库等的开发工具包,而JVM是Java平台的核心,负责执行字节码并实现跨平台运行。常见问题包括版本不匹配、环境变量配置错误、内存溢出和线程死锁。解决办法包括选择合适JDK版本、正确配置环境变量、调整JVM内存参数和避免线程死锁。通过代码示例展示了JVM内存管理和基本Java程序结构,帮助初学者更好地理解JDK和JVM在Java编程中的作用。
23 0
|
6天前
|
设计模式 算法 安全
Java多线程编程实战:从入门到精通
【4月更文挑战第30天】本文介绍了Java多线程编程的基础,包括线程概念、创建线程(继承`Thread`或实现`Runnable`)、线程生命周期。还讨论了线程同步与锁(同步代码块、`ReentrantLock`)、线程间通信(等待/通知、并发集合)以及实战技巧,如使用线程池、线程安全设计模式和避免死锁。性能优化方面,建议减少锁粒度和使用非阻塞算法。理解这些概念和技术对于编写高效、可靠的多线程程序至关重要。
|
6天前
|
算法 Java 大数据
Java从入门到精通学习报告
Java从入门到精通学习报告
20 1
|
6天前
|
数据采集 存储 前端开发
Nutch库入门指南:利用Java编写采集程序,快速抓取北京车展重点车型
2024年北京车展凸显电动车全球热度,中国引领市场,展出117台全球首发车,包括30台跨国公司电动车。借助Nutch库抓取汽车网站数据,分析电动车市场趋势。通过配置代理和多线程爬虫,高效收集新车信息,助力理解市场动态,推动可持续交通发展。
Nutch库入门指南:利用Java编写采集程序,快速抓取北京车展重点车型