2222import org .apache .lucene .search .TopFieldDocs ;
2323import org .apache .lucene .store .Directory ;
2424import org .apache .lucene .store .FSDirectory ;
25+ import org .apache .lucene .util .NumericUtils ;
2526import org .jetbrains .annotations .Nullable ;
2627import org .json .JSONObject ;
2728import org .labkey .api .data .Container ;
5051import java .util .StringTokenizer ;
5152import java .util .regex .Matcher ;
5253import java .util .regex .Pattern ;
54+ import java .util .stream .Collectors ;
5355
5456import static org .labkey .jbrowse .JBrowseFieldUtils .VARIABLE_SAMPLES ;
5557import static org .labkey .jbrowse .JBrowseFieldUtils .getSession ;
@@ -61,6 +63,8 @@ public class JBrowseLuceneSearch
6163 private final JsonFile _jsonFile ;
6264 private final User _user ;
6365 private final String [] specialStartPatterns = {"*:* -" , "+" , "-" };
66+ private static final String ALL_DOCS = "all" ;
67+ private static final String GENOMIC_POSITION = "genomicPosition" ;
6468
6569 private JBrowseLuceneSearch (final JBrowseSession session , final JsonFile jsonFile , User u )
6670 {
@@ -130,7 +134,7 @@ public String extractFieldName(String queryString) {
130134 return parts .length > 0 ? parts [0 ].trim () : null ;
131135 }
132136
133- public JSONObject doSearch (User u , String searchString , final int pageSize , final int offset ) throws IOException , ParseException
137+ public JSONObject doSearch (User u , String searchString , final int pageSize , final int offset , String sortField , boolean sortReverse ) throws IOException , ParseException
134138 {
135139 searchString = tryUrlDecode (searchString );
136140 File indexPath = _jsonFile .getExpectedLocationOfLuceneIndex (true );
@@ -146,7 +150,7 @@ public JSONObject doSearch(User u, String searchString, final int pageSize, fina
146150 IndexSearcher indexSearcher = new IndexSearcher (indexReader );
147151
148152 List <String > stringQueryParserFields = new ArrayList <>();
149- List <String > numericQueryParserFields = new ArrayList <>();
153+ Map <String , SortField . Type > numericQueryParserFields = new HashMap <>();
150154 PointsConfig intPointsConfig = new PointsConfig (new DecimalFormat (), Integer .class );
151155 PointsConfig doublePointsConfig = new PointsConfig (new DecimalFormat (), Double .class );
152156 Map <String , PointsConfig > pointsConfigMap = new HashMap <>();
@@ -161,11 +165,11 @@ public JSONObject doSearch(User u, String searchString, final int pageSize, fina
161165 {
162166 case Flag , String , Character -> stringQueryParserFields .add (field );
163167 case Float -> {
164- numericQueryParserFields .add (field );
168+ numericQueryParserFields .put (field , SortField . Type . DOUBLE );
165169 pointsConfigMap .put (field , doublePointsConfig );
166170 }
167171 case Integer -> {
168- numericQueryParserFields .add (field );
172+ numericQueryParserFields .put (field , SortField . Type . INT );
169173 pointsConfigMap .put (field , intPointsConfig );
170174 }
171175 }
@@ -182,14 +186,14 @@ public JSONObject doSearch(User u, String searchString, final int pageSize, fina
182186
183187 BooleanQuery .Builder booleanQueryBuilder = new BooleanQuery .Builder ();
184188
185- if (searchString .equals ("all" )) {
189+ if (searchString .equals (ALL_DOCS )) {
186190 booleanQueryBuilder .add (new MatchAllDocsQuery (), BooleanClause .Occur .MUST );
187191 }
188192
189193 // Split input into tokens, 1 token per query separated by &
190194 StringTokenizer tokenizer = new StringTokenizer (searchString , "&" );
191195
192- while (tokenizer .hasMoreTokens () && !searchString .equals ("all" ))
196+ while (tokenizer .hasMoreTokens () && !searchString .equals (ALL_DOCS ))
193197 {
194198 String queryString = tokenizer .nextToken ();
195199 Query query = null ;
@@ -205,7 +209,7 @@ public JSONObject doSearch(User u, String searchString, final int pageSize, fina
205209 {
206210 query = queryParser .parse (queryString );
207211 }
208- else if (numericQueryParserFields .contains (fieldName ))
212+ else if (numericQueryParserFields .containsKey (fieldName ))
209213 {
210214 try
211215 {
@@ -226,16 +230,28 @@ else if (numericQueryParserFields.contains(fieldName))
226230
227231 BooleanQuery query = booleanQueryBuilder .build ();
228232
233+ // By default, sort in INDEXORDER, which is by genomicPosition
234+ Sort sort = Sort .INDEXORDER ;
235+
236+ // If the sort field is not genomicPosition, use the provided sorting data
237+ if (!sortField .equals (GENOMIC_POSITION )) {
238+ SortField .Type fieldType ;
239+
240+ if (stringQueryParserFields .contains (sortField )) {
241+ fieldType = SortField .Type .STRING ;
242+ } else if (numericQueryParserFields .containsKey (sortField )) {
243+ fieldType = numericQueryParserFields .get (sortField );
244+ } else {
245+ throw new IllegalArgumentException ("Could not find type for sort field: " + sortField );
246+ }
247+
248+ sort = new Sort (new SortField (sortField , fieldType , sortReverse ));
249+ }
250+
229251 // Get chunks of size {pageSize}. Default to 1 chunk -- add to the offset to get more.
230252 // We then iterate over the range of documents we want based on the offset. This does grow in memory
231253 // linearly with the number of documents, but my understanding is that these are just score,id pairs
232254 // rather than full documents, so mem usage *should* still be pretty low.
233- //TopDocs topDocs = indexSearcher.search(query, pageSize * (offset + 1));
234-
235- // Define sort field
236- SortField sortField = new SortField ("pos" , SortField .Type .INT , false );
237- Sort sort = new Sort (sortField );
238-
239255 // Perform the search with sorting
240256 TopFieldDocs topDocs = indexSearcher .search (query , pageSize * (offset + 1 ), sort );
241257
@@ -253,10 +269,8 @@ else if (numericQueryParserFields.contains(fieldName))
253269 String fieldName = field .name ();
254270 String [] fieldValues = doc .getValues (fieldName );
255271 if (fieldValues .length > 1 ) {
256- // If there is more than one value, put the array of values into the JSON object.
257272 elem .put (fieldName , fieldValues );
258273 } else {
259- // If there is only one value, just put this single value into the JSON object.
260274 elem .put (fieldName , fieldValues [0 ]);
261275 }
262276 }
0 commit comments