1、什么是Lucene

Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包,由资深全文检索专家Doug Cutting所撰写。它是一个全文检索引擎的架构,提供了完整的创建索引查询索引,以及部分文本分析的引擎。

Lucene在全文检索领域是一个经典的祖先,现在很多检索引擎都是在其基础上创建的,思想是相通的。

Lucene是根据关健字来搜索的文本搜索工具,但只能在某个网站内部搜索文本内容,不能跨网站搜索


Lucene is a Java full-text search engine.  Lucene is not a complete application, but rather a code library and API that can easily be used to add search capabilities to applications.



2、Lucene通常用在什么地方

Lucece不能用在互联网搜索(即像百度那样),只能用在网站内部的文本搜索(即只能在CRM,RAX,ERP内部使用),但思想是相通的。



3、Lucene中存的什么内容

Lucene中存的就是一系列的二进制压缩文件和一些控制文件,它们位于计算机的硬盘上,

这些内容统称为索引库索引库有二部份组成:

(1)原始记录 

     存入到索引库中的原始文本

(2)词汇表

     按照一定的拆分策略(即分词器)将原始记录中的每个字符拆开后,存入一个供将来搜索的表




4、为什么网站内部有些地方要用Lucene来索搜,而不全用SQL来搜索

(1)SQL只能针对数据库表搜索,不能直接针对硬盘上的文本搜索

(2)SQL没有相关度排名

(3)SQL搜索结果没有关健字高亮显示

(4)SQL需要数据库的支持,数据库本身需要内存开销较大,例如:Oracle

(5)SQL搜索有时较慢,尤其是数据库不在本地时,超慢,例如:Oracle




5、Lucene版本和JDK版本


不同的Lucene版本对JDK的版本的要求也不同,对于这一问题,我还不太懂。


6、下载Lucene 3.0.2版本


下载地址: https://archive.apache.org/dist/lucene/java/


wKiom1fXHyWyb2qRAACyXlWB1NM955.jpg


下载两个文件:lucene-3.0.2-src.zip(源码包)和lucene-3.0.2.zip(jar包)



7、入门

解压lucene-3.0.2.zip文件,找到如下4个jar文件:

  lucene-core-3.0.2.jar【Lucene核心】

  lucene-analyzers-3.0.2.jar【分词器】

  lucene-highlighter-3.0.2.jar【Lucene会将搜索出来的字,高亮显示,提示用户】

  lucene-memory-3.0.2.jar【索引库优化策略】


将这4个jar包引入到项目当中,编写代码进行测试:

package com.rk.lucene.firstapp;

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

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.junit.Test;

import com.rk.lucene.entity.Article;

public class FirstLucene {
	/**
	 * 创建索引库
	 * 将Aritcle对象放入索引库中的原始记录表中,从而形成词汇表
	 */
	@Test
	public void createIndexDB() throws Exception{
		//创建Article对象
		Article article = new Article(1,"你好","欢迎来到我的世界");
		//创建Document对象
		Document document = new Document();

		//将Article对象中的三个属性值分别绑定到Document对象中
		/*
		 *参数一:document对象中的属性名叫xid,article对象中的属性名叫id,项目中提倡相同
		 *参数二:document对象中的属性xid的值,与article对象中相同
		 *参数三:是否将xid属性值存入由原始记录表中转存入词汇表
		 *       Store.YES表示该属性值会存入词汇表
		 *       Store.NO表示该属性值不会存入词汇表
		 *       项目中提倡非id值都存入词汇表
		 *参数四:是否将xid属性值进行分词算法
		 *       Index.ANALYZED表示该属性值会进行词汇拆分
		 *       Index.NOT_ANALYZED表示该属性值不会进行词汇拆分
		 *       项目中提倡非id值都进行词汇拆分
		 *       目前将分词理解为分汇拆分,目前认为一个汉字一个分词拆分 
		 */
		document.add(new Field("xid", article.getId().toString(), Store.YES, Index.ANALYZED));
		document.add(new Field("xtitle", article.getTitle(), Store.YES, Index.ANALYZED));
		document.add(new Field("xcontent", article.getContent(), Store.YES, Index.ANALYZED));
		//创建IndexWriter字符流对象
		/*
		 * 参数一:lucene索引库最终应对于硬盘中的目录,例如:D:/rk/indexDB
		 * 参数二:采用什么策略将文本拆分,一个策略就是一个具体的实现类
		 * 参数三:最多将文本拆分出多少词汇,LIMITED表示1W个,即只取前1W个词汇,如果不足1W个词汇个,以实际为准
		 */
		Directory directory = FSDirectory.open(new File("D:/rk/indexDB"));
		Version version = Version.LUCENE_30;
		Analyzer analyzer = new StandardAnalyzer(version);
		MaxFieldLength maxFieldLength = MaxFieldLength.LIMITED;
		IndexWriter indexWriter = new IndexWriter(directory, analyzer, maxFieldLength);
		//将document对象写入lucene索引库
		indexWriter.addDocument(document);
		//关闭IndexWriter字符流对象
		indexWriter.close();
	}

