Recursive search in JCR repo via java

1.8k views Asked by At

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)?

3

There are 3 answers

0
Adriano On

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:

3
d33t On

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("...");

0
michid On

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());
        }
    }
});