lucene全文检索之 中文分词 以及 高亮显示

这篇文章主要写一下中文分词以及高亮显示的案例

针对lucene的 增删改查我单独写了两篇文件,供参考,如下:

lucene全文检索多种查询检索简单案例,点击进入

lucene全文检索的 增、删、改 简单操作案例,点击进入

另外分享一个 lucene索引库查看器 ,点击进入

lucene官网 http://lucene.apache.org

关于中文分词及高亮显示还需要额外再引入两个maven依赖,
我先把maven依赖配置贴一下,如下:

 <dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>5.3.1</version>
</dependency>

<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-queryparser</artifactId>
    <version>5.3.1</version>
</dependency>

<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers-common</artifactId>
    <version>5.3.1</version>
</dependency>

<!-- 中文分词器  -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers-smartcn</artifactId>
    <version>5.3.1</version>
</dependency>

<!-- 高亮显示  -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-highlighter</artifactId>
    <version>5.3.1</version>
</dependency>

一、中文分词代码段

//创建中文分词器
SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();

二、高亮显示代段

/*
 * 创建 QueryScorer 实例
 * QueryScorer主要是对命中结果进行评分操作
 * 比如一篇很长的文章,通过关键次搜素后需要展示一部分片段摘要信息,
 * 这时候就需要这个评分器,对文中判断进行评分,最后将评分高的片段显示
 */
QueryScorer scorer=new QueryScorer(query);

/*
 * 创建 Fragmenter实例
 * Fragmenter 通过评分器将原始字符串拆分成独立的片段
 */
Fragmenter fragmenter=new SimpleSpanFragmenter(scorer);

/* 创建 SimpleHTMLFormatter 实例
 * 这是常用的格式化Html标签器,提供一个构造函数传入高亮颜色标签,默认使用黑色
 * 第一个参数为在关键词前面加的内容,第二个则是在关键词后面加内容的参数
 * 如  第一个参数为 <b><font color='red'> , 第二个参数为</font></b>
 * 假设关键词为 测试 那么输出后为 <b><font color='red'>测试</font></b>
 * 这样就可以自己设置需要的高亮样式
 */
SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("<b><font color='red'>","</font></b>");

/*
 * 创建 Highlighter实例
 * Highlighter主要负责获取匹配上的高亮片段
 */
Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer);
highlighter.setTextFragmenter(fragmenter);

/*
 * 创建 TokenStream实例
 * TokenStream主要提供静态方法,支持从数据源中获取TokenStream,进行token处理
 */
TokenStream tokenStream=analyzer.tokenStream("desc", new StringReader(centent));

//获取评分后的高亮片段,并重新复制给centent
centent = highlighter.getBestFragment(tokenStream, centent);

上面是我针对这两个功能单独写出来的代码段,方便比较
下面我把整个代码全部贴出来把,如下:

package luceneDemo;

import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
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.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.search.highlight.SimpleSpanFragmenter;
import org.apache.lucene.analysis.TokenStream;

/**
 * 全文检索案例
 * @author mszhou
 */
public class luceneDemo {

    /**
     * 将数据写入索引文件中(这里使用的是中文分词器)
     * 这里演示写入数据到索引中,在项目中我们会定时将数据写入索引文件中,
     * 然后在索引文件中进行搜索,这样比在数据库中搜索效率高的多,
     * 而且检索远比数据库的模糊匹配强大的多,这也是检索工具牛逼之处
     * 
     * 需要注意的是,如果在项目中定时写入索引,
     * 需要判断下该数据是否存在,存在则修改,不存在再进行新增
     * 
     * @param luceneDir 该参数表示索引所存放的目录
     */
    public static void writeIndex(String luceneDir){
        IndexWriter writer = null;//声明创建索引写入器 
        try{
            //创建索引目录 , 这里参数luceneDir 表示索引存放目录
            Directory dir=FSDirectory.open(Paths.get(luceneDir));

            //创建标准分词器(一般英文使用这个,不适合中文使用)改用下面的中文分词器
            //Analyzer analyzer=new StandardAnalyzer(); 

            //创建中文分词器(中文需要使用该分词器)
            SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();

            IndexWriterConfig iwc=new IndexWriterConfig(analyzer);

            //创建索引写入器实例,通过IndexWriterConfig设置相关配置
            writer=new IndexWriter(dir, iwc);


            //=== 这里造一些数据用作演示,也可以直接读取数据库中的数据 开始========
            List <Map<String,String>> list=new ArrayList<Map<String,String>>();

            //假数据1 
            Map<String,String> map1 =new HashMap<String, String>();
            map1.put("title", "小说标题的撒啊啊");//表示标题字段
            map1.put("centent", "这是小说内容,我就随便写了 受到收到的市场 是否是啊啥就卡 ");//表示标题字段
            list.add(map1);

            //假数据2 
            Map<String,String> map2 =new HashMap<String, String>();
            map2.put("title", "介绍小说");//表示标题字段
            map2.put("centent", "以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。人物、情节、环境是小说的三要素。情节一般包括开端、发展、高潮、结局四部分,有的包括序幕、尾声。环境包括自然环境和社会环境。 小说按照篇幅及容量可分为长篇、中篇、短篇和微型小说(小小说)。按照表现的内容可分为神话、科幻、公案、传奇、武侠、言情、同人、官宦等。按照体制可分为章回体小说、日记体小说、书信体小说、自传体小说。按照语言形式可分为文言小说和白话小说。");//表示标题字段    
            list.add(map2);

            //假数据1 
            Map<String,String> map3 =new HashMap<String, String>();
            map3.put("title", "demo 12i131s ss");//表示标题字段
            map3.put("centent", "hdaiads sd s g r44e r hgg f hd 4 ");//表示标题字段   
            list.add(map3);
            //=== 这里造一些数据用作演示,也可以直接读取数据库中的数据 结束========

            //遍历数据,并且将数据写入到索引中
            for(int i=0 ;i<list.size();i++){

                // ======   获取数据开始 ====== 
                Map<String,String> mapCen=list.get(i);
                String title =  mapCen.get("title").toString();//获取标题
                String centent = mapCen.get("centent").toString();//获取内容
                // ======   获取数据结束 ====== 

                //========= 将数据写入索引开始  =================
                //创建Document对象,Document表示lucene索引的数据结构单元
                Document doc=new Document();
                //添加id 数据,最后面Field.Store.YES表示存储该字段
                doc.add(new IntField("id", i,  Field.Store.YES));

                /*添加标题数据,最后面Field.Store.YES 表示存储该字段
                 *注意:如果需要检索该字段,建议使用TextField类型,并且设置为 Field.Store.YES
                 */
                doc.add(new TextField("title",title , Field.Store.YES));

                /*
                 * 添加标题内容数据,最后面Field.Store.YES 表示存储该字段
                 * 如果为 Field.Store.NO 表示不存储
                 */
                doc.add(new TextField("centent", centent, Field.Store.YES));

                // 写入索引
                writer.addDocument(doc); 
                //========= 将数据写入索引结束  =================
            }
        }catch(Exception e){
            System.out.println("发生了异常!");
            e.printStackTrace();
        }finally{
            try {
                writer.close();//关闭流,一定要记得关闭
            } catch (IOException e) {
            }
        }
    }

