001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
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.lar.ExportImportThreadLocal;
026    import com.liferay.portal.kernel.log.Log;
027    import com.liferay.portal.kernel.log.LogFactoryUtil;
028    import com.liferay.portal.kernel.search.facet.AssetEntriesFacet;
029    import com.liferay.portal.kernel.search.facet.Facet;
030    import com.liferay.portal.kernel.search.facet.MultiValueFacet;
031    import com.liferay.portal.kernel.search.facet.ScopeFacet;
032    import com.liferay.portal.kernel.trash.TrashHandler;
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.HashUtil;
037    import com.liferay.portal.kernel.util.ListUtil;
038    import com.liferay.portal.kernel.util.LocaleUtil;
039    import com.liferay.portal.kernel.util.PropsKeys;
040    import com.liferay.portal.kernel.util.PropsUtil;
041    import com.liferay.portal.kernel.util.SetUtil;
042    import com.liferay.portal.kernel.util.StringPool;
043    import com.liferay.portal.kernel.util.StringUtil;
044    import com.liferay.portal.kernel.util.Time;
045    import com.liferay.portal.kernel.util.UnicodeProperties;
046    import com.liferay.portal.kernel.util.Validator;
047    import com.liferay.portal.kernel.workflow.WorkflowConstants;
048    import com.liferay.portal.model.Address;
049    import com.liferay.portal.model.AttachedModel;
050    import com.liferay.portal.model.AuditedModel;
051    import com.liferay.portal.model.BaseModel;
052    import com.liferay.portal.model.ClassedModel;
053    import com.liferay.portal.model.Country;
054    import com.liferay.portal.model.Group;
055    import com.liferay.portal.model.GroupedModel;
056    import com.liferay.portal.model.Region;
057    import com.liferay.portal.model.ResourcedModel;
058    import com.liferay.portal.model.TrashedModel;
059    import com.liferay.portal.model.User;
060    import com.liferay.portal.model.WorkflowedModel;
061    import com.liferay.portal.security.auth.CompanyThreadLocal;
062    import com.liferay.portal.security.permission.ActionKeys;
063    import com.liferay.portal.security.permission.PermissionChecker;
064    import com.liferay.portal.security.permission.PermissionThreadLocal;
065    import com.liferay.portal.service.CountryServiceUtil;
066    import com.liferay.portal.service.GroupLocalServiceUtil;
067    import com.liferay.portal.service.RegionServiceUtil;
068    import com.liferay.portal.service.ServiceContext;
069    import com.liferay.portal.service.ServiceContextThreadLocal;
070    import com.liferay.portal.service.UserLocalServiceUtil;
071    import com.liferay.portal.util.PortalUtil;
072    import com.liferay.portlet.asset.AssetRendererFactoryRegistryUtil;
073    import com.liferay.portlet.asset.model.AssetCategory;
074    import com.liferay.portlet.asset.model.AssetEntry;
075    import com.liferay.portlet.asset.model.AssetRendererFactory;
076    import com.liferay.portlet.asset.model.AssetTag;
077    import com.liferay.portlet.asset.service.AssetCategoryLocalServiceUtil;
078    import com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil;
079    import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
080    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
081    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
082    import com.liferay.portlet.dynamicdatamapping.util.DDMIndexerUtil;
083    import com.liferay.portlet.expando.model.ExpandoBridge;
084    import com.liferay.portlet.expando.model.ExpandoColumnConstants;
085    import com.liferay.portlet.expando.util.ExpandoBridgeFactoryUtil;
086    import com.liferay.portlet.expando.util.ExpandoBridgeIndexerUtil;
087    import com.liferay.portlet.messageboards.model.MBMessage;
088    import com.liferay.portlet.ratings.model.RatingsStats;
089    import com.liferay.portlet.ratings.service.RatingsStatsLocalServiceUtil;
090    import com.liferay.portlet.trash.model.TrashEntry;
091    import com.liferay.portlet.trash.service.TrashEntryLocalServiceUtil;
092    
093    import java.io.Serializable;
094    
095    import java.util.ArrayList;
096    import java.util.Date;
097    import java.util.HashMap;
098    import java.util.HashSet;
099    import java.util.List;
100    import java.util.Locale;
101    import java.util.Map;
102    import java.util.Set;
103    
104    import javax.portlet.PortletURL;
105    
106    /**
107     * @author Brian Wing Shun Chan
108     * @author Hugo Huijser
109     * @author Ryan Park
110     * @author Raymond Aug??
111     */
112    public abstract class BaseIndexer implements Indexer {
113    
114            /**
115             * @deprecated As of 7.0.0, with no direct replacement
116             */
117            public static final int INDEX_FILTER_SEARCH_LIMIT = GetterUtil.getInteger(
118                    PropsUtil.get(PropsKeys.INDEX_FILTER_SEARCH_LIMIT));
119    
120            @Override
121            public void addRelatedEntryFields(Document document, Object obj)
122                    throws Exception {
123            }
124    
125            @Override
126            public void delete(long companyId, String uid) throws SearchException {
127                    try {
128                            SearchEngineUtil.deleteDocument(
129                                    getSearchEngineId(), companyId, uid, _commitImmediately);
130                    }
131                    catch (SearchException se) {
132                            throw se;
133                    }
134                    catch (Exception e) {
135                            throw new SearchException(e);
136                    }
137            }
138    
139            @Override
140            public void delete(Object obj) throws SearchException {
141                    try {
142                            doDelete(obj);
143                    }
144                    catch (SearchException se) {
145                            throw se;
146                    }
147                    catch (Exception e) {
148                            throw new SearchException(e);
149                    }
150            }
151    
152            @Override
153            public boolean equals(Object object) {
154                    if (object == this) {
155                            return true;
156                    }
157    
158                    if (!(object instanceof Indexer)) {
159                            return false;
160                    }
161    
162                    Indexer indexer = (Indexer)object;
163    
164                    String[] classNames1 = getClassNames();
165                    String[] classNames2 = indexer.getClassNames();
166    
167                    if (classNames1.length != classNames2.length) {
168                            return false;
169                    }
170    
171                    for (int i = 0; i < classNames1.length; i++) {
172                            if (!Validator.equals(classNames1[i], classNames2[i])) {
173                                    return false;
174                            }
175                    }
176    
177                    return true;
178            }
179    
180            @Override
181            public Document getDocument(Object obj) throws SearchException {
182                    try {
183                            Document document = doGetDocument(obj);
184    
185                            for (IndexerPostProcessor indexerPostProcessor :
186                                            _indexerPostProcessors) {
187    
188                                    indexerPostProcessor.postProcessDocument(document, obj);
189                            }
190    
191                            if (document == null) {
192                                    return null;
193                            }
194    
195                            Map<String, Field> fields = document.getFields();
196    
197                            Field groupIdField = fields.get(Field.GROUP_ID);
198    
199                            if (groupIdField != null) {
200                                    long groupId = GetterUtil.getLong(groupIdField.getValue());
201    
202                                    addStagingGroupKeyword(document, groupId);
203                            }
204    
205                            return document;
206                    }
207                    catch (SearchException se) {
208                            throw se;
209                    }
210                    catch (Exception e) {
211                            throw new SearchException(e);
212                    }
213            }
214    
215            @Override
216            public BooleanQuery getFacetQuery(
217                            String className, SearchContext searchContext)
218                    throws Exception {
219    
220                    BooleanQuery facetQuery = BooleanQueryFactoryUtil.create(searchContext);
221    
222                    facetQuery.addExactTerm(Field.ENTRY_CLASS_NAME, className);
223    
224                    if (searchContext.getUserId() > 0) {
225                            SearchPermissionChecker searchPermissionChecker =
226                                    SearchEngineUtil.getSearchPermissionChecker();
227    
228                            facetQuery =
229                                    (BooleanQuery)searchPermissionChecker.getPermissionQuery(
230                                            searchContext.getCompanyId(), searchContext.getGroupIds(),
231                                            searchContext.getUserId(), className, facetQuery,
232                                            searchContext);
233                    }
234    
235                    return facetQuery;
236            }
237    
238            @Override
239            public BooleanQuery getFullQuery(SearchContext searchContext)
240                    throws SearchException {
241    
242                    try {
243                            searchContext.setSearchEngineId(getSearchEngineId());
244    
245                            String[] entryClassNames = getClassNames();
246    
247                            if (searchContext.isIncludeAttachments()) {
248                                    entryClassNames = ArrayUtil.append(
249                                            entryClassNames, DLFileEntry.class.getName());
250                            }
251    
252                            if (searchContext.isIncludeDiscussions()) {
253                                    entryClassNames = ArrayUtil.append(
254                                            entryClassNames, MBMessage.class.getName());
255    
256                                    searchContext.setAttribute("discussion", Boolean.TRUE);
257                            }
258    
259                            searchContext.setEntryClassNames(entryClassNames);
260    
261                            if (searchContext.isIncludeAttachments() ||
262                                    searchContext.isIncludeDiscussions()) {
263    
264                                    searchContext.setAttribute(
265                                            "relatedEntryClassNames", getClassNames());
266                            }
267    
268                            BooleanQuery contextQuery = BooleanQueryFactoryUtil.create(
269                                    searchContext);
270    
271                            addSearchAssetCategoryIds(contextQuery, searchContext);
272                            addSearchAssetTagNames(contextQuery, searchContext);
273                            addSearchEntryClassNames(contextQuery, searchContext);
274                            addSearchFolderId(contextQuery, searchContext);
275                            addSearchGroupId(contextQuery, searchContext);
276                            addSearchLayout(contextQuery, searchContext);
277                            addSearchUserId(contextQuery, searchContext);
278    
279                            BooleanQuery fullQuery = createFullQuery(
280                                    contextQuery, searchContext);
281    
282                            fullQuery.setQueryConfig(searchContext.getQueryConfig());
283    
284                            return fullQuery;
285                    }
286                    catch (SearchException se) {
287                            throw se;
288                    }
289                    catch (Exception e) {
290                            throw new SearchException(e);
291                    }
292            }
293    
294            @Override
295            public IndexerPostProcessor[] getIndexerPostProcessors() {
296                    return _indexerPostProcessors;
297            }
298    
299            @Override
300            public String getSearchEngineId() {
301                    if (_searchEngineId != null) {
302                            return _searchEngineId;
303                    }
304    
305                    Class<?> clazz = getClass();
306    
307                    String searchEngineId = GetterUtil.getString(
308                            PropsUtil.get(
309                                    PropsKeys.INDEX_SEARCH_ENGINE_ID, new Filter(clazz.getName())));
310    
311                    if (Validator.isNotNull(searchEngineId)) {
312                            SearchEngine searchEngine = SearchEngineUtil.getSearchEngine(
313                                    searchEngineId);
314    
315                            if (searchEngine != null) {
316                                    _searchEngineId = searchEngineId;
317                            }
318                    }
319    
320                    if (_searchEngineId == null) {
321                            _searchEngineId = SearchEngineUtil.getDefaultSearchEngineId();
322                    }
323    
324                    if (_log.isDebugEnabled()) {
325                            _log.debug(
326                                    "Search engine ID for " + clazz.getName() + " is " +
327                                            searchEngineId);
328                    }
329    
330                    return _searchEngineId;
331            }
332    
333            @Override
334            public String getSortField(String orderByCol) {
335                    String sortField = doGetSortField(orderByCol);
336    
337                    if (_document.isDocumentSortableTextField(sortField)) {
338                            return DocumentImpl.getSortableFieldName(sortField);
339                    }
340    
341                    return sortField;
342            }
343    
344            @Override
345            public String getSortField(String orderByCol, int sortType) {
346                    if ((sortType == Sort.DOUBLE_TYPE) || (sortType == Sort.FLOAT_TYPE) ||
347                            (sortType == Sort.INT_TYPE) || (sortType == Sort.LONG_TYPE)) {
348    
349                            return DocumentImpl.getSortableFieldName(orderByCol);
350                    }
351    
352                    return getSortField(orderByCol);
353            }
354    
355            @Override
356            public Summary getSummary(
357                            Document document, Locale locale, String snippet,
358                            PortletURL portletURL)
359                    throws SearchException {
360    
361                    try {
362                            Summary summary = doGetSummary(
363                                    document, locale, snippet, portletURL);
364    
365                            for (IndexerPostProcessor indexerPostProcessor :
366                                            _indexerPostProcessors) {
367    
368                                    indexerPostProcessor.postProcessSummary(
369                                            summary, document, locale, snippet, portletURL);
370                            }
371    
372                            return summary;
373                    }
374                    catch (SearchException se) {
375                            throw se;
376                    }
377                    catch (Exception e) {
378                            throw new SearchException(e);
379                    }
380            }
381    
382            @Override
383            public int hashCode() {
384                    String[] classNames = getClassNames();
385    
386                    int hash = 0;
387    
388                    for (String className : classNames) {
389                            hash = HashUtil.hash(hash, className);
390                    }
391    
392                    return hash;
393            }
394    
395            @Override
396            public boolean hasPermission(
397                            PermissionChecker permissionChecker, String entryClassName,
398                            long entryClassPK, String actionId)
399                    throws Exception {
400    
401                    return true;
402            }
403    
404            public boolean isCommitImmediately() {
405                    return _commitImmediately;
406            }
407    
408            @Override
409            public boolean isFilterSearch() {
410                    return _filterSearch;
411            }
412    
413            public boolean isIndexerEnabled() {
414                    return _indexerEnabled;
415            }
416    
417            @Override
418            public boolean isPermissionAware() {
419                    return _permissionAware;
420            }
421    
422            @Override
423            public boolean isStagingAware() {
424                    return _stagingAware;
425            }
426    
427            @Override
428            public boolean isVisible(long classPK, int status) throws Exception {
429                    return true;
430            }
431    
432            @Override
433            public boolean isVisibleRelatedEntry(long classPK, int status)
434                    throws Exception {
435    
436                    return true;
437            }
438    
439            @Override
440            public void postProcessContextQuery(
441                            BooleanQuery contextQuery, SearchContext searchContext)
442                    throws Exception {
443            }
444    
445            @Override
446            public void postProcessSearchQuery(
447                            BooleanQuery searchQuery, SearchContext searchContext)
448                    throws Exception {
449    
450                    String keywords = searchContext.getKeywords();
451    
452                    if (Validator.isNull(keywords)) {
453                            addSearchTerm(searchQuery, searchContext, Field.DESCRIPTION, false);
454                            addSearchTerm(searchQuery, searchContext, Field.TITLE, false);
455                            addSearchTerm(searchQuery, searchContext, Field.USER_NAME, false);
456                    }
457            }
458    
459            @Override
460            public void registerIndexerPostProcessor(
461                    IndexerPostProcessor indexerPostProcessor) {
462    
463                    List<IndexerPostProcessor> indexerPostProcessorsList =
464                            ListUtil.fromArray(_indexerPostProcessors);
465    
466                    indexerPostProcessorsList.add(indexerPostProcessor);
467    
468                    _indexerPostProcessors = indexerPostProcessorsList.toArray(
469                            new IndexerPostProcessor[indexerPostProcessorsList.size()]);
470            }
471    
472            @Override
473            public void reindex(Object obj) throws SearchException {
474                    try {
475                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled() ||
476                                    ExportImportThreadLocal.isImportInProcess()) {
477    
478                                    return;
479                            }
480    
481                            if (obj instanceof List<?>) {
482                                    List<?> list = (List<?>)obj;
483    
484                                    for (Object element : list) {
485                                            doReindex(element);
486                                    }
487                            }
488                            else {
489                                    doReindex(obj);
490                            }
491                    }
492                    catch (SearchException se) {
493                            throw se;
494                    }
495                    catch (Exception e) {
496                            throw new SearchException(e);
497                    }
498            }
499    
500            @Override
501            public void reindex(String className, long classPK) throws SearchException {
502                    try {
503                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled() ||
504                                    ExportImportThreadLocal.isImportInProcess() || (classPK <= 0)) {
505    
506                                    return;
507                            }
508    
509                            doReindex(className, classPK);
510                    }
511                    catch (NoSuchModelException nsme) {
512                            if (_log.isWarnEnabled()) {
513                                    _log.warn("Unable to index " + className + " " + classPK, nsme);
514                            }
515                    }
516                    catch (SearchException se) {
517                            throw se;
518                    }
519                    catch (Exception e) {
520                            throw new SearchException(e);
521                    }
522            }
523    
524            @Override
525            public void reindex(String[] ids) throws SearchException {
526                    long companyThreadLocalCompanyId = CompanyThreadLocal.getCompanyId();
527    
528                    try {
529                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled() ||
530                                    ExportImportThreadLocal.isImportInProcess()) {
531    
532                                    return;
533                            }
534    
535                            if (ids.length > 0) {
536                                    long companyId = GetterUtil.getLong(ids[0]);
537    
538                                    CompanyThreadLocal.setCompanyId(companyId);
539                            }
540    
541                            doReindex(ids);
542                    }
543                    catch (SearchException se) {
544                            throw se;
545                    }
546                    catch (Exception e) {
547                            throw new SearchException(e);
548                    }
549                    finally {
550                            CompanyThreadLocal.setCompanyId(companyThreadLocalCompanyId);
551                    }
552            }
553    
554            @Override
555            public void reindexDDMStructures(List<Long> ddmStructureIds)
556                    throws SearchException {
557    
558                    try {
559                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled() ||
560                                    ExportImportThreadLocal.isImportInProcess()) {
561    
562                                    return;
563                            }
564    
565                            doReindexDDMStructures(ddmStructureIds);
566                    }
567                    catch (SearchException se) {
568                            throw se;
569                    }
570                    catch (Exception e) {
571                            throw new SearchException(e);
572                    }
573            }
574    
575            @Override
576            public Hits search(SearchContext searchContext) throws SearchException {
577                    try {
578                            Hits hits = null;
579    
580                            PermissionChecker permissionChecker =
581                                    PermissionThreadLocal.getPermissionChecker();
582    
583                            if ((permissionChecker != null) &&
584                                    isUseSearchResultPermissionFilter(searchContext)) {
585    
586                                    if (searchContext.getUserId() == 0) {
587                                            searchContext.setUserId(permissionChecker.getUserId());
588                                    }
589    
590                                    SearchResultPermissionFilter searchResultPermissionFilter =
591                                            new DefaultSearchResultPermissionFilter(
592                                                    this, permissionChecker);
593    
594                                    hits = searchResultPermissionFilter.search(searchContext);
595                            }
596                            else {
597                                    hits = doSearch(searchContext);
598                            }
599    
600                            processHits(searchContext, hits);
601    
602                            return hits;
603                    }
604                    catch (SearchException se) {
605                            throw se;
606                    }
607                    catch (Exception e) {
608                            throw new SearchException(e);
609                    }
610            }
611    
612            public void setCommitImmediately(boolean commitImmediately) {
613                    _commitImmediately = commitImmediately;
614            }
615    
616            @Override
617            public void unregisterIndexerPostProcessor(
618                    IndexerPostProcessor indexerPostProcessor) {
619    
620                    List<IndexerPostProcessor> indexerPostProcessorsList =
621                            ListUtil.fromArray(_indexerPostProcessors);
622    
623                    indexerPostProcessorsList.remove(indexerPostProcessor);
624    
625                    _indexerPostProcessors = indexerPostProcessorsList.toArray(
626                            new IndexerPostProcessor[indexerPostProcessorsList.size()]);
627            }
628    
629            protected void addAssetFields(
630                            Document document, String className, long classPK)
631                    throws SystemException {
632    
633                    AssetRendererFactory assetRendererFactory =
634                            AssetRendererFactoryRegistryUtil.getAssetRendererFactoryByClassName(
635                                    className);
636    
637                    if ((assetRendererFactory == null) ||
638                            !assetRendererFactory.isSelectable()) {
639    
640                            return;
641                    }
642    
643                    AssetEntry assetEntry = AssetEntryLocalServiceUtil.fetchEntry(
644                            className, classPK);
645    
646                    if (assetEntry == null) {
647                            return;
648                    }
649    
650                    if (!document.hasField(Field.CREATE_DATE)) {
651                            document.addDate(Field.CREATE_DATE, assetEntry.getCreateDate());
652                    }
653    
654                    if (assetEntry.getExpirationDate() != null) {
655                            document.addDate(
656                                    Field.EXPIRATION_DATE, assetEntry.getExpirationDate());
657                    }
658                    else {
659                            document.addDate(Field.EXPIRATION_DATE, new Date(Long.MAX_VALUE));
660                    }
661    
662                    if (!document.hasField(Field.MODIFIED_DATE)) {
663                            document.addDate(Field.MODIFIED_DATE, assetEntry.getModifiedDate());
664                    }
665    
666                    document.addNumber(Field.PRIORITY, assetEntry.getPriority());
667    
668                    if (assetEntry.getPublishDate() != null) {
669                            document.addDate(Field.PUBLISH_DATE, assetEntry.getPublishDate());
670                    }
671                    else {
672                            document.addDate(Field.PUBLISH_DATE, new Date(0));
673                    }
674    
675                    RatingsStats ratingsStats = RatingsStatsLocalServiceUtil.fetchStats(
676                            className, classPK);
677    
678                    if (ratingsStats != null) {
679                            document.addNumber(Field.RATINGS, ratingsStats.getAverageScore());
680                    }
681                    else {
682                            document.addNumber(Field.RATINGS, 0.0f);
683                    }
684    
685                    document.addNumber(Field.VIEW_COUNT, assetEntry.getViewCount());
686    
687                    document.addLocalizedKeyword(
688                            "localized_title",
689                            populateMap(assetEntry, assetEntry.getTitleMap()), true, true);
690                    document.addKeyword("visible", assetEntry.isVisible());
691            }
692    
693            /**
694             * @deprecated As of 6.2.0, replaced by {@link
695             *             #addSearchLocalizedTerm(BooleanQuery, SearchContext, String,
696             *             boolean)}
697             */
698            protected void addLocalizedSearchTerm(
699                            BooleanQuery searchQuery, SearchContext searchContext, String field,
700                            boolean like)
701                    throws Exception {
702    
703                    addSearchLocalizedTerm(searchQuery, searchContext, field, like);
704            }
705    
706            protected void addRelatedClassNames(
707                            BooleanQuery contextQuery, SearchContext searchContext)
708                    throws Exception {
709    
710                    searchContext.setAttribute("relatedClassName", Boolean.TRUE);
711    
712                    String[] relatedEntryClassNames = (String[])searchContext.getAttribute(
713                            "relatedEntryClassNames");
714    
715                    if (ArrayUtil.isEmpty(relatedEntryClassNames)) {
716                            return;
717                    }
718    
719                    BooleanQuery relatedQueries = BooleanQueryFactoryUtil.create(
720                            searchContext);
721    
722                    for (String relatedEntryClassName : relatedEntryClassNames) {
723                            Indexer indexer = IndexerRegistryUtil.getIndexer(
724                                    relatedEntryClassName);
725    
726                            if (indexer == null) {
727                                    continue;
728                            }
729    
730                            BooleanQuery relatedQuery = BooleanQueryFactoryUtil.create(
731                                    searchContext);
732    
733                            indexer.postProcessContextQuery(relatedQuery, searchContext);
734    
735                            relatedQuery.addRequiredTerm(
736                                    Field.CLASS_NAME_ID,
737                                    PortalUtil.getClassNameId(relatedEntryClassName));
738    
739                            relatedQueries.add(relatedQuery, BooleanClauseOccur.SHOULD);
740                    }
741    
742                    contextQuery.add(relatedQueries, BooleanClauseOccur.MUST);
743    
744                    searchContext.setAttribute("relatedClassName", Boolean.FALSE);
745            }
746    
747            protected void addSearchArrayQuery(
748                            BooleanQuery searchQuery, SearchContext searchContext, String field)
749                    throws Exception {
750    
751                    if (Validator.isNull(field)) {
752                            return;
753                    }
754    
755                    Object fieldValues = searchContext.getAttribute(field);
756    
757                    if (fieldValues == null) {
758                            return;
759                    }
760    
761                    BooleanQuery fieldQuery = null;
762    
763                    if (fieldValues instanceof int[]) {
764                            int[] fieldValuesArray = (int[])fieldValues;
765    
766                            if (fieldValuesArray.length == 0) {
767                                    return;
768                            }
769    
770                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
771    
772                            for (int fieldValue : fieldValuesArray) {
773                                    fieldQuery.addTerm(field, fieldValue);
774                            }
775                    }
776                    else if (fieldValues instanceof Integer[]) {
777                            Integer[] fieldValuesArray = (Integer[])fieldValues;
778    
779                            if (fieldValuesArray.length == 0) {
780                                    return;
781                            }
782    
783                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
784    
785                            for (Integer fieldValue : fieldValuesArray) {
786                                    fieldQuery.addTerm(field, fieldValue);
787                            }
788                    }
789                    else if (fieldValues instanceof long[]) {
790                            long[] fieldValuesArray = (long[])fieldValues;
791    
792                            if (fieldValuesArray.length == 0) {
793                                    return;
794                            }
795    
796                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
797    
798                            for (long fieldValue : fieldValuesArray) {
799                                    fieldQuery.addTerm(field, fieldValue);
800                            }
801                    }
802                    else if (fieldValues instanceof Long[]) {
803                            Long[] fieldValuesArray = (Long[])fieldValues;
804    
805                            if (fieldValuesArray.length == 0) {
806                                    return;
807                            }
808    
809                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
810    
811                            for (Long fieldValue : fieldValuesArray) {
812                                    fieldQuery.addTerm(field, fieldValue);
813                            }
814                    }
815                    else if (fieldValues instanceof short[]) {
816                            short[] fieldValuesArray = (short[])fieldValues;
817    
818                            if (fieldValuesArray.length == 0) {
819                                    return;
820                            }
821    
822                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
823    
824                            for (short fieldValue : fieldValuesArray) {
825                                    fieldQuery.addTerm(field, fieldValue);
826                            }
827                    }
828                    else if (fieldValues instanceof Short[]) {
829                            Short[] fieldValuesArray = (Short[])fieldValues;
830    
831                            if (fieldValuesArray.length == 0) {
832                                    return;
833                            }
834    
835                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
836    
837                            for (Short fieldValue : fieldValuesArray) {
838                                    fieldQuery.addTerm(field, fieldValue);
839                            }
840                    }
841    
842                    if (fieldQuery != null) {
843                            if (searchContext.isAndSearch()) {
844                                    searchQuery.add(fieldQuery, BooleanClauseOccur.MUST);
845                            }
846                            else {
847                                    searchQuery.add(fieldQuery, BooleanClauseOccur.SHOULD);
848                            }
849                    }
850            }
851    
852            protected void addSearchAssetCategoryIds(
853                            BooleanQuery contextQuery, SearchContext searchContext)
854                    throws Exception {
855    
856                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
857    
858                    multiValueFacet.setFieldName(Field.ASSET_CATEGORY_IDS);
859                    multiValueFacet.setStatic(true);
860                    multiValueFacet.setValues(searchContext.getAssetCategoryIds());
861    
862                    searchContext.addFacet(multiValueFacet);
863            }
864    
865            protected void addSearchAssetCategoryTitles(
866                    Document document, String field, List<AssetCategory> assetCategories) {
867    
868                    Map<Locale, List<String>> assetCategoryTitles =
869                            new HashMap<Locale, List<String>>();
870    
871                    Locale defaultLocale = LocaleUtil.getDefault();
872    
873                    for (AssetCategory assetCategory : assetCategories) {
874                            Map<Locale, String> titleMap = assetCategory.getTitleMap();
875    
876                            for (Map.Entry<Locale, String> entry : titleMap.entrySet()) {
877                                    Locale locale = entry.getKey();
878                                    String title = entry.getValue();
879    
880                                    if (Validator.isNull(title)) {
881                                            continue;
882                                    }
883    
884                                    List<String> titles = assetCategoryTitles.get(locale);
885    
886                                    if (titles == null) {
887                                            titles = new ArrayList<String>();
888    
889                                            assetCategoryTitles.put(locale, titles);
890                                    }
891    
892                                    titles.add(StringUtil.toLowerCase(title));
893                            }
894                    }
895    
896                    for (Map.Entry<Locale, List<String>> entry :
897                                    assetCategoryTitles.entrySet()) {
898    
899                            Locale locale = entry.getKey();
900                            List<String> titles = entry.getValue();
901    
902                            String[] titlesArray = titles.toArray(new String[0]);
903    
904                            if (locale.equals(defaultLocale)) {
905                                    document.addText(field, titlesArray);
906                            }
907    
908                            document.addText(
909                                    field.concat(StringPool.UNDERLINE).concat(locale.toString()),
910                                    titlesArray);
911                    }
912            }
913    
914            protected void addSearchAssetTagNames(
915                            BooleanQuery contextQuery, SearchContext searchContext)
916                    throws Exception {
917    
918                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
919    
920                    multiValueFacet.setFieldName(Field.ASSET_TAG_NAMES);
921                    multiValueFacet.setStatic(true);
922                    multiValueFacet.setValues(searchContext.getAssetTagNames());
923    
924                    searchContext.addFacet(multiValueFacet);
925            }
926    
927            protected void addSearchClassTypeIds(
928                            BooleanQuery contextQuery, SearchContext searchContext)
929                    throws Exception {
930    
931                    long[] classTypeIds = searchContext.getClassTypeIds();
932    
933                    if ((classTypeIds == null) || (classTypeIds.length <= 0)) {
934                            return;
935                    }
936    
937                    BooleanQuery classTypeIdsQuery = BooleanQueryFactoryUtil.create(
938                            searchContext);
939    
940                    for (long classTypeId : classTypeIds) {
941                            classTypeIdsQuery.addTerm(Field.CLASS_TYPE_ID, classTypeId);
942                    }
943    
944                    contextQuery.add(classTypeIdsQuery, BooleanClauseOccur.MUST);
945            }
946    
947            protected void addSearchDDMStruture(
948                            BooleanQuery searchQuery, SearchContext searchContext,
949                            DDMStructure ddmStructure)
950                    throws Exception {
951    
952                    Set<String> fieldNames = ddmStructure.getFieldNames();
953    
954                    for (String fieldName : fieldNames) {
955                            String indexType = ddmStructure.getFieldProperty(
956                                    fieldName, "indexType");
957    
958                            if (Validator.isNull(indexType)) {
959                                    continue;
960                            }
961    
962                            String name = DDMIndexerUtil.encodeName(
963                                    ddmStructure.getStructureId(), fieldName,
964                                    searchContext.getLocale());
965    
966                            boolean like = false;
967    
968                            if (indexType.equals("text")) {
969                                    like = true;
970                            }
971    
972                            addSearchTerm(searchQuery, searchContext, name, like);
973                    }
974            }
975    
976            protected void addSearchEntryClassNames(
977                            BooleanQuery contextQuery, SearchContext searchContext)
978                    throws Exception {
979    
980                    Facet facet = new AssetEntriesFacet(searchContext);
981    
982                    facet.setStatic(true);
983    
984                    searchContext.addFacet(facet);
985            }
986    
987            protected void addSearchExpando(
988                            BooleanQuery searchQuery, SearchContext searchContext,
989                            String keywords)
990                    throws Exception {
991    
992                    ExpandoBridge expandoBridge = ExpandoBridgeFactoryUtil.getExpandoBridge(
993                            searchContext.getCompanyId(), getClassName(searchContext));
994    
995                    Set<String> attributeNames = SetUtil.fromEnumeration(
996                            expandoBridge.getAttributeNames());
997    
998                    for (String attributeName : attributeNames) {
999                            UnicodeProperties properties = expandoBridge.getAttributeProperties(
1000                                    attributeName);
1001    
1002                            int indexType = GetterUtil.getInteger(
1003                                    properties.getProperty(ExpandoColumnConstants.INDEX_TYPE));
1004    
1005                            if (indexType != ExpandoColumnConstants.INDEX_TYPE_NONE) {
1006                                    String fieldName = ExpandoBridgeIndexerUtil.encodeFieldName(
1007                                            attributeName);
1008    
1009                                    if (Validator.isNotNull(keywords)) {
1010                                            if (searchContext.isAndSearch()) {
1011                                                    searchQuery.addRequiredTerm(fieldName, keywords);
1012                                            }
1013                                            else {
1014                                                    searchQuery.addTerm(fieldName, keywords);
1015                                            }
1016                                    }
1017                            }
1018                    }
1019            }
1020    
1021            protected void addSearchFolderId(
1022                            BooleanQuery contextQuery, SearchContext searchContext)
1023                    throws Exception {
1024    
1025                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
1026    
1027                    multiValueFacet.setFieldName(Field.TREE_PATH);
1028                    multiValueFacet.setStatic(true);
1029                    multiValueFacet.setValues(searchContext.getFolderIds());
1030    
1031                    searchContext.addFacet(multiValueFacet);
1032            }
1033    
1034            protected void addSearchGroupId(
1035                            BooleanQuery contextQuery, SearchContext searchContext)
1036                    throws Exception {
1037    
1038                    Facet facet = new ScopeFacet(searchContext);
1039    
1040                    facet.setStatic(true);
1041    
1042                    searchContext.addFacet(facet);
1043            }
1044    
1045            protected void addSearchKeywords(
1046                            BooleanQuery searchQuery, SearchContext searchContext)
1047                    throws Exception {
1048    
1049                    String keywords = searchContext.getKeywords();
1050    
1051                    if (Validator.isNull(keywords)) {
1052                            return;
1053                    }
1054    
1055                    searchQuery.addTerms(Field.KEYWORDS, keywords, searchContext.isLike());
1056    
1057                    addSearchExpando(searchQuery, searchContext, keywords);
1058    
1059                    addSearchLocalizedTerm(
1060                            searchQuery, searchContext, Field.ASSET_CATEGORY_TITLES,
1061                            searchContext.isLike());
1062            }
1063    
1064            protected void addSearchLayout(
1065                            BooleanQuery contextQuery, SearchContext searchContext)
1066                    throws Exception {
1067    
1068                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
1069    
1070                    multiValueFacet.setFieldName(Field.LAYOUT_UUID);
1071                    multiValueFacet.setStatic(true);
1072    
1073                    searchContext.addFacet(multiValueFacet);
1074            }
1075    
1076            protected void addSearchLocalizedTerm(
1077                            BooleanQuery searchQuery, SearchContext searchContext, String field,
1078                            boolean like)
1079                    throws Exception {
1080    
1081                    addSearchTerm(searchQuery, searchContext, field, like);
1082                    addSearchTerm(
1083                            searchQuery, searchContext,
1084                            DocumentImpl.getLocalizedName(searchContext.getLocale(), field),
1085                            like);
1086            }
1087    
1088            protected void addSearchTerm(
1089                            BooleanQuery searchQuery, SearchContext searchContext, String field,
1090                            boolean like)
1091                    throws Exception {
1092    
1093                    if (Validator.isNull(field)) {
1094                            return;
1095                    }
1096    
1097                    String value = null;
1098    
1099                    Serializable serializable = searchContext.getAttribute(field);
1100    
1101                    if (serializable != null) {
1102                            Class<?> clazz = serializable.getClass();
1103    
1104                            if (clazz.isArray()) {
1105                                    value = StringUtil.merge((Object[])serializable);
1106                            }
1107                            else {
1108                                    value = GetterUtil.getString(serializable);
1109                            }
1110                    }
1111                    else {
1112                            value = GetterUtil.getString(serializable);
1113                    }
1114    
1115                    if (Validator.isNotNull(value) &&
1116                            (searchContext.getFacet(field) != null)) {
1117    
1118                            return;
1119                    }
1120    
1121                    if (Validator.isNull(value)) {
1122                            value = searchContext.getKeywords();
1123                    }
1124    
1125                    if (Validator.isNull(value)) {
1126                            return;
1127                    }
1128    
1129                    if (searchContext.isAndSearch()) {
1130                            searchQuery.addRequiredTerm(field, value, like);
1131                    }
1132                    else {
1133                            searchQuery.addTerm(field, value, like);
1134                    }
1135            }
1136    
1137            protected void addSearchUserId(
1138                            BooleanQuery contextQuery, SearchContext searchContext)
1139                    throws Exception {
1140    
1141                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
1142    
1143                    multiValueFacet.setFieldName(Field.USER_ID);
1144                    multiValueFacet.setStatic(true);
1145    
1146                    long userId = GetterUtil.getLong(
1147                            searchContext.getAttribute(Field.USER_ID));
1148    
1149                    if (userId > 0) {
1150                            multiValueFacet.setValues(new long[] {userId});
1151                    }
1152    
1153                    searchContext.addFacet(multiValueFacet);
1154            }
1155    
1156            protected void addStagingGroupKeyword(Document document, long groupId)
1157                    throws Exception {
1158    
1159                    if (!isStagingAware()) {
1160                            return;
1161                    }
1162    
1163                    boolean stagingGroup = false;
1164    
1165                    Group group = GroupLocalServiceUtil.getGroup(groupId);
1166    
1167                    if (group.isLayout()) {
1168                            group = GroupLocalServiceUtil.getGroup(group.getParentGroupId());
1169                    }
1170    
1171                    if (group.isStagingGroup()) {
1172                            stagingGroup = true;
1173                    }
1174    
1175                    document.addKeyword(Field.STAGING_GROUP, stagingGroup);
1176            }
1177    
1178            protected void addStatus(
1179                            BooleanQuery contextQuery, SearchContext searchContext)
1180                    throws Exception {
1181    
1182                    int status = GetterUtil.getInteger(
1183                            searchContext.getAttribute(Field.STATUS),
1184                            WorkflowConstants.STATUS_APPROVED);
1185    
1186                    if (status != WorkflowConstants.STATUS_ANY) {
1187                            contextQuery.addRequiredTerm(Field.STATUS, status);
1188                    }
1189                    else {
1190                            BooleanQuery statusQuery = BooleanQueryFactoryUtil.create(
1191                                    searchContext);
1192    
1193                            statusQuery.addTerm(
1194                                    Field.STATUS, WorkflowConstants.STATUS_IN_TRASH);
1195    
1196                            contextQuery.add(statusQuery, BooleanClauseOccur.MUST_NOT);
1197                    }
1198            }
1199    
1200            protected void addTrashFields(Document document, TrashedModel trashedModel)
1201                    throws SystemException {
1202    
1203                    TrashEntry trashEntry = null;
1204    
1205                    try {
1206                            trashEntry = trashedModel.getTrashEntry();
1207                    }
1208                    catch (PortalException pe) {
1209                            if (_log.isDebugEnabled()) {
1210                                    _log.debug("Unable to get trash entry for " + trashedModel);
1211                            }
1212                    }
1213    
1214                    if (trashEntry == null) {
1215                            document.addDate(Field.REMOVED_DATE, new Date());
1216    
1217                            ServiceContext serviceContext =
1218                                    ServiceContextThreadLocal.getServiceContext();
1219    
1220                            if (serviceContext != null) {
1221                                    try {
1222                                            User user = UserLocalServiceUtil.getUser(
1223                                                    serviceContext.getUserId());
1224    
1225                                            document.addKeyword(
1226                                                    Field.REMOVED_BY_USER_NAME, user.getFullName(), true);
1227                                    }
1228                                    catch (PortalException pe) {
1229                                    }
1230                            }
1231                    }
1232                    else {
1233                            document.addDate(Field.REMOVED_DATE, trashEntry.getCreateDate());
1234                            document.addKeyword(
1235                                    Field.REMOVED_BY_USER_NAME, trashEntry.getUserName(), true);
1236    
1237                            if (trashedModel.isInTrash() &&
1238                                    !_isInTrashExplicitly(trashedModel)) {
1239    
1240                                    document.addKeyword(
1241                                            Field.ROOT_ENTRY_CLASS_NAME, trashEntry.getClassName());
1242                                    document.addKeyword(
1243                                            Field.ROOT_ENTRY_CLASS_PK, trashEntry.getClassPK());
1244                            }
1245                    }
1246    
1247                    TrashHandler trashHandler = trashedModel.getTrashHandler();
1248    
1249                    try {
1250                            TrashRenderer trashRenderer = null;
1251    
1252                            if ((trashHandler != null) && (trashEntry != null)) {
1253                                    trashRenderer = trashHandler.getTrashRenderer(
1254                                            trashEntry.getClassPK());
1255                            }
1256    
1257                            if (trashRenderer != null) {
1258                                    document.addKeyword(Field.TYPE, trashRenderer.getType(), true);
1259                            }
1260                    }
1261                    catch (PortalException pe) {
1262                            if (_log.isDebugEnabled()) {
1263                                    _log.debug(
1264                                            "Unable to get trash renderer for " +
1265                                                    trashEntry.getClassName());
1266                            }
1267                    }
1268            }
1269    
1270            protected BooleanQuery createFullQuery(
1271                            BooleanQuery contextQuery, SearchContext searchContext)
1272                    throws Exception {
1273    
1274                    BooleanQuery searchQuery = BooleanQueryFactoryUtil.create(
1275                            searchContext);
1276    
1277                    addSearchKeywords(searchQuery, searchContext);
1278                    postProcessSearchQuery(searchQuery, searchContext);
1279    
1280                    for (IndexerPostProcessor indexerPostProcessor :
1281                                    _indexerPostProcessors) {
1282    
1283                            indexerPostProcessor.postProcessSearchQuery(
1284                                    searchQuery, searchContext);
1285                    }
1286    
1287                    Map<String, Facet> facets = searchContext.getFacets();
1288    
1289                    for (Facet facet : facets.values()) {
1290                            BooleanClause facetClause = facet.getFacetClause();
1291    
1292                            if (facetClause != null) {
1293                                    contextQuery.add(
1294                                            facetClause.getQuery(),
1295                                            facetClause.getBooleanClauseOccur());
1296                            }
1297                    }
1298    
1299                    BooleanQuery fullQuery = BooleanQueryFactoryUtil.create(searchContext);
1300    
1301                    fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
1302    
1303                    if (searchQuery.hasClauses()) {
1304                            fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
1305                    }
1306    
1307                    BooleanClause[] booleanClauses = searchContext.getBooleanClauses();
1308    
1309                    if (booleanClauses != null) {
1310                            for (BooleanClause booleanClause : booleanClauses) {
1311                                    fullQuery.add(
1312                                            booleanClause.getQuery(),
1313                                            booleanClause.getBooleanClauseOccur());
1314                            }
1315                    }
1316    
1317                    postProcessFullQuery(fullQuery, searchContext);
1318    
1319                    for (IndexerPostProcessor indexerPostProcessor :
1320                                    _indexerPostProcessors) {
1321    
1322                            indexerPostProcessor.postProcessFullQuery(fullQuery, searchContext);
1323                    }
1324    
1325                    return fullQuery;
1326            }
1327    
1328            protected Summary createLocalizedSummary(Document document, Locale locale) {
1329                    return createLocalizedSummary(
1330                            document, locale, Field.TITLE, Field.CONTENT);
1331            }
1332    
1333            protected Summary createLocalizedSummary(
1334                    Document document, Locale locale, String titleField,
1335                    String contentField) {
1336    
1337                    Locale snippetLocale = getSnippetLocale(document, locale);
1338    
1339                    String prefix = Field.SNIPPET + StringPool.UNDERLINE;
1340    
1341                    String title = document.get(
1342                            snippetLocale, prefix + titleField, titleField);
1343    
1344                    String content = document.get(
1345                            snippetLocale, prefix + contentField, contentField);
1346    
1347                    return new Summary(snippetLocale, title, content, null);
1348            }
1349    
1350            protected Summary createSummary(Document document) {
1351                    return createSummary(document, Field.TITLE, Field.CONTENT);
1352            }
1353    
1354            protected Summary createSummary(
1355                    Document document, String titleField, String contentField) {
1356    
1357                    String prefix = Field.SNIPPET + StringPool.UNDERLINE;
1358    
1359                    String title = document.get(prefix + titleField, titleField);
1360                    String content = document.get(prefix + contentField, contentField);
1361    
1362                    return new Summary(title, content, null);
1363            }
1364    
1365            protected void deleteDocument(long companyId, long field1)
1366                    throws Exception {
1367    
1368                    deleteDocument(companyId, String.valueOf(field1));
1369            }
1370    
1371            protected void deleteDocument(long companyId, long field1, String field2)
1372                    throws Exception {
1373    
1374                    deleteDocument(companyId, String.valueOf(field1), field2);
1375            }
1376    
1377            protected void deleteDocument(long companyId, String field1)
1378                    throws Exception {
1379    
1380                    Document document = new DocumentImpl();
1381    
1382                    document.addUID(getPortletId(), field1);
1383    
1384                    SearchEngineUtil.deleteDocument(
1385                            getSearchEngineId(), companyId, document.get(Field.UID),
1386                            _commitImmediately);
1387            }
1388    
1389            protected void deleteDocument(long companyId, String field1, String field2)
1390                    throws Exception {
1391    
1392                    Document document = new DocumentImpl();
1393    
1394                    document.addUID(getPortletId(), field1, field2);
1395    
1396                    SearchEngineUtil.deleteDocument(
1397                            getSearchEngineId(), companyId, document.get(Field.UID),
1398                            _commitImmediately);
1399            }
1400    
1401            protected abstract void doDelete(Object obj) throws Exception;
1402    
1403            protected abstract Document doGetDocument(Object obj) throws Exception;
1404    
1405            protected String doGetSortField(String orderByCol) {
1406                    return orderByCol;
1407            }
1408    
1409            protected abstract Summary doGetSummary(
1410                            Document document, Locale locale, String snippet,
1411                            PortletURL portletURL)
1412                    throws Exception;
1413    
1414            protected abstract void doReindex(Object obj) throws Exception;
1415    
1416            protected abstract void doReindex(String className, long classPK)
1417                    throws Exception;
1418    
1419            protected abstract void doReindex(String[] ids) throws Exception;
1420    
1421            protected void doReindexDDMStructures(List<Long> structureIds)
1422                    throws Exception {
1423            }
1424    
1425            protected Hits doSearch(SearchContext searchContext)
1426                    throws SearchException {
1427    
1428                    searchContext.setSearchEngineId(getSearchEngineId());
1429    
1430                    BooleanQuery fullQuery = getFullQuery(searchContext);
1431    
1432                    QueryConfig queryConfig = searchContext.getQueryConfig();
1433    
1434                    fullQuery.setQueryConfig(queryConfig);
1435    
1436                    return SearchEngineUtil.search(searchContext, fullQuery);
1437            }
1438    
1439            /**
1440             * @deprecated As of 7.0.0, with no direct replacement
1441             */
1442            protected Hits filterSearch(
1443                    Hits hits, PermissionChecker permissionChecker,
1444                    SearchContext searchContext) {
1445    
1446                    List<Document> docs = new ArrayList<Document>();
1447                    List<Float> scores = new ArrayList<Float>();
1448    
1449                    int start = searchContext.getStart();
1450                    int end = searchContext.getEnd();
1451    
1452                    String paginationType = GetterUtil.getString(
1453                            searchContext.getAttribute("paginationType"), "more");
1454    
1455                    boolean hasMore = false;
1456    
1457                    Document[] documents = hits.getDocs();
1458    
1459                    int excludeDocsSize = 0;
1460    
1461                    for (int i = 0; i < documents.length; i++) {
1462                            try {
1463                                    Document document = documents[i];
1464    
1465                                    String entryClassName = document.get(Field.ENTRY_CLASS_NAME);
1466                                    long entryClassPK = GetterUtil.getLong(
1467                                            document.get(Field.ENTRY_CLASS_PK));
1468    
1469                                    Indexer indexer = IndexerRegistryUtil.getIndexer(
1470                                            entryClassName);
1471    
1472                                    if ((indexer.isFilterSearch() &&
1473                                             indexer.hasPermission(
1474                                                     permissionChecker, entryClassName, entryClassPK,
1475                                                     ActionKeys.VIEW)) ||
1476                                            !indexer.isFilterSearch() ||
1477                                            !indexer.isPermissionAware()) {
1478    
1479                                            docs.add(document);
1480                                            scores.add(hits.score(i));
1481                                    }
1482                                    else {
1483                                            excludeDocsSize++;
1484                                    }
1485                            }
1486                            catch (Exception e) {
1487                                    excludeDocsSize++;
1488                            }
1489    
1490                            if (paginationType.equals("more") && (end > 0) &&
1491                                    (end < documents.length) && (docs.size() >= end)) {
1492    
1493                                    hasMore = true;
1494    
1495                                    break;
1496                            }
1497                    }
1498    
1499                    int length = docs.size();
1500    
1501                    if (hasMore) {
1502                            length = documents.length - excludeDocsSize;
1503                    }
1504    
1505                    hits.setLength(length);
1506    
1507                    if ((start != QueryUtil.ALL_POS) && (end != QueryUtil.ALL_POS)) {
1508                            if (end > length) {
1509                                    end = length;
1510                            }
1511    
1512                            if (start > end) {
1513                                    start = length;
1514                            }
1515    
1516                            docs = docs.subList(start, end);
1517                    }
1518    
1519                    hits.setDocs(docs.toArray(new Document[docs.size()]));
1520                    hits.setScores(scores.toArray(new Float[scores.size()]));
1521    
1522                    hits.setSearchTime(
1523                            (float)(System.currentTimeMillis() - hits.getStart()) /
1524                                    Time.SECOND);
1525    
1526                    return hits;
1527            }
1528    
1529            protected Document getBaseModelDocument(
1530                            String portletId, BaseModel<?> baseModel)
1531                    throws SystemException {
1532    
1533                    return getBaseModelDocument(portletId, baseModel, baseModel);
1534            }
1535    
1536            protected Document getBaseModelDocument(
1537                            String portletId, BaseModel<?> baseModel,
1538                            BaseModel<?> workflowedBaseModel)
1539                    throws SystemException {
1540    
1541                    Document document = newDocument();
1542    
1543                    String className = baseModel.getModelClassName();
1544    
1545                    long classPK = 0;
1546                    long resourcePrimKey = 0;
1547    
1548                    if (baseModel instanceof ResourcedModel) {
1549                            ResourcedModel resourcedModel = (ResourcedModel)baseModel;
1550    
1551                            classPK = resourcedModel.getResourcePrimKey();
1552                            resourcePrimKey = resourcedModel.getResourcePrimKey();
1553                    }
1554                    else {
1555                            classPK = (Long)baseModel.getPrimaryKeyObj();
1556                    }
1557    
1558                    document.addUID(portletId, classPK);
1559    
1560                    List<AssetCategory> assetCategories =
1561                            AssetCategoryLocalServiceUtil.getCategories(className, classPK);
1562    
1563                    long[] assetCategoryIds = StringUtil.split(
1564                            ListUtil.toString(
1565                                    assetCategories, AssetCategory.CATEGORY_ID_ACCESSOR),
1566                            0L);
1567    
1568                    document.addKeyword(Field.ASSET_CATEGORY_IDS, assetCategoryIds);
1569    
1570                    addSearchAssetCategoryTitles(
1571                            document, Field.ASSET_CATEGORY_TITLES, assetCategories);
1572    
1573                    long classNameId = PortalUtil.getClassNameId(className);
1574    
1575                    List<AssetTag> assetTags = AssetTagLocalServiceUtil.getTags(
1576                            classNameId, classPK);
1577    
1578                    String[] assetTagNames = StringUtil.split(
1579                            ListUtil.toString(assetTags, AssetTag.NAME_ACCESSOR));
1580    
1581                    document.addText(Field.ASSET_TAG_NAMES, assetTagNames);
1582    
1583                    long[] assetTagsIds = StringUtil.split(
1584                            ListUtil.toString(assetTags, AssetTag.TAG_ID_ACCESSOR), 0L);
1585    
1586                    document.addKeyword(Field.ASSET_TAG_IDS, assetTagsIds);
1587    
1588                    document.addKeyword(Field.ENTRY_CLASS_NAME, className);
1589                    document.addKeyword(Field.ENTRY_CLASS_PK, classPK);
1590                    document.addKeyword(Field.PORTLET_ID, portletId);
1591    
1592                    if (resourcePrimKey > 0) {
1593                            document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, resourcePrimKey);
1594                    }
1595    
1596                    if (baseModel instanceof AttachedModel) {
1597                            AttachedModel attachedModel = (AttachedModel)baseModel;
1598    
1599                            document.addKeyword(
1600                                    Field.CLASS_NAME_ID, attachedModel.getClassNameId());
1601                            document.addKeyword(Field.CLASS_PK, attachedModel.getClassPK());
1602                    }
1603    
1604                    if (baseModel instanceof AuditedModel) {
1605                            AuditedModel auditedModel = (AuditedModel)baseModel;
1606    
1607                            document.addKeyword(Field.COMPANY_ID, auditedModel.getCompanyId());
1608                            document.addDate(Field.CREATE_DATE, auditedModel.getCreateDate());
1609                            document.addDate(
1610                                    Field.MODIFIED_DATE, auditedModel.getModifiedDate());
1611                            document.addKeyword(Field.USER_ID, auditedModel.getUserId());
1612    
1613                            String userName = PortalUtil.getUserName(
1614                                    auditedModel.getUserId(), auditedModel.getUserName());
1615    
1616                            document.addKeyword(Field.USER_NAME, userName, true);
1617                    }
1618    
1619                    GroupedModel groupedModel = null;
1620    
1621                    if (baseModel instanceof GroupedModel) {
1622                            groupedModel = (GroupedModel)baseModel;
1623    
1624                            document.addKeyword(
1625                                    Field.GROUP_ID, getSiteGroupId(groupedModel.getGroupId()));
1626                            document.addKeyword(
1627                                    Field.SCOPE_GROUP_ID, groupedModel.getGroupId());
1628                    }
1629    
1630                    if (workflowedBaseModel instanceof WorkflowedModel) {
1631                            WorkflowedModel workflowedModel =
1632                                    (WorkflowedModel)workflowedBaseModel;
1633    
1634                            document.addKeyword(Field.STATUS, workflowedModel.getStatus());
1635                    }
1636    
1637                    if ((groupedModel != null) && (baseModel instanceof TrashedModel)) {
1638                            TrashedModel trashedModel = (TrashedModel)baseModel;
1639    
1640                            if (trashedModel.isInTrash()) {
1641                                    addTrashFields(document, trashedModel);
1642                            }
1643                    }
1644    
1645                    addAssetFields(document, className, classPK);
1646    
1647                    ExpandoBridgeIndexerUtil.addAttributes(
1648                            document, baseModel.getExpandoBridge());
1649    
1650                    return document;
1651            }
1652    
1653            protected String getClassName(SearchContext searchContext) {
1654                    String[] classNames = getClassNames();
1655    
1656                    return classNames[0];
1657            }
1658    
1659            protected Set<String> getLocalizedCountryNames(Country country) {
1660                    Set<String> countryNames = new HashSet<String>();
1661    
1662                    Locale[] locales = LanguageUtil.getAvailableLocales();
1663    
1664                    for (Locale locale : locales) {
1665                            String countryName = country.getName(locale);
1666    
1667                            countryName = StringUtil.toLowerCase(countryName);
1668    
1669                            countryNames.add(countryName);
1670                    }
1671    
1672                    return countryNames;
1673            }
1674    
1675            /**
1676             * @deprecated As of 6.2.0 renamed to {@link #getSiteGroupId(long)}
1677             */
1678            protected long getParentGroupId(long groupId) {
1679                    return getSiteGroupId(groupId);
1680            }
1681    
1682            protected abstract String getPortletId(SearchContext searchContext);
1683    
1684            protected long getSiteGroupId(long groupId) {
1685                    long siteGroupId = groupId;
1686    
1687                    try {
1688                            Group group = GroupLocalServiceUtil.getGroup(groupId);
1689    
1690                            if (group.isLayout()) {
1691                                    siteGroupId = group.getParentGroupId();
1692                            }
1693                    }
1694                    catch (Exception e) {
1695                    }
1696    
1697                    return siteGroupId;
1698            }
1699    
1700            protected Locale getSnippetLocale(Document document, Locale locale) {
1701                    String prefix = Field.SNIPPET + StringPool.UNDERLINE;
1702    
1703                    String localizedAssetCategoryTitlesName =
1704                            prefix +
1705                            DocumentImpl.getLocalizedName(locale, Field.ASSET_CATEGORY_TITLES);
1706                    String localizedContentName =
1707                            prefix + DocumentImpl.getLocalizedName(locale, Field.CONTENT);
1708                    String localizedDescriptionName =
1709                            prefix + DocumentImpl.getLocalizedName(locale, Field.DESCRIPTION);
1710                    String localizedTitleName =
1711                            prefix + DocumentImpl.getLocalizedName(locale, Field.TITLE);
1712    
1713                    if ((document.getField(localizedAssetCategoryTitlesName) != null) ||
1714                            (document.getField(localizedContentName) != null) ||
1715                            (document.getField(localizedDescriptionName) != null) ||
1716                            (document.getField(localizedTitleName) != null)) {
1717    
1718                            return locale;
1719                    }
1720    
1721                    return null;
1722            }
1723    
1724            protected boolean isUseSearchResultPermissionFilter(
1725                    SearchContext searchContext) {
1726    
1727                    return isFilterSearch();
1728            }
1729    
1730            protected boolean isVisible(int entryStatus, int queryStatus) {
1731                    if (((queryStatus != WorkflowConstants.STATUS_ANY) &&
1732                             (entryStatus == queryStatus)) ||
1733                            (entryStatus != WorkflowConstants.STATUS_IN_TRASH)) {
1734    
1735                            return true;
1736                    }
1737    
1738                    return false;
1739            }
1740    
1741            protected Document newDocument() {
1742                    return (Document)_document.clone();
1743            }
1744    
1745            protected void populateAddresses(
1746                            Document document, List<Address> addresses, long regionId,
1747                            long countryId)
1748                    throws PortalException, SystemException {
1749    
1750                    List<String> cities = new ArrayList<String>();
1751    
1752                    List<String> countries = new ArrayList<String>();
1753    
1754                    if (countryId > 0) {
1755                            try {
1756                                    Country country = CountryServiceUtil.getCountry(countryId);
1757    
1758                                    countries.addAll(getLocalizedCountryNames(country));
1759                            }
1760                            catch (NoSuchCountryException nsce) {
1761                                    if (_log.isWarnEnabled()) {
1762                                            _log.warn(nsce.getMessage());
1763                                    }
1764                            }
1765                    }
1766    
1767                    List<String> regions = new ArrayList<String>();
1768    
1769                    if (regionId > 0) {
1770                            try {
1771                                    Region region = RegionServiceUtil.getRegion(regionId);
1772    
1773                                    regions.add(StringUtil.toLowerCase(region.getName()));
1774                            }
1775                            catch (NoSuchRegionException nsre) {
1776                                    if (_log.isWarnEnabled()) {
1777                                            _log.warn(nsre.getMessage());
1778                                    }
1779                            }
1780                    }
1781    
1782                    List<String> streets = new ArrayList<String>();
1783                    List<String> zips = new ArrayList<String>();
1784    
1785                    for (Address address : addresses) {
1786                            cities.add(StringUtil.toLowerCase(address.getCity()));
1787                            countries.addAll(getLocalizedCountryNames(address.getCountry()));
1788                            regions.add(StringUtil.toLowerCase(address.getRegion().getName()));
1789                            streets.add(StringUtil.toLowerCase(address.getStreet1()));
1790                            streets.add(StringUtil.toLowerCase(address.getStreet2()));
1791                            streets.add(StringUtil.toLowerCase(address.getStreet3()));
1792                            zips.add(StringUtil.toLowerCase(address.getZip()));
1793                    }
1794    
1795                    document.addText("city", cities.toArray(new String[cities.size()]));
1796                    document.addText(
1797                            "country", countries.toArray(new String[countries.size()]));
1798                    document.addText("region", regions.toArray(new String[regions.size()]));
1799                    document.addText("street", streets.toArray(new String[streets.size()]));
1800                    document.addText("zip", zips.toArray(new String[zips.size()]));
1801            }
1802    
1803            protected Map<Locale, String> populateMap(
1804                    AssetEntry assetEntry, Map<Locale, String> map) {
1805    
1806                    Locale[] availableLocales = LanguageUtil.getAvailableLocales(
1807                            assetEntry.getGroupId());
1808    
1809                    String defaultValue = map.get(
1810                            LocaleUtil.fromLanguageId(assetEntry.getDefaultLanguageId()));
1811    
1812                    for (Locale availableLocale : availableLocales) {
1813                            if (!map.containsKey(availableLocale) ||
1814                                    Validator.isNull(map.get(availableLocale))) {
1815    
1816                                    map.put(availableLocale, defaultValue);
1817                            }
1818                    }
1819    
1820                    return map;
1821            }
1822    
1823            protected void postProcessFullQuery(
1824                            BooleanQuery fullQuery, SearchContext searchContext)
1825                    throws Exception {
1826            }
1827    
1828            protected void processHits(SearchContext searchContext, Hits hits)
1829                    throws SearchException {
1830    
1831                    HitsProcessor hitsProcessor =
1832                            HitsProcessorRegistryUtil.getDefaultHitsProcessor();
1833    
1834                    if (hitsProcessor != null) {
1835                            hitsProcessor.process(searchContext, hits);
1836                    }
1837            }
1838    
1839            protected void setFilterSearch(boolean filterSearch) {
1840                    _filterSearch = filterSearch;
1841            }
1842    
1843            protected void setIndexerEnabled(boolean indexerEnabled) {
1844                    _indexerEnabled = indexerEnabled;
1845            }
1846    
1847            protected void setPermissionAware(boolean permissionAware) {
1848                    _permissionAware = permissionAware;
1849            }
1850    
1851            protected void setSortableTextFields(String[] sortableTextFields) {
1852                    _document.setSortableTextFields(sortableTextFields);
1853            }
1854    
1855            protected void setStagingAware(boolean stagingAware) {
1856                    _stagingAware = stagingAware;
1857            }
1858    
1859            private boolean _isInTrashExplicitly(TrashedModel trashedModel)
1860                    throws SystemException {
1861    
1862                    if (!trashedModel.isInTrash()) {
1863                            return false;
1864                    }
1865    
1866                    ClassedModel classedModel = (ClassedModel)trashedModel;
1867    
1868                    TrashEntry trashEntry = TrashEntryLocalServiceUtil.fetchEntry(
1869                            classedModel.getModelClassName(),
1870                            trashedModel.getTrashEntryClassPK());
1871    
1872                    if (trashEntry != null) {
1873                            return true;
1874                    }
1875    
1876                    return false;
1877            }
1878    
1879            private static Log _log = LogFactoryUtil.getLog(BaseIndexer.class);
1880    
1881            private boolean _commitImmediately;
1882            private Document _document = new DocumentImpl();
1883            private boolean _filterSearch;
1884            private boolean _indexerEnabled = true;
1885            private IndexerPostProcessor[] _indexerPostProcessors =
1886                    new IndexerPostProcessor[0];
1887            private boolean _permissionAware;
1888            private String _searchEngineId;
1889            private boolean _stagingAware = true;
1890    
1891    }