From e26c56db7784941df4355a00740fce61da9a6911 Mon Sep 17 00:00:00 2001 From: Holger Voormann Date: Tue, 4 Feb 2025 20:50:15 +0100 Subject: [PATCH] Refactor org.eclipse.help.internal.search.SearchIndex#openSearcher() In `org.eclipse.help.internal.search.SearchIndex`, refactor the `openSearcher()` method to return an `AutoCloseable` object so that it can be used in a try-with-resources statement that implicitly registers and unregisters the search. Minor performance improvement: Use double-checked locking idiom for lazy initialization of the `IndexSearcher`. Grant access to the `searches` and `analyzerDescriptor` fields via `SearcherInfo#getIndexSearcher()` and `SearcherInfo#getAnalyzerDescriptor()`. These two getter methods are not (yet) used internally. Their only purpose is to be able to implement a hybrid search that combines a semantic search with a sparse keyword search. And the sparse keyword search uses the existing `SearchIndex`, which requires access to these fields. --- .../help/internal/search/SearchIndex.java | 95 +++++++++++-------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/ua/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchIndex.java b/ua/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchIndex.java index daf6b7bc5d0..18d59efd843 100644 --- a/ua/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchIndex.java +++ b/ua/org.eclipse.help.base/src/org/eclipse/help/internal/search/SearchIndex.java @@ -133,7 +133,7 @@ public class SearchIndex implements IHelpSearchIndex { private final HTMLSearchParticipant htmlSearchParticipant; - private IndexSearcher searcher; + private volatile IndexSearcher searcher; private final Object searcherCreateLock = new Object(); @@ -141,12 +141,34 @@ public class SearchIndex implements IHelpSearchIndex { private volatile boolean closed = false; - // Collection of searches occuring now + // Collection of searches occurring now private final Collection searches = new ArrayList<>(); private FileLock lock; private RandomAccessFile raf = null; + public class SearcherInfo implements AutoCloseable { + + private SearcherInfo() { + } + + @Override + public void close() throws Exception { + synchronized (searches) { + searches.remove(Thread.currentThread()); + } + } + + public IndexSearcher getIndexSearcher() { + return searcher; + } + + public AnalyzerDescriptor getAnalyzerDescriptor() { + return analyzerDescriptor; + } + + } + /** * Constructor. * @@ -629,38 +651,26 @@ public boolean exists() { /** * Performs a query search on this index */ - public void search(ISearchQuery searchQuery, ISearchHitCollector collector) - throws QueryTooComplexException { - try { - if (closed) - return; - registerSearch(Thread.currentThread()); - if (closed) - return; + public void search(ISearchQuery searchQuery, ISearchHitCollector collector) throws QueryTooComplexException { + try (var searcherInfo = openSearcher()) { + if (searcherInfo == null) + return; // already closed QueryBuilder queryBuilder = new QueryBuilder(searchQuery.getSearchWord(), analyzerDescriptor); - Query luceneQuery = queryBuilder.getLuceneQuery(searchQuery.getFieldNames(), searchQuery - .isFieldSearch()); + Query luceneQuery = queryBuilder.getLuceneQuery(searchQuery.getFieldNames(), searchQuery.isFieldSearch()); if (HelpPlugin.DEBUG_SEARCH) { - System.out.println("Search Query: " + luceneQuery.toString()); //$NON-NLS-1$ - } - String highlightTerms = queryBuilder.gethighlightTerms(); - if (luceneQuery != null) { - if (searcher == null) { - openSearcher(); - } - TopDocs topDocs = searcher.search(luceneQuery, 1000); - collector.addHits(LocalSearchManager.asList(topDocs, searcher), highlightTerms); + System.out.println("Search Query: " + luceneQuery); //$NON-NLS-1$ } + if (luceneQuery == null) + return; + TopDocs topDocs = searcher.search(luceneQuery, 1000); + collector.addHits(LocalSearchManager.asList(topDocs, searcher), queryBuilder.gethighlightTerms()); } catch (IndexSearcher.TooManyClauses tmc) { collector.addQTCException(new QueryTooComplexException()); } catch (QueryTooComplexException qe) { collector.addQTCException(qe); } catch (Exception e) { - ILog.of(getClass()).error( - "Exception occurred performing search for: " //$NON-NLS-1$ + ILog.of(getClass()).error("Exception occurred performing search for: " //$NON-NLS-1$ + searchQuery.getSearchWord() + ".", e); //$NON-NLS-1$ - } finally { - unregisterSearch(Thread.currentThread()); } } @@ -806,12 +816,27 @@ public void setInconsistent(boolean inconsistent) { } @SuppressWarnings("resource") - public void openSearcher() throws IOException { - synchronized (searcherCreateLock) { - if (searcher == null) { - searcher = new IndexSearcher(DirectoryReader.open(luceneDirectory)); + public SearcherInfo openSearcher() { + if (closed) + return null; + synchronized (searches) { + searches.add(Thread.currentThread()); + } + if (closed) + return null; + if (searcher == null) { + synchronized (searcherCreateLock) { + if (searcher == null) { + try { + searcher = new IndexSearcher(DirectoryReader.open(luceneDirectory)); + } catch (Exception e) { + ILog.of(getClass()).error("Failed to open search index. Index directory: " + indexDir, e); //$NON-NLS-1$ + return null; + } + } } } + return new SearcherInfo(); } /** @@ -929,18 +954,6 @@ public TocManager getTocManager() { return tocManager; } - private void registerSearch(Thread t) { - synchronized (searches) { - searches.add(t); - } - } - - private void unregisterSearch(Thread t) { - synchronized (searches) { - searches.remove(t); - } - } - /** * @return Returns the closed. */