分享

Lucene全文检索

 太极混元天尊 2018-05-18



全文检索是程序开发中非常重要的一个应用,今天带大家来一起学习Java基于Lucene的全文检索机制。


全文检索的概念


1) 从大量的信息中快速、准确地查找出要的信息。

2) 搜索的内容是文本信息(不是多媒体)。

3) 搜索的方式:不是根据语句的意思进行处理。如果要搜索的文本为'西安',那么含有这些词(西安程序员、西安)就能搜索出来。每一个词都是关键词。

4) 全面、快速、准确是衡量全文检索系统的关键指标。

5) 概括:

a) 只处理文本。

b) 不处理语义。

a) 搜索时英文不区分大小写。

b) 结果列表有相关度排序。


全文检索的应用场景


1.站内搜索

通常用于在大量数据出现的系统中,找出你想要的资料。

bbs的关键字搜索:如百度贴吧。

商品网站的搜索:如淘宝、京东、中关村在线等。

文件管理系统:对文件的搜索功能。Window的文件搜索等。


2. 垂直搜索

a) 是针对 某个行业的搜索引擎。

b) 是搜索引擎的细分和延伸。

c) 是针对网页库中的专门信息的整合。

d) 其特点是专、深、精,并具有行业色彩。

e) 可以应用于购物搜索、房产搜索、人才搜索。


全文检索与数据库搜索的区别


1. 数据库的搜索

类似:select * from  表名 where 字段名 like ‘%关键字%’。

例如:select * from article where content like’%here%’。

结果:  where   here  shere。

缺点:

1) 搜索效果比较差。

2) 在搜索的结果中,有大量的数据被搜索出来,有很多数据是没有用的。

3) 查询速度在大量数据的情况下是很难做到快速的。


2. 全文检索

1) 搜索结果按相关度排序:意味着只有前几个页面对于用户来说是比较有用的,其他的结果与用户想要的答案很可能相差甚远。数据库搜索是做不到相关度排序的。

2) 因为全文检索是采用引索的方式,所以在速度上肯定比数据库方式like要快。

3) 所以数据库不能代替全文检索。


全文检索只是一个概念,而具体实现有很多框架,Lucene是其中的一种。Lucene的主页http://lucene./。本文用的是3.0.1版本。


互联网搜索结构框图



1) 当用户打开www.baidu.com网页搜索某些数据的时候,不是直接找的网页,而是找的百度的索引库。索引库里包含的内容有索引号和摘要。当我们打开www.baidu.com时,看到的就是摘要的内容。

2) 百度的索引库的索引和互联网的某一个网站对应。

3) 当用户数据要查询的关键字,返回的页面首先是从索引库中得到的。

4) 点击每一个搜索出来的内容进行相关网页查找,这个时候才找的是互联网中的网页。


Lucene的大致结构框图



说明:

1) 在数据库中,数据库中的数据文件存储在磁盘上。索引库也是同样,索引库中的索引数据也在磁盘上存在,我们用Directory这个类来描述。

2) 我们可以通过API来实现对索引库的增、删、改、查的操作。

3) 在数据库中,各种数据形式都可以概括为一种:表。在索引库中,各种数据形式也可以抽象出一种数据格式为Document。

4) Document的结构为:Document(List)

5) Field里存放一个键值对。键值对都为字符串的形式。

6) 对索引库中索引的操作实际上也就是对Document的操作。


在搜索引擎中我们经常会看到这样的情景: 



红色部分的Java称之为高亮显示,Lucene提供了HighLighter模块来实现这一功能。 


接下来我们就使用Lucene来模拟实现这一功能,在新闻信息中查找'西安'关键字,并自动生成高亮显示的html代码。


1.搭建Lucene环境,pom.xml:


