Lucene 应用

Lucene 应用,第1张

1. 索引创建和搜索流程 1.1 索引创建流程    (1) 一些要索引的原文档(Document)数据采集

   采集数据分类:
   1、对于互联网上网页,可以使用工具将网页抓取到本地生成html文件。
   2、数据库中的数据,可以直接连接数据库读取表中的数据。
   3、文件系统中的某个文件,可以通过I/O *** 作读取文件的内容。

   (2) 创建文档对象 进行词法分析 语言处理 将原文档传给分词器(Tokenizer) 形成一系列词(Term)

   获取原始内容的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档中包括一个一个的域(Field),域中存储内容。每个Document可以有多个Field
   每个文档都有一个唯一的编号,就是文档id
   当然这里传给分词器的 *** 作在addDocument后会自行调用 ,将原始内容创建为包含域(Field)的文档(document),需要再对域中的内容进行分析,分析成为一个一个的单词(Term)

   (3) 索引创建 将得到的词(Term)传给索引组件(Indexer) 形成倒排索引结构

   对所有文档分析得出的词汇单元进行索引,索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到Document(文档)。
   创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构。
   倒排索引结构是根据内容(词汇)找文档,如下图:

   (4) 通过索引存储器 将索引写入到磁盘
1.2 搜索过程

查询索引一般会经过以下步骤:

a) 用户输入查询语句
b) 对查询语句经过词法分析和语言分析得到一系列词(Term)
c) 通过语法分析得到一个查询树
d) 通过索引存储将索引读到内存
e) 利用查询树搜索索引,从而得到每个词(Term)的文档列表,对文档列表进行交、差、并得到结果文档
f) 将搜索到的结果文档按照对查询语句的相关性进行排序
g) 返回查询结果给用户

2. Lucene 索引创建与搜索实现 2.1 使用Java创建索引 Maven依赖:
<dependencies> 
	<dependency> 
		<groupId>junitgroupId> 
		<artifactId>junitartifactId> 
		<version>4.8.2version> 
	dependency> 
	<dependency> 
		<groupId>org.apache.lucenegroupId> 
		<artifactId>lucene-coreartifactId> 
		<version>7.7.3version> 
	dependency> 
	<dependency> 
		<groupId>org.apache.lucenegroupId> 
		<artifactId>lucene-queryparserartifactId> 
		<version>7.7.3version> 
	dependency> 
	<dependency> 
		<groupId>org.apache.lucenegroupId> 
		<artifactId>lucene-analyzers-commonartifactId> 
		<version>7.7.3version> 
	dependency> 
	<dependency> 
		<groupId>commons-iogroupId> 
		<artifactId>commons-ioartifactId> 
		<version>2.5version> 
	dependency> 
