Lucene

Lucene 基础教程

简介

Lucene是什么?
 Lucene是一个全文搜索框架,提供了一种可以实现类似于Google、Baidu搜索引擎功能的工具,但并非应用产品。
Lucene原理
 lucene提供的服务实际包含两部分:
 写入:将你提供的源(本质是字符串)写入索引或者将其从索引中删除;
 读出:即向用户提供全文搜索服务,让用户可以通过关键词定位源。
写入详解
源字符串首先经过analyzer(分词器)处理,分成一个个词语。
将源中需要的信息加入Document的各个Field中,并把需要索引的Field索引起来,把需要存储的Field存储起来。
将索引写入存储器存储器可以是内存或磁盘。
读出详解
用户提供搜索关键词,经过analyzer分词处理。
对处理后的关键词搜索索引找出对应的Document域。
用户根据需要从找到的Document域中提取需要的文段。
构建索引简单的代码片段
    IndexWriter writer = new IndexWriter(“/data/index/”, new StandardAnalyzer(), true);
    Document doc = new Document();
    doc.add(new Field("title", "lucene introduction", Field.Store.YES, Field.Index.TOKENIZED));
    doc.add(new Field("content", "lucene works well", Field.Store.YES, Field.Index.TOKENIZED));
    writer.addDocument(doc);
    writer.optimize();
    writer.close();
代码分析
1.创建一个writer,指定存放索引目录为“/data/index”
2.使用的分析器为StandardAnalyzer,
3.我们新建一个document,覆盖索引目录已存在的索引文件(第三个参数)
4.向document添加名为“title"的field,内容是“lucene introduction”,存储并索引。
5.添加名为“content”的field,内容是“lucene works well”,存储并索引。
6.将此文档添加到索引中,如有多个文档,重复以上操作,创建document并添加。
7.添加完所有document,对索引进行优化,将多个segment合并,提高索引速度。
8.r关闭writer(重要)。
如果你想把纯文本文件索引起来,而不想自己将它们读入字符串创建field,你可以用下面的代码创建field:
Field field = new Field("content", new FileReader(file));
这里的file就是该文本文件。该构造函数实际上是读去文件内容,并对其进行索引,但不存储。

实现简单的索引

在根目录下建一个名为data的文件夹,文件夹里面建两个个txt文件:"1.txt","2.txt"
其中1.txt的内容如下:
Lucene是开源项目。
它可以用于任何应用程序来搜索功能。
Lucene是简单而功能强大的基于Java的搜索库。
"2.txt"内容可以随便写。
这里我们给2.txt的内容为:
索引和搜索。
它是可扩展的,高性能的库用于索引和搜索几乎任何类型的文本。
Lucene库提供了所需的任何搜索应用程序的核心业务。
索引和搜索。
建立索引
package com.goson.lucene;

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.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

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

/**
 * Created by dllo on 17/12/26.
 */
