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 CMISContainsValueExpression containsValueExpression =
342 new CMISContainsValueExpression(term);
343
344 cmisConjunction.add(containsValueExpression);
345 }
346
347 cmisJunction.add(cmisConjunction);
348 }
349 else if (query instanceof TermRangeQuery) {
350 return;
351 }
352 }
353
354 protected void traversePropertiesQuery(
355 CMISJunction cmisJunction, Query query, QueryConfig queryConfig)
356 throws SearchException {
357
358 if (query instanceof BooleanQuery) {
359 BooleanQuery booleanQuery = (BooleanQuery)query;
360
361 List<BooleanClause> booleanClauses = booleanQuery.clauses();
362
363 CMISConjunction anyCMISConjunction = new CMISConjunction();
364 CMISDisjunction cmisDisjunction = new CMISDisjunction();
365 CMISConjunction notCMISConjunction = new CMISConjunction();
366
367 for (BooleanClause booleanClause : booleanClauses) {
368 CMISJunction currentCMISJunction = cmisDisjunction;
369
370 BooleanClauseOccur booleanClauseOccur =
371 booleanClause.getBooleanClauseOccur();
372
373 if (booleanClauseOccur.equals(BooleanClauseOccur.MUST)) {
374 currentCMISJunction = anyCMISConjunction;
375 }
376 else if (booleanClauseOccur.equals(
377 BooleanClauseOccur.MUST_NOT)) {
378
379 currentCMISJunction = notCMISConjunction;
380 }
381
382 Query booleanClauseQuery = booleanClause.getQuery();
383
384 traversePropertiesQuery(
385 currentCMISJunction, booleanClauseQuery, queryConfig);
386 }
387
388 if (!anyCMISConjunction.isEmpty()) {
389 cmisJunction.add(anyCMISConjunction);
390 }
391
392 if (!cmisDisjunction.isEmpty()) {
393 cmisJunction.add(cmisDisjunction);
394 }
395
396 if (!notCMISConjunction.isEmpty()) {
397 cmisJunction.add(new CMISNotExpression(notCMISConjunction));
398 }
399 }
400 else if (query instanceof TermQuery) {
401 TermQuery termQuery = (TermQuery)query;
402
403 QueryTerm queryTerm = termQuery.getQueryTerm();
404
405 if (!isSupportedField(queryTerm.getField())) {
406 return;
407 }
408
409 CMISCriterion cmisCriterion = buildFieldExpression(
410 queryTerm.getField(), queryTerm.getValue(),
411 CMISSimpleExpressionOperator.EQ, queryConfig);
412
413 if (cmisCriterion != null) {
414 cmisJunction.add(cmisCriterion);
415 }
416 }
417 else if (query instanceof TermRangeQuery) {
418 TermRangeQuery termRangeQuery = (TermRangeQuery)query;
419
420 if (!isSupportedField(termRangeQuery.getField())) {
421 return;
422 }
423
424 String fieldName = termRangeQuery.getField();
425
426 String cmisField = getCmisField(fieldName);
427 String cmisLowerTerm = CMISParameterValueUtil.formatParameterValue(
428 fieldName, termRangeQuery.getLowerTerm(), false, queryConfig);
429 String cmisUpperTerm = CMISParameterValueUtil.formatParameterValue(
430 fieldName, termRangeQuery.getUpperTerm(), false, queryConfig);
431
432 CMISCriterion cmisCriterion = new CMISBetweenExpression(
433 cmisField, cmisLowerTerm, cmisUpperTerm,
434 termRangeQuery.includesLower(), termRangeQuery.includesUpper());
435
436 cmisJunction.add(cmisCriterion);
437 }
438 else if (query instanceof WildcardQuery) {
439 WildcardQuery wildcardQuery = (WildcardQuery)query;
440
441 QueryTerm queryTerm = wildcardQuery.getQueryTerm();
442
443 if (!isSupportedField(queryTerm.getField())) {
444 return;
445 }
446
447 CMISCriterion cmisCriterion = buildFieldExpression(
448 queryTerm.getField(), queryTerm.getValue(),
449 CMISSimpleExpressionOperator.LIKE, queryConfig);
450
451 if (cmisCriterion != null) {
452 cmisJunction.add(cmisCriterion);
453 }
454 }
455 }
456
457 private boolean _isContentFieldQueryTerm(QueryTerm queryTerm) {
458 String fieldName = queryTerm.getField();
459
460 return fieldName.equals(Field.CONTENT);
461 }
462
463 private static final String _STAR_PATTERN = Pattern.quote(StringPool.STAR);
464
465 private static Log _log = LogFactoryUtil.getLog(
466 BaseCmisSearchQueryBuilder.class);
467
468 private static Map<String, String> _cmisFields;
469 private static Set<String> _supportedFields;
470
471 static {
472 _cmisFields = new HashMap<String, String>();
473
474 _cmisFields.put(Field.CREATE_DATE, "cmis:creationDate");
475 _cmisFields.put(Field.MODIFIED_DATE, "cmis:lastModificationDate");
476 _cmisFields.put(Field.NAME, "cmis:name");
477 _cmisFields.put(Field.TITLE, "cmis:name");
478 _cmisFields.put(Field.USER_ID, "cmis:createdBy");
479 _cmisFields.put(Field.USER_NAME, "cmis:createdBy");
480
481 _supportedFields = new HashSet<String>();
482
483 _supportedFields.add(Field.CREATE_DATE);
484 _supportedFields.add(Field.FOLDER_ID);
485 _supportedFields.add(Field.MODIFIED_DATE);
486 _supportedFields.add(Field.NAME);
487 _supportedFields.add(Field.TITLE);
488 _supportedFields.add(Field.USER_ID);
489 _supportedFields.add(Field.USER_NAME);
490 }
491
492 }