Skip to content

Commit a623725

Browse files
authored
Merge pull request #38 from evolvedbinary/6.x.x/hotfix/range-index-attr-prefix
[6.x.x] Make sure that Attribute QNames are correctly handled within conditions of the Range Index config
2 parents 9154468 + a36a2f6 commit a623725

File tree

4 files changed

+106
-20
lines changed

4 files changed

+106
-20
lines changed

extensions/indexes/range/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@
186186
<include>pom.xml</include>
187187
<include>src/test/resources-filtered/conf.xml</include>
188188
<include>src/test/resources/log4j2.xml</include>
189+
<include>src/test/xquery/range/conditions.xql</include>
189190
<include>src/main/java/org/exist/indexing/range/RangeIndexAnalyzer.java</include>
190191
<include>src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java</include>
191192
<include>src/main/java/org/exist/indexing/range/RangeIndexConfigElement.java</include>
@@ -203,6 +204,7 @@
203204
<exclude>pom.xml</exclude>
204205
<exclude>src/test/resources-filtered/conf.xml</exclude>
205206
<exclude>src/test/resources/log4j2.xml</exclude>
207+
<exclude>src/test/xquery/range/conditions.xql</exclude>
206208
<exclude>src/main/java/org/exist/indexing/range/RangeIndexAnalyzer.java</exclude>
207209
<exclude>src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java</exclude>
208210
<exclude>src/main/java/org/exist/indexing/range/RangeIndexConfigElement.java</exclude>

extensions/indexes/range/src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
package org.exist.indexing.range;
4747

4848
import org.exist.dom.QName;
49+
import org.exist.dom.persistent.ElementImpl;
50+
import org.exist.dom.persistent.NodeImpl;
4951
import org.exist.storage.ElementValue;
5052
import org.exist.storage.NodePath;
5153
import org.exist.util.DatabaseConfigurationException;
@@ -77,8 +79,7 @@
7779
*/
7880
public class RangeIndexConfigAttributeCondition extends RangeIndexConfigCondition{
7981

80-
private final String attributeName;
81-
private final QName attribute;
82+
private final QName attributeName;
8283
private @Nullable final String value;
8384
private final Operator operator;
8485
private final boolean caseSensitive;
@@ -94,13 +95,23 @@ public RangeIndexConfigAttributeCondition(Element elem, NodePath parentPath) thr
9495
throw new DatabaseConfigurationException("Range index module: Attribute condition cannot be defined for an attribute:" + parentPath.toString());
9596
}
9697

97-
this.attributeName = elem.getAttribute("attribute");
98-
if (this.attributeName.isEmpty()) {
98+
final String attributeValue = elem.getAttribute("attribute");
99+
if (attributeValue.isEmpty()) {
99100
throw new DatabaseConfigurationException("Range index module: Empty or no attribute qname in condition");
100101
}
101102

102103
try {
103-
this.attribute = new QName(QName.extractLocalName(this.attributeName), XMLConstants.NULL_NS_URI, QName.extractPrefix(this.attributeName), ElementValue.ATTRIBUTE);
104+
final String attrLocalName = QName.extractLocalName(attributeValue);
105+
@Nullable final String attrPrefix = QName.extractPrefix(attributeValue);
106+
if (attrPrefix != null) {
107+
@Nullable final String attrNamespace = findNamespaceForPrefix(attrPrefix, (ElementImpl) elem);
108+
if (attrNamespace == null) {
109+
throw new DatabaseConfigurationException("Range index module: Missing namespace declaration for attribute qname in condition");
110+
}
111+
this.attributeName = new QName(attrLocalName, attrNamespace, attrPrefix, ElementValue.ATTRIBUTE);
112+
} else {
113+
this.attributeName = new QName(attrLocalName, XMLConstants.NULL_NS_URI, null, ElementValue.ATTRIBUTE);
114+
}
104115
} catch (final QName.IllegalQNameException e) {
105116
throw new DatabaseConfigurationException("Rand index module error: " + e.getMessage(), e);
106117
}
@@ -169,6 +180,24 @@ public RangeIndexConfigAttributeCondition(Element elem, NodePath parentPath) thr
169180

170181
}
171182

