zoukankan      html  css  js  c++  java
  • 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.lhc.lucenedao;
    
    import com.lhc.entity.Chapter;
    import com.lhc.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;
        }
    }
  • 相关阅读:
    SuperSocket框架中BinaryRequestInfo协议的使用
    UIImageView学习笔记
    UITextField学习小结
    Java数据结构相关类的实现原理
    Android 中把尺寸转换方法
    Win8 & WP8.1 轻型数据库
    隐私策略
    Windows 10(UWP)开发技巧
    【UWP】FFmpeg库的编译
    【UWP】拖拽列表项的排序功能实现
  • 原文地址:https://www.cnblogs.com/lhc-hhh/p/10234555.html
Copyright © 2011-2022 走看看