Lucene初步使用、理解及代码

Lucene全文检索

1.什么是全文检索

日常中我们所接触到的数据,可以大概的分为:
  结构化数据:指有固定长度、格式的数据。如:数据库数据,元数据等
  非结构化数据:没有固定长度、格式的数据。如:word文档、txt文件等。
  结构化数据我们可以通过sql语句等进行查询,可以很方便的查询到我们所需要的数据,但是非结构化数据,我们要想从中获取我们想要的词汇或者数据时,就会很麻烦,他们没有规律可以查找。
  要想从非结构化数据中查找数据,我们可以:
  把非结构化数据按照一定的格式,提取出来,重新组织,形成结构化的数据,这样我们就可以如结构化数据一样,很容易查找到我们想要的数据,这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引。
  例如:字典,每个字的解释都是非结构化的数据,查找起来就会很麻烦,我们可以先把每个字的读音或者偏旁提取出来,形成一个索引,每个都对应一个页码,这样我们在查找时,就可以先通过偏旁或者读音先查找到对应的页码,这样查找单词就可以很方便。

  这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。

全文搜索流程:
在这里插入图片描述
1、绿色表示索引过程,对要搜索的原始内容进行索引构建一个索引库,索引过程包括:
确定原始内容即要搜索的内容采集文档创建文档分析文档索引文档

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

2.lucene中的一些概念

原始文件:
  即我们要在那些数据中查找,这些数据即为原始数据。
  如:数据库中的数据、磁盘中的txt文件…


文档对象:
  获取到原始文件中的内容之后,我们需要创建索引,并把内容添加到document对象中,然后存放在索引库中,而document对象中包含很多的field对象。document对象可以看作数据库的表,而field可以看作数据库中的属性。
在这里插入图片描述
注意:每个Document可以有多个Field,不同的Document可以有不同的Field,同一个Document可以有相同的Field(域名和域值都相同)

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


3.lucene的自我理解

lucene其实就是读取原始数据,然后把每一个原始数据都封装为一个单独的document对象,添加直索引库时,会自动的根据分词器把每个field进行分词处理,形成一个词典,并对这个词典进行索引处理,然后再索引库中添加的既是索引(词典)和document对象,还有他们之间的联系关系(倒排索引结构)。当用户查找词汇时,会先查找索引(词典),然后根据词典和document对象的关系,查找到对应的document对象,再查找对应的field对象,就可以查出内容了。


倒排索引结构:
在这里插入图片描述
左边为词典列表,右边为倒排表,即为每个词存在于那些文档的文档id。也就是查询词,找到对应的文档,查找内容,即为倒排索引结构。

4.lucene的使用步骤

1.导入jar包。
在这里插入图片描述

2.创建原始文件
在这里插入图片描述
3.创建索引库,把上边的文件全部解析并记载进索引库中

@Test
    public void createLucene() throws IOException {
    
    
        //1.创建directory对象,指定索引库的存放位置
        //此方式为在本地磁盘存储索引库
        Directory dir = FSDirectory.open(new File("D:\\luncene").toPath());
        //此方式为索引库存放于内存中(不建议:每次加载太慢)
        //Directory dir1 = new RAMDirectory();
        //2.配置设置:默认使用的是StandardAnalyzer解析器,对中文不友好,后期可以在构造参数中指定中文解析器
        IndexWriterConfig config = new IndexWriterConfig();
        //3.根据directory和indexWriterConfig创建IndexWriter对象,用于向索引库中写数据
        IndexWriter writer = new IndexWriter(dir,config);
        //4.加载数据,并封装为document对象
        File filePaths = new File("D:\\lucenePro");
        //获取文件夹下所有的file
        File[] files = filePaths.listFiles();
        //遍历files数组,获取每一个file
        for (File file : files) {
    
    
            //获得文件名
            String fileName = file.getName();
            //获取文件的路径
            String filePath = file.getPath();
            //获取文件的内容
            String fileContent = FileUtils.readFileToString(file);
            //获取文件的大小
            long fileSize = FileUtils.sizeOf(file);

            //创建域(属性)对象,并在域对象中存放对应的值
            Field fieldName = new TextField("name",fileName, Field.Store.YES);
            Field fieldPath = new TextField("path",filePath, Field.Store.YES);
            Field fieldContent = new TextField("content",fileContent, Field.Store.YES);
            Field fieldSize = new TextField("size",fileSize+"", Field.Store.YES);
            //创建document对象,并把域对象赋值
            Document document = new Document();
            document.add(fieldName);
            document.add(fieldPath);
            document.add(fieldContent);
            document.add(fieldSize);

            //写入到索引库
            writer.addDocument(document);

        }

        //释放写资源
        writer.close();

    }

注:如果使用了FileUtis,需要导入commons.io.jar包

执行完毕,找到创建Directory对象的地方的文件夹位置,即可看到:
在这里插入图片描述
就上传成功了。

5.使用luke查看索引库

luke下载地址:
luke下载地址

下载完毕必须要进入目录,在pom.xml文件所在目录下,打开黑窗口,运行命令:mvn package命令,才能使用luke,否则出现
ERROR,unable to access jarfile:.\target\luke-swings-with-deps.jar错误

另外:下载最新的lucene,会自带luke,直接双击就能用。

6.查询索引库

