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

com.day.cq.search.eval.PredicateGroupEvaluator 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.search.eval;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.jcr.query.Row;

import org.apache.felix.scr.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.day.cq.search.Predicate;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.facets.FacetExtractor;

/**
 * Allows to build nested conditions. Groups can contain nested groups.
 * Everything in a querybuilder query is implicitly in a root group, which
 * can have p.or and p.not as well.
 *
 * 

* Example for matching either one of two properties against a value: * group.p.or=true * group.1_property=jcr:title * group.1_property.value=My Page * group.2_property=navTitle * group.2_property.value=My Page * * This is conceptually (1_property OR 2_property). * *

* Example for nested groups: * fulltext=Management * group.p.or=true * group.1_group.path=/content/geometrixx/en * group.1_group.type=cq:Page * group.2_group.path=/content/dam/geometrixx * group.2_group.type=dam:Asset * * This searches for the term "Management" within pages in /content/geometrixx/en * or in assets in /content/dam/geometrixx. This is conceptually * fulltext AND ( (path AND type) OR (path AND type) ). * Be aware that such OR joins need good indexes for performance. * *

Name:

* group * *

Properties:

*
*
p.or
if set to "true", only one predicate in the group must match (defaults to "false", meaning all must match)
*
p.not
if set to "true", negates the group (defaults to "false")
*
<predicate>
*
add nested predicates
*
N_<predicate>
*
add multiple nested predicates of the same time, e.g. 1_property, 2_property, ...
*
* * @since 5.2 */ @Component(metatype = false, factory="com.day.cq.search.eval.PredicateEvaluator/group") public class PredicateGroupEvaluator extends AbstractPredicateEvaluator { private static final Logger log = LoggerFactory.getLogger(PredicateGroupEvaluator.class); protected static String FORCED_FILTERING = PredicateGroupEvaluator.class.getName() + "forced-filtering"; protected static String UNSUPPORTED_FILTER_WARNING_GIVEN = PredicateGroupEvaluator.class.getName() + ".filter-warning"; protected String getOpeningBracket() { return "("; } protected String getClosingBracket() { return ")"; } @Override public String getXPathExpression(Predicate p, EvaluationContext context) { if (p == null || !(p instanceof PredicateGroup)) { return null; } PredicateGroup group = (PredicateGroup) p; // fast check: if this group is filtering, no xpath at all if (isForcedFiltering(group, context)) { return ""; } // first collect all expressions and skip empty strings List expressions = new ArrayList(); for (Predicate pred : group) { if (pred.ignored()) { continue; } PredicateEvaluator evaluator = context.getPredicateEvaluator(pred.getType()); if (evaluator != null /* && evaluator.canXpath(pred, context) */) { String ex = evaluator.getXPathExpression(pred, context); if (ex != null && ex.length() > 0) { expressions.add(ex); } } } // then collect xpath expressions in a stringbuffer StringBuffer xpath = new StringBuffer(); if (expressions.size() > 0) { // for example: (@jcr:primaryType = 'nt:file' and jcr:contains(. "foobar")) if (group.isNegated()) { // for example: not(@jcr:primaryType = 'nt:file' and jcr:contains(. "foobar")) xpath.append("not"); } xpath.append(getOpeningBracket()); Iterator exIter = expressions.iterator(); while (exIter.hasNext()) { // for example: @jcr:primaryType = 'nt:file' xpath.append(exIter.next()); if (exIter.hasNext()) { if (group.allRequired()) { xpath.append(" and "); } else { xpath.append(" or "); } } } xpath.append(getClosingBracket()); } return xpath.toString(); } @Override public boolean includes(Predicate p, Row row, EvaluationContext context) { if (p == null || !(p instanceof PredicateGroup)) { return false; } PredicateGroup group = (PredicateGroup) p; // and vs. or boolean result = group.allRequired() ? andInclude(group, row, context) : orInclude(group, row, context); // negate group return group.isNegated() ? !result : result; } private boolean andInclude(PredicateGroup group, Row row, EvaluationContext context) { if (group.isEmpty()) { // an empty group should not have any influence, thus return true return true; } final boolean forcedFiltering = (context.get(FORCED_FILTERING) != null); for (Predicate p : group) { if (p.ignored()) { continue; } PredicateEvaluator evaluator = context.getPredicateEvaluator(p.getType()); // only ask filtering-only evaluators (or when filtering is forced in general for this group) if (evaluator != null && (forcedFiltering || !evaluator.canXpath(p, context))) { // check for non-inclusion if (!evaluator.includes(p, row, context)) { if (log.isTraceEnabled()) { log.trace("AND group: predicate '" + p.getName() + "' (" + p.getType() + ") denied row " + context.getPath(row)); } return false; } } } return true; } private boolean orInclude(PredicateGroup group, Row row, EvaluationContext context) { int predicatesAsked = 0; final boolean inheritedForcedFiltering = (context.get(FORCED_FILTERING) != null); final boolean forcedFiltering = inheritedForcedFiltering || isForcedFiltering(group, context); if (!inheritedForcedFiltering && forcedFiltering) { context.put(FORCED_FILTERING, true); } try { for (Predicate p : group) { if (p.ignored()) { continue; } PredicateEvaluator evaluator = context.getPredicateEvaluator(p.getType()); // only ask filtering-only evaluators (or when filtering is forced in general for this group) if (evaluator != null && (forcedFiltering || !evaluator.canXpath(p, context))) { // check for inclusion if (evaluator.includes(p, row, context)) { // log if evaluator is not supporting filtering if (forcedFiltering && context.get(UNSUPPORTED_FILTER_WARNING_GIVEN) == null && !evaluator.canFilter(p, context)) { log.warn("Search result might be incorrect - forcing filtering with a PredicateEvaluator " + "that does NOT support filtering: '" + p.getPath() + "' = " + evaluator.getClass().getName()); context.put(UNSUPPORTED_FILTER_WARNING_GIVEN, true); } return true; } predicatesAsked++; } } if (predicatesAsked == 0) { // an empty group (or one where all are ignored) should not have any influence, thus return true return true; } if (log.isTraceEnabled()) { log.trace("OR group: no predicate in group '" + group.getName() + "' accepted row " + context.getPath(row)); } // no child predicate ever returned true => don't include return false; } finally { if (!inheritedForcedFiltering && forcedFiltering) { context.put(FORCED_FILTERING, null); } } } protected boolean isForcedFiltering(PredicateGroup group, EvaluationContext context) { // force filtering = OR group + at least one predicate that requires filtering if (group.allRequired()) { // skip AND group return false; } for (Predicate p: group) { if (p.ignored()) { continue; } PredicateEvaluator evaluator = context.getPredicateEvaluator(p.getType()); // filtering is required if no xpath is available if (evaluator != null && !evaluator.canXpath(p, context)) { return true; } } return false; } @Override public boolean canXpath(Predicate predicate, EvaluationContext context) { if (predicate == null || !(predicate instanceof PredicateGroup)) { return false; } PredicateGroup group = (PredicateGroup) predicate; // a group can do xpath only if *all* (non-ignored) child predicates can do xpath for (Predicate p: group) { if (p.ignored()) { continue; } PredicateEvaluator evaluator = context.getPredicateEvaluator(p.getType()); if (evaluator != null && !evaluator.canXpath(p, context)) { return false; } } return true; } @Override public boolean canFilter(Predicate predicate, EvaluationContext context) { if (predicate == null || !(predicate instanceof PredicateGroup)) { return false; } PredicateGroup group = (PredicateGroup) predicate; // a group can filter only if *all* (non-ignored) child predicates can filter for (Predicate p: group) { if (p.ignored()) { continue; } PredicateEvaluator evaluator = context.getPredicateEvaluator(p.getType()); if (evaluator != null && !evaluator.canFilter(p, context)) { return false; } } return true; } public String listFilteringPredicates(PredicateGroup group, EvaluationContext context) { StringBuffer result = new StringBuffer(); // or + filtering forced boolean groupHasForcedFiltering = isForcedFiltering(group, context); for (Predicate p : group) { if (p.ignored()) { continue; } if (groupHasForcedFiltering) { // all child predicates are "forced" to filter if (result.length() > 0) { result.append(", "); } PredicateEvaluator evaluator = context.getPredicateEvaluator(p.getType()); if (evaluator != null && !evaluator.canFilter(p, context)) { result.append("WARN - NO FILTERING SUPPORT: "); } result.append("{").append(p.toString()).append("}"); } else if (p instanceof PredicateGroup) { // recursively walk down the predicate tree if (result.length() > 0) { result.append(", "); } result.append(listFilteringPredicates((PredicateGroup) p, context)); } else { PredicateEvaluator evaluator = context.getPredicateEvaluator(p.getType()); // filtering ones are those with no xpath capability if (evaluator != null && !evaluator.canXpath(p, context)) { if (result.length() > 0) { result.append(", "); } result.append("{").append(p.toString()).append("}"); } } } return result.toString(); } @Override public FacetExtractor getFacetExtractor(Predicate predicate, EvaluationContext context) { // Facets map one-to-one on concrete predicateEvaluator (types), so there is no // such thing as a "GroupFacetExtractor" which is comparable to the GroupPredicate. // Collecting the facet extractors from the sub-predicates is handled by the calling // framework, thus we return null here. return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy