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                            doReindex(obj);
439                    }
440                    catch (SearchException se) {
441                            throw se;
442                    }
443                    catch (Exception e) {
444                            throw new SearchException(e);
445                    }
446            }
447    
448            @Override
449            public void reindex(String className, long classPK) throws SearchException {
450                    try {
451                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled() ||
452                                    ExportImportThreadLocal.isImportInProcess() || (classPK <= 0)) {
453    
454                                    return;
455                            }
456    
457                            doReindex(className, classPK);
458                    }
459                    catch (NoSuchModelException nsme) {
460                            if (_log.isWarnEnabled()) {
461                                    _log.warn("Unable to index " + className + " " + classPK, nsme);
462                            }
463                    }
464                    catch (SearchException se) {
465                            throw se;
466                    }
467                    catch (Exception e) {
468                            throw new SearchException(e);
469                    }
470            }
471    
472            @Override
473            public void reindex(String[] ids) throws SearchException {
474                    try {
475                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled() ||
476                                    ExportImportThreadLocal.isImportInProcess()) {
477    
478                                    return;
479                            }
480    
481                            doReindex(ids);
482                    }
483                    catch (SearchException se) {
484                            throw se;
485                    }
486                    catch (Exception e) {
487                            throw new SearchException(e);
488                    }
489            }
490    
491            @Override
492            public void reindexDDMStructures(List<Long> ddmStructureIds)
493                    throws SearchException {
494    
495                    try {
496                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled() ||
497                                    ExportImportThreadLocal.isImportInProcess()) {
498    
499                                    return;
500                            }
501    
502                            doReindexDDMStructures(ddmStructureIds);
503                    }
504                    catch (SearchException se) {
505                            throw se;
506                    }
507                    catch (Exception e) {
508                            throw new SearchException(e);
509                    }
510            }
511    
512            @Override
513            public Hits search(SearchContext searchContext) throws SearchException {
514                    try {
515                            Hits hits = null;
516    
517                            PermissionChecker permissionChecker =
518                                    PermissionThreadLocal.getPermissionChecker();
519    
520                            if ((permissionChecker != null) &&
521                                    isUseSearchResultPermissionFilter(searchContext)) {
522    
523                                    SearchResultPermissionFilter searchResultPermissionFilter =
524                                            new DefaultSearchResultPermissionFilter(
525                                                    this, permissionChecker);
526    
527                                    hits = searchResultPermissionFilter.search(searchContext);
528                            }
529                            else {
530                                    hits = doSearch(searchContext);
531                            }
532    
533                            processHits(searchContext, hits);
534    
535                            return hits;
536                    }
537                    catch (SearchException se) {
538                            throw se;
539                    }
540                    catch (Exception e) {
541                            throw new SearchException(e);
542                    }
543            }
544    
545            public void setCommitImmediately(boolean commitImmediately) {
546                    _commitImmediately = commitImmediately;
547            }
548    
549            @Override
550            public void unregisterIndexerPostProcessor(
551                    IndexerPostProcessor indexerPostProcessor) {
552    
553                    List<IndexerPostProcessor> indexerPostProcessorsList =
554                            ListUtil.fromArray(_indexerPostProcessors);
555    
556                    indexerPostProcessorsList.remove(indexerPostProcessor);
557    
558                    _indexerPostProcessors = indexerPostProcessorsList.toArray(
559                            new IndexerPostProcessor[indexerPostProcessorsList.size()]);
560            }
561    
562            protected void addAssetFields(
563                            Document document, String className, long classPK)
564                    throws SystemException {
565    
566                    AssetRendererFactory assetRendererFactory =
567                            AssetRendererFactoryRegistryUtil.getAssetRendererFactoryByClassName(
568                                    className);
569    
570                    if ((assetRendererFactory == null) ||
571                            !assetRendererFactory.isSelectable()) {
572    
573                            return;
574                    }
575    
576                    AssetEntry assetEntry = AssetEntryLocalServiceUtil.fetchEntry(
577                            className, classPK);
578    
579                    if (assetEntry == null) {
580                            return;
581                    }
582    
583                    if (!document.hasField(Field.CREATE_DATE)) {
584                            document.addDate(Field.CREATE_DATE, assetEntry.getCreateDate());
585                    }
586    
587                    if (assetEntry.getExpirationDate() != null) {
588                            document.addDate(
589                                    Field.EXPIRATION_DATE, assetEntry.getExpirationDate());
590                    }
591                    else {
592                            document.addDate(Field.EXPIRATION_DATE, new Date(Long.MAX_VALUE));
593                    }
594    
595                    if (!document.hasField(Field.MODIFIED_DATE)) {
596                            document.addDate(Field.MODIFIED_DATE, assetEntry.getModifiedDate());
597                    }
598    
599                    document.addNumber(Field.PRIORITY, assetEntry.getPriority());
600    
601                    if (assetEntry.getPublishDate() != null) {
602                            document.addDate(Field.PUBLISH_DATE, assetEntry.getPublishDate());
603                    }
604                    else {
605                            document.addDate(Field.PUBLISH_DATE, new Date(0));
606                    }
607    
608                    RatingsStats ratingsStats = RatingsStatsLocalServiceUtil.getStats(
609                            className, classPK);
610    
611                    document.addNumber(Field.RATINGS, ratingsStats.getAverageScore());
612    
613                    document.addNumber(Field.VIEW_COUNT, assetEntry.getViewCount());
614    
615                    document.addLocalizedKeyword(
616                            "localized_title",
617                            populateMap(assetEntry, assetEntry.getTitleMap()), true, true);
618                    document.addKeyword("visible", assetEntry.isVisible());
619            }
620    
621            /**
622             * @deprecated As of 6.2.0, replaced by {@link
623             *             #addSearchLocalizedTerm(BooleanQuery, SearchContext, String,
624             *             boolean)}
625             */
626            protected void addLocalizedSearchTerm(
627                            BooleanQuery searchQuery, SearchContext searchContext, String field,
628                            boolean like)
629                    throws Exception {
630    
631                    addSearchLocalizedTerm(searchQuery, searchContext, field, like);
632            }
633    
634            protected void addRelatedClassNames(
635                            BooleanQuery contextQuery, SearchContext searchContext)
636                    throws Exception {
637    
638                    searchContext.setAttribute("relatedClassName", Boolean.TRUE);
639    
640                    String[] relatedEntryClassNames = (String[])searchContext.getAttribute(
641                            "relatedEntryClassNames");
642    
643                    if (ArrayUtil.isEmpty(relatedEntryClassNames)) {
644                            return;
645                    }
646    
647                    BooleanQuery relatedQueries = BooleanQueryFactoryUtil.create(
648                            searchContext);
649    
650                    for (String relatedEntryClassName : relatedEntryClassNames) {
651                            Indexer indexer = IndexerRegistryUtil.getIndexer(
652                                    relatedEntryClassName);
653    
654                            if (indexer == null) {
655                                    continue;
656                            }
657    
658                            BooleanQuery relatedQuery = BooleanQueryFactoryUtil.create(
659                                    searchContext);
660    
661                            indexer.postProcessContextQuery(relatedQuery, searchContext);
662    
663                            relatedQuery.addRequiredTerm(
664                                    Field.CLASS_NAME_ID,
665                                    PortalUtil.getClassNameId(relatedEntryClassName));
666    
667                            relatedQueries.add(relatedQuery, BooleanClauseOccur.SHOULD);
668                    }
669    
670                    contextQuery.add(relatedQueries, BooleanClauseOccur.MUST);
671    
672                    searchContext.setAttribute("relatedClassName", Boolean.FALSE);
673            }
674    
675            protected void addSearchArrayQuery(
676                            BooleanQuery searchQuery, SearchContext searchContext, String field)
677                    throws Exception {
678    
679                    if (Validator.isNull(field)) {
680                            return;
681                    }
682    
683                    Object fieldValues = searchContext.getAttribute(field);
684    
685                    if (fieldValues == null) {
686                            return;
687                    }
688    
689                    BooleanQuery fieldQuery = null;
690    
691                    if (fieldValues instanceof int[]) {
692                            int[] fieldValuesArray = (int[])fieldValues;
693    
694                            if (fieldValuesArray.length == 0) {
695                                    return;
696                            }
697    
698                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
699    
700                            for (int fieldValue : fieldValuesArray) {
701                                    fieldQuery.addTerm(field, fieldValue);
702                            }
703                    }
704                    else if (fieldValues instanceof Integer[]) {
705                            Integer[] fieldValuesArray = (Integer[])fieldValues;
706    
707                            if (fieldValuesArray.length == 0) {
708                                    return;
709                            }
710    
711                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
712    
713                            for (Integer fieldValue : fieldValuesArray) {
714                                    fieldQuery.addTerm(field, fieldValue);
715                            }
716                    }
717                    else if (fieldValues instanceof long[]) {
718                            long[] fieldValuesArray = (long[])fieldValues;
719    
720                            if (fieldValuesArray.length == 0) {
721                                    return;
722                            }
723    
724                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
725    
726                            for (long fieldValue : fieldValuesArray) {
727                                    fieldQuery.addTerm(field, fieldValue);
728                            }
729                    }
730                    else if (fieldValues instanceof Long[]) {
731                            Long[] fieldValuesArray = (Long[])fieldValues;
732    
733                            if (fieldValuesArray.length == 0) {
734                                    return;
735                            }
736    
737                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
738    
739                            for (Long fieldValue : fieldValuesArray) {
740                                    fieldQuery.addTerm(field, fieldValue);
741                            }
742                    }
743                    else if (fieldValues instanceof short[]) {
744                            short[] fieldValuesArray = (short[])fieldValues;
745    
746                            if (fieldValuesArray.length == 0) {
747                                    return;
748                            }
749    
750                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
751    
752                            for (short fieldValue : fieldValuesArray) {
753                                    fieldQuery.addTerm(field, fieldValue);
754                            }
755                    }
756                    else if (fieldValues instanceof Short[]) {
757                            Short[] fieldValuesArray = (Short[])fieldValues;
758    
759                            if (fieldValuesArray.length == 0) {
760                                    return;
761                            }
762    
763                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
764    
765                            for (Short fieldValue : fieldValuesArray) {
766                                    fieldQuery.addTerm(field, fieldValue);
767                            }
768                    }
769    
770                    if (fieldQuery != null) {
771                            if (searchContext.isAndSearch()) {
772                                    searchQuery.add(fieldQuery, BooleanClauseOccur.MUST);
773                            }
774                            else {
775                                    searchQuery.add(fieldQuery, BooleanClauseOccur.SHOULD);
776                            }
777                    }
778            }
779    
780            protected void addSearchAssetCategoryIds(
781                            BooleanQuery contextQuery, SearchContext searchContext)
782                    throws Exception {
783    
784                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
785    
786                    multiValueFacet.setFieldName(Field.ASSET_CATEGORY_IDS);
787                    multiValueFacet.setStatic(true);
788                    multiValueFacet.setValues(searchContext.getAssetCategoryIds());
789    
790                    searchContext.addFacet(multiValueFacet);
791            }
792    
793            protected void addSearchAssetCategoryTitles(
794                    Document document, String field, List<AssetCategory> assetCategories) {
795    
796                    Map<Locale, List<String>> assetCategoryTitles =
797                            new HashMap<Locale, List<String>>();
798    
799                    Locale defaultLocale = LocaleUtil.getDefault();
800    
801                    for (AssetCategory assetCategory : assetCategories) {
802                            Map<Locale, String> titleMap = assetCategory.getTitleMap();
803    
804                            for (Map.Entry<Locale, String> entry : titleMap.entrySet()) {
805                                    Locale locale = entry.getKey();
806                                    String title = entry.getValue();
807    
808                                    if (Validator.isNull(title)) {
809                                            continue;
810                                    }
811    
812                                    List<String> titles = assetCategoryTitles.get(locale);
813    
814                                    if (titles == null) {
815                                            titles = new ArrayList<String>();
816    
817                                            assetCategoryTitles.put(locale, titles);
818                                    }
819    
820                                    titles.add(StringUtil.toLowerCase(title));
821                            }
822                    }
823    
824                    for (Map.Entry<Locale, List<String>> entry :
825                                    assetCategoryTitles.entrySet()) {
826    
827                            Locale locale = entry.getKey();
828                            List<String> titles = entry.getValue();
829    
830                            String[] titlesArray = titles.toArray(new String[0]);
831    
832                            if (locale.equals(defaultLocale)) {
833                                    document.addText(field, titlesArray);
834                            }
835    
836                            document.addText(
837                                    field.concat(StringPool.UNDERLINE).concat(locale.toString()),
838                                    titlesArray);
839                    }
840            }
841    
842            protected void addSearchAssetTagNames(
843                            BooleanQuery contextQuery, SearchContext searchContext)
844                    throws Exception {
845    
846                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
847    
848                    multiValueFacet.setFieldName(Field.ASSET_TAG_NAMES);
849                    multiValueFacet.setStatic(true);
850                    multiValueFacet.setValues(searchContext.getAssetTagNames());
851    
852                    searchContext.addFacet(multiValueFacet);
853            }
854    
855            protected void addSearchClassTypeIds(
856                            BooleanQuery contextQuery, SearchContext searchContext)
857                    throws Exception {
858    
859                    long[] classTypeIds = searchContext.getClassTypeIds();
860    
861                    if ((classTypeIds == null) || (classTypeIds.length <= 0)) {
862                            return;
863                    }
864    
865                    BooleanQuery classTypeIdsQuery = BooleanQueryFactoryUtil.create(
866                            searchContext);
867    
868                    for (long classTypeId : classTypeIds) {
869                            classTypeIdsQuery.addTerm(Field.CLASS_TYPE_ID, classTypeId);
870                    }
871    
872                    contextQuery.add(classTypeIdsQuery, BooleanClauseOccur.MUST);
873            }
874    
875            protected void addSearchDDMStruture(
876                            BooleanQuery searchQuery, SearchContext searchContext,
877                            DDMStructure ddmStructure)
878                    throws Exception {
879    
880                    Set<String> fieldNames = ddmStructure.getFieldNames();
881    
882                    for (String fieldName : fieldNames) {
883                            String indexType = ddmStructure.getFieldProperty(
884                                    fieldName, "indexType");
885    
886                            if (Validator.isNull(indexType)) {
887                                    continue;
888                            }
889    
890                            String name = DDMIndexerUtil.encodeName(
891                                    ddmStructure.getStructureId(), fieldName,
892                                    searchContext.getLocale());
893    
894                            boolean like = false;
895    
896                            if (indexType.equals("text")) {
897                                    like = true;
898                            }
899    
900                            addSearchTerm(searchQuery, searchContext, name, like);
901                    }
902            }
903    
904            protected void addSearchEntryClassNames(
905                            BooleanQuery contextQuery, SearchContext searchContext)
906                    throws Exception {
907    
908                    Facet facet = new AssetEntriesFacet(searchContext);
909    
910                    facet.setStatic(true);
911    
912                    searchContext.addFacet(facet);
913            }
914    
915            protected void addSearchExpando(
916                            BooleanQuery searchQuery, SearchContext searchContext,
917                            String keywords)
918                    throws Exception {
919    
920                    ExpandoBridge expandoBridge = ExpandoBridgeFactoryUtil.getExpandoBridge(
921                            searchContext.getCompanyId(), getClassName(searchContext));
922    
923                    Set<String> attributeNames = SetUtil.fromEnumeration(
924                            expandoBridge.getAttributeNames());
925    
926                    for (String attributeName : attributeNames) {
927                            UnicodeProperties properties = expandoBridge.getAttributeProperties(
928                                    attributeName);
929    
930                            int indexType = GetterUtil.getInteger(
931                                    properties.getProperty(ExpandoColumnConstants.INDEX_TYPE));
932    
933                            if (indexType != ExpandoColumnConstants.INDEX_TYPE_NONE) {
934                                    String fieldName = ExpandoBridgeIndexerUtil.encodeFieldName(
935                                            attributeName);
936    
937                                    if (Validator.isNotNull(keywords)) {
938                                            if (searchContext.isAndSearch()) {
939                                                    searchQuery.addRequiredTerm(fieldName, keywords);
940                                            }
941                                            else {
942                                                    searchQuery.addTerm(fieldName, keywords);
943                                            }
944                                    }
945                            }
946                    }
947            }
948    
949            protected void addSearchFolderId(
950                            BooleanQuery contextQuery, SearchContext searchContext)
951                    throws Exception {
952    
953                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
954    
955                    multiValueFacet.setFieldName(Field.TREE_PATH);
956                    multiValueFacet.setStatic(true);
957                    multiValueFacet.setValues(searchContext.getFolderIds());
958    
959                    searchContext.addFacet(multiValueFacet);
960            }
961    
962            protected void addSearchGroupId(
963                            BooleanQuery contextQuery, SearchContext searchContext)
964                    throws Exception {
965    
966                    Facet facet = new ScopeFacet(searchContext);
967    
968                    facet.setStatic(true);
969    
970                    searchContext.addFacet(facet);
971            }
972    
973            protected void addSearchKeywords(
974                            BooleanQuery searchQuery, SearchContext searchContext)
975                    throws Exception {
976    
977                    String keywords = searchContext.getKeywords();
978    
979                    if (Validator.isNull(keywords)) {
980                            return;
981                    }
982    
983                    searchQuery.addTerms(Field.KEYWORDS, keywords, searchContext.isLike());
984    
985                    addSearchExpando(searchQuery, searchContext, keywords);
986    
987                    addSearchLocalizedTerm(
988                            searchQuery, searchContext, Field.ASSET_CATEGORY_TITLES,
989                            searchContext.isLike());
990            }
991    
992            protected void addSearchLayout(
993                            BooleanQuery contextQuery, SearchContext searchContext)
994                    throws Exception {
995    
996                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
997    
998                    multiValueFacet.setFieldName(Field.LAYOUT_UUID);
999                    multiValueFacet.setStatic(true);
1000    
1001                    searchContext.addFacet(multiValueFacet);
1002            }
1003    
1004            protected void addSearchLocalizedTerm(
1005                            BooleanQuery searchQuery, SearchContext searchContext, String field,
1006                            boolean like)
1007                    throws Exception {
1008    
1009                    addSearchTerm(searchQuery, searchContext, field, like);
1010                    addSearchTerm(
1011                            searchQuery, searchContext,
1012                            DocumentImpl.getLocalizedName(searchContext.getLocale(), field),
1013                            like);
1014            }
1015    
1016            protected void addSearchTerm(
1017                            BooleanQuery searchQuery, SearchContext searchContext, String field,
1018                            boolean like)
1019                    throws Exception {
1020    
1021                    if (Validator.isNull(field)) {
1022                            return;
1023                    }
1024    
1025                    String value = null;
1026    
1027                    Serializable serializable = searchContext.getAttribute(field);
1028    
1029                    if (serializable != null) {
1030                            Class<?> clazz = serializable.getClass();
1031    
1032                            if (clazz.isArray()) {
1033                                    value = StringUtil.merge((Object[])serializable);
1034                            }
1035                            else {
1036                                    value = GetterUtil.getString(serializable);
1037                            }
1038                    }
1039                    else {
1040                            value = GetterUtil.getString(serializable);
1041                    }
1042    
1043                    if (Validator.isNotNull(value) &&
1044                            (searchContext.getFacet(field) != null)) {
1045    
1046                            return;
1047                    }
1048    
1049                    if (Validator.isNull(value)) {
1050                            value = searchContext.getKeywords();
1051                    }
1052    
1053                    if (Validator.isNull(value)) {
1054                            return;
1055                    }
1056    
1057                    if (searchContext.isAndSearch()) {
1058                            searchQuery.addRequiredTerm(field, value, like);
1059                    }
1060                    else {
1061                            searchQuery.addTerm(field, value, like);
1062                    }
1063            }
1064    
1065            protected void addSearchUserId(
1066                            BooleanQuery contextQuery, SearchContext searchContext)
1067                    throws Exception {
1068    
1069                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
1070    
1071                    multiValueFacet.setFieldName(Field.USER_ID);
1072                    multiValueFacet.setStatic(true);
1073    
1074                    long userId = GetterUtil.getLong(
1075                            searchContext.getAttribute(Field.USER_ID));
1076    
1077                    if (userId > 0) {
1078                            multiValueFacet.setValues(new long[] {userId});
1079                    }
1080    
1081                    searchContext.addFacet(multiValueFacet);
1082            }
1083    
1084            protected void addStagingGroupKeyword(Document document, long groupId)
1085                    throws Exception {
1086    
1087                    if (!isStagingAware()) {
1088                            return;
1089                    }
1090    
1091                    boolean stagingGroup = false;
1092    
1093                    Group group = GroupLocalServiceUtil.getGroup(groupId);
1094    
1095                    if (group.isLayout()) {
1096                            group = GroupLocalServiceUtil.getGroup(group.getParentGroupId());
1097                    }
1098    
1099                    if (group.isStagingGroup()) {
1100                            stagingGroup = true;
1101                    }
1102    
1103                    document.addKeyword(Field.STAGING_GROUP, stagingGroup);
1104            }
1105    
1106            protected void addStatus(
1107                            BooleanQuery contextQuery, SearchContext searchContext)
1108                    throws Exception {
1109    
1110                    int status = GetterUtil.getInteger(
1111                            searchContext.getAttribute(Field.STATUS),
1112                            WorkflowConstants.STATUS_APPROVED);
1113    
1114                    if (status != WorkflowConstants.STATUS_ANY) {
1115                            contextQuery.addRequiredTerm(Field.STATUS, status);
1116                    }
1117                    else {
1118                            BooleanQuery statusQuery = BooleanQueryFactoryUtil.create(
1119                                    searchContext);
1120    
1121                            statusQuery.addTerm(
1122                                    Field.STATUS, WorkflowConstants.STATUS_IN_TRASH);
1123    
1124                            contextQuery.add(statusQuery, BooleanClauseOccur.MUST_NOT);
1125                    }
1126            }
1127    
1128            protected void addTrashFields(Document document, TrashedModel trashedModel)
1129                    throws SystemException {
1130    
1131                    TrashEntry trashEntry = null;
1132    
1133                    try {
1134                            trashEntry = trashedModel.getTrashEntry();
1135                    }
1136                    catch (PortalException pe) {
1137                            if (_log.isDebugEnabled()) {
1138                                    _log.debug("Unable to get trash entry for " + trashedModel);
1139                            }
1140                    }
1141    
1142                    if (trashEntry == null) {
1143                            document.addDate(Field.REMOVED_DATE, new Date());
1144    
1145                            ServiceContext serviceContext =
1146                                    ServiceContextThreadLocal.getServiceContext();
1147    
1148                            if (serviceContext != null) {
1149                                    try {
1150                                            User user = UserLocalServiceUtil.getUser(
1151                                                    serviceContext.getUserId());
1152    
1153                                            document.addKeyword(
1154                                                    Field.REMOVED_BY_USER_NAME, user.getFullName(), true);
1155                                    }
1156                                    catch (PortalException pe) {
1157                                    }
1158                            }
1159                    }
1160                    else {
1161                            document.addDate(Field.REMOVED_DATE, trashEntry.getCreateDate());
1162                            document.addKeyword(
1163                                    Field.REMOVED_BY_USER_NAME, trashEntry.getUserName(), true);
1164    
1165                            if (trashedModel.isInTrash() &&
1166                                    !_isInTrashExplicitly(trashedModel)) {
1167    
1168                                    document.addKeyword(
1169                                            Field.ROOT_ENTRY_CLASS_NAME, trashEntry.getClassName());
1170                                    document.addKeyword(
1171                                            Field.ROOT_ENTRY_CLASS_PK, trashEntry.getClassPK());
1172                            }
1173                    }
1174    
1175                    TrashHandler trashHandler = trashedModel.getTrashHandler();
1176    
1177                    try {
1178                            TrashRenderer trashRenderer = null;
1179    
1180                            if ((trashHandler != null) && (trashEntry != null)) {
1181                                    trashRenderer = trashHandler.getTrashRenderer(
1182                                            trashEntry.getClassPK());
1183                            }
1184    
1185                            if (trashRenderer != null) {
1186                                    document.addKeyword(Field.TYPE, trashRenderer.getType(), true);
1187                            }
1188                    }
1189                    catch (PortalException pe) {
1190                            if (_log.isDebugEnabled()) {
1191                                    _log.debug(
1192                                            "Unable to get trash renderer for " +
1193                                                    trashEntry.getClassName());
1194                            }
1195                    }
1196            }
1197    
1198            protected BooleanQuery createFullQuery(
1199                            BooleanQuery contextQuery, SearchContext searchContext)
1200                    throws Exception {
1201    
1202                    BooleanQuery searchQuery = BooleanQueryFactoryUtil.create(
1203                            searchContext);
1204    
1205                    addSearchKeywords(searchQuery, searchContext);
1206                    postProcessSearchQuery(searchQuery, searchContext);
1207    
1208                    for (IndexerPostProcessor indexerPostProcessor :
1209                                    _indexerPostProcessors) {
1210    
1211                            indexerPostProcessor.postProcessSearchQuery(
1212                                    searchQuery, searchContext);
1213                    }
1214    
1215                    Map<String, Facet> facets = searchContext.getFacets();
1216    
1217                    for (Facet facet : facets.values()) {
1218                            BooleanClause facetClause = facet.getFacetClause();
1219    
1220                            if (facetClause != null) {
1221                                    contextQuery.add(
1222                                            facetClause.getQuery(),
1223                                            facetClause.getBooleanClauseOccur());
1224                            }
1225                    }
1226    
1227                    BooleanQuery fullQuery = BooleanQueryFactoryUtil.create(searchContext);
1228    
1229                    fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
1230    
1231                    if (searchQuery.hasClauses()) {
1232                            fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
1233                    }
1234    
1235                    BooleanClause[] booleanClauses = searchContext.getBooleanClauses();
1236    
1237                    if (booleanClauses != null) {
1238                            for (BooleanClause booleanClause : booleanClauses) {
1239                                    fullQuery.add(
1240                                            booleanClause.getQuery(),
1241                                            booleanClause.getBooleanClauseOccur());
1242                            }
1243                    }
1244    
1245                    postProcessFullQuery(fullQuery, searchContext);
1246    
1247                    for (IndexerPostProcessor indexerPostProcessor :
1248                                    _indexerPostProcessors) {
1249    
1250                            indexerPostProcessor.postProcessFullQuery(fullQuery, searchContext);
1251                    }
1252    
1253                    return fullQuery;
1254            }
1255    
1256            protected Summary createLocalizedSummary(Document document, Locale locale) {
1257                    return createLocalizedSummary(
1258                            document, locale, Field.TITLE, Field.CONTENT);
1259            }
1260    
1261            protected Summary createLocalizedSummary(
1262                    Document document, Locale locale, String titleField,
1263                    String contentField) {
1264    
1265                    Locale snippetLocale = getSnippetLocale(document, locale);
1266    
1267                    String prefix = Field.SNIPPET + StringPool.UNDERLINE;
1268    
1269                    String title = document.get(
1270                            snippetLocale, prefix + titleField, titleField);
1271    
1272                    String content = document.get(
1273                            snippetLocale, prefix + contentField, contentField);
1274    
1275                    return new Summary(snippetLocale, title, content, null);
1276            }
1277    
1278            protected Summary createSummary(Document document) {
1279                    return createSummary(document, Field.TITLE, Field.CONTENT);
1280            }
1281    
1282            protected Summary createSummary(
1283                    Document document, String titleField, String contentField) {
1284    
1285                    String prefix = Field.SNIPPET + StringPool.UNDERLINE;
1286    
1287                    String title = document.get(prefix + titleField, titleField);
1288                    String content = document.get(prefix + contentField, contentField);
1289    
1290                    return new Summary(title, content, null);
1291            }
1292    
1293            protected void deleteDocument(long companyId, long field1)
1294                    throws Exception {
1295    
1296                    deleteDocument(companyId, String.valueOf(field1));
1297            }
1298    
1299            protected void deleteDocument(long companyId, long field1, String field2)
1300                    throws Exception {
1301    
1302                    deleteDocument(companyId, String.valueOf(field1), field2);
1303            }
1304    
1305            protected void deleteDocument(long companyId, String field1)
1306                    throws Exception {
1307    
1308                    Document document = new DocumentImpl();
1309    
1310                    document.addUID(getPortletId(), field1);
1311    
1312                    SearchEngineUtil.deleteDocument(
1313                            getSearchEngineId(), companyId, document.get(Field.UID),
1314                            _commitImmediately);
1315            }
1316    
1317            protected void deleteDocument(long companyId, String field1, String field2)
1318                    throws Exception {
1319    
1320                    Document document = new DocumentImpl();
1321    
1322                    document.addUID(getPortletId(), field1, field2);
1323    
1324                    SearchEngineUtil.deleteDocument(
1325                            getSearchEngineId(), companyId, document.get(Field.UID),
1326                            _commitImmediately);
1327            }
1328    
1329            protected abstract void doDelete(Object obj) throws Exception;
1330    
1331            protected abstract Document doGetDocument(Object obj) throws Exception;
1332    
1333            protected String doGetSortField(String orderByCol) {
1334                    return orderByCol;
1335            }
1336    
1337            protected abstract Summary doGetSummary(
1338                            Document document, Locale locale, String snippet,
1339                            PortletURL portletURL)
1340                    throws Exception;
1341    
1342            protected abstract void doReindex(Object obj) throws Exception;
1343    
1344            protected abstract void doReindex(String className, long classPK)
1345                    throws Exception;
1346    
1347            protected abstract void doReindex(String[] ids) throws Exception;
1348    
1349            protected void doReindexDDMStructures(List<Long> structureIds)
1350                    throws Exception {
1351            }
1352    
1353            protected Hits doSearch(SearchContext searchContext)
1354                    throws SearchException {
1355    
1356                    searchContext.setSearchEngineId(getSearchEngineId());
1357    
1358                    BooleanQuery fullQuery = getFullQuery(searchContext);
1359    
1360                    QueryConfig queryConfig = searchContext.getQueryConfig();
1361    
1362                    fullQuery.setQueryConfig(queryConfig);
1363    
1364                    return SearchEngineUtil.search(searchContext, fullQuery);
1365            }
1366    
1367            /**
1368             * @deprecated As of 7.0.0, with no direct replacement
1369             */
1370            protected Hits filterSearch(
1371                    Hits hits, PermissionChecker permissionChecker,
1372                    SearchContext searchContext) {
1373    
1374                    List<Document> docs = new ArrayList<Document>();
1375                    List<Float> scores = new ArrayList<Float>();
1376    
1377                    int start = searchContext.getStart();
1378                    int end = searchContext.getEnd();
1379    
1380                    String paginationType = GetterUtil.getString(
1381                            searchContext.getAttribute("paginationType"), "more");
1382    
1383                    boolean hasMore = false;
1384    
1385                    Document[] documents = hits.getDocs();
1386    
1387                    int excludeDocsSize = 0;
1388    
1389                    for (int i = 0; i < documents.length; i++) {
1390                            try {
1391                                    Document document = documents[i];
1392    
1393                                    String entryClassName = document.get(Field.ENTRY_CLASS_NAME);
1394                                    long entryClassPK = GetterUtil.getLong(
1395                                            document.get(Field.ENTRY_CLASS_PK));
1396    
1397                                    Indexer indexer = IndexerRegistryUtil.getIndexer(
1398                                            entryClassName);
1399    
1400                                    if ((indexer.isFilterSearch() &&
1401                                             indexer.hasPermission(
1402                                                     permissionChecker, entryClassName, entryClassPK,
1403                                                     ActionKeys.VIEW)) ||
1404                                            !indexer.isFilterSearch() ||
1405                                            !indexer.isPermissionAware()) {
1406    
1407                                            docs.add(document);
1408                                            scores.add(hits.score(i));
1409                                    }
1410                                    else {
1411                                            excludeDocsSize++;
1412                                    }
1413                            }
1414                            catch (Exception e) {
1415                                    excludeDocsSize++;
1416                            }
1417    
1418                            if (paginationType.equals("more") && (end > 0) &&
1419                                    (end < documents.length) && (docs.size() >= end)) {
1420    
1421                                    hasMore = true;
1422    
1423                                    break;
1424                            }
1425                    }
1426    
1427                    int length = docs.size();
1428    
1429                    if (hasMore) {
1430                            length = documents.length - excludeDocsSize;
1431                    }
1432    
1433                    hits.setLength(length);
1434    
1435                    if ((start != QueryUtil.ALL_POS) && (end != QueryUtil.ALL_POS)) {
1436                            if (end > length) {
1437                                    end = length;
1438                            }
1439    
1440                            if (start > end) {
1441                                    start = length;
1442                            }
1443    
1444                            docs = docs.subList(start, end);
1445                    }
1446    
1447                    hits.setDocs(docs.toArray(new Document[docs.size()]));
1448                    hits.setScores(scores.toArray(new Float[scores.size()]));
1449    
1450                    hits.setSearchTime(
1451                            (float)(System.currentTimeMillis() - hits.getStart()) /
1452                                    Time.SECOND);
1453    
1454                    return hits;
1455            }
1456    
1457            protected Document getBaseModelDocument(
1458                            String portletId, BaseModel<?> baseModel)
1459                    throws SystemException {
1460    
1461                    return getBaseModelDocument(portletId, baseModel, baseModel);
1462            }
1463    
1464            protected Document getBaseModelDocument(
1465                            String portletId, BaseModel<?> baseModel,
1466                            BaseModel<?> workflowedBaseModel)
1467                    throws SystemException {
1468    
1469                    Document document = newDocument();
1470    
1471                    String className = baseModel.getModelClassName();
1472    
1473                    long classPK = 0;
1474                    long resourcePrimKey = 0;
1475    
1476                    if (baseModel instanceof ResourcedModel) {
1477                            ResourcedModel resourcedModel = (ResourcedModel)baseModel;
1478    
1479                            classPK = resourcedModel.getResourcePrimKey();
1480                            resourcePrimKey = resourcedModel.getResourcePrimKey();
1481                    }
1482                    else {
1483                            classPK = (Long)baseModel.getPrimaryKeyObj();
1484                    }
1485    
1486                    document.addUID(portletId, classPK);
1487    
1488                    List<AssetCategory> assetCategories =
1489                            AssetCategoryLocalServiceUtil.getCategories(className, classPK);
1490    
1491                    long[] assetCategoryIds = StringUtil.split(
1492                            ListUtil.toString(
1493                                    assetCategories, AssetCategory.CATEGORY_ID_ACCESSOR),
1494                            0L);
1495    
1496                    document.addKeyword(Field.ASSET_CATEGORY_IDS, assetCategoryIds);
1497    
1498                    addSearchAssetCategoryTitles(
1499                            document, Field.ASSET_CATEGORY_TITLES, assetCategories);
1500    
1501                    long classNameId = PortalUtil.getClassNameId(className);
1502    
1503                    List<AssetTag> assetTags = AssetTagLocalServiceUtil.getTags(
1504                            classNameId, classPK);
1505    
1506                    String[] assetTagNames = StringUtil.split(
1507                            ListUtil.toString(assetTags, AssetTag.NAME_ACCESSOR));
1508    
1509                    document.addText(Field.ASSET_TAG_NAMES, assetTagNames);
1510    
1511                    long[] assetTagsIds = StringUtil.split(
1512                            ListUtil.toString(assetTags, AssetTag.TAG_ID_ACCESSOR), 0L);
1513    
1514                    document.addKeyword(Field.ASSET_TAG_IDS, assetTagsIds);
1515    
1516                    document.addKeyword(Field.ENTRY_CLASS_NAME, className);
1517                    document.addKeyword(Field.ENTRY_CLASS_PK, classPK);
1518                    document.addKeyword(Field.PORTLET_ID, portletId);
1519    
1520                    if (resourcePrimKey > 0) {
1521                            document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, resourcePrimKey);
1522                    }
1523    
1524                    if (baseModel instanceof AttachedModel) {
1525                            AttachedModel attachedModel = (AttachedModel)baseModel;
1526    
1527                            document.addKeyword(
1528                                    Field.CLASS_NAME_ID, attachedModel.getClassNameId());
1529                            document.addKeyword(Field.CLASS_PK, attachedModel.getClassPK());
1530                    }
1531    
1532                    if (baseModel instanceof AuditedModel) {
1533                            AuditedModel auditedModel = (AuditedModel)baseModel;
1534    
1535                            document.addKeyword(Field.COMPANY_ID, auditedModel.getCompanyId());
1536                            document.addDate(Field.CREATE_DATE, auditedModel.getCreateDate());
1537                            document.addDate(
1538                                    Field.MODIFIED_DATE, auditedModel.getModifiedDate());
1539                            document.addKeyword(Field.USER_ID, auditedModel.getUserId());
1540    
1541                            String userName = PortalUtil.getUserName(
1542                                    auditedModel.getUserId(), auditedModel.getUserName());
1543    
1544                            document.addKeyword(Field.USER_NAME, userName, true);
1545                    }
1546    
1547                    GroupedModel groupedModel = null;
1548    
1549                    if (baseModel instanceof GroupedModel) {
1550                            groupedModel = (GroupedModel)baseModel;
1551    
1552                            document.addKeyword(
1553                                    Field.GROUP_ID, getSiteGroupId(groupedModel.getGroupId()));
1554                            document.addKeyword(
1555                                    Field.SCOPE_GROUP_ID, groupedModel.getGroupId());
1556                    }
1557    
1558                    if (workflowedBaseModel instanceof WorkflowedModel) {
1559                            WorkflowedModel workflowedModel =
1560                                    (WorkflowedModel)workflowedBaseModel;
1561    
1562                            document.addKeyword(Field.STATUS, workflowedModel.getStatus());
1563                    }
1564    
1565                    if ((groupedModel != null) && (baseModel instanceof TrashedModel)) {
1566                            TrashedModel trashedModel = (TrashedModel)baseModel;
1567    
1568                            if (trashedModel.isInTrash()) {
1569                                    addTrashFields(document, trashedModel);
1570                            }
1571                    }
1572    
1573                    addAssetFields(document, className, classPK);
1574    
1575                    ExpandoBridgeIndexerUtil.addAttributes(
1576                            document, baseModel.getExpandoBridge());
1577    
1578                    return document;
1579            }
1580    
1581            protected String getClassName(SearchContext searchContext) {
1582                    String[] classNames = getClassNames();
1583    
1584                    return classNames[0];
1585            }
1586    
1587            protected Set<String> getLocalizedCountryNames(Country country) {
1588                    Set<String> countryNames = new HashSet<String>();
1589    
1590                    Locale[] locales = LanguageUtil.getAvailableLocales();
1591    
1592                    for (Locale locale : locales) {
1593                            String countryName = country.getName(locale);
1594    
1595                            countryName = StringUtil.toLowerCase(countryName);
1596    
1597                            countryNames.add(countryName);
1598                    }
1599    
1600                    return countryNames;
1601            }
1602    
1603            /**
1604             * @deprecated As of 6.2.0 renamed to {@link #getSiteGroupId(long)}
1605             */
1606            protected long getParentGroupId(long groupId) {
1607                    return getSiteGroupId(groupId);
1608            }
1609    
1610            protected abstract String getPortletId(SearchContext searchContext);
1611    
1612            protected long getSiteGroupId(long groupId) {
1613                    long siteGroupId = groupId;
1614    
1615                    try {
1616                            Group group = GroupLocalServiceUtil.getGroup(groupId);
1617    
1618                            if (group.isLayout()) {
1619                                    siteGroupId = group.getParentGroupId();
1620                            }
1621                    }
1622                    catch (Exception e) {
1623                    }
1624    
1625                    return siteGroupId;
1626            }
1627    
1628            protected Locale getSnippetLocale(Document document, Locale locale) {
1629                    String prefix = Field.SNIPPET + StringPool.UNDERLINE;
1630    
1631                    String localizedAssetCategoryTitlesName =
1632                            prefix +
1633                            DocumentImpl.getLocalizedName(locale, Field.ASSET_CATEGORY_TITLES);
1634                    String localizedContentName =
1635                            prefix + DocumentImpl.getLocalizedName(locale, Field.CONTENT);
1636                    String localizedDescriptionName =
1637                            prefix + DocumentImpl.getLocalizedName(locale, Field.DESCRIPTION);
1638                    String localizedTitleName =
1639                            prefix + DocumentImpl.getLocalizedName(locale, Field.TITLE);
1640    
1641                    if ((document.getField(localizedAssetCategoryTitlesName) != null) ||
1642                            (document.getField(localizedContentName) != null) ||
1643                            (document.getField(localizedDescriptionName) != null) ||
1644                            (document.getField(localizedTitleName) != null)) {
1645    
1646                            return locale;
1647                    }
1648    
1649                    return null;
1650            }
1651    
1652            protected boolean isUseSearchResultPermissionFilter(
1653                    SearchContext searchContext) {
1654    
1655                    return isFilterSearch();
1656            }
1657    
1658            protected boolean isVisible(int entryStatus, int queryStatus) {
1659                    if (((queryStatus != WorkflowConstants.STATUS_ANY) &&
1660                             (entryStatus == queryStatus)) ||
1661                            (entryStatus != WorkflowConstants.STATUS_IN_TRASH)) {
1662    
1663                            return true;
1664                    }
1665    
1666                    return false;
1667            }
1668    
1669            protected Document newDocument() {
1670                    return (Document)_document.clone();
1671            }
1672    
1673            protected void populateAddresses(
1674                            Document document, List<Address> addresses, long regionId,
1675                            long countryId)
1676                    throws PortalException, SystemException {
1677    
1678                    List<String> cities = new ArrayList<String>();
1679    
1680                    List<String> countries = new ArrayList<String>();
1681    
1682                    if (countryId > 0) {
1683                            try {
1684                                    Country country = CountryServiceUtil.getCountry(countryId);
1685    
1686                                    countries.addAll(getLocalizedCountryNames(country));
1687                            }
1688                            catch (NoSuchCountryException nsce) {
1689                                    if (_log.isWarnEnabled()) {
1690                                            _log.warn(nsce.getMessage());
1691                                    }
1692                            }
1693                    }
1694    
1695                    List<String> regions = new ArrayList<String>();
1696    
1697                    if (regionId > 0) {
1698                            try {
1699                                    Region region = RegionServiceUtil.getRegion(regionId);
1700    
1701                                    regions.add(StringUtil.toLowerCase(region.getName()));
1702                            }
1703                            catch (NoSuchRegionException nsre) {
1704                                    if (_log.isWarnEnabled()) {
1705                                            _log.warn(nsre.getMessage());
1706                                    }
1707                            }
1708                    }
1709    
1710                    List<String> streets = new ArrayList<String>();
1711                    List<String> zips = new ArrayList<String>();
1712    
1713                    for (Address address : addresses) {
1714                            cities.add(StringUtil.toLowerCase(address.getCity()));
1715                            countries.addAll(getLocalizedCountryNames(address.getCountry()));
1716                            regions.add(StringUtil.toLowerCase(address.getRegion().getName()));
1717                            streets.add(StringUtil.toLowerCase(address.getStreet1()));
1718                            streets.add(StringUtil.toLowerCase(address.getStreet2()));
1719                            streets.add(StringUtil.toLowerCase(address.getStreet3()));
1720                            zips.add(StringUtil.toLowerCase(address.getZip()));
1721                    }
1722    
1723                    document.addText("city", cities.toArray(new String[cities.size()]));
1724                    document.addText(
1725                            "country", countries.toArray(new String[countries.size()]));
1726                    document.addText("region", regions.toArray(new String[regions.size()]));
1727                    document.addText("street", streets.toArray(new String[streets.size()]));
1728                    document.addText("zip", zips.toArray(new String[zips.size()]));
1729            }
1730    
1731            protected Map<Locale, String> populateMap(
1732                    AssetEntry assetEntry, Map<Locale, String> map) {
1733    
1734                    Locale[] availableLocales = LanguageUtil.getAvailableLocales(
1735                            assetEntry.getGroupId());
1736    
1737                    String defaultValue = map.get(
1738                            LocaleUtil.fromLanguageId(assetEntry.getDefaultLanguageId()));
1739    
1740                    for (Locale availableLocale : availableLocales) {
1741                            if (!map.containsKey(availableLocale) ||
1742                                    Validator.isNull(map.get(availableLocale))) {
1743    
1744                                    map.put(availableLocale, defaultValue);
1745                            }
1746                    }
1747    
1748                    return map;
1749            }
1750    
1751            protected void postProcessFullQuery(
1752                            BooleanQuery fullQuery, SearchContext searchContext)
1753                    throws Exception {
1754            }
1755    
1756            protected void processHits(SearchContext searchContext, Hits hits)
1757                    throws SearchException {
1758    
1759                    HitsProcessor hitsProcessor =
1760                            HitsProcessorRegistryUtil.getDefaultHitsProcessor();
1761    
1762                    if (hitsProcessor != null) {
1763                            hitsProcessor.process(searchContext, hits);
1764                    }
1765            }
1766    
1767            protected void setFilterSearch(boolean filterSearch) {
1768                    _filterSearch = filterSearch;
1769            }
1770    
1771            protected void setIndexerEnabled(boolean indexerEnabled) {
1772                    _indexerEnabled = indexerEnabled;
1773            }
1774    
1775            protected void setPermissionAware(boolean permissionAware) {
1776                    _permissionAware = permissionAware;
1777            }
1778    
1779            protected void setSortableTextFields(String[] sortableTextFields) {
1780                    _document.setSortableTextFields(sortableTextFields);
1781            }
1782    
1783            protected void setStagingAware(boolean stagingAware) {
1784                    _stagingAware = stagingAware;
1785            }
1786    
1787            private boolean _isInTrashExplicitly(TrashedModel trashedModel)
1788                    throws SystemException {
1789    
1790                    if (!trashedModel.isInTrash()) {
1791                            return false;
1792                    }
1793    
1794                    ClassedModel classedModel = (ClassedModel)trashedModel;
1795    
1796                    TrashEntry trashEntry = TrashEntryLocalServiceUtil.fetchEntry(
1797                            classedModel.getModelClassName(),
1798                            trashedModel.getTrashEntryClassPK());
1799    
1800                    if (trashEntry != null) {
1801                            return true;
1802                    }
1803    
1804                    return false;
1805            }
1806    
1807            private static Log _log = LogFactoryUtil.getLog(BaseIndexer.class);
1808    
1809            private boolean _commitImmediately;
1810            private Document _document = new DocumentImpl();
1811            private boolean _filterSearch;
1812            private boolean _indexerEnabled = true;
1813            private IndexerPostProcessor[] _indexerPostProcessors =
1814                    new IndexerPostProcessor[0];
1815            private boolean _permissionAware;
1816            private String _searchEngineId;
1817            private boolean _stagingAware = true;
1818    
1819    }