com.smartitengineering.cms.spi.impl.content.search.ContentSearcherImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.smartitengineering.cms.spi.impl.content.search.ContentSearcherImpl.java

Source

/*
 *
 * This is a simple Content Management System (CMS)
 * Copyright (C) 2009  Imran M Yousuf (imyousuf@smartitengineering.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.smartitengineering.cms.spi.impl.content.search;

import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.smartitengineering.cms.api.content.Content;
import com.smartitengineering.cms.api.content.ContentId;
import com.smartitengineering.cms.api.content.Filter;
import com.smartitengineering.cms.api.common.SearchResult;
import com.smartitengineering.cms.api.event.Event.EventType;
import com.smartitengineering.cms.api.event.Event.Type;
import com.smartitengineering.cms.api.event.EventListener;
import com.smartitengineering.cms.api.factory.SmartContentAPI;
import com.smartitengineering.cms.api.type.ContentStatus;
import com.smartitengineering.cms.api.type.ContentTypeId;
import com.smartitengineering.cms.api.workspace.WorkspaceId;
import com.smartitengineering.cms.spi.content.ContentSearcher;
import com.smartitengineering.cms.spi.impl.SearchBeanLoader;
import com.smartitengineering.cms.spi.impl.events.SolrFieldNames;
import com.smartitengineering.common.dao.search.CommonFreeTextSearchDao;
import com.smartitengineering.dao.common.queryparam.BasicCompoundQueryParameter;
import com.smartitengineering.dao.common.queryparam.BiOperandQueryParameter;
import com.smartitengineering.dao.common.queryparam.MatchMode;
import com.smartitengineering.dao.common.queryparam.OperatorType;
import com.smartitengineering.dao.common.queryparam.Order;
import com.smartitengineering.dao.common.queryparam.ParameterType;
import com.smartitengineering.dao.common.queryparam.QueryParameter;
import com.smartitengineering.dao.common.queryparam.QueryParameterCastHelper;
import com.smartitengineering.dao.common.queryparam.QueryParameterFactory;
import com.smartitengineering.dao.common.queryparam.StringLikeQueryParameter;
import com.smartitengineering.dao.common.queryparam.UniOperandQueryParameter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author kaisar
 */
public class ContentSearcherImpl implements ContentSearcher {

    public static final String REINDEX_LISTENER_NAME = "contentReindexEventListener";
    protected final transient Logger logger = LoggerFactory.getLogger(getClass());
    @Inject
    private CommonFreeTextSearchDao<Content> textSearchDao;
    @Inject
    private SearchBeanLoader<Content, ContentId> contentLoader;
    // Injected so that the quartz service starts
    @Inject
    @Named(REINDEX_LISTENER_NAME)
    private EventListener reindexListener;
    private final ExecutorService executorService = Executors.newSingleThreadExecutor();
    private final String disjunctionSeperator = " OR ";
    private final String conjunctionSeperator = " AND ";
    private static final String SOLR_DATE_FORMAT = DateFormatUtils.ISO_DATETIME_FORMAT.getPattern() + "'Z'";

