描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787302477082
本书介绍了分布式搜索引擎开发的原理与Java实现,主要包括全文检索的原理与实现、分布式算法与代码实现、SolrCloud和ElasticSearch的使用与原理等内容,并着重介绍了一种实现分布式中文搜索引擎的方法。
本书适合有Java程序设计基础的开发人员或者对分布式搜索引擎技术感兴趣的从业人员使用
目 录
第1章 搜索引擎…. 1
1.1 搜索引擎基本模块… 2
1.2 开发环境… 3
1.3 搜索引擎工作原理… 4
1.3.1 网络爬虫… 5
1.3.2 全文索引… 5
1.3.3 搜索用户界面… 8
1.3.4 分布式计算… 9
1.3.5 文本挖掘… 9
1.4 算法基础… 9
1.4.1 折半查找… 10
1.4.2 排序… 10
1.4.3 小生成树… 12
1.5 软件工具… 15
1.6 单元测试… 15
1.7 本章小结… 17
1.8 术语表… 18
第2章 自己动手写全文检索…. 19
2.1 构建索引… 22
2.2 生成索引文件… 23
2.3 读入索引文件… 25
2.4 查询… 26
2.5 有限状态机… 29
2.5.1 运算… 29
2.5.2 编辑距离有限状态机… 30
2.6 本章小结… 32
第3章 Lucene的原理与应用…. 33
3.1
Lucene快速入门… 34
3.1.1 创建索引… 34
3.1.2 查询索引库… 35
3.1.3 创建文档索引… 36
3.1.4 查询文档索引… 36
3.2 创建和维护索引库… 37
3.2.1 设计索引库结构… 37
3.2.2 创建索引库… 38
3.2.3 向索引库中添加索引文档… 40
3.2.4 删除索引库中的索引文档… 43
3.2.5 更新索引库中的索引文档… 44
3.2.6 关闭索引库… 45
3.2.7 索引的优化与合并… 45
3.2.8 灵活索引… 46
3.2.9 索引文件格式… 47
3.2.10 定制索引存储结构… 49
3.2.11 写索引集成到爬虫… 54
3.2.12 多线程写索引… 56
3.2.13 分发索引… 58
3.2.14 修复索引… 61
3.3 查找索引库… 61
3.3.1 查询过程… 61
3.3.2 常用查询… 64
3.3.3 基本词查询… 65
3.3.4 模糊匹配… 65
3.3.5 布尔查询… 67
3.3.6 短语查询… 69
3.3.7 跨度查询… 71
3.3.8 FieldScoreQuery. 74
3.3.9 排序… 77
3.3.10 使用Filter筛选搜索结果… 81
3.3.11 使用Collector筛选搜索
结果… 82
3.3.12 遍历索引库… 85
3.3.13 关键词高亮显示… 88
3.3.14 列合并… 91
3.3.15 关联内容(BlockJoinQuery) 92
3.3.16 查询大容量索引… 94
3.4 读写并发… 95
3.5
Lucene深入介绍… 95
3.5.1 整体结构… 96
3.5.2 索引原理… 97
3.5.3 文档值… 100
3.5.4 FST. 102
3.6 查询语法与解析… 102
3.6.1 JavaCC.. 104
3.6.2 生成一个查询解析器… 114
3.6.3 简单的查询解析器… 114
3.6.4 灵活的查询解析器… 114
3.7 检索模型… 119
3.7.1 向量空间模型… 121
3.7.2 DFR.. 125
3.7.3 BM25概率模型… 130
3.7.4 BM25F概率模型… 136
3.7.5 统计语言模型… 138
3.7.6 相关性反馈… 140
3.7.7 隐含语义索引… 140
3.7.8 学习评分… 141
3.7.9 查询与相关度… 142
3.7.10 使用Payload调整相关性… 142
3.8 查询原理… 146
3.8.1 布尔匹配… 147
3.8.2 短语查询… 150
3.8.3 索引统计… 150
3.8.4 相关性… 152
3.9 分析文本… 155
3.9.1 Analyzer 156
3.9.2 TokenStream.. 162
3.9.3 定制Tokenizer 164
3.9.4 重用Tokenizer 166
3.9.5 有限状态转换… 167
3.9.6 索引数值列… 168
3.9.7 检索结果排序… 171
3.9.8 处理价格… 171
3.10
Lucene中的压缩算法… 172
3.10.1 变长压缩… 172
3.10.2 Gamma. 174
3.10.3 PForDelta. 176
3.10.4 VSEncoding. 178
3.10.5 前缀压缩… 179
3.10.6 差分编码… 180
3.10.7 静态索引裁剪… 182
3.11
搜索中文… 182
3.11.1 Lucene切分原理… 185
3.11.2 Lucene中的Analyzer 186
3.11.3 自己写Analyzer 188
3.11.4 Lietu中文分词… 191
3.11.5 字词混合索引… 191
3.12
搜索英文… 196
3.12.1 英文分词… 196
3.12.2 词性标注… 199
3.12.3 原型化… 201
3.13 索引数据库中的文本… 202
3.14 优化使用Lucene. 204
3.14.1 系统优化… 204
3.14.2 查询优化… 205
3.14.3 实现时间加权排序… 207
3.14.4 词性标注… 210
3.14.5 个性化搜索… 213
3.15 实时搜索… 213
3.16 语义搜索… 215
3.16.1 发现同义词… 215
3.16.2 垂直领域同义词… 219
3.16.3 同义词扩展… 219
3.16.4 语义标注… 225
3.17 本章小结… 225
3.18 术语表… 226
第4章 搜索引擎用户界面…. 227
4.1 实现Lucene搜索… 228
4.1.1 测试搜索功能… 228
4.1.2 加载索引… 229
4.2 搜索页面设计… 231
4.2.1 Struts2实现的搜索界面… 232
4.2.2 用于显示搜索结果的
Taglib. 234
4.2.3 实现翻页… 235
4.3 实现搜索接口… 238
4.3.1 编码识别… 238
4.3.2 布尔搜索… 241
4.3.3 指定范围搜索… 241
4.3.4 搜索结果排序… 242
4.3.5 索引缓存与更新… 243
4.4 实现分类统计视图… 249
4.4.1 单值列分类统计… 255
4.4.2 侧钻… 256
4.5 实现相似文档搜索… 257
4.6 实现AJAX搜索联想词… 259
4.6.1 估计查询词的文档频率… 259
4.6.2 搜索联想词总体结构… 259
4.6.3 服务器端处理… 260
4.6.4 浏览器端处理… 265
4.6.5 拼音提示… 267
4.6.6 部署总结… 267
4.7 推荐搜索词… 268
4.7.1 挖掘相关搜索词… 268
4.7.2 使用多线程计算相关
搜索词… 270
4.8 查询意图理解… 271
4.8.1 拼音搜索… 271
4.8.2 无结果处理… 272
4.9 集成其他功能… 272
4.9.1 拼写检查… 272
4.9.2 分类统计… 276
4.9.3 相关搜索… 281
4.9.4 再次查找… 284
4.9.5 搜索日志… 284
4.10
查询分析… 286
4.10.1 历史搜索词记录… 286
4.10.2 日志信息过滤… 286
4.10.3 信息统计… 287
4.10.4 挖掘日志信息… 289
4.10.5 查询词意图分析… 290
4.11
部署网站… 290
4.11.1 部署到Web服务器… 290
4.11.2 防止攻击… 292
4.12
手机搜索界面… 295
4.13
本章小结… 296
第5章 Solr分布式搜索引擎…. 297
5.1
Solr简介… 298
5.2
Solr基本用法… 299
5.2.1 Solr服务器端的配置与中文
支持… 300
5.2.2 数据类型… 304
5.2.3 解析器… 306
5.2.4 把数据放进Solr 307
5.2.5 删除数据… 312
5.2.6 查询语法… 313
5.3 使用SolrJ. 313
5.3.1 Solr客户端与搜索界面… 313
5.3.2 Solr索引库的查找… 315
5.3.3 分类统计… 317
5.3.4 高亮… 319
5.3.5 同义词… 322
5.3.6 嵌入式Solr 322
5.3.7 Spring实现的搜索界面… 323
5.3.8 索引分发… 331
5.3.9 Solr搜索优化… 333
5.4 从FAST Search移植到Solr 336
5.5
Solr扩展与定制… 337
5.5.1 缺省查询… 337
5.5.2 插件… 338
5.5.3 Solr中字词混合索引… 338
5.5.4 相关检索… 340
5.5.5 搜索结果去重… 341
5.5.6 定制输入输出… 344
5.5.7 聚类… 348
5.5.8 分布式搜索… 348
5.5.9 分布式索引… 352
5.5.10 SolrJ查询分析器… 353
5.5.11 扩展SolrJ. 360
5.5.12 扩展Solr 361
5.5.13 日文搜索… 364
5.5.14 查询Web图… 365
5.6
SolrNet 367
5.6.1 使用SolrNet实现全文搜索… 367
5.6.2 实现原理… 370
5.6.3 扩展SolrNet 371
5.7
Solr的PHP客户端… 373
5.8
Solr的其他客户端… 376
5.9 为网站增加搜索功能… 376
5.10
SolrCloud. 377
5.10.1 Zab协议… 377
5.10.2 ZooKeeper 377
5.10.3 使用SolrCloud. 379
5.10.4 SQL查询… 380
5.11
Solr原理… 381
5.11.1 支持Solr的中文分词… 381
5.11.2 缓存技术… 383
5.12
本章小结… 384
第6章 ElasticSearch分布式搜索
引擎…. 387
6.1 安装… 389
6.2 搜索集群… 390
6.2.1 Zen发现机制… 390
6.2.2 JGroups. 391
6.3 创建索引… 393
6.4
Java客户端接口… 396
6.4.1 创建索引… 398
6.4.2 插入数据… 398
6.4.3 索引库结构… 400
6.5 查询… 401
6.6 高亮显示… 405
6.7 分页… 406
6.8 中文搜索… 407
6.8.1 中文AnalyzerProvider 407
6.8.2 字词混合索引… 409
6.9 分组统计… 412
6.10
与爬虫集成… 413
6.11
Percolate. 413
6.12
权限… 414
6.13
SQL支持… 415
6.14
本章小结… 419
前 言
搜索引擎成为人们获取信息不可或缺的工具。大数据技术的发展推动了多机集群的分布式搜索引擎技术走向成熟。普通的机器就可以搭建分布式搜索引擎。一些开源的分布式搜索引擎系统在数据存储、数据分析等方面的功能越来越强大。本书希望用通俗易懂的语言,让任何对分布式搜索引擎技术感兴趣的读者都能够有所收获。
本书的很多内容来源于搜索引擎、自然语言处理、金融等领域的项目开发和教学实践。在此感谢开源软件的开发者们,他们无私的工作丰富了本书的内容。
本书的第1章介绍开发分布式搜索引擎所需要的基本算法;第2章介绍如何从头开始自己动手写一个简单的全文检索软件包;第3章介绍Lucene的基本使用方法及其原理;第4章介绍使用JSP或者Struts 2开发搜索引擎用户界面,以及用户界面常用的Taglib;第5章介绍Solr实现分布式搜索引擎的解决方案——SolrCloud,以及它对SQL查询的支持;第6章介绍如何使用基于Lucene的ElasticSearch实现分布式搜索引擎。
鉴于ElasticSearch处于快速发展中,一些新版本的具体使用情况可以加入QQ群460405445,进行讨论。
本书配套的光盘中提供了相关的源代码,有的来源于猎兔搜索多年的开发经验积累,有的是经典算法实现。其中很多源代码都可以直接用于项目实践。
本书适合需要具体实现搜索引擎的程序员使用,对于信息检索等相关领域的研究人员也有一定的参考价值,同时猎兔搜索技术团队已经开发出以本书为基础的专门培训课程和商业软件。目前的一些分布式搜索引擎软件仍然有很多功能有待完善,作者真诚地希望通过本书把读者带入分布式搜索引擎开发的大门并认识更多的朋友。
感谢早期合著者、合作伙伴、员工、学员的支持,给我们提供了良好的工作基础。在将来,希望我们的分布式搜索引擎代码和技术能够像雨后春笋一样快速生长。
本书由罗刚、崔智杰编著,另外参与本书编写的还有张晓斐、石天盈、张继红、张进威、刘宇、何淑琴、任通通、高丹丹、徐友峰、孙宽,在此一并表示感谢。
编
者
第2章 自己动手写全文检索
很多软件系统都需要对应的数据结构。信息检索中常用的数据结构是倒排索引。全文索引如图2-1所示。
图2-1 以词为基础的全文索引
倒排索引就是一个词到文档列表的映射。用HashMap实现的一个简单的倒排索引代码如下。
public class InvertedIndex {
Map> index =
new HashMap>(); //词和这个词在文档中出现的位置信息
//
索引文档
public
void indexDoc(String docName, ArrayList words) {
int
pos = 0;
for
(String word : words) {
pos ;
// 位置
List
idx = index.get(word);
if
(idx == null) {
idx
= new LinkedList();
index.put(word,
idx);
}
idx.add(new
Tuple(docName, pos));
System.out.println(“indexed
” docName ” : ” pos ” words”);
}
}
//
搜索
public
void search(List words) {
for
(String word : words) {
Set
answer = new HashSet();
List
idx = index.get(word);
if
(idx != null) {
for
(Tuple t : idx) { //找到了一些文档
answer.add(t.docName);
}
}
System.out.print(word);
for
(String f : answer) {
System.out.print(”
” f); //输出文件名
}
System.out.println(“”);
}
}
private
class Tuple { //元组
private
String docName; // 文档名
private
int position; // 位置
public
Tuple(String d, int position) {
this.docName
= d;
this.position
= position;
}
}
}
如果用户的查询中包含多个词,需要统计这些词在文档中出现的区间大小。区间越小的文档相关度越高。
public class Span {
public
int start; // 开始位置
public
int end; // 结束位置
public
Span(int s, int e) {
start
= s;
end
= e;
}
public
int length() {
return
end – start 1;
}
public
String toString() {
return
start “-” end;
}
}
建立索引往往很耗时,所以应把建立好的倒排索引保存到文件。查询之前先读入建立好的索引。
倒排索引由两个文件组成:一是文件存储倒排列表;另外一个是B树(Btree)存储词到倒排列表的映射。
索引的实现接口如下。
/** 一个索引应该实现的功能模板 */
public interface Index {
/**
使用数据库统计类构建索引 */
public
void build(DatabaseStatistics s);
/**
从文件加载倒排索引 */
public
void read(String filename) throws IOException;
/**
把内存中建好的倒排索引存入文件 */
public
void flush(String filename) throws IOException;
}
可以把创建索引和读入索引的方法分到不同的类实现,分别为IndexWriter和IndexReader类。
2.1 构 建 索 引
为了按词建立索引,需要对文档分词。把这个分词类叫作Analyzer。文档内容作为输入流Reader的实例传入。
public class Analyzer {
String[]
split(Reader reader) {
//对文档内容分词,并返回分词后的词数组
}
}
首先建立文档索引,也就是正排索引,然后建立倒排索引。
正排索引用到的DocConsumer类如下。
public class DocConsumer {
public
int docid; //文档编号
//词到频率的映射,频率存在长度是1的整数数组中
HashMap frequencyList;
int
words; //文档长度,也就是这个文档包含多少个词
}
然后建立倒排索引。倒排索引用到的Posting类如下。
public class Posting implements
Serializable {
public
int docid; //文档编号
public
int freq; //这个词在文档中出现了多少次
Posting(int
doc, int freq) {
this.docid
= doc;
this.freq
= freq;
}
}
这里把文档作为一个字符串,如果有多列,可以把文档作为一个自定义的对象。
public class Document {
public
String title; //标题列
public
String content; //内容列
}
为了更灵活地定义文档的结构,可以专门定义一个Field类。
public class Field {
/**
列名 */
public final String name;
/**
列值 */
public String fieldsData;
}
一个Document类的实例中可以包含多个Field类的实例。
在真正的信息检索中,都是有多个列的,而且很多列有同等重要的地位。对于多个列的检索,简单的方法是:给每个倒排列附加一个bit-map,假设有N列,则Bitmap长度是N。例如,title出现,则第1位为1,content没出现,则第2位为0。实际的做法是:把一个列中所有的词连续存放到一起,一个专门的列定义文件中包括某个列中这些词信息的开始位置,如图2-2所示。
图2-2 多列索引
2.2 生成索引文件
词表是类似这样的数据结构:SortedMap。如果词表很小,内存能够放下,则可以使用折半查找算法来查询一个词对应的文档序列。如果内存不能完全放下倒排索引中的词表,如何利用索引文件查找?理论上,可以采用B树存储词表。为了简化实现,Lucene 3以前的版本使用了跳表。跳表把词组织成固定大小的块,例如每32个词放入一个新的块,然后在块上建立一个索引。记住每个块的开始词在文件中的位置。
但是,更好的分块方式是根据词共享了哪些前缀。按前缀组织词产生的词块大小是变化的。把相同前缀的词放到一起比按顺序放更好,这样在每个区块内,可化共享前缀的词,并且减少了产生的词索引。
这样做也可以加速词密集的查询。因为词索引成为了前缀Trie树。如果词典中不包含这个词,可以快速报告:“没有找到。”不需要在查找某一个词块之后才能确定没有。
这种方法叫作BlockTree。例如,词表中包含如下一些词。
able
above
apple
perfect
preface
prefecture
prefix
previous
profit
programmer
project
zoo
把a开头的3个词组织成一个词块:{able,above,apple}。p开头的词有8个,超过4个。把pre开头的4个词组织成一个词块:{ preface, prefecture, prefix , previous }。把pro开头的3个词组织成一个词块:{
profit, programmer, project }。因为以z开头的词只有1个,所以zoo单独组成一个词块。BlockTree索引如图2-3所示。
图2-3 BlockTree索引
根据有限状态转换找到词块在文件中存放的位置,如图2-4所示。
图2-4 有限状态转换
BlockTreeTermsReader类用于读索引。BlockTreeTermsWriter类用于写索引。
对于主键列来说,可以把所有的词和postings一起存到一个FST中。把FST保存到硬盘。
可以利用BlockTree的结构加快排序的速度。
2.3 读入索引文件
如果词表太大,则只读入上面几层的索引。为了批量读入数据,把数据分页存放。参考B树索引的分页读取。
public class Page{
public
Page() {
data
= new byte[MAX_SPACE];
}
public
Page(byte[] apage) {
data
= apage;
}
/**
*返回数据的字节数组
*
* @return页面的字节数组
*/
public
byte[] getpage() {
return
data;
}
/**
*用给定的字节数组设置页面
*
* @param array
* 页面大小的字节数组
*/
public
void setpage(byte[] array) {
data
= array;
}
public
byte[] getData() {
return
this.data;
}
/**
*受保护的属性:字节数组
*
*/
protected
byte[] data;
}
评论
还没有评论。