- 浏览: 634023 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
liuche20083736:
非常好
从问题看本质: 研究TCP close_wait的内幕 -
xiaopohai85707:
优化算法与原来需求不符
过滤字符的性能调优?挤一挤还是有的 -
kmy_白衣:
生成的area图有时候 标签的数值和图标上看上去的数值不一致。 ...
OpenFlashChart2之恶心文档 -
tom&jerry:
大神,请教一个问题,按名称排序为何无效,用的2.4.3 XPA ...
深入浅出jackrabbit之十三 查询之AST和QT -
jd2bs:
改成精确匹配可以了< filter-mapping &g ...
细谈Ehcache页面缓存的使用
任何一个数据库都离不开一个技术----索引技术,jackrabbit作为内容仓库的开源实现亦不能例外,从前面的文章中我们已经清楚的知道jackrabbit使用lucene来进行索引任务和查询任务。而查询正是基于索引,所以在本文中,ahuaxuan将和大家一起来学习jackrabbit中建立索引的方法。
事实上,jackrabbit中建立索引的流程是比较冗长和复杂的,如同query一样,在本文中ahuaxuan将把注意里放在整体流程上,而流程的每一步可以说都是比较复杂的,这些复杂的实现细节将会放在后面的文章中阐述,本文的主要目的是了解jackrabbit中创建流程的一般过程。
首先,我们需要寻找一个入口点,一个接口,这个接口是建立索引的入口,任何人需要建立索引的功能必须要调用这个接口,那么这个接口在哪里呢,通过对源代码的类结构分析,ahuaxuan得出来以下的类图,从图中我们可以看出,建立索引的api其实是一个监听器(监听器模式来源于观察者模式,两者并没有什么本质上的不同),任何人需要建立索引只要发事件给对应的监听器,那么监听器就会执行建立索引的流程(同时请注意下图中标注出来创建索引的两个入口,一个是Update类,还有一个是ClustorNode):
上图即描述了建立索引前的主要逻辑,ObervationDispatcher类,当ClustorNode中发现其他节点有save,update,delete或者本node中有update操作,那么ObervationDispatcher将会把这种事件分发给对这类事件感兴趣的类,那么显然,对这类event最感兴趣的便是图中的SearchManager,也就是说SearchManager是索引的入口。而且从图中所示的类结构图可知,SearchManager就是一个EventListener。
既然知道了index的入口,不妨让我们来看一下SearchManager中索引的入口方法onEvent()
这段代码很简单,就是把传进来的event对象转变成两个iterator,一个是需要remove的node iterator,另外一个是需要add的node iterator。由于lucene不支持索引的更新,所以对于lucene来说,save,update,delete再本质上就是delete和save。Update被拆成了先delete后save两个环节,也就是说如果update一个节点,那么这个节点的id既会出现在removedIds里,也会出现在addedStates里。
Onevent方法的最终走向是handler.updateNodes(),所以我们也需要进入到updateNodes方法中。
跟随着代码,我们到达了updateNodes方法:
这段代码很简单,把原来的两个iterator作了一个decorator,也就是说在原有的iterator的功能上又添加了一些功能,代码虽然很简单,但是却很重要,尤其是在处理那些需要add的node上,在第二个decorator中,有一个next方法,这个方法包含着创建index的主要逻辑:创建属于这个node的document对象。为node创建document是索引过程中第一个最重要的环节,这意味着,我们需要掌握一个node中,哪些属性被建立索引,建了那些索引,这关乎到查询的范围,如果一个property不能被索引,那么在查询的时候就不能以它作为依据。
所以请大家记住createDocument这个方法,在后文中,我们会详细分析一个node有哪些field会被索引进文件。
所以到目前为止,我们已经得知了index流程的前面三个步骤
接着,我们进入到index.update()方法,index对象是MultiIndex类的实例,对于update方法来说,无论用什么言语来描述它的重要性都不为过,因为在这个方法中,我们会看到jackrabbit建立索引的核心流程,但是我们需要知道的是,它并不包含任何细节,细节隐藏在流程的其他对象中:
//请注意方法前的synchronized,这个同步非常重要,因为他能控制一个非//常重要的流程的顺序执行。
从代码的注释,我们可以看到这段代码的主要功能,就是把该删的删掉,该加的加进去,然后该flush就flush,最后方法退出。那么代码的主要逻辑在哪里呢?
在DeleteNode和AddNode类中,还有就是在flush方法中。
看上去很简单了,但是这只是看上去简单而已,就到目前为止,这个以上流程的主要工作还停留在如何准备索引数据:即document,接着就是短短几行代码来描述如何使用document,
不过再继续下去之前,我们得回头看看createDocument中究竟有哪些field被创建,究竟node中有哪些信息被放到索引中了呢,同时我们将会看到如何通过修改field的属性来降低高亮功能所带来的时间消耗,to be continue。
哦,这个是因为当一个节点被删除或者增加时有一些其他的的模块需要得到通知,所以用了观察者模式,那么需要得到通知的模块作为观察者,只要节点有变化就能收到通知,当有新的模块需要得到这些信息的时候,这要写一个观察者注册进去就行了。其中主要的一个观察者就是SearchManager,因为节点的变化它必须要能反映到索引文件中去。
而节点的crud逻辑本身并不涉及到观察者,只是最后在提交的node的改变的时候通知一下观察者。
事实上,jackrabbit中建立索引的流程是比较冗长和复杂的,如同query一样,在本文中ahuaxuan将把注意里放在整体流程上,而流程的每一步可以说都是比较复杂的,这些复杂的实现细节将会放在后面的文章中阐述,本文的主要目的是了解jackrabbit中创建流程的一般过程。
首先,我们需要寻找一个入口点,一个接口,这个接口是建立索引的入口,任何人需要建立索引的功能必须要调用这个接口,那么这个接口在哪里呢,通过对源代码的类结构分析,ahuaxuan得出来以下的类图,从图中我们可以看出,建立索引的api其实是一个监听器(监听器模式来源于观察者模式,两者并没有什么本质上的不同),任何人需要建立索引只要发事件给对应的监听器,那么监听器就会执行建立索引的流程(同时请注意下图中标注出来创建索引的两个入口,一个是Update类,还有一个是ClustorNode):
上图即描述了建立索引前的主要逻辑,ObervationDispatcher类,当ClustorNode中发现其他节点有save,update,delete或者本node中有update操作,那么ObervationDispatcher将会把这种事件分发给对这类事件感兴趣的类,那么显然,对这类event最感兴趣的便是图中的SearchManager,也就是说SearchManager是索引的入口。而且从图中所示的类结构图可知,SearchManager就是一个EventListener。
既然知道了index的入口,不妨让我们来看一下SearchManager中索引的入口方法onEvent()
public void onEvent(EventIterator events) { // nodes that need to be removed from the index. final Set removedNodes = new HashSet(); // nodes that need to be added to the index. final Map addedNodes = new HashMap(); // property events List propEvents = new ArrayList(); while (events.hasNext()) { EventImpl e = (EventImpl) events.nextEvent(); if (!isExcluded(e)) { long type = e.getType(); if (type == Event.NODE_ADDED) { addedNodes.put(e.getChildId(), e); // quick'n dirty fix for JCR-905 if (e.isExternal()) { removedNodes.add(e.getChildId()); } } else if (type == Event.NODE_REMOVED) { removedNodes.add(e.getChildId()); } else { propEvents.add(e); } } } // sort out property events for (int i = 0; i < propEvents.size(); i++) { EventImpl e = (EventImpl) propEvents.get(i); NodeId nodeId = e.getParentId(); if (e.getType() == Event.PROPERTY_ADDED) { if (addedNodes.put(nodeId, e) == null) { // only property added // need to re-index removedNodes.add(nodeId); } else { // the node where this prop belongs to is also new } } else if (e.getType() == Event.PROPERTY_CHANGED) { // need to re-index addedNodes.put(nodeId, e); removedNodes.add(nodeId); } else { // property removed event is only generated when node still exists addedNodes.put(nodeId, e); removedNodes.add(nodeId); } } NodeStateIterator addedStates = new NodeStateIterator() { //创建需要add的node iterator ……………. }; NodeIdIterator removedIds = new NodeIdIterator() { …………………… }; if (removedNodes.size() > 0 || addedNodes.size() > 0) { try { handler.updateNodes(removedIds, addedStates); } catch (RepositoryException e) { log.error("Error indexing node.", e); } catch (IOException e) { log.error("Error indexing node.", e); } } if (log.isDebugEnabled()) { log.debug("onEvent: indexing finished in " + String.valueOf(System.currentTimeMillis() - time) + " ms."); } }
这段代码很简单,就是把传进来的event对象转变成两个iterator,一个是需要remove的node iterator,另外一个是需要add的node iterator。由于lucene不支持索引的更新,所以对于lucene来说,save,update,delete再本质上就是delete和save。Update被拆成了先delete后save两个环节,也就是说如果update一个节点,那么这个节点的id既会出现在removedIds里,也会出现在addedStates里。
Onevent方法的最终走向是handler.updateNodes(),所以我们也需要进入到updateNodes方法中。
跟随着代码,我们到达了updateNodes方法:
public void updateNodes(NodeIdIterator remove, NodeStateIterator add) throws RepositoryException, IOException { checkOpen(); final Map aggregateRoots = new HashMap(); final Set removedNodeIds = new HashSet(); final Set addedNodeIds = new HashSet(); index.update(new AbstractIteratorDecorator(remove) { public Object next() { NodeId nodeId = (NodeId) super.next(); removedNodeIds.add(nodeId); return nodeId.getUUID(); } }, new AbstractIteratorDecorator(add) { public Object next() { NodeState state = (NodeState) super.next(); if (state == null) { return null; } addedNodeIds.add(state.getNodeId()); removedNodeIds.remove(state.getNodeId()); Document doc = null; try { //非常重要的一个方法,但是它会在那里执行呢? doc = createDocument(state, getNamespaceMappings(), index.getIndexFormatVersion()); retrieveAggregateRoot(state, aggregateRoots); } catch (RepositoryException e) { log.warn("Exception while creating document for node: " + state.getNodeId() + ": " + e.toString()); } return doc; } }); }
这段代码很简单,把原来的两个iterator作了一个decorator,也就是说在原有的iterator的功能上又添加了一些功能,代码虽然很简单,但是却很重要,尤其是在处理那些需要add的node上,在第二个decorator中,有一个next方法,这个方法包含着创建index的主要逻辑:创建属于这个node的document对象。为node创建document是索引过程中第一个最重要的环节,这意味着,我们需要掌握一个node中,哪些属性被建立索引,建了那些索引,这关乎到查询的范围,如果一个property不能被索引,那么在查询的时候就不能以它作为依据。
所以请大家记住createDocument这个方法,在后文中,我们会详细分析一个node有哪些field会被索引进文件。
所以到目前为止,我们已经得知了index流程的前面三个步骤
1. 调用index流程的两个入口 2. 监听器解析event,并生成两个iterator 3. Decorate两个iterator,目的是override其next方法,并将在next方法中创建document。
接着,我们进入到index.update()方法,index对象是MultiIndex类的实例,对于update方法来说,无论用什么言语来描述它的重要性都不为过,因为在这个方法中,我们会看到jackrabbit建立索引的核心流程,但是我们需要知道的是,它并不包含任何细节,细节隐藏在流程的其他对象中:
//请注意方法前的synchronized,这个同步非常重要,因为他能控制一个非//常重要的流程的顺序执行。
synchronized void update(Iterator remove, Iterator add) throws IOException { synchronized (updateMonitor) { updateInProgress = true; } try { long transactionId = nextTransactionId++; executeAndLog(new Start(transactionId)); boolean flush = false; //从索引中删除需要删除的node while (remove.hasNext()) { executeAndLog(new DeleteNode(transactionId, (UUID) remove.next())); } //将需要增加的node的document加入到索引中去,这里的next //方法就是上一个方法中定义的decorator中的next()方法,创建//document就是在此时 while (add.hasNext()) { Document doc = (Document) add.next(); if (doc != null) { executeAndLog(new AddNode(transactionId, doc)); // commit volatile index if needed flush |= checkVolatileCommit(); } } executeAndLog(new Commit(transactionId)); // flush whole index when volatile index has been commited. if (flush) { flush(); } } finally { synchronized (updateMonitor) { updateInProgress = false; updateMonitor.notifyAll(); if (multiReader != null) { multiReader.close(); multiReader = null; } } } }
从代码的注释,我们可以看到这段代码的主要功能,就是把该删的删掉,该加的加进去,然后该flush就flush,最后方法退出。那么代码的主要逻辑在哪里呢?
在DeleteNode和AddNode类中,还有就是在flush方法中。
看上去很简单了,但是这只是看上去简单而已,就到目前为止,这个以上流程的主要工作还停留在如何准备索引数据:即document,接着就是短短几行代码来描述如何使用document,
不过再继续下去之前,我们得回头看看createDocument中究竟有哪些field被创建,究竟node中有哪些信息被放到索引中了呢,同时我们将会看到如何通过修改field的属性来降低高亮功能所带来的时间消耗,to be continue。
评论
2 楼
ahuaxuan
2009-07-14
lxiaodao 写道
很好的文章,分析很到位,有机会多交流。
我有个问题:
在对节点的保存/更新/删除时候,使用观察者模式的优点到底是什么?因为我觉得如果象hibernate那样session.save(Node)操作更好。
如果有时间,请你解一下我的困惑。
我有个问题:
在对节点的保存/更新/删除时候,使用观察者模式的优点到底是什么?因为我觉得如果象hibernate那样session.save(Node)操作更好。
如果有时间,请你解一下我的困惑。
哦,这个是因为当一个节点被删除或者增加时有一些其他的的模块需要得到通知,所以用了观察者模式,那么需要得到通知的模块作为观察者,只要节点有变化就能收到通知,当有新的模块需要得到这些信息的时候,这要写一个观察者注册进去就行了。其中主要的一个观察者就是SearchManager,因为节点的变化它必须要能反映到索引文件中去。
而节点的crud逻辑本身并不涉及到观察者,只是最后在提交的node的改变的时候通知一下观察者。
1 楼
lxiaodao
2009-07-14
很好的文章,分析很到位,有机会多交流。
我有个问题:
在对节点的保存/更新/删除时候,使用观察者模式的优点到底是什么?因为我觉得如果象hibernate那样session.save(Node)操作更好。
如果有时间,请你解一下我的困惑。
我有个问题:
在对节点的保存/更新/删除时候,使用观察者模式的优点到底是什么?因为我觉得如果象hibernate那样session.save(Node)操作更好。
如果有时间,请你解一下我的困惑。
发表评论
-
深入浅出jcr之16 该死的RMI,我们需要HTTP+简单RPC协议
2009-12-12 13:22 6660从这篇文 ... -
深入浅出jackrabbit之十五 文档提取优化2.docx
2009-10-22 18:38 3831/** *author:ahuaxuan *2009- ... -
深入浅出jackrabbit之十四 分布式文档提取
2009-09-24 12:20 4682/** *author:ahuaxuan *200 ... -
深入浅出jackrabbit之十三 查询之AST和QT
2009-09-10 10:12 3350简介:在前面的文章中 ... -
深入浅出jcr之十二 key-value存储系统
2009-08-26 09:31 3699作者:ahuaxuan 在写文章方面,惰性心理 ... -
深入浅出jcr之十一 jackrabbit改进要点
2009-08-18 18:22 3566作者,ahuaxuan 在看过前 ... -
深入浅出jcr之十 redolog 和 recovery.docx
2009-08-18 18:14 2051作者:ahuaxuan 在前面的 ... -
深入浅出 jackrabbit 九 索引合并(下)
2009-07-22 14:16 2014在上文中,ahuaxuan讲到了索引创建的主体流程,但是索引合 ... -
深入浅出 jackrabbit 八 索引合并(上)
2009-07-21 17:32 2481我们从文本提取的逻辑中走出来,回到主体流程。 在前面的文 ... -
深入浅出 jackrabbit 七 文本提取(下)
2009-07-21 17:29 2484接上文,说到文本提取,在上一篇文章中,我们是管中窥豹,并没有把 ... -
深入浅出 jackrabbit 六 文本提取(上)
2009-07-21 17:27 3266用lucene作过索引的同 ... -
深入浅出 jackrabbit 之五 索引提交(下)
2009-07-14 17:53 2208接上文,在上面一篇文章中,我们谈到了update中的Delet ... -
深入浅出 jackrabbit 之四 索引提交(上)
2009-07-14 09:10 2851在上上篇文章中,我们了解了创建索引的一般流程,在上篇文章中,我 ... -
深入浅出 jackrabbit 3 创建 document
2009-07-01 13:03 4227/** *作者:ahuaxuan 张荣华 *日期:2009-0 ... -
深入浅出 jackrabbit 十 查询概览
2009-06-20 10:29 6255/** *author: ahuaxuan *date: ... -
深入浅出 jackrabbit 1
2009-05-19 18:31 12374/** * author:ahuaxuan( ...
相关推荐
NULL 博文链接:https://ahuaxuan.iteye.com/blog/391361
深入浅出讲解jackrabbit 共分十个专题。PDF 文档
jackrabbit最全入门教程,jackrabbit最全入门教程,jackrabbit最全入门教程,jackrabbit最全入门教程
jackrabbit 1.5.6 jar
jackrabbit-standalone-1.6.5.jar是webDav的支持jar包。
jackrabbit开发用jar包,jackrabbit是基于Lucene的一种站内搜索技术,它用xml文件为他的元数据,自动穿件索引,使用xpath或者xquery的查询方法。
Apache Jackrabbit API html ,非常详细,
开源Jazz Jackrabbit 2重新实现由带给您介绍Jazz²Resurrection是对1998年发行的Jazz Jackrabbit 2游戏的重新实现。支持该游戏的各种版本(共享软件演示,Holiday Hare '98,The Secret Files和Christmas Chronicles...
jackrabbit-api-1.5.0.jar
一个Eclipse项目, 内含三个Apache Jackrabbit的入门实例, 以及所有需要的包, 在Eclipse中可直接运行。
jackrabbit教程,主要是网上整合的资源。比较全面。用法等。
常见问题查询。作为JCR的一个实例,为用户提供一种网络存储、共享、应用的方式
jackrabbit-webdav-2.1.0.jar 具体用法可以网上查找
apache内容管理apache内容管理
jackrabbit-core-1.5.5.jar
jackrabbit 研究初步,不想多说,肯定值。研究了好久哦。
jackrabbit, 在amqplib上,简单的amqp/rabbitmq作业队列基于 node Jackrabbitnode.js 在不讨厌生命的情况下。producer.js:var jackrabbit = require('jackrabbit');var rabbit = jackrabbit(process
里面包含两个工程,一个是官方的三个小demo,另一个是在ibm页面看到的实例,比小demo稍微高级一些,希望可以帮助大家迅速入门和理解jackrabbit。
jackrabbit-webdav-1.5.5.jar
Rabbit BL1800 Jackrabbit 说明书pdf,Rabbit BL1800 Jackrabbit 说明书