    @Override
    public SearchResult<Content> search(Filter filter) {
        final StringBuilder finalQuery = new StringBuilder();
        String seperator = filter.isDisjunction() ? disjunctionSeperator : conjunctionSeperator;
        int count = 0;
        Set<ContentTypeId> contentTypeIds = filter.getContentTypeFilters();
        finalQuery.append(SolrFieldNames.TYPE).append(": ").append(ContentHelper.CONTENT);
        final WorkspaceId workspaceId = filter.getWorkspaceId();
        if (workspaceId != null) {
            finalQuery.append(conjunctionSeperator);
            finalQuery.append((" ("));
            finalQuery.append(SolrFieldNames.WORKSPACEID).append(": ")
                    .append(ClientUtils.escapeQueryChars(workspaceId.toString()));
            if (filter.isFriendliesIncluded()) {
                Collection<WorkspaceId> friendlies = workspaceId.getWorkspace().getFriendlies();
                if (friendlies != null && !friendlies.isEmpty()) {
                    finalQuery.append(disjunctionSeperator).append("(private: false AND (");
                    boolean first = true;
                    for (WorkspaceId friendly : friendlies) {
                        if (friendly != null) {
                            if (first) {
                                first = false;
                            } else {
                                finalQuery.append(disjunctionSeperator);
                            }
                            finalQuery.append(SolrFieldNames.WORKSPACEID).append(": ")
                                    .append(ClientUtils.escapeQueryChars(friendly.toString()));
                        }
                    }
                    finalQuery.append("))");
                }
            }
            finalQuery.append((") "));
        }
        final StringBuilder query = new StringBuilder();
        if (contentTypeIds != null && !contentTypeIds.isEmpty()) {
            if (query.length() > 0) {
                query.append(seperator);
            }
            query.append("(");
        }
        for (ContentTypeId contentTypeId : contentTypeIds) {
            if (count > 0) {
                query.append(disjunctionSeperator);
            }
            if (contentTypeId != null) {
                query.append(SolrFieldNames.INSTANCE_OF).append(": ")
                        .append(ClientUtils.escapeQueryChars(contentTypeId.toString()));
            }
            count++;
        }
        if (contentTypeIds != null && !contentTypeIds.isEmpty()) {
            query.append(")");
        }
        if (StringUtils.isNotBlank(filter.getSearchTerms())) {
            if (query.length() > 0) {
                query.append(seperator);
            }
            query.append(SolrFieldNames.ALL_TEXT).append(": ")
                    .append(ClientUtils.escapeQueryChars(filter.getSearchTerms()));
        }
        Set<ContentStatus> statuses = filter.getStatusFilters();
        for (ContentStatus contentStatus : statuses) {
            if (query.length() > 0) {
                query.append(seperator);
            }
            if (StringUtils.isNotBlank(contentStatus.getName())) {
                query.append(SolrFieldNames.STATUS).append(": ")
                        .append(ClientUtils.escapeQueryChars(contentStatus.getName()));
            }
        }
        Collection<QueryParameter> fieldQuery = new ArrayList<QueryParameter>(filter.getFieldFilters());
        final QueryParameter orderParam = findAndRemoveOrderByParam(fieldQuery);
        processParams(fieldQuery, query, seperator, filter.isFieldParamsEscaped());
        if (filter.getCreationDateFilter() != null) {
            if (query.length() > 0) {
                query.append(seperator);
            } else {
                query.append("contentTypeId: [* TO *]").append(seperator);
            }
            QueryParameter<Date> creationDateFilter = filter.getCreationDateFilter();
            String queryStr = generateDateQuery(SolrFieldNames.CREATIONDATE, creationDateFilter);
            query.append(queryStr);
        }
        if (filter.getLastModifiedDateFilter() != null) {
            if (query.length() > 0) {
                query.append(seperator);
            } else {
                query.append("contentTypeId: [* TO *]").append(seperator);
            }
            QueryParameter<Date> lastModifiedDateFilter = filter.getLastModifiedDateFilter();
            String queryStr = generateDateQuery(SolrFieldNames.LASTMODIFIEDDATE, lastModifiedDateFilter);
            query.append(queryStr);
        }
        if (query.length() > 0) {
            finalQuery.append(conjunctionSeperator).append('(').append(query.toString()).append(')');
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Query q = " + finalQuery.toString());
        }
        final QueryParameter sortParam;
        if (orderParam != null) {
            sortParam = orderParam;
        } else {
            sortParam = QueryParameterFactory.getOrderByParam(SolrFieldNames.CREATIONDATE, Order.DESC);
        }
        final com.smartitengineering.common.dao.search.SearchResult<Content> searchResult;
        searchResult = textSearchDao.detailedSearch(
                QueryParameterFactory.getStringLikePropertyParam("q", finalQuery.toString()),
                QueryParameterFactory.getFirstResultParam(filter.getStartFrom()),
                QueryParameterFactory.getMaxResultsParam(filter.getMaxContents()), sortParam);
        final Collection<Content> result;
        if (searchResult == null || searchResult.getResult() == null || searchResult.getResult().isEmpty()) {
            result = Collections.emptyList();
        } else {
            result = new ArrayList<Content>();
            for (Content content : searchResult.getResult()) {
                if (content != null) {
                    result.add(content);
                }
            }
        }
        return SmartContentAPI.getInstance().getContentLoader().createSearchResult(result,
                searchResult.getTotalResults());
    }