	/**
	 * 根据关键字从索引库中搜索符合条件的内容
	 */
	@Test
	public void findIndexDB() throws Exception{
		//一、处理关键字
		String keywords = "世界";
		List<Article> articleList = new ArrayList<Article>();
		Version version = Version.LUCENE_30;
		Analyzer analyzer = new StandardAnalyzer(version);
		//创建查询解析器对象
		/*
		 * 参数一:使用分词器的版本,提倡使用该jar包中的最高版本
		 * 参数二:争对document对象中的哪个属性进行搜索
		 */
		QueryParser queryParser = new QueryParser(version, "xcontent", analyzer);
		//创建对象封装查询关键字
		Query query = queryParser.parse(keywords);
		
		//二、搜索关键字
		//创建IndexSearcher字符流对象
		Directory directory = FSDirectory.open(new File("D:/rk/indexDB"));
		IndexSearcher indexSearcher = new IndexSearcher(directory);

		int MAX_RECORD = 100;
		//根据关键字,去索引库中的词汇表搜索
		/*
		 * 参数一:表示封装关键字查询对象,其它QueryParser表示查询解析器
		 * 参数二:MAX_RECORD表示如果根据关键字搜索出来的内容较多,只取前MAX_RECORD个内容
		 *        不足MAX_RECORD个数的话,以实际为准
		 */
		TopDocs topDocs = indexSearcher.search(query, MAX_RECORD);
		//迭代词汇表中符合条件的编号 
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			//取出封装编号和分数的ScoreDoc对象
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			//取出每一个编号,例如:0,1,2
			int no = scoreDoc.doc;
			//根据编号去索引库中的原始记录表中查询对应的document对象
			Document document = indexSearcher.doc(no);
			//获取document对象中的三个属性值
			String xid = document.get("xid");
			String xtitle = document.get("xtitle");
			String xcontent = document.get("xcontent");
			//封装到article对象中
			Article article = new Article(Integer.parseInt(xid), xtitle, xcontent);
			//将article对象加入到list集合中
			articleList.add(article);
		}
		
		//迭代结果集
		for(Article a : articleList){
			System.out.println(a);
		}
		//关闭IndexSearcher对象
		indexSearcher.close();	
	}
}


Article.java

package com.rk.lucene.entity;

public class Article {
	private Integer id;
	private String title;//标题
	private String content;//内容
	
	public Article() {
	}
	
	public Article(Integer id, String title, String content) {
		this.id = id;
		this.title = title;
		this.content = content;
	}

	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;
	}

	@Override
	public String toString() {
		return "Article [编号=" + id + ", 标题=" + title + ", 内容=" + content + "]";
	}
	
}


生成的文件:

wKioL1fXIVKhk9drAAA0SrEmS2U970.jpg

输出的结果:

Article [编号=1, 标题=你好, 内容=欢迎来到我的世界]



8、代码使用Lucene的流程图


创建索引库:

1)创建JavaBean对象

2)创建Document对象

3)将JavaBean对象所有的属性值,均放到Document对象中去,属性名可以和JavaBean相同或不同

4)创建IndexWriter对象

5)将Document对象通过IndexWriter对象写入索引库中

6)关闭IndexWriter对象


根据关键字查询索引库中的内容:

1)创建IndexSearcher对象

2)创建QueryParser对象

3)创建Query对象来封装关键字

4)用IndexSearcher对象去索引库中查询符合条件的前100条记录,不足100条记录的以实际为准

5)获取符合条件的编号

6)用indexSearcher对象去索引库中查询编号对应的Document对象

7)将Document对象中的所有属性取出,再封装回JavaBean对象中去,并加入到集合中保存,以备将之用


package com.rk.lucene.firstapp;

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

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.junit.Test;

import com.rk.lucene.entity.Article;

public class DemoA {
	@Test
	public void createIndexDB() throws Exception{
		//1、得到JavaBean对象
		Article article = new Article(1, "你好", "欢迎来到我的世界");
		//2、将JavaBean对象转换为Document对象
		Document doc = new Document();
		doc.add(new Field("xid", article.getId().toString(), Store.YES, Index.ANALYZED));
		doc.add(new Field("xtitle", article.getTitle(), Store.YES, Index.ANALYZED));
		doc.add(new Field("xcontent", article.getContent(), Store.YES, Index.ANALYZED));
		//3、利用IndexWriter将Document对象写入到索引库内
		//	 3.1、创建索引库位置
		Directory directory = FSDirectory.open(new File("D:/rk/indexDB"));
		//	 3.2、创建分词器
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
		//  3.3、创建IndexWriter对象
		IndexWriter indexWriter = new IndexWriter(directory, analyzer, MaxFieldLength.LIMITED);
		//  3.4、将Document写入到索引库
		indexWriter.addDocument(doc);
		//  3.5、关闭IndexWriter对象
		indexWriter.close();
	}
	
	@Test
	public void findIndexDB() throws Exception{
		//1、对搜索的“关键字”进行分词处理,“分词处理的结果”是Query对象	
		String keywords = "世界";
		Version version = Version.LUCENE_30;
		Analyzer analyzer = new StandardAnalyzer(version);
		QueryParser queryParser = new QueryParser(version, "xcontent", analyzer);
		Query query = queryParser.parse(keywords);
		
		//2、在“索引库”中,对“分词处理的结果”进行查询,得到TopDocs对象
		Directory directory = FSDirectory.open(new File("D:/rk/indexDB"));
		IndexSearcher indexSearcher = new IndexSearcher(directory);
		TopDocs topDocs = indexSearcher.search(query, 10);
		
		//3、从TopDocs中取出Document对象,然后转换为JavaBean对象
		List<Article> articleList = new ArrayList<Article>();
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int no = scoreDoc.doc;
			Document document = indexSearcher.doc(no);
			String xid = document.get("xid");
			String xtitle = document.get("xtitle");
			String xcontent = document.get("xcontent");
			
			Article article = new Article(Integer.parseInt(xid), xtitle, xcontent);
			articleList.add(article);
		}
		//4、将查询结果输出
		for(Article article : articleList){
			System.out.println(article);
		}
		
	}
}