mirror of https://github.com/apache/lucene.git
SOLR-1297: fix sort by function parsing
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1003107 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d029a5f222
commit
708cea853c
|
@ -118,7 +118,7 @@ New Features
|
|||
|
||||
* SOLR-1625: Add regexp support for TermsComponent (Uri Boness via noble)
|
||||
|
||||
* SOLR-1297: Add sort by Function capability (gsingers)
|
||||
* SOLR-1297: Add sort by Function capability (gsingers, yonik)
|
||||
|
||||
* SOLR-1139: Add TermsComponent Query and Response Support in SolrJ (Matt Weber via shalin)
|
||||
|
||||
|
|
|
@ -194,7 +194,9 @@ public class QueryComponent extends SearchComponent
|
|||
String[] funcs = params.getParams(GroupParams.GROUP_FUNC);
|
||||
String[] queries = params.getParams(GroupParams.GROUP_QUERY);
|
||||
String groupSortStr = params.get(GroupParams.GROUP_SORT);
|
||||
Sort groupSort = groupSortStr != null ? QueryParsing.parseSort(groupSortStr, req.getSchema()) : null;
|
||||
|
||||
// TODO: don't use groupSort==null to test for the presense of a sort since "score desc" will normalize to null
|
||||
Sort groupSort = groupSortStr != null ? QueryParsing.parseSort(groupSortStr, req) : null;
|
||||
|
||||
int limitDefault = cmd.getLen(); // this is normally from "rows"
|
||||
int docsPerGroupDefault = params.getInt(GroupParams.GROUP_LIMIT, 1);
|
||||
|
|
|
@ -48,7 +48,7 @@ public class FunctionQParser extends QParser {
|
|||
}
|
||||
|
||||
public void setParseToEnd(boolean parseToEnd) {
|
||||
this.parseMultipleSources = parseMultipleSources;
|
||||
this.parseToEnd = parseToEnd;
|
||||
}
|
||||
|
||||
/** throw exception if there is extra stuff at the end of the parsed valuesource(s). */
|
||||
|
|
|
@ -117,7 +117,7 @@ class OldLuceneQParser extends LuceneQParser {
|
|||
public SortSpec getSort(boolean useGlobal) throws ParseException {
|
||||
SortSpec sort = super.getSort(useGlobal);
|
||||
if (sortStr != null && sortStr.length()>0 && sort.getSort()==null) {
|
||||
Sort oldSort = QueryParsing.parseSort(sortStr, getReq().getSchema());
|
||||
Sort oldSort = QueryParsing.parseSort(sortStr, getReq());
|
||||
if( oldSort != null ) {
|
||||
sort.sort = oldSort;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.apache.lucene.queryParser.ParseException;
|
|||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.Sort;
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
import org.apache.solr.common.params.MapSolrParams;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.StrUtils;
|
||||
|
@ -41,6 +42,10 @@ public abstract class QParser {
|
|||
|
||||
protected Query query;
|
||||
|
||||
protected String stringIncludingLocalParams; // the original query string including any local params
|
||||
protected boolean valFollowedParams; // true if the value "qstr" followed the localParams
|
||||
protected int localParamsEnd; // the position one past where the localParams ended
|
||||
|
||||
/**
|
||||
* Constructor for the QParser
|
||||
* @param qstr The part of the query string specific to this parser
|
||||
|
@ -214,7 +219,7 @@ public abstract class QParser {
|
|||
|
||||
Sort sort = null;
|
||||
if( sortStr != null ) {
|
||||
sort = QueryParsing.parseSort(sortStr, req.getSchema());
|
||||
sort = QueryParsing.parseSort(sortStr, req);
|
||||
}
|
||||
return new SortSpec( sort, start, rows );
|
||||
}
|
||||
|
@ -241,7 +246,32 @@ public abstract class QParser {
|
|||
* then the prefix query parser will be used.
|
||||
*/
|
||||
public static QParser getParser(String qstr, String defaultType, SolrQueryRequest req) throws ParseException {
|
||||
SolrParams localParams = QueryParsing.getLocalParams(qstr, req.getParams());
|
||||
// SolrParams localParams = QueryParsing.getLocalParams(qstr, req.getParams());
|
||||
|
||||
String stringIncludingLocalParams = qstr;
|
||||
SolrParams localParams = null;
|
||||
SolrParams globalParams = req.getParams();
|
||||
boolean valFollowedParams = true;
|
||||
int localParamsEnd = -1;
|
||||
|
||||
if (qstr != null && qstr.startsWith(QueryParsing.LOCALPARAM_START)) {
|
||||
Map<String, String> localMap = new HashMap<String, String>();
|
||||
localParamsEnd = QueryParsing.parseLocalParams(qstr, 0, localMap, globalParams);
|
||||
|
||||
String val = localMap.get(QueryParsing.V);
|
||||
if (val != null) {
|
||||
// val was directly specified in localParams via v=<something> or v=$arg
|
||||
valFollowedParams = false;
|
||||
} else {
|
||||
// use the remainder of the string as the value
|
||||
valFollowedParams = true;
|
||||
val = qstr.substring(localParamsEnd);
|
||||
localMap.put(QueryParsing.V, val);
|
||||
}
|
||||
localParams = new MapSolrParams(localMap);
|
||||
}
|
||||
|
||||
|
||||
String type;
|
||||
|
||||
if (localParams == null) {
|
||||
|
@ -254,7 +284,12 @@ public abstract class QParser {
|
|||
type = type==null ? QParserPlugin.DEFAULT_QTYPE : type;
|
||||
|
||||
QParserPlugin qplug = req.getCore().getQueryPlugin(type);
|
||||
return qplug.createParser(qstr, localParams, req.getParams(), req);
|
||||
}
|
||||
QParser parser = qplug.createParser(qstr, localParams, req.getParams(), req);
|
||||
|
||||
parser.stringIncludingLocalParams = stringIncludingLocalParams;
|
||||
parser.valFollowedParams = valFollowedParams;
|
||||
parser.localParamsEnd = localParamsEnd;
|
||||
return parser;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,10 +38,12 @@ import org.apache.solr.common.params.MapSolrParams;
|
|||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.request.LocalSolrQueryRequest;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.schema.FieldType;
|
||||
import org.apache.solr.schema.IndexSchema;
|
||||
import org.apache.solr.schema.SchemaField;
|
||||
import org.apache.solr.search.function.FunctionQuery;
|
||||
import org.apache.solr.search.function.QueryValueSource;
|
||||
import org.apache.solr.search.function.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -66,6 +68,10 @@ public class QueryParsing {
|
|||
public static final char LOCALPARAM_END = '}';
|
||||
public static final String DOCID = "_docid_";
|
||||
|
||||
// true if the value was specified by the "v" param (i.e. v=myval, or v=$param)
|
||||
public static final String VAL_EXPLICIT = "__VAL_EXPLICIT__";
|
||||
|
||||
|
||||
/**
|
||||
* Returns the "prefered" default operator for use by Query Parsers,
|
||||
* based on the settings in the IndexSchema which may be overridden using
|
||||
|
@ -253,20 +259,17 @@ public class QueryParsing {
|
|||
Map<String, String> localParams = new HashMap<String, String>();
|
||||
int start = QueryParsing.parseLocalParams(txt, 0, localParams, params);
|
||||
|
||||
String val;
|
||||
if (start >= txt.length()) {
|
||||
// if the rest of the string is empty, check for "v" to provide the value
|
||||
val = localParams.get(V);
|
||||
val = val == null ? "" : val;
|
||||
} else {
|
||||
String val = localParams.get(V);
|
||||
if (val == null) {
|
||||
val = txt.substring(start);
|
||||
localParams.put(V, val);
|
||||
} else {
|
||||
// localParams.put(VAL_EXPLICIT, "true");
|
||||
}
|
||||
localParams.put(V, val);
|
||||
return new MapSolrParams(localParams);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns null if the sortSpec is the standard sort desc.
|
||||
* <p/>
|
||||
|
@ -287,148 +290,127 @@ public class QueryParsing {
|
|||
* height desc,weight asc #sort by height descending, using weight ascending as a tiebreaker
|
||||
* </pre>
|
||||
*/
|
||||
public static Sort parseSort(String sortSpec, IndexSchema schema) {
|
||||
public static Sort parseSort(String sortSpec, SolrQueryRequest req) {
|
||||
if (sortSpec == null || sortSpec.length() == 0) return null;
|
||||
char[] chars = sortSpec.toCharArray();
|
||||
int i = 0;
|
||||
StringBuilder buffer = new StringBuilder(sortSpec.length());
|
||||
String sort = null;
|
||||
String order = null;
|
||||
int functionDepth = 0;
|
||||
boolean score = true;
|
||||
List<SortField> lst = new ArrayList<SortField>(5);
|
||||
boolean needOrder = false;
|
||||
while (i < chars.length) {
|
||||
if (Character.isWhitespace(chars[i]) && functionDepth == 0) {
|
||||
if (buffer.length() == 0) {
|
||||
//do nothing
|
||||
} else {
|
||||
if (needOrder == false) {
|
||||
sort = buffer.toString().trim();
|
||||
buffer.setLength(0);
|
||||
needOrder = true;
|
||||
List<SortField> lst = new ArrayList<SortField>(4);
|
||||
|
||||
try {
|
||||
|
||||
StrParser sp = new StrParser(sortSpec);
|
||||
while (sp.pos < sp.end) {
|
||||
sp.eatws();
|
||||
|
||||
int start = sp.pos;
|
||||
|
||||
String field = sp.getId(null);
|
||||
ValueSource vs = null;
|
||||
|
||||
if (field == null || sp.ch() != ' ') {
|
||||
// let's try it as a function instead
|
||||
String funcStr = sp.val.substring(start);
|
||||
|
||||
QParser parser = QParser.getParser(funcStr, FunctionQParserPlugin.NAME, req);
|
||||
Query q = null;
|
||||
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 {
|
||||
order = buffer.toString().trim();
|
||||
buffer.setLength(0);
|
||||
needOrder = false;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// OK, now we have our query.
|
||||
if (q instanceof FunctionQuery) {
|
||||
vs = ((FunctionQuery)q).getValueSource();
|
||||
} else {
|
||||
vs = new QueryValueSource(q, 0.0f);
|
||||
}
|
||||
}
|
||||
} else if (chars[i] == '(' && functionDepth >= 0) {
|
||||
buffer.append(chars[i]);
|
||||
functionDepth++;
|
||||
} else if (chars[i] == ')' && functionDepth > 0) {
|
||||
buffer.append(chars[i]);
|
||||
functionDepth--;//close up one layer
|
||||
} else if (chars[i] == ',' && functionDepth == 0) {//can either be a separator of sort declarations, or a separator in a function
|
||||
//we have a separator between sort declarations,
|
||||
// We may need an order still, but then evaluate it, as we should have everything we need
|
||||
if (needOrder == true && buffer.length() > 0){
|
||||
order = buffer.toString().trim();
|
||||
buffer.setLength(0);
|
||||
needOrder = false;
|
||||
|
||||
// now we have our field or value source, so find the sort order
|
||||
String order = sp.getId("Expected sort order asc/desc");
|
||||
boolean top;
|
||||
if ("desc".equals(order) || "top".equals(order)) {
|
||||
top = true;
|
||||
} else if ("asc".equals(order) || "bottom".equals(order)) {
|
||||
top = false;
|
||||
} else {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown sort order: " + order);
|
||||
}
|
||||
score = processSort(schema, sort, order, lst);
|
||||
sort = null;
|
||||
order = null;
|
||||
buffer.setLength(0);//get ready for the next one, if there is one
|
||||
} else if (chars[i] == ',' && functionDepth > 0) {
|
||||
//we are in a function
|
||||
buffer.append(chars[i]);
|
||||
} else {
|
||||
//just a regular old char, add it to the buffer
|
||||
buffer.append(chars[i]);
|
||||
|
||||
if (vs == null) {
|
||||
//we got the order, now deal with the sort
|
||||
if ("score".equals(field)) {
|
||||
if (top) {
|
||||
lst.add(SortField.FIELD_SCORE);
|
||||
} else {
|
||||
lst.add(new SortField(null, SortField.SCORE, true));
|
||||
}
|
||||
} else if (DOCID.equals(field)) {
|
||||
lst.add(new SortField(null, SortField.DOC, top));
|
||||
} else {
|
||||
//See if we have a Field first, then see if it is a function, then throw an exception
|
||||
// getField could throw an exception if the name isn't found
|
||||
SchemaField sf = req.getSchema().getField(field);
|
||||
|
||||
// TODO: remove this - it should be up to the FieldType
|
||||
if (!sf.indexed()) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "can not sort on unindexed field: " + field);
|
||||
}
|
||||
|
||||
lst.add(sf.getType().getSortField(sf, top));
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
lst.add(vs.getSortField(top));
|
||||
}
|
||||
|
||||
sp.eatws();
|
||||
if (sp.pos < sp.end) {
|
||||
sp.expect(",");
|
||||
}
|
||||
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (buffer.length() > 0 && needOrder){//see if we have anything left, at most it should be an order
|
||||
order = buffer.toString().trim();
|
||||
buffer.setLength(0);
|
||||
needOrder = false;
|
||||
|
||||
} catch (ParseException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "error in sort: " + sortSpec, e);
|
||||
} catch (IOException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "error in sort: " + sortSpec, e);
|
||||
}
|
||||
|
||||
//do some sanity checks
|
||||
if (functionDepth != 0){
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unable to parse sort spec, mismatched parentheses: " + sortSpec);
|
||||
}
|
||||
if (buffer.length() > 0){//there's something wrong, as everything should have been parsed by now
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unable to parse sort spec: " + sortSpec);
|
||||
}
|
||||
if (needOrder == false && sort != null && sort.equals("") == false && order != null && order.equals("") == false){//handle the last declaration
|
||||
score = processSort(schema, sort, order, lst);
|
||||
}
|
||||
//If the normal case (by score desc) do nothing
|
||||
if (lst.size() == 1 && score == true && lst.get(0).getReverse() == false) {
|
||||
return null; // do normal scoring...
|
||||
|
||||
// normalize a sort on score desc to null
|
||||
if (lst.size()==1 && lst.get(0) == SortField.FIELD_SCORE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Sort((SortField[]) lst.toArray(new SortField[lst.size()]));
|
||||
}
|
||||
|
||||
private static boolean processSort(IndexSchema schema, String sort, String order, List<SortField> lst) {
|
||||
boolean score = false;
|
||||
if (sort != null && order != null) {
|
||||
boolean top = true;
|
||||
if ("desc".equals(order) || "top".equals(order)) {
|
||||
top = true;
|
||||
} else if ("asc".equals(order) || "bottom".equals(order)) {
|
||||
top = false;
|
||||
} else {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown sort order: " + order);
|
||||
}
|
||||
//we got the order, now deal with the sort
|
||||
if ("score".equals(sort)) {
|
||||
score = true;
|
||||
if (top) {
|
||||
lst.add(SortField.FIELD_SCORE);
|
||||
} else {
|
||||
lst.add(new SortField(null, SortField.SCORE, true));
|
||||
}
|
||||
} else if (DOCID.equals(sort)) {
|
||||
lst.add(new SortField(null, SortField.DOC, top));
|
||||
} else {
|
||||
//See if we have a Field first, then see if it is a function, then throw an exception
|
||||
// getField could throw an exception if the name isn't found
|
||||
SchemaField f = null;
|
||||
try {
|
||||
f = schema.getField(sort);
|
||||
}
|
||||
catch (SolrException e) {
|
||||
//Not an error just yet
|
||||
}
|
||||
if (f != null) {
|
||||
if (f == null || !f.indexed()) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "can not sort on unindexed field: " + sort);
|
||||
}
|
||||
lst.add(f.getType().getSortField(f, top));
|
||||
} else {
|
||||
//See if we have a function:
|
||||
FunctionQuery query = null;
|
||||
try {
|
||||
query = parseFunction(sort, schema);
|
||||
if (query != null) {
|
||||
ValueSource valueSource = query.getValueSource();
|
||||
//We have a function query
|
||||
try {
|
||||
lst.add(valueSource.getSortField(top));
|
||||
} catch (IOException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "error getting the sort for this function: " + sort, e);
|
||||
}
|
||||
} else {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "can not sort on undefined function: " + sort);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "can not sort on undefined field or function: " + sort, e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else if (sort == null) {//no sort value
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||
"Must declare sort field or function");
|
||||
} else if (order == null) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Missing sort order: ");
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
|
@ -640,6 +622,10 @@ public class QueryParsing {
|
|||
while (pos < end && Character.isWhitespace(val.charAt(pos))) pos++;
|
||||
}
|
||||
|
||||
char ch() {
|
||||
return pos < end ? val.charAt(pos) : 0;
|
||||
}
|
||||
|
||||
void skip(int nChars) {
|
||||
pos = Math.max(pos + nChars, end);
|
||||
}
|
||||
|
@ -756,12 +742,17 @@ public class QueryParsing {
|
|||
|
||||
|
||||
String getId() throws ParseException {
|
||||
return getId("Expected identifier");
|
||||
}
|
||||
|
||||
String getId(String errMessage) throws ParseException {
|
||||
eatws();
|
||||
int id_start = pos;
|
||||
if (pos < end && Character.isJavaIdentifierStart(val.charAt(pos))) {
|
||||
char ch;
|
||||
if (pos < end && (ch = val.charAt(pos)) != '$' && Character.isJavaIdentifierStart(ch)) {
|
||||
pos++;
|
||||
while (pos < end) {
|
||||
char ch = val.charAt(pos);
|
||||
ch = val.charAt(pos);
|
||||
if (!Character.isJavaIdentifierPart(ch) && ch != '.') {
|
||||
break;
|
||||
}
|
||||
|
@ -769,7 +760,11 @@ public class QueryParsing {
|
|||
}
|
||||
return val.substring(id_start, pos);
|
||||
}
|
||||
throw new ParseException("Expected identifier at pos " + pos + " str='" + val + "'");
|
||||
|
||||
if (errMessage != null) {
|
||||
throw new ParseException(errMessage + " at pos " + pos + " str='" + val + "'");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// return null if not a string
|
||||
|
|
|
@ -63,7 +63,7 @@ public class OldRequestHandler implements SolrRequestHandler {
|
|||
// we can use the Lucene sort ability.
|
||||
Sort sort = null;
|
||||
if (commands.size() >= 2) {
|
||||
sort = QueryParsing.parseSort(commands.get(1), req.getSchema());
|
||||
sort = QueryParsing.parseSort(commands.get(1), req);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ public class TestRequestHandler implements SolrRequestHandler {
|
|||
// we can use the Lucene sort ability.
|
||||
Sort sort = null;
|
||||
if (commands.size() >= 2) {
|
||||
sort = QueryParsing.parseSort(commands.get(1), req.getSchema());
|
||||
sort = QueryParsing.parseSort(commands.get(1), req);
|
||||
}
|
||||
|
||||
SolrIndexSearcher searcher = req.getSearcher();
|
||||
|
|
|
@ -366,7 +366,7 @@ public class SolrPluginUtils {
|
|||
String otherQueryS = req.getParams().get(CommonParams.EXPLAIN_OTHER);
|
||||
if (otherQueryS != null && otherQueryS.length() > 0) {
|
||||
DocList otherResults = doSimpleQuery
|
||||
(otherQueryS, req.getSearcher(), req.getSchema(), 0, 10);
|
||||
(otherQueryS, req, 0, 10);
|
||||
dbg.add("otherQuery", otherQueryS);
|
||||
NamedList<Explanation> explainO
|
||||
= getExplanations(query, otherResults, searcher, schema);
|
||||
|
@ -467,26 +467,30 @@ public class SolrPluginUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Executes a basic query in lucene syntax
|
||||
* Executes a basic query
|
||||
*/
|
||||
public static DocList doSimpleQuery(String sreq,
|
||||
SolrIndexSearcher searcher,
|
||||
IndexSchema schema,
|
||||
SolrQueryRequest req,
|
||||
int start, int limit) throws IOException {
|
||||
List<String> commands = StrUtils.splitSmart(sreq,';');
|
||||
|
||||
String qs = commands.size() >= 1 ? commands.get(0) : "";
|
||||
Query query = QueryParsing.parseQuery(qs, schema);
|
||||
try {
|
||||
Query query = QParser.getParser(qs, null, req).getQuery();
|
||||
|
||||
// If the first non-query, non-filter command is a simple sort on an indexed field, then
|
||||
// we can use the Lucene sort ability.
|
||||
Sort sort = null;
|
||||
if (commands.size() >= 2) {
|
||||
sort = QueryParsing.parseSort(commands.get(1), schema);
|
||||
sort = QueryParsing.parseSort(commands.get(1), req);
|
||||
}
|
||||
|
||||
DocList results = searcher.getDocList(query,(DocSet)null, sort, start, limit);
|
||||
DocList results = req.getSearcher().getDocList(query,(DocSet)null, sort, start, limit);
|
||||
return results;
|
||||
} catch (ParseException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing query: " + qs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -855,7 +859,7 @@ public class SolrPluginUtils {
|
|||
SolrException sortE = null;
|
||||
Sort ss = null;
|
||||
try {
|
||||
ss = QueryParsing.parseSort(sort, req.getSchema());
|
||||
ss = QueryParsing.parseSort(sort, req);
|
||||
} catch (SolrException e) {
|
||||
sortE = e;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.apache.lucene.search.Sort;
|
|||
import org.apache.lucene.search.SortField;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.schema.IndexSchema;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
@ -39,22 +40,23 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
|
|||
@Test
|
||||
public void testSort() throws Exception {
|
||||
Sort sort;
|
||||
SolrQueryRequest req = req();
|
||||
|
||||
IndexSchema schema = h.getCore().getSchema();
|
||||
sort = QueryParsing.parseSort("score desc", schema);
|
||||
sort = QueryParsing.parseSort("score desc", req);
|
||||
assertNull("sort", sort);//only 1 thing in the list, no Sort specified
|
||||
|
||||
sort = QueryParsing.parseSort("score asc", schema);
|
||||
sort = QueryParsing.parseSort("score asc", req);
|
||||
SortField[] flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.SCORE);
|
||||
assertTrue(flds[0].getReverse());
|
||||
|
||||
sort = QueryParsing.parseSort("weight desc", schema);
|
||||
sort = QueryParsing.parseSort("weight desc", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.FLOAT);
|
||||
assertEquals(flds[0].getField(), "weight");
|
||||
assertEquals(flds[0].getReverse(), true);
|
||||
sort = QueryParsing.parseSort("weight desc,bday asc", schema);
|
||||
sort = QueryParsing.parseSort("weight desc,bday asc", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.FLOAT);
|
||||
assertEquals(flds[0].getField(), "weight");
|
||||
|
@ -63,7 +65,7 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
|
|||
assertEquals(flds[1].getField(), "bday");
|
||||
assertEquals(flds[1].getReverse(), false);
|
||||
//order aliases
|
||||
sort = QueryParsing.parseSort("weight top,bday asc", schema);
|
||||
sort = QueryParsing.parseSort("weight top,bday asc", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.FLOAT);
|
||||
assertEquals(flds[0].getField(), "weight");
|
||||
|
@ -71,7 +73,7 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
|
|||
assertEquals(flds[1].getType(), SortField.LONG);
|
||||
assertEquals(flds[1].getField(), "bday");
|
||||
assertEquals(flds[1].getReverse(), false);
|
||||
sort = QueryParsing.parseSort("weight top,bday bottom", schema);
|
||||
sort = QueryParsing.parseSort("weight top,bday bottom", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.FLOAT);
|
||||
assertEquals(flds[0].getField(), "weight");
|
||||
|
@ -81,20 +83,20 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
|
|||
assertEquals(flds[1].getReverse(), false);
|
||||
|
||||
//test weird spacing
|
||||
sort = QueryParsing.parseSort("weight desc, bday asc", schema);
|
||||
sort = QueryParsing.parseSort("weight desc, bday asc", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.FLOAT);
|
||||
assertEquals(flds[0].getField(), "weight");
|
||||
assertEquals(flds[1].getField(), "bday");
|
||||
assertEquals(flds[1].getType(), SortField.LONG);
|
||||
//handles trailing commas
|
||||
sort = QueryParsing.parseSort("weight desc,", schema);
|
||||
sort = QueryParsing.parseSort("weight desc,", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.FLOAT);
|
||||
assertEquals(flds[0].getField(), "weight");
|
||||
|
||||
//test functions
|
||||
sort = QueryParsing.parseSort("pow(weight, 2) desc", schema);
|
||||
sort = QueryParsing.parseSort("pow(weight, 2) desc", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.CUSTOM);
|
||||
//Not thrilled about the fragility of string matching here, but...
|
||||
|
@ -102,12 +104,12 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
|
|||
assertEquals(flds[0].getField(), "pow(float(weight),const(2.0))");
|
||||
|
||||
//test functions (more deep)
|
||||
sort = QueryParsing.parseSort("sum(product(r_f,sum(d_f,t_f,1)),a_f) asc", schema);
|
||||
sort = QueryParsing.parseSort("sum(product(r_f,sum(d_f,t_f,1)),a_f) asc", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.CUSTOM);
|
||||
assertEquals(flds[0].getField(), "sum(product(float(r_f),sum(float(d_f),float(t_f),const(1.0))),float(a_f))");
|
||||
|
||||
sort = QueryParsing.parseSort("pow(weight, 2) desc", schema);
|
||||
sort = QueryParsing.parseSort("pow(weight, 2) desc", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.CUSTOM);
|
||||
//Not thrilled about the fragility of string matching here, but...
|
||||
|
@ -115,7 +117,7 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
|
|||
assertEquals(flds[0].getField(), "pow(float(weight),const(2.0))");
|
||||
|
||||
|
||||
sort = QueryParsing.parseSort("pow(weight, 2) desc, weight desc, bday asc", schema);
|
||||
sort = QueryParsing.parseSort("pow(weight, 2) desc, weight desc, bday asc", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.CUSTOM);
|
||||
|
||||
|
@ -129,19 +131,19 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
|
|||
assertEquals(flds[2].getType(), SortField.LONG);
|
||||
|
||||
//handles trailing commas
|
||||
sort = QueryParsing.parseSort("weight desc,", schema);
|
||||
sort = QueryParsing.parseSort("weight desc,", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.FLOAT);
|
||||
assertEquals(flds[0].getField(), "weight");
|
||||
|
||||
//Test literals in functions
|
||||
sort = QueryParsing.parseSort("strdist(foo_s, \"junk\", jw) desc", schema);
|
||||
sort = QueryParsing.parseSort("strdist(foo_s, \"junk\", jw) desc", req);
|
||||
flds = sort.getSort();
|
||||
assertEquals(flds[0].getType(), SortField.CUSTOM);
|
||||
//the value sources get wrapped, so the out field is different than the input
|
||||
assertEquals(flds[0].getField(), "strdist(str(foo_s),literal(junk), dist=org.apache.lucene.search.spell.JaroWinklerDistance)");
|
||||
|
||||
sort = QueryParsing.parseSort("", schema);
|
||||
sort = QueryParsing.parseSort("", req);
|
||||
assertNull(sort);
|
||||
|
||||
}
|
||||
|
@ -149,44 +151,45 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
|
|||
@Test
|
||||
public void testBad() throws Exception {
|
||||
Sort sort;
|
||||
SolrQueryRequest req = req();
|
||||
|
||||
IndexSchema schema = h.getCore().getSchema();
|
||||
//test some bad vals
|
||||
try {
|
||||
sort = QueryParsing.parseSort("weight, desc", schema);
|
||||
sort = QueryParsing.parseSort("weight, desc", req);
|
||||
assertTrue(false);
|
||||
} catch (SolrException e) {
|
||||
//expected
|
||||
}
|
||||
try {
|
||||
sort = QueryParsing.parseSort("w", schema);
|
||||
sort = QueryParsing.parseSort("w", req);
|
||||
assertTrue(false);
|
||||
} catch (SolrException e) {
|
||||
//expected
|
||||
}
|
||||
try {
|
||||
sort = QueryParsing.parseSort("weight desc, bday", schema);
|
||||
sort = QueryParsing.parseSort("weight desc, bday", req);
|
||||
assertTrue(false);
|
||||
} catch (SolrException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
//bad number of commas
|
||||
sort = QueryParsing.parseSort("pow(weight,,2) desc, bday asc", schema);
|
||||
sort = QueryParsing.parseSort("pow(weight,,2) desc, bday asc", req);
|
||||
assertTrue(false);
|
||||
} catch (SolrException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
//bad function
|
||||
sort = QueryParsing.parseSort("pow() desc, bday asc", schema);
|
||||
sort = QueryParsing.parseSort("pow() desc, bday asc", req);
|
||||
assertTrue(false);
|
||||
} catch (SolrException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
//bad number of parens
|
||||
sort = QueryParsing.parseSort("pow((weight,2) desc, bday asc", schema);
|
||||
sort = QueryParsing.parseSort("pow((weight,2) desc, bday asc", req);
|
||||
assertTrue(false);
|
||||
} catch (SolrException e) {
|
||||
}
|
||||
|
|
|
@ -359,6 +359,52 @@ public class TestFunctionQuery extends SolrTestCaseJ4 {
|
|||
purgeFieldCache(FieldCache.DEFAULT); // avoid FC insanity
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByFunc() throws Exception {
|
||||
assertU(adoc("id", "1", "x_i", "100"));
|
||||
assertU(adoc("id", "2", "x_i", "300"));
|
||||
assertU(adoc("id", "3", "x_i", "200"));
|
||||
assertU(commit());
|
||||
|
||||
String desc = "/response/docs==[{'x_i':300},{'x_i':200},{'x_i':100}]";
|
||||
String asc = "/response/docs==[{'x_i':100},{'x_i':200},{'x_i':300}]";
|
||||
|
||||
String q = "id:[1 TO 3]";
|
||||
assertJQ(req("q",q, "fl","x_i", "sort","add(x_i,x_i) desc")
|
||||
,desc
|
||||
);
|
||||
|
||||
// param sub of entire function
|
||||
assertJQ(req("q",q, "fl","x_i", "sort", "$x asc", "x","add(x_i,x_i)")
|
||||
,asc
|
||||
);
|
||||
|
||||
// multiple functions
|
||||
assertJQ(req("q",q, "fl","x_i", "sort", "$x asc, $y desc", "x", "5", "y","add(x_i,x_i)")
|
||||
,desc
|
||||
);
|
||||
|
||||
// multiple functions inline
|
||||
assertJQ(req("q",q, "fl","x_i", "sort", "add( 10 , 10 ) asc, add(x_i , $const) desc", "const","50")
|
||||
,desc
|
||||
);
|
||||
|
||||
// test function w/ local params + func inline
|
||||
assertJQ(req("q",q, "fl","x_i", "sort", "{!key=foo}add(x_i,x_i) desc")
|
||||
,desc
|
||||
);
|
||||
|
||||
// test multiple functions w/ local params + func inline
|
||||
assertJQ(req("q",q, "fl","x_i", "sort", "{!key=bar}add(10,20) asc, {!key=foo}add(x_i,x_i) desc")
|
||||
,desc
|
||||
);
|
||||
|
||||
// test multiple functions w/ local param value not inlined
|
||||
assertJQ(req("q",q, "fl","x_i", "sort", "{!key=bar v=$s1} asc, {!key=foo v=$s2} desc", "s1","add(3,4)", "s2","add(x_i,5)")
|
||||
,desc
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDegreeRads() throws Exception {
|
||||
assertU(adoc("id", "1", "x_td", "0", "y_td", "0"));
|
||||
|
|
Loading…
Reference in New Issue