public class Index {
    public void index() {
         // IndexWriter类充当创造或者在索引过程中更新指标的核心组成部分
        IndexWriter indexWriter = null;
        try {
            // 创建索引的目录对象  (Directory类 是一个静态类,常用的地方为创建目录和目录管理。)
            Directory directory = FSDirectory.open(FileSystems.getDefault().getPath(".../Lucene/index"));           //getPath中填写目录路径

            // 创建分词器 (//主流分词器   1:IK   2.HanLP )
            // 作用:是把一个字符串按某种规则划分成一个个词语,并去除其中的无效词语,
            // 这里说的无效词语是指英文中的“of”、“the”,中文中的“的”、“地”等词语,
            // 这些词语在文章中大量出现,但是本身不包含什么关键信息,去掉有利于缩小索引文件、提高效率、提高命中率。
            Analyzer analyzer = new StandardAnalyzer();

            //创建page
            IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);

            // 创建索引的写入对象
            // IndexWriter类充当创造或者在索引过程中更新指标的核心组成部分
            indexWriter = new IndexWriter(directory, indexWriterConfig);

            // 删除之前的索引
            indexWriter.deleteAll();

            // data文件夹是数据来源
            // index文件夹是索引的结果
            File dataFile = new File(".../Lucene/data");   //填写源信息读取路径

            //获取data文件夹下所有的文件
            File[] fileList = dataFile.listFiles();
            for (File file : fileList) {

                //每一个文件对应一个document
                // 用户提供的源是一条条记录,它们可以是文本文件、字符串或者数据库表的一条记录等等。一条记录经过索引之后,就是以一个Document的形式存储在索引文件中的。用户进行搜索,也是以Document列表的形式返回。
                Document document = new Document();

                //给Document添加索引内容
                 /**
                 *  field
                 一个Document可以包含多个信息域,例如一篇文章可以包含“标题”、“正文”、“最后修改时间”等信息域,这些信息域就是通过Field在Document中存储的。
                 Field有两个属性可选:存储和索引。通过存储属性你可以控制是否对这个Field进行存储;通过索引属性你可以控制是否对该Field进行索引。这看起来似乎有些废话,事实上对这两个属性的正确组合很重要,下面举例说明:还是以刚才的文章为例子,我们需要对标题和正文进行全文搜索,所以我们要把索引属性设置为真,同时我们希望能直接从搜索结果中提取文章标题,所以我们把标题域的存储属性设置为真,但是由于正文域太大了,我们为了缩小索引文件大小,将正文域的存储属性设置为假,当需要时再直接读取文件;我们只是希望能从搜索解果中提取最后修改时间,不需要对它进行搜索,所以我们把最后修改时间域的存储属性设置为真,索引属性设置为假。上面的三个域涵盖了两个属性的三种组合,还有一种全为假的没有用到,事实上Field不允许你那么设置,因为既不存储又不索引的域是没有意义的。
                 */

                document.add(new Field("content", FileUtils.readFileToString(file, "utf-8"), TextField.TYPE_STORED));
                document.add(new Field("fileName", file.getName(), TextField.TYPE_STORED));

                //indexWriter写入的都是document
                indexWriter.addDocument(document);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (indexWriter != null) {
                try {
                    indexWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}
查询
package com.goson.lucene;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

import java.io.IOException;
import java.io.StringReader;
import java.nio.file.FileSystems;

/**
 * Created by dllo on 17/12/26.
 */
public class Search {

    public void search(String keyword) {

        try {
            //获取索引文件
            Directory directory = FSDirectory.open(FileSystems.getDefault().getPath(".../Lucene/index"));           //检索内容存储路径

            //创建索引文件的读取器
            DirectoryReader reader = DirectoryReader.open(directory);

            //创建检索对象
            IndexSearcher indexSearcher = new IndexSearcher(reader);

            //下面开始对用户输入的关键词做处理

            //创建分词器
            Analyzer analyzer = new StandardAnalyzer();

            //创建查询解析器
            QueryParser queryParser = new QueryParser("content", analyzer);
            //对keyword进行解析
            Query query = queryParser.parse(keyword);

            //检索  并生成结果集  参数一: 检索的关键词  参数2: 显示的条数
            TopDocs search = indexSearcher.search(query, 2);

            //高亮的评分
            QueryScorer scorer = new QueryScorer(query);

            //将原始的字符串拆分成独立的片段
            Fragmenter fragmenter = new SimpleSpanFragmenter(scorer);

            //创建html高亮标签
            SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<font color='red'","</font>");

            //高亮分析器
            Highlighter highlighter = new Highlighter(simpleHTMLFormatter,scorer);
            highlighter.setTextFragmenter(fragmenter);

            for(ScoreDoc scoreDoc : search.scoreDocs){
                Document document = indexSearcher.doc(scoreDoc.doc);
                String content = document.get("content");
                if(content !=null){

                    // tokenStream是分词器做好处理之后得到的一个流
                    // 这个流里面储存了粉刺的各种信息
                    // 可以通过tokenStream有效的获取分词单元
                    TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(content));
                    String hContent = highlighter.getBestFragment(tokenStream, content);
                    System.out.println(hContent);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (InvalidTokenOffsetsException e) {
            e.printStackTrace();
        }
    }
}
测试并在控制台显示
package com.goson.lucene;
import org.junit.Test;

/**
 * Created by dllo on 17/12/26.
 */
public class MainTest {

    @Test
    public void test1() {
        Index index = new Index();
        index.index();
        Search search = new Search();
        search.search("扩展搜索");
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值