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