예제 #1
0
  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);
    }
  }
예제 #2
0
  /**
   * The form of the sort specification string currently parsed is:
   *
   * <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<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);
  }