Querying by node name

I have a need to query documents by their node name. We are using the HstQueryBuilder and it works fine where the node name starts with a letter:

HstQuery getByName(HstRequestContext context, String name) {
    String expression = String.format("fn:name()='%s'", name);
    HippoBean folder = context.getSiteContentBaseBean();
    
    return HstQueryBuilder.create(folder).ofTypes(HippoBean.class)
        .where(constraint(".").jcrExpression(expression)
        .limit(2).build();
}

Consider I have documents “top-8-rules” and “8-simple-rules”

builds an XPath query that looks like:

//element(*,myproject:basedocument)[(@hippo:paths='9db2d725-d54e-4331-81de-275d8ce158e4') and (@hippo:availability='live') and not(@jcr:primaryType='nt:frozenNode') and (fn:name()='top-8-rules')]

which works fine. However, when the name is “8-simple-rules”, the corresponding query doesn’t find the document.

Using the /cms/repository, can test several queries:

  • works: //element(*, cmsarchetype:contentdocument)[fn:name()=‘top-8-rules’]
  • fails: //element(*, cmsarchetype:contentdocument)[fn:name()=‘8-simple-rules’]
  • works (but isn’t what HstQueryBuilder creates): //top-8-rules[@hippo:availability=‘live’]
  • also works: //8-simple-rules[@hippo:availability=‘live’]

Are there other JCR expressions we can use than fn:name() = {value} ? Or should I build my query with a different API?

Node names starting with a number must be encoded properly.
You can use org.hippoecm.repository.api.NodeNameCodec utility class for this

Thank you, that encoding looks unusual but promising.

I discovered NodeNameCodec.java doesn’t behave as advertised. :frowning:

However, I did find a java method that DOES work that is part of the distribution: org.apache.jackrabbit.util.ISO9075.encode(name)

import org.hippoecm.repository.api.NodeNameCodec
import spock.lang.Specification
import spock.lang.Unroll

class NameEncodingSpec extends Specification {

    //@Unroll
    def "NodeNameCodec should encode #name to #value"() {
        expect:
        value == NodeNameCodec.encode(name)

        where:
        name             | value
        'top-8-rules'    | 'top-8-rules'
        '8-simple-rules' | '_x0038_-simple-rules'   // FAILS
    }

    @Unroll
    def "jackrabbit encoding #name to #value"() {
        expect:
        value == org.apache.jackrabbit.util.ISO9075.encode(name)

        where:
        name             | value
        'top-8-rules'    | 'top-8-rules'
        '8-simple-rules' | '_x0038_-simple-rules'
        'a'              | 'a'
        '8'              | '_x0038_'
        'ab'             | 'ab'
        'abc'            | 'abc'
        'abc-123'        | 'abc-123'
        '123-abc'        | '_x0031_23-abc'
    }
}

I heard from other sources the NodeNameCode was designed to handle encoding nodes with namespaces in their name, such as myproject:8-simple-rules. Instead the jackrabbit method is appropriate for my case.