All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.search.SortSpecParsing Maven / Gradle / Ivy

There is a newer version: 9.7.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.solr.search;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.lucene.queries.function.FunctionQuery;
import org.apache.lucene.queries.function.valuesource.QueryValueSource;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.solr.common.SolrException;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaField;

public class SortSpecParsing {

  public static final String DOCID = "_docid_";
  public static final String SCORE = "score";

  /**
   * The form of the sort specification string currently parsed is:
   *
   * 
   * SortSpec ::= SingleSort [, SingleSort]*
   * SingleSort ::= <fieldname|function> SortDirection
   * SortDirection ::= top | desc | bottom | asc
   * 
* * Examples: * *
   *   score desc               #normal sort by score (will return null)
   *   weight bottom            #sort by weight ascending
   *   weight desc              #sort by weight descending
   *   height desc,weight desc  #sort by height descending, and use weight descending to break any ties
   *   height desc,weight asc   #sort by height descending, using weight ascending as a tiebreaker
   * 
* * @return a SortSpec object populated with the appropriate Sort (which may be null if default * score sort is used) and SchemaFields (where applicable) using hardcoded default count & * offset values. */ public static SortSpec parseSortSpec(String sortSpec, SolrQueryRequest req) { return parseSortSpecImpl(sortSpec, req.getSchema(), req); } /** * The form of the (function free) sort specification string currently parsed is: * *
   * SortSpec ::= SingleSort [, SingleSort]*
   * SingleSort ::= <fieldname> SortDirection
   * SortDirection ::= top | desc | bottom | asc
   * 
* * Examples: * *
   *   score desc               #normal sort by score (will return null)
   *   weight bottom            #sort by weight ascending
   *   weight desc              #sort by weight descending
   *   height desc,weight desc  #sort by height descending, and use weight descending to break any ties
   *   height desc,weight asc   #sort by height descending, using weight ascending as a tiebreaker
   * 
* * @return a SortSpec object populated with the appropriate Sort (which may be null if default * score sort is used) and SchemaFields (where applicable) using hardcoded default count & * offset values. */ public static SortSpec parseSortSpec(String sortSpec, IndexSchema schema) { return parseSortSpecImpl(sortSpec, schema, null); } private static SortSpec parseSortSpecImpl( String sortSpec, IndexSchema schema, SolrQueryRequest optionalReq) { if (sortSpec == null || sortSpec.length() == 0) return newEmptySortSpec(); List sorts = new ArrayList<>(4); List fields = new ArrayList<>(4); try { StrParser sp = new StrParser(sortSpec); while (sp.pos < sp.end) { sp.eatws(); final int start = sp.pos; // short circuit test for a really simple field name String field = sp.getId(null); Exception qParserException = null; if ((field == null || !Character.isWhitespace(sp.peekChar())) && (optionalReq != null)) { // let's try it as a function instead field = null; String funcStr = sp.val.substring(start); QParser parser = QParser.getParser(funcStr, FunctionQParserPlugin.NAME, optionalReq); Query q = null; try { if (parser instanceof FunctionQParser) { FunctionQParser fparser = (FunctionQParser) parser; fparser.setParseMultipleSources(false); fparser.setParseToEnd(false); q = fparser.getQuery(); if (fparser.localParams != null) { if (fparser.valFollowedParams) { // need to find the end of the function query via the string parser int leftOver = fparser.sp.end - fparser.sp.pos; sp.pos = sp.end - leftOver; // reset our parser to the same amount of leftover } else { // the value was via the "v" param in localParams, so we need to find // the end of the local params themselves to pick up where we left off sp.pos = start + fparser.localParamsEnd; } } else { // need to find the end of the function query via the string parser int leftOver = fparser.sp.end - fparser.sp.pos; sp.pos = sp.end - leftOver; // reset our parser to the same amount of leftover } } else { // A QParser that's not for function queries. // It must have been specified via local params. q = parser.getQuery(); assert parser.getLocalParams() != null; sp.pos = start + parser.localParamsEnd; } Boolean top = sp.getSortDirection(); if (null != top) { // we have a Query and a valid direction if (q instanceof FunctionQuery) { sorts.add(((FunctionQuery) q).getValueSource().getSortField(top)); } else { sorts.add((new QueryValueSource(q, 0.0f)).getSortField(top)); } fields.add(null); continue; } } catch (Exception e) { // hang onto this in case the string isn't a full field name either qParserException = e; } } // if we made it here, we either have a "simple" field name, // or there was a problem parsing the string as a complex func/quer if (field == null) { // try again, simple rules for a field name with no whitespace sp.pos = start; field = sp.getSimpleString(); } Boolean top = sp.getSortDirection(); if (null == top) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Can't determine a Sort Order (asc or desc) in sort spec " + sp); } if (SCORE.equals(field)) { if (top) { sorts.add(SortField.FIELD_SCORE); } else { sorts.add(new SortField(null, SortField.Type.SCORE, true)); } fields.add(null); } else if (DOCID.equals(field)) { sorts.add(new SortField(null, SortField.Type.DOC, top)); fields.add(null); } else { // try to find the field SchemaField sf = schema.getFieldOrNull(field); if (null == sf) { if (null != qParserException) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "sort param could not be parsed as a query, and is not a " + "field that exists in the index: " + field, qParserException); } throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "sort param field can't be found: " + field); } sorts.add(sf.getSortField(top)); fields.add(sf); } } } catch (SyntaxError e) { throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "error in sort: " + sortSpec, e); } // normalize a sort on score desc to null if (sorts.size() == 1 && SortField.FIELD_SCORE.equals(sorts.get(0))) { return newEmptySortSpec(); } Sort s = new Sort(sorts.toArray(new SortField[0])); return new SortSpec(s, fields); } private static SortSpec newEmptySortSpec() { return new SortSpec(null, Collections.emptyList()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy