Lucene全文索引学习笔记(一)

目录

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

1.1、数据库的搜索 

1.2、全文检索

二、Lucene入门

2.1、 Lucene是什么?

2.2、Lucene的结构

2.3、开发环境

2.4.1、建立索引结构图

2.4.2、代码

2.5.1、搜索结构图

2.5.2、代码

2.6、Lucene知识点

三、分词器

3.1、英文分词器

3.2、中文分词器

3.3   IKAnalyzer分词器词库的扩充

四、查询索引库

1.索引方式

1.1、字符串查询

1.2、自己创建与配置Query对象

1.3、相关度得分

1.4、按照某个字段进行排序

1.5、高亮

五、修改、删除

5.1、删除

5.2、修改


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

1.1、数据库的搜索 

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

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

缺点:

  1. 搜索效果比较差
  2. 在搜索的结果中,有大量的数据被搜索出来,有很多数据是没有用的。
  3. 查询速度在大量数据的情况下是很难做到快速的。

1.2、全文检索

  1. 搜索结果按相关度排序:意味着只有前几个页面对于用户来说是比较有用的,其他的结果与用户想要的答案很可能相差甚远。数据库搜索是做不到相关度排序的。
  2. 因为全文检索是采用引索的方式,所以在速度上肯定比数据库方式like要快。
  3. 所以数据库不能代替全文检索。

二、Lucene入门

2.1、 Lucene是什么?

Lucene是一个用Java写的高性能、可伸缩的全文检索引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。Lucene的目标是为各种中小型应用程序加入全文检索功能。Lucene的主页http://lucene.apache.org/    本文为3.0.1版本

2.2、

Lucene的结构

  1. Lucene 的analysis 模块主要负责词法分析及语言处理而形成Term。
  2. Lucene的index模块主要负责索引的创建,里面有IndexWriter。
  3. Lucene的store模块主要负责索引的读写。
  4. Lucene 的QueryParser主要负责语法分析。
  5. Lucene的search模块主要负责对索引的搜索

2.3、开发环境

  搭建lucene的开发环境,要准备lucene的jar包,要加入的jar包至少有:

  1. lucene-core-3.1.0.jar     (核心包)
  2. lucene-analyzers-3.1.0.jar    (分词器)
  3. lucene-highlighter-3.1.0.jar    (高亮器)
  4. lucene-memory-3.1.0.jar       (高亮器)

2.4.1、建立索引结构图

2.4.2、代码

/**
	 * 创建索引
	 */
	@Test
	public void testCreateIndex() throws Exception{
		/**
		 * 1、创建一个article对象
		 * 2、创建一个IndexWriter对象
		 * 3、把article对象变成document对象
		 * 4、把document对象放入到索引库中
		 * 5、关闭资源
		 */
		Article article = new Article();
		article.setAid(1L);
		article.setTitle("lucene是一个全文检索引擎");
		article.setContent("baidu,google都是很好的全文检索引擎");
		//创建IndexWriter
		/**
		 * 第一个参数
		 *      索引库的位置
		 */
		Directory directory = FSDirectory.open(new File("./Dirindex"));
		/**
		 * 第二个参数
		 *       分词器
		 */
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
		/**
		 * 第三个参数
		 *       限制索引库中字段的大小
		 */
		IndexWriter indexWriter = new IndexWriter(directory, analyzer, MaxFieldLength.LIMITED);
		//把article转化成document
		Document document = new Document();
		/**
		 * 参数
		 *     name  存储在索引库的名字
		 *     value 存储在索引库中的值
         *     store 代表是否存储在索引库中
         *            YES 存储    NO  不存储
         *     index 是否更新索引列表
         *        NO  表示在添加内容的时候,不忘索引目录里添加
         *        ANALYZED  表示在添加内容时,使用分词往目录里添加相应的目录
         *        NOT_ANALYZED  表示在添加内容时,整个字符串作为一个索引添加到相应的目录
		 */
		Field idField =  new Field("aid", article.getAid().toString(), Store.YES, Index.NOT_ANALYZED);
		Field titleField =  new Field("title", article.getTitle(), Store.YES, Index.NO);
		Field contentField =  new Field("content", article.getContent(), Store.YES, Index.ANALYZED);
		//把上面的field放入到document中
		document.add(idField);
		document.add(titleField);
		document.add(contentField);
		
		indexWriter.addDocument(document);
		
		indexWriter.close();
	}

运行后会出现此文件

2.5.1、搜索结构图

2.5.2、代码

/**
	 * 从索引库中根据关键字把信息检索出来
	 */
	@Test
	public void testSearchIndex() throws Exception{
		/**
		 * 创建一个IndexSearch对象
		 */
		Directory directory = FSDirectory.open(new File("./Dirindex"));
		IndexSearcher indexSearcher = new IndexSearcher(directory);
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
		/**
		 * 第一个参数为版本号
		 * 第二个参数为在哪个字段中进行检索
		 */
		//QueryParser queryParser = new QueryParser(Version.LUCENE_30,"content",analyzer);
		QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30,new String[]{"title","content"},analyzer);
		/**
		 * 关键词
		 */
		Query query = queryParser.parse("全文检索引擎");
		/**
		 * 第二个参数  
		 *      查找前多少个
		 *  TopDocs-->Top Documents
		 */
		TopDocs topDocs = indexSearcher.search(query, 1);
		int count = topDocs.totalHits;//根据关键词计算出来的总的记录数
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		List<Article> articles = new ArrayList<Article>();
		for (ScoreDoc scoreDoc : scoreDocs) {
			/**
			 * 关键词的索引
			 */
			int index = scoreDoc.doc;
			/**
			 * 根据关键词的索引查找到document
			 */
			Document document = indexSearcher.doc(index);
			//把document转化成article
			Article article = new Article();
			article.setAid(Long.parseLong(document.get("aid")));
			article.setTitle(document.get("title"));
			article.setContent(document.get("content"));
			articles.add(article);
		}
		
		for (Article article : articles) {
			System.out.println(article.getAid());
			System.out.println(article.getTitle());
			System.out.println(article.getContent());
		}
	}
  1. 创建IndexSearch
  2. 创建Query对象
  3. 进行搜索
  4. 获得总结果数和前N行记录ID列表
  5. 根据目录ID列表把Document转为为JavaBean并放入集合中。
  6. 循环出要检索的内容

2.6、Lucene知识点

  1. 执行两次建立引索      说明:执行两次同样的JavaBean数据增加的引索都能成功,说明JavaBean中的ID不是唯一确定索引的标示。在lucene中,唯一确定索引的标示(目录ID)是由lucene内部生成的。
  2. 在搜索的时候,可以尝试用”Lucene”或者”lucene”来测试,结果是一样的。因为分词器把输入的关键字都变成小写。
  3. 在建立索引和搜索索引的时候都用到了分词器。  
  4. 在索引库中存放的有目录和内容两大类数据。
  5. Store这个参数表明是否将内容存放到索引库内容中。
  6. Index这个参数表明是否存放关键字到索引目录中。
  7. 当一个IndexWriter在进行读索引库操作的时候,lucene会索索引库,以防止其他IndexWriter访问索引库而导致数据不一致,直到IndexWriter关闭为止。                             结论:同一个索引库只能有一个IndexWriter进行操作,这里用单例模式做比较好。

   8.lucene在执行删除的时候,是先把要删除的元素形成了一个文件del文件,然后再和cfs文件进行整合得出最后结果。

三、分词器

3.1、英文分词器

      步骤:eg:切割    Creates a searcher searching the index in the named directory

(1)切分关键词

               Creates  

               a  

               searcher 

               searching 

                the 

                index 

                the 

               named 

               directory

(2)去除停用词

停用词:有些词在文本中出现的频率非常高。但对本文的语义产生不了多大的影响。例如英文的a、an、the、of等。或中文的”的、了、呢等”。这样的词称为停用词。停用词经常被过滤掉,不会被进行索引。在检索的过程中,如果用户的查询词中含有停用词,系统会自动过滤掉。停用词可以加快索引的速度,减少索引库文件的大小。

Creates

searcher

searching

index

named

directory

(3)转为小写(搜索时不区分大小写,因为分词器会帮你转化)

creates

searcher

searching

index

named

directory

3.2、中文分词器

3.2.1单字分词

           Analyzer analyzer2 = new ChineseAnalyzer();

              把汉字一个字一个字分解出来。效率比较低。

3.2.2二分法分词

Analyzer analyzer3 = new CJKAnalyzer(Version.LUCENE_30);

        把相邻的两个字组成词分解出来,效率也比较低。而且很多情况下分的词不对。

3.2.3词库分词(IKAnalyzer)

Analyzer analyzer4 = new IKAnalyzer();

基本上可以把词分出来(经常用的分词器)

3.3   IKAnalyzer分词器词库的扩充

如图:(编码必须为UTF-8)

ext_stopword.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>

四、查询索引库

MultiFieldQueryParser.这个类的好处可以选择多个属性进行查询,QueryParser只能选择一个。

1.索引方式

1.1、字符串查询

   QueryParser->Query对象

   可以使用查询条件

         “lucene AND 互联网”     都出现符合查询条件

         “lucene OR 互联网”  只要出现其一就符合查询条件

1.2、自己创建与配置Query对象

1.2.1、关键词查询(TermQuery)

