@NotNull @ThreadSafe private static QueryWrapper createQuery(@NotNull String queryString) throws SearchException { PhraseDetectingQueryParser queryParser = new PhraseDetectingQueryParser( IndexRegistry.LUCENE_VERSION, Fields.CONTENT.key(), IndexRegistry.getAnalyzer()); queryParser.setAllowLeadingWildcard(true); RewriteMethod rewriteMethod = MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE; queryParser.setMultiTermRewriteMethod(rewriteMethod); if (!SettingsConf.Bool.UseOrOperator.get()) queryParser.setDefaultOperator(QueryParser.AND_OPERATOR); try { Query query = queryParser.parse(queryString); boolean isPhraseQuery = queryParser.isPhraseQuery(); return new QueryWrapper(query, isPhraseQuery); } catch (IllegalArgumentException e) { /* * This happens for example when you enter a fuzzy search with * similarity >= 1, e.g. "fuzzy~1". */ String msg = Msg.invalid_query.get() + "\n\n" + e.getMessage(); throw new SearchException(msg); } catch (ParseException e) { String msg = Msg.invalid_query.get() + "\n\n" + e.getMessage(); throw new SearchException(msg); } }
/** Updates the cached indexes and replaces the current Lucene searcher with a new one. */ @ThreadSafe @VisibleForPackageGroup public void replaceLuceneSearcher() { writeLock.lock(); try { Closeables.close(luceneSearcher, false); setLuceneSearcher(indexRegistry.getIndexes()); } catch (IOException e) { ioException = e; // Will be thrown later } finally { writeLock.unlock(); } }
/** * Disposes of the receiver. The caller should make sure that no more search requests are * submitted to the receiver after this method is called. */ @ThreadSafe public void shutdown() { if (ioException != null) Util.printErr(ioException); writeLock.lock(); try { indexRegistry.removeListeners(addedListener, null); Closeables.closeQuietly(luceneSearcher); } finally { writeLock.unlock(); } /* * This should be done after closing the Lucene searcher in order to * ensure that no indexes will be deleted outside the deletion queue * while the Lucene searcher is still open. */ synchronized (this) { deletionThread.interrupt(); } }
/** * This method should not be called by clients. Use {@link IndexRegistry#getSearcher()} instead. * * @param corruptedIndexes A list that will be filled by this constructor with indexes that * couldn't be loaded. */ @VisibleForPackageGroup public Searcher( @NotNull IndexRegistry indexRegistry, @NotNull FileFactory fileFactory, @NotNull OutlookMailFactory outlookMailFactory, @NotNull final List<CorruptedIndex> corruptedIndexes) throws IOException { Util.checkNotNull(indexRegistry, fileFactory, outlookMailFactory); this.indexRegistry = indexRegistry; this.fileFactory = fileFactory; this.outlookMailFactory = outlookMailFactory; readLock = indexRegistry.getReadLock(); writeLock = indexRegistry.getWriteLock(); // Handler for index additions addedListener = new Event.Listener<LuceneIndex>() { public void update(LuceneIndex eventData) { replaceLuceneSearcher(); } }; /* * This lock could be moved into the indexes handler, but we'll put it * here to avoid releasing and reacquiring it. */ writeLock.lock(); try { indexRegistry.addListeners( new ExistingIndexesHandler() { // Handle existing indexes public void handleExistingIndexes(List<LuceneIndex> indexes) { try { corruptedIndexes.addAll(setLuceneSearcher(indexes)); } catch (IOException e) { ioException = e; } } }, addedListener, null); // removedListener is null, see deletion thread below } finally { writeLock.unlock(); } if (ioException != null) throw ioException; // Handler for index removals deletionThread = new Thread(Searcher.class.getName() + " (Approve pending deletions)") { public void run() { while (true) { try { List<PendingDeletion> deletions = deletionQueue.take(); replaceLuceneSearcher(); for (PendingDeletion deletion : deletions) deletion.setApprovedBySearcher(); } catch (InterruptedException e) { break; } } } }; deletionThread.start(); }