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