001    /**
002     * Copyright (c) 2000-present 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.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.messaging.DestinationNames;
020    import com.liferay.portal.kernel.messaging.proxy.ProxyModeThreadLocal;
021    import com.liferay.portal.kernel.search.queue.QueuingSearchEngine;
022    import com.liferay.portal.kernel.security.pacl.permission.PortalRuntimePermission;
023    import com.liferay.portal.kernel.util.ClassUtil;
024    import com.liferay.portal.kernel.util.GetterUtil;
025    import com.liferay.portal.kernel.util.PropsKeys;
026    import com.liferay.portal.kernel.util.PropsUtil;
027    import com.liferay.portal.kernel.util.ProxyFactory;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.security.permission.PermissionThreadLocal;
030    import com.liferay.registry.Registry;
031    import com.liferay.registry.RegistryUtil;
032    import com.liferay.registry.ServiceReference;
033    import com.liferay.registry.ServiceTracker;
034    import com.liferay.registry.ServiceTrackerCustomizer;
035    
036    import java.util.Collection;
037    import java.util.HashMap;
038    import java.util.HashSet;
039    import java.util.Iterator;
040    import java.util.List;
041    import java.util.Locale;
042    import java.util.Map;
043    import java.util.Set;
044    import java.util.concurrent.ConcurrentHashMap;
045    
046    /**
047     * @author Bruno Farache
048     * @author Raymond Aug??
049     * @author Michael C. Han
050     */
051    public class SearchEngineUtil {
052    
053            public static final String GENERIC_ENGINE_ID = "GENERIC_ENGINE";
054    
055            public static final String SYSTEM_ENGINE_ID = "SYSTEM_ENGINE";
056    
057            /**
058             * @deprecated As of 7.0.0, replaced by {@link #addDocument(String, long,
059             *             Document, boolean)}
060             */
061            @Deprecated
062            public static void addDocument(
063                            String searchEngineId, long companyId, Document document)
064                    throws SearchException {
065    
066                    addDocument(searchEngineId, companyId, document, false);
067            }
068    
069            public static void addDocument(
070                            String searchEngineId, long companyId, Document document,
071                            boolean commitImmediately)
072                    throws SearchException {
073    
074                    if (isIndexReadOnly()) {
075                            return;
076                    }
077    
078                    if (_log.isDebugEnabled()) {
079                            _log.debug("Add document " + document.toString());
080                    }
081    
082                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
083    
084                    IndexWriter indexWriter = searchEngine.getIndexWriter();
085    
086                    _searchPermissionChecker.addPermissionFields(companyId, document);
087    
088                    SearchContext searchContext = new SearchContext();
089    
090                    searchContext.setCompanyId(companyId);
091                    searchContext.setSearchEngineId(searchEngineId);
092    
093                    setCommitImmediately(searchContext, commitImmediately);
094    
095                    indexWriter.addDocument(searchContext, document);
096            }
097    
098            /**
099             * @deprecated As of 7.0.0, replaced by {@link #addDocuments(String, long,
100             *             Collection, boolean)}
101             */
102            @Deprecated
103            public static void addDocuments(
104                            String searchEngineId, long companyId,
105                            Collection<Document> documents)
106                    throws SearchException {
107    
108                    addDocuments(searchEngineId, companyId, documents, false);
109            }
110    
111            public static void addDocuments(
112                            String searchEngineId, long companyId,
113                            Collection<Document> documents, boolean commitImmediately)
114                    throws SearchException {
115    
116                    if (isIndexReadOnly() || (documents == null) || documents.isEmpty()) {
117                            return;
118                    }
119    
120                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
121    
122                    IndexWriter indexWriter = searchEngine.getIndexWriter();
123    
124                    for (Document document : documents) {
125                            if (_log.isDebugEnabled()) {
126                                    _log.debug("Add document " + document.toString());
127                            }
128    
129                            _searchPermissionChecker.addPermissionFields(companyId, document);
130                    }
131    
132                    SearchContext searchContext = new SearchContext();
133    
134                    searchContext.setCompanyId(companyId);
135                    searchContext.setSearchEngineId(searchEngineId);
136    
137                    setCommitImmediately(searchContext, commitImmediately);
138    
139                    indexWriter.addDocuments(searchContext, documents);
140            }
141    
142            public synchronized static void backup(long companyId, String backupName)
143                    throws SearchException {
144    
145                    for (SearchEngine searchEngine : _searchEngines.values()) {
146                            searchEngine.backup(companyId, backupName);
147                    }
148            }
149    
150            public synchronized static String backup(
151                            long companyId, String searchEngineId, String backupName)
152                    throws SearchException {
153    
154                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
155    
156                    return searchEngine.backup(companyId, backupName);
157            }
158    
159            public synchronized static void backup(String backupName)
160                    throws SearchException {
161    
162                    for (SearchEngine searchEngine : _searchEngines.values()) {
163                            for (long companyId : _companyIds.keySet()) {
164                                    searchEngine.backup(companyId, backupName);
165                            }
166                    }
167            }
168    
169            /**
170             * @deprecated As of 7.0.0, replaced by {@link #deleteDocument(String, long,
171             *             String, boolean)}
172             */
173            @Deprecated
174            public static void deleteDocument(
175                            String searchEngineId, long companyId, String uid)
176                    throws SearchException {
177    
178                    deleteDocument(searchEngineId, companyId, uid, false);
179            }
180    
181            public static void deleteDocument(
182                            String searchEngineId, long companyId, String uid,
183                            boolean commitImmediately)
184                    throws SearchException {
185    
186                    if (isIndexReadOnly()) {
187                            return;
188                    }
189    
190                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
191    
192                    IndexWriter indexWriter = searchEngine.getIndexWriter();
193    
194                    SearchContext searchContext = new SearchContext();
195    
196                    searchContext.setCompanyId(companyId);
197                    searchContext.setSearchEngineId(searchEngineId);
198    
199                    setCommitImmediately(searchContext, commitImmediately);
200    
201                    indexWriter.deleteDocument(searchContext, uid);
202            }
203    
204            /**
205             * @deprecated As of 7.0.0, replaced by {@link #deleteDocuments(String,
206             *             long, Collection, boolean)}
207             */
208            @Deprecated
209            public static void deleteDocuments(
210                            String searchEngineId, long companyId, Collection<String> uids)
211                    throws SearchException {
212    
213                    deleteDocuments(searchEngineId, companyId, uids, false);
214            }
215    
216            public static void deleteDocuments(
217                            String searchEngineId, long companyId, Collection<String> uids,
218                            boolean commitImmediately)
219                    throws SearchException {
220    
221                    if (isIndexReadOnly() || (uids == null) || uids.isEmpty()) {
222                            return;
223                    }
224    
225                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
226    
227                    IndexWriter indexWriter = searchEngine.getIndexWriter();
228    
229                    SearchContext searchContext = new SearchContext();
230    
231                    searchContext.setCompanyId(companyId);
232                    searchContext.setSearchEngineId(searchEngineId);
233    
234                    setCommitImmediately(searchContext, commitImmediately);
235    
236                    indexWriter.deleteDocuments(searchContext, uids);
237            }
238    
239            public static void deleteEntityDocuments(
240                            String searchEngineId, long companyId, String className,
241                            boolean commitImmediately)
242                    throws SearchException {
243    
244                    if (isIndexReadOnly()) {
245                            return;
246                    }
247    
248                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
249    
250                    if (searchEngine == null) {
251                            return;
252                    }
253    
254                    IndexWriter indexWriter = searchEngine.getIndexWriter();
255    
256                    SearchContext searchContext = new SearchContext();
257    
258                    searchContext.setCompanyId(companyId);
259                    searchContext.setSearchEngineId(searchEngineId);
260    
261                    setCommitImmediately(searchContext, commitImmediately);
262    
263                    indexWriter.deleteEntityDocuments(searchContext, className);
264            }
265    
266            /**
267             * @deprecated As of 7.0.0, replaced by {@link
268             *             #deleteEntityDocuments(String, long, String, boolean)}
269             */
270            @Deprecated
271            public static void deletePortletDocuments(
272                            String searchEngineId, long companyId, String portletId)
273                    throws SearchException {
274    
275                    deleteEntityDocuments(searchEngineId, companyId, portletId, false);
276            }
277    
278            public static void flushQueuedSearchEngine() {
279                    synchronized (_queuingSearchEngines) {
280                            for (QueuingSearchEngine queuingSearchEngine :
281                                            _queuingSearchEngines.values()) {
282    
283                                    queuingSearchEngine.flush();
284                            }
285    
286                            _queuingSearchEngines.clear();
287                    }
288            }
289    
290            public static void flushQueuedSearchEngine(String searchEngineId) {
291                    QueuingSearchEngine queuingSearchEngine = null;
292    
293                    synchronized (_queuingSearchEngines) {
294                            queuingSearchEngine = _queuingSearchEngines.remove(searchEngineId);
295                    }
296    
297                    if (queuingSearchEngine != null) {
298                            queuingSearchEngine.flush();
299                    }
300            }
301    
302            public static String getDefaultSearchEngineId() {
303                    if (_defaultSearchEngineId == null) {
304                            return SYSTEM_ENGINE_ID;
305                    }
306    
307                    return _defaultSearchEngineId;
308            }
309    
310            public static String[] getEntryClassNames() {
311                    Set<String> assetEntryClassNames = new HashSet<>();
312    
313                    for (Indexer<?> indexer : IndexerRegistryUtil.getIndexers()) {
314                            for (String className : indexer.getSearchClassNames()) {
315                                    if (!_excludedEntryClassNames.contains(className)) {
316                                            assetEntryClassNames.add(className);
317                                    }
318                            }
319                    }
320    
321                    return assetEntryClassNames.toArray(
322                            new String[assetEntryClassNames.size()]);
323            }
324    
325            public static String getQueryString(
326                    SearchContext searchContext, Query query) {
327    
328                    SearchEngine searchEngine = getSearchEngine(
329                            searchContext.getSearchEngineId());
330    
331                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
332    
333                    try {
334                            return indexSearcher.getQueryString(searchContext, query);
335                    }
336                    catch (ParseException pe) {
337                            if (_log.isDebugEnabled()) {
338                                    _log.debug("Unable to parse query " + query, pe);
339                            }
340                    }
341    
342                    return StringPool.BLANK;
343            }
344    
345            public static SearchEngine getSearchEngine(String searchEngineId) {
346                    PortalRuntimePermission.checkSearchEngine(searchEngineId);
347    
348                    SearchEngine searchEngine = _searchEngines.get(searchEngineId);
349    
350                    if (searchEngine != null) {
351                            return searchEngine;
352                    }
353    
354                    synchronized (_queuingSearchEngines) {
355                            searchEngine = _queuingSearchEngines.get(searchEngineId);
356    
357                            if (searchEngine == null) {
358                                    QueuingSearchEngine queuingSearchEngine =
359                                            new QueuingSearchEngine(_queueCapacity);
360    
361                                    _queuingSearchEngines.put(searchEngineId, queuingSearchEngine);
362    
363                                    searchEngine = queuingSearchEngine;
364                            }
365    
366                            return searchEngine;
367                    }
368            }
369    
370            public static String getSearchEngineId(Collection<Document> documents) {
371                    if (!documents.isEmpty()) {
372                            Iterator<Document> iterator = documents.iterator();
373    
374                            Document document = iterator.next();
375    
376                            return getSearchEngineId(document);
377                    }
378    
379                    return getDefaultSearchEngineId();
380            }
381    
382            public static String getSearchEngineId(Document document) {
383                    String entryClassName = document.get("entryClassName");
384    
385                    Indexer<?> indexer = IndexerRegistryUtil.getIndexer(entryClassName);
386    
387                    String searchEngineId = indexer.getSearchEngineId();
388    
389                    if (_log.isDebugEnabled()) {
390                            _log.debug(
391                                    "Search engine ID " + searchEngineId + " is associated with " +
392                                            ClassUtil.getClassName(indexer));
393                    }
394    
395                    return searchEngineId;
396            }
397    
398            public static Set<String> getSearchEngineIds() {
399                    PortalRuntimePermission.checkGetBeanProperty(
400                            SearchEngineUtil.class, "searchEngineIds");
401    
402                    return _searchEngines.keySet();
403            }
404    
405            public static SearchEngine getSearchEngineSilent(String searchEngineId) {
406                    PortalRuntimePermission.checkSearchEngine(searchEngineId);
407    
408                    return _searchEngines.get(searchEngineId);
409            }
410    
411            public static SearchPermissionChecker getSearchPermissionChecker() {
412                    PortalRuntimePermission.checkGetBeanProperty(
413                            SearchEngineUtil.class, "searchPermissionChecker");
414    
415                    return _searchPermissionChecker;
416            }
417    
418            public static String getSearchReaderDestinationName(String searchEngineId) {
419                    return DestinationNames.SEARCH_READER.concat(StringPool.SLASH).concat(
420                            searchEngineId);
421            }
422    
423            public static String getSearchWriterDestinationName(String searchEngineId) {
424                    return DestinationNames.SEARCH_WRITER.concat(StringPool.SLASH).concat(
425                            searchEngineId);
426            }
427    
428            public static void indexKeyword(
429                            long companyId, String querySuggestion, float weight,
430                            String keywordType, Locale locale)
431                    throws SearchException {
432    
433                    String searchEngineId = getDefaultSearchEngineId();
434    
435                    indexKeyword(
436                            searchEngineId, companyId, querySuggestion, weight, keywordType,
437                            locale);
438            }
439    
440            public static void indexKeyword(
441                            String searchEngineId, long companyId, String querySuggestion,
442                            float weight, String keywordType, Locale locale)
443                    throws SearchException {
444    
445                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
446    
447                    IndexWriter indexWriter = searchEngine.getIndexWriter();
448    
449                    SearchContext searchContext = new SearchContext();
450    
451                    searchContext.setCompanyId(companyId);
452                    searchContext.setSearchEngineId(searchEngineId);
453                    searchContext.setKeywords(querySuggestion);
454                    searchContext.setLocale(locale);
455    
456                    indexWriter.indexKeyword(searchContext, weight, keywordType);
457            }
458    
459            public static void indexQuerySuggestionDictionaries(long companyId)
460                    throws SearchException {
461    
462                    Set<String> searchEngineIds = getSearchEngineIds();
463    
464                    for (String searchEngineId : searchEngineIds) {
465                            indexQuerySuggestionDictionaries(searchEngineId, companyId);
466                    }
467            }
468    
469            public static void indexQuerySuggestionDictionaries(
470                            String searchEngineId, long companyId)
471                    throws SearchException {
472    
473                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
474    
475                    IndexWriter indexWriter = searchEngine.getIndexWriter();
476    
477                    SearchContext searchContext = new SearchContext();
478    
479                    searchContext.setCompanyId(companyId);
480                    searchContext.setSearchEngineId(searchEngineId);
481    
482                    indexWriter.indexQuerySuggestionDictionaries(searchContext);
483            }
484    
485            public static void indexQuerySuggestionDictionary(
486                            long companyId, Locale locale)
487                    throws SearchException {
488    
489                    String searchEngineId = getDefaultSearchEngineId();
490    
491                    indexQuerySuggestionDictionary(searchEngineId, companyId, locale);
492            }
493    
494            public static void indexQuerySuggestionDictionary(
495                            String searchEngineId, long companyId, Locale locale)
496                    throws SearchException {
497    
498                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
499    
500                    IndexWriter indexWriter = searchEngine.getIndexWriter();
501    
502                    SearchContext searchContext = new SearchContext();
503    
504                    searchContext.setCompanyId(companyId);
505                    searchContext.setSearchEngineId(searchEngineId);
506                    searchContext.setLocale(locale);
507    
508                    indexWriter.indexQuerySuggestionDictionary(searchContext);
509            }
510    
511            public static void indexSpellCheckerDictionaries(long companyId)
512                    throws SearchException {
513    
514                    String searchEngineId = getDefaultSearchEngineId();
515    
516                    indexSpellCheckerDictionaries(searchEngineId, companyId);
517            }
518    
519            public static void indexSpellCheckerDictionaries(
520                            String searchEngineId, long companyId)
521                    throws SearchException {
522    
523                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
524    
525                    IndexWriter indexWriter = searchEngine.getIndexWriter();
526    
527                    SearchContext searchContext = new SearchContext();
528    
529                    searchContext.setCompanyId(companyId);
530                    searchContext.setSearchEngineId(searchEngineId);
531    
532                    indexWriter.indexSpellCheckerDictionaries(searchContext);
533            }
534    
535            public static void indexSpellCheckerDictionary(
536                            long companyId, Locale locale)
537                    throws SearchException {
538    
539                    String searchEngineId = getDefaultSearchEngineId();
540    
541                    indexSpellCheckerDictionary(searchEngineId, companyId, locale);
542            }
543    
544            public static void indexSpellCheckerDictionary(
545                            String searchEngineId, long companyId, Locale locale)
546                    throws SearchException {
547    
548                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
549    
550                    IndexWriter indexWriter = searchEngine.getIndexWriter();
551    
552                    SearchContext searchContext = new SearchContext();
553    
554                    searchContext.setCompanyId(companyId);
555                    searchContext.setSearchEngineId(searchEngineId);
556                    searchContext.setLocale(locale);
557    
558                    indexWriter.indexSpellCheckerDictionary(searchContext);
559            }
560    
561            public synchronized static void initialize(long companyId) {
562                    if (_companyIds.containsKey(companyId)) {
563                            return;
564                    }
565    
566                    _companyIds.put(companyId, companyId);
567    
568                    for (SearchEngine searchEngine : _searchEngines.values()) {
569                            searchEngine.initialize(companyId);
570                    }
571            }
572    
573            public static boolean isIndexReadOnly() {
574                    PortalRuntimePermission.checkGetBeanProperty(
575                            SearchEngineUtil.class, "indexReadOnly");
576    
577                    return _indexReadOnly;
578            }
579    
580            public static void partiallyUpdateDocument(
581                            String searchEngineId, long companyId, Document document,
582                            boolean commitImmediately)
583                    throws SearchException {
584    
585                    if (isIndexReadOnly()) {
586                            return;
587                    }
588    
589                    if (_log.isDebugEnabled()) {
590                            _log.debug("Document " + document.toString());
591                    }
592    
593                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
594    
595                    IndexWriter indexWriter = searchEngine.getIndexWriter();
596    
597                    _searchPermissionChecker.addPermissionFields(companyId, document);
598    
599                    SearchContext searchContext = new SearchContext();
600    
601                    searchContext.setCompanyId(companyId);
602                    searchContext.setSearchEngineId(searchEngineId);
603    
604                    setCommitImmediately(searchContext, commitImmediately);
605    
606                    indexWriter.partiallyUpdateDocument(searchContext, document);
607            }
608    
609            public static void partiallyUpdateDocuments(
610                            String searchEngineId, long companyId,
611                            Collection<Document> documents, boolean commitImmediately)
612                    throws SearchException {
613    
614                    if (isIndexReadOnly() || (documents == null) || documents.isEmpty()) {
615                            return;
616                    }
617    
618                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
619    
620                    IndexWriter indexWriter = searchEngine.getIndexWriter();
621    
622                    for (Document document : documents) {
623                            if (_log.isDebugEnabled()) {
624                                    _log.debug("Document " + document.toString());
625                            }
626    
627                            _searchPermissionChecker.addPermissionFields(companyId, document);
628                    }
629    
630                    SearchContext searchContext = new SearchContext();
631    
632                    searchContext.setCompanyId(companyId);
633                    searchContext.setSearchEngineId(searchEngineId);
634    
635                    setCommitImmediately(searchContext, commitImmediately);
636    
637                    indexWriter.partiallyUpdateDocuments(searchContext, documents);
638            }
639    
640            public synchronized static void removeBackup(
641                            long companyId, String backupName)
642                    throws SearchException {
643    
644                    for (SearchEngine searchEngine : _searchEngines.values()) {
645                            searchEngine.removeBackup(companyId, backupName);
646                    }
647            }
648    
649            public synchronized static void removeBackup(String backupName)
650                    throws SearchException {
651    
652                    for (SearchEngine searchEngine : _searchEngines.values()) {
653                            for (long companyId : _companyIds.keySet()) {
654                                    searchEngine.removeBackup(companyId, backupName);
655                            }
656                    }
657            }
658    
659            public synchronized static void removeCompany(long companyId) {
660                    if (!_companyIds.containsKey(companyId)) {
661                            return;
662                    }
663    
664                    for (SearchEngine searchEngine : _searchEngines.values()) {
665                            searchEngine.removeCompany(companyId);
666                    }
667    
668                    _companyIds.remove(companyId);
669            }
670    
671            public static SearchEngine removeSearchEngine(String searchEngineId) {
672                    PortalRuntimePermission.checkSearchEngine(searchEngineId);
673    
674                    return _searchEngines.remove(searchEngineId);
675            }
676    
677            public synchronized static void restore(long companyId, String backupName)
678                    throws SearchException {
679    
680                    for (SearchEngine searchEngine : _searchEngines.values()) {
681                            searchEngine.restore(companyId, backupName);
682                    }
683            }
684    
685            public synchronized static void restore(String backupName)
686                    throws SearchException {
687    
688                    for (SearchEngine searchEngine : _searchEngines.values()) {
689                            for (long companyId : _companyIds.keySet()) {
690                                    searchEngine.restore(companyId, backupName);
691                            }
692                    }
693            }
694    
695            public static Hits search(SearchContext searchContext, Query query)
696                    throws SearchException {
697    
698                    if (_log.isDebugEnabled()) {
699                            _log.debug("Search query " + getQueryString(searchContext, query));
700                    }
701    
702                    SearchEngine searchEngine = getSearchEngine(
703                            searchContext.getSearchEngineId());
704    
705                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
706    
707                    return indexSearcher.search(searchContext, query);
708            }
709    
710            /**
711             * @deprecated As of 7.0.0, replaced by {@link #search(SearchContext,
712             *             Query)}
713             */
714            @Deprecated
715            public static Hits search(
716                            String searchEngineId, long companyId, Query query, int start,
717                            int end)
718                    throws SearchException {
719    
720                    return search(
721                            searchEngineId, companyId, query, SortFactoryUtil.getDefaultSorts(),
722                            start, end);
723            }
724    
725            /**
726             * @deprecated As of 7.0.0, replaced by {@link #search(SearchContext,
727             *             Query)}
728             */
729            @Deprecated
730            public static Hits search(
731                            String searchEngineId, long companyId, Query query, Sort sort,
732                            int start, int end)
733                    throws SearchException {
734    
735                    return search(
736                            searchEngineId, companyId, query, new Sort[] {sort}, start, end);
737            }
738    
739            /**
740             * @deprecated As of 7.0.0, replaced by {@link #search(SearchContext,
741             *             Query)}
742             */
743            @Deprecated
744            public static Hits search(
745                            String searchEngineId, long companyId, Query query, Sort[] sorts,
746                            int start, int end)
747                    throws SearchException {
748    
749                    SearchContext searchContext = new SearchContext();
750    
751                    searchContext.setCompanyId(companyId);
752                    searchContext.setEnd(end);
753                    searchContext.setSearchEngineId(searchEngineId);
754                    searchContext.setSorts(sorts);
755                    searchContext.setStart(start);
756    
757                    return search(searchContext, query);
758            }
759    
760            public static long searchCount(SearchContext searchContext, Query query)
761                    throws SearchException {
762    
763                    if (_log.isDebugEnabled()) {
764                            _log.debug("Search query " + getQueryString(searchContext, query));
765                    }
766    
767                    SearchEngine searchEngine = getSearchEngine(
768                            searchContext.getSearchEngineId());
769    
770                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
771    
772                    return indexSearcher.searchCount(searchContext, query);
773            }
774    
775            public static void setDefaultSearchEngineId(String defaultSearchEngineId) {
776                    PortalRuntimePermission.checkSetBeanProperty(
777                            SearchEngineUtil.class, "defaultSearchEngineId");
778    
779                    _defaultSearchEngineId = defaultSearchEngineId;
780            }
781    
782            public static void setIndexReadOnly(boolean indexReadOnly) {
783                    PortalRuntimePermission.checkSetBeanProperty(
784                            SearchEngineUtil.class, "indexReadOnly");
785    
786                    _indexReadOnly = indexReadOnly;
787            }
788    
789            public static void setSearchEngine(
790                    String searchEngineId, SearchEngine searchEngine) {
791    
792                    PortalRuntimePermission.checkSearchEngine(searchEngineId);
793    
794                    _searchEngines.put(searchEngineId, searchEngine);
795    
796                    for (Long companyId : _companyIds.keySet()) {
797                            searchEngine.initialize(companyId);
798                    }
799    
800                    synchronized (_queuingSearchEngines) {
801                            QueuingSearchEngine queuingSearchEngine = _queuingSearchEngines.get(
802                                    searchEngineId);
803    
804                            if (queuingSearchEngine != null) {
805                                    try {
806                                            queuingSearchEngine.invokeQueued(
807                                                    searchEngine.getIndexWriter());
808                                    }
809                                    catch (Exception e) {
810                                            if (_log.isWarnEnabled()) {
811                                                    _log.warn(
812                                                            "Unable to execute pending write events for " +
813                                                                    "engine: " + searchEngineId,
814                                                            e);
815                                            }
816                                    }
817                            }
818                    }
819            }
820    
821            public static String spellCheckKeywords(SearchContext searchContext)
822                    throws SearchException {
823    
824                    if (_log.isDebugEnabled()) {
825                            _log.debug("Spell checking " + searchContext.getKeywords());
826                    }
827    
828                    SearchEngine searchEngine = getSearchEngine(
829                            searchContext.getSearchEngineId());
830    
831                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
832    
833                    return indexSearcher.spellCheckKeywords(searchContext);
834            }
835    
836            public static Map<String, List<String>> spellCheckKeywords(
837                            SearchContext searchContext, int max)
838                    throws SearchException {
839    
840                    if (_log.isDebugEnabled()) {
841                            _log.debug("Spell checking " + searchContext.getKeywords());
842                    }
843    
844                    SearchEngine searchEngine = getSearchEngine(
845                            searchContext.getSearchEngineId());
846    
847                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
848    
849                    return indexSearcher.spellCheckKeywords(searchContext, max);
850            }
851    
852            public static String[] suggestKeywordQueries(
853                            SearchContext searchContext, int max)
854                    throws SearchException {
855    
856                    if (_log.isDebugEnabled()) {
857                            _log.debug(
858                                    "Suggesting keyword queries" + searchContext.getKeywords());
859                    }
860    
861                    SearchEngine searchEngine = getSearchEngine(
862                            searchContext.getSearchEngineId());
863    
864                    IndexSearcher indexSearcher = searchEngine.getIndexSearcher();
865    
866                    return indexSearcher.suggestKeywordQueries(searchContext, max);
867            }
868    
869            /**
870             * @deprecated As of 7.0.0, replaced by {@link #updateDocument(String, long,
871             *             Document, boolean)}
872             */
873            @Deprecated
874            public static void updateDocument(
875                            String searchEngineId, long companyId, Document document)
876                    throws SearchException {
877    
878                    updateDocument(searchEngineId, companyId, document, false);
879            }
880    
881            public static void updateDocument(
882                            String searchEngineId, long companyId, Document document,
883                            boolean commitImmediately)
884                    throws SearchException {
885    
886                    if (isIndexReadOnly()) {
887                            return;
888                    }
889    
890                    if (_log.isDebugEnabled()) {
891                            _log.debug("Document " + document.toString());
892                    }
893    
894                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
895    
896                    IndexWriter indexWriter = searchEngine.getIndexWriter();
897    
898                    _searchPermissionChecker.addPermissionFields(companyId, document);
899    
900                    SearchContext searchContext = new SearchContext();
901    
902                    searchContext.setCompanyId(companyId);
903                    searchContext.setSearchEngineId(searchEngineId);
904    
905                    setCommitImmediately(
906                            searchContext,
907                            commitImmediately || ProxyModeThreadLocal.isForceSync());
908    
909                    indexWriter.updateDocument(searchContext, document);
910            }
911    
912            /**
913             * @deprecated As of 7.0.0, replaced by {@link #updateDocuments(String,
914             *             long, Collection, boolean)}
915             */
916            @Deprecated
917            public static void updateDocuments(
918                            String searchEngineId, long companyId,
919                            Collection<Document> documents)
920                    throws SearchException {
921    
922                    updateDocuments(searchEngineId, companyId, documents, false);
923            }
924    
925            public static void updateDocuments(
926                            String searchEngineId, long companyId,
927                            Collection<Document> documents, boolean commitImmediately)
928                    throws SearchException {
929    
930                    if (isIndexReadOnly() || (documents == null) || documents.isEmpty()) {
931                            return;
932                    }
933    
934                    SearchEngine searchEngine = getSearchEngine(searchEngineId);
935    
936                    IndexWriter indexWriter = searchEngine.getIndexWriter();
937    
938                    for (Document document : documents) {
939                            if (_log.isDebugEnabled()) {
940                                    _log.debug("Document " + document.toString());
941                            }
942    
943                            _searchPermissionChecker.addPermissionFields(companyId, document);
944                    }
945    
946                    SearchContext searchContext = new SearchContext();
947    
948                    searchContext.setCompanyId(companyId);
949                    searchContext.setSearchEngineId(searchEngineId);
950    
951                    setCommitImmediately(searchContext, commitImmediately);
952    
953                    indexWriter.updateDocuments(searchContext, documents);
954            }
955    
956            public static void updatePermissionFields(String name, String primKey) {
957                    if (isIndexReadOnly()) {
958                            return;
959                    }
960    
961                    if (PermissionThreadLocal.isFlushResourcePermissionEnabled(
962                                    name, primKey)) {
963    
964                            _searchPermissionChecker.updatePermissionFields(name, primKey);
965                    }
966            }
967    
968            public void setExcludedEntryClassNames(
969                    List<String> excludedEntryClassNames) {
970    
971                    PortalRuntimePermission.checkSetBeanProperty(
972                            getClass(), "excludedEntryClassNames");
973    
974                    _excludedEntryClassNames.addAll(excludedEntryClassNames);
975            }
976    
977            public void setQueueCapacity(int queueCapacity) {
978                    _queueCapacity = queueCapacity;
979            }
980    
981            protected static void setCommitImmediately(
982                    SearchContext searchContext, boolean commitImmediately) {
983    
984                    if (!commitImmediately) {
985                            searchContext.setCommitImmediately(_INDEX_COMMIT_IMMEDIATELY);
986                    }
987                    else {
988                            searchContext.setCommitImmediately(true);
989                    }
990            }
991    
992            private SearchEngineUtil() {
993                    Registry registry = RegistryUtil.getRegistry();
994    
995                    _serviceTracker = registry.trackServices(
996                            SearchEngineConfigurator.class,
997                            new SearchEngineConfiguratorServiceTrackerCustomizer());
998    
999                    _serviceTracker.open();
1000            }
1001    
1002            private static final boolean _INDEX_COMMIT_IMMEDIATELY =
1003                    GetterUtil.getBoolean(
1004                            PropsUtil.get(PropsKeys.INDEX_COMMIT_IMMEDIATELY));
1005    
1006            private static final Log _log = LogFactoryUtil.getLog(
1007                    SearchEngineUtil.class);
1008    
1009            private static final Map<Long, Long> _companyIds =
1010                    new ConcurrentHashMap<>();
1011            private static String _defaultSearchEngineId;
1012            private static final Set<String> _excludedEntryClassNames = new HashSet<>();
1013            private static boolean _indexReadOnly = GetterUtil.getBoolean(
1014                    PropsUtil.get(PropsKeys.INDEX_READ_ONLY));
1015            private static int _queueCapacity = 200;
1016            private static final Map<String, QueuingSearchEngine>
1017                    _queuingSearchEngines = new HashMap<>();
1018            private static final Map<String, SearchEngine> _searchEngines =
1019                    new ConcurrentHashMap<>();
1020            private static final SearchPermissionChecker _searchPermissionChecker =
1021                    ProxyFactory.newServiceTrackedInstance(SearchPermissionChecker.class);
1022    
1023            private final ServiceTracker
1024                    <SearchEngineConfigurator, SearchEngineConfigurator> _serviceTracker;
1025    
1026            private class SearchEngineConfiguratorServiceTrackerCustomizer
1027                    implements ServiceTrackerCustomizer
1028                            <SearchEngineConfigurator, SearchEngineConfigurator> {
1029    
1030                    @Override
1031                    public SearchEngineConfigurator addingService(
1032                            ServiceReference<SearchEngineConfigurator> serviceReference) {
1033    
1034                            Registry registry = RegistryUtil.getRegistry();
1035    
1036                            SearchEngineConfigurator searchEngineConfigurator =
1037                                    registry.getService(serviceReference);
1038    
1039                            searchEngineConfigurator.afterPropertiesSet();
1040    
1041                            return searchEngineConfigurator;
1042                    }
1043    
1044                    @Override
1045                    public void modifiedService(
1046                            ServiceReference<SearchEngineConfigurator> serviceReference,
1047                            SearchEngineConfigurator searchEngineConfigurator) {
1048                    }
1049    
1050                    @Override
1051                    public void removedService(
1052                            ServiceReference<SearchEngineConfigurator> serviceReference,
1053                            SearchEngineConfigurator searchEngineConfigurator) {
1054    
1055                            Registry registry = RegistryUtil.getRegistry();
1056    
1057                            registry.ungetService(serviceReference);
1058    
1059                            searchEngineConfigurator.destroy();
1060                    }
1061    
1062            }
1063    
1064    }