SOLR-8283: factor out SortSpecParsing[Test] from QueryParsing[Test]

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1715049 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Christine Poerschke 2015-11-18 19:48:40 +00:00
parent e776333c02
commit b7195573ec
14 changed files with 456 additions and 385 deletions

View File

@ -532,7 +532,8 @@ Other Changes
* SOLR-8300: Use constants for the /overseer_elect znode (Varun Thacker)
* SOLR-8283: factor out StrParser from QueryParsing.StrParser (Christine Poerschke)
* SOLR-8283: factor out StrParser from QueryParsing.StrParser and SortSpecParsing[Test]
from QueryParsing[Test] (Christine Poerschke)
================== 5.3.1 ==================

View File

@ -88,9 +88,9 @@ import org.apache.solr.search.DocIterator;
import org.apache.solr.search.DocList;
import org.apache.solr.search.DocSlice;
import org.apache.solr.search.QParser;
import org.apache.solr.search.QueryParsing;
import org.apache.solr.search.SolrConstantScoreQuery;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.search.SortSpecParsing;
import org.apache.solr.util.plugin.PluginInfoInitialized;
import org.apache.solr.util.plugin.SolrCoreAware;
@ -169,7 +169,7 @@ public class ExpandComponent extends SearchComponent implements PluginInfoInitia
Sort sort = null;
if (sortParam != null) {
sort = QueryParsing.parseSortSpec(sortParam, rb.req).getSort();
sort = SortSpecParsing.parseSortSpec(sortParam, rb.req).getSort();
}
Query query;

View File

@ -85,6 +85,7 @@ import org.apache.solr.search.ReturnFields;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.search.SolrReturnFields;
import org.apache.solr.search.SortSpec;
import org.apache.solr.search.SortSpecParsing;
import org.apache.solr.search.SyntaxError;
import org.apache.solr.search.grouping.CommandHandler;
import org.apache.solr.search.grouping.GroupingSpecification;
@ -257,7 +258,7 @@ public class QueryComponent extends SearchComponent
// groupSort defaults to sort
String groupSortStr = params.get(GroupParams.GROUP_SORT);
//TODO: move weighting of sort
Sort sortWithinGroup = groupSortStr == null ? groupSort : searcher.weightSort(QueryParsing.parseSortSpec(groupSortStr, req).getSort());
Sort sortWithinGroup = groupSortStr == null ? groupSort : searcher.weightSort(SortSpecParsing.parseSortSpec(groupSortStr, req).getSort());
if (sortWithinGroup == null) {
sortWithinGroup = Sort.RELEVANCE;
}

View File

