001
014
015 package com.liferay.portal.kernel.search;
016
017 import com.liferay.portal.NoSuchModelException;
018 import com.liferay.portal.kernel.dao.orm.QueryUtil;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.util.GetterUtil;
022 import com.liferay.portal.kernel.util.PropsKeys;
023 import com.liferay.portal.kernel.util.PropsUtil;
024 import com.liferay.portal.kernel.util.Time;
025 import com.liferay.portal.kernel.util.Validator;
026 import com.liferay.portal.model.Group;
027 import com.liferay.portal.security.permission.ActionKeys;
028 import com.liferay.portal.security.permission.PermissionChecker;
029 import com.liferay.portal.security.permission.PermissionThreadLocal;
030 import com.liferay.portal.service.GroupLocalServiceUtil;
031 import com.liferay.portlet.asset.service.AssetCategoryServiceUtil;
032
033 import java.util.ArrayList;
034 import java.util.List;
035
036
039 public abstract class BaseIndexer implements Indexer {
040
041 public void delete(Object obj) throws SearchException {
042 try {
043 doDelete(obj);
044 }
045 catch (SearchException se) {
046 throw se;
047 }
048 catch (Exception e) {
049 throw new SearchException(e);
050 }
051 }
052
053 public Document getDocument(Object obj) throws SearchException {
054 try {
055 return doGetDocument(obj);
056 }
057 catch (SearchException se) {
058 throw se;
059 }
060 catch (Exception e) {
061 throw new SearchException(e);
062 }
063 }
064
065 public void reindex(Object obj) throws SearchException {
066 try {
067 if (SearchEngineUtil.isIndexReadOnly()) {
068 return;
069 }
070
071 doReindex(obj);
072 }
073 catch (SearchException se) {
074 throw se;
075 }
076 catch (Exception e) {
077 throw new SearchException(e);
078 }
079 }
080
081 public void reindex(String className, long classPK) throws SearchException {
082 try {
083 if (SearchEngineUtil.isIndexReadOnly()) {
084 return;
085 }
086
087 doReindex(className, classPK);
088 }
089 catch (NoSuchModelException nsme) {
090 if (_log.isWarnEnabled()) {
091 _log.warn("Unable to index " + className + " " + classPK);
092 }
093 }
094 catch (SearchException se) {
095 throw se;
096 }
097 catch (Exception e) {
098 throw new SearchException(e);
099 }
100 }
101
102 public void reindex(String[] ids) throws SearchException {
103 try {
104 if (SearchEngineUtil.isIndexReadOnly()) {
105 return;
106 }
107
108 doReindex(ids);
109 }
110 catch (SearchException se) {
111 throw se;
112 }
113 catch (Exception e) {
114 throw new SearchException(e);
115 }
116 }
117
118 public Hits search(SearchContext searchContext) throws SearchException {
119 try {
120 String className = getClassName(searchContext);
121
122 BooleanQuery contextQuery = BooleanQueryFactoryUtil.create();
123
124 addSearchAssetCategoryIds(contextQuery, searchContext);
125 addSearchAssetTagNames(contextQuery, searchContext);
126 addSearchGroupId(contextQuery, searchContext);
127 addSearchOwnerUserId(contextQuery, searchContext);
128 addSearchCategoryIds(contextQuery, searchContext);
129 addSearchNodeIds(contextQuery, searchContext);
130 addSearchFolderIds(contextQuery, searchContext);
131 addSearchPortletIds(contextQuery, searchContext);
132
133 BooleanQuery fullQuery = createFullQuery(
134 contextQuery, searchContext);
135
136 PermissionChecker permissionChecker =
137 PermissionThreadLocal.getPermissionChecker();
138
139 int start = searchContext.getStart();
140 int end = searchContext.getEnd();
141
142 if (isFilterSearch() && (permissionChecker != null)) {
143 start = 0;
144 end = end + INDEX_FILTER_SEARCH_LIMIT;
145 }
146
147 Hits hits = SearchEngineUtil.search(
148 searchContext.getCompanyId(), searchContext.getGroupIds(),
149 searchContext.getUserId(), className, fullQuery,
150 searchContext.getSorts(), start, end);
151
152 if (isFilterSearch() && (permissionChecker != null)) {
153 hits = filterSearch(hits, permissionChecker, searchContext);
154 }
155
156 return hits;
157 }
158 catch (SearchException se) {
159 throw se;
160 }
161 catch (Exception e) {
162 throw new SearchException(e);
163 }
164 }
165
166 protected void addSearchAssetCategoryIds(
167 BooleanQuery contextQuery, SearchContext searchContext)
168 throws Exception {
169
170 long[] assetCategoryIds = searchContext.getAssetCategoryIds();
171
172 if ((assetCategoryIds == null) || (assetCategoryIds.length == 0)) {
173 return;
174 }
175
176 BooleanQuery assetCategoryIdsQuery = BooleanQueryFactoryUtil.create();
177
178 for (long assetCategoryId : assetCategoryIds) {
179 if (searchContext.getUserId() > 0) {
180 try {
181 AssetCategoryServiceUtil.getCategory(assetCategoryId);
182 }
183 catch (Exception e) {
184 continue;
185 }
186 }
187
188 TermQuery termQuery = TermQueryFactoryUtil.create(
189 Field.ASSET_CATEGORY_IDS, assetCategoryId);
190
191 assetCategoryIdsQuery.add(termQuery, BooleanClauseOccur.MUST);
192 }
193
194 if (!assetCategoryIdsQuery.clauses().isEmpty()) {
195 contextQuery.add(assetCategoryIdsQuery, BooleanClauseOccur.MUST);
196 }
197 }
198
199 protected void addSearchAssetTagNames(
200 BooleanQuery contextQuery, SearchContext searchContext)
201 throws Exception {
202
203 String[] assetTagNames = searchContext.getAssetTagNames();
204
205 if ((assetTagNames == null) || (assetTagNames.length == 0)) {
206 return;
207 }
208
209 BooleanQuery assetTagNamesQuery = BooleanQueryFactoryUtil.create();
210
211 for (String assetTagName : assetTagNames) {
212 TermQuery termQuery = TermQueryFactoryUtil.create(
213 Field.ASSET_TAG_NAMES, assetTagName);
214
215 assetTagNamesQuery.add(termQuery, BooleanClauseOccur.MUST);
216 }
217
218 if (!assetTagNamesQuery.clauses().isEmpty()) {
219 contextQuery.add(assetTagNamesQuery, BooleanClauseOccur.MUST);
220 }
221 }
222
223 protected void addSearchCategoryIds(
224 BooleanQuery contextQuery, SearchContext searchContext)
225 throws Exception {
226
227 long[] categoryIds = searchContext.getCategoryIds();
228
229 if ((categoryIds == null) || (categoryIds.length == 0)) {
230 return;
231 }
232
233 BooleanQuery categoryIdsQuery = BooleanQueryFactoryUtil.create();
234
235 for (long categoryId : categoryIds) {
236 if (searchContext.getUserId() > 0) {
237 try {
238 checkSearchCategoryId(categoryId, searchContext);
239 }
240 catch (Exception e) {
241 continue;
242 }
243 }
244
245 TermQuery termQuery = TermQueryFactoryUtil.create(
246 Field.CATEGORY_ID, categoryId);
247
248 categoryIdsQuery.add(termQuery, BooleanClauseOccur.SHOULD);
249 }
250
251 if (!categoryIdsQuery.clauses().isEmpty()) {
252 contextQuery.add(categoryIdsQuery, BooleanClauseOccur.MUST);
253 }
254 }
255
256 protected void addSearchFolderIds(
257 BooleanQuery contextQuery, SearchContext searchContext)
258 throws Exception {
259
260 long[] folderIds = searchContext.getFolderIds();
261
262 if ((folderIds == null) || (folderIds.length == 0)) {
263 return;
264 }
265
266 BooleanQuery folderIdsQuery = BooleanQueryFactoryUtil.create();
267
268 for (long folderId : folderIds) {
269 if (searchContext.getUserId() > 0) {
270 try {
271 checkSearchFolderId(folderId, searchContext);
272 }
273 catch (Exception e) {
274 continue;
275 }
276 }
277
278 TermQuery termQuery = TermQueryFactoryUtil.create(
279 Field.FOLDER_ID, folderId);
280
281 folderIdsQuery.add(termQuery, BooleanClauseOccur.SHOULD);
282 }
283
284 if (!folderIdsQuery.clauses().isEmpty()) {
285 contextQuery.add(folderIdsQuery, BooleanClauseOccur.MUST);
286 }
287 }
288
289 protected void addSearchGroupId(
290 BooleanQuery contextQuery, SearchContext searchContext)
291 throws Exception {
292
293 long[] groupIds = searchContext.getGroupIds();
294
295 if ((groupIds == null) || (groupIds.length == 0) ||
296 ((groupIds.length == 1) && (groupIds[0] == 0))){
297
298 return;
299 }
300
301 BooleanQuery groupIdsQuery = BooleanQueryFactoryUtil.create();
302
303 for (int i = 0; i < groupIds.length; i ++) {
304 long groupId = groupIds[i];
305
306 if (groupId <= 0) {
307 continue;
308 }
309
310 try {
311 Group group = GroupLocalServiceUtil.getGroup(groupId);
312
313 long parentGroupId = groupId;
314
315 if (group.isLayout() || searchContext.isScopeStrict()) {
316 contextQuery.addRequiredTerm(
317 Field.SCOPE_GROUP_ID, groupId);
318 }
319
320 if (group.isLayout()) {
321 parentGroupId = group.getParentGroupId();
322 }
323
324 groupIdsQuery.addTerm(Field.GROUP_ID, parentGroupId);
325
326 groupIds[i] = parentGroupId;
327 }
328 catch (Exception e) {
329 continue;
330 }
331 }
332
333 searchContext.setGroupIds(groupIds);
334
335 if (!groupIdsQuery.clauses().isEmpty()) {
336 contextQuery.add(groupIdsQuery, BooleanClauseOccur.MUST);
337 }
338 }
339
340 protected void addSearchKeywords(
341 BooleanQuery searchQuery, SearchContext searchContext)
342 throws Exception {
343
344 String keywords = searchContext.getKeywords();
345
346 if (Validator.isNull(keywords)) {
347 return;
348 }
349
350 searchQuery.addTerms(_KEYWORDS_FIELDS, keywords);
351 }
352
353 protected void addSearchNodeIds(
354 BooleanQuery contextQuery, SearchContext searchContext)
355 throws Exception {
356
357 long[] nodeIds = searchContext.getNodeIds();
358
359 if ((nodeIds == null) || (nodeIds.length == 0)) {
360 return;
361 }
362
363 BooleanQuery nodeIdsQuery = BooleanQueryFactoryUtil.create();
364
365 for (long nodeId : nodeIds) {
366 if (searchContext.getUserId() > 0) {
367 try {
368 checkSearchNodeId(nodeId, searchContext);
369 }
370 catch (Exception e) {
371 continue;
372 }
373 }
374
375 TermQuery termQuery = TermQueryFactoryUtil.create(
376 Field.NODE_ID, nodeId);
377
378 nodeIdsQuery.add(termQuery, BooleanClauseOccur.SHOULD);
379 }
380
381 if (!nodeIdsQuery.clauses().isEmpty()) {
382 contextQuery.add(nodeIdsQuery, BooleanClauseOccur.MUST);
383 }
384 }
385
386 protected void addSearchOwnerUserId(
387 BooleanQuery contextQuery, SearchContext searchContext) {
388
389 long ownerUserId = searchContext.getOwnerUserId();
390
391 if (ownerUserId > 0) {
392 contextQuery.addRequiredTerm(Field.USER_ID, ownerUserId);
393 }
394 }
395
396 protected void addSearchPortletIds(
397 BooleanQuery contextQuery, SearchContext searchContext)
398 throws Exception {
399
400 String[] portletIds = searchContext.getPortletIds();
401
402 if ((portletIds == null) || (portletIds.length == 0)) {
403 contextQuery.addRequiredTerm(
404 Field.PORTLET_ID, getPortletId(searchContext));
405 }
406 else {
407 BooleanQuery portletIdsQuery = BooleanQueryFactoryUtil.create();
408
409 for (String portletId : portletIds) {
410 if (Validator.isNull(portletId)) {
411 continue;
412 }
413
414 TermQuery termQuery = TermQueryFactoryUtil.create(
415 Field.PORTLET_ID, portletId);
416
417 portletIdsQuery.add(termQuery, BooleanClauseOccur.SHOULD);
418 }
419
420 if (!portletIdsQuery.clauses().isEmpty()) {
421 contextQuery.add(portletIdsQuery, BooleanClauseOccur.MUST);
422 }
423 }
424 }
425
426 protected void checkSearchCategoryId(
427 long categoryId, SearchContext searchContext)
428 throws Exception {
429 }
430
431 protected void checkSearchFolderId(
432 long folderId, SearchContext searchContext)
433 throws Exception {
434 }
435
436 protected void checkSearchNodeId(
437 long nodeId, SearchContext searchContext)
438 throws Exception {
439 }
440
441 protected BooleanQuery createFullQuery(
442 BooleanQuery contextQuery, SearchContext searchContext)
443 throws Exception {
444
445 postProcessContextQuery(contextQuery, searchContext);
446
447 BooleanQuery searchQuery = BooleanQueryFactoryUtil.create();
448
449 addSearchKeywords(searchQuery, searchContext);
450 postProcessSearchQuery(searchQuery, searchContext);
451
452 BooleanQuery fullQuery = BooleanQueryFactoryUtil.create();
453
454 fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
455
456 if (!searchQuery.clauses().isEmpty()) {
457 fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
458 }
459
460 BooleanClause[] booleanClauses = searchContext.getBooleanClauses();
461
462 if (booleanClauses != null) {
463 for (BooleanClause booleanClause : booleanClauses) {
464 fullQuery.add(
465 booleanClause.getQuery(),
466 booleanClause.getBooleanClauseOccur());
467 }
468 }
469
470 postProcessFullQuery(fullQuery, searchContext);
471
472 return fullQuery;
473 }
474
475 protected abstract void doDelete(Object obj) throws Exception;
476
477 protected abstract Document doGetDocument(Object obj) throws Exception;
478
479 protected abstract void doReindex(Object obj) throws Exception;
480
481 protected abstract void doReindex(String className, long classPK)
482 throws Exception;
483
484 protected abstract void doReindex(String[] ids) throws Exception;
485
486 protected Hits filterSearch(
487 Hits hits, PermissionChecker permissionChecker,
488 SearchContext searchContext) {
489
490 List<Document> docs = new ArrayList<Document>();
491 List<Float> scores = new ArrayList<Float>();
492
493 for (int i = 0; i < hits.getLength(); i++) {
494 Document doc = hits.doc(i);
495
496 long entryClassPK = GetterUtil.getLong(
497 doc.get(Field.ENTRY_CLASS_PK));
498
499 try {
500 if (hasPermission(
501 permissionChecker, entryClassPK, ActionKeys.VIEW)) {
502
503 docs.add(hits.doc(i));
504 scores.add(hits.score(i));
505 }
506 }
507 catch (Exception e) {
508 }
509 }
510
511 int length = docs.size();
512
513 hits.setLength(length);
514
515 int start = searchContext.getStart();
516 int end = searchContext.getEnd();
517
518 if ((start != QueryUtil.ALL_POS) && (end != QueryUtil.ALL_POS)) {
519 if (end > length) {
520 end = length;
521 }
522
523 docs = docs.subList(start, end);
524 }
525
526 hits.setDocs(docs.toArray(new Document[docs.size()]));
527 hits.setScores(scores.toArray(new Float[docs.size()]));
528
529 hits.setSearchTime(
530 (float)(System.currentTimeMillis() - hits.getStart()) /
531 Time.SECOND);
532
533 return hits;
534 }
535
536 protected String getClassName(SearchContext searchContext) {
537 String[] classNames = getClassNames();
538
539 if (classNames.length != 1) {
540 throw new UnsupportedOperationException(
541 "Search method needs to be manually implemented for " +
542 "indexers with more than one class name");
543 }
544
545 return classNames[0];
546 }
547
548 protected long getParentGroupId(long groupId) {
549 long parentGroupId = groupId;
550
551 try {
552 Group group = GroupLocalServiceUtil.getGroup(groupId);
553
554 if (group.isLayout()) {
555 parentGroupId = group.getParentGroupId();
556 }
557 }
558 catch (Exception e) {
559 }
560
561 return parentGroupId;
562 }
563
564 protected abstract String getPortletId(SearchContext searchContext);
565
566 protected boolean hasPermission(
567 PermissionChecker permissionChecker, long entryClassPK,
568 String actionId)
569 throws Exception {
570
571 return true;
572 }
573
574 protected boolean isFilterSearch() {
575 return _FILTER_SEARCH;
576 }
577
578 protected void postProcessContextQuery(
579 BooleanQuery contextQuery, SearchContext searchContext)
580 throws Exception {
581 }
582
583 protected void postProcessFullQuery(
584 BooleanQuery fullQuery, SearchContext searchContext)
585 throws Exception {
586 }
587
588 protected void postProcessSearchQuery(
589 BooleanQuery searchQuery, SearchContext searchContext)
590 throws Exception {
591 }
592
593 private static final boolean _FILTER_SEARCH = false;
594
595 public static final int INDEX_FILTER_SEARCH_LIMIT = GetterUtil.getInteger(
596 PropsUtil.get(PropsKeys.INDEX_FILTER_SEARCH_LIMIT));
597
598 private static final String[] _KEYWORDS_FIELDS = {
599 Field.ASSET_TAG_NAMES, Field.COMMENTS, Field.CONTENT, Field.DESCRIPTION,
600 Field.PROPERTIES, Field.TITLE, Field.URL, Field.USER_NAME
601 };
602
603 private static Log _log = LogFactoryUtil.getLog(BaseIndexer.class);
604
605 }