001
014
015 package com.liferay.portal.kernel.search;
016
017 import com.liferay.portal.NoSuchCountryException;
018 import com.liferay.portal.NoSuchModelException;
019 import com.liferay.portal.NoSuchRegionException;
020 import com.liferay.portal.kernel.configuration.Filter;
021 import com.liferay.portal.kernel.dao.orm.QueryUtil;
022 import com.liferay.portal.kernel.exception.PortalException;
023 import com.liferay.portal.kernel.exception.SystemException;
024 import com.liferay.portal.kernel.language.LanguageUtil;
025 import com.liferay.portal.kernel.log.Log;
026 import com.liferay.portal.kernel.log.LogFactoryUtil;
027 import com.liferay.portal.kernel.search.facet.AssetEntriesFacet;
028 import com.liferay.portal.kernel.search.facet.Facet;
029 import com.liferay.portal.kernel.search.facet.MultiValueFacet;
030 import com.liferay.portal.kernel.search.facet.ScopeFacet;
031 import com.liferay.portal.kernel.trash.TrashHandler;
032 import com.liferay.portal.kernel.trash.TrashHandlerRegistryUtil;
033 import com.liferay.portal.kernel.trash.TrashRenderer;
034 import com.liferay.portal.kernel.util.GetterUtil;
035 import com.liferay.portal.kernel.util.ListUtil;
036 import com.liferay.portal.kernel.util.LocaleUtil;
037 import com.liferay.portal.kernel.util.PropsKeys;
038 import com.liferay.portal.kernel.util.PropsUtil;
039 import com.liferay.portal.kernel.util.SetUtil;
040 import com.liferay.portal.kernel.util.StringPool;
041 import com.liferay.portal.kernel.util.StringUtil;
042 import com.liferay.portal.kernel.util.Time;
043 import com.liferay.portal.kernel.util.UnicodeProperties;
044 import com.liferay.portal.kernel.util.Validator;
045 import com.liferay.portal.model.Address;
046 import com.liferay.portal.model.AttachedModel;
047 import com.liferay.portal.model.AuditedModel;
048 import com.liferay.portal.model.BaseModel;
049 import com.liferay.portal.model.Country;
050 import com.liferay.portal.model.Group;
051 import com.liferay.portal.model.GroupedModel;
052 import com.liferay.portal.model.Region;
053 import com.liferay.portal.model.ResourcedModel;
054 import com.liferay.portal.model.User;
055 import com.liferay.portal.model.WorkflowedModel;
056 import com.liferay.portal.security.permission.ActionKeys;
057 import com.liferay.portal.security.permission.PermissionChecker;
058 import com.liferay.portal.security.permission.PermissionThreadLocal;
059 import com.liferay.portal.service.CountryServiceUtil;
060 import com.liferay.portal.service.GroupLocalServiceUtil;
061 import com.liferay.portal.service.RegionServiceUtil;
062 import com.liferay.portal.service.ServiceContext;
063 import com.liferay.portal.service.ServiceContextThreadLocal;
064 import com.liferay.portal.service.UserLocalServiceUtil;
065 import com.liferay.portal.util.PortalUtil;
066 import com.liferay.portlet.asset.model.AssetCategory;
067 import com.liferay.portlet.asset.service.AssetCategoryLocalServiceUtil;
068 import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
069 import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
070 import com.liferay.portlet.dynamicdatamapping.util.DDMIndexerUtil;
071 import com.liferay.portlet.expando.model.ExpandoBridge;
072 import com.liferay.portlet.expando.model.ExpandoColumnConstants;
073 import com.liferay.portlet.expando.util.ExpandoBridgeFactoryUtil;
074 import com.liferay.portlet.expando.util.ExpandoBridgeIndexerUtil;
075 import com.liferay.portlet.trash.model.TrashEntry;
076 import com.liferay.portlet.trash.service.TrashEntryLocalServiceUtil;
077
078 import java.io.Serializable;
079
080 import java.util.ArrayList;
081 import java.util.Date;
082 import java.util.HashMap;
083 import java.util.HashSet;
084 import java.util.List;
085 import java.util.Locale;
086 import java.util.Map;
087 import java.util.Set;
088
089 import javax.portlet.PortletURL;
090
091
097 public abstract class BaseIndexer implements Indexer {
098
099 public static final int INDEX_FILTER_SEARCH_LIMIT = GetterUtil.getInteger(
100 PropsUtil.get(PropsKeys.INDEX_FILTER_SEARCH_LIMIT));
101
102 public void delete(long companyId, String uid) throws SearchException {
103 try {
104 SearchEngineUtil.deleteDocument(
105 getSearchEngineId(), companyId, uid);
106 }
107 catch (SearchException se) {
108 throw se;
109 }
110 catch (Exception e) {
111 throw new SearchException(e);
112 }
113 }
114
115 public void delete(Object obj) throws SearchException {
116 try {
117 doDelete(obj);
118 }
119 catch (SearchException se) {
120 throw se;
121 }
122 catch (Exception e) {
123 throw new SearchException(e);
124 }
125 }
126
127 public Document getDocument(Object obj) throws SearchException {
128 try {
129 Document document = doGetDocument(obj);
130
131 for (IndexerPostProcessor indexerPostProcessor :
132 _indexerPostProcessors) {
133
134 indexerPostProcessor.postProcessDocument(document, obj);
135 }
136
137 if (document == null) {
138 return null;
139 }
140
141 Map<String, Field> fields = document.getFields();
142
143 Field groupIdField = fields.get(Field.GROUP_ID);
144
145 if (groupIdField != null) {
146 long groupId = GetterUtil.getLong(groupIdField.getValue());
147
148 addStagingGroupKeyword(document, groupId);
149 }
150
151 return document;
152 }
153 catch (SearchException se) {
154 throw se;
155 }
156 catch (Exception e) {
157 throw new SearchException(e);
158 }
159 }
160
161 public BooleanQuery getFacetQuery(
162 String className, SearchContext searchContext)
163 throws Exception {
164
165 BooleanQuery facetQuery = BooleanQueryFactoryUtil.create(searchContext);
166
167 facetQuery.addExactTerm(Field.ENTRY_CLASS_NAME, className);
168
169 if (searchContext.getUserId() > 0) {
170 SearchPermissionChecker searchPermissionChecker =
171 SearchEngineUtil.getSearchPermissionChecker();
172
173 facetQuery =
174 (BooleanQuery)searchPermissionChecker.getPermissionQuery(
175 searchContext.getCompanyId(), searchContext.getGroupIds(),
176 searchContext.getUserId(), className, facetQuery,
177 searchContext);
178 }
179
180 return facetQuery;
181 }
182
183 public BooleanQuery getFullQuery(SearchContext searchContext)
184 throws SearchException {
185
186 try {
187 searchContext.setSearchEngineId(getSearchEngineId());
188
189 searchContext.setEntryClassNames(
190 new String[] {getClassName(searchContext)});
191
192 BooleanQuery contextQuery = BooleanQueryFactoryUtil.create(
193 searchContext);
194
195 addSearchAssetCategoryIds(contextQuery, searchContext);
196 addSearchAssetTagNames(contextQuery, searchContext);
197 addSearchEntryClassNames(contextQuery, searchContext);
198 addSearchGroupId(contextQuery, searchContext);
199
200 BooleanQuery fullQuery = createFullQuery(
201 contextQuery, searchContext);
202
203 fullQuery.setQueryConfig(searchContext.getQueryConfig());
204
205 return fullQuery;
206 }
207 catch (SearchException se) {
208 throw se;
209 }
210 catch (Exception e) {
211 throw new SearchException(e);
212 }
213 }
214
215 public IndexerPostProcessor[] getIndexerPostProcessors() {
216 return _indexerPostProcessors;
217 }
218
219 public String getSearchEngineId() {
220 if (_searchEngineId != null) {
221 return _searchEngineId;
222 }
223
224 Class<?> clazz = getClass();
225
226 String searchEngineId = GetterUtil.getString(
227 PropsUtil.get(
228 PropsKeys.INDEX_SEARCH_ENGINE_ID, new Filter(clazz.getName())));
229
230 if (Validator.isNotNull(searchEngineId)) {
231 SearchEngine searchEngine = SearchEngineUtil.getSearchEngine(
232 searchEngineId);
233
234 if (searchEngine != null) {
235 _searchEngineId = searchEngineId;
236 }
237 }
238
239 if (_searchEngineId == null) {
240 _searchEngineId = SearchEngineUtil.getDefaultSearchEngineId();
241 }
242
243 if (_log.isDebugEnabled()) {
244 _log.debug(
245 "Search engine ID for " + clazz.getName() + " is " +
246 searchEngineId);
247 }
248
249 return _searchEngineId;
250 }
251
252 public String getSortField(String orderByCol) {
253 String sortField = doGetSortField(orderByCol);
254
255 if (DocumentImpl.isSortableTextField(sortField)) {
256 return DocumentImpl.getSortableFieldName(sortField);
257 }
258
259 return sortField;
260 }
261
262 public Summary getSummary(
263 Document document, Locale locale, String snippet,
264 PortletURL portletURL)
265 throws SearchException {
266
267 try {
268 Summary summary = doGetSummary(
269 document, locale, snippet, portletURL);
270
271 for (IndexerPostProcessor indexerPostProcessor :
272 _indexerPostProcessors) {
273
274 indexerPostProcessor.postProcessSummary(
275 summary, document, locale, snippet, portletURL);
276 }
277
278 return summary;
279 }
280 catch (SearchException se) {
281 throw se;
282 }
283 catch (Exception e) {
284 throw new SearchException(e);
285 }
286 }
287
288 public boolean hasPermission(
289 PermissionChecker permissionChecker, String entryClassName,
290 long entryClassPK, String actionId)
291 throws Exception {
292
293 return true;
294 }
295
296 public boolean isFilterSearch() {
297 return _filterSearch;
298 }
299
300 public boolean isIndexerEnabled() {
301 return _indexerEnabled;
302 }
303
304 public boolean isPermissionAware() {
305 return _permissionAware;
306 }
307
308 public boolean isStagingAware() {
309 return _stagingAware;
310 }
311
312 public void postProcessContextQuery(
313 BooleanQuery contextQuery, SearchContext searchContext)
314 throws Exception {
315 }
316
317 public void postProcessSearchQuery(
318 BooleanQuery searchQuery, SearchContext searchContext)
319 throws Exception {
320
321 String keywords = searchContext.getKeywords();
322
323 if (Validator.isNull(keywords)) {
324 addSearchTerm(searchQuery, searchContext, Field.DESCRIPTION, false);
325 addSearchTerm(searchQuery, searchContext, Field.TITLE, false);
326 addSearchTerm(searchQuery, searchContext, Field.USER_NAME, false);
327 }
328 }
329
330 public void registerIndexerPostProcessor(
331 IndexerPostProcessor indexerPostProcessor) {
332
333 List<IndexerPostProcessor> indexerPostProcessorsList =
334 ListUtil.fromArray(_indexerPostProcessors);
335
336 indexerPostProcessorsList.add(indexerPostProcessor);
337
338 _indexerPostProcessors = indexerPostProcessorsList.toArray(
339 new IndexerPostProcessor[indexerPostProcessorsList.size()]);
340 }
341
342 public void reindex(Object obj) throws SearchException {
343 try {
344 if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled()) {
345 return;
346 }
347
348 doReindex(obj);
349 }
350 catch (SearchException se) {
351 throw se;
352 }
353 catch (Exception e) {
354 throw new SearchException(e);
355 }
356 }
357
358 public void reindex(String className, long classPK) throws SearchException {
359 try {
360 if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled()) {
361 return;
362 }
363
364 doReindex(className, classPK);
365 }
366 catch (NoSuchModelException nsme) {
367 if (_log.isWarnEnabled()) {
368 _log.warn("Unable to index " + className + " " + classPK);
369 }
370 }
371 catch (SearchException se) {
372 throw se;
373 }
374 catch (Exception e) {
375 throw new SearchException(e);
376 }
377 }
378
379 public void reindex(String[] ids) throws SearchException {
380 try {
381 if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled()) {
382 return;
383 }
384
385 doReindex(ids);
386 }
387 catch (SearchException se) {
388 throw se;
389 }
390 catch (Exception e) {
391 throw new SearchException(e);
392 }
393 }
394
395 public Hits search(SearchContext searchContext) throws SearchException {
396 try {
397 searchContext.setSearchEngineId(getSearchEngineId());
398
399 BooleanQuery fullQuery = getFullQuery(searchContext);
400
401 fullQuery.setQueryConfig(searchContext.getQueryConfig());
402
403 PermissionChecker permissionChecker =
404 PermissionThreadLocal.getPermissionChecker();
405
406 int end = searchContext.getEnd();
407 int start = searchContext.getStart();
408
409 if (isFilterSearch() && (permissionChecker != null)) {
410 searchContext.setEnd(end + INDEX_FILTER_SEARCH_LIMIT);
411 searchContext.setStart(0);
412 }
413
414 Hits hits = SearchEngineUtil.search(searchContext, fullQuery);
415
416 searchContext.setEnd(end);
417 searchContext.setStart(start);
418
419 if (isFilterSearch() && (permissionChecker != null)) {
420 hits = filterSearch(hits, permissionChecker, searchContext);
421 }
422
423 return hits;
424 }
425 catch (SearchException se) {
426 throw se;
427 }
428 catch (Exception e) {
429 throw new SearchException(e);
430 }
431 }
432
433 public void unregisterIndexerPostProcessor(
434 IndexerPostProcessor indexerPostProcessor) {
435
436 List<IndexerPostProcessor> indexerPostProcessorsList =
437 ListUtil.fromArray(_indexerPostProcessors);
438
439 indexerPostProcessorsList.remove(indexerPostProcessor);
440
441 _indexerPostProcessors = indexerPostProcessorsList.toArray(
442 new IndexerPostProcessor[indexerPostProcessorsList.size()]);
443 }
444
445
449 protected void addLocalizedSearchTerm(
450 BooleanQuery searchQuery, SearchContext searchContext, String field,
451 boolean like)
452 throws Exception {
453
454 addSearchLocalizedTerm(searchQuery, searchContext, field, like);
455 }
456
457 protected void addSearchArrayQuery(
458 BooleanQuery searchQuery, SearchContext searchContext, String field)
459 throws Exception {
460
461 if (Validator.isNull(field)) {
462 return;
463 }
464
465 Object fieldValues = searchContext.getAttribute(field);
466
467 if (fieldValues == null) {
468 return;
469 }
470
471 BooleanQuery fieldQuery = null;
472
473 if (fieldValues instanceof int[]) {
474 int[] fieldValuesArray = (int[])fieldValues;
475
476 if (fieldValuesArray.length == 0) {
477 return;
478 }
479
480 fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
481
482 for (int fieldValue : fieldValuesArray) {
483 fieldQuery.addTerm(field, fieldValue);
484 }
485 }
486 else if (fieldValues instanceof Integer[]) {
487 Integer[] fieldValuesArray = (Integer[])fieldValues;
488
489 if (fieldValuesArray.length == 0) {
490 return;
491 }
492
493 fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
494
495 for (Integer fieldValue : fieldValuesArray) {
496 fieldQuery.addTerm(field, fieldValue);
497 }
498 }
499 else if (fieldValues instanceof long[]) {
500 long[] fieldValuesArray = (long[])fieldValues;
501
502 if (fieldValuesArray.length == 0) {
503 return;
504 }
505
506 fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
507
508 for (long fieldValue : fieldValuesArray) {
509 fieldQuery.addTerm(field, fieldValue);
510 }
511 }
512 else if (fieldValues instanceof Long[]) {
513 Long[] fieldValuesArray = (Long[])fieldValues;
514
515 if (fieldValuesArray.length == 0) {
516 return;
517 }
518
519 fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
520
521 for (Long fieldValue : fieldValuesArray) {
522 fieldQuery.addTerm(field, fieldValue);
523 }
524 }
525 else if (fieldValues instanceof short[]) {
526 short[] fieldValuesArray = (short[])fieldValues;
527
528 if (fieldValuesArray.length == 0) {
529 return;
530 }
531
532 fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
533
534 for (short fieldValue : fieldValuesArray) {
535 fieldQuery.addTerm(field, fieldValue);
536 }
537 }
538 else if (fieldValues instanceof Short[]) {
539 Short[] fieldValuesArray = (Short[])fieldValues;
540
541 if (fieldValuesArray.length == 0) {
542 return;
543 }
544
545 fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
546
547 for (Short fieldValue : fieldValuesArray) {
548 fieldQuery.addTerm(field, fieldValue);
549 }
550 }
551
552 if (fieldQuery != null) {
553 if (searchContext.isAndSearch()) {
554 searchQuery.add(fieldQuery, BooleanClauseOccur.MUST);
555 }
556 else {
557 searchQuery.add(fieldQuery, BooleanClauseOccur.SHOULD);
558 }
559 }
560 }
561
562 protected void addSearchAssetCategoryIds(
563 BooleanQuery contextQuery, SearchContext searchContext)
564 throws Exception {
565
566 MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
567
568 multiValueFacet.setFieldName(Field.ASSET_CATEGORY_IDS);
569 multiValueFacet.setStatic(true);
570
571 searchContext.addFacet(multiValueFacet);
572 }
573
574 protected void addSearchAssetCategoryTitles(
575 Document document, String field, List<AssetCategory> assetCategories) {
576
577 Map<Locale, List<String>> assetCategoryTitles =
578 new HashMap<Locale, List<String>>();
579
580 Locale defaultLocale = LocaleUtil.getDefault();
581
582 for (AssetCategory assetCategory : assetCategories) {
583 Map<Locale, String> titleMap = assetCategory.getTitleMap();
584
585 for (Map.Entry<Locale, String> entry : titleMap.entrySet()) {
586 Locale locale = entry.getKey();
587 String title = entry.getValue();
588
589 if (Validator.isNull(title)) {
590 continue;
591 }
592
593 List<String> titles = assetCategoryTitles.get(locale);
594
595 if (titles == null) {
596 titles = new ArrayList<String>();
597
598 assetCategoryTitles.put(locale, titles);
599 }
600
601 titles.add(title);
602 }
603 }
604
605 for (Map.Entry<Locale, List<String>> entry :
606 assetCategoryTitles.entrySet()) {
607
608 Locale locale = entry.getKey();
609 List<String> titles = entry.getValue();
610
611 String[] titlesArray = titles.toArray(new String[0]);
612
613 if (locale.equals(defaultLocale)) {
614 document.addKeyword(field, titlesArray);
615 }
616
617 document.addKeyword(
618 field.concat(StringPool.UNDERLINE).concat(locale.toString()),
619 titlesArray);
620 }
621 }
622
623 protected void addSearchAssetTagNames(
624 BooleanQuery contextQuery, SearchContext searchContext)
625 throws Exception {
626
627 MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
628
629 multiValueFacet.setFieldName(Field.ASSET_TAG_NAMES);
630 multiValueFacet.setStatic(true);
631
632 searchContext.addFacet(multiValueFacet);
633 }
634
635 protected void addSearchDDMStruture(
636 BooleanQuery searchQuery, SearchContext searchContext,
637 DDMStructure ddmStructure)
638 throws Exception {
639
640 Set<String> fieldNames = ddmStructure.getFieldNames();
641
642 for (String fieldName : fieldNames) {
643 String name = DDMIndexerUtil.encodeName(
644 ddmStructure.getStructureId(), fieldName);
645
646 addSearchTerm(searchQuery, searchContext, name, false);
647 }
648 }
649
650 protected void addSearchEntryClassNames(
651 BooleanQuery contextQuery, SearchContext searchContext)
652 throws Exception {
653
654 Facet facet = new AssetEntriesFacet(searchContext);
655
656 facet.setStatic(true);
657
658 searchContext.addFacet(facet);
659 }
660
661 protected void addSearchExpando(
662 BooleanQuery searchQuery, SearchContext searchContext,
663 String keywords)
664 throws Exception {
665
666 ExpandoBridge expandoBridge = ExpandoBridgeFactoryUtil.getExpandoBridge(
667 searchContext.getCompanyId(), getClassName(searchContext));
668
669 Set<String> attributeNames = SetUtil.fromEnumeration(
670 expandoBridge.getAttributeNames());
671
672 for (String attributeName : attributeNames) {
673 UnicodeProperties properties = expandoBridge.getAttributeProperties(
674 attributeName);
675
676 int indexType = GetterUtil.getInteger(
677 properties.getProperty(ExpandoColumnConstants.INDEX_TYPE));
678
679 if (indexType != ExpandoColumnConstants.INDEX_TYPE_NONE) {
680 String fieldName = ExpandoBridgeIndexerUtil.encodeFieldName(
681 attributeName);
682
683 if (Validator.isNotNull(keywords)) {
684 if (searchContext.isAndSearch()) {
685 searchQuery.addRequiredTerm(fieldName, keywords);
686 }
687 else {
688 searchQuery.addTerm(fieldName, keywords);
689 }
690 }
691 }
692 }
693 }
694
695 protected void addSearchGroupId(
696 BooleanQuery contextQuery, SearchContext searchContext)
697 throws Exception {
698
699 Facet facet = new ScopeFacet(searchContext);
700
701 facet.setStatic(true);
702
703 searchContext.addFacet(facet);
704 }
705
706 protected void addSearchKeywords(
707 BooleanQuery searchQuery, SearchContext searchContext)
708 throws Exception {
709
710 String keywords = searchContext.getKeywords();
711
712 if (Validator.isNull(keywords)) {
713 return;
714 }
715
716 searchQuery.addTerms(Field.KEYWORDS, keywords);
717
718 addSearchExpando(searchQuery, searchContext, keywords);
719 }
720
721 protected void addSearchLocalizedTerm(
722 BooleanQuery searchQuery, SearchContext searchContext, String field,
723 boolean like)
724 throws Exception {
725
726 addSearchTerm(searchQuery, searchContext, field, like);
727 addSearchTerm(
728 searchQuery, searchContext,
729 DocumentImpl.getLocalizedName(searchContext.getLocale(), field),
730 like);
731 }
732
733 protected void addSearchTerm(
734 BooleanQuery searchQuery, SearchContext searchContext, String field,
735 boolean like)
736 throws Exception {
737
738 if (Validator.isNull(field)) {
739 return;
740 }
741
742 String value = null;
743
744 Serializable serializable = searchContext.getAttribute(field);
745
746 if (serializable != null) {
747 Class<?> clazz = serializable.getClass();
748
749 if (clazz.isArray()) {
750 value = StringUtil.merge((Object[])serializable);
751 }
752 else {
753 value = GetterUtil.getString(serializable);
754 }
755 }
756 else {
757 value = GetterUtil.getString(serializable);
758 }
759
760 if (Validator.isNotNull(value) &&
761 (searchContext.getFacet(field) != null)) {
762
763 return;
764 }
765
766 if (Validator.isNull(value)) {
767 value = searchContext.getKeywords();
768 }
769
770 if (Validator.isNull(value)) {
771 return;
772 }
773
774 if (searchContext.isAndSearch()) {
775 searchQuery.addRequiredTerm(field, value, like);
776 }
777 else {
778 searchQuery.addTerm(field, value, like);
779 }
780 }
781
782 protected void addStagingGroupKeyword(Document document, long groupId)
783 throws Exception {
784
785 if (!isStagingAware()) {
786 return;
787 }
788
789 boolean stagingGroup = false;
790
791 Group group = GroupLocalServiceUtil.getGroup(groupId);
792
793 if (group.isLayout()) {
794 group = GroupLocalServiceUtil.getGroup(group.getParentGroupId());
795 }
796
797 if (group.isStagingGroup()) {
798 stagingGroup = true;
799 }
800
801 document.addKeyword(Field.STAGING_GROUP, stagingGroup);
802 }
803
804 protected void addTrashFields(
805 Document document, String className, long classPK, Date removedDate,
806 String removedByUserName, String type)
807 throws SystemException {
808
809 TrashEntry trashEntry = TrashEntryLocalServiceUtil.fetchEntry(
810 className, classPK);
811
812 if (removedDate == null) {
813 if (trashEntry != null) {
814 removedDate = trashEntry.getCreateDate();
815 }
816 else {
817 removedDate = new Date();
818 }
819 }
820
821 document.addDate(Field.REMOVED_DATE, removedDate);
822
823 if (removedByUserName == null) {
824 if (trashEntry != null) {
825 removedByUserName = trashEntry.getUserName();
826 }
827 else {
828 ServiceContext serviceContext =
829 ServiceContextThreadLocal.getServiceContext();
830
831 if (serviceContext != null) {
832 try {
833 User user = UserLocalServiceUtil.getUser(
834 serviceContext.getUserId());
835
836 removedByUserName = user.getFullName();
837 }
838 catch (PortalException pe) {
839 }
840 }
841 }
842 }
843
844 if (Validator.isNotNull(removedByUserName)) {
845 document.addKeyword(
846 Field.REMOVED_BY_USER_NAME, removedByUserName, true);
847 }
848
849 if (type == null) {
850 if (trashEntry != null) {
851 TrashHandler trashHandler =
852 TrashHandlerRegistryUtil.getTrashHandler(
853 trashEntry.getClassName());
854
855 try {
856 TrashRenderer trashRenderer = trashHandler.getTrashRenderer(
857 trashEntry.getClassPK());
858
859 type = trashRenderer.getType();
860 }
861 catch (PortalException pe) {
862 }
863 }
864 }
865
866 if (Validator.isNotNull(type)) {
867 document.addKeyword(Field.TYPE, type, true);
868 }
869 }
870
871 protected BooleanQuery createFullQuery(
872 BooleanQuery contextQuery, SearchContext searchContext)
873 throws Exception {
874
875 BooleanQuery searchQuery = BooleanQueryFactoryUtil.create(
876 searchContext);
877
878 addSearchKeywords(searchQuery, searchContext);
879 postProcessSearchQuery(searchQuery, searchContext);
880
881 for (IndexerPostProcessor indexerPostProcessor :
882 _indexerPostProcessors) {
883
884 indexerPostProcessor.postProcessSearchQuery(
885 searchQuery, searchContext);
886 }
887
888 Map<String, Facet> facets = searchContext.getFacets();
889
890 for (Facet facet : facets.values()) {
891 BooleanClause facetClause = facet.getFacetClause();
892
893 if (facetClause != null) {
894 contextQuery.add(
895 facetClause.getQuery(),
896 facetClause.getBooleanClauseOccur());
897 }
898 }
899
900 BooleanQuery fullQuery = BooleanQueryFactoryUtil.create(searchContext);
901
902 fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
903
904 if (searchQuery.hasClauses()) {
905 fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
906 }
907
908 BooleanClause[] booleanClauses = searchContext.getBooleanClauses();
909
910 if (booleanClauses != null) {
911 for (BooleanClause booleanClause : booleanClauses) {
912 fullQuery.add(
913 booleanClause.getQuery(),
914 booleanClause.getBooleanClauseOccur());
915 }
916 }
917
918 postProcessFullQuery(fullQuery, searchContext);
919
920 for (IndexerPostProcessor indexerPostProcessor :
921 _indexerPostProcessors) {
922
923 indexerPostProcessor.postProcessFullQuery(fullQuery, searchContext);
924 }
925
926 return fullQuery;
927 }
928
929 protected Summary createLocalizedSummary(Document document, Locale locale) {
930 return createLocalizedSummary(
931 document, locale, Field.TITLE, Field.CONTENT);
932 }
933
934 protected Summary createLocalizedSummary(
935 Document document, Locale locale, String titleField,
936 String contentField) {
937
938 Locale snippetLocale = getSnippetLocale(document, locale);
939
940 String prefix = Field.SNIPPET + StringPool.UNDERLINE;
941
942 String title = document.get(
943 snippetLocale, prefix + titleField, titleField);
944
945 String content = document.get(
946 snippetLocale, prefix + contentField, contentField);
947
948 return new Summary(snippetLocale, title, content, null);
949 }
950
951 protected Summary createSummary(Document document) {
952 return createSummary(document, Field.TITLE, Field.CONTENT);
953 }
954
955 protected Summary createSummary(
956 Document document, String titleField, String contentField) {
957
958 String prefix = Field.SNIPPET + StringPool.UNDERLINE;
959
960 String title = document.get(prefix + titleField, titleField);
961 String content = document.get(prefix + contentField, contentField);
962
963 return new Summary(title, content, null);
964 }
965
966 protected void deleteDocument(long companyId, long field1)
967 throws Exception {
968
969 deleteDocument(companyId, String.valueOf(field1));
970 }
971
972 protected void deleteDocument(long companyId, long field1, String field2)
973 throws Exception {
974
975 deleteDocument(companyId, String.valueOf(field1), field2);
976 }
977
978 protected void deleteDocument(long companyId, String field1)
979 throws Exception {
980
981 Document document = new DocumentImpl();
982
983 document.addUID(getPortletId(), field1);
984
985 SearchEngineUtil.deleteDocument(
986 getSearchEngineId(), companyId, document.get(Field.UID));
987 }
988
989 protected void deleteDocument(long companyId, String field1, String field2)
990 throws Exception {
991
992 Document document = new DocumentImpl();
993
994 document.addUID(getPortletId(), field1, field2);
995
996 SearchEngineUtil.deleteDocument(
997 getSearchEngineId(), companyId, document.get(Field.UID));
998 }
999
1000 protected abstract void doDelete(Object obj) throws Exception;
1001
1002 protected abstract Document doGetDocument(Object obj) throws Exception;
1003
1004 protected String doGetSortField(String orderByCol) {
1005 return orderByCol;
1006 }
1007
1008 protected abstract Summary doGetSummary(
1009 Document document, Locale locale, String snippet,
1010 PortletURL portletURL)
1011 throws Exception;
1012
1013 protected abstract void doReindex(Object obj) throws Exception;
1014
1015 protected abstract void doReindex(String className, long classPK)
1016 throws Exception;
1017
1018 protected abstract void doReindex(String[] ids) throws Exception;
1019
1020 protected Hits filterSearch(
1021 Hits hits, PermissionChecker permissionChecker,
1022 SearchContext searchContext) {
1023
1024 List<Document> docs = new ArrayList<Document>();
1025 List<Float> scores = new ArrayList<Float>();
1026
1027 int start = searchContext.getStart();
1028 int end = searchContext.getEnd();
1029
1030 String paginationType = GetterUtil.getString(
1031 searchContext.getAttribute("paginationType"), "more");
1032
1033 boolean hasMore = false;
1034
1035 Document[] documents = hits.getDocs();
1036
1037 for (int i = 0; i < documents.length; i++) {
1038 try {
1039 Document document = documents[i];
1040
1041 String entryClassName = document.get(Field.ENTRY_CLASS_NAME);
1042 long entryClassPK = GetterUtil.getLong(
1043 document.get(Field.ENTRY_CLASS_PK));
1044
1045 Indexer indexer = IndexerRegistryUtil.getIndexer(
1046 entryClassName);
1047
1048 if ((indexer.isFilterSearch() &&
1049 indexer.hasPermission(
1050 permissionChecker, entryClassName, entryClassPK,
1051 ActionKeys.VIEW)) ||
1052 !indexer.isFilterSearch() ||
1053 !indexer.isPermissionAware()) {
1054
1055 docs.add(document);
1056 scores.add(hits.score(i));
1057 }
1058 }
1059 catch (Exception e) {
1060 }
1061
1062 if (paginationType.equals("more") && (docs.size() > end)) {
1063 hasMore = true;
1064
1065 break;
1066 }
1067 }
1068
1069 int length = docs.size();
1070
1071 if (hasMore) {
1072 length = length + (end - start);
1073 }
1074
1075 hits.setLength(length);
1076
1077 if ((start != QueryUtil.ALL_POS) && (end != QueryUtil.ALL_POS)) {
1078 if (end > length) {
1079 end = length;
1080 }
1081
1082 docs = docs.subList(start, end);
1083 }
1084
1085 hits.setDocs(docs.toArray(new Document[docs.size()]));
1086 hits.setScores(scores.toArray(new Float[docs.size()]));
1087
1088 hits.setSearchTime(
1089 (float)(System.currentTimeMillis() - hits.getStart()) /
1090 Time.SECOND);
1091
1092 return hits;
1093 }
1094
1095 protected Document getBaseModelDocument(
1096 String portletId, BaseModel<?> baseModel)
1097 throws SystemException {
1098
1099 return getBaseModelDocument(portletId, baseModel, baseModel);
1100 }
1101
1102 protected Document getBaseModelDocument(
1103 String portletId, BaseModel<?> baseModel,
1104 BaseModel<?> workflowedBaseModel)
1105 throws SystemException {
1106
1107 Document document = new DocumentImpl();
1108
1109 String className = baseModel.getModelClassName();
1110
1111 long classPK = 0;
1112 long resourcePrimKey = 0;
1113
1114 if (baseModel instanceof ResourcedModel) {
1115 ResourcedModel resourcedModel = (ResourcedModel)baseModel;
1116
1117 classPK = resourcedModel.getResourcePrimKey();
1118 resourcePrimKey = resourcedModel.getResourcePrimKey();
1119 }
1120 else {
1121 classPK = (Long)baseModel.getPrimaryKeyObj();
1122 }
1123
1124 document.addUID(portletId, classPK);
1125
1126 List<AssetCategory> assetCategories =
1127 AssetCategoryLocalServiceUtil.getCategories(className, classPK);
1128
1129 long[] assetCategoryIds = StringUtil.split(
1130 ListUtil.toString(
1131 assetCategories, AssetCategory.CATEGORY_ID_ACCESSOR),
1132 0L);
1133
1134 document.addKeyword(Field.ASSET_CATEGORY_IDS, assetCategoryIds);
1135
1136 addSearchAssetCategoryTitles(
1137 document, Field.ASSET_CATEGORY_TITLES, assetCategories);
1138
1139 String[] assetTagNames = AssetTagLocalServiceUtil.getTagNames(
1140 className, classPK);
1141
1142 document.addText(Field.ASSET_TAG_NAMES, assetTagNames);
1143
1144 document.addKeyword(Field.ENTRY_CLASS_NAME, className);
1145 document.addKeyword(Field.ENTRY_CLASS_PK, classPK);
1146 document.addKeyword(Field.PORTLET_ID, portletId);
1147
1148 if (resourcePrimKey > 0) {
1149 document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, resourcePrimKey);
1150 }
1151
1152 if (baseModel instanceof AttachedModel) {
1153 AttachedModel attachedModel = (AttachedModel)baseModel;
1154
1155 document.addKeyword(
1156 Field.CLASS_NAME_ID, attachedModel.getClassNameId());
1157 document.addKeyword(Field.CLASS_PK, attachedModel.getClassPK());
1158 }
1159
1160 if (baseModel instanceof AuditedModel) {
1161 AuditedModel auditedModel = (AuditedModel)baseModel;
1162
1163 document.addKeyword(Field.COMPANY_ID, auditedModel.getCompanyId());
1164 document.addDate(Field.CREATE_DATE, auditedModel.getCreateDate());
1165 document.addDate(
1166 Field.MODIFIED_DATE, auditedModel.getModifiedDate());
1167 document.addKeyword(Field.USER_ID, auditedModel.getUserId());
1168
1169 String userName = PortalUtil.getUserName(
1170 auditedModel.getUserId(), auditedModel.getUserName());
1171
1172 document.addKeyword(Field.USER_NAME, userName, true);
1173 }
1174
1175 GroupedModel groupedModel = null;
1176
1177 if (baseModel instanceof GroupedModel) {
1178 groupedModel = (GroupedModel)baseModel;
1179
1180 document.addKeyword(
1181 Field.GROUP_ID, getParentGroupId(groupedModel.getGroupId()));
1182 document.addKeyword(
1183 Field.SCOPE_GROUP_ID, groupedModel.getGroupId());
1184 }
1185
1186 if (workflowedBaseModel instanceof WorkflowedModel) {
1187 WorkflowedModel workflowedModel =
1188 (WorkflowedModel)workflowedBaseModel;
1189
1190 document.addKeyword(Field.STATUS, workflowedModel.getStatus());
1191
1192 if ((groupedModel != null) && workflowedModel.isInTrash()) {
1193 addTrashFields(document, className, classPK, null, null, null);
1194 }
1195 }
1196
1197 ExpandoBridgeIndexerUtil.addAttributes(
1198 document, baseModel.getExpandoBridge());
1199
1200 return document;
1201 }
1202
1203 protected String getClassName(SearchContext searchContext) {
1204 String[] classNames = getClassNames();
1205
1206 if (classNames.length != 1) {
1207 throw new UnsupportedOperationException(
1208 "Search method needs to be manually implemented for " +
1209 "indexers with more than one class name");
1210 }
1211
1212 return classNames[0];
1213 }
1214
1215 protected Set<String> getLocalizedCountryNames(Country country) {
1216 Set<String> countryNames = new HashSet<String>();
1217
1218 Locale[] locales = LanguageUtil.getAvailableLocales();
1219
1220 for (Locale locale : locales) {
1221 String countryName = country.getName(locale);
1222
1223 countryName = countryName.toLowerCase();
1224
1225 countryNames.add(countryName);
1226 }
1227
1228 return countryNames;
1229 }
1230
1231 protected long getParentGroupId(long groupId) {
1232 long parentGroupId = groupId;
1233
1234 try {
1235 Group group = GroupLocalServiceUtil.getGroup(groupId);
1236
1237 if (group.isLayout()) {
1238 parentGroupId = group.getParentGroupId();
1239 }
1240 }
1241 catch (Exception e) {
1242 }
1243
1244 return parentGroupId;
1245 }
1246
1247 protected abstract String getPortletId(SearchContext searchContext);
1248
1249 protected Locale getSnippetLocale(Document document, Locale locale) {
1250 String prefix = Field.SNIPPET + StringPool.UNDERLINE;
1251
1252 String localizedContentName =
1253 prefix + DocumentImpl.getLocalizedName(locale, Field.CONTENT);
1254 String localizedDescriptionName =
1255 prefix + DocumentImpl.getLocalizedName(locale, Field.DESCRIPTION);
1256 String localizedTitleName =
1257 prefix + DocumentImpl.getLocalizedName(locale, Field.TITLE);
1258
1259 if ((document.getField(localizedContentName) != null) ||
1260 (document.getField(localizedDescriptionName) != null) ||
1261 (document.getField(localizedTitleName) != null)) {
1262
1263 return locale;
1264 }
1265
1266 return null;
1267 }
1268
1269 protected void populateAddresses(
1270 Document document, List<Address> addresses, long regionId,
1271 long countryId)
1272 throws PortalException, SystemException {
1273
1274 List<String> cities = new ArrayList<String>();
1275
1276 List<String> countries = new ArrayList<String>();
1277
1278 if (countryId > 0) {
1279 try {
1280 Country country = CountryServiceUtil.getCountry(countryId);
1281
1282 countries.addAll(getLocalizedCountryNames(country));
1283 }
1284 catch (NoSuchCountryException nsce) {
1285 if (_log.isWarnEnabled()) {
1286 _log.warn(nsce.getMessage());
1287 }
1288 }
1289 }
1290
1291 List<String> regions = new ArrayList<String>();
1292
1293 if (regionId > 0) {
1294 try {
1295 Region region = RegionServiceUtil.getRegion(regionId);
1296
1297 regions.add(region.getName().toLowerCase());
1298 }
1299 catch (NoSuchRegionException nsre) {
1300 if (_log.isWarnEnabled()) {
1301 _log.warn(nsre.getMessage());
1302 }
1303 }
1304 }
1305
1306 List<String> streets = new ArrayList<String>();
1307 List<String> zips = new ArrayList<String>();
1308
1309 for (Address address : addresses) {
1310 cities.add(address.getCity().toLowerCase());
1311 countries.addAll(getLocalizedCountryNames(address.getCountry()));
1312 regions.add(address.getRegion().getName().toLowerCase());
1313 streets.add(address.getStreet1().toLowerCase());
1314 streets.add(address.getStreet2().toLowerCase());
1315 streets.add(address.getStreet3().toLowerCase());
1316 zips.add(address.getZip().toLowerCase());
1317 }
1318
1319 document.addText("city", cities.toArray(new String[cities.size()]));
1320 document.addText(
1321 "country", countries.toArray(new String[countries.size()]));
1322 document.addText("region", regions.toArray(new String[regions.size()]));
1323 document.addText("street", streets.toArray(new String[streets.size()]));
1324 document.addText("zip", zips.toArray(new String[zips.size()]));
1325 }
1326
1327 protected void postProcessFullQuery(
1328 BooleanQuery fullQuery, SearchContext searchContext)
1329 throws Exception {
1330 }
1331
1332 protected void setFilterSearch(boolean filterSearch) {
1333 _filterSearch = filterSearch;
1334 }
1335
1336 protected void setIndexerEnabled(boolean indexerEnabled) {
1337 _indexerEnabled = indexerEnabled;
1338 }
1339
1340 protected void setPermissionAware(boolean permissionAware) {
1341 _permissionAware = permissionAware;
1342 }
1343
1344 protected void setStagingAware(boolean stagingAware) {
1345 _stagingAware = stagingAware;
1346 }
1347
1348 private static Log _log = LogFactoryUtil.getLog(BaseIndexer.class);
1349
1350 private boolean _filterSearch;
1351 private boolean _indexerEnabled = true;
1352 private IndexerPostProcessor[] _indexerPostProcessors =
1353 new IndexerPostProcessor[0];
1354 private boolean _permissionAware;
1355 private String _searchEngineId;
1356 private boolean _stagingAware = true;
1357
1358 }