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