001    /**
002     * Copyright (c) 2000-2012 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            /**
388             * @deprecated {@link #addSearchLocalizedTerm(BooleanQuery, SearchContext,
389             *             String, boolean)}
390             */
391            protected void addLocalizedSearchTerm(
392                            BooleanQuery searchQuery, SearchContext searchContext, String field,
393                            boolean like)
394                    throws Exception {
395    
396                    addSearchLocalizedTerm(searchQuery, searchContext, field, like);
397            }
398    
399            protected void addSearchArrayQuery(
400                            BooleanQuery searchQuery, SearchContext searchContext, String field)
401                    throws Exception {
402    
403                    if (Validator.isNull(field)) {
404                            return;
405                    }
406    
407                    Object fieldValues = searchContext.getAttribute(field);
408    
409                    if (fieldValues == null) {
410                            return;
411                    }
412    
413                    BooleanQuery fieldQuery = null;
414    
415                    if (fieldValues instanceof int[]) {
416                            int[] fieldValuesArray = (int[])fieldValues;
417    
418                            if (fieldValuesArray.length == 0) {
419                                    return;
420                            }
421    
422                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
423    
424                            for (int fieldValue : fieldValuesArray) {
425                                    fieldQuery.addTerm(field, fieldValue);
426                            }
427                    }
428                    else if (fieldValues instanceof Integer[]) {
429                            Integer[] fieldValuesArray = (Integer[])fieldValues;
430    
431                            if (fieldValuesArray.length == 0) {
432                                    return;
433                            }
434    
435                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
436    
437                            for (Integer fieldValue : fieldValuesArray) {
438                                    fieldQuery.addTerm(field, fieldValue);
439                            }
440                    }
441                    else if (fieldValues instanceof long[]) {
442                            long[] fieldValuesArray = (long[])fieldValues;
443    
444                            if (fieldValuesArray.length == 0) {
445                                    return;
446                            }
447    
448                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
449    
450                            for (long fieldValue : fieldValuesArray) {
451                                    fieldQuery.addTerm(field, fieldValue);
452                            }
453                    }
454                    else if (fieldValues instanceof Long[]) {
455                            Long[] fieldValuesArray = (Long[])fieldValues;
456    
457                            if (fieldValuesArray.length == 0) {
458                                    return;
459                            }
460    
461                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
462    
463                            for (Long fieldValue : fieldValuesArray) {
464                                    fieldQuery.addTerm(field, fieldValue);
465                            }
466                    }
467                    else if (fieldValues instanceof short[]) {
468                            short[] fieldValuesArray = (short[])fieldValues;
469    
470                            if (fieldValuesArray.length == 0) {
471                                    return;
472                            }
473    
474                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
475    
476                            for (short fieldValue : fieldValuesArray) {
477                                    fieldQuery.addTerm(field, fieldValue);
478                            }
479                    }
480                    else if (fieldValues instanceof Short[]) {
481                            Short[] fieldValuesArray = (Short[])fieldValues;
482    
483                            if (fieldValuesArray.length == 0) {
484                                    return;
485                            }
486    
487                            fieldQuery = BooleanQueryFactoryUtil.create(searchContext);
488    
489                            for (Short fieldValue : fieldValuesArray) {
490                                    fieldQuery.addTerm(field, fieldValue);
491                            }
492                    }
493    
494                    if (fieldQuery != null) {
495                            if (searchContext.isAndSearch()) {
496                                    searchQuery.add(fieldQuery, BooleanClauseOccur.MUST);
497                            }
498                            else {
499                                    searchQuery.add(fieldQuery, BooleanClauseOccur.SHOULD);
500                            }
501                    }
502            }
503    
504            protected void addSearchAssetCategoryIds(
505                            BooleanQuery contextQuery, SearchContext searchContext)
506                    throws Exception {
507    
508                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
509    
510                    multiValueFacet.setFieldName(Field.ASSET_CATEGORY_IDS);
511                    multiValueFacet.setStatic(true);
512    
513                    searchContext.addFacet(multiValueFacet);
514            }
515    
516            protected void addSearchAssetTagNames(
517                            BooleanQuery contextQuery, SearchContext searchContext)
518                    throws Exception {
519    
520                    MultiValueFacet multiValueFacet = new MultiValueFacet(searchContext);
521    
522                    multiValueFacet.setFieldName(Field.ASSET_TAG_NAMES);
523                    multiValueFacet.setStatic(true);
524    
525                    searchContext.addFacet(multiValueFacet);
526            }
527    
528            protected void addSearchDDMStruture(
529                            BooleanQuery searchQuery, SearchContext searchContext,
530                            DDMStructure ddmStructure)
531                    throws Exception {
532    
533                    Set<String> fieldNames = ddmStructure.getFieldNames();
534    
535                    for (String fieldName : fieldNames) {
536                            String name = DDMIndexerUtil.encodeName(
537                                    ddmStructure.getStructureId(), fieldName);
538    
539                            addSearchTerm(searchQuery, searchContext, name, false);
540                    }
541            }
542    
543            protected void addSearchEntryClassNames(
544                            BooleanQuery contextQuery, SearchContext searchContext)
545                    throws Exception {
546    
547                    Facet facet = new AssetEntriesFacet(searchContext);
548    
549                    facet.setStatic(true);
550    
551                    searchContext.addFacet(facet);
552            }
553    
554            protected void addSearchExpando(
555                            BooleanQuery searchQuery, SearchContext searchContext,
556                            String keywords)
557                    throws Exception {
558    
559                    ExpandoBridge expandoBridge = ExpandoBridgeFactoryUtil.getExpandoBridge(
560                            searchContext.getCompanyId(), getClassName(searchContext));
561    
562                    Set<String> attributeNames = SetUtil.fromEnumeration(
563                            expandoBridge.getAttributeNames());
564    
565                    for (String attributeName : attributeNames) {
566                            UnicodeProperties properties = expandoBridge.getAttributeProperties(
567                                    attributeName);
568    
569                            int indexType = GetterUtil.getInteger(
570                                    properties.getProperty(ExpandoColumnConstants.INDEX_TYPE));
571    
572                            if (indexType != ExpandoColumnConstants.INDEX_TYPE_NONE) {
573                                    String fieldName = ExpandoBridgeIndexerUtil.encodeFieldName(
574                                            attributeName);
575    
576                                    if (Validator.isNotNull(keywords)) {
577                                            if (searchContext.isAndSearch()) {
578                                                    searchQuery.addRequiredTerm(fieldName, keywords);
579                                            }
580                                            else {
581                                                    searchQuery.addTerm(fieldName, keywords);
582                                            }
583                                    }
584                            }
585                    }
586            }
587    
588            protected void addSearchGroupId(
589                            BooleanQuery contextQuery, SearchContext searchContext)
590                    throws Exception {
591    
592                    Facet facet = new ScopeFacet(searchContext);
593    
594                    facet.setStatic(true);
595    
596                    searchContext.addFacet(facet);
597            }
598    
599            protected void addSearchKeywords(
600                            BooleanQuery searchQuery, SearchContext searchContext)
601                    throws Exception {
602    
603                    String keywords = searchContext.getKeywords();
604    
605                    if (Validator.isNull(keywords)) {
606                            return;
607                    }
608    
609                    searchQuery.addTerms(Field.KEYWORDS, keywords);
610    
611                    addSearchExpando(searchQuery, searchContext, keywords);
612            }
613    
614            protected void addSearchLocalizedTerm(
615                            BooleanQuery searchQuery, SearchContext searchContext, String field,
616                            boolean like)
617                    throws Exception {
618    
619                    addSearchTerm(searchQuery, searchContext, field, like);
620                    addSearchTerm(
621                            searchQuery, searchContext,
622                            DocumentImpl.getLocalizedName(searchContext.getLocale(), field),
623                            like);
624            }
625    
626            protected void addSearchTerm(
627                            BooleanQuery searchQuery, SearchContext searchContext, String field,
628                            boolean like)
629                    throws Exception {
630    
631                    if (Validator.isNull(field)) {
632                            return;
633                    }
634    
635                    String value = String.valueOf(searchContext.getAttribute(field));
636    
637                    if (Validator.isNull(value)) {
638                            value = searchContext.getKeywords();
639                    }
640    
641                    if (Validator.isNull(value)) {
642                            return;
643                    }
644    
645                    if (searchContext.isAndSearch()) {
646                            searchQuery.addRequiredTerm(field, value, like);
647                    }
648                    else {
649                            searchQuery.addTerm(field, value, like);
650                    }
651            }
652    
653            protected void addStagingGroupKeyword(Document document, long groupId)
654                    throws Exception {
655    
656                    if (!isStagingAware()) {
657                            return;
658                    }
659    
660                    boolean stagingGroup = false;
661    
662                    Group group = GroupLocalServiceUtil.getGroup(groupId);
663    
664                    if (group.isLayout()) {
665                            group = GroupLocalServiceUtil.getGroup(group.getParentGroupId());
666                    }
667    
668                    if (group.isStagingGroup()) {
669                            stagingGroup = true;
670                    }
671    
672                    document.addKeyword(Field.STAGING_GROUP, stagingGroup);
673            }
674    
675            protected BooleanQuery createFullQuery(
676                            BooleanQuery contextQuery, SearchContext searchContext)
677                    throws Exception {
678    
679                    BooleanQuery searchQuery = BooleanQueryFactoryUtil.create(
680                            searchContext);
681    
682                    addSearchKeywords(searchQuery, searchContext);
683                    postProcessSearchQuery(searchQuery, searchContext);
684    
685                    for (IndexerPostProcessor indexerPostProcessor :
686                                    _indexerPostProcessors) {
687    
688                            indexerPostProcessor.postProcessSearchQuery(
689                                    searchQuery, searchContext);
690                    }
691    
692                    Map<String, Facet> facets = searchContext.getFacets();
693    
694                    for (Facet facet : facets.values()) {
695                            BooleanClause facetClause = facet.getFacetClause();
696    
697                            if (facetClause != null) {
698                                    contextQuery.add(
699                                            facetClause.getQuery(),
700                                            facetClause.getBooleanClauseOccur());
701                            }
702                    }
703    
704                    BooleanQuery fullQuery = BooleanQueryFactoryUtil.create(searchContext);
705    
706                    fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
707    
708                    if (searchQuery.hasClauses()) {
709                            fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
710                    }
711    
712                    BooleanClause[] booleanClauses = searchContext.getBooleanClauses();
713    
714                    if (booleanClauses != null) {
715                            for (BooleanClause booleanClause : booleanClauses) {
716                                    fullQuery.add(
717                                            booleanClause.getQuery(),
718                                            booleanClause.getBooleanClauseOccur());
719                            }
720                    }
721    
722                    postProcessFullQuery(fullQuery, searchContext);
723    
724                    for (IndexerPostProcessor indexerPostProcessor :
725                                    _indexerPostProcessors) {
726    
727                            indexerPostProcessor.postProcessFullQuery(fullQuery, searchContext);
728                    }
729    
730                    return fullQuery;
731            }
732    
733            protected void deleteDocument(long companyId, long field1)
734                    throws Exception {
735    
736                    deleteDocument(companyId, String.valueOf(field1));
737            }
738    
739            protected void deleteDocument(long companyId, long field1, String field2)
740                    throws Exception {
741    
742                    deleteDocument(companyId, String.valueOf(field1), field2);
743            }
744    
745            protected void deleteDocument(long companyId, String field1)
746                    throws Exception {
747    
748                    Document document = new DocumentImpl();
749    
750                    document.addUID(getPortletId(), field1);
751    
752                    SearchEngineUtil.deleteDocument(companyId, document.get(Field.UID));
753            }
754    
755            protected void deleteDocument(long companyId, String field1, String field2)
756                    throws Exception {
757    
758                    Document document = new DocumentImpl();
759    
760                    document.addUID(getPortletId(), field1, field2);
761    
762                    SearchEngineUtil.deleteDocument(companyId, document.get(Field.UID));
763            }
764    
765            protected abstract void doDelete(Object obj) throws Exception;
766    
767            protected abstract Document doGetDocument(Object obj) throws Exception;
768    
769            protected String doGetSortField(String orderByCol) {
770                    return orderByCol;
771            }
772    
773            protected abstract Summary doGetSummary(
774                            Document document, Locale locale, String snippet,
775                            PortletURL portletURL)
776                    throws Exception;
777    
778            protected abstract void doReindex(Object obj) throws Exception;
779    
780            protected abstract void doReindex(String className, long classPK)
781                    throws Exception;
782    
783            protected abstract void doReindex(String[] ids) throws Exception;
784    
785            protected Hits filterSearch(
786                    Hits hits, PermissionChecker permissionChecker,
787                    SearchContext searchContext) {
788    
789                    List<Document> docs = new ArrayList<Document>();
790                    List<Float> scores = new ArrayList<Float>();
791    
792                    int start = searchContext.getStart();
793                    int end = searchContext.getEnd();
794    
795                    String paginationType = GetterUtil.getString(
796                            searchContext.getAttribute("paginationType"), "more");
797    
798                    boolean hasMore = false;
799    
800                    Document[] documents = hits.getDocs();
801    
802                    for (int i = 0; i < documents.length; i++) {
803                            try {
804                                    Document document = documents[i];
805    
806                                    String entryClassName = document.get(Field.ENTRY_CLASS_NAME);
807                                    long entryClassPK = GetterUtil.getLong(
808                                            document.get(Field.ENTRY_CLASS_PK));
809    
810                                    Indexer indexer = IndexerRegistryUtil.getIndexer(
811                                            entryClassName);
812    
813                                    if ((indexer.isFilterSearch() && indexer.hasPermission(
814                                                    permissionChecker, entryClassPK, ActionKeys.VIEW)) ||
815                                            !indexer.isFilterSearch() ||
816                                            !indexer.isPermissionAware()) {
817    
818                                            docs.add(document);
819                                            scores.add(hits.score(i));
820                                    }
821                            }
822                            catch (Exception e) {
823                            }
824    
825                            if (paginationType.equals("more") && (docs.size() > end)) {
826                                    hasMore = true;
827    
828                                    break;
829                            }
830                    }
831    
832                    int length = docs.size();
833    
834                    if (hasMore) {
835                            length = length + (end - start);
836                    }
837    
838                    hits.setLength(length);
839    
840                    if ((start != QueryUtil.ALL_POS) && (end != QueryUtil.ALL_POS)) {
841                            if (end > length) {
842                                    end = length;
843                            }
844    
845                            docs = docs.subList(start, end);
846                    }
847    
848                    hits.setDocs(docs.toArray(new Document[docs.size()]));
849                    hits.setScores(scores.toArray(new Float[docs.size()]));
850    
851                    hits.setSearchTime(
852                            (float)(System.currentTimeMillis() - hits.getStart()) /
853                                    Time.SECOND);
854    
855                    return hits;
856            }
857    
858            protected Document getBaseModelDocument(
859                            String portletId, BaseModel<?> baseModel)
860                    throws SystemException {
861    
862                    Document document = new DocumentImpl();
863    
864                    String className = baseModel.getModelClassName();
865    
866                    long classPK = 0;
867                    long resourcePrimKey = 0;
868    
869                    if (baseModel instanceof ResourcedModel) {
870                            ResourcedModel resourcedModel = (ResourcedModel)baseModel;
871    
872                            classPK = resourcedModel.getResourcePrimKey();
873                            resourcePrimKey = resourcedModel.getResourcePrimKey();
874                    }
875                    else {
876                            classPK = (Long)baseModel.getPrimaryKeyObj();
877                    }
878    
879                    document.addUID(portletId, classPK);
880    
881                    List<AssetCategory> assetCategories =
882                            AssetCategoryLocalServiceUtil.getCategories(className, classPK);
883    
884                    long[] assetCategoryIds = StringUtil.split(
885                            ListUtil.toString(
886                                    assetCategories, AssetCategory.CATEGORY_ID_ACCESSOR),
887                            0L);
888    
889                    document.addKeyword(Field.ASSET_CATEGORY_IDS, assetCategoryIds);
890    
891                    String[] assetCategoryNames = StringUtil.split(
892                            ListUtil.toString(assetCategories, AssetCategory.NAME_ACCESSOR));
893    
894                    document.addText(Field.ASSET_CATEGORY_NAMES, assetCategoryNames);
895    
896                    String[] assetTagNames = AssetTagLocalServiceUtil.getTagNames(
897                            className, classPK);
898    
899                    document.addText(Field.ASSET_TAG_NAMES, assetTagNames);
900    
901                    document.addKeyword(Field.ENTRY_CLASS_NAME, className);
902                    document.addKeyword(Field.ENTRY_CLASS_PK, classPK);
903                    document.addKeyword(Field.PORTLET_ID, portletId);
904    
905                    if (resourcePrimKey > 0) {
906                            document.addKeyword(Field.ROOT_ENTRY_CLASS_PK, resourcePrimKey);
907                    }
908    
909                    if (baseModel instanceof AttachedModel) {
910                            AttachedModel attachedModel = (AttachedModel)baseModel;
911    
912                            document.addKeyword(
913                                    Field.CLASS_NAME_ID, attachedModel.getClassNameId());
914                            document.addKeyword(Field.CLASS_PK, attachedModel.getClassPK());
915                    }
916    
917                    if (baseModel instanceof AuditedModel) {
918                            AuditedModel auditedModel = (AuditedModel)baseModel;
919    
920                            document.addKeyword(Field.COMPANY_ID, auditedModel.getCompanyId());
921                            document.addDate(Field.CREATE_DATE, auditedModel.getCreateDate());
922                            document.addDate(
923                                    Field.MODIFIED_DATE, auditedModel.getModifiedDate());
924                            document.addKeyword(Field.USER_ID, auditedModel.getUserId());
925    
926                            String userName = PortalUtil.getUserName(
927                                    auditedModel.getUserId(), auditedModel.getUserName());
928    
929                            document.addKeyword(Field.USER_NAME, userName, true);
930                    }
931    
932                    if (baseModel instanceof GroupedModel) {
933                            GroupedModel groupedModel = (GroupedModel)baseModel;
934    
935                            document.addKeyword(
936                                    Field.GROUP_ID, getParentGroupId(groupedModel.getGroupId()));
937                            document.addKeyword(
938                                    Field.SCOPE_GROUP_ID, groupedModel.getGroupId());
939                    }
940    
941                    if (baseModel instanceof WorkflowedModel) {
942                            WorkflowedModel workflowedModel = (WorkflowedModel)baseModel;
943    
944                            document.addKeyword(Field.STATUS, workflowedModel.getStatus());
945                    }
946    
947                    ExpandoBridgeIndexerUtil.addAttributes(
948                            document, baseModel.getExpandoBridge());
949    
950                    return document;
951            }
952    
953            protected String getClassName(SearchContext searchContext) {
954                    String[] classNames = getClassNames();
955    
956                    if (classNames.length != 1) {
957                            throw new UnsupportedOperationException(
958                                    "Search method needs to be manually implemented for " +
959                                            "indexers with more than one class name");
960                    }
961    
962                    return classNames[0];
963            }
964    
965            protected long getParentGroupId(long groupId) {
966                    long parentGroupId = groupId;
967    
968                    try {
969                            Group group = GroupLocalServiceUtil.getGroup(groupId);
970    
971                            if (group.isLayout()) {
972                                    parentGroupId = group.getParentGroupId();
973                            }
974                    }
975                    catch (Exception e) {
976                    }
977    
978                    return parentGroupId;
979            }
980    
981            protected abstract String getPortletId(SearchContext searchContext);
982    
983            protected void populateAddresses(
984                            Document document, List<Address> addresses, long regionId,
985                            long countryId)
986                    throws PortalException, SystemException {
987    
988                    List<String> cities = new ArrayList<String>();
989    
990                    List<String> countries = new ArrayList<String>();
991    
992                    if (countryId > 0) {
993                            try {
994                                    Country country = CountryServiceUtil.getCountry(countryId);
995    
996                                    countries.add(country.getName().toLowerCase());
997                            }
998                            catch (NoSuchCountryException nsce) {
999                                    if (_log.isWarnEnabled()) {
1000                                            _log.warn(nsce.getMessage());
1001                                    }
1002                            }
1003                    }
1004    
1005                    List<String> regions = new ArrayList<String>();
1006    
1007                    if (regionId > 0) {
1008                            try {
1009                                    Region region = RegionServiceUtil.getRegion(regionId);
1010    
1011                                    regions.add(region.getName().toLowerCase());
1012                            }
1013                            catch (NoSuchRegionException nsre) {
1014                                    if (_log.isWarnEnabled()) {
1015                                            _log.warn(nsre.getMessage());
1016                                    }
1017                            }
1018                    }
1019    
1020                    List<String> streets = new ArrayList<String>();
1021                    List<String> zips = new ArrayList<String>();
1022    
1023                    for (Address address : addresses) {
1024                            cities.add(address.getCity().toLowerCase());
1025                            countries.add(address.getCountry().getName().toLowerCase());
1026                            regions.add(address.getRegion().getName().toLowerCase());
1027                            streets.add(address.getStreet1().toLowerCase());
1028                            streets.add(address.getStreet2().toLowerCase());
1029                            streets.add(address.getStreet3().toLowerCase());
1030                            zips.add(address.getZip().toLowerCase());
1031                    }
1032    
1033                    document.addText("city", cities.toArray(new String[cities.size()]));
1034                    document.addText(
1035                            "country", countries.toArray(new String[countries.size()]));
1036                    document.addText("region", regions.toArray(new String[regions.size()]));
1037                    document.addText("street", streets.toArray(new String[streets.size()]));
1038                    document.addText("zip", zips.toArray(new String[zips.size()]));
1039            }
1040    
1041            protected void postProcessFullQuery(
1042                            BooleanQuery fullQuery, SearchContext searchContext)
1043                    throws Exception {
1044            }
1045    
1046            protected void setStagingAware(boolean stagingAware) {
1047                    _stagingAware = stagingAware;
1048            }
1049    
1050            private static final boolean _FILTER_SEARCH = false;
1051    
1052            private static final boolean _INDEXER_ENABLED = true;
1053    
1054            private static final boolean _PERMISSION_AWARE = false;
1055    
1056            private static Log _log = LogFactoryUtil.getLog(BaseIndexer.class);
1057    
1058            private IndexerPostProcessor[] _indexerPostProcessors =
1059                    new IndexerPostProcessor[0];
1060            private boolean _stagingAware = true;
1061    
1062    }