Skip to content

Commit 030537c

Browse files
authored
Merge pull request #17 from evolvedbinary/7.x.x/hotfix/range-index-attr-prefix
[7.x.x] Make sure that Attribute QNames are correctly handled within conditions of the Range Index config
2 parents 77a8330 + 59debd1 commit 030537c

File tree

4 files changed

+106
-15
lines changed

4 files changed

+106
-15
lines changed

extensions/indexes/range/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@
193193
<include>pom.xml</include>
194194
<include>src/test/resources-filtered/conf.xml</include>
195195
<include>src/test/resources/log4j2.xml</include>
196+
<include>src/test/xquery/range/conditions.xql</include>
196197
<include>src/main/java/org/exist/indexing/range/RangeIndexAnalyzer.java</include>
197198
<include>src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java</include>
198199
<include>src/main/java/org/exist/indexing/range/RangeIndexConfigElement.java</include>
@@ -210,6 +211,7 @@
210211
<exclude>pom.xml</exclude>
211212
<exclude>src/test/resources-filtered/conf.xml</exclude>
212213
<exclude>src/test/resources/log4j2.xml</exclude>
214+
<exclude>src/test/xquery/range/conditions.xql</exclude>
213215
<exclude>src/main/java/org/exist/indexing/range/RangeIndexAnalyzer.java</exclude>
214216
<exclude>src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java</exclude>
215217
<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 & 7 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;
@@ -78,8 +80,7 @@
7880
*/
7981
public class RangeIndexConfigAttributeCondition extends RangeIndexConfigCondition{
8082

81-
private final String attributeName;
82-
private final QName attribute;
83+
private final QName attributeName;
8384
private @Nullable final String value;
8485
private final Operator operator;
8586
private final boolean caseSensitive;
@@ -96,13 +97,23 @@ public RangeIndexConfigAttributeCondition(final Element elem, final NodePath par
9697
"Range index module: Attribute condition cannot be defined for an attribute:" + parentPath);
9798
}
9899

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

104105
try {
105-
this.attribute = new QName(QName.extractLocalName(this.attributeName), XMLConstants.NULL_NS_URI, QName.extractPrefix(this.attributeName), ElementValue.ATTRIBUTE);
106+
final String attrLocalName = QName.extractLocalName(attributeValue);
107+
@Nullable final String attrPrefix = QName.extractPrefix(attributeValue);
108+
if (attrPrefix != null) {
109+
@Nullable final String attrNamespace = findNamespaceForPrefix(attrPrefix, (ElementImpl) elem);
110+
if (attrNamespace == null) {
111+
throw new DatabaseConfigurationException("Range index module: Missing namespace declaration for attribute qname in condition");
112+
}
113+
this.attributeName = new QName(attrLocalName, attrNamespace, attrPrefix, ElementValue.ATTRIBUTE);
114+
} else {
115+
this.attributeName = new QName(attrLocalName, XMLConstants.NULL_NS_URI, null, ElementValue.ATTRIBUTE);
116+
}
106117
} catch (final QName.IllegalQNameException e) {
107118
throw new DatabaseConfigurationException("Rand index module error: " + e.getMessage(), e);
108119
}
@@ -171,6 +182,24 @@ public RangeIndexConfigAttributeCondition(final Element elem, final NodePath par
171182

172183
}
173184

185+
private static @Nullable String findNamespaceForPrefix(final String prefix, ElementImpl contextElem) {
186+
while (contextElem != null) {
187+
final String namespace = contextElem.getNamespaceForPrefix(prefix);
188+
if (namespace != null) {
189+
return namespace;
190+
}
191+
192+
@Nullable final Node parentNode = contextElem.getParentNode();
193+
if (parentNode != null && parentNode instanceof ElementImpl) {
194+
contextElem = (ElementImpl) parentNode;
195+
} else {
196+
contextElem = null;
197+
}
198+
}
199+
200+
return null;
201+
}
202+
174203
// lazily evaluate lowercase value to convert once when needed
175204
private String getLowercaseValue() {
176205
if (this.lowercaseValue == null) {
@@ -184,8 +213,16 @@ private String getLowercaseValue() {
184213

185214
@Override
186215
public boolean matches(final Node node) {
216+
final String attrValue;
217+
if (attributeName.hasNamespace()) {
218+
attrValue = ((Element) node).getAttributeNS(attributeName.getNamespaceURI(), attributeName.getLocalPart());
219+
} else {
220+
attrValue = ((Element) node).getAttribute(attributeName.getLocalPart());
221+
}
222+
187223
return node.getNodeType() == Node.ELEMENT_NODE
188-
&& matchValue(((Element) node).getAttribute(attributeName));
224+
&& matchValue(attrValue);
225+
189226
}
190227

191228
private boolean matchValue(final String testValue) {
@@ -312,7 +349,7 @@ public boolean find(final Predicate predicate) {
312349
}
313350

314351
final QName qname = testStep.getTest().getName();
315-
if (qname.getNameType() != ElementValue.ATTRIBUTE || !qname.equals(attribute)) {
352+
if (qname.getNameType() != ElementValue.ATTRIBUTE || !qname.equals(attributeName)) {
316353
return false;
317354
}
318355

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)