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