AJAX LUCENE 构建搜索引擎 Roger Xia
Ajax & Lucene Ajax Google Suggest  提示信息 Google Map  缩放,拖拽 网页响应速度更快,可以拖动,异步处理 Lucene 当前商业型项目中都需要有搜索的功能 有些可以通过 SQL 来实现搜索 对于全文检索(如对一个公司 5 年来所有报表数据和会议记录的搜索),数据库的速度和性能无法满足需求 购买 Google 等公司的搜索服务 自己开发搜索引擎  ebay … 使用 Apache 开源全文检索工具包 Lucene 方便地构建自己的搜索引擎 Eclipse 就是使用 Lucene 作为其内建的搜索工具
AJAX = Asynchronous +  JavaScript  + XML + CSS 浏览器的 JavaScript 对象模型 Location 对象:访问加载在窗口中的文档的 URL ,可以用 location 对象从当前网页定位到另一个网页, 如: location.href=“ http:// www.baidu.com ” History 对象:查看最近访问过的网址列表, 如: history.go(), history.back()
JavaScript Object-based:  直观 / 模块化 / 可复用 JavaScript 并不是完全面向对象(封装 / 复合 / 继承 / 多态,等)的语言 最简单的创建 JavaScript 对象的方法:调用 Object 类的内建构造函数 var myObject = new Object(); 创建了一个 Object 类型的对象 myObject, myObject 只是一个空的对象,没有任何属性和方法。 JavaScript 对象只是一组关联的数组,对象的属性 ( 变量 ) 和方法 ( 函数 ) 是可以通过数组下标和名字引用的。
JavaScript 对象 可读写 var person = new Object(); // 通过“ .” 给对象添加属性 person.name=“Roger”; person.sex=“male”;  // 通过数组下标引用 person[0]=“Roger”; person[1]=“male”; // 通过字符串实现 person[“name”]=“Roger”; person[“sex”]=“male”; 使用“ =” 操作符除了可以向对象添加 / 修改属性,还可以将方法动态地添加到一个对象,如: person.sayHello = function() { alert(“Hello, my name is ” + this.name); }
使用 JSON 创建数组和对象图 JavaScript 对象的某一个成员也可以是一个对象 对象结构复杂 可读性 / 可维护性差 JavaScript Object Notation 是 JavaScript 的核心特性 var cookbook=new Object(); cookbook.pageCount=100; cookbook.author={ firstName: “Harry”, secondName: “Christmas”, birthdate: new Date(1900,2,29), interests: [“cheese”, “basketball”, “music”] }
JavaScript 构造函数 function Person (name, age){ this.name=name; this.age=age; this.sayHello=function() { alert(“my name is ” + this.name); } } var myObj = new Person(“Roger”, 27); Java 语言中所有类都继承自 java.lang.Object 类 JavaScript 没有 Java 这种继承的概念,所有 JavaScript 对象都是同一个基类的实例,这个基类拥有在运行时将成员函数和变量动态邦定到自身的能力。 上面的代码缺点: a).  对于创建的每一个 Person 类的实例,都会创建一个新的 sayHello 函数,这对内存是一个极大的浪费,如果在程序中创建大量这样的对象,内存泄露问题会变得尤为明显。 b).  在构造函数中创建一个闭合区域,在涉及到 DOM 时,将会产生严重的问题。 更为安全的替代方案:通过使用 prototype 变得更加面向对象化
JavaScript -- prototype 通过使用构造函数的 prototype ,属性和行数可以关联到对象上。 function Person (name, age){ this.name=name; this.age=age; } Person.prototype.sayHello=function() { alert(“my name is ” + this.name); } var myObj = new Person(“Roger”, 27); myObj.sayHello(); 注意执行顺序:通过 prototype 向 JavaScript 对象添加的属性和方法是在构造函数调用的那一刻关联到所创建的对象上去的 。
JavaScript 对象反射 确定对象是否有某个属性: if (typeof(myObject.someProperty) != “undefined”){ …… } 测试某个对象的类型 instanceof if (myObj instanceof Array){ …… }
Rich Client “ 胖客户端”:相对于“瘦客户端”而言,在客户机器上安装配置的一个功能丰富的交互式用户界面。 MSN, IE, Oracle, DB2 数据库管理工具 传统的 Web 应用程序:客户端需进行页面跳转或刷新 Ajax 解决了:负载过大,响应时间长,同步交互过程带来的感觉不连贯
Ajax 技术使用 XMLHTTPRequest 对象发送请求并得到服务器响应,得到返回的数据后用 JavaScript 操作 DOM 来使页面更新
Cascading Style Sheets 优点:提供了从内容中分离应用样式和设计的机制 缺点:是构建跨浏览器应用的一大阻碍,不同的浏览器厂商支持不同的 CSS 级别。 随着 XML 的引入,数据描述成为单独的功能,而样式表则负责显示,内容和样式的分离给开发带来的极大地方便:可读性,一次性写入 W3C 对 CSS 标准的改进,与各浏览器版本兼容性
CSS 样式表由样式规则组成,样式规则包括:选择符和样式定义 选择符 { 属性 1:  值 1 ; /* 注释 */ 属性 2:  值 2 } 申明分组,继承
内部样式表 <html> <head><title> 内部样式表演示 </title> <style type=“text/css”> h1,h2,h3 { color: blue } /* 选择符分组 */ h1 { /* 属性分组 */ font-weight: bold; font-size: 30pt; font-family:  宋体 } h2 { /* 属性值分组 */ font: bold 28pt  宋体 } </style> </head> <body> ……</body> </html>
外部样式表 两种使用方式:连接到样式表上; 输入样式表。 ===out.css=== body { background-color: pink }  p { margin-left: 20%; margin-right: 20%; font-family:  宋体 ; font-size: 14 } <html> <head><title> 链式样式表 </title> <link rel=stylesheet type=“text/css” href=“out.css”> </head> <body> ……</body> </html> href 可以是相对路径或绝对路径 === 输入外部样式表 @import 可与其他方法结合使用,文档中定义的其他样式表格则 将覆盖输入样式表中作用在相同对象上的规则
样式类 样式的分类,可根据不同的风格需要对某一类型的标记设置几种不同的 CSS 属性。 第一种格式: 标记 . 样式类名 { 属性 1:  属性值 1;  属性 2:  属性值 2;  属性 3:  属性值 3} 第二种格式: 样式类名 { 属性 1:  属性值 1;  属性 2:  属性值 2;  属性 3:  属性值 3} 第二种方式规定的样式类没有特定于某一类标记,任何类型的标记都可以使用该样式类。 使用: < 标记  class=“ 样式类名” >
CSS 中的滤镜 filter: filtername (parameters) filter 是滤镜属性选择符 filtername 是滤镜属性名
DOM 与 JavaScript DHTML— 使用 JavaScript 操作页面内容的过程 DOM— 文档对象模型,能够更好的控制窗口内容的方法
使用 JavaScript 读取 XML 文档 <basic country=“China”> <name num=“1”>
Ajax 工作流程 Ajax 是使用客户端脚本与 Web 服务器交换数据的 Web 应用开发方法。这样 Web 页面不用打断交互流程进行重新加载,就可以动态地更新。有关数据验证和数据处理都由 Ajax 引擎来做,需要从服务器读取新数据时再由 Ajax 引擎代为向服务器提交请求 使用 JavaScript 中 XMLHttpRequest 对象(支持异步请求的技术)可以向服务器提出请求并处理响应,这个过程不会阻塞用户的其他操作。
使用 XMLHttpRequest 发送请求的过程
监视 Response 的状态 通过回调函数—监视浏览器的状态  req.onreadystatechange = callback; // 指定回调函数 操纵返回的数据  req.responseText  req.responseXML function sendRequest(callback, url) { // branch for native XMLHttpRequest object if (window.XMLHttpRequest) { req = new XMLHttpRequest(); // 创建 XMLHttpRequest req.onreadystatechange = callback; // 指定回调函数 req.open(“GET”, url, true);  // 建立请求 req.send(null); // 如有参数,则 method 必须是 post } // branch for IE/Windows ActiveX version else if (window.ActiveXObject) { req = new ActiveXObject(“Microsoft.XMLHTTP”); if (req) { ……  //  同上 } } } function callback() { if  (req.onreadystate == 4) { // 判断就绪状态 if (req.status == 200) { parseMessage();  // 接收返回的数据 } else { alert(“Not able to retrieve description” + req.statusText) ; } } else {} } function parseMessage() { var xmldoc = req.responseXML;  // 获取返回的结果 var name = xmldoc.getElementByTagName(“author”); // 获取文件中 <author> 标记 var content = document.getElementById(“display”); // 获取网页中 <display> 标记 content.innerHTML = name;  // 在网页中显示 }
Ajax 实例 提示等待 级联下拉框 Google suggest Slider (滑块) 提示等待: 在状态值不等于 4 时对 HTML 代码进行一些修改,让页面显示一些提示信息。 级联下拉框: 控件 B 的内容取决于控件 A 的内容,且控件 B 的内容需要根据控件 A 的内容再从服务器端获得相应数据。 在 A 的  onChange  事件发生时调用一个函数,创建 XMLHttpRequest 对象来和服务器进行交互,向服务器发送请求,参数为下拉框 A 中选定的内容。客户端在接收到服务器处理后的数据后,先对数据进行处理,再显示到下拉框 B 中。 滑块  --  是一个可以随意拖动而改变相应值的控件 拖动按钮得到一个新的范围后,就创建一个 XMLHttpRequest 对象来和服务器进行交互,参数就是滑块当前指定范围的最小值和最大值。
Google suggest 当在文本框中输入查询内容时,文本框控件会响应  onkeyup  事件, 在这个事件中调用 javascript 函数来创建 XMLHttpRequest 对象和服务器进行交互,向服务器发送请求,参数为文本框中的内容。服务器接收到请求后,将参数作为查询条件得到查询结果返回到客户端。客户端接收到这些返回的数据后,在文本框下方构造一个列表,来显示这些匹配查询条件的可选值。在显示过程中,还需对用户按键做出一系列判断,实现友好交互。 定义样式文件 style.css 用来控制下拉提示框、匹配的文本等的显示样式 build.js
Ajax 的安全问题 Ajax 的安全问题和性能问题 JavaScript 和浏览器的安全性 客户端脚本语言(安全性问题)  --  当访问一个使用 Ajax 技术构建的 Web 应用时, Web 服务器会将一系列 JavaScript 脚本代码发送到每一个访问者的机器上,然后用户浏览器便会在本地执行这些代码 1). JavaScript 修改系统设置(注册表,格式化硬盘,恶意破坏) 2). IFrame 问题:网页中 IFrame 指定的元素可以从其他域加载网页并执行 HTML 代码,相当于嵌在网页内部的一个独立框架窗口,但不同域的 JavaScript 却不能互相访问。
JavaScript 的性能问题(解释型语言) 1).  循环, 效率: while()  better than  for( ; ; )  better than  for( in )   查询散列键 2).  局部变量 ( 放在函数的堆栈中 ) 访问速度  better than  全局变量 3).   少用 eval ,使用 eval 相当于运行时再次调用解释引擎对内容进行解释运行。可使用 JavaScript 闭包实现模版代替 eval 函数。 4).  使用局部变量,减少对象查找(解释语言) var len = a.length; 5). s += a; better than s = s + a; s += a + b +c; better than s+=a; s+=b; s+=c; 6).  类型转换(弱类型), ( “” + ) > String() > .toString() > new String() 7).  使用直接量, var foo = { };  解释引擎直接解释  > var foo = new Object();  内部构造器 var reg = /…/; faster than var reg = new RegExp() 8).  对字符串进行的循环操作,如替换、查找,应优先使用正则表达式( C 语言写的 API ) 9).  复用或缓存以减少创建高级对象,如: Date 、 RegExp 10).  直接使用下标访问快于使用“ .” 11).  创建 DOM 节点:使用字符串直接写 HTML 语句来创建节点 效率低于 使用 document.createElement() 方法, 如果文档中存在现成的样板节点,可以使用 cloneNode() 来减少多次设置元素的属性。
搜索引擎架构 --Lucene http://lucene.apache.org/ Roger Xia
当前商业型项目中都需要有搜索的功能 有些可以通过 SQL 来实现搜索 对于全文检索(如对一个公司 5 年来所有报表数据和会议记录的搜索),数据库的速度和性能无法满足需求 购买 Google 等公司的搜索服务 自己开发搜索引擎  ebay … 使用 Apache 开源全文检索工具包 Lucene 方便地构建自己的搜索引擎 (Eclipse 就是使用 Lucene 作为其内建的搜索工具 )
信息获取 与搜索引擎 信息获取: 包含信息的表示、存储、组织和对信息的访问方法。 数据 索引 检索 排序 a.   构造文本数据库 ,存放将来需要检索的数据 b.   建立文档的索引 ,用来提高信息检索速度。如,倒排索引技术 c. 利用文本处理技术 分析用户的查询 。在进行查询之前还可以进行一些与处理。 d.   对检索结果进行处理 (排序) DB/File 客户输入查询条件 反馈客户查询结果 信息
信息获取与 搜索引擎 搜索引擎: 全文搜索引擎 ( FullText Search Engine )   :通过一个叫网络机器人或叫网络蜘蛛的软件,自动分析网络上的各种链接并获取网页信息内容,按规则加以分析整理,记入数据库。典型的系统有: Google 和百度 分类目录 ( Directory )   :则是 通过人工的方式收集整理网站资料形成的数据库 ,如雅虎中国、搜狐、新浪、网易等网站的分类目录。 第一代搜索引擎:依靠人工分拣的分类目录搜索, e.g. yahoo 第二代搜索引擎:依靠机器抓取,并建立在超级链接分析技术基础之上的网页搜索, e.g. Google 特点:信息量大,更新及时,但返回无关信息过多 第三代搜索引擎:融入“智能化”,“人机交互”功能。将自动分类技术,中文内容分析技术及区域识别技术应用到大型搜索引擎中 。 信息更新速度快,频率高 网页相关检索,拼音纠错,模糊查询,语音查询技术有很高水准。 此外,还同时兼备了新闻, MP3 ,图片, Flash 搜索功能。
全文检索 全文检索:计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序根据事先建立的索引进行查找,并将查询结果反馈。 按字检索 ,对英文,字与词合一,中文有很大区别; 按词检索 ,对语义单位建立索引,检索时按词检索,并可以处理同义项。   中文文字的切分字词是全文检索的技术难点。
全文检索系统 全文检索系统:具有 建立索引 、 处理查询返回结果集 、 增加索引 、 优化索引结构 等功能,外围则由各种不同应用具有的功能组成。 查询引擎 文本处理引擎 索引引擎 二次开发应用接口 应用程序 Web 程序 其他 磁盘索引文件
全文检索 ≠  like &quot;%keyword%&quot;   由于数据库索引不是为全文索引设计的, 当使用 like “%word%” 时,数据库索引是不起作用的 ,所以对于含有模糊查询的数据库服务来说, LIKE 对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配: like “%word1%“ and like ”%word2%” 其效率也就可想而了。 建立一个高效检索系统的 关键是建立一个反向索引机制 ,将数据源(比如多篇文章)排序顺序存储的同时,有另外一个排好序的关键词列表,用于存储关键词 => 文章映射关系, 利用这样的映射关系索引: [ 关键词 => 出现关键词的文章编号,出现次数,出现频率 ]  ,检索过程: 把模糊查询变成多个可以利用索引的精确查询的 逻辑组合 的过程 。
Lucene Lucene 是一个开放源代码的全文检索引擎工具包,是 Apache 软件基金会 Jakarta 项目的子项目。 Lucene 不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供完整的查询引擎和索引引擎及部分文本分析引擎(最初:英文、德文)。 不同程序语言版本 (.Net , C , for script language: ruby, python)
Lucene 全文检索实现机制 Lucene 的 API 接口设计的比较通用,输入输出结构都很像数据库的 表 ==> 记录 ==> 字段 ,所以很多传统的应用的文件、数据库等都可以比较方便的映射到 Lucene 的存储结构 / 接口中。总体上看:可以先把 Lucene 当成一个支持全文索引的数据库系统 。 RecordSet :查询结果集,由多个 Record 组成 Hits :查询结果集,由匹配的 Document 组成 2.3 -> TopDocCollector collector = new TopDocCollector(10); Field :字段 Field :字段 Record :记录,包含多个字段 Document :一个需要进行索引的“单元” 一个 Document 由多个字段组成 索引数据源: record(field1,field2...) record(field1..)   \  SQL: insert/   _____________   | DB  Index  |   -------------   / SQL: select \ 结果输出: results(record(field1,field2..) record(field1...)) 索引数据源: doc(field1,field2...) doc(field1,field2...)   \  indexer /   _____________   | Lucene Index|   --------------   / searcher \   结果输出: Hits(doc(field1,field2) doc(field1...)) Database Lucene
全文检索 和 数据库应用 最大的 不同 在于: 让最相关的头 100 条结果满足 98% 以上用户的需求 。 没有接口或接口复杂,无法定制 。 通过不同的语言分析接口实现,可以方便的定制出符合应用需要的索引规则(包括对中文的支持) 可定制性  使用率低,模糊匹配规则简单或者需要模糊查询的资料量少 高负载的模糊查询应用,需要复杂的模糊查询规则,索引的资料量比较大 结论 返回所有的结果集,在匹配条目非常多的时候需要大量的内存存放这些临时结果集。  通过特别的算法,将最匹配度最高的头 100 条结果输出,结果集是缓冲式的小批量读取的。  结果输出  没有匹配程度的控制:比如有记录中 net 出现 5 词和出现 1 次的,结果是一样的。  有匹配度算法,将匹配程度(相似度)比较高的结果排在前面。  匹配度  使用: like “%net%”  会把 netherlands 也匹配出来;使用 like &quot;%com%net%&quot; :就不能匹配词序颠倒的 xxx.net..xxx.com  通过词元 (term) 进行匹配,通过语言分析接口的实现,可以实现对中文等非英语的支持。 匹配效果 对于 LIKE 查询来说,数据传统的索引是根本用不上的。  将数据源中的数据都通过全文索引一一建立反向索引  索引 数据库   Lucene 全文索引
Lucene 的创新之处 大部分的搜索(数据库)引擎都是用 B 树结构来维护索引,索引的更新会导致大量的 IO 操作。 Lucene 对此稍微有所改进: 不是维护一个索引文件,而是在扩展索引的时候不断创建新的索引文件,然后定期的把这些新的小索引文件合并到原先的大索引中 (针对不同的更新策略,批次的大小可以调整),这样在不影响检索的效率的前提下,提高了索引的效率。 Lucene 并没有规定数据源的格式,而只提供了一个通用的结构( Document 对象)来接受索引的输入,因此输入的数据源可以是:数据库, WORD 文档, PDF 文档, HTML 文档……只要能够设计相应的解析转换器将数据源构造成 Docuement 对象即可进行索引。
索引 与 搜索 索引   --  特殊的数据结构,提高搜索效率,不支持快速信息更新,定期更新索引。 索引技术:倒排索引(对于关键词的搜索非常有效, Lucene ), 后缀数组(短语查询速度快,数据结构构造维护复杂), 签名文件(流行于 20 世纪 80 年代,被倒排索引超越) 搜索  – 为用户提供高质量(响应时间、准确度)的搜索结果。高效的搜索算法及对搜索结果丰富的排序方法
倒排索引 文档 A : This is a dog B : This dog is a kind of animal 如右图 通过比较: 一般的索引结构建立的是一种“文档到单词”的映射关系, 而倒排索引建立的则是一种“单词到文档”的映射关系。 用户在进行检索时,都是输入关键字进行查询,如果使用一般的索引结构,在查询某一关键字时需要遍历所有索引,索引量大时,效率差。 1 B kind 1 kind B 1 dog B 1 B animal 1,1 A,B dog 出现次数 出现的文档 单词 倒排索引结构 1 animal B 1 dog A 出现次数 出现单词 文档编号 一般索引结构
Lucene 使用 Lucene 来为本地文件建立索引
建立索引
Lucene 建立索引过程 1).  提取文本: 从各种文档中提取文本信息 2).  构建 Document 将提取出的文本组装成 Lucene 可以识别的格式来为索引建立做准备 3).  分析 对要建立索引的数据进行分析,使建立索引时更容易处理数据 4).  建立索引 调用 IndexWriter 类的 addDocument() 方法建立索引。
Lucene -- Document Lucene 的 Document 代表了一个需要进行 索引的“单元”, 任何需要进行索引的数据源(如:文件) 都必须被转化成 Document 对象 才能够被索引和搜索到。
Lucene -- IndexWriter 功能:将文档加入索引,同时控制索引过程中的各种参数。 初始化: 参数: 索引存放的路径: String, java.io.File, Directory IndexWriter writer1 = new IndexWriter(“c:\\index1”, new StandardAnalyzer(), true); File f = new File(“c:\\index2”); IndexWriter writer2 = new IndexWriter(f, new StandardAnalyzer(), true); Directory d = new RAMDirectory(); IndexWriter writer3 = new IndexWriter(d, new StandardAnalyzer(), true); 分析器:在 IndexWriter 将文档写入索引前,把文本信息切分成一个个可以进行索引的词条。继承自 org.apache.lucene.analysis.Analyzer 类的分析器 重新创建索引 true/ 增量索引 false
Lucene -- IndexWriter 向索引添加文档 调整性能参数 1. mergeFactor :默认值为 10 ,用来控制 lucene 在把索引从内存写入磁盘上的文件系统时内存中最大的 Document 数量及最大 Segment 数量。影响建立索引时所花费的磁盘 I/O 时间和内存。设置太小, I/O 操作频繁,经常进行 segment 合并,设置太大,内存中持有的 Document 数量多,建立索引快,但大量占用内存。 public   void  setMergeFactor( int  mergeFactor) { getLogMergePolicy().setMergeFactor(mergeFactor); } 2. maxMergeDocs :限制一个 segment 中最大的文档数量。 public   void  setMaxMergeDocs( int  maxMergeDocs) { getLogMergePolicy().setMaxMergeDocs(maxMergeDocs); } 限制 Field 长度: maxFieldLength ,默认值为 10000 ,只对前 10000 个 term 索引。
Lucene 的索引文件格式 规定了 Lucene 的文件格式采取的存储单位、组织结构、命名规范等等内容 索引文件格式:多文件索引文件格式、复合索引格式 setUseCompoundFile( boolean  isCompoundFile)
Lucene 文件格式中定义的数据类型 在 Lucene 的文件格式中,以字节为基础,并且定义了自身的数据类型 . 由于它们都以字节为基础定义而来,因此保证了是平台无关,这也是 Lucene 索引文件格式平台无关的主要原因 .
Lucene 索引文件概念结构 Segment :一个段是一个独立的索引,可以由 IndexSearcher 进行单独查找。 Lucene 的工作其实就是不停地往磁盘加入新的 Segment ,并按一定的算法合并不同的 Segment 以创建一个新的段。 项( Term )是最小的索引概念单位,它直接代表了一个字符串以及其在文件中的位置、出现次数等信息。 域( Field )是一个关联的元组,由一个域名和一个域值组成,域名是一个字串,域值是一个项
Lucene 索引文件结构组成 域集合与项集合中的文件组采用了一种类似的存储办法:一个小型的索引文件,运行时载入内存;一个对应于索引文件的实际信息文件,可以按照索引中指示的偏移量随机访问; 索引文件与信息文件在记录的排列顺序上存在隐式的对应关系,即索引文件中按照“索引项 1 、索引项 2…” 排列,则信息文件则也按照“信息项 1 、信息项 2…” 排列。
一共有四个文件( .tii, .tis,.frq,.prx )用来记录 Term 相关的信息: --  在 .tis 文件中,对每个 Term 都有一项 TermInfo, 而且 TermInfo 是根据字典顺序排序。 TermInfo 中记录的了 Term 本身,以及一些以在 .frq 和 .prx 中和该 Term 相关详细的指针。 -- .tii 是对 .tis 的一个索引,用来加速对 Term 的查找。 -- .frq 文件记录了每个 Term 出现的文档号以及其频率, -- .prx 文件记录了每个 Term 在每篇文档中出现的位置信息。
索引存储抽象  --  索引存放位置 磁盘  –  FSDirectory  将索引存放在文件系统中, Lucene 会自动在内存中建立缓存,然后到一定时候就写入磁盘。 IndexWriter writer = new IndexWriter(FSDirectory.getDirectory(path, true), new StandardAnalyzer(), true); 内存  –  RAMDirectory ,速度快,非持久化(虚拟机退出,索引丢失),因此需一方法将内存中的索引转入磁盘。 IndexWriter writer = new IndexWriter(RAMDirectory.getDirectory(), new StandardAnalyzer(), true);
索引合并 索引的 合并 :当开发者在不同的地方创建许多索引,某种情况下,需进行合并。 public   void  addIndexes(Directory[]  dirs ) , 可以合并 FSDirecotry 和 RAMDirectory ,也可以合并两个 FSDirectory ramWriter.addDocument(doc1); fsWriter.addDocument(doc2); ramWriter.close();// 索引创建完毕,关闭内存所引器,使所有文档进入内存索引文件 fsWriter.addIndexes(new Directory[]{fsDir}); fsWriter.close(); 删除文档格式:删除特定文档、按 Field 来删除 IndexReader deleteDocuments(int docNum) ,  undeleteAll() deleteDocuments(Term term)
Lucene 索引优化、同步机制 optimize() :通过优化可以减少一个搜索器要同时打开的文件数,降低 I/O 。 将磁盘上多个 Segment 进行合并,组成一个全新的 Segment ,对磁盘空间需求大,应在大批量索引建立完成后再进行调用,注意索引优化周期。 多线程环境下的同步机制(同步法则): 任何数量的只读操作可以被同时执行,如使用 IndexSearcher 进行检索 任何数量的只读操作都可以在索引正在被修改时执行,如,往索引中新加文档、删除旧文档或索引优化过程 同一时间内,只可以有一个修改索引操作。 IndexWriter/IndexReader Lucene 的索引“锁”: Lucene 设置了 文件锁 ,默认放置在系统临时文件夹下,该文件夹路经由 java.io.tempdir 系统变量指定。 writer.lock :防止多线程同时修改一索引文件,由 IndexWriter 初始化时获得,在关闭时释放。 commit.lock :在索引的 Segment 被建立、合并或读取时生成。
Lucene 搜索 搜索的流程 初始化 IndexSearcher :  Searcher searcher = new IndexSearcher(indexDir); 构建 Query : 如查询方式(模糊查询、语义查询、短语查询、范围查询、 组合查询等)  Query query = QueryParser.parse(queryString) 搜索并处理返回结果 : Hits hits = searcher.search(query);  排序、过滤 Web 开发时,将 Lucene 与数据库相结合,在索引中存入一些关键的 ID 字段、路径字段或是简单的文本,真正的数据提取则从数据库中得到。
检索结果  -- Hits Lazy load result, LRU doubly-linked cache
Lucene 的评分机制 评分机制就是对检索结果按某种标准进行评估,然后按分值的高低来对结果进行排序。 对于一个商用的搜索引擎来说,评分机制是其收入来源的一部分。 Lucene 评分算法:(简单理解成某个关键字在文档中出现的频率) 可以通过 Document 的 setBoost(float boost) 来改变文档 boost 因子,进而改变文档评分
构建 Query 按词条搜索 –  TermQuery Term term = new Term(“contents”, “java”); Query query = new TermQuery(term); BooleanQuery public void add(BooleanClause clause) RangeQuery public RangeQuery(Term lowerTerm, Term upperTerm, boolean inclusive) PrefixQuery public PrefixQuery(Term prefix) 多关键字搜索  --  PhraseQuery public void add(Term term) public void setSlop(int s)  // Sets the number of other words permitted between words in  //query phrase.  If zero, then this is an exact phrase search. 相近词语的搜索 –  FuzzyQuery 通配符搜索 –   WildcardQuery   // *, ? …… 解析用户输入字符串  --  QueryParser
Lucene 分析器 管道过滤器模式 :分析器 (Analyzer) 使用分词器 (Tokenizer) 和过滤器 (TokenFilter) 构成一个管道,文本在“流过”这个管道后,就成为可以进入索引的最小单元。 JavaCC 与 StandardAnalyzer(JavaCC 为 Lucene 构建的分析器 ) TokenStream Tokenizer TokenFilter
Lucene 内建分析器
高级搜索技巧 对搜索结果的排序 多域搜索和多索引搜索 对搜索结构的过滤
对搜索结果的排序 例如: 把查询结果按照类别进行分组 在组内再按照与查询的相关度进行排序
搜索引擎的中文问题 关于亚洲语言的的切分词问题 对于中文来说,全文索引首先还要解决一个语言分析的问题。英文语句中单词之间是天然通过空格分开的,但亚洲语言的中日韩文语句中的字是一个字挨一个,所有,首先要把语句中按“词”进行索引的话,这个词如何切分出来就是一个很大的问题。 首先,肯定不能用单个字符作 (si-gram) 为索引单元,否则查“上海”时,含有“海上”也能匹配。 但一句话:“北京天安门”,计算机如何按照中文的语言习惯进行切分呢? “北京 天安门” 还是“北 京 天安门”?让计算机能够按照语言习惯进行切分,往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。 另外一个解决的办法是采用自动切分算法:将单词按照 2 元语法 (bigram) 方式切分出来,比如: “北京天安门”  ==> “ 北京 京天 天安 安门”。 这样,在查询的时候,无论是查询“北京” 还是查询“天安门”,将查询词组按同样的规则进行切分:“北京”,“天安门”,多个关键词之间按与“ and” 的关系组合,同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言:韩文,日文都是通用的。 基于自动切分的最大优点是没有词表维护成本,实现简单,缺点是索引效率低。
中文分词 lucene 自身提供的 StandardAnalyzer 已经具备中文分词的功能,但是不一定能够满足大多数应用的需要。 另外我们谈的比较多的中文分词器还有: CJKAnalyzer  二元分词 ChineseAnalyzer  基本等同 StandardAnalyzer IK_CAnalyzer ( MIK_CAnalyzer ) 还有一些网友自己写的比较不错的分词器 paoding ,有兴趣的可以自己研究研究。 CJKAnalyzer 和 ChineseAnalyzer 分别是 lucene-2.4.0 目录下 contrib 目录下 lucene-analyzers-2.4.0 的 analyzers 提供的。分别位于 cn 和 cjk 目录。 IK_CAnalyzer ( MIK_CAnalyzer )是基于分词词典,目前最新的 1.4 版本是基于 lucene2.0 开发的。 正向全切分分词器: org.mira.lucene.analysis.IK_CAnalyzer (适合建索引时使用)  正向最大全切分分词器: org.mira.lucene.analysis.MIK_CAnalyzer (适合用户输入检索时使用)
用 lucene 和 Heritrix 构建了一个 Web  搜索应用程序 Lucene  是基于  Java  的全文信息检索包,它目前是  Apache Jakarta  家族下面的一个开源项目。 Lucene 很强大,但是,无论多么强大的搜索引擎工具,在其后台,都需要一样东西来支援它,那就是网络爬虫 Spider 。网络爬虫 Spider ,或称网络机器人、 BOT 等,由于爬虫的存在,才使得搜索引擎有了丰富的资源。 Heritrix 是一个纯由 Java 开发的、开源的 Web 网络爬虫,用户可以使用它从网络上抓取想要的资源。它来自于 www.archive.org 。 Heritrix 最出色之处在于它的可扩展性,开发者可以扩展它的各个组件,来实现自己的抓取逻辑。
系统架构 在前端流程中,用户在搜索引擎提供的界面中输入要搜索的关键词,这里提到的用户界面一般是一个带有输入框的  Web  页面,然后应用程序将搜索的关键词解析成搜索引擎可以理解的形式,并在索引文件上进行搜索操作。在排序后,搜索引擎返回搜索结果给用户。在后端流程中,网络爬虫从因特网上获取  Web  页面,然后索引子系统解析这些  Web  页面并存入索引文件中。
DB  全文检索 Mysql Sql server
Hibernate Lucene Integration   http://wiki.redsaga.com/confluence/display/HART/Hibernate+Lucene+Integration Lucene 使用者沙龙 http://blog.cnblog.org/archives/2005/07/luceneaecee.html

