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