    /**
     * 检索匹配一下内容centent字段的数据,并高亮显示
     * (这里使用的是中文分词器)
     * @param luceneDir 该参数表示索引所存放的目录
     * @param titleStr 需要检索的关键词
     */
    public static void search(String luceneDir,String titleStr){

        IndexReader reader = null;//声明索引读取器
        try{
            //创建索引目录 , 这里参数luceneDir 表示索引存放目录
            Directory dir=FSDirectory.open(Paths.get(luceneDir));
            //创建索引读取器
            reader=DirectoryReader.open(dir);

            //创建索引搜索器
            IndexSearcher is=new IndexSearcher(reader);

            //创建标准分词器(一般英文使用这个,不适合中文使用)
            //Analyzer analyzer=new StandardAnalyzer();
            //创建中文分词器(中文需要使用该分词器),这里使用中文分词器
            SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer();

            //创建QueryParser查询器,查询title属性字段
            QueryParser parser=new QueryParser("title", analyzer);
            //设置查询关键词
            Query query=parser.parse(titleStr);

            //这里查询前10条匹配的数据,第二个参数为10表示查前十条
            TopDocs hits=is.search(query, 10);

            //打印信息
            System.out.println("匹配 "+titleStr+" , 查询到"+hits.totalHits+"个记录");

            //遍历查询到的数据
            for(ScoreDoc scoreDoc:hits.scoreDocs){
                Document doc=is.doc(scoreDoc.doc);
                String title  = doc.get("title").toString();//获取标题
                String centent  = doc.get("centent").toString();//获取内容
                //判断下为空的情况
                if(centent==null || "".equals(centent)){
                    return ;
                }
                //==============================================
                //===== 对centen字段数据搜素关键词高亮显示=====
                //==============================================
                /*
                 * 创建 QueryScorer 实例
                 * QueryScorer主要是对命中结果进行评分操作
                 * 比如一篇很长的文章,通过关键次搜素后需要展示一部分片段摘要信息,
                 * 这时候就需要这个评分器,对文中判断进行评分,最后将评分高的片段显示
                 */
                QueryScorer scorer=new QueryScorer(query);

                /*
                 * 创建 Fragmenter实例
                 * Fragmenter 通过评分器将原始字符串拆分成独立的片段
                 */
                Fragmenter fragmenter=new SimpleSpanFragmenter(scorer);

                /* 创建 SimpleHTMLFormatter 实例
                 * 这是常用的格式化Html标签器,提供一个构造函数传入高亮颜色标签,默认使用黑色
                 * 第一个参数为在关键词前面加的内容,第二个则是在关键词后面加内容的参数
                 * 如  第一个参数为 <b><font color='red'> , 第二个参数为</font></b>
                 * 假设关键词为 测试 那么输出后为 <b><font color='red'>测试</font></b>
                 * 这样就可以自己设置需要的高亮样式
                 */
                SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("<b><font color='red'>","</font></b>");

                /*
                 * 创建 Highlighter实例
                 * Highlighter主要负责获取匹配上的高亮片段
                 */
                Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer);
                highlighter.setTextFragmenter(fragmenter);

                /*
                 * 创建 TokenStream实例
                 * TokenStream主要提供静态方法,支持从数据源中获取TokenStream,进行token处理
                 */
                TokenStream tokenStream=analyzer.tokenStream("desc", new StringReader(centent));

                //获取评分后的高亮片段,并重新复制给centent
                centent = highlighter.getBestFragment(tokenStream, centent);

                System.out.println("title是:"+title+" ,centent是:"+centent);//输出
                //==============================================
                //==== 对centen字段数据搜素关键词高亮结束======
                //==============================================
            }
        }catch(Exception e){
            System.out.println("发生了异常!");
            e.printStackTrace();
        }finally{
            try {
                reader.close();//关闭流,一定要记得关闭
            } catch (IOException e) {
            }
        }
    }


    /**
     * 测试上面的方法
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {

        //表示索引目录
        String luceneDir =  "D://lucene";

        //测试该方法写入数据到索引文件中
        //writeIndex(luceneDir);

        //检索并高亮显示
        search(luceneDir,"小说");

    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值