dependencies>
java 代码:
public void testCreateIndex() throws Exception { 
	// 1. 采集数据 
	List<Book> bookList = new ArrayList<Book>(); 
	Book booka = new Book(); 
	booka.setId(1); 
	booka.setDesc("Lucene Core is a Java library providing powerful indexing and search features, as well as spellchecking, hit highlighting and advanced analysis/tokenization capabilities. The PyLucene sub project provides Python bindings for Lucene Core. "); 
	booka.setName("Lucene"); 
	booka.setPrice(100.45f); 
	bookList.add(booka); 
	Book bookb = new Book(); 
	bookb.setId(11); 
	bookb.setDesc("Solr is highly scalable, providing fully fault tolerant distributed indexing, search and analytics. It exposes Lucene's features through easy to use JSON/HTTP interfaces or native clients for Java and other languages. "); 
	bookb.setName("Solr"); 
	bookb.setPrice(320.45f); 
	bookList.add(bookb); 
	Book bookc = new Book(); 
	bookc.setId(21); 
	bookc.setDesc("The Apache Hadoop software library is a framework that allows for the distributed processing of large data sets across clusters of computers using simple programming models."); 
	bookc.setName("Hadoop"); 
	bookc.setPrice(620.45f); 
	bookList.add(bookc); 
	// 2. 创建Document文档对象 
	List<Document> documents = new ArrayList<>(); 
	for (Book book : bookList) { 
		Document document = new Document(); 
		// Document文档中添加Field域 
		// 图书Id Store.YES:表示存储到文档域中 
		document.add(new TextField("id", book.getId().toString(),Field.Store.YES)); 
		document.add(new TextField("name", book.getName(),Field.Store.YES)); 
		document.add(new TextField("price", book.getPrice().toString(),Field.Store.YES)); 
		document.add(new TextField("desc", book.getDesc(), Field.Store.YES)); 
		// 把Document放到list中 
		documents.add(document);

	}
	// 3. 创建Analyzer分词器,分析文档,对文档进行分词 
	Analyzer analyzer = new StandardAnalyzer(); 
	// 创建Directory对象,声明索引库的位置 
	Directory directory = FSDirectory.open(Paths.get("D:/lucene/index")); 
	// 创建IndexWriteConfig对象,写入索引需要的配置 
	IndexWriterConfig config = new IndexWriterConfig(analyzer); 
	// 4.创建IndexWriter写入对象 添加文档对象
	document IndexWriter indexWriter = new IndexWriter(directory, config); 	
	for (Document doc : documents) { 
		indexWriter.addDocument(doc); 
		}
	// 释放资源 
	indexWriter.close(); 
}
2.3 使用Java进行搜索
@Test 
public void testSearchIndex() throws Exception { 
	// 1. 创建Query搜索对象 
	// 创建分词器 
	Analyzer analyzer = new StandardAnalyzer(); 
	// 创建搜索解析器,第一个参数:默认Field域,第二个参数:分词器 
	QueryParser queryParser = new QueryParser("id", analyzer); 
	// 创建搜索对象 
	Query query = queryParser.parse("desc:java OR name:solr"); 
	// 2. 创建Directory流对象,声明索引库位置 
	Directory directory = FSDirectory.open(Paths.get("D:/lucene/index")); 
	// 3. 创建索引读取对象
	IndexReader IndexReader reader = DirectoryReader.open(directory); 
	// 4. 创建索引搜索对象 
	IndexSearcher searcher = new IndexSearcher(reader); 
	// 5. 使用索引搜索对象,执行搜索,返回结果集TopDocs 
	// 第一个参数:搜索对象,第二个参数:返回的数据条数,指定查询结果最顶部的n条数据返回 
	TopDocs topDocs = searcher.search(query, 10); 
	System.out.println("查询到的数据总条数是:" + topDocs.totalHits); 
	// 获取查询结果集 
	ScoreDoc[] docs = topDocs.scoreDocs; 
	// 6. 解析结果集 
	for (ScoreDoc scoreDoc : docs) { 
		// 获取文档 
		int docID = scoreDoc.doc; 
		Document doc = searcher.doc(docID); 	
		System.out.println("============================="); 
		System.out.println("score:"+scoreDoc.score); 
		System.out.println("docID:" + docID); 
		System.out.println("bookId:" + doc.get("id")); 
		System.out.println("name:" + doc.get("name")); 
		System.out.println("price:" + doc.get("price")); 
		System.out.println("desc:" + doc.get("desc")); 
	}
	// 释放资源 
	reader.close(); 
}
3. Field 域的使用 3.1 Field 属性

  Lucene存储对象是以Document为存储单元,对象中相关的属性值则存放到Field中。
  Field是文档中的域,包括Field名和Field值两部分,一个文档可以包括多个Field,Document只是Field的一个承载体,Field值即为要索引的内容,也是要搜索的内容。
  Field的三大属性:

  • 是否分词(tokenized)
    是:作分词处理,即将Field值进行分词,分词的目的是为了索引
    比如:商品名称、商品简介等,这些内容用户要输入关键字搜索,由于搜索的内容格式不固定、内容多需要分词后将语汇单元索引。
    否:不作分词处理 比如:订单号、身份z号等
  • 是否索引(indexed)
    是:进行索引。将Field分词后的词或整个Field值进行索引,索引的目的是为了搜索。
    比如:商品名称、商品简介分词后进行索引,订单号、身份z号不用分词但要索引,这些将来都要作为查询 条件。
    否:不索引。该域的内容无法搜索到
    比如:文件路径、图片路径等,不用作为查询条件的不用索引。
  • 是否存储(stored)
    是:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取。
    比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。
    否:不存储Field值,不存储的Field无法通过Document获取
    比如:商品简介,内容较大不用存储。如果要向用户展示商品简介可以从系统的关系数据库中获取商品简 介。
    如果需要商品描述,则根据搜索出的商品ID去数据库中查询,然后显示出商品描述信息即可。
3.2 Field 常用类型
Field类型数据类型是否分词是否索引是否存储说明
StringField(FieldName, FieldValue, Store.YES)字符串NYY/N字符串类型Field, 不分词, 作为一个整体进行索引(如: 身份z 号, 订单编号), 是否需要存储由Store.YES或Store.NO决定
TextField(FieldName, FieldValue, Store.NO)文本类型YYY/N文本类型Field, 分词并且索引, 是否需要存储由Store.YES或 Store.NO决定
LongField(FieldName, FieldValue, Store.YES) 或 LongPoint(String name, int… point)等数值类型YYY/N在Lucene 6.0中,LongField替换为LongPoint,IntField替换 为IntPoint,FloatField替换为FloatPoint,DoubleField替换 为DoublePoint。对数值型字段索引,索引不存储。要存储结 合StoredField即可。
StoredField(FieldName, FieldValue)支持多种类型NNY构建不同类型的Field, 不分词, 不索引, 要存储. (如: 商品图片路 径)
3.3 Field应用
public void createIndex() throws Exception { 
	for (Book book : bookList) { 
		document = new Document(); 
		// IntPoint 分词 索引 不存储 存储结合 
		StoredField Field id = new IntPoint("id", book.getId()); 
		Field id_v = new StoredField("id", book.getId()); 
		// 分词、索引、存储 
		TextField Field name = new TextField("name", book.getName(), Field.Store.YES); 
		// 分词、索引、不存储 但是是数字类型,所以使用FloatPoint 
		Field price = new FloatPoint("price", book.getPrice()); 
		// 分词、索引、不存储 
		TextField Field desc = new TextField("desc", book.getDesc(), Field.Store.NO); 
		// 将field域设置到Document对象中 
		document.add(id); 
		//document.add(id_v); 
		document.add(name); 
		document.add(price); 
		document.add(desc); 
		docList.add(document); 
	}
	//3.创建Analyzer 分词器 对文档进行分词 
	Analyzer analyzer = new StandardAnalyzer();
	// 创建Directory 和 IndexWriterConfig 对象 
	Directory directory = FSDirectory.open(Paths.get("D:/lucene/index3")); 
	IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer); 
	// 4.创建IndexWriter 写入对象 
	IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig); 
	// 添加文档对象 
	for (Document doc : docList) { 
		indexWriter.addDocument(doc); 
	}
	// 释放资源 
	indexWriter.close(); 
}
4. 索引库的维护 4.1 索引库的添加