Ajax Lucence

  • 1.
  • 2.
    Ajax & LuceneAjax Google Suggest 提示信息 Google Map 缩放,拖拽 网页响应速度更快,可以拖动,异步处理 Lucene 当前商业型项目中都需要有搜索的功能 有些可以通过 SQL 来实现搜索 对于全文检索(如对一个公司 5 年来所有报表数据和会议记录的搜索),数据库的速度和性能无法满足需求 购买 Google 等公司的搜索服务 自己开发搜索引擎 ebay … 使用 Apache 开源全文检索工具包 Lucene 方便地构建自己的搜索引擎 Eclipse 就是使用 Lucene 作为其内建的搜索工具
  • 3.
    AJAX = Asynchronous+ JavaScript + XML + CSS 浏览器的 JavaScript 对象模型 Location 对象:访问加载在窗口中的文档的 URL ,可以用 location 对象从当前网页定位到另一个网页, 如: location.href=“ http:// www.baidu.com ” History 对象:查看最近访问过的网址列表, 如: history.go(), history.back()
  • 4.
    JavaScript Object-based: 直观 / 模块化 / 可复用 JavaScript 并不是完全面向对象(封装 / 复合 / 继承 / 多态,等)的语言 最简单的创建 JavaScript 对象的方法:调用 Object 类的内建构造函数 var myObject = new Object(); 创建了一个 Object 类型的对象 myObject, myObject 只是一个空的对象,没有任何属性和方法。 JavaScript 对象只是一组关联的数组,对象的属性 ( 变量 ) 和方法 ( 函数 ) 是可以通过数组下标和名字引用的。
  • 5.
    JavaScript 对象 可读写var person = new Object(); // 通过“ .” 给对象添加属性 person.name=“Roger”; person.sex=“male”; // 通过数组下标引用 person[0]=“Roger”; person[1]=“male”; // 通过字符串实现 person[“name”]=“Roger”; person[“sex”]=“male”; 使用“ =” 操作符除了可以向对象添加 / 修改属性,还可以将方法动态地添加到一个对象,如: person.sayHello = function() { alert(“Hello, my name is ” + this.name); }
  • 6.
    使用 JSON 创建数组和对象图JavaScript 对象的某一个成员也可以是一个对象 对象结构复杂 可读性 / 可维护性差 JavaScript Object Notation 是 JavaScript 的核心特性 var cookbook=new Object(); cookbook.pageCount=100; cookbook.author={ firstName: “Harry”, secondName: “Christmas”, birthdate: new Date(1900,2,29), interests: [“cheese”, “basketball”, “music”] }
  • 7.
    JavaScript 构造函数 functionPerson (name, age){ this.name=name; this.age=age; this.sayHello=function() { alert(“my name is ” + this.name); } } var myObj = new Person(“Roger”, 27); Java 语言中所有类都继承自 java.lang.Object 类 JavaScript 没有 Java 这种继承的概念,所有 JavaScript 对象都是同一个基类的实例,这个基类拥有在运行时将成员函数和变量动态邦定到自身的能力。 上面的代码缺点: a). 对于创建的每一个 Person 类的实例,都会创建一个新的 sayHello 函数,这对内存是一个极大的浪费,如果在程序中创建大量这样的对象,内存泄露问题会变得尤为明显。 b). 在构造函数中创建一个闭合区域,在涉及到 DOM 时,将会产生严重的问题。 更为安全的替代方案:通过使用 prototype 变得更加面向对象化
  • 8.
    JavaScript -- prototype通过使用构造函数的 prototype ,属性和行数可以关联到对象上。 function Person (name, age){ this.name=name; this.age=age; } Person.prototype.sayHello=function() { alert(“my name is ” + this.name); } var myObj = new Person(“Roger”, 27); myObj.sayHello(); 注意执行顺序:通过 prototype 向 JavaScript 对象添加的属性和方法是在构造函数调用的那一刻关联到所创建的对象上去的 。
  • 9.
    JavaScript 对象反射 确定对象是否有某个属性:if (typeof(myObject.someProperty) != “undefined”){ …… } 测试某个对象的类型 instanceof if (myObj instanceof Array){ …… }
  • 10.
    Rich Client “胖客户端”:相对于“瘦客户端”而言,在客户机器上安装配置的一个功能丰富的交互式用户界面。 MSN, IE, Oracle, DB2 数据库管理工具 传统的 Web 应用程序:客户端需进行页面跳转或刷新 Ajax 解决了:负载过大,响应时间长,同步交互过程带来的感觉不连贯
  • 11.
    Ajax 技术使用 XMLHTTPRequest对象发送请求并得到服务器响应,得到返回的数据后用 JavaScript 操作 DOM 来使页面更新
  • 12.
    Cascading Style Sheets优点:提供了从内容中分离应用样式和设计的机制 缺点:是构建跨浏览器应用的一大阻碍,不同的浏览器厂商支持不同的 CSS 级别。 随着 XML 的引入,数据描述成为单独的功能,而样式表则负责显示,内容和样式的分离给开发带来的极大地方便:可读性,一次性写入 W3C 对 CSS 标准的改进,与各浏览器版本兼容性
  • 13.
    CSS 样式表由样式规则组成,样式规则包括:选择符和样式定义 选择符{ 属性 1: 值 1 ; /* 注释 */ 属性 2: 值 2 } 申明分组,继承
  • 14.
    内部样式表 <html> <head><title>内部样式表演示 </title> <style type=“text/css”> h1,h2,h3 { color: blue } /* 选择符分组 */ h1 { /* 属性分组 */ font-weight: bold; font-size: 30pt; font-family: 宋体 } h2 { /* 属性值分组 */ font: bold 28pt 宋体 } </style> </head> <body> ……</body> </html>
  • 15.
    外部样式表 两种使用方式:连接到样式表上; 输入样式表。===out.css=== body { background-color: pink } p { margin-left: 20%; margin-right: 20%; font-family: 宋体 ; font-size: 14 } <html> <head><title> 链式样式表 </title> <link rel=stylesheet type=“text/css” href=“out.css”> </head> <body> ……</body> </html> href 可以是相对路径或绝对路径 === 输入外部样式表 @import 可与其他方法结合使用,文档中定义的其他样式表格则 将覆盖输入样式表中作用在相同对象上的规则
  • 16.
    样式类 样式的分类,可根据不同的风格需要对某一类型的标记设置几种不同的 CSS属性。 第一种格式: 标记 . 样式类名 { 属性 1: 属性值 1; 属性 2: 属性值 2; 属性 3: 属性值 3} 第二种格式: 样式类名 { 属性 1: 属性值 1; 属性 2: 属性值 2; 属性 3: 属性值 3} 第二种方式规定的样式类没有特定于某一类标记,任何类型的标记都可以使用该样式类。 使用: < 标记 class=“ 样式类名” >
  • 17.
    CSS 中的滤镜 filter:filtername (parameters) filter 是滤镜属性选择符 filtername 是滤镜属性名
  • 18.
    DOM 与 JavaScriptDHTML— 使用 JavaScript 操作页面内容的过程 DOM— 文档对象模型,能够更好的控制窗口内容的方法
  • 19.
    使用 JavaScript 读取XML 文档 <basic country=“China”> <name num=“1”>
  • 20.
    Ajax 工作流程 Ajax是使用客户端脚本与 Web 服务器交换数据的 Web 应用开发方法。这样 Web 页面不用打断交互流程进行重新加载,就可以动态地更新。有关数据验证和数据处理都由 Ajax 引擎来做,需要从服务器读取新数据时再由 Ajax 引擎代为向服务器提交请求 使用 JavaScript 中 XMLHttpRequest 对象(支持异步请求的技术)可以向服务器提出请求并处理响应,这个过程不会阻塞用户的其他操作。
  • 21.
  • 22.
    监视 Response 的状态通过回调函数—监视浏览器的状态 req.onreadystatechange = callback; // 指定回调函数 操纵返回的数据 req.responseText req.responseXML function sendRequest(callback, url) { // branch for native XMLHttpRequest object if (window.XMLHttpRequest) { req = new XMLHttpRequest(); // 创建 XMLHttpRequest req.onreadystatechange = callback; // 指定回调函数 req.open(“GET”, url, true); // 建立请求 req.send(null); // 如有参数,则 method 必须是 post } // branch for IE/Windows ActiveX version else if (window.ActiveXObject) { req = new ActiveXObject(“Microsoft.XMLHTTP”); if (req) { …… // 同上 } } } function callback() { if (req.onreadystate == 4) { // 判断就绪状态 if (req.status == 200) { parseMessage(); // 接收返回的数据 } else { alert(“Not able to retrieve description” + req.statusText) ; } } else {} } function parseMessage() { var xmldoc = req.responseXML; // 获取返回的结果 var name = xmldoc.getElementByTagName(“author”); // 获取文件中 <author> 标记 var content = document.getElementById(“display”); // 获取网页中 <display> 标记 content.innerHTML = name; // 在网页中显示 }
  • 23.
    Ajax 实例 提示等待级联下拉框 Google suggest Slider (滑块) 提示等待: 在状态值不等于 4 时对 HTML 代码进行一些修改,让页面显示一些提示信息。 级联下拉框: 控件 B 的内容取决于控件 A 的内容,且控件 B 的内容需要根据控件 A 的内容再从服务器端获得相应数据。 在 A 的 onChange 事件发生时调用一个函数,创建 XMLHttpRequest 对象来和服务器进行交互,向服务器发送请求,参数为下拉框 A 中选定的内容。客户端在接收到服务器处理后的数据后,先对数据进行处理,再显示到下拉框 B 中。 滑块 -- 是一个可以随意拖动而改变相应值的控件 拖动按钮得到一个新的范围后,就创建一个 XMLHttpRequest 对象来和服务器进行交互,参数就是滑块当前指定范围的最小值和最大值。
  • 24.
    Google suggest 当在文本框中输入查询内容时,文本框控件会响应 onkeyup 事件, 在这个事件中调用 javascript 函数来创建 XMLHttpRequest 对象和服务器进行交互,向服务器发送请求,参数为文本框中的内容。服务器接收到请求后,将参数作为查询条件得到查询结果返回到客户端。客户端接收到这些返回的数据后,在文本框下方构造一个列表,来显示这些匹配查询条件的可选值。在显示过程中,还需对用户按键做出一系列判断,实现友好交互。 定义样式文件 style.css 用来控制下拉提示框、匹配的文本等的显示样式 build.js
  • 25.
    Ajax 的安全问题 Ajax的安全问题和性能问题 JavaScript 和浏览器的安全性 客户端脚本语言(安全性问题) -- 当访问一个使用 Ajax 技术构建的 Web 应用时, Web 服务器会将一系列 JavaScript 脚本代码发送到每一个访问者的机器上,然后用户浏览器便会在本地执行这些代码 1). JavaScript 修改系统设置(注册表,格式化硬盘,恶意破坏) 2). IFrame 问题:网页中 IFrame 指定的元素可以从其他域加载网页并执行 HTML 代码,相当于嵌在网页内部的一个独立框架窗口,但不同域的 JavaScript 却不能互相访问。
  • 26.
    JavaScript 的性能问题(解释型语言) 1). 循环, 效率: while() better than for( ; ; ) better than for( in ) 查询散列键 2). 局部变量 ( 放在函数的堆栈中 ) 访问速度 better than 全局变量 3). 少用 eval ,使用 eval 相当于运行时再次调用解释引擎对内容进行解释运行。可使用 JavaScript 闭包实现模版代替 eval 函数。 4). 使用局部变量,减少对象查找(解释语言) var len = a.length; 5). s += a; better than s = s + a; s += a + b +c; better than s+=a; s+=b; s+=c; 6). 类型转换(弱类型), ( “” + ) > String() > .toString() > new String() 7). 使用直接量, var foo = { }; 解释引擎直接解释 > var foo = new Object(); 内部构造器 var reg = /…/; faster than var reg = new RegExp() 8). 对字符串进行的循环操作,如替换、查找,应优先使用正则表达式( C 语言写的 API ) 9). 复用或缓存以减少创建高级对象,如: Date 、 RegExp 10). 直接使用下标访问快于使用“ .” 11). 创建 DOM 节点:使用字符串直接写 HTML 语句来创建节点 效率低于 使用 document.createElement() 方法, 如果文档中存在现成的样板节点,可以使用 cloneNode() 来减少多次设置元素的属性。
  • 27.
  • 28.
    当前商业型项目中都需要有搜索的功能 有些可以通过 SQL来实现搜索 对于全文检索(如对一个公司 5 年来所有报表数据和会议记录的搜索),数据库的速度和性能无法满足需求 购买 Google 等公司的搜索服务 自己开发搜索引擎 ebay … 使用 Apache 开源全文检索工具包 Lucene 方便地构建自己的搜索引擎 (Eclipse 就是使用 Lucene 作为其内建的搜索工具 )
  • 29.
    信息获取 与搜索引擎 信息获取:包含信息的表示、存储、组织和对信息的访问方法。 数据 索引 检索 排序 a. 构造文本数据库 ,存放将来需要检索的数据 b. 建立文档的索引 ,用来提高信息检索速度。如,倒排索引技术 c. 利用文本处理技术 分析用户的查询 。在进行查询之前还可以进行一些与处理。 d. 对检索结果进行处理 (排序) DB/File 客户输入查询条件 反馈客户查询结果 信息
  • 30.
    信息获取与 搜索引擎 搜索引擎:全文搜索引擎 ( FullText Search Engine ) :通过一个叫网络机器人或叫网络蜘蛛的软件,自动分析网络上的各种链接并获取网页信息内容,按规则加以分析整理,记入数据库。典型的系统有: Google 和百度 分类目录 ( Directory ) :则是 通过人工的方式收集整理网站资料形成的数据库 ,如雅虎中国、搜狐、新浪、网易等网站的分类目录。 第一代搜索引擎:依靠人工分拣的分类目录搜索, e.g. yahoo 第二代搜索引擎:依靠机器抓取,并建立在超级链接分析技术基础之上的网页搜索, e.g. Google 特点:信息量大,更新及时,但返回无关信息过多 第三代搜索引擎:融入“智能化”,“人机交互”功能。将自动分类技术,中文内容分析技术及区域识别技术应用到大型搜索引擎中 。 信息更新速度快,频率高 网页相关检索,拼音纠错,模糊查询,语音查询技术有很高水准。 此外,还同时兼备了新闻, MP3 ,图片, Flash 搜索功能。
  • 31.
    全文检索 全文检索:计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序根据事先建立的索引进行查找,并将查询结果反馈。 按字检索,对英文,字与词合一,中文有很大区别; 按词检索 ,对语义单位建立索引,检索时按词检索,并可以处理同义项。 中文文字的切分字词是全文检索的技术难点。
  • 32.
    全文检索系统 全文检索系统:具有 建立索引、 处理查询返回结果集 、 增加索引 、 优化索引结构 等功能,外围则由各种不同应用具有的功能组成。 查询引擎 文本处理引擎 索引引擎 二次开发应用接口 应用程序 Web 程序 其他 磁盘索引文件
  • 33.
    全文检索 ≠ like &quot;%keyword%&quot; 由于数据库索引不是为全文索引设计的, 当使用 like “%word%” 时,数据库索引是不起作用的 ,所以对于含有模糊查询的数据库服务来说, LIKE 对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配: like “%word1%“ and like ”%word2%” 其效率也就可想而了。 建立一个高效检索系统的 关键是建立一个反向索引机制 ,将数据源(比如多篇文章)排序顺序存储的同时,有另外一个排好序的关键词列表,用于存储关键词 => 文章映射关系, 利用这样的映射关系索引: [ 关键词 => 出现关键词的文章编号,出现次数,出现频率 ] ,检索过程: 把模糊查询变成多个可以利用索引的精确查询的 逻辑组合 的过程 。
  • 34.
    Lucene Lucene 是一个开放源代码的全文检索引擎工具包,是Apache 软件基金会 Jakarta 项目的子项目。 Lucene 不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供完整的查询引擎和索引引擎及部分文本分析引擎(最初:英文、德文)。 不同程序语言版本 (.Net , C , for script language: ruby, python)
  • 35.
    Lucene 全文检索实现机制 Lucene的 API 接口设计的比较通用,输入输出结构都很像数据库的 表 ==> 记录 ==> 字段 ,所以很多传统的应用的文件、数据库等都可以比较方便的映射到 Lucene 的存储结构 / 接口中。总体上看:可以先把 Lucene 当成一个支持全文索引的数据库系统 。 RecordSet :查询结果集,由多个 Record 组成 Hits :查询结果集,由匹配的 Document 组成 2.3 -> TopDocCollector collector = new TopDocCollector(10); Field :字段 Field :字段 Record :记录,包含多个字段 Document :一个需要进行索引的“单元” 一个 Document 由多个字段组成 索引数据源: record(field1,field2...) record(field1..) \ SQL: insert/ _____________ | DB Index | ------------- / SQL: select \ 结果输出: results(record(field1,field2..) record(field1...)) 索引数据源: doc(field1,field2...) doc(field1,field2...) \ indexer / _____________ | Lucene Index| -------------- / searcher \ 结果输出: Hits(doc(field1,field2) doc(field1...)) Database Lucene
  • 36.
    全文检索 和 数据库应用最大的 不同 在于: 让最相关的头 100 条结果满足 98% 以上用户的需求 。 没有接口或接口复杂,无法定制 。 通过不同的语言分析接口实现,可以方便的定制出符合应用需要的索引规则(包括对中文的支持) 可定制性 使用率低,模糊匹配规则简单或者需要模糊查询的资料量少 高负载的模糊查询应用,需要复杂的模糊查询规则,索引的资料量比较大 结论 返回所有的结果集,在匹配条目非常多的时候需要大量的内存存放这些临时结果集。 通过特别的算法,将最匹配度最高的头 100 条结果输出,结果集是缓冲式的小批量读取的。 结果输出 没有匹配程度的控制:比如有记录中 net 出现 5 词和出现 1 次的,结果是一样的。 有匹配度算法,将匹配程度(相似度)比较高的结果排在前面。 匹配度 使用: like “%net%” 会把 netherlands 也匹配出来;使用 like &quot;%com%net%&quot; :就不能匹配词序颠倒的 xxx.net..xxx.com 通过词元 (term) 进行匹配,通过语言分析接口的实现,可以实现对中文等非英语的支持。 匹配效果 对于 LIKE 查询来说,数据传统的索引是根本用不上的。 将数据源中的数据都通过全文索引一一建立反向索引 索引 数据库 Lucene 全文索引
  • 37.
    Lucene 的创新之处 大部分的搜索(数据库)引擎都是用B 树结构来维护索引,索引的更新会导致大量的 IO 操作。 Lucene 对此稍微有所改进: 不是维护一个索引文件,而是在扩展索引的时候不断创建新的索引文件,然后定期的把这些新的小索引文件合并到原先的大索引中 (针对不同的更新策略,批次的大小可以调整),这样在不影响检索的效率的前提下,提高了索引的效率。 Lucene 并没有规定数据源的格式,而只提供了一个通用的结构( Document 对象)来接受索引的输入,因此输入的数据源可以是:数据库, WORD 文档, PDF 文档, HTML 文档……只要能够设计相应的解析转换器将数据源构造成 Docuement 对象即可进行索引。
  • 38.
    索引 与 搜索索引 -- 特殊的数据结构,提高搜索效率,不支持快速信息更新,定期更新索引。 索引技术:倒排索引(对于关键词的搜索非常有效, Lucene ), 后缀数组(短语查询速度快,数据结构构造维护复杂), 签名文件(流行于 20 世纪 80 年代,被倒排索引超越) 搜索 – 为用户提供高质量(响应时间、准确度)的搜索结果。高效的搜索算法及对搜索结果丰富的排序方法
  • 39.
    倒排索引 文档 A: This is a dog B : This dog is a kind of animal 如右图 通过比较: 一般的索引结构建立的是一种“文档到单词”的映射关系, 而倒排索引建立的则是一种“单词到文档”的映射关系。 用户在进行检索时,都是输入关键字进行查询,如果使用一般的索引结构,在查询某一关键字时需要遍历所有索引,索引量大时,效率差。 1 B kind 1 kind B 1 dog B 1 B animal 1,1 A,B dog 出现次数 出现的文档 单词 倒排索引结构 1 animal B 1 dog A 出现次数 出现单词 文档编号 一般索引结构
  • 40.
    Lucene 使用 Lucene来为本地文件建立索引
  • 41.
  • 42.
    Lucene 建立索引过程 1). 提取文本: 从各种文档中提取文本信息 2). 构建 Document 将提取出的文本组装成 Lucene 可以识别的格式来为索引建立做准备 3). 分析 对要建立索引的数据进行分析,使建立索引时更容易处理数据 4). 建立索引 调用 IndexWriter 类的 addDocument() 方法建立索引。
  • 43.
    Lucene -- DocumentLucene 的 Document 代表了一个需要进行 索引的“单元”, 任何需要进行索引的数据源(如:文件) 都必须被转化成 Document 对象 才能够被索引和搜索到。
  • 44.
    Lucene -- IndexWriter功能:将文档加入索引,同时控制索引过程中的各种参数。 初始化: 参数: 索引存放的路径: String, java.io.File, Directory IndexWriter writer1 = new IndexWriter(“c:\\index1”, new StandardAnalyzer(), true); File f = new File(“c:\\index2”); IndexWriter writer2 = new IndexWriter(f, new StandardAnalyzer(), true); Directory d = new RAMDirectory(); IndexWriter writer3 = new IndexWriter(d, new StandardAnalyzer(), true); 分析器:在 IndexWriter 将文档写入索引前,把文本信息切分成一个个可以进行索引的词条。继承自 org.apache.lucene.analysis.Analyzer 类的分析器 重新创建索引 true/ 增量索引 false
  • 45.
    Lucene -- IndexWriter向索引添加文档 调整性能参数 1. mergeFactor :默认值为 10 ,用来控制 lucene 在把索引从内存写入磁盘上的文件系统时内存中最大的 Document 数量及最大 Segment 数量。影响建立索引时所花费的磁盘 I/O 时间和内存。设置太小, I/O 操作频繁,经常进行 segment 合并,设置太大,内存中持有的 Document 数量多,建立索引快,但大量占用内存。 public void setMergeFactor( int mergeFactor) { getLogMergePolicy().setMergeFactor(mergeFactor); } 2. maxMergeDocs :限制一个 segment 中最大的文档数量。 public void setMaxMergeDocs( int maxMergeDocs) { getLogMergePolicy().setMaxMergeDocs(maxMergeDocs); } 限制 Field 长度: maxFieldLength ,默认值为 10000 ,只对前 10000 个 term 索引。
  • 46.
    Lucene 的索引文件格式 规定了Lucene 的文件格式采取的存储单位、组织结构、命名规范等等内容 索引文件格式:多文件索引文件格式、复合索引格式 setUseCompoundFile( boolean isCompoundFile)
  • 47.
    Lucene 文件格式中定义的数据类型 在Lucene 的文件格式中,以字节为基础,并且定义了自身的数据类型 . 由于它们都以字节为基础定义而来,因此保证了是平台无关,这也是 Lucene 索引文件格式平台无关的主要原因 .
  • 48.
    Lucene 索引文件概念结构 Segment:一个段是一个独立的索引,可以由 IndexSearcher 进行单独查找。 Lucene 的工作其实就是不停地往磁盘加入新的 Segment ,并按一定的算法合并不同的 Segment 以创建一个新的段。 项( Term )是最小的索引概念单位,它直接代表了一个字符串以及其在文件中的位置、出现次数等信息。 域( Field )是一个关联的元组,由一个域名和一个域值组成,域名是一个字串,域值是一个项
  • 49.
    Lucene 索引文件结构组成 域集合与项集合中的文件组采用了一种类似的存储办法:一个小型的索引文件,运行时载入内存;一个对应于索引文件的实际信息文件,可以按照索引中指示的偏移量随机访问;索引文件与信息文件在记录的排列顺序上存在隐式的对应关系,即索引文件中按照“索引项 1 、索引项 2…” 排列,则信息文件则也按照“信息项 1 、信息项 2…” 排列。
  • 50.
    一共有四个文件( .tii, .tis,.frq,.prx )用来记录Term 相关的信息: -- 在 .tis 文件中,对每个 Term 都有一项 TermInfo, 而且 TermInfo 是根据字典顺序排序。 TermInfo 中记录的了 Term 本身,以及一些以在 .frq 和 .prx 中和该 Term 相关详细的指针。 -- .tii 是对 .tis 的一个索引,用来加速对 Term 的查找。 -- .frq 文件记录了每个 Term 出现的文档号以及其频率, -- .prx 文件记录了每个 Term 在每篇文档中出现的位置信息。
  • 51.
    索引存储抽象 -- 索引存放位置 磁盘 – FSDirectory 将索引存放在文件系统中, Lucene 会自动在内存中建立缓存,然后到一定时候就写入磁盘。 IndexWriter writer = new IndexWriter(FSDirectory.getDirectory(path, true), new StandardAnalyzer(), true); 内存 – RAMDirectory ,速度快,非持久化(虚拟机退出,索引丢失),因此需一方法将内存中的索引转入磁盘。 IndexWriter writer = new IndexWriter(RAMDirectory.getDirectory(), new StandardAnalyzer(), true);
  • 52.
    索引合并 索引的 合并:当开发者在不同的地方创建许多索引,某种情况下,需进行合并。 public void addIndexes(Directory[] dirs ) , 可以合并 FSDirecotry 和 RAMDirectory ,也可以合并两个 FSDirectory ramWriter.addDocument(doc1); fsWriter.addDocument(doc2); ramWriter.close();// 索引创建完毕,关闭内存所引器,使所有文档进入内存索引文件 fsWriter.addIndexes(new Directory[]{fsDir}); fsWriter.close(); 删除文档格式:删除特定文档、按 Field 来删除 IndexReader deleteDocuments(int docNum) , undeleteAll() deleteDocuments(Term term)
  • 53.
    Lucene 索引优化、同步机制 optimize():通过优化可以减少一个搜索器要同时打开的文件数,降低 I/O 。 将磁盘上多个 Segment 进行合并,组成一个全新的 Segment ,对磁盘空间需求大,应在大批量索引建立完成后再进行调用,注意索引优化周期。 多线程环境下的同步机制(同步法则): 任何数量的只读操作可以被同时执行,如使用 IndexSearcher 进行检索 任何数量的只读操作都可以在索引正在被修改时执行,如,往索引中新加文档、删除旧文档或索引优化过程 同一时间内,只可以有一个修改索引操作。 IndexWriter/IndexReader Lucene 的索引“锁”: Lucene 设置了 文件锁 ,默认放置在系统临时文件夹下,该文件夹路经由 java.io.tempdir 系统变量指定。 writer.lock :防止多线程同时修改一索引文件,由 IndexWriter 初始化时获得,在关闭时释放。 commit.lock :在索引的 Segment 被建立、合并或读取时生成。
  • 54.
    Lucene 搜索 搜索的流程初始化 IndexSearcher : Searcher searcher = new IndexSearcher(indexDir); 构建 Query : 如查询方式(模糊查询、语义查询、短语查询、范围查询、 组合查询等) Query query = QueryParser.parse(queryString) 搜索并处理返回结果 : Hits hits = searcher.search(query); 排序、过滤 Web 开发时,将 Lucene 与数据库相结合,在索引中存入一些关键的 ID 字段、路径字段或是简单的文本,真正的数据提取则从数据库中得到。
  • 55.
    检索结果 --Hits Lazy load result, LRU doubly-linked cache
  • 56.
    Lucene 的评分机制 评分机制就是对检索结果按某种标准进行评估,然后按分值的高低来对结果进行排序。对于一个商用的搜索引擎来说,评分机制是其收入来源的一部分。 Lucene 评分算法:(简单理解成某个关键字在文档中出现的频率) 可以通过 Document 的 setBoost(float boost) 来改变文档 boost 因子,进而改变文档评分
  • 57.
    构建 Query 按词条搜索– TermQuery Term term = new Term(“contents”, “java”); Query query = new TermQuery(term); BooleanQuery public void add(BooleanClause clause) RangeQuery public RangeQuery(Term lowerTerm, Term upperTerm, boolean inclusive) PrefixQuery public PrefixQuery(Term prefix) 多关键字搜索 -- PhraseQuery public void add(Term term) public void setSlop(int s) // Sets the number of other words permitted between words in //query phrase. If zero, then this is an exact phrase search. 相近词语的搜索 – FuzzyQuery 通配符搜索 – WildcardQuery // *, ? …… 解析用户输入字符串 -- QueryParser
  • 58.
    Lucene 分析器 管道过滤器模式:分析器 (Analyzer) 使用分词器 (Tokenizer) 和过滤器 (TokenFilter) 构成一个管道,文本在“流过”这个管道后,就成为可以进入索引的最小单元。 JavaCC 与 StandardAnalyzer(JavaCC 为 Lucene 构建的分析器 ) TokenStream Tokenizer TokenFilter
  • 59.
  • 60.
  • 61.
    对搜索结果的排序 例如: 把查询结果按照类别进行分组在组内再按照与查询的相关度进行排序
  • 62.
    搜索引擎的中文问题 关于亚洲语言的的切分词问题 对于中文来说,全文索引首先还要解决一个语言分析的问题。英文语句中单词之间是天然通过空格分开的,但亚洲语言的中日韩文语句中的字是一个字挨一个,所有,首先要把语句中按“词”进行索引的话,这个词如何切分出来就是一个很大的问题。首先,肯定不能用单个字符作 (si-gram) 为索引单元,否则查“上海”时,含有“海上”也能匹配。 但一句话:“北京天安门”,计算机如何按照中文的语言习惯进行切分呢? “北京 天安门” 还是“北 京 天安门”?让计算机能够按照语言习惯进行切分,往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。 另外一个解决的办法是采用自动切分算法:将单词按照 2 元语法 (bigram) 方式切分出来,比如: “北京天安门” ==> “ 北京 京天 天安 安门”。 这样,在查询的时候,无论是查询“北京” 还是查询“天安门”,将查询词组按同样的规则进行切分:“北京”,“天安门”,多个关键词之间按与“ and” 的关系组合,同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言:韩文,日文都是通用的。 基于自动切分的最大优点是没有词表维护成本,实现简单,缺点是索引效率低。
  • 63.
    中文分词 lucene 自身提供的StandardAnalyzer 已经具备中文分词的功能,但是不一定能够满足大多数应用的需要。 另外我们谈的比较多的中文分词器还有: CJKAnalyzer 二元分词 ChineseAnalyzer 基本等同 StandardAnalyzer IK_CAnalyzer ( MIK_CAnalyzer ) 还有一些网友自己写的比较不错的分词器 paoding ,有兴趣的可以自己研究研究。 CJKAnalyzer 和 ChineseAnalyzer 分别是 lucene-2.4.0 目录下 contrib 目录下 lucene-analyzers-2.4.0 的 analyzers 提供的。分别位于 cn 和 cjk 目录。 IK_CAnalyzer ( MIK_CAnalyzer )是基于分词词典,目前最新的 1.4 版本是基于 lucene2.0 开发的。 正向全切分分词器: org.mira.lucene.analysis.IK_CAnalyzer (适合建索引时使用) 正向最大全切分分词器: org.mira.lucene.analysis.MIK_CAnalyzer (适合用户输入检索时使用)
  • 64.
    用 lucene 和Heritrix 构建了一个 Web 搜索应用程序 Lucene 是基于 Java 的全文信息检索包,它目前是 Apache Jakarta 家族下面的一个开源项目。 Lucene 很强大,但是,无论多么强大的搜索引擎工具,在其后台,都需要一样东西来支援它,那就是网络爬虫 Spider 。网络爬虫 Spider ,或称网络机器人、 BOT 等,由于爬虫的存在,才使得搜索引擎有了丰富的资源。 Heritrix 是一个纯由 Java 开发的、开源的 Web 网络爬虫,用户可以使用它从网络上抓取想要的资源。它来自于 www.archive.org 。 Heritrix 最出色之处在于它的可扩展性,开发者可以扩展它的各个组件,来实现自己的抓取逻辑。
  • 65.
    系统架构 在前端流程中,用户在搜索引擎提供的界面中输入要搜索的关键词,这里提到的用户界面一般是一个带有输入框的 Web 页面,然后应用程序将搜索的关键词解析成搜索引擎可以理解的形式,并在索引文件上进行搜索操作。在排序后,搜索引擎返回搜索结果给用户。在后端流程中,网络爬虫从因特网上获取 Web 页面,然后索引子系统解析这些 Web 页面并存入索引文件中。
  • 66.
    DB 全文检索Mysql Sql server
  • 67.
    Hibernate Lucene Integration http://wiki.redsaga.com/confluence/display/HART/Hibernate+Lucene+Integration Lucene 使用者沙龙 http://blog.cnblog.org/archives/2005/07/luceneaecee.html