采集数据分类:
1、对于互联网上网页,可以使用工具将网页抓取到本地生成html文件。
2、数据库中的数据,可以直接连接数据库读取表中的数据。
3、文件系统中的某个文件,可以通过I/O *** 作读取文件的内容。
获取原始内容的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档中包括一个一个的域(Field),域中存储内容。每个Document可以有多个Field
每个文档都有一个唯一的编号,就是文档id
当然这里传给分词器的 *** 作在addDocument后会自行调用 ,将原始内容创建为包含域(Field)的文档(document),需要再对域中的内容进行分析,分析成为一个一个的单词(Term)
对所有文档分析得出的词汇单元进行索引,索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到Document(文档)。
创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构。
倒排索引结构是根据内容(词汇)找文档,如下图:
1.2 搜索过程
查询索引一般会经过以下步骤:
2. Lucene 索引创建与搜索实现 2.1 使用Java创建索引 Maven依赖:a) 用户输入查询语句
b) 对查询语句经过词法分析和语言分析得到一系列词(Term)
c) 通过语法分析得到一个查询树
d) 通过索引存储将索引读到内存
e) 利用查询树搜索索引,从而得到每个词(Term)的文档列表,对文档列表进行交、差、并得到结果文档
f) 将搜索到的结果文档按照对查询语句的相关性进行排序
g) 返回查询结果给用户
<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的三大属性:
3.2 Field 常用类型
- 是否分词(tokenized)
是:作分词处理,即将Field值进行分词,分词的目的是为了索引
比如:商品名称、商品简介等,这些内容用户要输入关键字搜索,由于搜索的内容格式不固定、内容多需要分词后将语汇单元索引。
否:不作分词处理 比如:订单号、身份z号等- 是否索引(indexed)
是:进行索引。将Field分词后的词或整个Field值进行索引,索引的目的是为了搜索。
比如:商品名称、商品简介分词后进行索引,订单号、身份z号不用分词但要索引,这些将来都要作为查询 条件。
否:不索引。该域的内容无法搜索到
比如:文件路径、图片路径等,不用作为查询条件的不用索引。- 是否存储(stored)
是:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取。
比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。
否:不存储Field值,不存储的Field无法通过Document获取
比如:商品简介,内容较大不用存储。如果要向用户展示商品简介可以从系统的关系数据库中获取商品简 介。
如果需要商品描述,则根据搜索出的商品ID去数据库中查询,然后显示出商品描述信息即可。
Field类型 | 数据类型 | 是否分词 | 是否索引 | 是否存储 | 说明 |
---|---|---|---|---|---|
StringField(FieldName, FieldValue, Store.YES) | 字符串 | N | Y | Y/N | 字符串类型Field, 不分词, 作为一个整体进行索引(如: 身份z 号, 订单编号), 是否需要存储由Store.YES或Store.NO决定 |
TextField(FieldName, FieldValue, Store.NO) | 文本类型 | Y | Y | Y/N | 文本类型Field, 分词并且索引, 是否需要存储由Store.YES或 Store.NO决定 |
LongField(FieldName, FieldValue, Store.YES) 或 LongPoint(String name, int… point)等 | 数值类型 | Y | Y | Y/N | 在Lucene 6.0中,LongField替换为LongPoint,IntField替换 为IntPoint,FloatField替换为FloatPoint,DoubleField替换 为DoublePoint。对数值型字段索引,索引不存储。要存储结 合StoredField即可。 |
StoredField(FieldName, FieldValue) | 支持多种类型 | N | N | Y | 构建不同类型的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等)、大写转小写、词的形还原(复数形式转成单数形参、过去式转成现在式。。。)等。
对于分词来说,不同的语言,分词规则不同。Lucene作为一个工具包提供不同国家的分词器,本例子使用StandardAnalyzer
,它可以对用英文进行分词。
StandardAnalyzer
单字分词:就是按照中文一个字一个字地进行分词。如:“我是拉勾人啊”,
效果:“我”、“是”、“拉”、“勾” 、“人”、“啊”。
CJKAnalyzer
二分法分词:按两个字进行切分。如:“我是拉勾人啊”,效果:“我是”、“是拉”、“拉勾”、 “勾人”、 “人 啊”。
上边两个分词器无法满足需求。
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
是存放停用词的地方
最终分词效果 和之前的分词器对比
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)