All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.wcm.foundation.Search Maven / Gradle / Ivy

/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
package com.day.cq.wcm.foundation;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.query.Query;
import javax.jcr.query.RowIterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.commons.query.GQL;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.request.RequestParameter;
import org.apache.sling.api.resource.Resource;

import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.dam.api.DamConstants;
import com.day.cq.search.Predicate;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.SimpleSearch;
import com.day.cq.search.Trends;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.facets.Facet;
import com.day.cq.search.result.ResultPage;
import com.day.cq.search.result.SearchResult;
import com.day.cq.wcm.api.NameConstants;
import com.day.cq.wcm.api.components.Component;
import com.day.cq.wcm.commons.WCMUtils;

/**
 * The Search class implements the search logic used in the
 * foundation search component and exposes the query result in a scripting
 * friendly object structure.
 *
 * This class does a fulltext query, which means wildcards like '*' and '?' will
 * be filtered out. Please use {@link QueryBuilder} or execute a query directly
 * on the JCR Session if wildcard support is needed.
 */
public final class Search {

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

    /**
     * The name for the query parameter.
     */
    private static final String QUERY_PARAM_NAME = "q";

    /**
     * The name for the start parameter.
     */
    private static final String START_PARAM_NAME = "start";

    /**
     * The name for the language facet parameter.
     */
    private static final String LANGUAGE_FACET_PARAM_NAME = "language";

 	  /**
     * The name for the charset parameter (usually set when posting).
     */
    private static final String CHARSET_PARAM_NAME = "_charset_";

    /**
     * The name for the tag facet parameter.
     */
    private static final String TAG_FACET_PARAM_NAME = "tag";

    /**
     * The name for the mime type facet parameter.
     */
    private static final String MIME_TYPE_FACET_PARAM_NAME = "mimeType";

    /**
     * The name for the last modified facet parameter, which specifies the
     * lower bound of the date range.
     */
    private static final String FROM_FACET_PARAM_NAME = "from";

    /**
     * The name for the last modified facet parameter, which specifies the
     * upper bound of the date range.
     */
    private static final String TO_FACET_PARAM_NAME = "to";

    /**
     * The name for the node types parameter
     */
    private static final String NODE_TYPE_PARAM_NAME = "nodeType";

    /**
     * HTML extension.
     */
    private static final String HTML_EXT = "html";

    /**
     * Regular expression that matches special characters
     */
    private static final String SPECIAL_CHARS_REGEX = "[!@#$%^&*()?_,;'\"|<>`~±§{}\\-\\\\\\/\\.\\s\\n\\r]*";

    /**
     * The query template to check the spelling of a fulltext query statement.
     */
    private static final String SPELLCHECK_QUERY = "SELECT [rep:spellcheck()] FROM [${nodeType}] AS a WHERE [jcr:path] = '/' AND SPELLCHECK('${query}')";

    /**
     * Default node types that will be searched if the node type request parameter is empty
     */
    private List defaultNodeTypes = new ArrayList(Arrays.asList("cq:Page", "dam:Asset"));

    /**
     * The current request.
     */
    private final SlingHttpServletRequest request;

    /**
     * The underlying search instance.
     */
    private final SimpleSearch search;

    /**
     * The current resource.
     */
    private final Resource resource;

    /**
     * The query result. Initially null.
     */
    private Result result;

    /**
     * The result pages.
     */
    private List resultPages;

    /**
     * The raw query as read from the request or manually set.
     */
    private String query;

    /**
     * Set to true if there was a tag predicate set in the request.
     */
    private boolean tagPredicateSet;

