SSM整合Lucene实现全文检索

1.Lucene概述

Lucene是一款使用Java语言编写的全文检索框架

Lucene是简单而功能强大的基于Java的搜索库。它可以用于任何应用程序来搜索功能。 Lucene是开源项目。它是可扩展的,高性能的库用于索引和搜索几乎任何类型的文本。 Lucene库提供了所需的任何搜索应用程序的核心业务。索引和搜索。

搜索应用程序的工作原理

  • 任何搜索应用程序执行一些或全部下列操作:
步骤标题描述
1获取原始内容任何搜索应用程序的第一个步骤是收集在其上的搜索是要进行的目标内容
2构建文档下一步是建立从原始内容的搜索应用程序可以理解和容易理解的文件
3分析文档在索引过程启动,该文件是要分析作为其文本部分是一个候选索引。这个过程被称为分析文档
4索引文件一旦文档被构建和分析,下一步是将索引它们使得该文件可被检索

Lucene基本组件

组件描述
IndexWriter

此类充当创造/在索引过程中更新指标的核心组成部分

Directory

此类表示索引的存储位置。子类:FSDirectory,RAMDirectory

Document

Document代表一个虚拟文档与字段,其中字段是可包含在物理文档的内容,元数据等对象。Analyzer只能理解文档。

Field

Field是最低单元或索引过程的起点。它代表其中一个键被用于识别要被索引的值的键值对关系。用于表示一个文件内容的字段将具有键为“内容”,值可以包含文本或文档的数字内容的部分或全部。 Lucene能索引仅文本或仅数字内容。子类:StringField,BinaryDocValuesField, DoubleField, FloatField, IntField, LongField等

IndexSearcher

这个类充当读取/搜索索引的过程后创建索引的核心组成部分。它需要目录实例指向包含索引的位置

Term

这个类是搜索的最低单位。它是在索引过程中类似字段 Field

Query

Query是一个抽象类,包含各种实用方法,所有类型查询的父在Lucene的搜索过程中使用

TopDocs

TopDocs指向相匹配的搜索条件的前N个搜索结果。它是指针的简单容器指向它们的搜索结果输出的文档。

Analyzer

Analyzer类负责分析一个文件,并从将被索引的文本获取令牌/字。不加分析完成后,IndexWriter不能创建索引。

2.配置文件applicationContext-Lucene.xml的编写

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd">

   <!-- IKAnalyzer 分词器-->
   <bean id="luceneAnalyzer" class="org.wltea.analyzer.lucene.IKAnalyzer"/>

   <!-- SimpleFSDirectory 硬盘索引库目录 -->
   <bean id="path" class="java.io.File">
      <constructor-arg value="D:/data/Lucene"/>
   </bean>
   <bean id="luceneDirectory" class="org.apache.lucene.store.SimpleFSDirectory" >
      <constructor-arg name="path" ref="path"/>
   </bean>

   <!-- IndexWriterConfig 用于构建IndexWriter-->
   <bean id="indexWriterConfig" class="org.apache.lucene.index.IndexWriterConfig">
      <constructor-arg name="matchVersion" value="LUCENE_44"/>
      <constructor-arg name="analyzer" ref="luceneAnalyzer"/>
   </bean>

   <!--IndexWriter 用于进行索引写操作-->
   <bean id="indexWriter" class="org.apache.lucene.index.IndexWriter" >
      <constructor-arg ref="luceneDirectory" />
      <constructor-arg ref="indexWriterConfig" />
   </bean>
</beans>

3.Lucene的各种操作的使用示例

(1)工具类LuceneUtil

注 :Spring管理的组件中无需使用,可直采用注解方式自动注入
public class LuceneUtil {
    private static Directory directory_sp = null;

    private static IndexWriterConfig config = null;

    private static Version matchVersion = null;

    private static Analyzer analyzer = null;

    private static Directory ramDirectory = null;

    /**单例**/
    private static IndexWriter indexWriter;