@ -295,7 +295,7 @@ public class CollapsingQParserPlugin extends QParserPlugin {
this.sortSpec = GroupHeadSelectorType.SORT.equals(groupHeadSelector.type)
? QueryParsing.parseSortSpec(groupHeadSelector.selectorText, request)
? SortSpecParsing.parseSortSpec(groupHeadSelector.selectorText, request)
: null;
this.hint = localParams.get("hint");

View File

@ -86,7 +86,7 @@ class OldLuceneQParser extends LuceneQParser {
public SortSpec getSort(boolean useGlobal) throws SyntaxError {
SortSpec sort = super.getSort(useGlobal);
if (sortStr != null && sortStr.length()>0 && sort.getSort()==null) {
SortSpec oldSort = QueryParsing.parseSortSpec(sortStr, getReq());
SortSpec oldSort = SortSpecParsing.parseSortSpec(sortStr, getReq());
if( oldSort.getSort() != null ) {
sort.setSortAndFields(oldSort.getSort(), oldSort.getSchemaFields());
}

View File

@ -244,7 +244,7 @@ public abstract class QParser {
int start = startS != null ? Integer.parseInt(startS) : 0;
int rows = rowsS != null ? Integer.parseInt(rowsS) : 10;
SortSpec sort = QueryParsing.parseSortSpec(sortStr, req);
SortSpec sort = SortSpecParsing.parseSortSpec(sortStr, req);
sort.setOffset(start);
sort.setCount(rows);

View File

@ -18,8 +18,6 @@
package org.apache.solr.search;
import org.apache.lucene.index.Term;
import org.apache.lucene.queries.function.FunctionQuery;
import org.apache.lucene.queries.function.valuesource.QueryValueSource;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BoostQuery;
@ -28,25 +26,19 @@ import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.CharsRefBuilder;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.parser.QueryParser;
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 java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -64,9 +56,6 @@ public class QueryParsing {
public static final String DEFTYPE = "defType"; // default parser for any direct subqueries
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__";
@ -221,162 +210,6 @@ public class QueryParsing {
return new MapSolrParams(localParams);
}
/**
* <p>
* The form of the sort specification string currently parsed is:
* </p>
* <pre>
* SortSpec ::= SingleSort [, SingleSort]*
* SingleSort ::= &lt;fieldname|function&gt; SortDirection
* SortDirection ::= top | desc | bottom | asc
* </pre>
* Examples:
* <pre>
* 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
* </pre>
* @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 &amp; offset values.
*/
public static SortSpec parseSortSpec(String sortSpec, SolrQueryRequest req) {
if (sortSpec == null || sortSpec.length() == 0) return newEmptySortSpec();
List<SortField> sorts = new ArrayList<>(4);
List<SchemaField> 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())) {
// let's try it as a function instead
field = null;
String funcStr = sp.val.substring(start);
QParser parser = QParser.getParser(funcStr, FunctionQParserPlugin.NAME, req);
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 = 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 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 && sorts.get(0) == SortField.FIELD_SCORE) {
return newEmptySortSpec();
}
Sort s = new Sort(sorts.toArray(new SortField[sorts.size()]));
return new SortSpec(s, fields);
}
private static SortSpec newEmptySortSpec() {
return new SortSpec(null, Collections.<SchemaField>emptyList());
}
///////////////////////////

View File

@ -0,0 +1,195 @@
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.SchemaField;
/*
* 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.
*/
public class SortSpecParsing {
public static final String DOCID = "_docid_";
public static final String SCORE = "score";
/**
* <p>
* The form of the sort specification string currently parsed is:
* </p>
* <pre>
* SortSpec ::= SingleSort [, SingleSort]*
* SingleSort ::= &lt;fieldname|function&gt; SortDirection
* SortDirection ::= top | desc | bottom | asc
* </pre>
* Examples:
* <pre>
* 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
* </pre>
* @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 &amp; offset values.
*/
public static SortSpec parseSortSpec(String sortSpec, SolrQueryRequest req) {
if (sortSpec == null || sortSpec.length() == 0) return newEmptySortSpec();
List<SortField> sorts = new ArrayList<>(4);
List<SchemaField> 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())) {
// let's try it as a function instead
field = null;
String funcStr = sp.val.substring(start);
QParser parser = QParser.getParser(funcStr, FunctionQParserPlugin.NAME, req);
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 = 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 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 && sorts.get(0) == SortField.FIELD_SCORE) {
return newEmptySortSpec();
}
Sort s = new Sort(sorts.toArray(new SortField[sorts.size()]));
return new SortSpec(s, fields);
}
private static SortSpec newEmptySortSpec() {
return new SortSpec(null, Collections.<SchemaField>emptyList());
}
}

View File

@ -76,6 +76,7 @@ import org.apache.solr.search.ReturnFields;
import org.apache.solr.search.SolrCache;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.search.SolrQueryParser;
import org.apache.solr.search.SortSpecParsing;
import org.apache.solr.search.SyntaxError;
/**
@ -465,7 +466,7 @@ public class SolrPluginUtils {
// we can use the Lucene sort ability.
Sort sort = null;
if (commands.size() >= 2) {
sort = QueryParsing.parseSortSpec(commands.get(1), req).getSort();
sort = SortSpecParsing.parseSortSpec(commands.get(1), req).getSort();
}
DocList results = req.getSearcher().getDocList(query,(DocSet)null, sort, start, limit);
@ -907,7 +908,7 @@ public class SolrPluginUtils {
SolrException sortE = null;
Sort ss = null;
try {
ss = QueryParsing.parseSortSpec(sort, req).getSort();
ss = SortSpecParsing.parseSortSpec(sort, req).getSort();
} catch (SolrException e) {
sortE = e;
}

View File

@ -62,7 +62,7 @@ public class CursorMarkTest extends SolrTestCaseJ4 {
final IndexSchema schema = req.getSchema();
final String randomSortString = CursorPagingTest.buildRandomSort(allFieldNames);
final SortSpec ss = QueryParsing.parseSortSpec(randomSortString, req);
final SortSpec ss = SortSpecParsing.parseSortSpec(randomSortString, req);
final CursorMark previous = new CursorMark(schema, ss);
previous.parseSerializedTotem(CURSOR_MARK_START);
@ -74,7 +74,7 @@ public class CursorMarkTest extends SolrTestCaseJ4 {
try {
// append to our random sort string so we know it has wrong num clauses
final SortSpec otherSort = QueryParsing.parseSortSpec(randomSortString+",id asc", req);
final SortSpec otherSort = SortSpecParsing.parseSortSpec(randomSortString+",id asc", req);
CursorMark trash = previous.createNext(Arrays.<Object>asList
(buildRandomSortObjects(otherSort)));
fail("didn't fail on next with incorrect num of sortvalues");
@ -88,7 +88,7 @@ public class CursorMarkTest extends SolrTestCaseJ4 {
final IndexSchema schema = req.getSchema();
try {
final SortSpec ss = QueryParsing.parseSortSpec("str desc, score desc", req);
final SortSpec ss = SortSpecParsing.parseSortSpec("str desc, score desc", req);
final CursorMark totem = new CursorMark(schema, ss);
fail("no failure from sort that doesn't include uniqueKey field");
} catch (SolrException e) {
@ -98,7 +98,7 @@ public class CursorMarkTest extends SolrTestCaseJ4 {
for (final String dir : Arrays.asList("asc", "desc")) {
try {
final SortSpec ss = QueryParsing.parseSortSpec("score " + dir, req);
final SortSpec ss = SortSpecParsing.parseSortSpec("score " + dir, req);
final CursorMark totem = new CursorMark(schema, ss);
fail("no failure from score only sort: " + dir);
} catch (SolrException e) {
@ -107,7 +107,7 @@ public class CursorMarkTest extends SolrTestCaseJ4 {
}
try {
final SortSpec ss = QueryParsing.parseSortSpec("_docid_ "+dir+", id desc", req);
final SortSpec ss = SortSpecParsing.parseSortSpec("_docid_ "+dir+", id desc", req);
final CursorMark totem = new CursorMark(schema, ss);
fail("no failure from sort that includes _docid_: " + dir);
} catch (SolrException e) {
@ -121,7 +121,7 @@ public class CursorMarkTest extends SolrTestCaseJ4 {
public void testGarbageParsing() throws IOException {
final SolrQueryRequest req = req();
final IndexSchema schema = req.getSchema();
final SortSpec ss = QueryParsing.parseSortSpec("str asc, float desc, id asc", req);
final SortSpec ss = SortSpecParsing.parseSortSpec("str asc, float desc, id asc", req);
final CursorMark totem = new CursorMark(schema, ss);
// totem string that isn't even valid base64
@ -153,7 +153,7 @@ public class CursorMarkTest extends SolrTestCaseJ4 {
// totem string from sort with diff num clauses
try {
final SortSpec otherSort = QueryParsing.parseSortSpec("double desc, id asc", req);
final SortSpec otherSort = SortSpecParsing.parseSortSpec("double desc, id asc", req);
final CursorMark otherTotem = new CursorMark(schema, otherSort);
otherTotem.setSortValues(Arrays.<Object>asList(buildRandomSortObjects(otherSort)));
@ -176,7 +176,7 @@ public class CursorMarkTest extends SolrTestCaseJ4 {
final int numRandomSorts = atLeast(50);
final int numRandomValIters = atLeast(10);
for (int i = 0; i < numRandomSorts; i++) {
final SortSpec ss = QueryParsing.parseSortSpec
final SortSpec ss = SortSpecParsing.parseSortSpec
(CursorPagingTest.buildRandomSort(allFieldNames), req);
final CursorMark totemIn = new CursorMark(schema, ss);
final CursorMark totemOut = new CursorMark(schema, ss);

View File

@ -72,205 +72,6 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
}
}
@Test
public void testSort() throws Exception {
Sort sort;
SortSpec spec;
SolrQueryRequest req = req();
sort = QueryParsing.parseSortSpec("score desc", req).getSort();
assertNull("sort", sort);//only 1 thing in the list, no Sort specified
spec = QueryParsing.parseSortSpec("score desc", req);
assertNotNull("spec", spec);
assertNull(spec.getSort());
assertNotNull(spec.getSchemaFields());
assertEquals(0, spec.getSchemaFields().size());
// SOLR-4458 - using different case variations of asc and desc
sort = QueryParsing.parseSortSpec("score aSc", req).getSort();
SortField[] flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.SCORE);
assertTrue(flds[0].getReverse());
spec = QueryParsing.parseSortSpec("score aSc", req);
flds = spec.getSort().getSort();
assertEquals(1, flds.length);
assertEquals(flds[0].getType(), SortField.Type.SCORE);
assertTrue(flds[0].getReverse());
assertEquals(1, spec.getSchemaFields().size());
assertNull(spec.getSchemaFields().get(0));
sort = QueryParsing.parseSortSpec("weight dEsC", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
spec = QueryParsing.parseSortSpec("weight dEsC", req);
flds = spec.getSort().getSort();
assertEquals(1, flds.length);
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
assertEquals(1, spec.getSchemaFields().size());
assertNotNull(spec.getSchemaFields().get(0));
assertEquals("weight", spec.getSchemaFields().get(0).getName());
sort = QueryParsing.parseSortSpec("weight desc,bday ASC", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
assertEquals(flds[1].getType(), SortField.Type.LONG);
assertEquals(flds[1].getField(), "bday");
assertEquals(flds[1].getReverse(), false);
//order aliases
sort = QueryParsing.parseSortSpec("weight top,bday asc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
assertEquals(flds[1].getType(), SortField.Type.LONG);
assertEquals(flds[1].getField(), "bday");
assertEquals(flds[1].getReverse(), false);
sort = QueryParsing.parseSortSpec("weight top,bday bottom", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
assertEquals(flds[1].getType(), SortField.Type.LONG);
assertEquals(flds[1].getField(), "bday");
assertEquals(flds[1].getReverse(), false);
//test weird spacing
sort = QueryParsing.parseSortSpec("weight DESC, bday asc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[1].getField(), "bday");
assertEquals(flds[1].getType(), SortField.Type.LONG);
//handles trailing commas
sort = QueryParsing.parseSortSpec("weight desc,", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
//test functions
sort = QueryParsing.parseSortSpec("pow(weight, 2) desc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
//Not thrilled about the fragility of string matching here, but...
//the value sources get wrapped, so the out field is different than the input
assertEquals(flds[0].getField(), "pow(float(weight),const(2))");
//test functions (more deep)
sort = QueryParsing.parseSortSpec("sum(product(r_f1,sum(d_f1,t_f1,1.0)),a_f1) asc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
assertEquals(flds[0].getField(), "sum(product(float(r_f1),sum(float(d_f1),float(t_f1),const(1.0))),float(a_f1))");
sort = QueryParsing.parseSortSpec("pow(weight, 2.0) desc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
//Not thrilled about the fragility of string matching here, but...
//the value sources get wrapped, so the out field is different than the input
assertEquals(flds[0].getField(), "pow(float(weight),const(2.0))");
spec = QueryParsing.parseSortSpec("pow(weight, 2.0) desc, weight desc, bday asc", req);
flds = spec.getSort().getSort();
List<SchemaField> schemaFlds = spec.getSchemaFields();
assertEquals(3, flds.length);
assertEquals(3, schemaFlds.size());
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
//Not thrilled about the fragility of string matching here, but...
//the value sources get wrapped, so the out field is different than the input
assertEquals(flds[0].getField(), "pow(float(weight),const(2.0))");
assertNull(schemaFlds.get(0));
assertEquals(flds[1].getType(), SortField.Type.FLOAT);
assertEquals(flds[1].getField(), "weight");
assertNotNull(schemaFlds.get(1));
assertEquals("weight", schemaFlds.get(1).getName());
assertEquals(flds[2].getField(), "bday");
assertEquals(flds[2].getType(), SortField.Type.LONG);
assertNotNull(schemaFlds.get(2));
assertEquals("bday", schemaFlds.get(2).getName());
//handles trailing commas
sort = QueryParsing.parseSortSpec("weight desc,", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
//Test literals in functions
sort = QueryParsing.parseSortSpec("strdist(foo_s1, \"junk\", jw) desc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
//the value sources get wrapped, so the out field is different than the input
assertEquals(flds[0].getField(), "strdist(str(foo_s1),literal(junk), dist=org.apache.lucene.search.spell.JaroWinklerDistance)");
sort = QueryParsing.parseSortSpec("", req).getSort();
assertNull(sort);
spec = QueryParsing.parseSortSpec("", req);
assertNotNull(spec);
assertNull(spec.getSort());
req.close();
}
@Test
public void testBad() throws Exception {
Sort sort;
SolrQueryRequest req = req();
//test some bad vals
try {
sort = QueryParsing.parseSortSpec("weight, desc", req).getSort();
assertTrue(false);
} catch (SolrException e) {
//expected
}
try {
sort = QueryParsing.parseSortSpec("w", req).getSort();
assertTrue(false);
} catch (SolrException e) {
//expected
}
try {
sort = QueryParsing.parseSortSpec("weight desc, bday", req).getSort();
assertTrue(false);
} catch (SolrException e) {
}
try {
//bad number of commas
sort = QueryParsing.parseSortSpec("pow(weight,,2) desc, bday asc", req).getSort();
assertTrue(false);
} catch (SolrException e) {
}
try {
//bad function
sort = QueryParsing.parseSortSpec("pow() desc, bday asc", req).getSort();
assertTrue(false);
} catch (SolrException e) {
}
try {
//bad number of parens
sort = QueryParsing.parseSortSpec("pow((weight,2) desc, bday asc", req).getSort();
assertTrue(false);
} catch (SolrException e) {
}
req.close();
}
public void testLiteralFunction() throws Exception {
final String NAME = FunctionQParserPlugin.NAME;

View File

@ -0,0 +1,239 @@
package org.apache.solr.search;
/*
* 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.
*/
import org.apache.lucene.search.Query;
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.SchemaField;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
/**
*
*
**/
public class SortSpecParsingTest extends SolrTestCaseJ4 {
@BeforeClass
public static void beforeClass() throws Exception {
initCore("solrconfig.xml","schema.xml");
}
@Test
public void testSort() throws Exception {
Sort sort;
SortSpec spec;
SolrQueryRequest req = req();
sort = SortSpecParsing.parseSortSpec("score desc", req).getSort();
assertNull("sort", sort);//only 1 thing in the list, no Sort specified
spec = SortSpecParsing.parseSortSpec("score desc", req);
assertNotNull("spec", spec);
assertNull(spec.getSort());
assertNotNull(spec.getSchemaFields());
assertEquals(0, spec.getSchemaFields().size());
// SOLR-4458 - using different case variations of asc and desc
sort = SortSpecParsing.parseSortSpec("score aSc", req).getSort();
SortField[] flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.SCORE);
assertTrue(flds[0].getReverse());
spec = SortSpecParsing.parseSortSpec("score aSc", req);
flds = spec.getSort().getSort();
assertEquals(1, flds.length);
assertEquals(flds[0].getType(), SortField.Type.SCORE);
assertTrue(flds[0].getReverse());
assertEquals(1, spec.getSchemaFields().size());
assertNull(spec.getSchemaFields().get(0));
sort = SortSpecParsing.parseSortSpec("weight dEsC", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
spec = SortSpecParsing.parseSortSpec("weight dEsC", req);
flds = spec.getSort().getSort();
assertEquals(1, flds.length);
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
assertEquals(1, spec.getSchemaFields().size());
assertNotNull(spec.getSchemaFields().get(0));
assertEquals("weight", spec.getSchemaFields().get(0).getName());
sort = SortSpecParsing.parseSortSpec("weight desc,bday ASC", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
assertEquals(flds[1].getType(), SortField.Type.LONG);
assertEquals(flds[1].getField(), "bday");
assertEquals(flds[1].getReverse(), false);
//order aliases
sort = SortSpecParsing.parseSortSpec("weight top,bday asc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
assertEquals(flds[1].getType(), SortField.Type.LONG);
assertEquals(flds[1].getField(), "bday");
assertEquals(flds[1].getReverse(), false);
sort = SortSpecParsing.parseSortSpec("weight top,bday bottom", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[0].getReverse(), true);
assertEquals(flds[1].getType(), SortField.Type.LONG);
assertEquals(flds[1].getField(), "bday");
assertEquals(flds[1].getReverse(), false);
//test weird spacing
sort = SortSpecParsing.parseSortSpec("weight DESC, bday asc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
assertEquals(flds[1].getField(), "bday");
assertEquals(flds[1].getType(), SortField.Type.LONG);
//handles trailing commas
sort = SortSpecParsing.parseSortSpec("weight desc,", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
//test functions
sort = SortSpecParsing.parseSortSpec("pow(weight, 2) desc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
//Not thrilled about the fragility of string matching here, but...
//the value sources get wrapped, so the out field is different than the input
assertEquals(flds[0].getField(), "pow(float(weight),const(2))");
//test functions (more deep)
sort = SortSpecParsing.parseSortSpec("sum(product(r_f1,sum(d_f1,t_f1,1.0)),a_f1) asc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
assertEquals(flds[0].getField(), "sum(product(float(r_f1),sum(float(d_f1),float(t_f1),const(1.0))),float(a_f1))");
sort = SortSpecParsing.parseSortSpec("pow(weight, 2.0) desc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
//Not thrilled about the fragility of string matching here, but...
//the value sources get wrapped, so the out field is different than the input
assertEquals(flds[0].getField(), "pow(float(weight),const(2.0))");
spec = SortSpecParsing.parseSortSpec("pow(weight, 2.0) desc, weight desc, bday asc", req);
flds = spec.getSort().getSort();
List<SchemaField> schemaFlds = spec.getSchemaFields();
assertEquals(3, flds.length);
assertEquals(3, schemaFlds.size());
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
//Not thrilled about the fragility of string matching here, but...
//the value sources get wrapped, so the out field is different than the input
assertEquals(flds[0].getField(), "pow(float(weight),const(2.0))");
assertNull(schemaFlds.get(0));
assertEquals(flds[1].getType(), SortField.Type.FLOAT);
assertEquals(flds[1].getField(), "weight");
assertNotNull(schemaFlds.get(1));
assertEquals("weight", schemaFlds.get(1).getName());
assertEquals(flds[2].getField(), "bday");
assertEquals(flds[2].getType(), SortField.Type.LONG);
assertNotNull(schemaFlds.get(2));
assertEquals("bday", schemaFlds.get(2).getName());
//handles trailing commas
sort = SortSpecParsing.parseSortSpec("weight desc,", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.FLOAT);
assertEquals(flds[0].getField(), "weight");
//Test literals in functions
sort = SortSpecParsing.parseSortSpec("strdist(foo_s1, \"junk\", jw) desc", req).getSort();
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.Type.REWRITEABLE);
//the value sources get wrapped, so the out field is different than the input
assertEquals(flds[0].getField(), "strdist(str(foo_s1),literal(junk), dist=org.apache.lucene.search.spell.JaroWinklerDistance)");
sort = SortSpecParsing.parseSortSpec("", req).getSort();
assertNull(sort);
spec = SortSpecParsing.parseSortSpec("", req);
assertNotNull(spec);
assertNull(spec.getSort());
req.close();
}
@Test
public void testBad() throws Exception {
Sort sort;
SolrQueryRequest req = req();
//test some bad vals
try {
sort = SortSpecParsing.parseSortSpec("weight, desc", req).getSort();
assertTrue(false);
} catch (SolrException e) {
//expected
}
try {
sort = SortSpecParsing.parseSortSpec("w", req).getSort();
assertTrue(false);
} catch (SolrException e) {
//expected
}
try {
sort = SortSpecParsing.parseSortSpec("weight desc, bday", req).getSort();
assertTrue(false);
} catch (SolrException e) {
}
try {
//bad number of commas
sort = SortSpecParsing.parseSortSpec("pow(weight,,2) desc, bday asc", req).getSort();
assertTrue(false);
} catch (SolrException e) {
}
try {
//bad function
sort = SortSpecParsing.parseSortSpec("pow() desc, bday asc", req).getSort();
assertTrue(false);
} catch (SolrException e) {
}
try {
//bad number of parens
sort = SortSpecParsing.parseSortSpec("pow((weight,2) desc, bday asc", req).getSort();
assertTrue(false);
} catch (SolrException e) {
}
req.close();
}
}

View File

@ -135,7 +135,7 @@ public class TestSort extends SolrTestCaseJ4 {
SortField[] sorts = null;
List<SchemaField> fields = null;
try {
SortSpec spec = QueryParsing.parseSortSpec(input.toString(), req);
SortSpec spec = SortSpecParsing.parseSortSpec(input.toString(), req);
sorts = spec.getSort().getSort();
fields = spec.getSchemaFields();
} catch (RuntimeException e) {

View File

@ -42,7 +42,7 @@ import java.util.regex.Pattern;
*/
public class SolrQuery extends ModifiableSolrParams
{
public static final String DOCID = "_docid_"; // duplicate of org.apache.solr.search.QueryParsing.DOCID which is not accessible from here
public static final String DOCID = "_docid_"; // duplicate of org.apache.solr.search.SortSpecParsing.DOCID which is not accessible from here
public enum ORDER { desc, asc;
public ORDER reverse() {