注:因为保存引索的时候是通过分词器保存,所以所有的因为在索引库里都为小写,所以lucene必须得小写,不然查询不到

如果使用查询字符串进行查询,对应的语法格式为:title:lucene

1.2.2、查询所有文档

                   如果使用查询字符串,对应语法:*:*

1.2.3、范围查询

                       如果使用查询字符串,

                            第一个: id:[5 TO 15]

                            第二个:   id:{5 TO 15}

                  注:在lucene中,处理数字是不能直接写入的,要进行转化。NumberStringTools帮助类给出了转化工具:

                       在工具类DocumentUtils中也做相应的转化:

   1.2.4、通配符查询

                       如果使用查询字符串:title:lucen?

1.2.5、短语查询

                     上面的0代表第0个位置

                    上面的1代表第3个位置

                              使用查询字符串:title:"lucene ? ? 互联网"          

1.2.6、Boolean查询

可以把多个查询条件组合成一个查询条件

  如图为:同时满足title中有lucene关键字和ID为5到15的所有索引数据。不包括5和15

             使用查询字符串:+id:{5 TO 15} +title:lucene

注意:

  1.  单独使用MUST_NOT  没有意义
  2.  MUST_NOT和MUST_NOT  无意义,检索无结果
  3. 单独使用SHOULD:结果相当于MUST
  4. SHOULD和MUST_NOT: 此时SHOULD相当于MUST,结果同MUST和MUST_NOT
  5. MUST和SHOULD:此时SHOULD无意义,结果为MUST子句的检索结果

1.3、相关度得分

利用Document.setBoost可以控制得分。默认值为1

结论:  利用Document.setBoost可以人为控制相关度得分,从而把某一个引索内容排到最前面。

1.4、按照某个字段进行排序

如图所示的代码,重载了indexSearcher.search方法。在这个方法中,Sort对  象就是指定的按照id升序排列。SortField.INT指定了ID的类型。类型不一样,大小的比较就不一样。最后一个参数为reverse   :     Reverse为false   升序(默认)      Reverse 为true    降序

1.5、高亮

(1)创建和配置高量器

                一高亮器的创建需要两个条件:

                            Formatter    要把关键词显示成什么样子

                            Scorer        查询条件

                           Fragmenter设置文本的长度。默认为100。

         如果文本的内容长度超过所设定的大小,超过的部分将显示不出来。

(2)使用高亮器

                getBestFragment为得到高亮后的文本。

                 参数:

     1、分词器。

                        如果是英文:StandardAnalyzer       如果是中文:IKAnalyzer

    2、在哪个属性上进行高亮

    3、要高亮的内容

五、修改、删除

5.1、删除

/**
	 * 删除
	 *   不是把原来的cfs文件删除掉了,而是多了一个del文件
	 */
	@Test
	public boolean delete(String id){
		IndexWriter indexWriter = null;
		try {
			/**
			 * 第一个参数 :索引库的位置
			 */
			Directory directory = FSDirectory.open(new File(dirPath));
			/**
			 * 第二个参数:分词器
			 */
			Analyzer analyzer =  new IKAnalyzer(); 
			/**
			 * 第三个参数:限制索引库中字段的大小
			 */
			 indexWriter = new IndexWriter(directory, analyzer, MaxFieldLength.LIMITED);
			 Term term = new Term("id",id);
			 indexWriter.deleteDocuments(term);
			 return true;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}finally{
			try {
				indexWriter.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

说明:indexWriter.deleteDocuments的参数为Term.Term指关键词。因为ID的索引保存类型为Index.NOT_ANALYZED,因为直接写ID即可。

5.2、修改

/**
	 * 修改
	 *    先删除后增加
	 */
	@Test
	public boolean update(LuceneBean luceneBean){
		IndexWriter indexWriter = null;
		try {
			/**
			 * 第一个参数 :索引库的位置
			 */
			Directory directory = FSDirectory.open(new File(dirPath));
			/**
			 * 第二个参数:分词器
			 */
			Analyzer analyzer =  new IKAnalyzer(); 
			/**
			 * 第三个参数:限制索引库中字段的大小
			 */
			 indexWriter = new IndexWriter(directory, analyzer, MaxFieldLength.LIMITED);
			 Term term = new Term("id",luceneBean.getId());
			 //把lucene转化成document
			 Document document = DocumentUtils.bean2Document(luceneBean);
			 indexWriter.updateDocument(term, document);
			 return true;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}finally{
			try {
				indexWriter.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

说明:lucene的更新操作与数据库的更新操作是不一样的。因为在更新的时候,有可能变换了关键字的位置,这样分词器对关键字还得重新查找,而且还得在目录和内容中替换,这样做的效率比较低,所以lucene的更新操作是删除增加两步骤来完成的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值