    protected void processParams(Collection<QueryParameter> queries, final StringBuilder query, String seperator,
            boolean paramsEscaped) throws IllegalArgumentException {
        if (queries != null && !queries.isEmpty()) {
            for (QueryParameter parameter : queries) {
                if (parameter.getParameterType().equals(ParameterType.PARAMETER_TYPE_PROPERTY)
                        && parameter instanceof StringLikeQueryParameter) {
                    if (query.length() > 0) {
                        query.append(seperator);
                    }
                    StringLikeQueryParameter param = QueryParameterCastHelper.STRING_PARAM_HELPER.cast(parameter);
                    query.append(param.getPropertyName()).append(": ").append(
                            paramsEscaped ? param.getValue() : ClientUtils.escapeQueryChars(param.getValue()));
                } else if (parameter.getParameterType().equals(ParameterType.PARAMETER_TYPE_CONJUNCTION)) {
                    BasicCompoundQueryParameter compoundQueryParameter = (BasicCompoundQueryParameter) parameter;
                    StringBuilder nestedJunction = new StringBuilder();
                    processParams(compoundQueryParameter.getNestedParameters(), nestedJunction,
                            conjunctionSeperator, paramsEscaped);
                    if (nestedJunction.length() > 0) {
                        if (query.length() > 0) {
                            query.append(seperator);
                        }
                        query.append('(').append(nestedJunction).append(')');
                    }
                } else if (parameter.getParameterType().equals(ParameterType.PARAMETER_TYPE_DISJUNCTION)) {
                    BasicCompoundQueryParameter compoundQueryParameter = (BasicCompoundQueryParameter) parameter;
                    StringBuilder nestedJunction = new StringBuilder();
                    processParams(compoundQueryParameter.getNestedParameters(), nestedJunction,
                            disjunctionSeperator, paramsEscaped);
                    if (nestedJunction.length() > 0) {
                        if (query.length() > 0) {
                            query.append(seperator);
                        }
                        query.append('(').append(nestedJunction).append(')');
                    }
                }
            }
        }
    }

    public static String generateDateQuery(String fieldName, QueryParameter<Date> creationDateFilter) {
        StringBuilder query = new StringBuilder(fieldName).append(": ");
        String dateQuery = "";
        switch (creationDateFilter.getParameterType()) {
        case PARAMETER_TYPE_PROPERTY:
            if (creationDateFilter instanceof UniOperandQueryParameter) {
                UniOperandQueryParameter<Date> param = (UniOperandQueryParameter<Date>) creationDateFilter;
                switch (param.getOperatorType()) {
                case OPERATOR_EQUAL:
                    dateQuery = formatDateInSolrFormat(param.getValue());
                    break;
                case OPERATOR_LESSER:
                    query.insert(0, "NOT ");
                    dateQuery = "[" + formatDateInSolrFormat(param.getValue()) + " TO *]";
                    //              dateQuery = "-[" + param.getValue() + " TO *]";
                    break;
                case OPERATOR_GREATER_EQUAL:
                    dateQuery = "[" + formatDateInSolrFormat(param.getValue()) + " TO *]";
                    break;
                case OPERATOR_GREATER:
                    query.insert(0, "NOT ");
                    dateQuery = "[* TO " + formatDateInSolrFormat(param.getValue()) + "]";
                    //              dateQuery = "-[* TO " + param.getValue() + "]";
                    break;
                case OPERATOR_LESSER_EQUAL:
                    dateQuery = "[* TO " + formatDateInSolrFormat(param.getValue()) + "]";
                    break;
                default:
                    dateQuery = "[* TO *]";
                }
            }
            if (creationDateFilter instanceof BiOperandQueryParameter) {
                BiOperandQueryParameter<Date> param = (BiOperandQueryParameter<Date>) creationDateFilter;
                if (param.getOperatorType().equals(OperatorType.OPERATOR_BETWEEN)) {
                    dateQuery = "[" + formatDateInSolrFormat(param.getFirstValue()) + " TO "
                            + formatDateInSolrFormat(param.getSecondValue()) + "]";
                }
            }
            break;
        default:
            UniOperandQueryParameter<Date> param = (UniOperandQueryParameter<Date>) creationDateFilter;
            dateQuery = param.getPropertyName() + ": [* TO *]";
            break;
        }
        query.append(dateQuery);
        return query.toString();
    }

