001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.repository.cmis.search;
016    
017    import com.liferay.portal.kernel.exception.SystemException;
018    import com.liferay.portal.kernel.search.BooleanClause;
019    import com.liferay.portal.kernel.search.BooleanClauseOccur;
020    import com.liferay.portal.kernel.search.BooleanQuery;
021    import com.liferay.portal.kernel.search.Field;
022    import com.liferay.portal.kernel.search.Query;
023    import com.liferay.portal.kernel.search.QueryConfig;
024    import com.liferay.portal.kernel.search.QueryTerm;
025    import com.liferay.portal.kernel.search.SearchContext;
026    import com.liferay.portal.kernel.search.SearchException;
027    import com.liferay.portal.kernel.search.Sort;
028    import com.liferay.portal.kernel.search.TermQuery;
029    import com.liferay.portal.kernel.search.TermRangeQuery;
030    import com.liferay.portal.kernel.search.WildcardQuery;
031    import com.liferay.portal.kernel.util.GetterUtil;
032    import com.liferay.portal.kernel.util.StringBundler;
033    import com.liferay.portal.kernel.util.Validator;
034    import com.liferay.portal.model.RepositoryEntry;
035    import com.liferay.portal.model.User;
036    import com.liferay.portal.service.RepositoryEntryLocalServiceUtil;
037    import com.liferay.portal.service.UserLocalServiceUtil;
038    
039    import java.util.HashMap;
040    import java.util.HashSet;
041    import java.util.List;
042    import java.util.Map;
043    import java.util.Set;
044    
045    /**
046     * @author Mika Koivisto
047     */
048    public class BaseCmisSearchQueryBuilder implements CMISSearchQueryBuilder {
049    
050            @Override
051            public String buildQuery(SearchContext searchContext, Query query)
052                    throws SearchException {
053    
054                    StringBundler sb = new StringBundler();
055    
056                    sb.append("SELECT cmis:objectId");
057    
058                    QueryConfig queryConfig = searchContext.getQueryConfig();
059    
060                    if (queryConfig.isScoreEnabled()) {
061                            sb.append(", SCORE() AS HITS");
062                    }
063    
064                    sb.append(" FROM cmis:document");
065    
066                    CMISConjunction cmisConjunction = new CMISConjunction();
067    
068                    traverseQuery(cmisConjunction, query, queryConfig);
069    
070                    if (!cmisConjunction.isEmpty()) {
071                            sb.append(" WHERE ");
072                            sb.append(cmisConjunction.toQueryFragment());
073                    }
074    
075                    Sort[] sorts = searchContext.getSorts();
076    
077                    if (queryConfig.isScoreEnabled() ||
078                            ((sorts != null) && (sorts.length > 0))) {
079    
080                            sb.append(" ORDER BY ");
081                    }
082    
083                    if ((sorts != null) && (sorts.length > 0)) {
084                            int i = 0;
085    
086                            for (Sort sort : sorts) {
087                                    String fieldName = sort.getFieldName();
088    
089                                    if (!isSupportedField(fieldName)) {
090                                            continue;
091                                    }
092    
093                                    if (i > 0) {
094                                            sb.append(", ");
095                                    }
096    
097                                    sb.append(getCmisField(fieldName));
098    
099                                    if (sort.isReverse()) {
100                                            sb.append(" DESC");
101                                    }
102                                    else {
103                                            sb.append(" ASC");
104                                    }
105    
106                                    i++;
107                            }
108                    }
109                    else if (queryConfig.isScoreEnabled()) {
110                            sb.append("HITS DESC");
111                    }
112    
113                    return sb.toString();
114            }
115    
116            protected CMISCriterion buildFieldExpression(
117                            String field, String value,
118                            CMISSimpleExpressionOperator cmisSimpleExpressionOperator)
119                    throws SearchException {
120    
121                    CMISCriterion cmisCriterion = null;
122    
123                    boolean wildcard =
124                            CMISSimpleExpressionOperator.LIKE == cmisSimpleExpressionOperator;
125    
126                    if (field.equals(Field.CONTENT)) {
127                            value = CMISParameterValueUtil.formatParameterValue(field, value);
128    
129                            cmisCriterion = new CMISContainsExpression(value);
130                    }
131                    else if (field.equals(Field.FOLDER_ID)) {
132                            long folderId = GetterUtil.getLong(value);
133    
134                            try {
135                                    RepositoryEntry repositoryEntry =
136                                            RepositoryEntryLocalServiceUtil.fetchRepositoryEntry(
137                                                    folderId);
138    
139                                    if (repositoryEntry != null) {
140                                            String objectId = repositoryEntry.getMappedId();
141    
142                                            objectId = CMISParameterValueUtil.formatParameterValue(
143                                                    field, objectId, wildcard);
144    
145                                            cmisCriterion = new CMISInFolderExpression(objectId);
146                                    }
147                            }
148                            catch (SystemException se) {
149                                    throw new SearchException(
150                                            "Unable to determine folder {folderId=" + folderId + "}",
151                                            se);
152                            }
153                    }
154                    else if (field.equals(Field.USER_ID)) {
155                            try {
156                                    long userId = GetterUtil.getLong(value);
157    
158                                    User user = UserLocalServiceUtil.getUserById(userId);
159    
160                                    String screenName = CMISParameterValueUtil.formatParameterValue(
161                                            field, user.getScreenName(), wildcard);
162    
163                                    cmisCriterion = new CMISSimpleExpression(
164                                            getCmisField(field), screenName,
165                                            cmisSimpleExpressionOperator);
166                            }
167                            catch (Exception e) {
168                                    if (e instanceof SearchException) {
169                                            throw (SearchException)e;
170                                    }
171    
172                                    throw new SearchException(
173                                            "Unable to determine user {" + field + "=" + value + "}",
174                                            e);
175                            }
176                    }
177                    else {
178                            value = CMISParameterValueUtil.formatParameterValue(
179                                    field, value, wildcard);
180    
181                            cmisCriterion = new CMISSimpleExpression(
182                                    getCmisField(field), value, cmisSimpleExpressionOperator);
183                    }
184    
185                    return cmisCriterion;
186            }
187    
188            protected String getCmisField(String field) {
189                    return _cmisFields.get(field);
190            }
191    
192            protected boolean isSupportedField(String field) {
193                    return _supportedFields.contains(field);
194            }
195    
196            protected boolean isSupportsFullText(QueryConfig queryConfig) {
197                    String capabilityQuery = (String)queryConfig.getAttribute(
198                            "capabilityQuery");
199    
200                    if (Validator.isNull(capabilityQuery)) {
201                            return false;
202                    }
203    
204                    if (capabilityQuery.equals("bothcombined") ||
205                            capabilityQuery.equals("fulltextonly")) {
206    
207                            return true;
208                    }
209    
210                    return false;
211            }
212    
213            protected boolean isSupportsOnlyFullText(QueryConfig queryConfig) {
214                    String capabilityQuery = (String)queryConfig.getAttribute(
215                            "capabilityQuery");
216    
217                    if (Validator.isNull(capabilityQuery)) {
218                            return false;
219                    }
220    
221                    if (capabilityQuery.equals("fulltextonly")) {
222                            return true;
223                    }
224    
225                    return false;
226            }
227    
228            protected void traverseQuery(
229                            CMISJunction criterion, Query query, QueryConfig queryConfig)
230                    throws SearchException {
231    
232                    if (query instanceof BooleanQuery) {
233                            BooleanQuery booleanQuery = (BooleanQuery)query;
234    
235                            List<BooleanClause> booleanClauses = booleanQuery.clauses();
236    
237                            CMISConjunction anyCMISConjunction = new CMISConjunction();
238                            CMISConjunction notCMISConjunction = new CMISConjunction();
239                            CMISDisjunction cmisDisjunction = new CMISDisjunction();
240    
241                            for (BooleanClause booleanClause : booleanClauses) {
242                                    CMISJunction cmisJunction = cmisDisjunction;
243    
244                                    BooleanClauseOccur booleanClauseOccur =
245                                            booleanClause.getBooleanClauseOccur();
246    
247                                    if (booleanClauseOccur.equals(BooleanClauseOccur.MUST)) {
248                                            cmisJunction = anyCMISConjunction;
249                                    }
250                                    else if (booleanClauseOccur.equals(
251                                                            BooleanClauseOccur.MUST_NOT)) {
252    
253                                            cmisJunction = notCMISConjunction;
254                                    }
255    
256                                    Query booleanClauseQuery = booleanClause.getQuery();
257    
258                                    traverseQuery(cmisJunction, booleanClauseQuery, queryConfig);
259                            }
260    
261                            if (!anyCMISConjunction.isEmpty()) {
262                                    criterion.add(anyCMISConjunction);
263                            }
264    
265                            if (!cmisDisjunction.isEmpty()) {
266                                    criterion.add(cmisDisjunction);
267                            }
268    
269                            if (!notCMISConjunction.isEmpty()) {
270                                    criterion.add(new CMISNotExpression(notCMISConjunction));
271                            }
272                    }
273                    else if (query instanceof TermQuery) {
274                            TermQuery termQuery = (TermQuery)query;
275    
276                            QueryTerm queryTerm = termQuery.getQueryTerm();
277    
278                            if (!isSupportedField(queryTerm.getField())) {
279                                    return;
280                            }
281    
282                            CMISCriterion cmisExpression = buildFieldExpression(
283                                    queryTerm.getField(), queryTerm.getValue(),
284                                    CMISSimpleExpressionOperator.EQ);
285    
286                            if (cmisExpression != null) {
287                                    boolean add = true;
288    
289                                    if ((cmisExpression instanceof CMISContainsExpression) &&
290                                            !isSupportsFullText(queryConfig)) {
291    
292                                            add = false;
293                                    }
294                                    else if (!((cmisExpression instanceof CMISContainsExpression) ||
295                                                       (cmisExpression instanceof CMISInFolderExpression) ||
296                                                       (cmisExpression instanceof CMISInTreeExpression)) &&
297                                                     isSupportsOnlyFullText(queryConfig)) {
298    
299                                            add = false;
300                                    }
301    
302                                    if (add) {
303                                            criterion.add(cmisExpression);
304                                    }
305                            }
306                    }
307                    else if (query instanceof TermRangeQuery) {
308                            TermRangeQuery termRangeQuery = (TermRangeQuery)query;
309    
310                            if (!isSupportedField(termRangeQuery.getField())) {
311                                    return;
312                            }
313    
314                            String fieldName = termRangeQuery.getField();
315    
316                            String cmisField = getCmisField(fieldName);
317                            String cmisLowerTerm = CMISParameterValueUtil.formatParameterValue(
318                                    fieldName, termRangeQuery.getLowerTerm());
319                            String cmisUpperTerm = CMISParameterValueUtil.formatParameterValue(
320                                    fieldName, termRangeQuery.getUpperTerm());
321    
322                            CMISCriterion cmisCriterion = new CMISBetweenExpression(
323                                    cmisField, cmisLowerTerm, cmisUpperTerm,
324                                    termRangeQuery.includesLower(), termRangeQuery.includesUpper());
325    
326                            criterion.add(cmisCriterion);
327                    }
328                    else if (query instanceof WildcardQuery) {
329                            WildcardQuery wildcardQuery = (WildcardQuery)query;
330    
331                            QueryTerm queryTerm = wildcardQuery.getQueryTerm();
332    
333                            if (!isSupportedField(queryTerm.getField())) {
334                                    return;
335                            }
336    
337                            CMISCriterion cmisCriterion = buildFieldExpression(
338                                    queryTerm.getField(), queryTerm.getValue(),
339                                    CMISSimpleExpressionOperator.LIKE);
340    
341                            if (cmisCriterion != null) {
342                                    boolean add = true;
343    
344                                    if ((cmisCriterion instanceof CMISContainsExpression) &&
345                                            !isSupportsFullText(queryConfig)) {
346    
347                                            add = false;
348                                    }
349                                    else if (!((cmisCriterion instanceof CMISContainsExpression) ||
350                                                       (cmisCriterion instanceof CMISInFolderExpression) ||
351                                                       (cmisCriterion instanceof CMISInTreeExpression)) &&
352                                                     isSupportsOnlyFullText(queryConfig)) {
353    
354                                            add = false;
355                                    }
356    
357                                    if (add) {
358                                            criterion.add(cmisCriterion);
359                                    }
360                            }
361                    }
362            }
363    
364            private static Map<String, String> _cmisFields;
365            private static Set<String> _supportedFields;
366    
367            static {
368                    _cmisFields = new HashMap<String, String>();
369    
370                    _cmisFields.put(Field.CREATE_DATE, "cmis:creationDate");
371                    _cmisFields.put(Field.MODIFIED_DATE, "cmis:lastModificationDate");
372                    _cmisFields.put(Field.NAME, "cmis:name");
373                    _cmisFields.put(Field.TITLE, "cmis:name");
374                    _cmisFields.put(Field.USER_ID, "cmis:createdBy");
375                    _cmisFields.put(Field.USER_NAME, "cmis:createdBy");
376    
377                    _supportedFields = new HashSet<String>();
378    
379                    _supportedFields.add(Field.CONTENT);
380                    _supportedFields.add(Field.CREATE_DATE);
381                    _supportedFields.add(Field.FOLDER_ID);
382                    _supportedFields.add(Field.MODIFIED_DATE);
383                    _supportedFields.add(Field.NAME);
384                    _supportedFields.add(Field.TITLE);
385                    _supportedFields.add(Field.USER_ID);
386                    _supportedFields.add(Field.USER_NAME);
387            }
388    
389    }