Lucene小结

一、全文检索概述

    1.常见的全文检索

        (1)window系统中的指定盘符中的某一个位置来搜索.
        (2)百度或google中,可以搜索互联网中的信息,有网页,pdf,word,音频,视频等内容.
        (3)在bbs系统中,有搜索文章的功能.
        上边的查询功能都相似,都是查询的文本内容,查询方法也相似即找出含有指定字符串的资源,只不过是查询的范围不一样(硬盘,帮助文档,互联网).

    2.全文检索的概念

        (1)从大量的信息中快速、准确地找出需要的信息.
        (2)搜索的内容是文本信息(不是多媒体)
        (3)搜索的方式:不是根据语句的意思进行处理.
        (4)全面、快速、准确是衡量全文检索系统的关键指标.
        (5)概括:
                * 只处理文本.
                * 不处理语义.
                * 搜索英文时不区分大小写.
                * 结果列表有相关度排序.

    3.全文检索的应用场景

        常用在有大量数据出现的系统中.
        * 站内搜索
            (1)bbs的关键字搜索
                百度贴吧等
            (2)商品网站的搜索
                中关村在线
            (3)文件管理系统
                Windows的文件搜索.
        * 垂直搜索
            (1)针对某个行业的搜索引擎
            (2)搜索引擎的细分和延伸
            (3)是针对网页库中的专门信息的整合
            (4)其特点是专、深、精,并具有行业色彩
            (5)可以应用于购物搜索、房产搜索、人才搜索

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

        (1)数据库的搜索
            类似:select * from 表名 where 字段名 like '%关键字%'
            缺点:
                * 搜索效果比较差
                * 在搜索的结果中,有大量数据被搜索出来,有很多数据是没有用的.
                * 查询速度在大量数据的情况下是很难做到快速的.
        (2)全文检索
            * 搜索结果按相关度排序:意味着只有前几个页面对于用户来说是比较有用的,其他的结果与用户想要的答案很可能相差甚远.
                             数据库搜索是做不到相关度排序的.
            * 因为全文检索是采用索引的方式,所以在速度上肯定比数据库方式like要快.
            * 因此数据库搜索不能代替全文检索.

二、Lucene概述

    1.lucene简介

         * 全文检索是一个概念,而具体的实现有很多框架,lucene是其中一种.
         * lucene是一个开放源代码的全文检索引擎工具包,由Apache软件基金会支持和提供.
         * lucene是一个高效的,可扩展的,全文检索库.
         * 全部用java实现,无需配置.
         * 仅支持纯文本文件的索引(Indexing)和搜索(Search)
         * 不负责由其他格式的文件抽取纯文本文件,或从网络中抓取文件的过程.

    2.互联网搜索流程

         * 当用户打开百度网页搜索某些数据时,不是直接找的网页,而是找百度的索引库.索引库里包含的内容有
             索引号和摘要,查询出来的是摘要的内容
         * 百度的索引库的索引和互联网的某一个网站相对应.
         * 用户点击每一个搜索出来的内容惊醒网页查找时,这个时候找的才是互联网中的网页.

    3.lucene相关细节

         * 在数据库中,数据库中的数据文件存储在磁盘上,索引库也是同样,索引库中的索引数据也在磁盘上存在,我们用
             Directory这个类来描述.
         * 我们可以通过API来实现对索引库的增删改查的操作.
         * 在数据库中,各种数据形式都可以概括为一种:表.而在索引库中,各种数据形式也可以抽象出一种数据格式:Document.
         * Document的结构为:Document(List<Field>)    
         * Field里存放一个键值对,键值对都为字符串的形式.
         * 对索引库中索引的操作实际上也就是对Document的操作.

    4.lucene的索引结构

        (1) 索引(Index):
                 * 在lucene中一个索引是放在一个文件夹中的.
                 * 同一个文件夹中的所有文件构成一个lucene索引.
         (2)段(Segment):
                 * 一个索引可以包含多个段,段与段之间是独立的 ,添加新文档可以生成新的段,不同的段可以合并.
                 * 具有相同前缀文件的属同一个段.
                 * segments.gen和segments_5是段的元数据文件,也即它们保存了段的属性信息.
         (3)文档(Document)
                 * 文档是我们建立索引的基本单位,不同的文档是保存在不同的段中的,一个段可以包含多篇文档.
                 * 新添加的文档是单独保存在一个新生成的段中,随着段的合并,不同的文档合并到同一个段中.
         (4)域(Field):
                 * 一篇文档包含不同类型的信息,可以分开索引,比如标题,时间,正文,作者等,都可以保存在不同的域里.
                 * 不同域的索引方式可以不同.
         (5)词(Term):
                 * 词是索引的最小单位,是经过词法分析和语言处理后的字符串.
         说明:lucene的索引结构中,及保存了正向信息,也保存了反向信息.
         正向信息: 按层次保存了从索引,一直到词的包含关系:
                     索引(Index)-->段(segment)-->文档(Document)-->域(Field)-->词(Term)
         反向信息:保存了词典到倒排表的映射
                     词(Term)-->文档(Document)

    5.简单代码示例

        (1)准备开发环境
            需要的jar包:
                * lucene-core-3.1.0.jar         (核心包)
                * lucene-analyzers-3.1.0.jar    (分词器)
                * lucene-highlighter-3.1.0.jar  (高亮器)
                * lucene-memory-3.1.0.jar       (高亮器)

        (2)创建索引

	public void testCreateIndex() throws Exception{
		//Article类,有id,title,content字段,并有get,set方法.
		Article article = new Article();
		article.setId(1L);
		article.setTitle("lucene");
		article.setContent("baidu,google all good");
		
		Directory directory = FSDirectory.open(new File("./indexDir"));
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
		//第一步:创建indexWriter
		IndexWriter indexWriter = new IndexWriter(directory,analyzer,MaxFieldLength.LIMITED);
		
		Document document = new Document();
		Field idField = new Field("id",article.getId().toString(),Store.YES,Index.NOT_ANALYZED);
		Field titleField = new Field("title",article.getTitle(),Store.YES,Index.ANALYZED);
		Field contentField = new Field("content",article.getContent(),Store.YES,Index.ANALYZED);
		//其中Stroe表示是否存储,Index..表示是否分词,例如id需要存储但不用分词.
		document.add(idField);
		document.add(titleField);
		document.add(contentField);
		//第二部:添加document
		indexWriter.addDocument(document);
		//第三部:关闭indexWriter
		indexWriter.close();
	}

        (3)搜索

