资讯专栏INFORMATION COLUMN

Lucene学习笔记

justCoding / 2753人阅读

摘要:全文检索概述数据分类结构化数据具有固定格式或者长度有限的数据,例如数据库中的表。语句非结构化数据与结构化数据对立,例如邮件网页文档。

全文检索概述 数据分类

结构化数据:具有固定格式或者长度有限的数据,例如数据库中的表。【SQL语句】

非结构化数据:与结构化数据对立,例如:邮件、网页、word文档。【数据扫描、全文检索】

半结构化数据:介于两者之间,例如xml或者json格式的数据。

全文检索过程


反向索引(倒排表):由字符串到文件的映射是文件到字符串映射的反向过程。

索引创建

索引检索

Lucene数学模型 文档、域、词元

文档是Lucene搜索和索引的原子单位,文档为包含一个或者多个的容器,而域则是依次包含“真正的”被搜索的内容,域值通过分词技术处理,得到多个词元

For Example,一篇小说(斗破苍穹)信息可以称为一个文档,小说信息又包含多个域,例如:标题(斗破苍穹)、作者、简介、最后更新时间等等,对标题这个域采用分词技术又可以得到一个或者多个词元(斗、破、苍、穹)。

词元权重计算

Term Frequency(tf):此term在文档中出现的次数,tf越大则该词元越重要

Document Frequency(df):有多少文档包含此term,df越大该词元越不重要

空间向量模型如下:

计算夹角的余弦值,夹角越小,余弦值越大,分值越大,从而相关性越大。

Lucene文件结构 层次结构

index:一个索引存放在一个目录中;

segment:一个索引中可以有多个段,段与段之间是独立的,添加新的文档可能产生新段,不同的段可以合并成一个新段;

document:文档是创建索引的基本单位,不同的文档保存在不同的段中,一个段可以包含多个文档;

field:域,一个文档包含不同类型的信息,可以拆分开索引;

term:词,索引的最小单位,是经过词法分析和语言处理后的数据。

正向信息

按照层次依次保存了从索引到词的包含关系:index-->segment-->document-->field-->term。

反向信息

反向信息保存了词典的倒排表映射:term-->document

配置Lucene开发环境

Lucene是ASF的开源项目,最新版本是5.2.1,但是鉴于网络上大多数教程使用的是Lucene 4,在本文中使用的版本是Lucene 4.3.1,下载解压,到对应目录找到以下的jar包并添加到构建路径中。

如果使用maven其maven依赖如下:


	org.apache.lucene
	lucene-core
	4.3.1


	org.apache.lucene
	lucene-queryparser
	4.3.1


	org.apache.lucene
	lucene-queries
	4.3.1


	org.apache.lucene
	lucene-highlighter
	4.3.1


	org.apache.lucene
	lucene-analyzers-smartcn
	4.3.1


	org.apache.lucene
	lucene-analyzers-common
	4.3.1
Lucene常用功能介绍 索引创建 创建索的关键类

创建索引的示例代码:

/**
 * 索引创建
 */
