一、索引维护
1、需求
管理人员通过电商系统更改图书信息,这时更新的是关系数据库,如果使用lucene搜索图书信息,需要
在数据库表book信息变化时及时更新lucene索引库。
2、添加索引
调用 indexWriter.addDocument(doc)添加索引。
入门程序的创建索引
//7.写入文档到索引库
for (Document doc : documents) {
writer.addDocument(doc);
}
(1)
/**
* 索引库修改操作
* @throws IOException
*/
@Test
public void updateIndexTest() throws IOException {
//需要更新的文档内容(也就是数据库的一条数据)
Document document = new Document();
document.add(new StringField("id", "100000003145", Field.Store.YES ));
document.add(new TextField("name", "xxxxName", Field.Store.YES ));
document.add(new IntPoint("price", 123));
document.add(new StoredField("price", 123));
document.add(new StringField("categoryName", "xxxx", Field.Store.YES ));
document.add(new StringField("brandName", "BrandName", Field.Store.YES ));
//3.创建分词器:把文档对象中的内容提取出来,进行切分词 => 把一句一句话,切分成一个一个词 => 把词组成索引 => 查询的时候,可以通过索引,找到文档对象
//3.1 StandardAnalyzer 标准分词器,对英文分词效果好;对中文是单个字分词,也就是一个字默认是一个词
Analyzer analyzer = new StandardAnalyzer();
//4.创建Directory目录对象 => 目录对象表示索引库的位置(存到硬盘的哪个位置)
Directory directory = FSDirectory.open(Paths.get("D:/ORACLE/JAVA26/lucene-dir"));
//5.创建IndexWriterConfig对象(输出流初始化对象),这个对象中指定切分词使用的分词器
IndexWriterConfig config = new IndexWriterConfig(analyzer);
//6.创建IndexWriter输出流对象,指定输出的目录位置和使用的config初始化对象
writer = new IndexWriter(directory, config);
//7.修改文档到索引库 第一个参数:修改条件;第二个参数:修改成的内容对象
writer.updateDocument(new Term("id", "100000003145"), document);
}
(2)修改之后的数据文档已经不存在于原来的索引处
原因:会根据下面的查询条件,把需要修改的文档查找到并且删除;删除之后,在文档集合的最后会添加一条数据,也就是待更新的文档。
//7.修改文档到索引库 第一个参数:修改条件;第二个参数:修改成的内容对象 writer.updateDocument(new Term("id", "100000003145"), document);
4、删除索引
4.1 删除指定索引
/**
* 测试根据条件删除
* @throws IOException
*/
@Test
public void deleteIndexTest() throws IOException {
//3.创建分词器:把文档对象中的内容提取出来,进行切分词 => 把一句一句话,切分成一个一个词 => 把词组成索引 => 查询的时候,可以通过索引,找到文档对象
//3.1 StandardAnalyzer 标准分词器,对英文分词效果好;对中文是单个字分词,也就是一个字默认是一个词
Analyzer analyzer = new StandardAnalyzer();
//4.创建Directory目录对象 => 目录对象表示索引库的位置(存到硬盘的哪个位置)
Directory directory = FSDirectory.open(Paths.get("D:/ORACLE/JAVA26/lucene-dir"));
//5.创建IndexWriterConfig对象(输出流初始化对象),这个对象中指定切分词使用的分词器
IndexWriterConfig config = new IndexWriterConfig(analyzer);
//6.创建IndexWriter输出流对象,指定输出的目录位置和使用的config初始化对象
writer = new IndexWriter(directory, config);
//7.根据条件删除
writer.deleteDocuments(new Term("id", "100000003145"));
}
1、分词理解
(1)
在对Document中的内容进行索引之前,需要使用分词器进行分词 ,分词的目的是为了搜索。分词的主
要过程就是先分词后过滤。
索引:会将文档(数据)中域值部分提取出来(一句一句话),进行切分词(单个的词) => 做成索引,相当于目录。
搜索:会将用户输入的关键词再次使用分词器进行切分词 => 单个的词,拿这些词和索引进行对比,匹配到相同的词,之后,通过索引再快速查找到文档(一条数据)。
(2)
什么是停用词?停用词是为节省存储空间和提高搜索效率,搜索引擎在索引页面或处理搜索请求时会自
动忽略某些字或词,这些字或词即被称为Stop Words(停用词)。比如语气助词、副词、介词、连接词
等,通常自身并无明确的意义,只有将其放入一个完整的句子中才有一定作用,如常见
的“的”、“在”、“是”、“啊”等。
2、Analyzer分词器使用时机
2.1 索引时使用Analyzer
输入关键字进行搜索,当需要让该关键字与文档域内容所包含的词进行匹配时需要对文档域内容进行分
析,需要经过Analyzer分析器处理生成语汇单元(Token)。分析器分析的对象是文档中的Field域。当
Field的属性tokenized(是否分词)为true时会对Field值进行分析,如下图4:
2.2 搜索时使用Analyzer
对搜索关键字进行分析和索引分析一样,使用Analyzer对搜索关键字进行分析、分词处理,使用分析后
每个词语进行搜索。比如:搜索关键字:spring web ,经过分析器进行分词,得出:spring web拿词
去索引词典表查找 ,找到索引链接到Document,解析Document内容。
对于匹配整体Field域的查询可以在搜索时不分析,比如根据订单号、身份证号查询等。
注意:搜索使用的分析器要和索引使用的分析器一致。
3、Lucene原生分词器
特点:可以对英文进行分词,对中文是单字分词,也就是一个字认为是一个词。
Tokenizer就是分词器,负责将reader转换为语汇单元即进行分词处理,Lucene提供了很多的分词器,
也可以使用第三方的分词,比如IKAnalyzer一个中文分词器。
TokenFilter是分词过滤器,负责对语汇单元进行过滤,TokenFilter可以是一个过滤器链儿,Lucene提
供了很多的分词器过滤器,比如大小写转换、去除停用词等。
仅仅是去掉了空格,没有其他任何操作,不支持中文。
(1)
@Test
public void testWhitespaceAnalyzer() throws Exception{
//1.采集对象
List skuList = skuDao.querySkuList();
//2.创建文档对象集合
List documents = new ArrayList<>(); //并不是数据表中所有的属性都要放入到文档中,需要哪些字段才放
for (Sku sku : skuList) {
//2.1 创建一个文档对象
Document document = new Document();
//2.2 创建域对象,并放入文档对象中
/**
* 是否分词
* 是否索引
* 是否存储:是,因为在业务上有重要作用,存储后,才可以获取到id
*/
document.add(new StringField("id", sku.getId(), Field.Store.YES));
document.add(new TextField("name", sku.getName(), Field.Store.YES));
document.add(new IntPoint("price", sku.getPrice()));
document.add(new StoredField("price", sku.getPrice()));
document.add(new StoredField("image", sku.getImage()));
document.add(new StringField("categoryName", sku.getCategoryName(), Field.Store.YES));
document.add(new StringField("brandName", sku.getBrandName(), Field.Store.YES));
document.add(new TextField("spec", sku.getSpec(), Field.Store.YES));
documents.add(document);
}
// 3. 创建分词器,分析文档,对文档进行分词
Analyzer analyzer = new WhitespaceAnalyzer();
// 4. 创建Directory对象,声明索引库的位置
Directory directory = FSDirectory.open(Paths.get("D:/ORACLE/JAVA26/lucene-dir"));
// 5. 创建IndexWriteConfig对象,写入索引需要的配置
IndexWriterConfig config = new IndexWriterConfig(analyzer);
// 6.创建IndexWriter写入对象
writer = new IndexWriter(directory, config);
// 7.写入到索引库,通过IndexWriter添加文档对象document
for (Document doc : documents) {
writer.addDocument(doc);
}
// 8.释放资源
}
(2)
4.1 第三方中文分词器简介
最新提交的代码在 2008-06-03,在svn中最新也是2010年提交,已经过时,不予考虑。
mmseg4j-solr,支持Lucene 4.10,且在github中最新提交代码是2014年6月,从09年~14年一
共有:18个版本,也就是一年几乎有3个大小版本,有较大的活跃度,用了mmseg算法。
12月推出1.0版开始, IKAnalyzer已经推出了4个大版本。最初,它是以开源项目Luence为应用主
体的,结合词典分词和文法分析算法的中文分词组件。从3.0版本开 始,IK发展为面向Java的公用
分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了
简单的分词 歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。 但是也就是
2012年12月后没有在更新。
2014年更新了大小6次,但是作者本人在2014年10月10日说明:“可能我以后没有精力来维护
ansj_seg了”,现在由”nlp_china”管理。2014年11月有更新。并未说明是否支持Lucene,是一个
由CRF(条件随机场)算法所做的分词算法。
新更新也在2009年5月,下载源码,不支持Lucene 4.10 。是利用HMM(隐马尔科夫链)算法。
用mmseg算法。
IKAnalyzer继承Lucene的Analyzer抽象类,使用IKAnalyzer和Lucene自带的分析器方法一样,将
Analyzer测试代码改为IKAnalyzer测试中文分词效果。
如果使用中文分词器ik-analyzer,就需要在索引和搜索程序中使用一致的分词器:IK-analyzer。
(1). 添加依赖, pom.xml中加入依赖
org.wltea.ik-analyzer
ik-analyzer
8.1.0
(2)它包含两种词典
(3)扩展中文词库
如果想配置扩展词和停用词,就创建扩展词的文件和停用词的文件。
从ikanalyzer包中拷贝配置文件(可到我的资源上传查找相关配置文件:https://mp.csdn.net/console/upDetailed)
(4)导入中文分词词典及配置文件
IK Analyzer 扩展配置 ext.dic; stopword.dic;
停用词典中的词例如: a, an, the, 的, 地, 得等词汇, 凡是出现在停用词典中的字或者词, 在切分词的时候
会被过滤掉.
扩展词典中的词例如: 传智播客, 黑马程序员, 贵州茅台等专有名词, 在汉语中一些公司名称, 行业名称, 分
类, 品牌等不是汉语中的词汇, 是专有名词. 这些分词器默认不识别, 所以需要放入扩展词典中, 效果是被
强制分成一个词.
(5)测试
/**
*测试中文分词器IKAnalyzer
* @throws Exception
*/
@Test
public void TestIKAnalyzer() throws Exception{
// 1. 创建分词器,分析文档,对文档进行分词
Analyzer analyzer = new IKAnalyzer();
// 2. 创建Directory对象,声明索引库的位置
Directory directory = FSDirectory.open(Paths.get("D:/ORACLE/JAVA26/lucene-dir"));
// 3. 创建IndexWriteConfig对象,写入索引需要的配置
IndexWriterConfig config = new IndexWriterConfig(analyzer);
// 4.创建IndexWriter写入对象
writer = new IndexWriter(directory, config);
// 5.写入到索引库,通过IndexWriter添加文档对象document
Document doc = new Document();
doc.add(new TextField("name", "vivo X23 8GB+128GB 幻夜蓝,东华 大学水滴屏全面屏,游戏手机.移 动联通电信全网通4G手机 东华", Field.Store.YES));
writer.addDocument(doc);
// 6.释放资源
}