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