Lucene全文检索随笔

Posted lhc-hhh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lucene全文检索随笔相关的知识,希望对你有一定的参考价值。

一,什么是全文检索

全文检索是计算机程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置。当用户查询时根据建立的索引查找,类似于通过字典的检索字表查字的过程。

全文检索(Full-Text Retrieval)以文本作为检索对象,找出含有指定词汇的文本。全面、准确和快速是衡量全文检索系统的关键指标。

关于全文检索,我们要知道:

   1,只处理文本。

   2,不处理语义。

   3,搜索时英文不区分大小写。

   4,结果列表有相关度排序

 

二、 全文检索与数据库检索的区别

 

全文检索不同于数据库的SQL查询。(他们所解决的问题不一样,解决的方案也不一样,所以不应进行对比)。在数据库中的搜索就是使用SQL,如:SELECT * FROM t WHERE content like ‘%ant%’。这样会有如下问题:

 

1匹配效果:如搜索ant会搜索出planting。这样就会搜出很多无关的信息。

 

2相关度排序:查出的结果没有相关度排序,不知道我想要的结果在哪一页。我们在使用百度搜索时,一般不需要翻页,为什么?因为百度做了相关度排序:为每一条结果打一个分数,这条结果越符合搜索条件,得分就越高,叫做相关度得分,结果列表会按照这个分数由高到低排列,所以第1页的结果就是我们最想要的结果。

 

3全文检索的速度大大快于SQLlike搜索的速度。这是因为查询方式不同造成的,以查字典举例:数据库的like就是一页一页的翻,一行一行的找,而全文检索是先查目录,得到结果所在的页码,再直接翻到这一页。

 

 

 

三、 全文检索的使用场景

 

   我们使用Lucene,主要是做站内搜索,即对一个系统内的资源进行搜索。如BBS(论坛)BLOG(博客)中的文章搜索,网上商店中的商品搜索等。使用Lucene的项目有Eclipse,智联招聘,天,京东等。一般不做互联网中资源的搜索,因为不易获取与管理海量资源(专业搜索方向的公司除外)

四。Lucene创建索引

(1).maven 依赖

1   <dependency>
2       <groupId>org.apache.lucene</groupId>
3       <artifactId>lucene-core</artifactId>
4       <version>4.4.0</version>
5     </dependency>

(2)创建索引

 @Test
    public void create() throws IOException {
        //索引库
        Directory dir = FSDirectory.open(new File("e:/index"));
        Analyzer analyzer = new IKAnalyzer();//分词器,常用为IK分词器
        //索引写入器的相关配置
        IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_44, analyzer);
        //创建索引写入器
        IndexWriter indexWriter = new IndexWriter(dir, conf);
        //article---->document
        //field。store yes:保存元数据,通常不想显示详细信息时为no,入:商品详情(一般存入数据                     库),文章正文,需要查询数据库而不是查索引库
        for (int i=0;i<10;i++)
        {
            Document document = new Document();
            document.add(new StringField("id",     String.valueOf(i), Field.Store.YES));
            document.add(new StringField("title", "背影", Field.Store.YES));
            document.add(new StringField("author", "朱自清", Field.Store.YES));
            document.add(new TextField("content", "你站在这里不要动,我去给你买几个橘子百知教育", Field.Store.YES));
            document.add(new StringField("date", "2019-1-2", Field.Store.YES));
            indexWriter.addDocument(document);
        }
        indexWriter.commit();
        indexWriter.close();
    }

(3)搜索索引

 @Test
    public void search() throws IOException {
        Directory directory = FSDirectory.open(new File("e:/index"));
        IndexReader reader = DirectoryReader.open(directory);
        IndexSearcher indexSearcher = new IndexSearcher(reader);
        //第一个参数 搜索的条件   查询出的条数
        Query query=new TermQuery(new Term("content","橘子"));
        TopDocs topDocs = indexSearcher.search(query, 100); //相关度排序
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (int i = 0; i < scoreDocs.length; i++) {
            ScoreDoc scoreDoc = scoreDocs[i];
            int doc = scoreDoc.doc;
            Document document = indexSearcher.doc(doc);
            System.out.println("this is score"+scoreDoc.score);
            System.out.println("this is id"+document.get("id"));
            System.out.println("this is title"+document.get("title"));
            System.out.println("this is author"+document.get("author"));
            System.out.println("this is content"+document.get("content"));
            System.out.println("this is date"+document.get("date"));
        }
    }

(4)删除索引

 @Test
    public void test3() throws IOException {
        Directory dir = FSDirectory.open(new File("e:/index"));
        Analyzer analyzer = new IKAnalyzer();
        IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_44, analyzer);
        IndexWriter indexWriter = new IndexWriter(dir, conf);
