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