public void testSearchIndex() throws Exception{
		Directory directory = FSDirectory.open(new File("./indexDir"));
		//第一步:创建IndexSearcher
		IndexSearcher indexSearch = new IndexSearcher(directory);
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
		//下面的content表明要搜索的Field,即要搜索的范围
		QueryParser queryParser = new QueryParser(Version.LUCENE_30,"content",analyzer);
		Query query = queryParser.parse("baidu");//要搜索的关键词,查询方式很多种,query生成种类不同
		//第二步:进行搜索,search(query,5),5表示要显示前5条记录
		TopDocs topDocs = indexSearch.search(query, 5);
		
		int count = topDocs.totalHits;//获得总记录数
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;//获得前5行目录的id列表
		
		List<Article> articleList = new  ArrayList<Article>();
		for(ScoreDoc scoreDoc : scoreDocs){
			float score = scoreDoc.score;//获取相关度得分,可以根据此值进行相关度排名.
			int index = scoreDoc.doc;//获取目录列表id
			Document document = indexSearch.doc(index);//根据id获得Document
			//把document转化为Article
			Article article = new Article();
			article.setId(Long.parseLong(document.get("id")));
			article.setTitle(document.get("title"));
			article.setContent(document.get("content"));
			articleList.add(article);
		}
		//循环输出要检索的内容.
		for(Article article : articleList){
			System.out.println(article.getId());
			System.out.println(article.getTitle());
			System.out.println(article.getContent());
		}
	}

        (4)简单优化
            * Document到Article,与Article到Document的转化可以封装成一个工具类.
             * Directory与Analyzer,也可以封装成一个类中.
            * 原生的分词器对中文分词并不好,要引入另外一个分词器:IKAnalyzer,除了jar包还有三个文件
                ①ext_stopword.dic为停止词的词库,词库里的词都被当作为停止词使用.    
                ②mydict.dic中添加自己所需要的词,扩充词库.
                ③IKAnalyzer.cfg.xml为IKAnalyzer的配置文件.
                    <?xml version="1.0" encoding="UTF-8"?>
                    <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">  
                    <properties>  
                        <comment>IK Analyzer 扩展配置</comment>
                        <!-- 配置扩展词典 -->
                        <entry key="ext_dict">/mydict.dic</entry>  
                         <!-- 配置扩展停用词词典 -->
                        <entry key="ext_stopwords">/ext_stopword.dic</entry>
                    </properties>
            * 在indexWriter.close()之前加入indexWriter.optimize(),当添加同一document创建索引时,覆盖原有的索引文件(内部其实是先进行删除标识).
                    防止相同索引文件占用空间.

    6.查询方式

        (1)关键词查询(区分大小写)
                Term term = new Term("title","lucene");
                Query query = new TermQuery(term);
                indexSearch.search(query, 5);
        (2)查询所有的文档(常用)
                Query query = new MatchAllDocsQuery();
                indexSearch.search(query, 5);
        (3) 通配符查询(常用)
                 * 代表任意多个任意字符
                 ? 任意一个任意字符
                Term term = new Term("title","*.java");
                Query query = new WildcardQuery(term);
                indexSearch.search(query, 5);
        (4) 短语查询
                 所有的关键词对象必须针对同一个属性        
                Term term = new Term("title","lucene");
                Term term2 = new Term("title","搜索");
                PhraseQuery query = new PhraseQuery();
                query.add(term,0);//针对第几条索引进行查询
                query.add(term2,4);
                indexSearch.search(query, 5);
        (5)boolean查询
                各种关键词的组合
                Term term = new Term("title","北京");
                TermQuery termQuery = new TermQuery(term);
                Term term2 = new Term("title","美女");
                TermQuery termQuery3 = new TermQuery(term2);
                BooleanQuery query = new BooleanQuery();
                query.add(termQuery,Occur.SHOULD);
                query.add(termQuery2,Occur.MUST); //表明搜索条件为,必须有美女关键字,而北京关键字可有可无,should表示或
                indexSearch.search(query, 5);
        (6)范围查询
                //搜索id为5到10的索引,含头也含尾
                Query query = NumericRangeQuery.newLongRange("id", 5L, 10L, true, true);        
                indexSearch.search(query, 5);

    7.高亮

