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