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