    static {
        try {
            directory_sp = FSDirectory.open(new File(LuceneConstant.INDEXURL_ALL));
            matchVersion = Version.LUCENE_44;
            analyzer = new IKAnalyzer();
            config = new IndexWriterConfig(matchVersion, analyzer);
            System.out.println("directory_sp    " + directory_sp);
            //创建内存索引库
            ramDirectory = new RAMDirectory(directory_sp, null);
            //创建indexWriter对象
            indexWriter=new IndexWriter(directory_sp, config);
            /**当当前线程结束时,自动关闭IndexWriter,使用Runtime对象*/
            Runtime.getRuntime().addShutdownHook(new Thread(){
                @Override
                public void run() {
                    try {
                        indexWriter.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*
     * 返回用于操作索引的对象
     * */
    public static IndexWriter getIndexWriter(){
        return indexWriter;
    }


    /*
     * 返回用于读取索引的对象
     * */
    public static IndexSearcher getIndexSearcher() throws IOException {

        IndexReader indexReader = DirectoryReader.open(ramDirectory);

        IndexSearcher indexSearcher = new IndexSearcher(indexReader);

        return indexSearcher;
    }

    /*
     * 获取lucene当前的版本
     * */
    public static Version getMatchVersion() {
        return matchVersion;
    }

    /*
     * 获取分词器
     * */
    public static Analyzer getAnalyzer() {
        return analyzer;
    }

}

(2)获取IndexWriter实例

FSDirectory directory= FSDirectory.open(new File("D:/data/lucene_test/test003"));
IKAnalyzer ikAnalyzer=new IKAnalyzer();
IndexWriterConfig config=new IndexWriterConfig(Version.LUCENE_44,ikAnalyzer);
RAMDirectory ramDirectory=new RAMDirectory(directory,null);

IndexWriter writer=new IndexWriter(directory,config);

(3)向索引库中添加数据

IndexWriter indexWriter = LuceneUtil.getIndexWriter();

//(1)建立文档
Document document = new Document();
//(2)建立字段
StringField idfield = new StringField("id", "4", Field.Store.YES);
TextField goodNamefield = new TextField("goodName", "商品4",Field.Store.YES);
//(3)添加字段到文档
document.add(idfield);
document.add(goodNamefield);
//(4)添加文档到索引库
indexWriter.addDocument(document);

indexWriter.commit();
indexWriter.close();

(4)从索引库中检索数据

Lucene的查询方式很 丰富,对于数值类型的数据,采取TermRangeQuery的方式,对于String类型的,就可以采取TermQuery等,查询方式了,可以通过采取合适的查询方式,检索到数据。Queryparser这个查询方式包含了其他几种查询方式。

查询方式意义
TermQuery精确查询
TermRangeQuery查询一个范围
PrefixQuery前缀匹配查询
WildcardQuery通配符查询
BooleanQuery多条件查询
PhraseQuery短语查询
FuzzyQuery模糊查询
NumericRangeQuery数值范围查询
Queryparser万能查询(上面的都可以用这个来查询到)

1.简单检索

    //(1)获取IndexReader对象
    IndexReader indexReader=DirectoryReader.open(FSDirectory.open(new File(LuceneConstant.INDEXURL_ALL)));
    //(2)获取IndexSearcher对象
    IndexSearcher indexSearcher=new IndexSearcher(indexReader);
    //(3)创建Query对象
    QueryParser queryParser=new QueryParser(LuceneUtil.getMatchVersion(),"goodName",LuceneUtil.getAnalyzer());
    //(4)封装查询条件
    Query query=queryParser.parse("商品");
    //(5)使用IndexSearcher对象进行检索,得到结果文档
    TopDocs topDocs = indexSearcher.search(query, 10);
    //(6)进行结果处理
    for (ScoreDoc scoreDoc:topDocs.scoreDocs){
        Document doc = indexSearcher.doc(scoreDoc.doc);
        System.out.println(doc.get("id")+doc.get("goodName"));
    }

2.通配符检索(WildcardQuery)

    //(1)获取IndexSearcher对象
    IndexSearcher indexSearcher = LuceneUtil.getIndexSearcher();
    //(2)建立Quary对象,获取查询条件
    String name="ORF全长";
    Query query=new WildcardQuery(new Term("name","*"+name+"*"));
    long start=System.currentTimeMillis();
    //(3)进行结果集处理
    TopDocs topDocs = indexSearcher.search(query, 200);
    int num=1;
    for (ScoreDoc sdoc:topDocs.scoreDocs){
        Document doc = indexSearcher.doc(sdoc.doc);
        Globalkit globalkit=new Globalkit();
        globalkit.setId(doc.get("id"));
        globalkit.setName(doc.get("name"));
        globalkit.setCompany(doc.get("company"));
        globalkit.setAlias(doc.get("alias"));
        globalkit.setNumber(doc.get("number"));
        globalkit.setPrice(doc.get("price"));
        System.out.println(globalkit+"--第"+num);
        num++;
    }
    long end=System.currentTimeMillis();
    System.out.println("檢索時間:"+(end-start)+"ms");
}

3.多条件查询(BooleanQuery,WildcardQuery,NumericRangeQuery)

    //(1)创建IndexSearcher实例
    IndexSearcher indexSearcher=LuceneUtil.getIndexSearcherOfSP();
    String name="ORF全长";
    //(2)创建booleanQuery实例
    BooleanQuery booleanQuery=new BooleanQuery();

    //(3)创建Query对象封装条件
    WildcardQuery query1=new WildcardQuery(new Term("name","*"+name+"*"));
    NumericRangeQuery query2=NumericRangeQuery.newDoubleRange("price",0.0,1000.0,true,true);
    booleanQuery.add(query1,BooleanClause.Occur.MUST);
    booleanQuery.add(query2,BooleanClause.Occur.MUST);

    //(4)检索
    long start=System.currentTimeMillis();
    TopDocs topDocs = indexSearcher.search(booleanQuery, 200);
    //int num=1;
    for (ScoreDoc sdoc:topDocs.scoreDocs){
        Document doc = indexSearcher.doc(sdoc.doc);
        Globalkit globalkit=new Globalkit();
        globalkit.setId(doc.get("id"));
        globalkit.setName(doc.get("name"));
        globalkit.setCompany(doc.get("company"));
        globalkit.setAlias(doc.get("alias"));
        globalkit.setNumber(doc.get("number"));
        globalkit.setPrice(doc.get("price"));
        //System.out.println(globalkit+"--第"+num);
        //num++;
    }
    long end=System.currentTimeMillis();
    System.out.println("檢索時間:"+(end-start)+"ms");

4.查询结果设置高亮(使用IK分词器只能对中文进行分词、高亮)

注意:

  • 使用中文分词器无法对英文加高亮
  • 使用英文分词器无法对中文加高亮
  • 中英混合无法高亮

    //(1)创建IndexSearcher实例
    IndexSearcher indexSearcher=LuceneUtil.getIndexSearcherOfSP();
    String name="基因";
    //(2)创建booleanQuery实例
    BooleanQuery booleanQuery=new BooleanQuery();
    
    //(3)创建Query对象封装条件
    WildcardQuery query1=new WildcardQuery(new Term("name","*"+name+"*"));
    NumericRangeQuery query2=NumericRangeQuery.newDoubleRange("price",0.0,1000.0,true,true);
    booleanQuery.add(query1,BooleanClause.Occur.MUST);
    booleanQuery.add(query2,BooleanClause.Occur.MUST);
    
    //(4)检索
    long start=System.currentTimeMillis();
    TopDocs topDocs = indexSearcher.search(booleanQuery, 200);
    
    //高亮部分
    //1.创建QueryScorer对象
    QueryScorer scorer=new QueryScorer(booleanQuery);
    //2.创建Formatter对象(设置高亮前缀、后缀)
    SimpleHTMLFormatter formatter=new SimpleHTMLFormatter("<font color='red'>","</font>");
    //3.创建fragmenter对象
    Fragmenter fragmenter=new SimpleSpanFragmenter(scorer);
    //4.创建Highlighter对象(设置scorer、formatter和 fragmenter)
    Highlighter highlighter=new Highlighter(formatter,scorer);
    highlighter.setTextFragmenter(fragmenter);
    
    int num=1;
    for (ScoreDoc sdoc:topDocs.scoreDocs){
        Document doc = indexSearcher.doc(sdoc.doc);
        //5.获取高亮的结果(highlighter.getBestFragment(tokenStream, value))
        String value = doc.get("name");
        TokenStream tokenStream = LuceneUtil.getAnalyzer().tokenStream("name", new StringReader(value));
        String result = highlighter.getBestFragment(tokenStream, value);
        System.out.println(result+"--第"+num);
        num++;
    }
    long end=System.currentTimeMillis();
    System.out.println("檢索時間:"+(end-start)+"ms");
    

4.Lucene中常用的分词器

分词器特点
WhitespaceAnalyzer以空格作为切词标准,不对语汇单元进行其他规范化处理。很明显这个实用英文,单词之间用空格。
SimpleAnalyzer以非字母符来分割文本信息,并将语汇单元统一为小写形式,并去掉数字类型的字符。很明显不适用于中文环境。
StopAnalyzer停顿词分析器会去除一些常有a,the,an等等,也可以自定义禁用词,不适用于中文环境
StandardAnalyzer标准分析器是Lucene内置的分析器,会将语汇单元转成小写形式,并去除停用词及标点符号,很明显也是不适合于中文环境
CJKAnalyzer中日韩分析器,能对中,日,韩语言进行分析的分词器,但是对中文支持效果一般,一般不用
SmartChineseAnalyzer对中文支持稍好,但扩展性差,扩展词库,禁用词库和同义词库等不好处理
IKAnalyzer

采用了多子处理器分析模式,支持:英文字母(IP地址、Email、URL)、数字(日期,常用中文数量词,罗马数字,科学计数法),中文词汇(姓名、地名处理)等分词处理。

对中英联合支持不是很好,在这方面的处理比较麻烦.需再做一次查询,同时是支持个人词条的优化的词典存储,更小的内存占用。 支持用户词典扩展定义。

针对Lucene全文检索优化的查询分析器IKQueryParser;采用歧义分析算法优化查询关键字的搜索排列组合,能极大的提高Lucene检索的命中率。


结束


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值