project xmlns='http://maven./POM/4.0.0' xmlns:xsi='http://www./2001/XMLSchema-instance'
 xsi:schemaLocation='http://maven./POM/4.0.0 http://maven./maven-v4_0_0.xsd'>

 modelVersion>4.0.0modelVersion>
 groupId>com.southwindgroupId>
 artifactId>luceneartifactId>
 packaging>warpackaging>
 version>0.0.1-SNAPSHOTversion>
 name>lucene Maven Webappname>
 url>http://maven.url>
 dependencies>
   dependency>
       groupId>org.apache.lucenegroupId>
       artifactId>lucene-coreartifactId>
       version>4.7.2version>
   dependency>

   dependency>
   groupId>org.apache.lucenegroupId>
       artifactId>lucene-queryparserartifactId>
       version>4.7.2version>
   dependency>

   dependency>
       groupId>org.apache.lucenegroupId>
       artifactId>lucene-analyzers-commonartifactId>
       version>4.7.2version>
   dependency>

   dependency>
       groupId>org.apache.lucenegroupId>
       artifactId>lucene-analyzers-smartcnartifactId>
       version>4.7.2version>
   dependency>

   dependency>
       groupId>org.apache.lucenegroupId>
       artifactId>lucene-highlighterartifactId>
       version>4.7.2version>
   dependency>

   dependency>
   groupId>com.janeluogroupId>
   artifactId>ikanalyzerartifactId>
   version>2012_u6version>
   dependency>

   dependency>
       groupId>junitgroupId>
       artifactId>junitartifactId>
       version>4.12version>
   dependency>
 dependencies>
 build>
   finalName>lucenefinalName>
 build>
project>


2.创建News对象,保存新闻信息。


package com.southwind.entity;

import java.util.ArrayList;
import java.util.List;

/**
* 新闻
* @author southwind
*
*/

public class News {
   private Integer id;
   private String title;
   private String content;
   private String author;

   public Integer getId() {
       return id;
   }
   public void setId(Integer id) {
       this.id = id;
   }
   public String getTitle() {
       return title;
   }
   public void setTitle(String title) {
       this.title = title;
   }
   public String getContent() {
       return content;
   }
   public void setContent(String content) {
       this.content = content;
   }
   public String getAuthor() {
       return author;
   }
   public void setAuthor(String author) {
       this.author = author;
   }
}


3.创建三个新闻对象,保存到List中,建立索引。

 

步骤:

1) 创建IndexWriter对象。

2) 把File转化为Document。

3) 利用IndexWriter.addDocument方法增加索引。

4) 关闭资源。



package com.southwind.test;

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

import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

import com.southwind.entity.News;

/**
* 创建索引
*
* @author southwind
*
*/

public class Index {

   private IndexWriter indexWriter;

   private Directory dir;

   public Index() throws Exception {
       File file = new File('d:/lucene');
       if (!file.exists())
           file.mkdirs();
       dir = FSDirectory.open(file);
       SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer(Version.LUCENE_47);
       IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_47, analyzer);
       indexWriter = new IndexWriter(dir, config);
       indexWriter.deleteAll();
       System.out.println('已经建立的索引的个数:' + indexWriter.maxDoc());
       System.out.println('已经建立的索引的个数:' + indexWriter.numDocs());
   }

   /**
    * 建立索引
    *
    * @throws Exception
    */

   public void createIndex(List list) throws Exception {
       for (News n : list) {
           Document doc = new Document();
           doc.add(new IntField('id', n.getId(), Store.YES));
           doc.add(new StringField('title', n.getTitle(), Store.YES));
           doc.add(new TextField('content', n.getContent(), Store.YES));
           indexWriter.addDocument(doc);
       }
       System.out.println('已经建立的索引的个数:' + indexWriter.maxDoc());
       System.out.println('已经建立的索引的个数:' + indexWriter.numDocs());
   }

   /**
    * 关闭索引
    *
    * @throws Excpetion
    */

   public void close() throws Exception {
       indexWriter.close();
   }

   public static void main(String[] args) {
       List list = new ArrayList();
       News n1 = new News();
       n1.setId(1);
       n1.setTitle('西安IT');
       n1.setContent('西北总部落户西安 阿里巴巴与西安政府达成战略合作。');
       n1.setAuthor('南风');
       list.add(n1);

       News n2 = new News();
       n2.setId(2);
       n2.setTitle('Lucene高亮显示');
       n2.setContent('红色部分我们称之为高亮显示,lucene提供了HighLighter模块来实现这一功能。 ');
       n2.setAuthor('南风');
       list.add(n2);

       News n3 = new News();
       n3.setId(3);
       n3.setTitle('全文检索');
       n3.setContent('全文数据库是全文检索系统的主要构成部分。所谓全文数据库是将一个完整的信息源的全部内容转化为计算机可以识别、处理的信息单元而形成的数据集合。');
       n3.setAuthor('南风');
       list.add(n3);
       Index i = null;
       try {
           i = new Index();
           i.createIndex(list);
           i.indexWriter.close();

       } catch (Exception e) {
           e.printStackTrace();
       } finally {
           if(i!=null)
               try {
                   i.close();
               } catch (Exception e) {
                   // TODO Auto-generated catch block
                   e.printStackTrace();
               }
       }
   }

}