public void testSearchIndex() throws Exception{
	IndexSearcher indexSearcher = new IndexSearcher(LuceneUtils.directory);
	QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[]{"title","content"}, LuceneUtils.analyzer);
	Query query = queryParser.parse("Lucene");
	TopDocs topDocs = indexSearcher.search(query, 25);
	ScoreDoc[] scoreDocs = topDocs.scoreDocs;
	/***********************************************************************/
		//给关键字加上前缀和后缀
		Formatter formatter = new SimpleHTMLFormatter("<font color='red'>","</font>");
		//scorer封装了关键字
		Scorer scorer = new QueryScorer(query);
		Highlighter highlighter = new Highlighter(formatter,scorer);
		//创建一个摘要
		Fragmenter fragmenter = new SimpleFragmenter(10);//指定摘要大小,如果使用无参构造器,默认为100.
		highlighter.setTextFragmenter(fragmenter);
	/***********************************************************************/
	List<Article> articleList = new ArrayList<Article>();
	for(ScoreDoc scoreDoc:scoreDocs){
		float score = scoreDoc.score;
		System.out.println(score);//相关的得分
		Document document =  indexSearcher.doc(scoreDoc.doc);
		Article article = DocumentUtils.document2Article(document);
		//使用高亮器
		//1、分词器:查找关键词.2、字段:在哪个字段上进行高亮.3、字段的内容:把字段的内容提取出来.
		String titleText = highlighter.getBestFragment(LuceneUtils.analyzer, "title", document.get("title"));
		String contentText = highlighter.getBestFragment(LuceneUtils.analyzer, "content", document.get("content"));
		if(titleText!=null){
			article.setTitle(titleText);
		}
		if(contentText!=null){
			article.setContent(contentText);
		}
		articleList.add(article);
	}
	//循环输出检索内容
	for(Article article:articleList){
		System.out.println(article.getId());
		System.out.println(article.getTitle());
		System.out.println(article.getContent());
	}
}

    8.核心API

        (1)IndexWriter
            * 此类可以对索引进行增、删、改操作.
            * 利用构造方法可以构造一个IndexWriter的对象.
            * addDocument: 向索引库中添加一个Document
            * updateDocument: 更新一个Document
            * deleteDocuments:删除一个Document        
        (2)Directory(指向索引库的位置)
            * FSDirectory
                ①通过    FSDirectory.open(new File("./indexDir"))建立一个indexDir的文件夹,而这个文件夹就是索引库存放的位置.
                ②通过这种方法建立索引库时如果indexDire文件夹不存在,程序将自动创建一个,如果存在就用原来的这个.
                ③通过这个类可以知道所建立的索引库在磁盘上,能永久性的保存数据。这是优点.
                ④缺点为因为程序要访问磁盘上的数据,这个操作可能引发大量的IO操作,会降低性能.                
            * RAMDirectory
                ①通过构造函数的形式Directory ramdirectory = new RAMDirectory(fsdirectory)可以建立RAMDirectory    
                ②这种方法建立的索引库会在内存中开辟一定的空间,通过构造函数的形式把fsdirectory移动到内存中    
                ③这种方法索引库中的数据是暂时的,只要内存的数据消失,这个索引库就跟着消失了    
                ④因为程序是在内存中跟索引库交互,所以利用这种方法创建的索引的好处就在效率比较高,访问速度比较快    
        (3)Document
            * 一个Directory是由很多Document组成的。用户从客户端输入的要搜索的关键内容被服务器端包装成JavaBean,然后再转化为Document
        (4)Field
            * Field相当于javaBean的属性.
            * new Field("title",article.getTitle(),Store.YES,Index.ANALYZED);
                第一参数为属性,第二个为属性值,第三个参数为是否向索引库里存储.
                第四个参数为是否更新索引库:
                    NO:不进行引索.
                    ANALYZED:进行分词引索.
                    NOT_ANALYZED:进行引索,把整个输入作为一个词对待.    
        (5)MaxFieldLength
            * 能存储的最大长度.
            * 在IndexWriter的构造方法里使用.
            * 值:
                LIMITED:限制的最大长度,值为10000
                UNLIMITED:没有限制的最大长度(一般不使用)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值