- 浏览: 639641 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
liuche20083736:
非常好
从问题看本质: 研究TCP close_wait的内幕 -
xiaopohai85707:
优化算法与原来需求不符
过滤字符的性能调优?挤一挤还是有的 -
kmy_白衣:
生成的area图有时候 标签的数值和图标上看上去的数值不一致。 ...
OpenFlashChart2之恶心文档 -
tom&jerry:
大神,请教一个问题,按名称排序为何无效,用的2.4.3 XPA ...
深入浅出jackrabbit之十三 查询之AST和QT -
jd2bs:
改成精确匹配可以了< filter-mapping &g ...
细谈Ehcache页面缓存的使用
/**
*author: ahuaxuan
*date: 2009-06-20
*/
查询进阶
在第一篇文章的例子中,我们看到jackrabbit中存放数据的流程还是比较清晰的,而且我们也基本确定了其中数据的存储方式,一颗m叉树。
正如db一样,insert都是看上去相对简单的,select相对总是没有这么简单的,如同关系数据库一样,jackrabbit中查询也有类似的流程,一般来说分成三个部分:1解析查询语句,2查询,3返回结果。这个应该是一个比较通用的模型。Jackrabbit的search部分亦不过如此。下面我们来看一个search的小例子。
这个例子很简单,使用xpath语法来查询world节点中包含指定字符串的同层节点。然后按照指定的属性排序并返回。从上面的例子中我们大概就只能得到这么多信息了,但是我想爱好技术的你一定不会满足,也一定不会止步于这个小小的例子。如果同第一篇文章所讲,我们不能只看到事物的表面,而是要通过这个表面来挖掘其背后的本质。
所以接下来让我们来看看事物背后的本质。众所周知,只要是数据存储方案,那么必定离不开一个东西,那就是索引,索引可以帮助我们快速找到我们想要的数据。那么jackrabbit是怎么根据xpath语法就找到对应的数据的呢,无疑,它也是在运用搜索技术,而且通过它的package列表我们可以看到,它其实就是利用lucene来完成了它的查询模块。也就是说在jackrabbit中,利用了xpath和lucene实现了它的查询模块。
搞清楚了这两点,我们就可以继续往下深入了。导入源代码,让我们从createQuery方法开始,这个方法很重要,可以说是非常重要,而且这个方法中也包含了一门基础学科-编译原理(jackrabbit中是使用javacc来生成xpath的词法语法分析类的org.apache.jackrabbit.core.query.xpath.Xpath,并且它的语法文件xpath.jj长达9000行,ahuaxuan暂时还没有这个闲心看下去)。createQuery方法的两个参数,一个是查询语句,还有一个是语句的语法,目前jackrabbit支持xpath语法和sql语法,他们在整个流程上属于一个可以相互替换的节点上,就是生成AST。再生产语法树之后,会生成查询节点树,这个颗树中包含了lucene需要的很多信息,比如需要搜索的key word,搜索时的排序字段等等。所以在第一大步骤createquery中我们可以细分为两个小的步骤:
1 编译query statement,生成AST
2 通过AST生成querynode
通过上面的两个主要步骤,我们顺利的得到了query对象,query对象就包含的查询所需要的基本条件了,很显然它和hibernate的query对象有异曲同工之妙。都是通过一个查询语句来生产query对象,然后query对象中包含了查询的所有信息,一旦执行execute方法,就真正的触发了的查询的流程。
说到这里就不得不看一下一个关键的方法(org.apache.jackrabbit.core.query.lucene.QueryImpl):
通过上面的代码注释,那么我们可以得到一个有效的信息,那就是在ahuaxuan提供的示例代码中执行query.execute()的时候,这个时候,我们需要的结果其实就已经加载到内存中了。有的人会感到疑惑,为什么上面的QueryResultImpl中带有这么多构造参数,其实那些对象是用来组装lucene出来的结果的。因为放到索引中的数据并不是node的所有信息,比如一个node,因为放到索引中只是nodeid,其对应的node对象还是需要通过itemMgr这样的对象来得到。
这样我们又确定了第二大步骤包含了二个步骤:
1 根据querynode生成lucene的query对象
2 执行lucene的query对象并得到结果集
现在数据,也有了,那么接下来做什么呢?当然是遍历数据,拿出结果来,显然这个和jdbc的工作流程有得一拼啊(看来这里的时候,大家对查询的使用是不是比较清楚了,它和很多持久化框架在流程上是相当一致的,熟悉了一个之后,再来看其他的,学习的速度明显得到加强,当前前提是你已经充分理解了某个框架的内部原理)。那么接下来的事情只有这些:
看上去很简单。实际上也比较简单,但是其中确有一个ahuaxuan认为比较愚蠢的设计。拿这个例子来说,接下来的流程就是得到得到数据,并作一些客户端需要的操作,比如高亮。
通过代码的阅读:在这个大步骤中,又细分为以下步骤:
1 根据getvalue中参数判断查询的内容,比如jcr:score,jcr:path等,这些属性很容易就能得到,可以说是现成的,稍微看一下代码就知道他们是怎么获得的,而在上面的例子中,我们是要找jcr:data这个属性,并高亮之。而且这个属性也是最复杂的,其他属性比较简单,下面我们把注意力集中到这个属性。Jcr:data是jackrabbit的内在属性,目前一个node只能有一个这样的属性,这个属性中存放着二进制文档提取之后的fulltext的信息(当然你可以手动指定这个属性存放什么text)。现在我们就是要在结果集中高亮这个我们的keyword->’Maven’,之前ahuaxuan将maven的一篇文档放到jackrabbit中了,所以现在要高亮这个结果集。
要高亮之,必须得到Maven在这个text中的offset,所以就出现了下面这段代码:
就是这段代码,可能会给我们带来一些些小麻烦。得到offset之后,那么下一步肯定就是高亮了,高亮的逻辑简要说来就是遍历这个offset,并根据每个offset指定的start和end来插入高亮的标识。
看上去,一切顺理成章,我们的例子是使用1m的maven文档,测试的结果是:
也就是说计算每个term和对应的offset集合用了370ms,是整个搜索过程耗时的75%以上,如果把这一步省掉,可以节约75%的时间,那么如何省掉呢,两个方案,一,修改analyser,只找keyword和对应的offset,二,搜索的时候将term和对应offset保存起来。这样此处的370ms应该可以大大变小。经过ahuaxuan的修改之后,将offset保存在index之中,然后高亮的时候直接拿出来,避免了实时分词。测试的结果如下
也就是说这个方法从370ms变成了4ms,速度大大加快(虽然索引文件会变大一些)。
除此之外,在高亮的时候我们可以不用高亮全文,也就是取前10个offset,然后高亮之,再次运行相同的测试:
高亮从146ms变成了1ms
这样虽然只会高亮前10个关键字,但是速度从146ms变成了1ms.对于用户来说,高亮一个摘要并非不能接受。带来的好处是:
1 用户可以更加快速的得到结果集
2 大家知道,文字处理是非常消耗cpu的,通过这种方式也一定程度上节约了服务器上的cpu
对服务器,对客户都好,何乐而不为
通过以上两处的修改,原来高亮1m的文本需要530ms,而现在只需要5ms,提高了100倍。ahuaxuan以为,为了速度,这样的修改是值得的。
说到这里,查询这块可以告一段落了:
下面总结一下查询过程中的主要步骤:
1 编译query statement,生成AST
2 通过AST生成querynode
3 根据querynode生成lucene的query对象
4 执行lucene的query对象并得到结果集
5 遍历中:通过itemmanager和查询结果中的nodeid得到需要的node
6 生成offset
7 高亮之
由于ahuaxuan水平有限,见解未必全面,如有错误,请大家拍砖。
这段代码我也没有了,因为我发现这样做还是有问题,这样做会使lucene的index文件增加3倍,其中offset和解析出来的文本是不需要放进去的,索引文件越大,那么搜索或者以后改造成分布式的困难就越高。我比较推荐的做法是高亮还是放在客户端做吧
*author: ahuaxuan
*date: 2009-06-20
*/
查询进阶
在第一篇文章的例子中,我们看到jackrabbit中存放数据的流程还是比较清晰的,而且我们也基本确定了其中数据的存储方式,一颗m叉树。
正如db一样,insert都是看上去相对简单的,select相对总是没有这么简单的,如同关系数据库一样,jackrabbit中查询也有类似的流程,一般来说分成三个部分:1解析查询语句,2查询,3返回结果。这个应该是一个比较通用的模型。Jackrabbit的search部分亦不过如此。下面我们来看一个search的小例子。
public static void search(String co) throws Exception { Repository repository = new TransientRepository(); Session session = repository.login(new SimpleCredentials("username", "password".toCharArray())); try { // path query Workspace workSpace = session.getWorkspace(); QueryManager queryMgr = workSpace.getQueryManager(); //查询语句 String queryPath2 = "//world [jcr:contains(., 'Maven')] order by jcr:score() descending, @name ascending"; //编译查询语句 Query query = queryMgr.createQuery(queryPath2, Query.XPATH); //执行查询任务 QueryResult queryResult = query.execute(); //得到结果 RowIterator rows = queryResult.getRows(); String result = ""; while (rows.hasNext()) { Row resultRow = rows.nextRow(); /* Value[] values = resultRow.getValues(); for (Value value : values) { System.out.println(" Value ------ " + value.getString()); } */ // // -------------------------------------------------------------------------- TimerMasker tm = new TimerMasker(); result = resultRow.getValue("rep:excerpt(jcr:data)").getString(); tm.print("----------------------------------"); // } } finally { session.logout(); } }
这个例子很简单,使用xpath语法来查询world节点中包含指定字符串的同层节点。然后按照指定的属性排序并返回。从上面的例子中我们大概就只能得到这么多信息了,但是我想爱好技术的你一定不会满足,也一定不会止步于这个小小的例子。如果同第一篇文章所讲,我们不能只看到事物的表面,而是要通过这个表面来挖掘其背后的本质。
所以接下来让我们来看看事物背后的本质。众所周知,只要是数据存储方案,那么必定离不开一个东西,那就是索引,索引可以帮助我们快速找到我们想要的数据。那么jackrabbit是怎么根据xpath语法就找到对应的数据的呢,无疑,它也是在运用搜索技术,而且通过它的package列表我们可以看到,它其实就是利用lucene来完成了它的查询模块。也就是说在jackrabbit中,利用了xpath和lucene实现了它的查询模块。
搞清楚了这两点,我们就可以继续往下深入了。导入源代码,让我们从createQuery方法开始,这个方法很重要,可以说是非常重要,而且这个方法中也包含了一门基础学科-编译原理(jackrabbit中是使用javacc来生成xpath的词法语法分析类的org.apache.jackrabbit.core.query.xpath.Xpath,并且它的语法文件xpath.jj长达9000行,ahuaxuan暂时还没有这个闲心看下去)。createQuery方法的两个参数,一个是查询语句,还有一个是语句的语法,目前jackrabbit支持xpath语法和sql语法,他们在整个流程上属于一个可以相互替换的节点上,就是生成AST。再生产语法树之后,会生成查询节点树,这个颗树中包含了lucene需要的很多信息,比如需要搜索的key word,搜索时的排序字段等等。所以在第一大步骤createquery中我们可以细分为两个小的步骤:
1 编译query statement,生成AST
2 通过AST生成querynode
通过上面的两个主要步骤,我们顺利的得到了query对象,query对象就包含的查询所需要的基本条件了,很显然它和hibernate的query对象有异曲同工之妙。都是通过一个查询语句来生产query对象,然后query对象中包含了查询的所有信息,一旦执行execute方法,就真正的触发了的查询的流程。
说到这里就不得不看一下一个关键的方法(org.apache.jackrabbit.core.query.lucene.QueryImpl):
public QueryResult execute(long offset, long limit) throws RepositoryException { if (log.isDebugEnabled()) { log.debug("Executing query: \n" + root.dump()); } TimerMasker tk = new TimerMasker(); // 这里创建lucene的query对象,同时,这是一个BooleanQuery Query query = LuceneQueryBuilder.createQuery(root, session, index.getContext().getItemStateManager(), index.getNamespaceMappings(), index.getTextAnalyzer(), propReg, index.getSynonymProvider(), index.getIndexFormatVersion()); //得到排序信息 OrderQueryNode orderNode = root.getOrderNode(); OrderQueryNode.OrderSpec[] orderSpecs; if (orderNode != null) { orderSpecs = orderNode.getOrderSpecs(); } else { orderSpecs = new OrderQueryNode.OrderSpec[0]; } Name[] orderProperties = new Name[orderSpecs.length]; boolean[] ascSpecs = new boolean[orderSpecs.length]; for (int i = 0; i < orderSpecs.length; i++) { orderProperties[i] = orderSpecs[i].getProperty(); ascSpecs[i] = orderSpecs[i].isAscending(); } //创建查询结果,事实上,这个一部才是真正执行lucene query的//地方。这一点可以从QueryResultImpl的构造方法中看出来,构造方法中//的getresult其实就是真正执行lucene search的地方,比较顺理成章 QueryResultImpl qri = new QueryResultImpl(index, itemMgr, session, session.getAccessManager(), this, query, new SpellSuggestion(index.getSpellChecker(), root), getSelectProperties(), orderProperties, ascSpecs, getRespectDocumentOrder(), offset, limit); tk.print("execute"); return qri; }
通过上面的代码注释,那么我们可以得到一个有效的信息,那就是在ahuaxuan提供的示例代码中执行query.execute()的时候,这个时候,我们需要的结果其实就已经加载到内存中了。有的人会感到疑惑,为什么上面的QueryResultImpl中带有这么多构造参数,其实那些对象是用来组装lucene出来的结果的。因为放到索引中的数据并不是node的所有信息,比如一个node,因为放到索引中只是nodeid,其对应的node对象还是需要通过itemMgr这样的对象来得到。
这样我们又确定了第二大步骤包含了二个步骤:
1 根据querynode生成lucene的query对象
2 执行lucene的query对象并得到结果集
现在数据,也有了,那么接下来做什么呢?当然是遍历数据,拿出结果来,显然这个和jdbc的工作流程有得一拼啊(看来这里的时候,大家对查询的使用是不是比较清楚了,它和很多持久化框架在流程上是相当一致的,熟悉了一个之后,再来看其他的,学习的速度明显得到加强,当前前提是你已经充分理解了某个框架的内部原理)。那么接下来的事情只有这些:
//得到结果 RowIterator rows = queryResult.getRows(); while (rows.hasNext()) { Row resultRow = rows.nextRow(); resultRow.getValue("rep:excerpt(jcr:data)").getString(); }
看上去很简单。实际上也比较简单,但是其中确有一个ahuaxuan认为比较愚蠢的设计。拿这个例子来说,接下来的流程就是得到得到数据,并作一些客户端需要的操作,比如高亮。
通过代码的阅读:在这个大步骤中,又细分为以下步骤:
1 根据getvalue中参数判断查询的内容,比如jcr:score,jcr:path等,这些属性很容易就能得到,可以说是现成的,稍微看一下代码就知道他们是怎么获得的,而在上面的例子中,我们是要找jcr:data这个属性,并高亮之。而且这个属性也是最复杂的,其他属性比较简单,下面我们把注意力集中到这个属性。Jcr:data是jackrabbit的内在属性,目前一个node只能有一个这样的属性,这个属性中存放着二进制文档提取之后的fulltext的信息(当然你可以手动指定这个属性存放什么text)。现在我们就是要在结果集中高亮这个我们的keyword->’Maven’,之前ahuaxuan将maven的一篇文档放到jackrabbit中了,所以现在要高亮这个结果集。
要高亮之,必须得到Maven在这个text中的offset,所以就出现了下面这段代码:
Reader r = new StringReader(text); TokenStream ts = index.getTextAnalyzer().tokenStream("", r); Token t; try { while ((t = ts.next()) != null) { TermVectorOffsetInfo[] info = (TermVectorOffsetInfo[]) termMap.get(t.termText()); if (info == null) { info = new TermVectorOffsetInfo[1]; } else { TermVectorOffsetInfo[] tmp = info; info = new TermVectorOffsetInfo[tmp.length + 1]; System.arraycopy(tmp, 0, info, 0, tmp.length); } info[info.length - 1] = new TermVectorOffsetInfo( t.startOffset(), t.endOffset()); termMap.put(t.termText(), info); } } catch (IOException e) { // should never happen, we are reading from a string }
就是这段代码,可能会给我们带来一些些小麻烦。得到offset之后,那么下一步肯定就是高亮了,高亮的逻辑简要说来就是遍历这个offset,并根据每个offset指定的start和end来插入高亮的标识。
看上去,一切顺理成章,我们的例子是使用1m的maven文档,测试的结果是:
createTermPositionVector >>>-------- total nano seconds : 373531154 highlight:2761 >>>-------- total nano seconds : 146860414
也就是说计算每个term和对应的offset集合用了370ms,是整个搜索过程耗时的75%以上,如果把这一步省掉,可以节约75%的时间,那么如何省掉呢,两个方案,一,修改analyser,只找keyword和对应的offset,二,搜索的时候将term和对应offset保存起来。这样此处的370ms应该可以大大变小。经过ahuaxuan的修改之后,将offset保存在index之中,然后高亮的时候直接拿出来,避免了实时分词。测试的结果如下
new createTermPositionVector >>>-------- total nano seconds : 4003012
也就是说这个方法从370ms变成了4ms,速度大大加快(虽然索引文件会变大一些)。
除此之外,在高亮的时候我们可以不用高亮全文,也就是取前10个offset,然后高亮之,再次运行相同的测试:
highlight:10 >>>-------- total nano seconds : 1264721
高亮从146ms变成了1ms
这样虽然只会高亮前10个关键字,但是速度从146ms变成了1ms.对于用户来说,高亮一个摘要并非不能接受。带来的好处是:
1 用户可以更加快速的得到结果集
2 大家知道,文字处理是非常消耗cpu的,通过这种方式也一定程度上节约了服务器上的cpu
对服务器,对客户都好,何乐而不为
通过以上两处的修改,原来高亮1m的文本需要530ms,而现在只需要5ms,提高了100倍。ahuaxuan以为,为了速度,这样的修改是值得的。
说到这里,查询这块可以告一段落了:
下面总结一下查询过程中的主要步骤:
1 编译query statement,生成AST
2 通过AST生成querynode
3 根据querynode生成lucene的query对象
4 执行lucene的query对象并得到结果集
5 遍历中:通过itemmanager和查询结果中的nodeid得到需要的node
6 生成offset
7 高亮之
由于ahuaxuan水平有限,见解未必全面,如有错误,请大家拍砖。
评论
5 楼
lnjzliulei
2013-06-03
兄弟,有源代码吗?48703577@qq.com请给发一份,谢谢!
4 楼
ahuaxuan
2009-12-14
无量胖佛 写道
有你写的源码可以给发一个吗?
wddsmzx@gmail.com
谢谢了 老哥
wddsmzx@gmail.com
谢谢了 老哥
这段代码我也没有了,因为我发现这样做还是有问题,这样做会使lucene的index文件增加3倍,其中offset和解析出来的文本是不需要放进去的,索引文件越大,那么搜索或者以后改造成分布式的困难就越高。我比较推荐的做法是高亮还是放在客户端做吧
3 楼
无量胖佛
2009-12-10
有你写的源码可以给发一个吗?
wddsmzx@gmail.com
谢谢了 老哥
wddsmzx@gmail.com
谢谢了 老哥
2 楼
ahuaxuan
2009-06-20
其实我是先看index的,index早于search,index的流程过两天写出来,我把search这部分改了一下,把创建offset放到index的流程中了,highlighter也变成只取10个offset来作,在大文本的情况下,查询速度从原来的500ms缩小为5ms
1 楼
lxiaodao
2009-06-20
不错文章,对于query有了一定得了解,如果能有分析一下index的原理的文章就更好了。
发表评论
-
深入浅出jcr之16 该死的RMI,我们需要HTTP+简单RPC协议
2009-12-12 13:22 6708从这篇文 ... -
深入浅出jackrabbit之十五 文档提取优化2.docx
2009-10-22 18:38 3873/** *author:ahuaxuan *2009- ... -
深入浅出jackrabbit之十四 分布式文档提取
2009-09-24 12:20 4730/** *author:ahuaxuan *200 ... -
深入浅出jackrabbit之十三 查询之AST和QT
2009-09-10 10:12 3430简介:在前面的文章中 ... -
深入浅出jcr之十二 key-value存储系统
2009-08-26 09:31 3753作者:ahuaxuan 在写文章方面,惰性心理 ... -
深入浅出jcr之十一 jackrabbit改进要点
2009-08-18 18:22 3611作者,ahuaxuan 在看过前 ... -
深入浅出jcr之十 redolog 和 recovery.docx
2009-08-18 18:14 2141作者:ahuaxuan 在前面的 ... -
深入浅出 jackrabbit 九 索引合并(下)
2009-07-22 14:16 2084在上文中,ahuaxuan讲到了索引创建的主体流程,但是索引合 ... -
深入浅出 jackrabbit 八 索引合并(上)
2009-07-21 17:32 2538我们从文本提取的逻辑中走出来,回到主体流程。 在前面的文 ... -
深入浅出 jackrabbit 七 文本提取(下)
2009-07-21 17:29 2518接上文,说到文本提取,在上一篇文章中,我们是管中窥豹,并没有把 ... -
深入浅出 jackrabbit 六 文本提取(上)
2009-07-21 17:27 3310用lucene作过索引的同 ... -
深入浅出 jackrabbit 之五 索引提交(下)
2009-07-14 17:53 2251接上文,在上面一篇文章中,我们谈到了update中的Delet ... -
深入浅出 jackrabbit 之四 索引提交(上)
2009-07-14 09:10 2934在上上篇文章中,我们了解了创建索引的一般流程,在上篇文章中,我 ... -
深入浅出 jackrabbit 3 创建 document
2009-07-01 13:03 4301/** *作者:ahuaxuan 张荣华 *日期:2009-0 ... -
深入浅出 jackrabbit 2 索引概览
2009-06-30 08:51 5371任何一个数据库都离不 ... -
深入浅出 jackrabbit 1
2009-05-19 18:31 12469/** * author:ahuaxuan( ...
相关推荐
《深入浅出 Jackrabbit 1》 Jackrabbit 是一个开源的、实现了 Java Content Repository (JCR) API 的内容管理系统,它允许程序通过统一的方式访问、存储和管理各种数据,包括文本、图像、视频等多媒体信息。这篇...
JackRabbit学习参考资料总汇涉及了深入浅出的JackRabbit内容仓库API的学习,内容涉及多个专题,整个学习资料是PDF文档格式。从标签来看,这份资料主要涉及JackRabbit以及JCR(Java Content Repository)的内容仓库...
这个“jackrabbit最全入门教程”将会带你深入理解这个强大的内容管理解决方案。 首先,我们需要了解什么是JCR。JCR提供了一种统一的方式来访问和管理数字内容,无论这些内容是文档、图像、视频还是其他形式的数据。...
在本文中,我们将深入探讨Apache Jackrabbit的基础知识,以及如何开始使用它。 一、JCR和Apache Jackrabbit的概念 1. JCR:JCR为存储和检索非结构化信息提供了一个模型和API。它允许开发者创建可以跨各种存储后端...
### Jackrabbit 在项目实施中的常见问题与解决方案 #### 一、Jackrabbit简介 Jackrabbit 是一个完全用 Java 编写的 JCR(Java Content Repository)实现,它可以作为一个独立的服务运行,也可以嵌入到更大的应用...
Apache Jackrabbit是一个开源的、实现了Java Content Repository (JCR) API的内容管理系统,它允许开发者创建、管理和查询结构化和非结构化的数据。JCR是Java社区制定的一个标准,旨在为各种应用程序提供统一的数据...
Apache Jackrabbit 是一个...对于开发人员来说,理解这些功能以及如何将 Jackrabbit 集成到现有应用中是深入学习的关键部分。通过实践示例代码和探索 Jackrabbit API 文档,你可以逐步掌握这个强大的内容管理系统框架。
Apache Jackrabbit 提供了一个丰富的API,允许开发者创建、读取、更新和删除内容,并执行复杂的查询。API 包括多个关键组件,如`Session`、`Node`、`Property`和`Workspace`等,这些组件共同构成了内容管理的基础。 ...
这两个项目将帮助我们深入理解和快速入门Jackrabbit的使用。 1. Jackrabbit核心概念: - JCR:JSR 170定义了内容存储的标准接口,使得应用程序可以透明地访问和操作不同类型的存储系统。 - Node:在JCR中,内容被...
jackrabbit 1.5.6 jar
jackrabbit-standalone-1.6.5.jar是webDav的支持jar包。
标题中的“查看jackrabbit仓库的小工具”指的是一个用于观察和管理Apache Jackrabbit仓库的实用程序。Jackrabbit是Java Content Repository (JCR) API的一个开源实现,它提供了一个内容管理系统(CMS)的基础框架,...
Apache Jackrabbit 2.6 是一个开放源代码的、基于Java的内容管理系统(CMS),它实现了JCR(Java Content Repository)规范,提供了一种用于存储、管理和检索非结构化数据的标准接口。Jackrabbit 提供了一个高性能、...
jackrabbit-webdav-2.1.0.jar 具体用法可以网上查找
通过深入理解和使用"jackrabbit-webdav-2.3.2.src.zip"中的源代码,开发者不仅可以学习WebDAV协议的工作原理,还能了解如何在Android环境中实现高效稳定的WebDAV客户端功能。此外,对于想要对Jackrabbit进行定制化...
jackrabbit-api-1.5.0.jar
标题中的"jackrabbit-webdav-2.7.1.zip"指的是Apache Jackrabbit的一个特定版本——2.7.1的WebDAV模块的压缩包。Apache Jackrabbit是Java内容存储库(Content Repository)的一个实现,它遵循JCR(Java Content ...
jackrabbit开发用jar包,jackrabbit是基于Lucene的一种站内搜索技术,它用xml文件为他的元数据,自动穿件索引,使用xpath或者xquery的查询方法。
杰克兔(Jackrabbit)是Apache软件基金会的一个开源项目,主要致力于实现Java Content Repository (JCR) 规范。这个规范定义了一种用于存储、管理和检索结构化内容的标准API。在给定的压缩包"jackrabbit-jcr-commons...