4.执行main方法,在D:/lucene中创建索引文件。



5.在D:/lucene的文件中,以'西安'为关键字对文本信息进行检索,并将'西安'进行标注,生成高亮显示的html代码。


步骤:

1) 创建IndexSearch。

2) 创建Query对象。

3) 进行搜索。

4) 获得总结果数和前N行记录ID列表。

5) 根据目录ID列表把Document,,并输入document存放的File信息。



package com.southwind.test;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.StringReader;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
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.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

public class Search {

   /**
    * 按内容检索
    * @param dirPath 索引文件所在的目录
    * @param qstr 要查询的内容
    * @throws Exception
    */

   public static void search(String dirPath, String qstr) throws Exception {
       File file =new File(dirPath);
       Directory dir = FSDirectory.open(file);
       //读取索引文件
       IndexReader indexReader = DirectoryReader.open(dir);
       //建立索引对象
       IndexSearcher is = new IndexSearcher(indexReader);
       //创建中文分词器
       SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer(Version.LUCENE_47);
       //创建两个词表
       Term t1 = new Term('title', qstr);
       Term t2 = new Term('content', qstr);
       //创建两个TermQuery
       TermQuery q1 =new TermQuery(t1);
       TermQuery q2 =new TermQuery(t2);
       //创建BooleanQuery对象
       BooleanQuery query = new BooleanQuery();
       //将两个TermQuery加入到BooleanQuery的子句中去,且其关系均为必须满足
       query.add(q1,BooleanClause.Occur.MUST_NOT);
       query.add(q2,BooleanClause.Occur.MUST);
       long l = System.currentTimeMillis();
       //获取查询结果
       TopDocs hits = is.search(query, 3);
       long l2 = System.currentTimeMillis();
       System.out.println('共用时:' + (l2 - l) / 1000);
       //创建QueryScorer对象
       QueryScorer scorer = new QueryScorer(query);
       //设置高亮显示
       Fragmenter fragmenter = new SimpleSpanFragmenter(scorer);
       SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter('', '');
       Highlighter highlighter = new Highlighter(simpleHTMLFormatter, scorer);
       highlighter.setTextFragmenter(fragmenter);
       for (ScoreDoc scoreDoc : hits.scoreDocs) {
           Document doc = is.doc(scoreDoc.doc);
           String content = doc.get('content');
           if (content != null) {
               TokenStream tokenStream = analyzer.tokenStream('content', new StringReader(content));
               String str = '';
               str += highlighter.getBestFragment(tokenStream, content);
               str += '';
               System.out.println(str);
               //将设置完成的高亮代码写入html
               OutputStream os = new FileOutputStream('demo.html');
               byte[] bs = str.getBytes();
               os.write(bs);
               os.close();
           }
       }
       indexReader.close();
   }

   public static void main(String[] args) throws Exception {
       Search.search('d:/lucene', '西安');
   }

}


6.生成的html代码。


html>
head>
   title>demotitle>
head>
body>
   西北总部落户b>font color='red'>西安font>b>
   阿里巴巴与b>font color='red'>西安font>b>政府达成战略合作。
body>
html>



7.在浏览器中运行html代码,看到结果。




源码:


github

https://github.com/southwind9801/Lucene.git




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多