/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. */ package com.liferay.document.library.repository.cmis.search; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.RepositoryEntry; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.search.BooleanClause; import com.liferay.portal.kernel.search.BooleanClauseOccur; import com.liferay.portal.kernel.search.BooleanQuery; import com.liferay.portal.kernel.search.Field; import com.liferay.portal.kernel.search.Query; import com.liferay.portal.kernel.search.QueryConfig; import com.liferay.portal.kernel.search.QueryTerm; import com.liferay.portal.kernel.search.SearchContext; import com.liferay.portal.kernel.search.SearchException; import com.liferay.portal.kernel.search.Sort; import com.liferay.portal.kernel.search.TermQuery; import com.liferay.portal.kernel.search.TermRangeQuery; import com.liferay.portal.kernel.search.WildcardQuery; import com.liferay.portal.kernel.search.generic.MatchQuery; import com.liferay.portal.kernel.service.RepositoryEntryLocalService; import com.liferay.portal.kernel.service.RepositoryEntryLocalServiceUtil; import com.liferay.portal.kernel.service.UserLocalService; import com.liferay.portal.kernel.service.UserLocalServiceUtil; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.Validator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; /** * @author Mika Koivisto */ public class BaseCmisSearchQueryBuilder implements CMISSearchQueryBuilder { public BaseCmisSearchQueryBuilder() { this( RepositoryEntryLocalServiceUtil.getService(), UserLocalServiceUtil.getService()); } @Override public String buildQuery(SearchContext searchContext, Query query) throws SearchException { StringBundler sb = new StringBundler(); sb.append("SELECT cmis:objectId"); QueryConfig queryConfig = searchContext.getQueryConfig(); if (queryConfig.isScoreEnabled()) { sb.append(", SCORE() AS HITS"); } sb.append(" FROM cmis:document"); CMISDisjunction cmisDisjunction = new CMISDisjunction(); if (_log.isDebugEnabled()) { _log.debug( "Repository query support " + queryConfig.getAttribute("capabilityQuery")); } if (!isSupportsOnlyFullText(queryConfig)) { traversePropertiesQuery(cmisDisjunction, query, queryConfig); } if (isSupportsFullText(queryConfig)) { CMISContainsExpression cmisContainsExpression = new CMISContainsExpression(); traverseContentQuery(cmisContainsExpression, query, queryConfig); if (!cmisContainsExpression.isEmpty()) { cmisDisjunction.add(cmisContainsExpression); } } if (!cmisDisjunction.isEmpty()) { sb.append(" WHERE "); sb.append(cmisDisjunction.toQueryFragment()); } Sort[] sorts = searchContext.getSorts(); if (queryConfig.isScoreEnabled() || ArrayUtil.isNotEmpty(sorts)) { sb.append(" ORDER BY "); } if (ArrayUtil.isNotEmpty(sorts)) { int i = 0; for (Sort sort : sorts) { String fieldName = sort.getFieldName(); if (!isSupportedField(fieldName)) { continue; } if (i > 0) { sb.append(", "); } sb.append(getCmisField(fieldName)); if (sort.isReverse()) { sb.append(" DESC"); } else { sb.append(" ASC"); } i++; } } else if (queryConfig.isScoreEnabled()) { sb.append("HITS DESC"); } if (_log.isDebugEnabled()) { _log.debug("CMIS query " + sb); } return sb.toString(); } protected BaseCmisSearchQueryBuilder( RepositoryEntryLocalService repositoryEntryLocalService, UserLocalService userLocalService) { _repositoryEntryLocalService = repositoryEntryLocalService; _userLocalService = userLocalService; } protected CMISCriterion buildFieldExpression( String field, String value, CMISSimpleExpressionOperator cmisSimpleExpressionOperator, QueryConfig queryConfig) throws SearchException { CMISCriterion cmisCriterion = null; boolean wildcard = false; if (CMISSimpleExpressionOperator.LIKE == cmisSimpleExpressionOperator) { wildcard = true; } if (field.equals(Field.FOLDER_ID)) { long folderId = GetterUtil.getLong(value); try { RepositoryEntry repositoryEntry = _repositoryEntryLocalService.fetchRepositoryEntry(folderId); if (repositoryEntry != null) { String objectId = repositoryEntry.getMappedId(); objectId = CMISParameterValueUtil.formatParameterValue( field, objectId, wildcard, queryConfig); if (queryConfig.isSearchSubfolders()) { cmisCriterion = new CMISInTreeExpression(objectId); } else { cmisCriterion = new CMISInFolderExpression(objectId); } } } catch (SystemException se) { throw new SearchException( "Unable to determine folder {folderId=" + folderId + "}", se); } } else if (field.equals(Field.USER_ID)) { try { long userId = GetterUtil.getLong(value); User user = _userLocalService.getUserById(userId); String screenName = CMISParameterValueUtil.formatParameterValue( field, user.getScreenName(), wildcard, queryConfig); cmisCriterion = new CMISSimpleExpression( getCmisField(field), screenName, cmisSimpleExpressionOperator); } catch (Exception e) { if (e instanceof SearchException) { throw (SearchException)e; } throw new SearchException( "Unable to determine user {" + field + "=" + value + "}", e); } } else { value = CMISParameterValueUtil.formatParameterValue( field, value, wildcard, queryConfig); cmisCriterion = new CMISSimpleExpression( getCmisField(field), value, cmisSimpleExpressionOperator); } return cmisCriterion; } protected String getCmisField(String field) { return _cmisFields.get(field); } protected boolean isSupportedField(String field) { return _supportedFields.contains(field); } protected boolean isSupportsFullText(QueryConfig queryConfig) { String capabilityQuery = (String)queryConfig.getAttribute( "capabilityQuery"); if (Validator.isNull(capabilityQuery)) { return false; } if (capabilityQuery.equals("bothcombined") || capabilityQuery.equals("fulltextonly")) { return true; } return false; } protected boolean isSupportsOnlyFullText(QueryConfig queryConfig) { String capabilityQuery = (String)queryConfig.getAttribute( "capabilityQuery"); if (Validator.isNull(capabilityQuery)) { return false; } if (capabilityQuery.equals("fulltextonly")) { return true; } return false; } protected void traverseContentQuery( CMISJunction cmisJunction, Query query, QueryConfig queryConfig) throws SearchException { if (query instanceof BooleanQuery) { BooleanQuery booleanQuery = (BooleanQuery)query; List<BooleanClause<Query>> booleanClauses = booleanQuery.clauses(); CMISFullTextConjunction anyCMISConjunction = new CMISFullTextConjunction(); CMISDisjunction cmisDisjunction = new CMISDisjunction(); CMISFullTextConjunction notCMISConjunction = new CMISFullTextConjunction(); for (BooleanClause<Query> booleanClause : booleanClauses) { CMISJunction currentCMISJunction = cmisDisjunction; BooleanClauseOccur booleanClauseOccur = booleanClause.getBooleanClauseOccur(); if (booleanClauseOccur.equals(BooleanClauseOccur.MUST)) { currentCMISJunction = anyCMISConjunction; } else if (booleanClauseOccur.equals( BooleanClauseOccur.MUST_NOT)) { currentCMISJunction = notCMISConjunction; } Query booleanClauseQuery = booleanClause.getClause(); traverseContentQuery( currentCMISJunction, booleanClauseQuery, queryConfig); } if (!anyCMISConjunction.isEmpty()) { cmisJunction.add(anyCMISConjunction); } if (!cmisDisjunction.isEmpty()) { cmisJunction.add(cmisDisjunction); } if (!notCMISConjunction.isEmpty()) { CMISContainsNotExpression cmisContainsNotExpression = new CMISContainsNotExpression(notCMISConjunction); cmisJunction.add(cmisContainsNotExpression); } } else if (query instanceof TermQuery) { TermQuery termQuery = (TermQuery)query; QueryTerm queryTerm = termQuery.getQueryTerm(); if (!_isContentFieldQueryTerm(queryTerm)) { return; } String field = queryTerm.getField(); String value = queryTerm.getValue(); value = CMISParameterValueUtil.formatParameterValue( field, value, false, queryConfig); CMISContainsValueExpression cmisContainsValueExpression = new CMISContainsValueExpression(value); cmisJunction.add(cmisContainsValueExpression); } else if (query instanceof WildcardQuery) { WildcardQuery wildcardQuery = (WildcardQuery)query; QueryTerm queryTerm = wildcardQuery.getQueryTerm(); if (!_isContentFieldQueryTerm(queryTerm)) { return; } String value = queryTerm.getValue(); String[] terms = value.split(_STAR_PATTERN); CMISConjunction cmisConjunction = new CMISConjunction(); for (String term : terms) { if (Validator.isNotNull(term)) { CMISContainsValueExpression containsValueExpression = new CMISContainsValueExpression(term); cmisConjunction.add(containsValueExpression); } } cmisJunction.add(cmisConjunction); } else if (query instanceof TermRangeQuery) { return; } } protected void traversePropertiesQuery( CMISJunction cmisJunction, Query query, QueryConfig queryConfig) throws SearchException { if (query instanceof BooleanQuery) { BooleanQuery booleanQuery = (BooleanQuery)query; List<BooleanClause<Query>> booleanClauses = booleanQuery.clauses(); CMISConjunction anyCMISConjunction = new CMISConjunction(); CMISDisjunction cmisDisjunction = new CMISDisjunction(); CMISConjunction notCMISConjunction = new CMISConjunction(); for (BooleanClause<Query> booleanClause : booleanClauses) { CMISJunction currentCMISJunction = cmisDisjunction; BooleanClauseOccur booleanClauseOccur = booleanClause.getBooleanClauseOccur(); if (booleanClauseOccur.equals(BooleanClauseOccur.MUST)) { currentCMISJunction = anyCMISConjunction; } else if (booleanClauseOccur.equals( BooleanClauseOccur.MUST_NOT)) { currentCMISJunction = notCMISConjunction; } Query booleanClauseQuery = booleanClause.getClause(); traversePropertiesQuery( currentCMISJunction, booleanClauseQuery, queryConfig); } if (!anyCMISConjunction.isEmpty()) { cmisJunction.add(anyCMISConjunction); } if (!cmisDisjunction.isEmpty()) { cmisJunction.add(cmisDisjunction); } if (!notCMISConjunction.isEmpty()) { cmisJunction.add(new CMISNotExpression(notCMISConjunction)); } } else if (query instanceof MatchQuery) { MatchQuery matchQuery = (MatchQuery)query; if (!isSupportedField(matchQuery.getField())) { return; } CMISCriterion cmisCriterion = buildFieldExpression( matchQuery.getField(), matchQuery.getValue(), CMISSimpleExpressionOperator.EQ, queryConfig); if (cmisCriterion != null) { cmisJunction.add(cmisCriterion); } } else if (query instanceof TermQuery) { TermQuery termQuery = (TermQuery)query; QueryTerm queryTerm = termQuery.getQueryTerm(); if (!isSupportedField(queryTerm.getField())) { return; } CMISCriterion cmisCriterion = buildFieldExpression( queryTerm.getField(), queryTerm.getValue(), CMISSimpleExpressionOperator.EQ, queryConfig); if (cmisCriterion != null) { cmisJunction.add(cmisCriterion); } } else if (query instanceof TermRangeQuery) { TermRangeQuery termRangeQuery = (TermRangeQuery)query; if (!isSupportedField(termRangeQuery.getField())) { return; } String fieldName = termRangeQuery.getField(); String cmisField = getCmisField(fieldName); String cmisLowerTerm = CMISParameterValueUtil.formatParameterValue( fieldName, termRangeQuery.getLowerTerm(), false, queryConfig); String cmisUpperTerm = CMISParameterValueUtil.formatParameterValue( fieldName, termRangeQuery.getUpperTerm(), false, queryConfig); CMISCriterion cmisCriterion = new CMISBetweenExpression( cmisField, cmisLowerTerm, cmisUpperTerm, termRangeQuery.includesLower(), termRangeQuery.includesUpper()); cmisJunction.add(cmisCriterion); } else if (query instanceof WildcardQuery) { WildcardQuery wildcardQuery = (WildcardQuery)query; QueryTerm queryTerm = wildcardQuery.getQueryTerm(); if (!isSupportedField(queryTerm.getField())) { return; } CMISCriterion cmisCriterion = buildFieldExpression( queryTerm.getField(), queryTerm.getValue(), CMISSimpleExpressionOperator.LIKE, queryConfig); if (cmisCriterion != null) { cmisJunction.add(cmisCriterion); } } } private boolean _isContentFieldQueryTerm(QueryTerm queryTerm) { String fieldName = queryTerm.getField(); return fieldName.equals(Field.CONTENT); } private static final String _STAR_PATTERN = Pattern.quote(StringPool.STAR); private static final Log _log = LogFactoryUtil.getLog( BaseCmisSearchQueryBuilder.class); private static final Map<String, String> _cmisFields; private static final Set<String> _supportedFields; static { _cmisFields = new HashMap<>(); _cmisFields.put(Field.CREATE_DATE, "cmis:creationDate"); _cmisFields.put(Field.MODIFIED_DATE, "cmis:lastModificationDate"); _cmisFields.put(Field.NAME, "cmis:name"); _cmisFields.put(Field.TITLE, "cmis:name"); _cmisFields.put(Field.USER_ID, "cmis:createdBy"); _cmisFields.put(Field.USER_NAME, "cmis:createdBy"); _supportedFields = new HashSet<>(); _supportedFields.add(Field.CREATE_DATE); _supportedFields.add(Field.FOLDER_ID); _supportedFields.add(Field.MODIFIED_DATE); _supportedFields.add(Field.NAME); _supportedFields.add(Field.TITLE); _supportedFields.add(Field.USER_ID); _supportedFields.add(Field.USER_NAME); } private final RepositoryEntryLocalService _repositoryEntryLocalService; private final UserLocalService _userLocalService; }