001    /**
002     * Copyright (c) 2000-2012 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            public String buildQuery(SearchContext searchContext, Query query)
051                    throws SearchException {
052    
053                    StringBundler sb = new StringBundler();
054    
055                    sb.append("SELECT cmis:objectId");
056    
057                    QueryConfig queryConfig = searchContext.getQueryConfig();
058    
059                    if (queryConfig.isScoreEnabled()) {
060                            sb.append(", SCORE() AS HITS");
061                    }
062    
063                    sb.append(" FROM cmis:document");
064    
065                    CMISConjunction cmisConjunction = new CMISConjunction();
066    
067                    traverseQuery(cmisConjunction, query, queryConfig);
068    
069                    if (!cmisConjunction.isEmpty()) {
070                            sb.append(" WHERE ");
071                            sb.append(cmisConjunction.toQueryFragment());
072                    }
073    
074                    Sort[] sorts = searchContext.getSorts();
075    
076                    if (queryConfig.isScoreEnabled() ||
077                            ((sorts != null) && sorts.length > 0)) {
078    
079                            sb.append(" ORDER BY ");
080                    }
081    
082                    if ((sorts != null) && (sorts.length > 0)) {
083                            int i = 0;
084    
085                            for (Sort sort : sorts) {
086                                    String fieldName = sort.getFieldName();
087    
088                                    if (!isSupportedField(fieldName)) {
089                                            continue;
090                                    }
091    
092                                    if (i > 0) {
093                                            sb.append(", ");
094                                    }
095    
096                                    sb.append(getCmisField(fieldName));
097    
098                                    if (sort.isReverse()) {
099                                            sb.append(" DESC");
100                                    }
101                                    else {
102                                            sb.append(" ASC");
103                                    }
104    
105                                    i++;
106                            }
107                    }
108                    else if (queryConfig.isScoreEnabled()) {
109                            sb.append("HITS DESC");
110                    }
111    
112                    return sb.toString();
113            }
114    
115            protected CMISCriterion buildFieldExpression(
116                            String field, String value,
117                            CMISSimpleExpressionOperator cmisSimpleExpressionOperator)
118                    throws SearchException {
119    
120                    CMISCriterion cmisCriterion = null;
121    
122                    boolean wildcard =
123                            CMISSimpleExpressionOperator.LIKE == cmisSimpleExpressionOperator;
124    
125                    if (field.equals(Field.CONTENT)) {
126                            value = CMISParameterValueUtil.formatParameterValue(field, value);
127    
128                            cmisCriterion = new CMISContainsExpression(value);
129                    }
130                    else if (field.equals(Field.FOLDER_ID)) {
131                            long folderId = GetterUtil.getLong(value);
132    
133                            try {
134                                    RepositoryEntry repositoryEntry =
135                                            RepositoryEntryLocalServiceUtil.fetchRepositoryEntry(
136                                                    folderId);
137    
138                                    if (repositoryEntry != null) {
139                                            String objectId = repositoryEntry.getMappedId();
140    
141                                            objectId = CMISParameterValueUtil.formatParameterValue(
142                                                    field, objectId, wildcard);
143    
144                                            cmisCriterion = new CMISInFolderExpression(objectId);
145                                    }
146                            }
147                            catch (SystemException se) {
148                                    throw new SearchException(
149                                            "Unable to determine folder {folderId=" + folderId + "}",
150                                            se);
151                            }
152                    }
153                    else if (field.equals(Field.USER_ID)) {
154                            try {
155                                    long userId = GetterUtil.getLong(value);
156    
157                                    User user = UserLocalServiceUtil.getUserById(userId);
158    
159                                    String screenName = CMISParameterValueUtil.formatParameterValue(
160                                            field, user.getScreenName(), wildcard);
161    
162                                    cmisCriterion = new CMISSimpleExpression(
163                                            getCmisField(field), screenName,
164                                            cmisSimpleExpressionOperator);
165                            }
166                            catch (Exception e) {
167                                    if (e instanceof SearchException) {
168                                            throw (SearchException)e;
169                                    }
170    
171                                    throw new SearchException(
172                                            "Unable to determine user {" + field + "=" + value + "}",
173                                            e);
174                            }
175                    }
176                    else {
177                            value = CMISParameterValueUtil.formatParameterValue(
178                                    field, value, wildcard);
179    
180                            cmisCriterion = new CMISSimpleExpression(
181                                    getCmisField(field), value, cmisSimpleExpressionOperator);
182                    }
183    
184                    return cmisCriterion;
185            }
186    
187            protected String getCmisField(String field) {
188                    return _cmisFields.get(field);
189            }
190    
191            protected boolean isSupportedField(String field) {
192                    return _supportedFields.contains(field);
193            }
194    
195            protected boolean isSupportsFullText(QueryConfig queryConfig) {
196                    String capabilityQuery = (String)queryConfig.getAttribute(
197                            "capabilityQuery");
198    
199                    if (Validator.isNull(capabilityQuery)) {
200                            return false;
201                    }
202    
203                    if (capabilityQuery.equals("bothcombined") ||
204                            capabilityQuery.equals("fulltextonly")) {
205    
206                            return true;
207                    }
208    
209                    return false;
210            }
211    
212            protected boolean isSupportsOnlyFullText(QueryConfig queryConfig) {
213                    String capabilityQuery = (String)queryConfig.getAttribute(
214                            "capabilityQuery");
215    
216                    if (Validator.isNull(capabilityQuery)) {
217                            return false;
218                    }
219    
220                    if (capabilityQuery.equals("fulltextonly")) {
221                            return true;
222                    }
223    
224                    return false;
225            }
226    
227            protected void traverseQuery(
228                            CMISJunction criterion, Query query, QueryConfig queryConfig)
229                    throws SearchException {
230    
231                    if (query instanceof BooleanQuery) {
232                            BooleanQuery booleanQuery = (BooleanQuery)query;
233    
234                            List<BooleanClause> booleanClauses = booleanQuery.clauses();
235    
236                            CMISConjunction anyCMISConjunction = new CMISConjunction();
237                            CMISConjunction notCMISConjunction = new CMISConjunction();
238                            CMISDisjunction cmisDisjunction = new CMISDisjunction();
239    
240                            for (BooleanClause booleanClause : booleanClauses) {
241                                    CMISJunction cmisJunction = cmisDisjunction;
242    
243                                    BooleanClauseOccur booleanClauseOccur =
244                                            booleanClause.getBooleanClauseOccur();
245    
246                                    if (booleanClauseOccur.equals(BooleanClauseOccur.MUST)) {
247                                            cmisJunction = anyCMISConjunction;
248                                    }
249                                    else if (booleanClauseOccur.equals(
250                                                            BooleanClauseOccur.MUST_NOT)) {
251    
252                                            cmisJunction = notCMISConjunction;
253                                    }
254    
255                                    Query booleanClauseQuery = booleanClause.getQuery();
256    
257                                    traverseQuery(cmisJunction, booleanClauseQuery, queryConfig);
258                            }
259    
260                            if (!anyCMISConjunction.isEmpty()) {
261                                    criterion.add(anyCMISConjunction);
262                            }
263    
264                            if (!cmisDisjunction.isEmpty()) {
265                                    criterion.add(cmisDisjunction);
266                            }
267    
268                            if (!notCMISConjunction.isEmpty()) {
269                                    criterion.add(new CMISNotExpression(notCMISConjunction));
270                            }
271                    }
272                    else if (query instanceof TermQuery) {
273                            TermQuery termQuery = (TermQuery)query;
274    
275                            QueryTerm queryTerm = termQuery.getQueryTerm();
276    
277                            if (!isSupportedField(queryTerm.getField())) {
278                                    return;
279                            }
280    
281                            CMISCriterion cmisExpression = buildFieldExpression(
282                                    queryTerm.getField(), queryTerm.getValue(),
283                                    CMISSimpleExpressionOperator.EQ);
284    
285                            if (cmisExpression != null) {
286                                    boolean add = true;
287    
288                                    if ((cmisExpression instanceof CMISContainsExpression) &&
289                                            !isSupportsFullText(queryConfig)) {
290    
291                                            add = false;
292                                    }
293                                    else if (!((cmisExpression instanceof CMISContainsExpression) ||
294                                                       (cmisExpression instanceof CMISInFolderExpression) ||
295                                                       (cmisExpression instanceof CMISInTreeExpression)) &&
296                                                     isSupportsOnlyFullText(queryConfig)) {
297    
298                                            add = false;
299                                    }
300    
301                                    if (add) {
302                                            criterion.add(cmisExpression);
303                                    }
304                            }
305                    }
306                    else if (query instanceof TermRangeQuery) {
307                            TermRangeQuery termRangeQuery = (TermRangeQuery)query;
308    
309                            if (!isSupportedField(termRangeQuery.getField())) {
310                                    return;
311                            }
312    
313                            String fieldName = termRangeQuery.getField();
314    
315                            String cmisField = getCmisField(fieldName);
316                            String cmisLowerTerm = CMISParameterValueUtil.formatParameterValue(
317                                    fieldName, termRangeQuery.getLowerTerm());
318                            String cmisUpperTerm = CMISParameterValueUtil.formatParameterValue(
319                                    fieldName, termRangeQuery.getUpperTerm());
320    
321                            CMISCriterion cmisCriterion = new CMISBetweenExpression(
322                                    cmisField, cmisLowerTerm, cmisUpperTerm,
323                                    termRangeQuery.includesLower(), termRangeQuery.includesUpper());
324    
325                            criterion.add(cmisCriterion);
326                    }
327                    else if (query instanceof WildcardQuery) {
328                            WildcardQuery wildcardQuery = (WildcardQuery)query;
329    
330                            QueryTerm queryTerm = wildcardQuery.getQueryTerm();
331    
332                            if (!isSupportedField(queryTerm.getField())) {
333                                    return;
334                            }
335    
336                            CMISCriterion cmisCriterion = buildFieldExpression(
337                                    queryTerm.getField(), queryTerm.getValue(),
338                                    CMISSimpleExpressionOperator.LIKE);
339    
340                            if (cmisCriterion != null) {
341                                    boolean add = true;
342    
343                                    if ((cmisCriterion instanceof CMISContainsExpression) &&
344                                            !isSupportsFullText(queryConfig)) {
345    
346                                            add = false;
347                                    }
348                                    else if (!((cmisCriterion instanceof CMISContainsExpression) ||
349                                                       (cmisCriterion instanceof CMISInFolderExpression) ||
350                                                       (cmisCriterion instanceof CMISInTreeExpression)) &&
351                                                     isSupportsOnlyFullText(queryConfig)) {
352    
353                                            add = false;
354                                    }
355    
356                                    if (add) {
357                                            criterion.add(cmisCriterion);
358                                    }
359                            }
360                    }
361            }
362    
363            private static Map<String, String> _cmisFields;
364            private static Set<String> _supportedFields;
365    
366            static {
367                    _cmisFields = new HashMap<String, String>();
368    
369                    _cmisFields.put(Field.CREATE_DATE, "cmis:creationDate");
370                    _cmisFields.put(Field.MODIFIED_DATE, "cmis:lastModificationDate");
371                    _cmisFields.put(Field.NAME, "cmis:name");
372                    _cmisFields.put(Field.TITLE, "cmis:name");
373                    _cmisFields.put(Field.USER_ID, "cmis:createdBy");
374                    _cmisFields.put(Field.USER_NAME, "cmis:createdBy");
375    
376                    _supportedFields = new HashSet<String>();
377    
378                    _supportedFields.add(Field.CONTENT);
379                    _supportedFields.add(Field.CREATE_DATE);
380                    _supportedFields.add(Field.FOLDER_ID);
381                    _supportedFields.add(Field.MODIFIED_DATE);
382                    _supportedFields.add(Field.NAME);
383                    _supportedFields.add(Field.TITLE);
384                    _supportedFields.add(Field.USER_ID);
385                    _supportedFields.add(Field.USER_NAME);
386            }
387    
388    }