    /**
     * Creates a new search based on the given request.
     *
     * @param request the current request.
     */
    public Search(SlingHttpServletRequest request) {
        this.request = request;
        this.resource = request.getResource();
        this.search = resource.adaptTo(SimpleSearch.class);

        String charset = "ISO-8859-1"; //would be used by the GET requests

        if (request.getParameter(CHARSET_PARAM_NAME) != null) {
            charset = request.getParameter(CHARSET_PARAM_NAME);
        }

        if (request.getParameter(QUERY_PARAM_NAME) != null) {
            try {
                setQuery(new String(request.getParameter(
                        QUERY_PARAM_NAME).getBytes(charset), "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                // ignore
            }
        }
        if (request.getParameter(START_PARAM_NAME) != null) {
            try {
                this.search.setStart(Long.parseLong(request.getParameter(START_PARAM_NAME)));
            } catch (NumberFormatException e) {
                // ignore
            }
        }

        // Note: the predicate names (first param to Predicate constructor) are the same
        // as in the facets returned and used in search.jsp
        Predicate languagePredicate = new Predicate("languages", "language");
        languagePredicate.set("language", request.getParameter(LANGUAGE_FACET_PARAM_NAME));
        this.search.addPredicate(languagePredicate);

        Predicate tagPredicate = new Predicate("tags", "tagid");
        tagPredicate.set("property", JcrConstants.JCR_CONTENT + "/" + NameConstants.PN_TAGS);
        tagPredicate.set("tagid", request.getParameter(TAG_FACET_PARAM_NAME));
        this.search.addPredicate(tagPredicate);
        this.tagPredicateSet = tagPredicate.get("tagid") != null;

        Predicate mimeTypePredicate = new Predicate("mimeTypes", "property");
        mimeTypePredicate.set("property", JcrConstants.JCR_CONTENT + "/" + JcrConstants.JCR_MIMETYPE);
        mimeTypePredicate.set("value", request.getParameter(MIME_TYPE_FACET_PARAM_NAME));
        this.search.addPredicate(mimeTypePredicate);

        Predicate lastModPredicate = new Predicate("lastModified", "daterange");
        lastModPredicate.set("property", JcrConstants.JCR_CONTENT + "/" + NameConstants.PN_PAGE_LAST_MOD);
        // TODO: consider lowerOperation/upperOperation (has to be additional request params)
        lastModPredicate.set("lowerBound", request.getParameter(FROM_FACET_PARAM_NAME));
        lastModPredicate.set("upperBound", request.getParameter(TO_FACET_PARAM_NAME));
        this.search.addPredicate(lastModPredicate);

        Predicate orderByScore = new Predicate("orderByScore", Predicate.ORDER_BY);
        orderByScore.set(Predicate.ORDER_BY, "@jcr:score");
        orderByScore.set(Predicate.PARAM_SORT, Predicate.SORT_DESCENDING);
        this.search.addPredicate(orderByScore);

        // add a predicate to filter out asset renditions
        Predicate assetRenditionFilterPred = new Predicate("assetRenditionFilter", "assetRenditionFilter");
        this.search.addPredicate(assetRenditionFilterPred);

        // add a predicate to filter out sub-assets
        Predicate subAssetFilterPred = new Predicate("subAssetFilter", "subAssetFilter");
        this.search.addPredicate(subAssetFilterPred);
        addNodeTypesPredicate(getNodeTypes());
    }

    /**
     * @return query trends (popular queries).
     */
    public Trends getTrends() {
        return search.getTrends();
    }

    /**
     * @return the query result or null if there is neither a query
     *         parameter set nor a tag predicate.
     * @throws RepositoryException if an exception occurs while executing the
     *                             query.
     */
    public Result getResult() throws RepositoryException {
        if (result == null
                && (search.getQuery().length() > 0 || tagPredicateSet)
                && search.getResult() != null) {
            result = new Result(search.getResult());
        }
        return result;
    }

    /**
     * @return queries that are related to the current one.
     * @throws RepositoryException if an error occurs while reading from
     *                             the repository.
     */
    public List getRelatedQueries() throws RepositoryException {
        return search.getRelatedQueries();
    }

    /**
     * @return the query supplied by the user or an empty String if none is
     *         provided.
     */
    public String getQuery() {
        return query != null ? query : "";
    }

    private String getSpellcheckerQuery() {
        String spellcheckerQuery = StringUtils.EMPTY;
        List nodeTypes = getNodeTypes();
        int i = 1;
        for(String nodeType: nodeTypes) {
            spellcheckerQuery += SPELLCHECK_QUERY
                    .replaceAll("\\$\\{query\\}", Matcher.quoteReplacement(getQuery()))
                    .replaceAll("\\$\\{nodeType\\}", Matcher.quoteReplacement(nodeType));
            i++;
            if(i <= nodeTypes.size()) {
                spellcheckerQuery += " UNION ";
            }
        }
        return spellcheckerQuery;
    }

    /**
     * Sets a new fulltext query that will be executed.
     *
     * @param query the fulltext query.
     */
    public void setQuery(String query) {
        this.query = query;
        // if the query string is surrounded by double quotes and does not contain any other double quote
        // (e.g. "foo bar"), we don't parse the string and use it as it is
        if (query != null && query.length() > 2 && query.startsWith("\"") && query.endsWith("\"")
                && !query.substring(1, query.length()-1).contains("\"")) {
            search.setQuery(query);
            return;
        }
        if (!StringUtils.isEmpty(query)) {
            // remove queries that contain only special characters
            query = query.replaceAll("^" + SPECIAL_CHARS_REGEX + "$", "");
            if (!StringUtils.isEmpty(query)) {
                // remove negation
                query = query.replaceAll("^" + SPECIAL_CHARS_REGEX + "-", "");
            }
        }
        // use GQL to filter out wildcards
        try {
            final StringBuilder sb = new StringBuilder();
            GQL.parse(query, request.getResourceResolver().adaptTo(Session.class),
                    new GQL.ParserCallback() {
                        public void term(String property, String value, boolean optional)
                                throws RepositoryException {
                            sb.append(" ");
                            if (optional) {
                                sb.append("OR ");
                            }
                            if (property.length() > 0) {
                                sb.append(property).append(":");
                            }
                            sb.append(value);
                        }
            });
            search.setQuery(sb.toString().trim());
        } catch (RepositoryException e) {
            search.setQuery("");
        }
    }

    /**
     * @return the names of the properties that will be used in an excerpt.
     *
     * @deprecated since 5.2. Excerpt properties can now only be specified in
     *             the configuration of the QueryBuilder interface. For 5.3,
     *             when Jackrabbit 1.5 is used, the excerpt properties can be
     *             configured in the repository.
     *
     */
    @Deprecated
    public String getExcerptPropertyNames() {
        return "";
    }

    /**
     * @param properties
     *            comma separated names of the properties that will be used in
     *            an excerpt.
     *
     * @deprecated since 5.2. Excerpt properties can now only be specified in
     *             the configuration of the QueryBuilder interface. For 5.3,
     *             when Jackrabbit 1.5 is used, the excerpt properties can be
     *             configured in the repository.
     *
     */
    @Deprecated
    public void setExcerptPropertyNames(String properties) {
    }

    /**
     * @return the number of hits to display per page.
     */
    public long getHitsPerPage() {
        return search.getHitsPerPage();
    }

    /**
     * @param num the number of hits to display on a page.
     */
    public void setHitsPerPage(long num) {
        search.setHitsPerPage(num);
    }

    /**
     * @return the location where to search in.
     */
    public String getSearchIn() {
        return search.getSearchIn();
    }

    /**
     * @param searchIn the location where to search in.
     */
    public void setSearchIn(String searchIn) {
        search.setSearchIn(searchIn);
    }

    /**
     * @return the names of the properties that will be searched.
     */
    public String getSearchProperties() {
        return search.getSearchProperties();
    }

    /**
     * @param properties comma separated names of the properties that will be searched.
     */
    public void setSearchProperties(String properties) {
        search.setSearchProperties(properties);
    }

    /**
     * A search result.
     */
    public final class Result {

        /**
         * The underyling CQ search result instance.
         */
        private final SearchResult result;

        /**
         * The hits on the current result page.
         */
        private final List hits;

        /**
         * The spell suggestion. Set only when requested. An empty string
         * indicates that a suggestion was returned by the spellcheck, but
         * does not return results for the current query.
         * See {@link #getSpellcheck()}.
         */
        private String spellSuggestion;

        /**
         * Creates a new result based on the given CQ search result.
         *
         * @param result the CQ search result.
         */
        private Result(SearchResult result) {
            this.result = result;
            this.hits = new ArrayList();
            for (com.day.cq.search.result.Hit h : result.getHits()) {
                this.hits.add(new Hit(h));
            }
        }

        /**
         * @return a List of {@link Page}es to display the navigation through the
         *         result pages.
         * @throws RepositoryException if an error occurs while reading from
         *              the query result.
         */
        public List getResultPages() throws RepositoryException {
            if (resultPages == null) {
                resultPages = new ArrayList();
                for (com.day.cq.search.result.ResultPage rp : result.getResultPages()) {
                    resultPages.add(new Page(rp));
                }
            }
            return resultPages;
        }

        /**
         * @return the page, which contains the information about the previous page.
         *         Returns null if there is no previous page (i.e. the
         *         current page is the first page).
         * @throws RepositoryException if an error occurs while reading from
         *              the query result.
         */
        public Page getPreviousPage() throws RepositoryException {
            ResultPage previous = result.getPreviousPage();
            if (previous != null) {
                return new Page(previous);
            } else {
                return null;
            }
        }

        /**
         * @return the page, which contains the information about the next page.
         *         Returns null if there is no next page (i.e. the
         *         current page is the last page).
         * @throws RepositoryException if an error occurs while reading from
         *              the query result.
         */
        public Page getNextPage() throws RepositoryException {
            ResultPage next = result.getNextPage();
            if (next != null) {
                return new Page(next);
            } else {
                return null;
            }
        }

        /**
         * @return the script for query and result tracking.
         */
        public String getTrackerScript() {
            StringBuffer sb = new StringBuffer();
            String contextPath = request.getContextPath();
            sb.append("");
            return sb.toString();
        }

        /**
         * @return the result of a spell check or null if spell
         *         checking is not supported or the repository thinks the spelling
         *         is correct.
         */
        public String getSpellcheck() {
            if (spellSuggestion == null) {
                try {
                    Session session = request.getResourceResolver().adaptTo(Session.class);
                    RowIterator rows = session.getWorkspace().getQueryManager().createQuery(getSpellcheckerQuery(), Query.JCR_SQL2).execute().getRows();
                    String suggestion = null;
                    if (rows.hasNext()) {
                        Value v = rows.nextRow().getValue("rep:spellcheck()");
                        if (v != null) {
                            suggestion = v.getString();
                        }
                    }
                    if (suggestion == null) {
                        return null;
                    }

                    // set query term to suggestion and run query again
                    search.setQuery(suggestion);
                    if (search.getResult().getTotalMatches() > 0) {
                        spellSuggestion = suggestion;
                    } else {
                        spellSuggestion = "";
                    }
                } catch (RepositoryException e) {
                    spellSuggestion = "";
                }
            }
            if (spellSuggestion.length() == 0) {
                return null;
            } else {
                return spellSuggestion;
            }
        }

        /**
         * @return the start index. i.e. from where to start to display the hits.
         */
        public long getStartIndex() {
            return result.getStartIndex();
        }

        /**
         * @return the total number of matches.
         */
        public long getTotalMatches() {
            return result.getTotalMatches();
        }

        /**
         * Returns the execution time in fractions of a second.
         *
         * Example: 0.08 (means, the query took 80 milliseconds to execute).
         * @return the execution time of the query.
         */
        public String getExecutionTime() {
            return result.getExecutionTime();
        }

        /**
         * Returns the execution time in milliseconds.
         *
         * @return the execution time of the query.
         */
        public long getExecutionTimeMillis() {
            return result.getExecutionTimeMillis();
        }

        /**
         * Returns the facets for this search result.
         *
         * @return the facets for this search result.
         * @throws RepositoryException if an error occurs while executing the query
         *          or calculating the facets.
         */
        public Map getFacets() throws RepositoryException {
            return result.getFacets();
        }

        /**
         * @return a List of {@link Hit}s to display on the result page.
         */
        public List getHits() {
            return hits;
        }
    }

    /**
     * A hit within the search result.
     */
    public final class Hit {

        /**
         * The underlying CQ hit.
         */
        private final com.day.cq.search.result.Hit hit;

        /**
         * Creates a new hit based on the given CQ hit.
         *
         * @param hit the CQ hit to wrap.
         */
        private Hit(com.day.cq.search.result.Hit hit) {
            this.hit = hit;
        }

        /**
         * Returns the title for this hit. The returned string may contain
         * HTML tags, which means it must not be escaped when written to the
         * response.
         *
         * @return the title for this hit.
         * @throws RepositoryException if an error occurs while reading form the
         * repository.
          */
        public String getTitle() throws RepositoryException {
            String excerpt = hit.getExcerpts().get(JcrConstants.JCR_TITLE);
            if (excerpt != null) {
                return excerpt;
            }
            return getPageOrAsset().getName();
        }

        /**
         * @return the default excerpt for this hit.
         * @throws RepositoryException if an error occurs while building the
         * excerpt.
         */
        public String getExcerpt() throws RepositoryException {
            return hit.getExcerpt();
        }

        /**
         * @return the url for this hit.
         * @throws RepositoryException if an error occurs while reading from the
         *              query result.
         */
        public String getURL() throws RepositoryException {
            Node n = getPageOrAsset();
            String url = request.getContextPath() + n.getPath();
            if (isPage(n)) {
                url += "." + HTML_EXT;
            }
            return url;
        }

        /**
         * @return the url that displays similar pages for this hit.
         * @throws RepositoryException if an error occurs while reading from
         * the query result.
         */
        public String getSimilarURL() throws RepositoryException {
            StringBuffer url = new StringBuffer();
            url.append(request.getRequestURI());
            url.append("?").append(QUERY_PARAM_NAME);
            url.append("=").append(encodeURL(SimpleSearch.RELATED_PREFIX));
            url.append(encodeURL(hit.getPath()));
            return url.toString();
        }

        /**
         * Returns an icon image for this hit.
         *
         * @return an icon image for this hit.
         * @throws RepositoryException if an error occurs while reading from
         * the repository.
         */
        public String getIcon() throws RepositoryException {
            String url = getURL();
            int idx = url.lastIndexOf('.');
            if (idx == -1) {
                // no extension
                return "";
            }
            String ext = url.substring(idx + 1);
            if (ext.equals(HTML_EXT)) {
                // do not provide an icon for html
                return "";
            }
            String path = getIconPath(ext);
            if (path == null) {
                return "";
            }
            StringBuffer sb = new StringBuffer();
            sb.append("");
            return sb.toString();
        }

        /**
         * Returns the extension of the url of this hit.
         * @return the extension
         * @throws RepositoryException if an error occurs
         */
        public String getExtension() throws RepositoryException {
            String url = getURL();
            int idx = url.lastIndexOf('.');
            return idx >=0
                    ? url.substring(idx+1)
                    : "";
        }

        /**
         * @return the content properties on this hit.
         * @throws RepositoryException if an error occurs while reading from the
         * repository.
         */
        public Map getProperties() throws RepositoryException {
            return hit.getProperties();
        }

        /**
         * Returns true if the given node is either of type
         * cq:Page, cq:PseudoPageor
         * dam:Asset.
         *
         * @param n the node to check.
         * @return true if the node represents either a page or an
         *         asset; false otherwise.
         * @throws RepositoryException if an error occurs during the check.
         */
        private boolean isPageOrAsset(Node n) throws RepositoryException {
            return isPage(n) || n.isNodeType(DamConstants.NT_DAM_ASSET);
        }

        /**
         * Returns true if the given node is either of type
         * cq:Page or cq:PseudoPage.
         *
         * @param n the node to check.
         * @return true if the node represents a page;
         *         false otherwise.
         * @throws RepositoryException if an error occurs during the check.
         */
        private boolean isPage(Node n) throws RepositoryException {
            return n.isNodeType(NameConstants.NT_PAGE)
                    || n.isNodeType(NameConstants.NT_PSEUDO_PAGE);
        }

        /**
         * Returns the page or asset node that contains the current hit node.
         *
         * @return the page or asset node that contains the current hit node.
         * @throws RepositoryException if an error occurs while reading from the
         *                             repository.
         */
        private Node getPageOrAsset() throws RepositoryException {
            Node n = hit.getNode();
            while (!isPageOrAsset(n) && n.getName().length() > 0) {
                n = n.getParent();
            }
            return n;
        }
    }

    /**
     * A result page.
     */
    public final class Page {

        /**
         * The underlying CQ result page.
         */
        private final ResultPage rp;

        /**
         * Creates a new page with the given CQ result page.
         *
         * @param rp the CQ result page.
         */
        private Page(ResultPage rp) {
            this.rp = rp;
        }

        /**
         * @return whether this page is currently displayed.
         */
        public boolean isCurrentPage() {
            return rp.isCurrentPage();
        }

        /**
         * @return zero based index of this result page.
         */
        public long getIndex() {
            return rp.getIndex();
        }

        /**
         * @return the URL to this search result page.
         */
        public String getURL() {
            StringBuffer url = new StringBuffer();
            url.append(request.getRequestURI());
            url.append("?").append(QUERY_PARAM_NAME);
            url.append("=").append(encodeURL(search.getQuery()));
            url.append("&").append(START_PARAM_NAME);
            url.append("=").append(rp.getStart());
            
            String lang = request.getParameter(LANGUAGE_FACET_PARAM_NAME);
            if (lang != null) {
                url.append("&").append(LANGUAGE_FACET_PARAM_NAME);
                url.append("=").append(lang);
            }
            String tag = request.getParameter(TAG_FACET_PARAM_NAME);
            if (tag != null) {
                url.append("&").append(TAG_FACET_PARAM_NAME);
                url.append("=").append(tag);
            }
            String mimeType = request.getParameter(MIME_TYPE_FACET_PARAM_NAME);
            if (mimeType != null) {
                url.append("&").append(MIME_TYPE_FACET_PARAM_NAME);
                url.append("=").append(mimeType);
            }
            String from = request.getParameter(FROM_FACET_PARAM_NAME);
            if (from != null) {
                url.append("&").append(FROM_FACET_PARAM_NAME);
                url.append("=").append(from);
            }
            String to = request.getParameter(TO_FACET_PARAM_NAME);
            if (to != null) {
                url.append("&").append(TO_FACET_PARAM_NAME);
                url.append("=").append(to);
            }
            
            return url.toString();
        }
    }

    /**
     * Encodes the given url using {@link URLEncoder#encode(String, String)}
     * with a UTF-8 encoding.
     *
     * @param url the url to encode.
     * @return the encoded url.
     */
    private static String encodeURL(String url) {
        try {
            return URLEncoder.encode(url, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // will never happen
            return url;
        }
    }

    /**
     * Returns the icon path for the given extension or null if
     * none is found for the given extension.
     *
     * @param extension the extension.
     * @return the icon path for the given extension or null.
     */
    private String getIconPath(String extension) {
        Component c = WCMUtils.getComponent(resource);
        if (c == null) {
            return null;
        }
        Resource icon = c.getLocalResource("resources/" + extension + ".gif");
        if (icon == null) {
            icon = c.getLocalResource("resources/default.gif");
        }
        return icon == null ? null : icon.getPath();
    }

    private List getNodeTypes() {
        RequestParameter[] nodeTypeParams = request.getRequestParameters(NODE_TYPE_PARAM_NAME);
        if (nodeTypeParams != null && nodeTypeParams.length > 0) {
            List nodeTypes = new ArrayList();
            for (RequestParameter parameter : nodeTypeParams) {
                String type = parameter.getString();
                nodeTypes.add(type);
            }
            return nodeTypes;
        } else {
            return defaultNodeTypes;
        }
    }

    private void addNodeTypesPredicate(List types) {
        PredicateGroup nodeTypesPredicate = new PredicateGroup("nodeTypes");
        for (String type : types) {
            if (StringUtils.isNotEmpty(type)) {
                Predicate predicate = new Predicate("type", "type");
                predicate.set("type", type);
                nodeTypesPredicate.add(predicate);
            }
        }
        nodeTypesPredicate.setAllRequired(false); // type1 OR type2
        this.search.addPredicate(nodeTypesPredicate);
        log.info("Searching with the type(s): {}", Arrays.toString(types.toArray()));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy