001    /**
002     * Copyright (c) 2000-2011 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.dao.orm.QueryUtil;
021    import com.liferay.portal.kernel.exception.PortalException;
022    import com.liferay.portal.kernel.exception.SystemException;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.search.facet.AssetEntriesFacet;
026    import com.liferay.portal.kernel.search.facet.Facet;
027    import com.liferay.portal.kernel.search.facet.MultiValueFacet;
028    import com.liferay.portal.kernel.search.facet.ScopeFacet;
029    import com.liferay.portal.kernel.util.GetterUtil;
030    import com.liferay.portal.kernel.util.ListUtil;
031    import com.liferay.portal.kernel.util.PropsKeys;
032    import com.liferay.portal.kernel.util.PropsUtil;
033    import com.liferay.portal.kernel.util.SetUtil;
034    import com.liferay.portal.kernel.util.StringUtil;
035    import com.liferay.portal.kernel.util.Time;
036    import com.liferay.portal.kernel.util.UnicodeProperties;
037    import com.liferay.portal.kernel.util.Validator;
038    import com.liferay.portal.model.Address;
039    import com.liferay.portal.model.AttachedModel;
040    import com.liferay.portal.model.AuditedModel;
041    import com.liferay.portal.model.BaseModel;
042    import com.liferay.portal.model.Country;
043    import com.liferay.portal.model.Group;
044    import com.liferay.portal.model.GroupedModel;
045    import com.liferay.portal.model.Region;
046    import com.liferay.portal.model.ResourcedModel;
047    import com.liferay.portal.model.WorkflowedModel;
048    import com.liferay.portal.security.permission.ActionKeys;
049    import com.liferay.portal.security.permission.PermissionChecker;
050    import com.liferay.portal.security.permission.PermissionThreadLocal;
051    import com.liferay.portal.service.CountryServiceUtil;
052    import com.liferay.portal.service.GroupLocalServiceUtil;
053    import com.liferay.portal.service.RegionServiceUtil;
054    import com.liferay.portal.util.PortalUtil;
055    import com.liferay.portlet.asset.model.AssetCategory;
056    import com.liferay.portlet.asset.service.AssetCategoryLocalServiceUtil;
057    import com.liferay.portlet.asset.service.AssetTagLocalServiceUtil;
058    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
059    import com.liferay.portlet.dynamicdatamapping.util.DDMIndexerUtil;
060    import com.liferay.portlet.expando.model.ExpandoBridge;
061    import com.liferay.portlet.expando.model.ExpandoColumnConstants;
062    import com.liferay.portlet.expando.util.ExpandoBridgeFactoryUtil;
063    import com.liferay.portlet.expando.util.ExpandoBridgeIndexerUtil;
064    
065    import java.util.ArrayList;
066    import java.util.List;
067    import java.util.Locale;
068    import java.util.Map;
069    import java.util.Set;
070    
071    import javax.portlet.PortletURL;
072    
073    /**
074     * @author Brian Wing Shun Chan
075     * @author Hugo Huijser
076     * @author Ryan Park
077     * @author Raymond Augé
078     */
079    public abstract class BaseIndexer implements Indexer {
080    
081            public static final int INDEX_FILTER_SEARCH_LIMIT = GetterUtil.getInteger(
082                    PropsUtil.get(PropsKeys.INDEX_FILTER_SEARCH_LIMIT));
083    
084            public void delete(long companyId, String uid) throws SearchException {
085                    try {
086                            SearchEngineUtil.deleteDocument(companyId, uid);
087                    }
088                    catch (SearchException se) {
089                            throw se;
090                    }
091                    catch (Exception e) {
092                            throw new SearchException(e);
093                    }
094            }
095    
096            public void delete(Object obj) throws SearchException {
097                    try {
098                            doDelete(obj);
099                    }
100                    catch (SearchException se) {
101                            throw se;
102                    }
103                    catch (Exception e) {
104                            throw new SearchException(e);
105                    }
106            }
107    
108            public Document getDocument(Object obj) throws SearchException {
109                    try {
110                            Document document = doGetDocument(obj);
111    
112                            for (IndexerPostProcessor indexerPostProcessor :
113                                            _indexerPostProcessors) {
114    
115                                    indexerPostProcessor.postProcessDocument(document, obj);
116                            }
117    
118                            if (document == null) {
119                                    return null;
120                            }
121    
122                            Map<String, Field> fields = document.getFields();
123    
124                            Field groupIdField = fields.get(Field.GROUP_ID);
125    
126                            if (groupIdField != null) {
127                                    long groupId = GetterUtil.getLong(groupIdField.getValue());
128    
129                                    addStagingGroupKeyword(document, groupId);
130                            }
131    
132                            return document;
133                    }
134                    catch (SearchException se) {
135                            throw se;
136                    }
137                    catch (Exception e) {
138                            throw new SearchException(e);
139                    }
140            }
141    
142            public BooleanQuery getFacetQuery(
143                            String className, SearchContext searchContext)
144                    throws Exception {
145    
146                    BooleanQuery facetQuery = BooleanQueryFactoryUtil.create(searchContext);
147    
148                    facetQuery.addExactTerm(Field.ENTRY_CLASS_NAME, className);
149    
150                    if (searchContext.getUserId() > 0) {
151                            SearchPermissionChecker searchPermissionChecker =
152                                    SearchEngineUtil.getSearchPermissionChecker();
153    
154                            facetQuery =
155                                    (BooleanQuery)searchPermissionChecker.getPermissionQuery(
156                                            searchContext.getCompanyId(), searchContext.getGroupIds(),
157                                            searchContext.getUserId(), className, facetQuery,
158                                            searchContext);
159                    }
160    
161                    return facetQuery;
162            }
163    
164            public BooleanQuery getFullQuery(SearchContext searchContext)
165                    throws SearchException {
166    
167                    try {
168                            searchContext.setSearchEngineId(getSearchEngineId());
169    
170                            searchContext.setEntryClassNames(
171                                    new String[] {getClassName(searchContext)});
172    
173                            BooleanQuery contextQuery = BooleanQueryFactoryUtil.create(
174                                    searchContext);
175    
176                            addSearchAssetCategoryIds(contextQuery, searchContext);
177                            addSearchAssetTagNames(contextQuery, searchContext);
178                            addSearchEntryClassNames(contextQuery, searchContext);
179                            addSearchGroupId(contextQuery, searchContext);
180    
181                            BooleanQuery fullQuery = createFullQuery(
182                                    contextQuery, searchContext);
183    
184                            fullQuery.setQueryConfig(searchContext.getQueryConfig());
185    
186                            return fullQuery;
187                    }
188                    catch (SearchException se) {
189                            throw se;
190                    }
191                    catch (Exception e) {
192                            throw new SearchException(e);
193                    }
194            }
195    
196            public IndexerPostProcessor[] getIndexerPostProcessors() {
197                    return _indexerPostProcessors;
198            }
199    
200            public String getSearchEngineId() {
201                    return SearchEngineUtil.SYSTEM_ENGINE_ID;
202            }
203    
204            public String getSortField(String orderByCol) {
205                    String sortField = doGetSortField(orderByCol);
206    
207                    if (DocumentImpl.isSortableTextField(sortField)) {
208                            return DocumentImpl.getSortableFieldName(sortField);
209                    }
210    
211                    return sortField;
212            }
213    
214            public Summary getSummary(
215                            Document document, Locale locale, String snippet,
216                            PortletURL portletURL)
217                    throws SearchException {
218    
219                    try {
220                            Summary summary = doGetSummary(
221                                    document, locale, snippet, portletURL);
222    
223                            for (IndexerPostProcessor indexerPostProcessor :
224                                            _indexerPostProcessors) {
225    
226                                    indexerPostProcessor.postProcessSummary(
227                                            summary, document, locale, snippet, portletURL);
228                            }
229    
230                            return summary;
231                    }
232                    catch (SearchException se) {
233                            throw se;
234                    }
235                    catch (Exception e) {
236                            throw new SearchException(e);
237                    }
238            }
239    
240            public boolean hasPermission(
241                            PermissionChecker permissionChecker, long entryClassPK,
242                            String actionId)
243                    throws Exception {
244    
245                    return true;
246            }
247    
248            public boolean isFilterSearch() {
249                    return _FILTER_SEARCH;
250            }
251    
252            public boolean isIndexerEnabled() {
253                    return _INDEXER_ENABLED;
254            }
255    
256            public boolean isPermissionAware() {
257                    return _PERMISSION_AWARE;
258            }
259    
260            public boolean isStagingAware() {
261                    return _stagingAware;
262            }
263    
264            public void postProcessContextQuery(
265                            BooleanQuery contextQuery, SearchContext searchContext)
266                    throws Exception {
267            }
268    
269            public void postProcessSearchQuery(
270                            BooleanQuery searchQuery, SearchContext searchContext)
271                    throws Exception {
272            }
273    
274            public void registerIndexerPostProcessor(
275                    IndexerPostProcessor indexerPostProcessor) {
276    
277                    List<IndexerPostProcessor> indexerPostProcessorsList =
278                            ListUtil.fromArray(_indexerPostProcessors);
279    
280                    indexerPostProcessorsList.add(indexerPostProcessor);
281    
282                    _indexerPostProcessors = indexerPostProcessorsList.toArray(
283                            new IndexerPostProcessor[indexerPostProcessorsList.size()]);
284            }
285    
286            public void reindex(Object obj) throws SearchException {
287                    try {
288                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled()) {
289                                    return;
290                            }
291    
292                            doReindex(obj);
293                    }
294                    catch (SearchException se) {
295                            throw se;
296                    }
297                    catch (Exception e) {
298                            throw new SearchException(e);
299                    }
300            }
301    
302            public void reindex(String className, long classPK) throws SearchException {
303                    try {
304                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled()) {
305                                    return;
306                            }
307    
308                            doReindex(className, classPK);
309                    }
310                    catch (NoSuchModelException nsme) {
311                            if (_log.isWarnEnabled()) {
312                                    _log.warn("Unable to index " + className + " " + classPK);
313                            }
314                    }
315                    catch (SearchException se) {
316                            throw se;
317                    }
318                    catch (Exception e) {
319                            throw new SearchException(e);
320                    }
321            }
322    
323            public void reindex(String[] ids) throws SearchException {
324                    try {
325                            if (SearchEngineUtil.isIndexReadOnly() || !isIndexerEnabled()) {
326                                    return;
327                            }
328    
329                            doReindex(ids);
330                    }
331                    catch (SearchException se) {
332                            throw se;
333                    }
334                    catch (Exception e) {
335                            throw new SearchException(e);
336                    }
337            }
338    
339            public Hits search(SearchContext searchContext) throws SearchException {
340                    try {
341                            BooleanQuery fullQuery = getFullQuery(searchContext);
342    
343                            fullQuery.setQueryConfig(searchContext.getQueryConfig());
344    
345                            PermissionChecker permissionChecker =
346                                    PermissionThreadLocal.getPermissionChecker();
347    
348                            int start = searchContext.getStart();
349                            int end = searchContext.getEnd();
350    
351                            if (isFilterSearch() && (permissionChecker != null)) {
352                                    searchContext.setStart(0);
353                                    searchContext.setEnd(end + INDEX_FILTER_SEARCH_LIMIT);
354                            }
355    
356                            Hits hits = SearchEngineUtil.search(searchContext, fullQuery);
357    
358                            searchContext.setStart(start);
359                            searchContext.setEnd(end);
360    
361                            if (isFilterSearch() && (permissionChecker != null)) {
362                                    hits = filterSearch(hits, permissionChecker, searchContext);
363                            }
364    
365                            return hits;
366                    }
367                    catch (SearchException se) {
368                            throw se;
369                    }
370                    catch (Exception e) {
371                            throw new SearchException(e);
372                    }
373            }
374    
375            public void unregisterIndexerPostProcessor(
376                    IndexerPostProcessor indexerPostProcessor) {
377    
378                    List<IndexerPostProcessor> indexerPostProcessorsList =
379                            ListUtil.fromArray(_indexerPostProcessors);
380    
381                    ListUtil.remove(indexerPostProcessorsList, indexerPostProcessor);
382    
383                    _indexerPostProcessors = indexerPostProcessorsList.toArray(
384                            new IndexerPostProcessor[indexerPostProcessorsList.size()]);
385            }
386    
387            protected void addLocalizedSearchTerm(
388                            BooleanQuery searchQuery, SearchContext searchContext,
389                            String field, boolean like)
390                    throws Exception {
391    
392                    addSearchTerm(searchQuery, searchContext, field, like);
393                    addSearchTerm(
394                            searchQuery, searchContext,
395                            DocumentImpl.getLocalizedName(searchContext.getLocale(), field),
396                            like);
397            }
398    
399            protected void addSearchAssetCategoryIds(
400                            BooleanQuery contextQuery, SearchContext searchContext)
401                    throws Exception {
402    
403                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
404    
405                    multiValueFacet.setFieldName(Field.ASSET_CATEGORY_IDS);
406                    multiValueFacet.setStatic(true);
407    
408                    searchContext.addFacet(multiValueFacet);
409            }
410    
411            protected void addSearchAssetTagNames(
412                            BooleanQuery contextQuery, SearchContext searchContext)
413                    throws Exception {
414    
415                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
416    
417                    multiValueFacet.setFieldName(Field.ASSET_TAG_NAMES);
418                    multiValueFacet.setStatic(true);
419    
420                    searchContext.addFacet(multiValueFacet);
421            }
422    
423            protected void addSearchDDMStruture(
424                            BooleanQuery searchQuery, SearchContext searchContext,
425                            DDMStructure ddmStructure)
426                    throws Exception {
427    
428                    Set<String> fieldNames = ddmStructure.getFieldNames();
429    
430                    for (String fieldName : fieldNames) {
431                            String name = DDMIndexerUtil.encodeName(
432                                    ddmStructure.getStructureId(), fieldName);
433    
434                            addSearchTerm(searchQuery, searchContext, name, false);
435                    }
436            }
437    
438            protected void addSearchEntryClassNames(
439                            BooleanQuery contextQuery, SearchContext searchContext)
440                    throws Exception {
441    
442                    Facet facet = new AssetEntriesFacet(searchContext);
443    
444                    facet.setStatic(true);
445    
446                    searchContext.addFacet(facet);
447            }
448    
449            protected void addSearchExpando(
450                            BooleanQuery searchQuery, SearchContext searchContext,
451                            String keywords)
452                    throws Exception {
453    
454                    ExpandoBridge expandoBridge =
455                            ExpandoBridgeFactoryUtil.getExpandoBridge(
456                                    searchContext.getCompanyId(), getClassName(searchContext));
457    
458                    Set<String> attributeNames = SetUtil.fromEnumeration(
459                            expandoBridge.getAttributeNames());
460    
461                    for (String attributeName : attributeNames) {
462                            UnicodeProperties properties = expandoBridge.getAttributeProperties(
463                                    attributeName);
464    
465                            int indexType = GetterUtil.getInteger(
466                                    properties.getProperty(ExpandoColumnConstants.INDEX_TYPE));
467    
468                            if (indexType != ExpandoColumnConstants.INDEX_TYPE_NONE) {
469                                    String fieldName = ExpandoBridgeIndexerUtil.encodeFieldName(
470                                            attributeName);
471    
472                                    if (Validator.isNotNull(keywords)) {
473                                            if (searchContext.isAndSearch()) {
474                                                    searchQuery.addRequiredTerm(fieldName, keywords);
475                                            }
476                                            else {
477                                                    searchQuery.addTerm(fieldName, keywords);
478                                            }
479                                    }
480                            }
481                    }
482            }
483    
484            protected void addSearchGroupId(
485                            BooleanQuery contextQuery, SearchContext searchContext)
486                    throws Exception {
487    
488                    Facet facet = new ScopeFacet(searchContext);
489    
490                    facet.setStatic(true);
491    
492                    searchContext.addFacet(facet);
493            }
494    
495            protected void addSearchKeywords(
496                            BooleanQuery searchQuery, SearchContext searchContext)
497                    throws Exception {
498    
499                    String keywords = searchContext.getKeywords();
500    
501                    if (Validator.isNull(keywords)) {
502                            return;
503                    }
504    
505                    searchQuery.addTerms(Field.KEYWORDS, keywords);
506    
507                    addSearchExpando(searchQuery, searchContext, keywords);
508            }
509    
510            protected void addSearchTerm(
511                            BooleanQuery searchQuery, SearchContext searchContext,
512                            String field, boolean like)
513                    throws Exception {
514    
515                    if (Validator.isNull(field)) {
516                            return;
517                    }
518    
519                    String value = String.valueOf(searchContext.getAttribute(field));
520    
521                    if (Validator.isNull(value)) {
522                            value = searchContext.getKeywords();
523                    }
524    
525                    if (Validator.isNull(value)) {
526                            return;
527                    }
528    
529                    if (searchContext.isAndSearch()) {
530                            searchQuery.addRequiredTerm(field, value, like);
531                    }
532                    else {
533                            searchQuery.addTerm(field, value, like);
534                    }
535            }
536    
537            protected void addStagingGroupKeyword(Document document, long groupId)
538                    throws Exception {
539    
540                    if (!isStagingAware()) {
541                            return;
542                    }
543    
544                    boolean stagingGroup = false;
545    
546                    Group group = GroupLocalServiceUtil.getGroup(groupId);
547    
548                    if (group.isLayout()) {
549                            group = GroupLocalServiceUtil.getGroup(group.getParentGroupId());
550                    }
551    
552                    if (group.isStagingGroup()) {
553                            stagingGroup = true;
554                    }
555    
556                    document.addKeyword(Field.STAGING_GROUP, stagingGroup);
557            }
558    
559            protected BooleanQuery createFullQuery(
560                            BooleanQuery contextQuery, SearchContext searchContext)
561                    throws Exception {
562    
563                    BooleanQuery searchQuery = BooleanQueryFactoryUtil.create(
564                            searchContext);
565    
566                    addSearchKeywords(searchQuery, searchContext);
567                    postProcessSearchQuery(searchQuery, searchContext);
568    
569                    for (IndexerPostProcessor indexerPostProcessor :
570                                    _indexerPostProcessors) {
571    
572                            indexerPostProcessor.postProcessSearchQuery(
573                                    searchQuery, searchContext);
574                    }
575    
576                    Map<String, Facet> facets = searchContext.getFacets();
577    
578                    for (Facet facet : facets.values()) {
579                            BooleanClause facetClause = facet.getFacetClause();
580    
581                            if (facetClause != null) {
582                                    contextQuery.add(
583                                            facetClause.getQuery(),
584                                            facetClause.getBooleanClauseOccur());
585                            }
586                    }
587    
588                    BooleanQuery fullQuery = BooleanQueryFactoryUtil.create(searchContext);
589    
590                    fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
591    
592                    if (searchQuery.hasClauses()) {
593                            fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
594                    }
595    
596                    BooleanClause[] booleanClauses = searchContext.getBooleanClauses();
597    
598                    if (booleanClauses != null) {
599                            for (BooleanClause booleanClause : booleanClauses) {
600                                    fullQuery.add(
601                                            booleanClause.getQuery(),
602                                            booleanClause.getBooleanClauseOccur());
603                            }
604                    }
605    
606                    postProcessFullQuery(fullQuery, searchContext);
607    
608                    for (IndexerPostProcessor indexerPostProcessor :
609                                    _indexerPostProcessors) {
610    
611                            indexerPostProcessor.postProcessFullQuery(fullQuery, searchContext);
612                    }
613    
614                    return fullQuery;
615            }
616    
617            protected void deleteDocument(long companyId, long field1)
618                    throws Exception {
619    
620                    deleteDocument(companyId, String.valueOf(field1));
621            }
622    
623            protected void deleteDocument(long companyId, long field1, String field2)
624                    throws Exception {
625    
626                    deleteDocument(companyId, String.valueOf(field1), field2);
627            }
628    
629            protected void deleteDocument(long companyId, String field1)
630                    throws Exception {
631    
632                    Document document = new DocumentImpl();
633    
634                    document.addUID(getPortletId(), field1);
635    
636                    SearchEngineUtil.deleteDocument(companyId, document.get(Field.UID));
637            }
638    
639            protected void deleteDocument(long companyId, String field1, String field2)
640                    throws Exception {
641    
642                    Document document = new DocumentImpl();
643    
644                    document.addUID(getPortletId(), field1, field2);
645    
646                    SearchEngineUtil.deleteDocument(companyId, document.get(Field.UID));
647            }
648    
649            protected abstract void doDelete(Object obj) throws Exception;
650    
651            protected abstract Document doGetDocument(Object obj) throws Exception;
652    
653            protected String doGetSortField(String orderByCol) {
654                    return orderByCol;
655            }
656    
657            protected abstract Summary doGetSummary(
658                            Document document, Locale locale, String snippet,
659                            PortletURL portletURL)
660                    throws Exception;
661    
662            protected abstract void doReindex(Object obj) throws Exception;
663    
664            protected abstract void doReindex(String className, long classPK)
665                    throws Exception;
666    
667            protected abstract void doReindex(String[] ids) throws Exception;
668    
669            protected Hits filterSearch(
670                    Hits hits, PermissionChecker permissionChecker,
671                    SearchContext searchContext) {
672    
673                    List<Document> docs = new ArrayList<Document>();
674                    List<Float> scores = new ArrayList<Float>();
675    
676                    int start = searchContext.getStart();
677                    int end = searchContext.getEnd();
678    
679                    String paginationType = GetterUtil.getString(
680                            searchContext.getAttribute("paginationType"), "more");
681    
682                    boolean hasMore = false;
683    
684                    Document[] documents = hits.getDocs();
685    
686                    for (int i = 0; i < documents.length; i++) {
687                            try {
688                                    Document document = documents[i];
689    
690                                    String entryClassName = document.get(Field.ENTRY_CLASS_NAME);
691                                    long entryClassPK = GetterUtil.getLong(
692                                            document.get(Field.ENTRY_CLASS_PK));
693    
694                                    Indexer indexer = IndexerRegistryUtil.getIndexer(
695                                            entryClassName);
696    
697                                    if ((indexer.isFilterSearch() && indexer.hasPermission(
698                                                    permissionChecker, entryClassPK, ActionKeys.VIEW)) ||
699                                            !indexer.isFilterSearch() ||
700                                            !indexer.isPermissionAware()) {
701    
702                                            docs.add(document);
703                                            scores.add(hits.score(i));
704                                    }
705                            }
706                            catch (Exception e) {
707                            }
708    
709                            if (paginationType.equals("more") && (docs.size() > end)) {
710                                    hasMore = true;
711    
712                                    break;
713                            }
714                    }
715    
716                    int length = docs.size();
717    
718                    if (hasMore) {
719                            length = length + (end - start);
720                    }
721    
722                    hits.setLength(length);
723    
724                    if ((start != QueryUtil.ALL_POS) && (end != QueryUtil.ALL_POS)) {
725                            if (end > length) {
726                                    end = length;
727                            }
728    
729                            docs = docs.subList(start, end);
730                    }
731    
732                    hits.setDocs(docs.toArray(new Document[docs.size()]));
733                    hits.setScores(scores.toArray(new Float[docs.size()]));
734    
735                    hits.setSearchTime(
736                            (float)(System.currentTimeMillis() - hits.getStart()) /
737                                    Time.SECOND);
738    
739                    return hits;
740            }
741    
742            protected Document getBaseModelDocument(
743                            String portletId, BaseModel<?> baseModel)
744                    throws SystemException {
745    
746                    Document document = new DocumentImpl();
747    
748                    String className = baseModel.getModelClassName();
749    
750                    long classPK = 0;
751                    long resourcePrimKey = 0;
752    
753                    if (baseModel instanceof ResourcedModel) {
754                            ResourcedModel resourcedModel = (ResourcedModel)baseModel;
755    
756                            classPK = resourcedModel.getResourcePrimKey();
757                            resourcePrimKey = resourcedModel.getResourcePrimKey();
758                    }
759                    else {
760                            classPK = (Long)baseModel.getPrimaryKeyObj();
761                    }
762    
763                    document.addUID(portletId, classPK);
764    
765                    List<AssetCategory> assetCategories =
766                            AssetCategoryLocalServiceUtil.getCategories(className, classPK);
767    
768                    long[] assetCategoryIds = StringUtil.split(
769                            ListUtil.toString(
770                                    assetCategories, AssetCategory.CATEGORY_ID_ACCESSOR),
771                            0L);
772    
773                    document.addKeyword(Field.ASSET_CATEGORY_IDS, assetCategoryIds);
774    
775                    String[] assetCategoryNames = StringUtil.split(
776                            ListUtil.toString(assetCategories, AssetCategory.NAME_ACCESSOR));
777    
778                    document.addText(Field.ASSET_CATEGORY_NAMES, assetCategoryNames);
779    
780                    String[] assetTagNames = AssetTagLocalServiceUtil.getTagNames(
781                            className, classPK);
782    
783                    document.addText(Field.ASSET_TAG_NAMES, assetTagNames);
784    
785                    document.addKeyword(Field.ENTRY_CLASS_NAME, className);
786                    document.addKeyword(Field.ENTRY_CLASS_PK, classPK);
787                    document.addKeyword(Field.PORTLET_ID, portletId);
788    
789                    if (resourcePrimKey > 0) {
790                            document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, resourcePrimKey);
791                    }
792    
793                    if (baseModel instanceof AttachedModel) {
794                            AttachedModel attachedModel = (AttachedModel)baseModel;
795    
796                            document.addKeyword(
797                                    Field.CLASS_NAME_ID, attachedModel.getClassNameId());
798                            document.addKeyword(Field.CLASS_PK, attachedModel.getClassPK());
799                    }
800    
801                    if (baseModel instanceof AuditedModel) {
802                            AuditedModel auditedModel = (AuditedModel)baseModel;
803    
804                            document.addKeyword(Field.COMPANY_ID, auditedModel.getCompanyId());
805                            document.addDate(Field.CREATE_DATE, auditedModel.getCreateDate());
806                            document.addDate(
807                                    Field.MODIFIED_DATE, auditedModel.getModifiedDate());
808                            document.addKeyword(Field.USER_ID, auditedModel.getUserId());
809    
810                            String userName = PortalUtil.getUserName(
811                                    auditedModel.getUserId(), auditedModel.getUserName());
812    
813                            document.addKeyword(Field.USER_NAME, userName, true);
814                    }
815    
816                    if (baseModel instanceof GroupedModel) {
817                            GroupedModel groupedModel = (GroupedModel)baseModel;
818    
819                            document.addKeyword(
820                                    Field.GROUP_ID, getParentGroupId(groupedModel.getGroupId()));
821                            document.addKeyword(
822                                    Field.SCOPE_GROUP_ID, groupedModel.getGroupId());
823                    }
824    
825                    if (baseModel instanceof WorkflowedModel) {
826                            WorkflowedModel workflowedModel = (WorkflowedModel)baseModel;
827    
828                            document.addKeyword(Field.STATUS, workflowedModel.getStatus());
829                    }
830    
831                    ExpandoBridgeIndexerUtil.addAttributes(
832                            document, baseModel.getExpandoBridge());
833    
834                    return document;
835            }
836    
837            protected String getClassName(SearchContext searchContext) {
838                    String[] classNames = getClassNames();
839    
840                    if (classNames.length != 1) {
841                            throw new UnsupportedOperationException(
842                                    "Search method needs to be manually implemented for " +
843                                            "indexers with more than one class name");
844                    }
845    
846                    return classNames[0];
847            }
848    
849            protected long getParentGroupId(long groupId) {
850                    long parentGroupId = groupId;
851    
852                    try {
853                            Group group = GroupLocalServiceUtil.getGroup(groupId);
854    
855                            if (group.isLayout()) {
856                                    parentGroupId = group.getParentGroupId();
857                            }
858                    }
859                    catch (Exception e) {
860                    }
861    
862                    return parentGroupId;
863            }
864    
865            protected abstract String getPortletId(SearchContext searchContext);
866    
867            protected void populateAddresses(
868                            Document document, List<Address> addresses, long regionId,
869                            long countryId)
870                    throws PortalException, SystemException {
871    
872                    List<String> cities = new ArrayList<String>();
873    
874                    List<String> countries = new ArrayList<String>();
875    
876                    if (countryId > 0) {
877                            try {
878                                    Country country = CountryServiceUtil.getCountry(countryId);
879    
880                                    countries.add(country.getName().toLowerCase());
881                            }
882                            catch (NoSuchCountryException nsce) {
883                                    if (_log.isWarnEnabled()) {
884                                            _log.warn(nsce.getMessage());
885                                    }
886                            }
887                    }
888    
889                    List<String> regions = new ArrayList<String>();
890    
891                    if (regionId > 0) {
892                            try {
893                                    Region region = RegionServiceUtil.getRegion(regionId);
894    
895                                    regions.add(region.getName().toLowerCase());
896                            }
897                            catch (NoSuchRegionException nsre) {
898                                    if (_log.isWarnEnabled()) {
899                                            _log.warn(nsre.getMessage());
900                                    }
901                            }
902                    }
903    
904                    List<String> streets = new ArrayList<String>();
905                    List<String> zips = new ArrayList<String>();
906    
907                    for (Address address : addresses) {
908                            cities.add(address.getCity().toLowerCase());
909                            countries.add(address.getCountry().getName().toLowerCase());
910                            regions.add(address.getRegion().getName().toLowerCase());
911                            streets.add(address.getStreet1().toLowerCase());
912                            streets.add(address.getStreet2().toLowerCase());
913                            streets.add(address.getStreet3().toLowerCase());
914                            zips.add(address.getZip().toLowerCase());
915                    }
916    
917                    document.addText("city", cities.toArray(new String[cities.size()]));
918                    document.addText(
919                            "country", countries.toArray(new String[countries.size()]));
920                    document.addText("region", regions.toArray(new String[regions.size()]));
921                    document.addText("street", streets.toArray(new String[streets.size()]));
922                    document.addText("zip", zips.toArray(new String[zips.size()]));
923            }
924    
925            protected void postProcessFullQuery(
926                            BooleanQuery fullQuery, SearchContext searchContext)
927                    throws Exception {
928            }
929    
930            protected void setStagingAware(boolean stagingAware) {
931                    _stagingAware = stagingAware;
932            }
933    
934            private static final boolean _FILTER_SEARCH = false;
935    
936            private static final boolean _INDEXER_ENABLED = true;
937    
938            private static final boolean _PERMISSION_AWARE = true;
939    
940            private static Log _log = LogFactoryUtil.getLog(BaseIndexer.class);
941    
942            private IndexerPostProcessor[] _indexerPostProcessors =
943                    new IndexerPostProcessor[0];
944            private boolean _stagingAware = true;
945    
946    }