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