Hi,
because of the (ever growing) growing size of some of our workspaces,
we had the need to tweak the indexing configuration to exclude
indexing on any properties based on a condition that specifies solely
the node name, the local name or the node namespace, and the default
implementation would not allow that easily. And we found that adding
explicit support for the ancestor-or-self and self axis was useful, so
I tried to make some changes to
org.apache.jackrabbit.core.query.lucene.IndexingConfigurationImpl
With this quick (and maybe not very elegant) patch (coded and tested,
but not very thoroughly, against jackrabbit-core 2.2.10), we can
support indexing with rules like the following:
<configuration xmlns=""
xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:html="http://www.w3.org/1999/xhtml">
<!-- exclude all the properties of nt:unstructured nodes called 'body'
in the default namespace and their children -->
<index-rule nodeType="nt:unstructured"
condition="ancestor-or-self::body">
</index-rule>
<!-- exclude all the properties of nt:unstructured nodes called
'headline' in any namespace and their children -->
<index-rule nodeType="nt:unstructured"
condition="ancestor-or-self::*:headline">
</index-rule>
<!-- exclude all the nt:unstructured children nodes of a node in the
html namespace -->
<index-rule nodeType="nt:unstructured"
condition="ancestor::html:*">
</index-rule>
</configuration>
The test for node name was already implemented, but it required the
test of a property value as well, and, while we got away with
something like the following, it didn't seem too intuitive.
<index-rule nodeType="nt:unstructured"
condition="ancestor::body/@jcr:primaryType='{http://www.jcp.org/jcr/nt/1.0}unstructured'">
</index-rule>
As a side note, if you see in the example pasted above we had to add
the empty namespace declaration to make the indexing rule recognize a
node or a property name in the default namespace (even before the
patch). Not sure if this is a well known behavior, but it took a while
for us to understand why our conditions were ignored. Adding the
namespace declaration fixed the problem.
Hoping it may be useful to some.
Alessandro
Index: IndexingConfigurationImpl.java
===================================================================
--- IndexingConfigurationImpl.java (revision 1237095)
+++ IndexingConfigurationImpl.java (working copy)
@@ -506,16 +506,23 @@
int axis;
Name elementTest = null;
Name nameTest = null;
- Name propertyName;
- String propertyValue;
+ Name propertyName = null;
+ String propertyValue = null;
// parse axis
if (conditionString.startsWith("ancestor::")) {
axis = PathExpression.ANCESTOR;
idx = "ancestor::".length();
+ }
+ else if (conditionString.startsWith("ancestor-or-self::")) {
+ axis = PathExpression.ANCESTOR_OR_SELF;
+ idx = "ancestor-or-self::".length();
} else if (conditionString.startsWith("parent::")) {
axis = PathExpression.PARENT;
idx = "parent::".length();
+ } else if (conditionString.startsWith("self::")) {
+ axis = PathExpression.PARENT;
+ idx = "self::".length();
} else if (conditionString.startsWith("@")) {
axis = PathExpression.SELF;
idx = "@".length();
@@ -538,28 +545,42 @@
elementTest = resolver.getQName(ISO9075.decode(type));
idx += ")/@".length();
} else {
- if (axis == PathExpression.ANCESTOR
+ if (axis == PathExpression.ANCESTOR
+ || axis == PathExpression.ANCESTOR_OR_SELF
|| axis == PathExpression.CHILD
|| axis == PathExpression.PARENT) {
// simple name test
- String name = conditionString.substring(idx,
- conditionString.indexOf('/', idx));
- if (!name.equals("*")) {
+ String name;
+ if (conditionString.indexOf('/')!=-1) {
+ name = conditionString.substring(idx,
+ conditionString.indexOf('/', idx));
+ } else {
+ name = conditionString.substring(idx);
+ }
+ if (name.endsWith(":*")){
+ // namespace test, using unlikely element name (could
be even more unlikely of course)
+ nameTest =
resolver.getQName(name.substring(0,name.indexOf(':')) + ":_star_");
+ } else if (name.startsWith("*:")){
+ // local name test, using unlikely namespace uri (could
be even more unlikely of course)
+ nameTest =
resolver.getQName("{_star_}"+name.substring(name.indexOf(':')+1));
+ } else if (!name.equals("*")) {
nameTest = resolver.getQName(ISO9075.decode(name));
- }
+ }
idx += name.length() + "/@".length();
}
}
- // parse property name
- int eq = conditionString.indexOf('=', idx);
- String name = conditionString.substring(idx, eq).trim();
- propertyName = resolver.getQName(ISO9075.decode(name));
-
- // parse string value
- int quote = conditionString.indexOf('\'', eq) + 1;
- propertyValue = conditionString.substring(quote,
- conditionString.indexOf('\'', quote));
+ if (idx < conditionString.length()) {
+ // parse property name
+ int eq = conditionString.indexOf('=', idx);
+ String name = conditionString.substring(idx, eq).trim();
+ propertyName = resolver.getQName(ISO9075.decode(name));
+
+ // parse string value
+ int quote = conditionString.indexOf('\'', eq) + 1;
+ propertyValue = conditionString.substring(quote,
+ conditionString.indexOf('\'', quote));
+ }
} catch (IndexOutOfBoundsException e) {
throw new MalformedPathException(conditionString);
}
@@ -899,6 +920,8 @@
static final int PARENT = 3;
+ static final int ANCESTOR_OR_SELF = 4;
+
private final int axis;
private final Name elementTest;
@@ -947,12 +970,13 @@
}
}
};
- } else if (axis == ANCESTOR) {
+ } else if (axis == ANCESTOR || axis == ANCESTOR_OR_SELF ) {
try {
nodeStates = new Iterator() {
- private NodeState next = context.getParentId() == null
? null :
- (NodeState)
ism.getItemState(context.getParentId());
+ private NodeState next = (axis == ANCESTOR) ?
+ (context.getParentId() == null ? null :
(NodeState) ism.getItemState(context.getParentId())) :
+ context;
public void remove() {
throw new UnsupportedOperationException();
@@ -1003,13 +1027,32 @@
&& !current.getNodeTypeName().equals(elementTest))
{
continue;
}
- if (nameTest != null
- &&
!hmgr.getName(current.getNodeId()).equals(nameTest)) {
- continue;
+ if (nameTest != null) {
+ Name nodeName = hmgr.getName(current.getNodeId());
+ if ((nameTest.getLocalName().equals("_star_"))) {
+ // check namespace uri for equality
+ if
(!nameTest.getNamespaceURI().equals(nodeName.getNamespaceURI())) {
+ continue;
+ }
+ } else if
(nameTest.getNamespaceURI().equals("{_star_}")) {
+ // check local name for equality
+ if
(!nameTest.getLocalName().equals(nodeName.getLocalName())) {
+ continue;
+ }
+ } else if (!nameTest.equals(nodeName)) {
+ continue;
+ }
}
+
+ // support for no property value test
+ if (null==propertyName) {
+ return true;
+ }
+
if (!current.hasPropertyName(propertyName)) {
continue;
}
+
PropertyId propId = new PropertyId(
current.getNodeId(), propertyName);
PropertyState propState =