@Test
    public void queryLucene() throws IOException {
    
    
        //创建Directory对象,指向索引库
        Directory dir = FSDirectory.open(new File("D:\\luncene").toPath());
        //创建reader对象
        IndexReader reader = DirectoryReader.open(dir);
        //创建sercher查找对象
        IndexSearcher searcher = new IndexSearcher(reader);
        //封装查询条件对象 term:第一个参数:你要查询那个域的,第二个参数:你要查询的参数
        Query query = new TermQuery(new Term("name","apache"));
        //查询 第一个参数:你封装的查询条件,第二个参数:最多查询多少条的数据
        TopDocs topDocs = searcher.search(query, 10);
        //获取查询的总条数
        TotalHits totalHits = topDocs.totalHits;
        System.out.println(totalHits);

        //根据获取的topDocs对象,获得ScoreDoc[],ScoreDoc里面存放的是document的id
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
    
    
            //根据document的id查询document对象
            Document doc = searcher.doc(scoreDoc.doc);
            //获取内容
            String name = doc.get("name");
            String path = doc.get("path");
            String content = doc.get("content");
            String size = doc.get("size");
            System.out.println(name);
            System.out.println(path);
            System.out.println(content);
            System.out.println(size);
            System.out.println("-----------------------------------");
        }
        reader.close();

    }

7.中文分词器

使用自带的标准分词器,它能够很好的把英文进行分词,但是中文就不太友好,会把每个字作为一个词来分。如果要分词中文,就需要使用中文分词器:IKAnalyzer
  使用方法:
1.导入jar包,导入配置
在这里插入图片描述
配置:
hotword.dic:表示一些特殊词,在里面添加之后,即可被解析器分词
stopword.dic:停用词、敏感词列表
IkAnalyzer.cfg.xml:IkAnalyzer配置信息

使用案例:

 /**
     * 使用中文分析器分词
     */
    @Test
    public void createIndexByIk() throws IOException {
    
    
        //创建directory对象
        Directory dir = FSDirectory.open(new File("D:\\luncene").toPath());
        //创建IndexWriterConfig对象,并指定分词器
        IndexWriterConfig writerConfig = new IndexWriterConfig(new IKAnalyzer());
        //创建IndexWriter对象
        IndexWriter writer = new IndexWriter(dir,writerConfig);
        //获取数据
        String text = "但是老外写的分词器对中文分词一般都是单字分词,分词的效果不好。 国人林良益写的IK Analyzer应该是最好的Lucene中文分词器之一,而且随着Lucene的版本更新而不断更新";
        Field field = new TextField("name",text, Field.Store.YES);
        Document document = new Document();
        document.add(field);

        writer.addDocument(document);
        writer.close();
    }

效果:
在这里插入图片描述
如果需要对一些词进行特殊的分词,例如公司名称等,则需要单独在hotword里面进行维护。

8.索引库得维护-添加

同初始索引库相同,只不过这只是单独一个文件而已。

9.索引库删除索引

1.删除全部索引

 @Test
    public void deleteAllIndex() throws IOException {
    
    
        IndexWriter writer = new IndexWriter(FSDirectory.open(new File("D:\\luncene").toPath()),new IndexWriterConfig(new IKAnalyzer()));
        //删除所有的索引,此方法慎用,一旦删除,无法恢复!!!
        writer.deleteAll();
        writer.close();
    }

2.根据Query删除索引

@Test
    public void deleteByQuery() throws IOException {
    
    
        IndexWriter writer = new IndexWriter(FSDirectory.open(new File("D:\\luncene").toPath()),new IndexWriterConfig(new IKAnalyzer()));
        //声明Query
        Query query = new TermQuery(new Term("name","web"));
        //根据query条件删除document
        writer.deleteDocuments(query);
        writer.close();
    }

10.更新索引库

在这里的更新其实就是先删除索引,在添加索引。

11.查看索引

1.Query:
TermQuery:关键词查询

  不演示,同上。


RangeQuery:范围查询

@Test
    public void selectQuery() throws IOException {
    
    
        //Query有两个子类:
        //1.TermQuery:关键词查询,需要提供查询的域和要查询的关键词,不演示
        //2.RangeQuery:范围查询,一般用于整数
        IndexReader indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\\luncene").toPath()));
        //获取查询对象
        IndexSearcher searcher = new IndexSearcher(indexReader);
        //获取查询条件LongPoint:这个要看你存储的时候,使用的是什么类型的,如果是int,就替换为intPoint
        Query query = LongPoint.newRangeQuery("size",10l,100l);
        TopDocs topDocs = searcher.search(query, 10);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
    
    
            System.out.println(searcher.doc(scoreDoc.doc).get("name"));
        }
        indexReader.close();

    }

2.queryparser:先把查询条件分词,在根据分词结构查询
需要再导入一个jar包:
在这里插入图片描述
测试:

 @Test
    public void QueryParserTest() throws IOException, ParseException {
    
    
        IndexReader indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\\luncene").toPath()));
        IndexSearcher searcher = new IndexSearcher(indexReader);
        //创建QueryParser对象  参数1:默认的搜索域,当没有提供搜索域的时候,去这个域搜索,参数2:使用的分析器
        QueryParser queryParser = new QueryParser("name",new IKAnalyzer());
        //设置查询条件
        Query query = queryParser.parse("Lucene是java开发的");
        //查询
        TopDocs topDocs = searcher.search(query, 10);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
    
    
            Document doc = searcher.doc(scoreDoc.doc);
            System.out.println(doc.get("name"));
        }
        indexReader.close();
    }

搜索结果就为:
先把你的查询条件分词,在根据分完词之后的每个词去查询,只要查询到一个相关的,就会查询出来。









Lucene初步使用结束,以后项目大多使用基于lucene开发的框架:es、solr等。

猜你喜欢

转载自blog.csdn.net/weixin_43431123/article/details/112480828
今日推荐