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

com.adobe.cq.social.commons.listing.QueryFilterUtil Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2013 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.adobe.cq.social.commons.listing;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.jcr.PropertyType;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.cq.social.commons.Comment;
import com.adobe.cq.social.commons.CommentSystem;
import com.adobe.cq.social.ugc.api.ComparisonType;
import com.adobe.cq.social.ugc.api.Constraint;
import com.adobe.cq.social.ugc.api.ConstraintGroup;
import com.adobe.cq.social.ugc.api.FullTextConstraint;
import com.adobe.cq.social.ugc.api.Operator;
import com.adobe.cq.social.ugc.api.ValueConstraint;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.tagging.TagConstants;

/**
 * 
 *
 * {@code
 * Query Filter Utility class uses to parse filter parameters. Filter query restricts the data returned in a query
 * request, which should take a form of
 * http://://.social.query..?
 *
 * A single filter uses the form: property operator expression where,
 * 
    *
  • property - the property name. @see MAP_INDEX_TYPE for the list of valid name.
  • *
  • operator - defines the type of filter match to use.
  • *
  • expression - states the values included in the results. There are couple of important rules for filter * expression. *
      *
    • URL-reversed characters - Characters such as '&' must be url-encoded.
    • *
    • Reversed characters - The comma and backslash must be backslash escaped when they appear in an * expression.
    • *
    *
  • *
* } *
*/ public class QueryFilterUtil { /** * General Query Filter Exception, thrown when there is an error while parsing a filter. */ public static class QueryFilterException extends Exception { /** * */ private static final long serialVersionUID = 1L; public QueryFilterException(final String msg) { super(msg); } public QueryFilterException(final String msg, final Exception cause) { super(msg, cause); } public QueryFilterException(final Exception cause) { super(cause); } } private static final String PROP_TAG = "tag"; private static final String NULL_STRING = "null"; public static final String PROP_USER_DISPLAY_NAME = "author_display_name"; // Definition of the indexed properties. private static Map MAP_INDEXED_TYPE; static { // Define by AbstractUgcNodeIndexerExtension MAP_INDEXED_TYPE = new HashMap(); MAP_INDEXED_TYPE.put("jcr:primaryType", PropertyType.STRING); MAP_INDEXED_TYPE.put(":uuid", PropertyType.STRING); MAP_INDEXED_TYPE.put(":path", PropertyType.STRING); MAP_INDEXED_TYPE.put(":parent", PropertyType.STRING); MAP_INDEXED_TYPE.put(":name", PropertyType.STRING); // Define by ModerationUgcNodeIndexerExtension: MAP_INDEXED_TYPE.put(Comment.PROP_FLAGGED, PropertyType.BOOLEAN); MAP_INDEXED_TYPE.put(Comment.PROP_SPAM, PropertyType.BOOLEAN); MAP_INDEXED_TYPE.put("read", PropertyType.BOOLEAN); MAP_INDEXED_TYPE.put("influence", PropertyType.BOOLEAN); MAP_INDEXED_TYPE.put("attachments", PropertyType.BOOLEAN); MAP_INDEXED_TYPE.put("sentiment", PropertyType.LONG); MAP_INDEXED_TYPE.put("flagged", PropertyType.BOOLEAN); MAP_INDEXED_TYPE.put("added", PropertyType.DATE); MAP_INDEXED_TYPE.put("modifiedDate", PropertyType.DATE); MAP_INDEXED_TYPE.put("state", PropertyType.STRING); MAP_INDEXED_TYPE.put("userIdentifier", PropertyType.STRING); MAP_INDEXED_TYPE.put("parentPath", PropertyType.STRING); MAP_INDEXED_TYPE.put("parentTitle", PropertyType.STRING); MAP_INDEXED_TYPE.put("replies", PropertyType.LONG); MAP_INDEXED_TYPE.put(JcrConstants.JCR_TITLE, PropertyType.STRING); MAP_INDEXED_TYPE.put(JcrConstants.JCR_DESCRIPTION, PropertyType.STRING); MAP_INDEXED_TYPE.put(CommentSystem.NN_COMMENT_ATTACHMENTS, PropertyType.BOOLEAN); // Define by JournalUgcNodeIndexerExtension MAP_INDEXED_TYPE.put(SlingConstants.NAMESPACE_PREFIX + ":" + SlingConstants.PROPERTY_RESOURCE_TYPE, PropertyType.STRING); // Define by ForumSCIndexExtension // MAP_INDEXED_TYPE.put("replies", PropertyType.LONG); MAP_INDEXED_TYPE.put("parentPath", PropertyType.STRING); MAP_INDEXED_TYPE.put("parentTitle", PropertyType.STRING); MAP_INDEXED_TYPE.put("allowThreadedReply", PropertyType.BOOLEAN); MAP_INDEXED_TYPE.put(Comment.PROP_IS_DRAFT, PropertyType.BOOLEAN); MAP_INDEXED_TYPE.put(Comment.PROP_PUBLISH_DATE, PropertyType.DATE); MAP_INDEXED_TYPE.put(Comment.PROP_PUBLISH_JOB_ID, PropertyType.STRING); // Define by QnaSCIndexExtension MAP_INDEXED_TYPE.put("answered", PropertyType.BOOLEAN); MAP_INDEXED_TYPE.put("chosenanswered", PropertyType.BOOLEAN); // Define by HBSIndexExtension MAP_INDEXED_TYPE.put(PROP_TAG, PropertyType.STRING); MAP_INDEXED_TYPE.put(TagConstants.PN_TAGS, PropertyType.STRING); MAP_INDEXED_TYPE.put(PROP_USER_DISPLAY_NAME, PropertyType.STRING); // Needed for calendar search by keyword MAP_INDEXED_TYPE.put("location_t", PropertyType.STRING); MAP_INDEXED_TYPE.put("social:key", PropertyType.STRING); } /** default log. */ private static final Logger LOG = LoggerFactory.getLogger(QueryFilterUtil.class); // Filter index private static int NUMBER_FILTER_TOKENS = 3; private static int FILTER_PROP_INDEX = 0; private static int FILTER_OPERATOR_INDEX = 1; private static int FILTER_EXPRESSION_INDEX = 2; /** * enum for avartar's size. */ public enum DATA_TYPE { /** * Data type for String, String Array, boolean, long and Date. */ STRING("s"), STRING_ARRAY("ss"), BOOLEAN("b"), LONG("l"), DATE("dt"); /** * Avartar's size. */ private final String type; /** * Constructor. * @param t the data type */ DATA_TYPE(final String t) { type = t; } @Override public String toString() { return String.valueOf(this.type); } public static DATA_TYPE getEnum(final String value) { for (final DATA_TYPE dt : values()) { if (StringUtils.equals(dt.type, value)) { return dt; } } throw new IllegalArgumentException("Unsupported data type " + value); } } /** * Query Comparator types - map our URL comparator to UGC Comparator type. */ public enum Comparator { EQ(ComparisonType.Equals), NE(ComparisonType.NotEquals), LT(ComparisonType.LessThan), LTE( ComparisonType.LessThanOrEqualTo), GT(ComparisonType.GreaterThan), GTE( ComparisonType.GreaterThanOrEqualTo), LIKE(null); // there is no comparison type for LIKE since it is a // FullTextConstraint ComparisonType comparator; Comparator(final ComparisonType comparator) { this.comparator = comparator; } ComparisonType getComparisonType() { return comparator; } public static Comparator fromString(final String text) throws QueryFilterException { if (text == null || text.length() == 0) { return null; } return Comparator.valueOf(text.toUpperCase()); } } /** * Abstract of a filter clause, which consists of a property name, a comparator, and a value. Example filter=name * eq admin */ public static class QueryFilter { private final Constraint constraint; private final String name; // the name of the constraint property. private final String value; // the value of the constraint property private final Comparator comp; @SuppressWarnings("unchecked") private QueryFilter(final String name, final Comparator comp, final String value, final Operator operator) throws QueryFilterException { if (StringUtils.isEmpty(name)) { throw new QueryFilterException("Name parameter can not be null."); } if (comp == null) { throw new QueryFilterException("Invalid comparator value"); } if (StringUtils.isEmpty(value)) { throw new QueryFilterException("Value parameter can not be null."); } if (operator == null) { throw new QueryFilterException("Operator parameter can not be null."); } this.name = name; this.value = value; this.comp = comp; final Object typedValue = getPropertyValue(name, value); this.constraint = isTextSearchProperty(name, comp) && (typedValue instanceof String) ? new FullTextConstraint( (String) typedValue, name, operator) : new ValueConstraint(name, typedValue, comp.getComparisonType(), operator); } /** * Gets the constraint. * @return the constraint */ public Constraint getConstraint() { return constraint; } /** * Gets the value.If the Query expression is "isDraft eq true", the API will return isDraft * @return the value */ public String getName() { return name; } /** * Gets the value.If the Query expression is "isDraft eq true", the API will return true * @return the value */ public String getValue() { return value; } /** * Gets the comparator. If the Query expression is "isDraft eq true", the API will return eq * @return the comparator */ public Comparator getComparator() { return comp; } /** * This api is for internal use only, and may be changed or removed at any time. * @param filter String * @param operator Operator * @return QueryFilter * @throws QueryFilterException QueryFilterException */ public static QueryFilter parse(final String filter, final Operator operator) throws QueryFilterException { return parse(filter, operator, false); } /** * This api is for internal use only, and may be changed or removed at any time * @param filter String * @param operator Operator * @param bSupportsMultiLingualSearch boolean * @return QueryFilter * @throws QueryFilterException QueryFilterException */ public static QueryFilter parse(final String filter, final Operator operator, final boolean bSupportsMultiLingualSearch) throws QueryFilterException { final String[] words = filter.trim().split(" ", NUMBER_FILTER_TOKENS); if (words.length < NUMBER_FILTER_TOKENS) { throw new QueryFilterException("Invalid filter expression: " + filter); } String name = words[FILTER_PROP_INDEX].trim(); if (name.equals(PROP_TAG)) { name = TagConstants.PN_TAGS; } final Comparator comp = Comparator.fromString(words[FILTER_OPERATOR_INDEX].trim()); final String value = escapeQueryValue(words[FILTER_EXPRESSION_INDEX].trim(), bSupportsMultiLingualSearch); return new QueryFilter(name, comp, value, operator); } private boolean isTextSearchProperty(final String name, final Comparator comp) { if (comp == Comparator.LIKE) { return true; } return false; } private static String escapeQueryValue(final String value, final boolean bSupportsMultiLingualSearch) { String escapeValue = value; // Remove quotes around the value if (escapeValue.startsWith("'") && escapeValue.endsWith("'")) { escapeValue = escapeValue.substring(1, escapeValue.length() - 1); } if (!bSupportsMultiLingualSearch) { if (escapeValue.length() > 1 && escapeValue.startsWith("\"") && escapeValue.endsWith("\"")) { escapeValue = escapeValue.substring(1, escapeValue.length() - 1); } } if (escapeValue.contains("\\,")) { escapeValue = escapeValue.replace("\\,", ","); } return escapeValue; } } /** * OR Logic Example: filter=name eq 'admin', name eq 'peter' This api is for internal use only, and may be changed * or removed at any time * @param expressions String * @param orFilters Map * @throws QueryFilterException QueryFilterException */ public static void parseOrFilters(final String expressions, final Map orFilters) throws QueryFilterException { parseOrFilters(expressions, orFilters, false); } /** * OR Logic Example: filter=name eq 'admin', name eq 'peter' This api is for internal use only, and may be changed * or removed at any time * @param expressions String * @param orFilters Map * @param bSupportsMultiLingualSearch boolean * @throws QueryFilterException QueryFilterException */ public static void parseOrFilters(final String expressions, final Map orFilters, final boolean bSupportsMultiLingualSearch) throws QueryFilterException { if (StringUtils.isEmpty(expressions)) { throw new QueryFilterException("Empty filter."); } final String[] filters = expressions.split("(? * {@code * AND logic - AND logic is achieved using multiple filters. Example: filter=name eq 'admin'&filter=message eq * 'testing' Combining AND and OR filter=name eq 'admin',name eq 'peter'&filter=message eq testing This api is for * internal use only, and may be changed or removed at any time * } *
* @param filters String[] * @return List of constraint groups * @throws QueryFilterException QueryFilterException */ public static List parseFilter(final String[] filters) throws QueryFilterException { return parseFilter(filters, false); } /**
     *    {@code
     *    AND logic - AND logic is achieved using multiple filters. Example: filter=name eq 'admin'&filter=message eq
     * 'testing' Combining AND and OR filter=name eq 'admin',name eq 'peter'&filter=message eq testing This api is for
     * internal use only, and may be changed or removed at any time
     * }
     * 
* @param filters String[] * @param bSupportsMultiLingualSearch boolean * @return List of constraint groups * @throws QueryFilterException QueryFilterException */ public static List parseFilter(final String[] filters, final boolean bSupportsMultiLingualSearch) throws QueryFilterException { // Create a map for this group OR filters final Map orFiltersMap = new HashMap(); final List andFilters = new ArrayList(); if (filters != null) { for (int i = 0; i < filters.length; i++) { final ConstraintGroup cg = new ConstraintGroup(); cg.setOperator(Operator.And); andFilters.add(cg); parseOrFilters(filters[i], orFiltersMap, bSupportsMultiLingualSearch); for (final Entry entry : orFiltersMap.entrySet()) { cg.or(entry.getValue()); } orFiltersMap.clear(); } } return andFilters; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy