11package org .labkey .jbrowse ;
22
33import org .apache .commons .lang3 .StringUtils ;
4+ import org .apache .logging .log4j .Logger ;
45import org .apache .lucene .analysis .Analyzer ;
56import org .apache .lucene .analysis .standard .StandardAnalyzer ;
67import org .apache .lucene .document .Document ;
1516import org .apache .lucene .search .BooleanClause ;
1617import org .apache .lucene .search .BooleanQuery ;
1718import org .apache .lucene .search .IndexSearcher ;
19+ import org .apache .lucene .search .LRUQueryCache ;
1820import org .apache .lucene .search .MatchAllDocsQuery ;
1921import org .apache .lucene .search .Query ;
22+ import org .apache .lucene .search .QueryCache ;
23+ import org .apache .lucene .search .QueryCachingPolicy ;
2024import org .apache .lucene .search .Sort ;
2125import org .apache .lucene .search .SortField ;
2226import org .apache .lucene .search .TopFieldDocs ;
27+ import org .apache .lucene .search .UsageTrackingQueryCachingPolicy ;
2328import org .apache .lucene .store .Directory ;
2429import org .apache .lucene .store .FSDirectory ;
25- import org .apache .lucene .util .NumericUtils ;
2630import org .jetbrains .annotations .Nullable ;
31+ import org .json .JSONArray ;
2732import org .json .JSONObject ;
33+ import org .labkey .api .cache .Cache ;
34+ import org .labkey .api .cache .CacheManager ;
2835import org .labkey .api .data .Container ;
2936import org .labkey .api .data .ContainerManager ;
3037import org .labkey .api .jbrowse .AbstractJBrowseFieldCustomizer ;
3340import org .labkey .api .module .ModuleLoader ;
3441import org .labkey .api .security .User ;
3542import org .labkey .api .settings .AppProps ;
43+ import org .labkey .api .util .logging .LogHelper ;
3644import org .labkey .jbrowse .model .JBrowseSession ;
3745import org .labkey .jbrowse .model .JsonFile ;
3846
5159import java .util .StringTokenizer ;
5260import java .util .regex .Matcher ;
5361import java .util .regex .Pattern ;
54- import java .util .stream .Collectors ;
5562
5663import static org .labkey .jbrowse .JBrowseFieldUtils .VARIABLE_SAMPLES ;
5764import static org .labkey .jbrowse .JBrowseFieldUtils .getSession ;
5865import static org .labkey .jbrowse .JBrowseFieldUtils .getTrack ;
5966
6067public class JBrowseLuceneSearch
6168{
69+ private static final Logger _log = LogHelper .getLogger (JBrowseLuceneSearch .class , "Logger related to JBrowse/Lucene indexing and queries" );
6270 private final JBrowseSession _session ;
6371 private final JsonFile _jsonFile ;
6472 private final User _user ;
6573 private final String [] specialStartPatterns = {"*:* -" , "+" , "-" };
6674 private static final String ALL_DOCS = "all" ;
6775 private static final String GENOMIC_POSITION = "genomicPosition" ;
76+ private static final int maxCachedQueries = 1000 ;
77+ private static final long maxRamBytesUsed = 250 * 1024 * 1024L ;
78+
79+ private static final Cache <String , LRUQueryCache > _cache = CacheManager .getStringKeyCache (1000 , CacheManager .UNLIMITED , "JBrowseLuceneSearchCache" );
6880
6981 private JBrowseLuceneSearch (final JBrowseSession session , final JsonFile jsonFile , User u )
7082 {
@@ -85,6 +97,17 @@ public static JBrowseLuceneSearch create(String sessionId, String trackId, User
8597 return new JBrowseLuceneSearch (session , getTrack (session , trackId , u ), u );
8698 }
8799
100+ private static synchronized QueryCache getCacheForSession (String trackObjectId ) {
101+ LRUQueryCache qc = _cache .get (trackObjectId );
102+ if (qc == null )
103+ {
104+ qc = new LRUQueryCache (maxCachedQueries , maxRamBytesUsed );
105+ _cache .put (trackObjectId , qc );
106+ }
107+
108+ return qc ;
109+ }
110+
88111 private String templateReplace (final String searchString ) {
89112 String result = searchString ;
90113 Pattern pattern = Pattern .compile ("~(.*?)~" );
@@ -148,6 +171,8 @@ public JSONObject doSearch(User u, String searchString, final int pageSize, fina
148171 )
149172 {
150173 IndexSearcher indexSearcher = new IndexSearcher (indexReader );
174+ indexSearcher .setQueryCache (getCacheForSession (_jsonFile .getObjectId ()));
175+ indexSearcher .setQueryCachingPolicy (new ForceMatchAllDocsCachingPolicy ());
151176
152177 List <String > stringQueryParserFields = new ArrayList <>();
153178 Map <String , SortField .Type > numericQueryParserFields = new HashMap <>();
@@ -263,7 +288,7 @@ else if (numericQueryParserFields.containsKey(fieldName))
263288 for (int i = pageSize * offset ; i < Math .min (pageSize * (offset + 1 ), topDocs .scoreDocs .length ); i ++)
264289 {
265290 JSONObject elem = new JSONObject ();
266- Document doc = indexSearcher .doc (topDocs .scoreDocs [i ].doc );
291+ Document doc = indexSearcher .storedFields (). document (topDocs .scoreDocs [i ].doc );
267292
268293 for (IndexableField field : doc .getFields ()) {
269294 String fieldName = field .name ();
@@ -345,4 +370,70 @@ public boolean isAvailable(Container c, User u)
345370 return true ;
346371 }
347372 }
373+
374+ public static class ForceMatchAllDocsCachingPolicy implements QueryCachingPolicy {
375+ private final UsageTrackingQueryCachingPolicy defaultPolicy = new UsageTrackingQueryCachingPolicy ();
376+
377+ @ Override
378+ public boolean shouldCache (Query query ) throws IOException {
379+ if (query instanceof BooleanQuery bq ) {
380+ for (BooleanClause clause : bq ) {
381+ if (clause .getQuery () instanceof MatchAllDocsQuery ) {
382+ return true ;
383+ }
384+ }
385+ }
386+
387+ return defaultPolicy .shouldCache (query );
388+ }
389+
390+ @ Override
391+ public void onUse (Query query ) {
392+ defaultPolicy .onUse (query );
393+ }
394+ }
395+
396+ public static JSONArray reportCacheInfo ()
397+ {
398+ JSONArray cacheInfo = new JSONArray ();
399+ for (String sessionId : _cache .getKeys ())
400+ {
401+ LRUQueryCache qc = _cache .get (sessionId );
402+ JSONObject info = new JSONObject ();
403+ info .put ("cacheSize" , qc .getCacheSize ());
404+ info .put ("cacheCount" , qc .getCacheCount ());
405+ info .put ("hitCount" , qc .getHitCount ());
406+ info .put ("missCount" , qc .getMissCount ());
407+ info .put ("evictionCount" , qc .getEvictionCount ());
408+ info .put ("totalCount" , qc .getTotalCount ());
409+ cacheInfo .put (info );
410+ }
411+
412+ return cacheInfo ;
413+ }
414+
415+ public void cacheDefaultQuery ()
416+ {
417+ try
418+ {
419+ JBrowseLuceneSearch .clearCache (_jsonFile .getObjectId ());
420+ doSearch (_user , ALL_DOCS , 100 , 0 , GENOMIC_POSITION , false );
421+ }
422+ catch (ParseException | IOException e )
423+ {
424+ _log .error ("Unable to cache default query for: " + _jsonFile .getObjectId (), e );
425+ }
426+ }
427+
428+ public static void clearCache (@ Nullable String jbrowseTrackId )
429+ {
430+ if (jbrowseTrackId == null )
431+ {
432+ _cache .clear ();
433+ }
434+ else
435+ {
436+ _cache .remove (jbrowseTrackId );
437+ }
438+ }
348439}
0 commit comments