如3.3中的添加字段

4.2 索引删除

根据Term项删除索引,满足条件的将全部删除。

indexWriter.deleteDocuments(new Term("name", "game"));

全部删除

indexWriter.deleteAll();
4.3 更新索引
public void indexUpdate() throws Exception { 
	// 创建分词器 
	Analyzer analyzer = new StandardAnalyzer(); 
	// 创建Directory流对象 
	Directory directory = FSDirectory.open(Paths.get("D:/lucene/index3")); 
	IndexWriterConfig config = new IndexWriterConfig(analyzer); 
	// 创建写入对象 
	IndexWriter indexWriter = new IndexWriter(directory, config); 
	// 创建Document 
	Document document = new Document(); 
	document.add(new TextField("id", "1002", Field.Store.YES)); 
	document.add(new TextField("name", "lucene测试test update1002", Field.Store.YES)); 
	// 执行更新,会把所有符合条件的Document删除,再新增。 
	indexWriter.updateDocument(new Term("name", "test"), document);
	 // 释放资源 
	 indexWriter.close(); 
}
5. 分词器 5.1 分词器概念

  分词器:采集到的数据会存储到Document对象的Field域中,分词器就是将Document中Field的value值切分成一个一个的词。
  停用词:停用词是为节省存储空间和提高搜索效率,搜索程序在索引页面或处理搜索请求时会自动忽略某些字或词,这些字或词即被称为Stop Words(停用词)。比如语气助词、副词、介词、连接词等,通常自身并无明确的意义,只有将其放入一个完整的句子中才有一定作用,如常见的“的”、“在”、“是”、“啊” 、 a、an、the 等。
  扩展词: 扩展词 就是分词器默认不会切出的词 但我们希望分词器切出这样的词 。
  过滤:包括去除标点符号过滤、去除停用词过滤(的、是、a、an、the等)、大写转小写、词的形还原(复数形式转成单数形参、过去式转成现在式。。。)等。

5.2 分词器案例

  对于分词来说,不同的语言,分词规则不同。Lucene作为一个工具包提供不同国家的分词器,本例子使用StandardAnalyzer,它可以对用英文进行分词。

5.3 中文分词器 5.3.1 Lucene 自带中文分词器

  StandardAnalyzer
  单字分词:就是按照中文一个字一个字地进行分词。如:“我是拉勾人啊”,
效果:“我”、“是”、“拉”、“勾” 、“人”、“啊”。
  CJKAnalyzer
  二分法分词:按两个字进行切分。如:“我是拉勾人啊”,效果:“我是”、“是拉”、“拉勾”、 “勾人”、 “人 啊”。
  上边两个分词器无法满足需求。

5.3.2 中文分词器IKAnalyzer

  IKAnalyzer继承Lucene的Analyzer抽象类,使用IKAnalyzer和Lucene自带的分析器方法一样,将Analyzer测试代码改为IKAnalyzer测试中文分词效果。
  如果使用中文分词器ik-analyzer,就需要在索引和搜索程序中使用一致的分词器:IK-analyzer

<dependency> 
	<groupId>com.github.magesegroupId> 
	<artifactId>ik-analyzerartifactId> 
	<version>7.7.1version> 
dependency>
5.3.3 扩展中文词库

如果想配置扩展词和停用词,就创建扩展词的文件和停用词的文件。
注意:不要用window自带的记事本保存扩展词文件和停用词文件,那样的话,格式中是含有bom的。
ikanalyzer包中拷贝配置文件 拷贝到资源文件夹中
IKAnalyzer.cfg.xml配置文件

 
DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 
<properties> 
	<comment>IK Analyzer 扩展配置comment>
 	 
 	<entry key="ext_dict">ext.dic;entry> 
 	 
 	<entry key="ext_stopwords">stopword.dic;entry> 
 properties>

中文词库,添加新词的地方ext.dic
stopword.dic是存放停用词的地方
最终分词效果 和之前的分词器对比

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/877944.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存