//        indexWriter.deleteAll();
        indexWriter.deleteDocuments(new Term("id", "9"));
    }

(5)集成web环境

service层调用数据库mapper后,还应为商品创建索引,为了让用户更快的得到响应,可以先执行数据库操作,响应后再创建索引

注意:springboot环境中,配置类中可注入LuceneDao,service层可以直接@Autowired直接注入Dao。如下:

@Configuration
public class LuceneConfig {
    @Bean
    public LuceneProductDao getLuceneProductDao() {
        return new LuceneProductDao();
    }
    @Bean
    public LuceneChapterDao getLuceneChapterDao(){return new LuceneChapterDao();}

}

 

(6)多属性查询

依赖

 <!-- https://mvnrepository.com/artifact/com.janeluo/ikanalyzer -->
        <dependency>
            <groupId>com.janeluo</groupId>
            <artifactId>ikanalyzer</artifactId>
            <version>2012_u6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-queryparser -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-queryparser</artifactId>
            <version>4.4.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-highlighter -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-highlighter</artifactId>
            <version>4.4.0</version>
        </dependency>

在一个项目的章节模块中,前台输入查询内容,后台去索引库执行查询,并将关键字以高亮飘红形式展示在前台的搜索界面,可用以下实现

首先在添加章节时已经创建索引,才可查到

package com.baizhi.lucenedao;

import com.baizhi.entity.Chapter;
import com.baizhi.util.LuceneUtil;
import org.apache.lucene.document.*;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.search.highlight.Scorer;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class LuceneChapterDao {
    //创建索引,添加资源时创建
    public void createIndex(Chapter chapter) {
        IndexWriter indexWriter = LuceneUtil.getIndexWriter();
        Document docFromPro = getDocFromPro(chapter);
        try {
            indexWriter.addDocument(docFromPro);
            LuceneUtil.commit(indexWriter);
        } catch (IOException e) {
            e.printStackTrace();
            LuceneUtil.rollback(indexWriter);
        }
    }

    //搜索索引
    public List<Chapter> SearcherIndex(String params) {
        int pageSize = 2;//此处可从前台传入,不应写死
        int pageNum = 1;
        IndexSearcher indexSearcher = LuceneUtil.getIndexSearcher();
        List<Chapter> list = null;
        try {
            String[] strs = {"title"};
            //常用查询
            MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(Version.LUCENE_44, strs, new IKAnalyzer());
            Query query = multiFieldQueryParser.parse(params);

            Formatter formatter = new SimplehtmlFormatter("<font color=‘red‘>", "</font>");
            Scorer scorer = new QueryScorer(query);
            Highlighter highlighter = new Highlighter(formatter, scorer);

            TopDocs topDocs = indexSearcher.search(query, pageNum * pageSize);
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            list = new ArrayList<>();
            
            for (int i=(pageNum - 1) * pageSize; i < scoreDocs.length; i++) {
                ScoreDoc scoreDoc = scoreDocs[i];
                int doc = scoreDoc.doc;
                Document document = indexSearcher.doc(doc);
                //高亮开始
                try {
                    String bestFragment = highlighter.getBestFragment(new IKAnalyzer(), "title", document.get("title"));
                    if (bestFragment == null) {
                        System.out.println("this is  title8" + document.get("id"));
                    } else {
                        System.out.println("this is  title9" + bestFragment);

                        //Chapter chapter = getProFromDoc(document);
                        Chapter chapter = new Chapter();
                        chapter.setTitle(bestFragment);
                        System.out.println(chapter);
                        //存入list
                        list.add(chapter);
                    }
                  //  System.out.println("this is  id3" + highlighter.getBestFragment(new IKAnalyzer(), "content", document.get("content")));
                } catch (InvalidTokenOffsetsException e) {
                    e.printStackTrace();
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }catch (IOException ee) {
            ee.printStackTrace();
        }
        return list;
    }
    public Document getDocFromPro(Chapter chapter) {
        Document document = new Document();
        // document.add(new StringField("id", chapter.getId()+"", Field.Store.YES));
        document.add(new TextField("title", chapter.getTitle(), Field.Store.YES));
        return document;
    }
    public Chapter getProFromDoc(Document document) {
        Chapter chapter = new Chapter();
        // chapter.setId(document.get("id"));
        chapter.setTitle(document.get("title"));
        return chapter;
    }
}

 

以上是关于Lucene全文检索随笔的主要内容,如果未能解决你的问题,请参考以下文章

三搜索引擎篇-lucene入门代码示例

Lucene的全文检索学习

Lucene学习总结

Lucene就是这么简单

Lucene就是这么简单

Lucene原理与代码分析解读笔记