diff --git a/extensions/indexes/range/pom.xml b/extensions/indexes/range/pom.xml index 4f55d02e27..1cb3ccc2ba 100644 --- a/extensions/indexes/range/pom.xml +++ b/extensions/indexes/range/pom.xml @@ -193,6 +193,7 @@ pom.xml src/test/resources-filtered/conf.xml src/test/resources/log4j2.xml + src/test/xquery/range/conditions.xql src/main/java/org/exist/indexing/range/RangeIndexAnalyzer.java src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java src/main/java/org/exist/indexing/range/RangeIndexConfigElement.java @@ -210,6 +211,7 @@ pom.xml src/test/resources-filtered/conf.xml src/test/resources/log4j2.xml + src/test/xquery/range/conditions.xql src/main/java/org/exist/indexing/range/RangeIndexAnalyzer.java src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java src/main/java/org/exist/indexing/range/RangeIndexConfigElement.java diff --git a/extensions/indexes/range/src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java b/extensions/indexes/range/src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java index 3f2d6c26ea..3953608811 100644 --- a/extensions/indexes/range/src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java +++ b/extensions/indexes/range/src/main/java/org/exist/indexing/range/RangeIndexConfigAttributeCondition.java @@ -46,6 +46,8 @@ package org.exist.indexing.range; import org.exist.dom.QName; +import org.exist.dom.persistent.ElementImpl; +import org.exist.dom.persistent.NodeImpl; import org.exist.storage.ElementValue; import org.exist.storage.NodePath; import org.exist.util.DatabaseConfigurationException; @@ -78,8 +80,7 @@ */ public class RangeIndexConfigAttributeCondition extends RangeIndexConfigCondition{ - private final String attributeName; - private final QName attribute; + private final QName attributeName; private @Nullable final String value; private final Operator operator; private final boolean caseSensitive; @@ -96,13 +97,23 @@ public RangeIndexConfigAttributeCondition(final Element elem, final NodePath par "Range index module: Attribute condition cannot be defined for an attribute:" + parentPath); } - this.attributeName = elem.getAttribute("attribute"); - if (this.attributeName.isEmpty()) { + final String attributeValue = elem.getAttribute("attribute"); + if (attributeValue.isEmpty()) { throw new DatabaseConfigurationException("Range index module: Empty or no attribute qname in condition"); } try { - this.attribute = new QName(QName.extractLocalName(this.attributeName), XMLConstants.NULL_NS_URI, QName.extractPrefix(this.attributeName), ElementValue.ATTRIBUTE); + final String attrLocalName = QName.extractLocalName(attributeValue); + @Nullable final String attrPrefix = QName.extractPrefix(attributeValue); + if (attrPrefix != null) { + @Nullable final String attrNamespace = findNamespaceForPrefix(attrPrefix, (ElementImpl) elem); + if (attrNamespace == null) { + throw new DatabaseConfigurationException("Range index module: Missing namespace declaration for attribute qname in condition"); + } + this.attributeName = new QName(attrLocalName, attrNamespace, attrPrefix, ElementValue.ATTRIBUTE); + } else { + this.attributeName = new QName(attrLocalName, XMLConstants.NULL_NS_URI, null, ElementValue.ATTRIBUTE); + } } catch (final QName.IllegalQNameException e) { throw new DatabaseConfigurationException("Rand index module error: " + e.getMessage(), e); } @@ -171,6 +182,24 @@ public RangeIndexConfigAttributeCondition(final Element elem, final NodePath par } + private static @Nullable String findNamespaceForPrefix(final String prefix, ElementImpl contextElem) { + while (contextElem != null) { + final String namespace = contextElem.getNamespaceForPrefix(prefix); + if (namespace != null) { + return namespace; + } + + @Nullable final Node parentNode = contextElem.getParentNode(); + if (parentNode != null && parentNode instanceof ElementImpl) { + contextElem = (ElementImpl) parentNode; + } else { + contextElem = null; + } + } + + return null; + } + // lazily evaluate lowercase value to convert once when needed private String getLowercaseValue() { if (this.lowercaseValue == null) { @@ -184,8 +213,16 @@ private String getLowercaseValue() { @Override public boolean matches(final Node node) { + final String attrValue; + if (attributeName.hasNamespace()) { + attrValue = ((Element) node).getAttributeNS(attributeName.getNamespaceURI(), attributeName.getLocalPart()); + } else { + attrValue = ((Element) node).getAttribute(attributeName.getLocalPart()); + } + return node.getNodeType() == Node.ELEMENT_NODE - && matchValue(((Element) node).getAttribute(attributeName)); + && matchValue(attrValue); + } private boolean matchValue(final String testValue) { @@ -312,7 +349,7 @@ public boolean find(final Predicate predicate) { } final QName qname = testStep.getTest().getName(); - if (qname.getNameType() != ElementValue.ATTRIBUTE || !qname.equals(attribute)) { + if (qname.getNameType() != ElementValue.ATTRIBUTE || !qname.equals(attributeName)) { return false; } diff --git a/extensions/indexes/range/src/test/xquery/range/conditions.xql b/extensions/indexes/range/src/test/xquery/range/conditions.xql index a3951eac99..a78572cbd4 100644 --- a/extensions/indexes/range/src/test/xquery/range/conditions.xql +++ b/extensions/indexes/range/src/test/xquery/range/conditions.xql @@ -1,4 +1,28 @@ (: + : Elemental + : Copyright (C) 2024, Evolved Binary Ltd + : + : admin@evolvedbinary.com + : https://www.evolvedbinary.com | https://www.elemental.xyz + : + : This library is free software; you can redistribute it and/or + : modify it under the terms of the GNU Lesser General Public + : License as published by the Free Software Foundation; version 2.1. + : + : This library is distributed in the hope that it will be useful, + : but WITHOUT ANY WARRANTY; without even the implied warranty of + : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + : Lesser General Public License for more details. + : + : You should have received a copy of the GNU Lesser General Public + : License along with this library; if not, write to the Free Software + : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + : + : NOTE: Parts of this file contain code from 'The eXist-db Authors'. + : The original license header is included below. + : + : ===================================================================== + : : eXist-db Open Source Native XML Database : Copyright (C) 2001 The eXist-db Authors : @@ -36,7 +60,9 @@ declare namespace stats="http://exist-db.org/xquery/profiling"; declare variable $ct:COLLECTION_CONFIG := + xmlns:tei="http://www.tei-c.org/ns/1.0" + xmlns:other1="http://other1" + xmlns:other2="http://other2"> @@ -59,6 +85,14 @@ declare variable $ct:COLLECTION_CONFIG := + + + + + + + + @@ -134,7 +168,7 @@ declare variable $ct:COLLECTION_CONFIG := declare variable $ct:DATA := - + conditional fields! @@ -143,7 +177,7 @@ declare variable $ct:DATA := - publiziert + publiziert literarisch @@ -161,10 +195,11 @@ declare variable $ct:DATA := Alexandria - startswithendswith - foo - foo - literarisch + startswithendswith + foo + foo + literarisch + other1-not-same-namespace @@ -533,3 +568,20 @@ function ct:optimize-matches-no-case() { collection($ct:COLLECTION)//tei:p[matches(@type, "bb")][. = "something"] }; +(:~ + : See: https://github.com/eXist-db/exist/issues/5189 + :) +declare + %test:assertEquals("publiziert", "startswithendswith") +function ct:other1-index-keys() { + range:index-keys-for-field("other1", function($k, $n) { $k }, 10) +}; + +(:~ + : See: https://github.com/eXist-db/exist/issues/5189 + :) +declare + %test:assertEquals("foo", "literarisch", "startswithendswith") +function ct:other2-index-keys() { + range:index-keys-for-field("other2", function($k, $n) { $k }, 10) +}; diff --git a/schema/collection.xconf.xsd b/schema/collection.xconf.xsd index 43574fe1ea..7a40be0898 100644 --- a/schema/collection.xconf.xsd +++ b/schema/collection.xconf.xsd @@ -333,7 +333,7 @@ - +