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