183+
private static @Nullable String findNamespaceForPrefix(final String prefix, ElementImpl contextElem) {
184+
while (contextElem != null) {
185+
final String namespace = contextElem.getNamespaceForPrefix(prefix);
186+
if (namespace != null) {
187+
return namespace;
188+
}
189+
190+
@Nullable final Node parentNode = contextElem.getParentNode();
191+
if (parentNode != null && parentNode instanceof ElementImpl) {
192+
contextElem = (ElementImpl) parentNode;
193+
} else {
194+
contextElem = null;
195+
}
196+
}
197+
198+
return null;
199+
}
200+
172201
// lazily evaluate lowercase value to convert once when needed
173202
private String getLowercaseValue() {
174203
if (this.lowercaseValue == null) {
@@ -180,15 +209,18 @@ private String getLowercaseValue() {
180209
return this.lowercaseValue;
181210
}
182211

183-
184212
@Override
185-
public boolean matches(Node node) {
186-
187-
if (node.getNodeType() == Node.ELEMENT_NODE && matchValue(((Element)node).getAttribute(attributeName))) {
188-
return true;
213+
public boolean matches(final Node node) {
214+
final String attrValue;
215+
if (attributeName.hasNamespace()) {
216+
attrValue = ((Element) node).getAttributeNS(attributeName.getNamespaceURI(), attributeName.getLocalPart());
217+
} else {
218+
attrValue = ((Element) node).getAttribute(attributeName.getLocalPart());
189219
}
190220

191-
return false;
221+
return node.getNodeType() == Node.ELEMENT_NODE
222+
&& matchValue(attrValue);
223+
192224
}
193225

194226
private boolean matchValue(String testValue) {
@@ -365,7 +397,7 @@ public boolean find(Predicate predicate) {
365397

366398
if (qname.getNameType() == ElementValue.ATTRIBUTE
367399
&& operator.equals(this.operator)
368-
&& qname.equals(this.attribute)
400+
&& qname.equals(attributeName)
369401
&& valueTypeMatches
370402
&& foundValue.equals(requiredValue)) {
371403

extensions/indexes/range/src/test/xquery/range/conditions.xql

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
(:
2+
: Elemental
3+
: Copyright (C) 2024, Evolved Binary Ltd
4+
:
5+
6+
: https://www.evolvedbinary.com | https://www.elemental.xyz
7+
:
8+
: This library is free software; you can redistribute it and/or
9+
: modify it under the terms of the GNU Lesser General Public
10+
: License as published by the Free Software Foundation; version 2.1.
11+
:
12+
: This library is distributed in the hope that it will be useful,
13+
: but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
: Lesser General Public License for more details.
16+
:
17+
: You should have received a copy of the GNU Lesser General Public
18+
: License along with this library; if not, write to the Free Software
19+
: Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
:
21+
: NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+
: The original license header is included below.
23+
:
24+
: =====================================================================
25+
:
226
: eXist-db Open Source Native XML Database
327
: Copyright (C) 2001 The eXist-db Authors
428
:
@@ -36,7 +60,9 @@ declare namespace stats="http://exist-db.org/xquery/profiling";
3660
declare variable $ct:COLLECTION_CONFIG :=
3761
<collection xmlns="http://exist-db.org/collection-config/1.0">
3862
<index xmlns:xs="http://www.w3.org/2001/XMLSchema"
39-
xmlns:tei="http://www.tei-c.org/ns/1.0">
63+
xmlns:tei="http://www.tei-c.org/ns/1.0"
64+
xmlns:other1="http://other1"
65+
xmlns:other2="http://other2">
4066
<range>
4167
<create qname="tei:note">
4268
<condition attribute="type" value="availability" />
@@ -59,6 +85,14 @@ declare variable $ct:COLLECTION_CONFIG :=
5985
<field name="orig_place" match="tei:place/tei:placeName" type="xs:string" case="no"></field>
6086
</create>
6187
<create qname="tei:note" type="xs:string" case="no" />
88+
<create qname="tei:note">
89+
<condition attribute="other1:type" value="other1" />
90+
<field name="other1" type="xs:string" case="no"/>
91+
</create>
92+
<create qname="tei:note">
93+
<condition attribute="other2:type" value="other2" />
94+
<field name="other2" type="xs:string" case="no"/>
95+
</create>
6296
<create qname="tei:placeName">
6397
<condition attribute="type" value="someType" />
6498
<condition attribute="cert" value="high" />
@@ -134,7 +168,7 @@ declare variable $ct:COLLECTION_CONFIG :=
134168

135169

136170
declare variable $ct:DATA :=
137-
<TEI xmlns="http://www.tei-c.org/ns/1.0">
171+
<TEI xmlns="http://www.tei-c.org/ns/1.0" xmlns:other1="http://other1" xmlns:other2="http://other2">
138172
<teiHeader>
139173
<fileDesc>
140174
<titleStmt><title>conditional fields!</title></titleStmt>
@@ -143,7 +177,7 @@ declare variable $ct:DATA :=
143177
<msDesc>
144178
<msContents>
145179
<msItemStruct>
146-
<note type="availability">publiziert</note>
180+
<note type="availability" other1:type="other1">publiziert</note>
147181
<note type="text_type">literarisch</note>
148182
<note type="orig_place">
149183
<place>
@@ -161,10 +195,11 @@ declare variable $ct:DATA :=
161195
<placeName cert="high" type="someOtherType">Alexandria</placeName>
162196
</place>
163197
</note>
164-
<note type="start_end">startswithendswith</note>
165-
<note>foo</note>
166-
<note type="bar">foo</note>
167-
<note type="something">literarisch</note>
198+
<note type="start_end" other1:type="other1" other2:type="other2">startswithendswith</note>
199+
<note type="other2" other1:type="start_end">foo</note>
200+
<note type="bar" other2:type="other2">foo</note>
201+
<note type="something" other2:type="other2">literarisch</note>
202+
<note xmlns:other1="http://not-other-one" other1:type="other1">other1-not-same-namespace</note>
168203
</msItemStruct>
169204
</msContents>
170205
</msDesc>
@@ -533,3 +568,20 @@ function ct:optimize-matches-no-case() {
533568
collection($ct:COLLECTION)//tei:p[matches(@type, "bb")][. = "something"]
534569
};
535570

571+
(:~
572+
: See: https://github.com/eXist-db/exist/issues/5189
573+
:)
574+
declare
575+
%test:assertEquals("publiziert", "startswithendswith")
576+
function ct:other1-index-keys() {
577+
range:index-keys-for-field("other1", function($k, $n) { $k }, 10)
578+
};
579+
580+
(:~
581+
: See: https://github.com/eXist-db/exist/issues/5189
582+
:)
583+
declare
584+
%test:assertEquals("foo", "literarisch", "startswithendswith")
585+
function ct:other2-index-keys() {
586+
range:index-keys-for-field("other2", function($k, $n) { $k }, 10)
587+
};

schema/collection.xconf.xsd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@
333333
<xs:attribute name="at" type="xs:anyURI" use="required" form="unqualified"/>
334334
</xs:attributeGroup>
335335
<xs:attributeGroup name="attributeReq">
336-
<xs:attribute name="attribute" type="xs:NCName" use="required"/>
336+
<xs:attribute name="attribute" type="xs:QName" use="required"/>
337337
</xs:attributeGroup>
338338
<xs:attributeGroup name="boostOpt">
339339
<xs:attribute name="boost" type="xs:double" use="optional" form="unqualified"/>

0 commit comments

Comments
 (0)