@Test
public void createIndex() {
	
	// 创建一个分词器(指定Lucene版本)
	Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_43);	
	// IndexWriter配置信息(指定Lucene版本和分词器)
	IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_43, analyzer); 
	// 设置索引的打开方式
	indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
	// 创建Directory对象和IndexWriter对象
	Directory directory = null;
	IndexWriter indexWriter = null;
	try {
		directory = FSDirectory.open(new File("Lucene_index/test"));
		
		// 检查Directory对象是否处于锁定状态(如果锁定则进行解锁)
		if (IndexWriter.isLocked(directory)) {
			IndexWriter.unlock(directory);
		}
		
		indexWriter = new IndexWriter(directory, indexWriterConfig);
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	// 创建测试文档并为其添加域
	Document doc1 = new Document();
	doc1.add(new StringField("id", "abcde", Store.YES)); 				  // 添加一个id域,域值为abcde
	doc1.add(new TextField("content", "使用Lucene实现全文检索", Store.YES)); // 文本域
	doc1.add(new IntField("num", 1, Store.YES));						  // 添加数值域
	
	// 将文档写入索引
	try {
		indexWriter.addDocument(doc1);
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	Document doc2 = new Document();
	doc2.add(new StringField("id", "yes", Store.YES)); 				 
	doc2.add(new TextField("content", "Docker容器技术简介", Store.YES)); 
	doc2.add(new IntField("num", 2, Store.YES));						 
	try {
		indexWriter.addDocument(doc2);
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	// 将IndexWriter提交
	try {
		indexWriter.commit();
	} catch (IOException e) {
		e.printStackTrace();
	} finally{
		try {
			indexWriter.close();
			directory.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

生成的索引文件如下:

索引检索 索引检索关键类

利用以下的检索程序对前面创建的索引进行检索:

/**
 * 索引检索
 */
@Test
public void searchIndex(){
	
	Directory directory = null;
	DirectoryReader dReader = null;
	try {
		directory = FSDirectory.open(new File("Lucene_index/test"));	// 索引文件
		dReader = DirectoryReader.open(directory);						// 读取索引文件
		IndexSearcher searcher = new IndexSearcher(dReader);			// 创建IndexSearcher对象
		
		Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_43);    // 指定分词技术(标准分词-与创建索引时使用的分词技术一致)
		
		// 创建查询字符串(指定搜索域和采用的分词技术)
		QueryParser parser = new QueryParser(Version.LUCENE_43, "content", analyzer);
		Query query = parser.parse("Docker"); 							// 创建Query对象(指定搜索词)
		
		// 检索索引(指定前10条)
		TopDocs topDocs = searcher.search(query, 10);
		if (topDocs != null) {
			System.out.println("符合条件的文档总数为:" + topDocs.totalHits);
			for (int i = 0; i < topDocs.scoreDocs.length; i++) {
				Document doc = searcher.doc(topDocs.scoreDocs[i].doc);
				System.out.println("id = " + doc.get("id") + ",content = " + doc.get("content") + ",num = " + doc.get("num"));
			}
		}
	} catch (IOException e) {
		e.printStackTrace();
	} catch (ParseException e) {
		e.printStackTrace();
	} finally{
		try {
			dReader.close();
			directory.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

运行结果:

Lucene分词器 常见分词器

其中IKAnalyzer需要下载专门的jar包

/**
 * 常见分词器
 */
@Test
public void testAnalyzer(){
	
	final String str = "利用Lucene 实现全文检索";
	Analyzer analyzer = null;
	
	analyzer = new StandardAnalyzer(Version.LUCENE_43); 	// 标准分词
	print(analyzer, str);
	analyzer = new IKAnalyzer();							// 第三方中文分词
	print(analyzer, str);
	analyzer = new WhitespaceAnalyzer(Version.LUCENE_43); 	// 空格分词
	print(analyzer, str);
	analyzer = new SimpleAnalyzer(Version.LUCENE_43); 		// 简单分词
	print(analyzer, str);
	analyzer = new CJKAnalyzer(Version.LUCENE_43);			// 二分法分词
	print(analyzer, str);
	analyzer = new KeywordAnalyzer();						// 关键字分词
	print(analyzer, str);
	analyzer = new StopAnalyzer(Version.LUCENE_43); 		//被忽略词分词器
	print(analyzer, str);

}

/**
 * 该方法用于打印分词器及其分词结果
 * @param analyzer 分词器
 * @param str 需要分词的字符串
 */
public void print(Analyzer analyzer,String str){
	
	StringReader stringReader = new StringReader(str);
	try {
		TokenStream tokenStream = analyzer.tokenStream("", stringReader); 			// 分词
		tokenStream.reset();
		
		CharTermAttribute term = tokenStream.getAttribute(CharTermAttribute.class); // 获取分词结果的CharTermAttribute
		System.out.println("分词技术:" + analyzer.getClass());
		while (tokenStream.incrementToken()) {
			System.out.print(term.toString() + "|");
		}
		System.out.println();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

运行结果:

Query创建



/**
 * 创建Query
 */
@Test
public void createQuery(){
	
	String key = "JAVA EE Lucene案例开发";
	String field = "name";
	String[] fields = {"name","content"};
	Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_43); // 分词器
	
	// 单域查询
	QueryParser parser1 = new QueryParser(Version.LUCENE_43, field, analyzer);
	Query query1 = null;
	try {
		query1 = parser1.parse(key); // 使用QueryParser创建Query
	} catch (ParseException e) {
		e.printStackTrace();
	}
	System.out.println(QueryParser.class + query1.toString());
	
	// 多域查询
	MultiFieldQueryParser parser2 = new MultiFieldQueryParser(Version.LUCENE_43, fields, analyzer);
	try {
		query1 = parser2.parse(key);
	} catch (ParseException e) {
		e.printStackTrace();
	}
	System.out.println(QueryParser.class + query1.toString());
	
	// 短语查询
	query1 = new TermQuery(new Term(field, key));
	System.out.println(TermQuery.class + query1.toString());
	
	// 前缀查询
	query1 = new PrefixQuery(new Term(field, key));
	System.out.println(PrefixQuery.class + query1.toString());
	
	// 短语查询
	PhraseQuery query2 = new PhraseQuery();
	query2.setSlop(2); // 设置短语间的最大距离是2
	query2.add(new Term(field, "Lucene"));
	query2.add(new Term(field, "案例"));
	System.out.println(PhraseQuery.class + query2.toString());
	
	// 通配符查询
	query1 = new WildcardQuery(new Term(field, "Lucene?"));
	System.out.println(WildcardQuery.class + query1.toString());
	
	// 字符串范围搜索
	query1 = TermRangeQuery.newStringRange(field, "abc", "azz", false, false);
	System.out.println(TermRangeQuery.class + query1.toString());
	
	// 布尔条件查询
	BooleanQuery query3 = new BooleanQuery();
	query3.add(new TermQuery(new Term(field, "Lucene")),Occur.SHOULD); // 添加条件
	query3.add(new TermQuery(new Term(field, "案例")),Occur.MUST); 
	query3.add(new TermQuery(new Term(field, "案例")),Occur.MUST_NOT); 
	System.out.println(BooleanQuery.class + query3.toString());
}

运行结果:

IndexSearcher常用方法

检索关键类

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64432.html

相关文章

发表评论

0条评论

最新活动
阅读需要支付1元查看
<