You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2010/12/13 18:46:56 UTC
svn commit: r1045256 - in /lucene/dev/branches/branch_3x/solr: ./
src/java/org/apache/solr/search/QueryParsing.java
src/test/org/apache/solr/search/function/TestFunctionQuery.java
Author: hossman
Date: Mon Dec 13 17:46:56 2010
New Revision: 1045256
URL: http://svn.apache.org/viewvc?rev=1045256&view=rev
Log:
SOLR-1297: merging 1045253 from trunk
Modified:
lucene/dev/branches/branch_3x/solr/ (props changed)
lucene/dev/branches/branch_3x/solr/src/java/org/apache/solr/search/QueryParsing.java
lucene/dev/branches/branch_3x/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java
Modified: lucene/dev/branches/branch_3x/solr/src/java/org/apache/solr/search/QueryParsing.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/solr/src/java/org/apache/solr/search/QueryParsing.java?rev=1045256&r1=1045255&r2=1045256&view=diff
==============================================================================
--- lucene/dev/branches/branch_3x/solr/src/java/org/apache/solr/search/QueryParsing.java (original)
+++ lucene/dev/branches/branch_3x/solr/src/java/org/apache/solr/search/QueryParsing.java Mon Dec 13 17:46:56 2010
@@ -66,6 +66,7 @@ public class QueryParsing {
public static final String LOCALPARAM_START = "{!";
public static final char LOCALPARAM_END = '}';
public static final String DOCID = "_docid_";
+ public static final String SCORE = "score";
// 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__";
@@ -284,10 +285,11 @@ public class QueryParsing {
while (sp.pos < sp.end) {
sp.eatws();
- int start = sp.pos;
+ final int start = sp.pos;
+ // short circuit test for a really simple field name
String field = sp.getId(null);
- ValueSource vs = null;
+ ParseException qParserException = null;
if (field == null || sp.ch() != ' ') {
// let's try it as a function instead
@@ -295,89 +297,100 @@ public class QueryParsing {
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) {
+ 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 {
- // 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();
+ // 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;
- }
+ 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);
+ Boolean top = sp.getSortDirection();
+ if (null != top) {
+ // we have a Query and a valid direction
+ if (q instanceof FunctionQuery) {
+ lst.add(((FunctionQuery)q).getValueSource().getSortField(top));
+ } else {
+ lst.add((new QueryValueSource(q, 0.0f)).getSortField(top));
+ }
+ continue;
+ }
+ } catch (ParseException e) {
+ // hang onto this in case the string isn't a full field name either
+ qParserException = e;
}
}
- // 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);
- }
+ // 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 (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));
+ 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 Sort Order: " + sp);
+ }
+
+ if (SCORE.equals(field)) {
+ if (top) {
+ lst.add(SortField.FIELD_SCORE);
} 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));
-
-
+ lst.add(new SortField(null, SortField.SCORE, true));
}
+ } else if (DOCID.equals(field)) {
+ lst.add(new SortField(null, SortField.DOC, top));
} else {
- lst.add(vs.getSortField(top));
- }
-
- sp.eatws();
- if (sp.pos < sp.end) {
- sp.expect(",");
+ // try to find the field
+ SchemaField sf = req.getSchema().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 fiedl can't be found: " + 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));
}
-
}
} catch (ParseException e) {
@@ -757,6 +770,56 @@ public class QueryParsing {
return null;
}
+ /**
+ * Skips leading whitespace and returns whatever sequence of non
+ * whitespace it can find (or hte empty string)
+ */
+ String getSimpleString() {
+ eatws();
+ int startPos = pos;
+ char ch;
+ while (pos < end) {
+ ch = val.charAt(pos);
+ if (Character.isWhitespace(ch)) break;
+ pos++;
+ }
+ return val.substring(startPos, pos);
+ }
+
+ /**
+ * Sort direction or null if current position does not inidcate a
+ * sort direction. (True is desc, False is asc).
+ * Position is advanced to after the comma (or end) when result is non null
+ */
+ Boolean getSortDirection() throws ParseException {
+ final int startPos = pos;
+ final String order = getId(null);
+
+ Boolean top = null;
+
+ if (null != order) {
+ if ("desc".equals(order) || "top".equals(order)) {
+ top = true;
+ } else if ("asc".equals(order) || "bottom".equals(order)) {
+ top = false;
+ }
+
+ // it's not a legal direction if more stuff comes after it
+ eatws();
+ final char c = ch();
+ if (0 == c) {
+ // :NOOP
+ } else if (',' == c) {
+ pos++;
+ } else {
+ top = null;
+ }
+ }
+
+ if (null == top) pos = startPos; // no direction, reset
+ return top;
+ }
+
// return null if not a string
String getQuotedString() throws ParseException {
eatws();
Modified: lucene/dev/branches/branch_3x/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_3x/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java?rev=1045256&r1=1045255&r2=1045256&view=diff
==============================================================================
--- lucene/dev/branches/branch_3x/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java (original)
+++ lucene/dev/branches/branch_3x/solr/src/test/org/apache/solr/search/function/TestFunctionQuery.java Mon Dec 13 17:46:56 2010
@@ -344,41 +344,48 @@ public class TestFunctionQuery extends S
@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(adoc("id", "1", "const_s", "xx", "x_i", "100", "1_s", "a"));
+ assertU(adoc("id", "2", "const_s", "xx", "x_i", "300", "1_s", "c"));
+ assertU(adoc("id", "3", "const_s", "xx", "x_i", "200", "1_s", "b"));
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 threeonetwo = "/response/docs==[{'x_i':200},{'x_i':100},{'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)")
+ assertJQ(req("q",q, "fl","x_i", "sort", "const_s asc, $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)")
+ assertJQ(req("q",q, "fl","x_i", "sort", "$x asc, const_s 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")
+ assertJQ(req("q",q, "fl","x_i", "sort", "add( 10 , 10 ) asc, const_s 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
+ assertJQ(req("q",q, "fl","x_i",
+ "sort", "const_s asc, {!key=foo}add(x_i,x_i) desc")
+ ,desc
+ );
+ assertJQ(req("q",q, "fl","x_i",
+ "sort", "{!key=foo}add(x_i,x_i) desc, const_s asc")
+ ,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")
+ assertJQ(req("q",q, "fl","x_i", "sort", "{!key=bar}add(10,20) asc, const_s asc, {!key=foo}add(x_i,x_i) desc")
,desc
);
@@ -386,6 +393,31 @@ public class TestFunctionQuery extends S
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
);
+
+ // no space between inlined localparams and sort order
+ assertJQ(req("q",q, "fl","x_i", "sort", "{!key=bar v=$s1}asc,const_s asc,{!key=foo v=$s2}desc", "s1","add(3,4)", "s2","add(x_i,5)")
+ ,desc
+ );
+
+ // field name that isn't a legal java Identifier
+ // and starts with a number to trick function parser
+ assertJQ(req("q",q, "fl","x_i", "sort", "1_s asc")
+ ,asc
+ );
+
+ // really ugly field name that isn't a java Id, and can't be
+ // parsed as a func, but sorted fine in Solr 1.4
+ assertJQ(req("q",q, "fl","x_i",
+ "sort", "[]_s asc, {!key=foo}add(x_i,x_i) desc")
+ ,desc
+ );
+ // use localparms to sort by a lucene query, then a function
+ assertJQ(req("q",q, "fl","x_i",
+ "sort", "{!lucene v='id:3'}desc, {!key=foo}add(x_i,x_i) asc")
+ ,threeonetwo
+ );
+
+
}
@Test
@@ -457,4 +489,4 @@ public class TestFunctionQuery extends S
}
-}
\ No newline at end of file
+}