Recursive search in JCR repo via java
Asked Answered
H

3

1

I know how to search for something in the JCR via JCR SQL2 queries.

However, I would like to use Java in certain cases, using the JCR API: javax.jcr.Node, javax.jcr.NodeIterator and the like.

I'm afraid I will simply reinvent the wheel by coding my own.

Is there anything already available (Gist, Github or else)?

Hollenbeck answered 21/12, 2016 at 16:11 Comment(0)
A
2

You can use for that SlingQuery. It's inspired by jQuery and follows it's syntax. You should use it only to search small number of nodes (in best case under 100), because traversal queries are slow.

Edit

Your example may be converted to the following SlingQueries (not tested):

SlingQuery.$(startResource).find("[name=teasers][title=awesome-teaser]")

SlingQuery.$(startResource).find("[name][controlName]")

SlingQuery is part of Apache Sling since a while, that's why the github repository seems to be abandoned.

Note: You can static import the Dollar sign and drop the static access over SlingQuery in your syntax such as $(resource).find("...");

Apocrypha answered 22/12, 2016 at 12:59 Comment(3)
Could you convert one of the method examples of my answer into a SlingQuery example?Hollenbeck
SlingQuery looks rather "quiet". Not sure if I'd like to rely on such project. See github.com/Cognifide/Sling-Query/graphs/contributorsHollenbeck
Please see my edit above. The SlingQuery project is part of Apache Sling now. I converted also your examples to SlingQueries, but I haven't tested them. The syntax is exactly as the jQuery syntax. I'm using the project in a couple customer projects and it works stable, no worry about that.Apocrypha
G
2

You could use a ItemVisitor to recursively traverse the repository collecting all items that match your criteria along the way.

E.g. printing out all properties of type Long:

Node root = session.getRootNode();
root.accept(new ItemVisitor() {
    @Override
    public void visit(Property property) throws RepositoryException {
        if (!property.isMultiple() && property.getType() == PropertyType.LONG) {
             System.out.println(property.getName() + " = " + property.getLong());
        }
    }

    @Override
    public void visit(Node node1) throws RepositoryException {
        NodeIterator children = node1.getNodes();
        while (children.hasNext()) {
            visit(children.nextNode());
        }
    }
});
Greenock answered 23/3, 2017 at 8:1 Comment(0)
H
1

I ended up writing my own implementations.

Feel free to improve or add comments for potential improvements.


Further info

Java is probably NOT the most efficient way of searching through the JCR, so mind the performance hit (vs using JCR SQL2).

However, there are cases where using JCR SQL2 will be rather annoying. For instance: JCR SQL2 - result query order as in JCR browser

I'd recommend launching your search as low as possible in the tree.


Solution

Read the comments above each method to find our more.

package com.nameoforganization.jcr;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class JcrSearchUtils {

    private static final Logger log = LoggerFactory.getLogger(JcrUtil.class);

    /*
     * Recursive search in JCR tree: properties matching values
     * Parameters:
     *  - node: node to start the search from
     *  - propertyValueConditions: properties searched along with the value expected for it
     *  - searchResults: set this to null when launching the search
     */
    public static ArrayList<Node> searchRecursivelyPropMatchVal(Node node, HashMap<String, String> propertyValueConditions, ArrayList<Node> searchResults) {
        if(searchResults == null){
            searchResults = new ArrayList<Node>();
        }
        try{    
            NodeIterator list = node.getNodes();

            while(list.hasNext())   {

                Node currentSubNode = list.nextNode();
                Boolean hasAllRequiredPropsAndVals = true;

                for (Map.Entry<String, String> entry : propertyValueConditions.entrySet()) {
                    String propertyName = entry.getKey();
                    Object searchedValue = entry.getValue();
                    if ( !currentSubNode.hasProperty(propertyName) || !currentSubNode.getProperty(propertyName).getString().equals(searchedValue) ){
                        hasAllRequiredPropsAndVals = false;
                    }                   
                }
                if ( hasAllRequiredPropsAndVals ){
                    searchResults.add(currentSubNode);
                }

                searchRecursivelyPropMatchVal(currentSubNode, propertyValueConditions, searchResults);
            }

            return searchResults;
        } catch (RepositoryException rpe){
            log.info("Recursive search in JCR tree (properties matching values) via JCR API failed");
        }
        return null;
    }


    /*
     * Recursive search in JCR tree: required properties present 
     * Parameters:
     *  - node: node to start the search from
     *  - propertyValueConditions: properties searched along with the value expected for it
     *  - searchResults: set this to null when launching the search
     */
    public static ArrayList<Node> searchRecursivelyPropPres(Node node, ArrayList<String> propertyPresConditions, ArrayList<Node> searchResults) {
        if(searchResults == null){
            searchResults = new ArrayList<Node>();
        }
        try{    
            NodeIterator list = node.getNodes();

            while(list.hasNext())   {

                Node currentSubNode = list.nextNode();
                Boolean hasAllRequiredProperties = true;

                for (String propertyName : propertyPresConditions) {
                    if ( !currentSubNode.hasProperty(propertyName) ){
                        hasAllRequiredProperties = false;
                    }
                }
                if( hasAllRequiredProperties ){
                    searchResults.add(currentSubNode);
                }

                searchRecursivelyPropPres(currentSubNode, propertyPresConditions, searchResults);
            }

            return searchResults;
        } catch (RepositoryException rpe){
            log.info("Recursive search in JCR tree (required properties present) via JCR API failed");
        }
        return null;
    }


}

Usage of first utility method

    /*
     *  Search nodes with properties:
     *  "name" having value "teasers"
     *  "title" having value "awesome-teaser"
     */
    // Node startingNode = set this yourself :)
    HashMap<String, String> propertyValueConditions = new HashMap<String, String>();
    propertyValueConditions.put("name", "teasers");
    propertyValueConditions.put("title", "awesome-teaser");
    ArrayList<Node> nodesFound = JcrUtil.searchRecursivelyPropMatchVal(startingNode, propertyValueConditions, null);

Usage of second utility method

    /*
     *  Search nodes having properties "name" and "controlName"
     */
    // Node startingNode = set this yourself :)
    ArrayList<String> propertyPresConditions = new ArrayList<String>();
    propertyPresConditions.add("name");
    propertyPresConditions.add("controlName");
    ArrayList<Node> nodesFound = JcrUtil.searchRecursivelyPropPres(startingNode, propertyPresConditions, null);

Resources:

Hollenbeck answered 22/12, 2016 at 9:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.