001
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.log.Log;
019 import com.liferay.portal.kernel.log.LogFactoryUtil;
020 import com.liferay.portal.kernel.search.BooleanClause;
021 import com.liferay.portal.kernel.search.BooleanClauseOccur;
022 import com.liferay.portal.kernel.search.BooleanQuery;
023 import com.liferay.portal.kernel.search.Field;
024 import com.liferay.portal.kernel.search.Query;
025 import com.liferay.portal.kernel.search.QueryConfig;
026 import com.liferay.portal.kernel.search.QueryTerm;
027 import com.liferay.portal.kernel.search.SearchContext;
028 import com.liferay.portal.kernel.search.SearchException;
029 import com.liferay.portal.kernel.search.Sort;
030 import com.liferay.portal.kernel.search.TermQuery;
031 import com.liferay.portal.kernel.search.TermRangeQuery;
032 import com.liferay.portal.kernel.search.WildcardQuery;
033 import com.liferay.portal.kernel.util.ArrayUtil;
034 import com.liferay.portal.kernel.util.GetterUtil;
035 import com.liferay.portal.kernel.util.StringBundler;
036 import com.liferay.portal.kernel.util.StringPool;
037 import com.liferay.portal.kernel.util.Validator;
038 import com.liferay.portal.model.RepositoryEntry;
039 import com.liferay.portal.model.User;
040 import com.liferay.portal.service.RepositoryEntryLocalServiceUtil;
041 import com.liferay.portal.service.UserLocalServiceUtil;
042
043 import java.util.HashMap;
044 import java.util.HashSet;
045 import java.util.List;
046 import java.util.Map;
047 import java.util.Set;
048 import java.util.regex.Pattern;
049
050
053 public class BaseCmisSearchQueryBuilder implements CMISSearchQueryBuilder {
054
055 @Override
056 public String buildQuery(SearchContext searchContext, Query query)
057 throws SearchException {
058
059 StringBundler sb = new StringBundler();
060
061 sb.append("SELECT cmis:objectId");
062
063 QueryConfig queryConfig = searchContext.getQueryConfig();
064
065 if (queryConfig.isScoreEnabled()) {
066 sb.append(", SCORE() AS HITS");
067 }
068
069 sb.append(" FROM cmis:document");
070
071 CMISDisjunction cmisDisjunction = new CMISDisjunction();
072
073 if (_log.isDebugEnabled()) {
074 _log.debug(
075 "Repository query support " +
076 queryConfig.getAttribute("capabilityQuery"));
077 }
078
079 if (!isSupportsOnlyFullText(queryConfig)) {
080 traversePropertiesQuery(cmisDisjunction, query, queryConfig);
081 }
082
083 if (isSupportsFullText(queryConfig)) {
084 CMISContainsExpression cmisContainsExpression =
085 new CMISContainsExpression();
086
087 traverseContentQuery(cmisContainsExpression, query, queryConfig);
088
089 if (!cmisContainsExpression.isEmpty()) {
090 cmisDisjunction.add(cmisContainsExpression);
091 }
092 }
093
094 if (!cmisDisjunction.isEmpty()) {
095 sb.append(" WHERE ");
096 sb.append(cmisDisjunction.toQueryFragment());
097 }
098
099 Sort[] sorts = searchContext.getSorts();
100
101 if (queryConfig.isScoreEnabled() || ArrayUtil.isNotEmpty(sorts)) {
102 sb.append(" ORDER BY ");
103 }
104
105 if (ArrayUtil.isNotEmpty(sorts)) {
106 int i = 0;
107
108 for (Sort sort : sorts) {
109 String fieldName = sort.getFieldName();
110
111 if (!isSupportedField(fieldName)) {
112 continue;
113 }
114
115 if (i > 0) {
116 sb.append(", ");
117 }
118
119 sb.append(getCmisField(fieldName));
120
121 if (sort.isReverse()) {
122 sb.append(" DESC");
123 }
124 else {
125 sb.append(" ASC");
126 }
127
128 i++;
129 }
130 }
131 else if (queryConfig.isScoreEnabled()) {
132 sb.append("HITS DESC");
133 }
134
135 if (_log.isDebugEnabled()) {
136 _log.debug("CMIS query " + sb);
137 }
138
139 return sb.toString();
140 }
141
142 protected CMISCriterion buildFieldExpression(
143 String field, String value,
144 CMISSimpleExpressionOperator cmisSimpleExpressionOperator,
145 QueryConfig queryConfig)
146 throws SearchException {
147
148 CMISCriterion cmisCriterion = null;
149
150 boolean wildcard =
151 CMISSimpleExpressionOperator.LIKE == cmisSimpleExpressionOperator;
152
153 if (field.equals(Field.FOLDER_ID)) {
154 long folderId = GetterUtil.getLong(value);
155
156 try {
157 RepositoryEntry repositoryEntry =
158 RepositoryEntryLocalServiceUtil.fetchRepositoryEntry(
159 folderId);
160
161 if (repositoryEntry != null) {
162 String objectId = repositoryEntry.getMappedId();
163
164 objectId = CMISParameterValueUtil.formatParameterValue(
165 field, objectId, wildcard, queryConfig);
166
167 if (queryConfig.isSearchSubfolders()) {
168 cmisCriterion = new CMISInTreeExpression(objectId);
169 }
170 else {
171 cmisCriterion = new CMISInFolderExpression(objectId);
172 }
173 }
174 }
175 catch (SystemException se) {
176 throw new SearchException(
177 "Unable to determine folder {folderId=" + folderId + "}",
178 se);
179 }
180 }
181 else if (field.equals(Field.USER_ID)) {
182 try {
183 long userId = GetterUtil.getLong(value);
184
185 User user = UserLocalServiceUtil.getUserById(userId);
186
187 String screenName = CMISParameterValueUtil.formatParameterValue(
188 field, user.getScreenName(), wildcard, queryConfig);
189
190 cmisCriterion = new CMISSimpleExpression(
191 getCmisField(field), screenName,
192 cmisSimpleExpressionOperator);
193 }
194 catch (Exception e) {
195 if (e instanceof SearchException) {
196 throw (SearchException)e;
197 }
198
199 throw new SearchException(
200 "Unable to determine user {" + field + "=" + value + "}",
201 e);
202 }
203 }
204 else {
205 value = CMISParameterValueUtil.formatParameterValue(
206 field, value, wildcard, queryConfig);
207
208 cmisCriterion = new CMISSimpleExpression(
209 getCmisField(field), value, cmisSimpleExpressionOperator);
210 }
211
212 return cmisCriterion;
213 }
214
215 protected String getCmisField(String field) {
216 return _cmisFields.get(field);
217 }
218
219 protected boolean isSupportedField(String field) {
220 return _supportedFields.contains(field);
221 }
222
223 protected boolean isSupportsFullText(QueryConfig queryConfig) {
224 String capabilityQuery = (String)queryConfig.getAttribute(
225 "capabilityQuery");
226
227 if (Validator.isNull(capabilityQuery)) {
228 return false;
229 }
230
231 if (capabilityQuery.equals("bothcombined") ||
232 capabilityQuery.equals("fulltextonly")) {
233
234 return true;
235 }
236
237 return false;
238 }
239
240 protected boolean isSupportsOnlyFullText(QueryConfig queryConfig) {
241 String capabilityQuery = (String)queryConfig.getAttribute(
242 "capabilityQuery");
243
244 if (Validator.isNull(capabilityQuery)) {
245 return false;
246 }
247
248 if (capabilityQuery.equals("fulltextonly")) {
249 return true;
250 }
251
252 return false;
253 }
254
255 protected void traverseContentQuery(
256 CMISJunction cmisJunction, Query query, QueryConfig queryConfig)
257 throws SearchException {
258
259 if (query instanceof BooleanQuery) {
260 BooleanQuery booleanQuery = (BooleanQuery)query;
261
262 List<BooleanClause> booleanClauses = booleanQuery.clauses();
263
264 CMISFullTextConjunction anyCMISConjunction =
265 new CMISFullTextConjunction();
266 CMISDisjunction cmisDisjunction = new CMISDisjunction();
267 CMISFullTextConjunction notCMISConjunction =
268 new CMISFullTextConjunction();
269
270 for (BooleanClause booleanClause : booleanClauses) {
271 CMISJunction currentCMISJunction = cmisDisjunction;
272
273 BooleanClauseOccur booleanClauseOccur =
274 booleanClause.getBooleanClauseOccur();
275
276 if (booleanClauseOccur.equals(BooleanClauseOccur.MUST)) {
277 currentCMISJunction = anyCMISConjunction;
278 }
279 else if (booleanClauseOccur.equals(
280 BooleanClauseOccur.MUST_NOT)) {
281
282 currentCMISJunction = notCMISConjunction;
283 }
284
285 Query booleanClauseQuery = booleanClause.getQuery();
286
287 traverseContentQuery(
288 currentCMISJunction, booleanClauseQuery, queryConfig);
289 }
290
291 if (!anyCMISConjunction.isEmpty()) {
292 cmisJunction.add(anyCMISConjunction);
293 }
294
295 if (!cmisDisjunction.isEmpty()) {
296 cmisJunction.add(cmisDisjunction);
297 }
298
299 if (!notCMISConjunction.isEmpty()) {
300 CMISContainsNotExpression cmisContainsNotExpression =
301 new CMISContainsNotExpression(notCMISConjunction);
302
303 cmisJunction.add(cmisContainsNotExpression);
304 }
305 }
306 else if (query instanceof TermQuery) {
307 TermQuery termQuery = (TermQuery)query;
308
309 QueryTerm queryTerm = termQuery.getQueryTerm();
310
311 if (!_isContentFieldQueryTerm(queryTerm)) {
312 return;
313 }
314
315 String field = queryTerm.getField();
316 String value = queryTerm.getValue();
317
318 value = CMISParameterValueUtil.formatParameterValue(
319 field, value, false, queryConfig);
320
321 CMISContainsValueExpression cmisContainsValueExpression =
322 new CMISContainsValueExpression(value);
323
324 cmisJunction.add(cmisContainsValueExpression);
325 }
326 else if (query instanceof WildcardQuery) {
327 WildcardQuery wildcardQuery = (WildcardQuery)query;
328
329 QueryTerm queryTerm = wildcardQuery.getQueryTerm();
330
331 if (!_isContentFieldQueryTerm(queryTerm)) {
332 return;
333 }
334
335 String value = queryTerm.getValue();
336 String[] terms = value.split(_STAR_PATTERN);
337
338 CMISConjunction cmisConjunction = new CMISConjunction();
339
340 for (String term : terms) {
341 if (Validator.isNotNull(term)) {
342 CMISContainsValueExpression containsValueExpression =
343 new CMISContainsValueExpression(term);
344
345 cmisConjunction.add(containsValueExpression);
346 }
347 }
348
349 cmisJunction.add(cmisConjunction);
350 }
351 else if (query instanceof TermRangeQuery) {
352 return;
353 }
354 }
355
356 protected void traversePropertiesQuery(
357 CMISJunction cmisJunction, Query query, QueryConfig queryConfig)
358 throws SearchException {
359
360 if (query instanceof BooleanQuery) {
361 BooleanQuery booleanQuery = (BooleanQuery)query;
362
363 List<BooleanClause> booleanClauses = booleanQuery.clauses();
364
365 CMISConjunction anyCMISConjunction = new CMISConjunction();
366 CMISDisjunction cmisDisjunction = new CMISDisjunction();
367 CMISConjunction notCMISConjunction = new CMISConjunction();
368
369 for (BooleanClause booleanClause : booleanClauses) {
370 CMISJunction currentCMISJunction = cmisDisjunction;
371
372 BooleanClauseOccur booleanClauseOccur =
373 booleanClause.getBooleanClauseOccur();
374
375 if (booleanClauseOccur.equals(BooleanClauseOccur.MUST)) {
376 currentCMISJunction = anyCMISConjunction;
377 }
378 else if (booleanClauseOccur.equals(
379 BooleanClauseOccur.MUST_NOT)) {
380
381 currentCMISJunction = notCMISConjunction;
382 }
383
384 Query booleanClauseQuery = booleanClause.getQuery();
385
386 traversePropertiesQuery(
387 currentCMISJunction, booleanClauseQuery, queryConfig);
388 }
389
390 if (!anyCMISConjunction.isEmpty()) {
391 cmisJunction.add(anyCMISConjunction);
392 }
393
394 if (!cmisDisjunction.isEmpty()) {
395 cmisJunction.add(cmisDisjunction);
396 }
397
398 if (!notCMISConjunction.isEmpty()) {
399 cmisJunction.add(new CMISNotExpression(notCMISConjunction));
400 }
401 }
402 else if (query instanceof TermQuery) {
403 TermQuery termQuery = (TermQuery)query;
404
405 QueryTerm queryTerm = termQuery.getQueryTerm();
406
407 if (!isSupportedField(queryTerm.getField())) {
408 return;
409 }
410
411 CMISCriterion cmisCriterion = buildFieldExpression(
412 queryTerm.getField(), queryTerm.getValue(),
413 CMISSimpleExpressionOperator.EQ, queryConfig);
414
415 if (cmisCriterion != null) {
416 cmisJunction.add(cmisCriterion);
417 }
418 }
419 else if (query instanceof TermRangeQuery) {
420 TermRangeQuery termRangeQuery = (TermRangeQuery)query;
421
422 if (!isSupportedField(termRangeQuery.getField())) {
423 return;
424 }
425
426 String fieldName = termRangeQuery.getField();
427
428 String cmisField = getCmisField(fieldName);
429 String cmisLowerTerm = CMISParameterValueUtil.formatParameterValue(
430 fieldName, termRangeQuery.getLowerTerm(), false, queryConfig);
431 String cmisUpperTerm = CMISParameterValueUtil.formatParameterValue(
432 fieldName, termRangeQuery.getUpperTerm(), false, queryConfig);
433
434 CMISCriterion cmisCriterion = new CMISBetweenExpression(
435 cmisField, cmisLowerTerm, cmisUpperTerm,
436 termRangeQuery.includesLower(), termRangeQuery.includesUpper());
437
438 cmisJunction.add(cmisCriterion);
439 }
440 else if (query instanceof WildcardQuery) {
441 WildcardQuery wildcardQuery = (WildcardQuery)query;
442
443 QueryTerm queryTerm = wildcardQuery.getQueryTerm();
444
445 if (!isSupportedField(queryTerm.getField())) {
446 return;
447 }
448
449 CMISCriterion cmisCriterion = buildFieldExpression(
450 queryTerm.getField(), queryTerm.getValue(),
451 CMISSimpleExpressionOperator.LIKE, queryConfig);
452
453 if (cmisCriterion != null) {
454 cmisJunction.add(cmisCriterion);
455 }
456 }
457 }
458
459 private boolean _isContentFieldQueryTerm(QueryTerm queryTerm) {
460 String fieldName = queryTerm.getField();
461
462 return fieldName.equals(Field.CONTENT);
463 }
464
465 private static final String _STAR_PATTERN = Pattern.quote(StringPool.STAR);
466
467 private static Log _log = LogFactoryUtil.getLog(
468 BaseCmisSearchQueryBuilder.class);
469
470 private static Map<String, String> _cmisFields;
471 private static Set<String> _supportedFields;
472
473 static {
474 _cmisFields = new HashMap<String, String>();
475
476 _cmisFields.put(Field.CREATE_DATE, "cmis:creationDate");
477 _cmisFields.put(Field.MODIFIED_DATE, "cmis:lastModificationDate");
478 _cmisFields.put(Field.NAME, "cmis:name");
479 _cmisFields.put(Field.TITLE, "cmis:name");
480 _cmisFields.put(Field.USER_ID, "cmis:createdBy");
481 _cmisFields.put(Field.USER_NAME, "cmis:createdBy");
482
483 _supportedFields = new HashSet<String>();
484
485 _supportedFields.add(Field.CREATE_DATE);
486 _supportedFields.add(Field.FOLDER_ID);
487 _supportedFields.add(Field.MODIFIED_DATE);
488 _supportedFields.add(Field.NAME);
489 _supportedFields.add(Field.TITLE);
490 _supportedFields.add(Field.USER_ID);
491 _supportedFields.add(Field.USER_NAME);
492 }
493
494 }