    public static String formatDateInSolrFormat(Date date) {
        return DateFormatUtils.formatUTC(date, SOLR_DATE_FORMAT);
    }

    @Override
    public void reIndex(ContentId contentId) {
        if (contentId != null) {
            Content content = contentLoader.loadById(contentId);
            if (content != null) {
                reindexListener.notify(SmartContentAPI.getInstance().getEventRegistrar()
                        .<Content>createEvent(EventType.CREATE, Type.CONTENT, content));
            }
        }
    }

    @Override
    public void reIndex(final WorkspaceId workspaceId) {
        if (logger.isInfoEnabled()) {
            logger.info(new StringBuilder("Re-Indexing ").append(workspaceId).toString());
        }
        executorService.submit(new Runnable() {

            @Override
            public void run() {
                try {
                    final QueryParameter param;
                    if (workspaceId == null) {
                        param = null;
                    } else {
                        param = QueryParameterFactory.getStringLikePropertyParam("id",
                                new StringBuilder(workspaceId.toString()).append(':').toString(), MatchMode.START);
                    }
                    final QueryParameter<Integer> maxResultsParam = QueryParameterFactory.getMaxResultsParam(100);
                    boolean hasMore = true;
                    ContentId lastId = null;
                    List<QueryParameter> params = new ArrayList<QueryParameter>();
                    logger.debug("Beginning iteration over contents");
                    while (hasMore) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Trying with Last ID " + lastId);
                        }
                        params.clear();
                        if (param != null) {
                            params.add(param);
                        }
                        params.add(maxResultsParam);
                        if (lastId != null) {
                            try {
                                params.add(QueryParameterFactory.getGreaterThanPropertyParam("id",
                                        contentLoader.getByteArrayFromId(lastId)));
                            } catch (Exception ex) {
                                logger.warn("Could not add last id clause " + lastId.toString(), ex);
                            }
                        }
                        List<Content> list = contentLoader.getQueryResult(params);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Has More " + hasMore);
                            logger.debug(
                                    "Content numbers in current iteration " + (list != null ? list.size() : -1));
                        }
                        if (list == null || list.isEmpty()) {
                            hasMore = false;
                        } else {
                            final Content[] contents = new Content[list.size()];
                            int index = 0;
                            for (Content content : list) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Attempting to index " + content.getContentId());
                                }
                                reindexListener.notify(SmartContentAPI.getInstance().getEventRegistrar()
                                        .<Content>createEvent(EventType.UPDATE, Type.CONTENT, content));
                                contents[index++] = content;
                            }
                            lastId = contents[contents.length - 1].getContentId();
                        }
                        if (logger.isDebugEnabled()) {
                            logger.debug("Has More " + hasMore);
                            logger.debug(
                                    "Content numbers in current iteration " + (list != null ? list.size() : -1));
                            logger.debug("Last ID " + lastId);
                            logger.debug("Going for next iteration " + hasMore);
                        }
                    }
                } catch (Exception ex) {
                    logger.error("Error reindexing", ex);
                }
            }
        });
    }

    private QueryParameter findAndRemoveOrderByParam(Collection<QueryParameter> fieldQuery) {
        Iterator<QueryParameter> iterator = fieldQuery.iterator();
        while (iterator.hasNext()) {
            QueryParameter parameter = iterator.next();
            if (parameter.getParameterType().equals(ParameterType.PARAMETER_TYPE_ORDER_BY)) {
                iterator.remove();
                return parameter;
            }
        }
        return null;
    }

    @Override
    public String escapeStringForSearch(String string) {
        return ClientUtils.escapeQueryChars(string);
    }
}