public static int parseLocalParams( String txt, int start, Map<String, String> target, SolrParams params, String startString, char endChar) throws SyntaxError { int off = start; if (!txt.startsWith(startString, off)) return start; StrParser p = new StrParser(txt, start, txt.length()); p.pos += startString.length(); // skip over "{!" for (; ; ) { /* if (p.pos>=txt.length()) { throw new SyntaxError("Missing '}' parsing local params '" + txt + '"'); } */ char ch = p.peek(); if (ch == endChar) { return p.pos + 1; } String id = p.getId(); if (id.length() == 0) { throw new SyntaxError( "Expected ending character '" + endChar + "' parsing local params '" + txt + '"'); } String val = null; ch = p.peek(); if (ch != '=') { // single word... treat {!func} as type=func for easy lookup val = id; id = TYPE; } else { // saw equals, so read value p.pos++; ch = p.peek(); boolean deref = false; if (ch == '$') { p.pos++; ch = p.peek(); deref = true; // dereference whatever value is read by treating it as a variable name } if (ch == '\"' || ch == '\'') { val = p.getQuotedString(); } else { // read unquoted literal ended by whitespace or endChar (normally '}') // there is no escaping. int valStart = p.pos; for (; ; ) { if (p.pos >= p.end) { throw new SyntaxError( "Missing end to unquoted value starting at " + valStart + " str='" + txt + "'"); } char c = p.val.charAt(p.pos); if (c == endChar || Character.isWhitespace(c)) { val = p.val.substring(valStart, p.pos); break; } p.pos++; } } if (deref) { // dereference parameter if (params != null) { val = params.get(val); } } } if (target != null) target.put(id, val); } }
/** * The form of the sort specification string currently parsed is: * * <pre> * SortSpec ::= SingleSort [, SingleSort]* * SingleSort ::= <fieldname|function> 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 & * offset values. */ public static SortSpec parseSortSpec(String sortSpec, SolrQueryRequest req) { if (sortSpec == null || sortSpec.length() == 0) return newEmptySortSpec(); List<SortField> sorts = new ArrayList<SortField>(4); List<SchemaField> fields = new ArrayList<SchemaField>(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); }