private QParser altQParser;
@Override
public Query parse() throws ParseException {
SolrParams localParams = getLocalParams();
SolrParams params = getParams();
SolrParams solrParams = localParams == null ? params : new DefaultSolrParams(localParams, params);
queryFields = U.parseFieldBoosts(solrParams.getParams(DMP.QF));
if (0 == queryFields.size()) {
queryFields.put(req.getSchema().getDefaultSearchFieldName(), 1.0f);
}
// Boosted phrase of the full query string
Map<String,Float> phraseFields =
U.parseFieldBoosts(solrParams.getParams(DMP.PF));
// Boosted Bi-Term Shingles from the query string
Map<String,Float> phraseFields2 =
U.parseFieldBoosts(solrParams.getParams("pf2"));
// Boosted Tri-Term Shingles from the query string
Map<String,Float> phraseFields3 =
U.parseFieldBoosts(solrParams.getParams("pf3"));
float tiebreaker = solrParams.getFloat(DMP.TIE, 0.0f);
int pslop = solrParams.getInt(DMP.PS, 0);
int qslop = solrParams.getInt(DMP.QS, 0);
// remove stopwords from mandatory "matching" component?
boolean stopwords = solrParams.getBool("stopwords", true);
/* the main query we will execute. we disable the coord because
* this query is an artificial construct
*/
BooleanQuery query = new BooleanQuery(true);
/* * * Main User Query * * */
parsedUserQuery = null;
String userQuery = getString();
altUserQuery = null;
if( userQuery == null || userQuery.length() < 1 ) {
// If no query is specified, we may have an alternate
String altQ = solrParams.get( DMP.ALTQ );
if (altQ != null) {
altQParser = subQuery(altQ, null);
altUserQuery = altQParser.getQuery();
query.add( altUserQuery , BooleanClause.Occur.MUST );
} else {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "missing query string" );
}
}
else {
// There is a valid query string
// userQuery = partialEscape(U.stripUnbalancedQuotes(userQuery)).toString();
boolean lowercaseOperators = solrParams.getBool("lowercaseOperators", true);
String mainUserQuery = userQuery;
ExtendedSolrQueryParser up =
new ExtendedSolrQueryParser(this, IMPOSSIBLE_FIELD_NAME);
up.addAlias(IMPOSSIBLE_FIELD_NAME,
tiebreaker, queryFields);
up.setPhraseSlop(qslop); // slop for explicit user phrase queries
up.setAllowLeadingWildcard(true);
// defer escaping and only do if lucene parsing fails, or we need phrases
// parsing fails. Need to sloppy phrase queries anyway though.
List<Clause> clauses = null;
boolean specialSyntax = false;
int numPluses = 0;
int numMinuses = 0;
int numOptional = 0;
int numAND = 0;
int numOR = 0;
int numNOT = 0;
boolean sawLowerAnd=false;
boolean sawLowerOr=false;
clauses = splitIntoClauses(userQuery, false);
for (Clause clause : clauses) {
if (!clause.isPhrase && clause.hasSpecialSyntax) {
specialSyntax = true;
}
if (clause.must == '+') numPluses++;
if (clause.must == '-') numMinuses++;
if (clause.isBareWord()) {
String s = clause.val;
if ("AND".equals(s)) {
numAND++;
} else if ("OR".equals(s)) {
numOR++;
} else if ("NOT".equals(s)) {
numNOT++;
} else if (lowercaseOperators) {
if ("and".equals(s)) {
numAND++;
sawLowerAnd=true;
} else if ("or".equals(s)) {
numOR++;
sawLowerOr=true;
}
}
}
}
numOptional = clauses.size() - (numPluses + numMinuses);
// convert lower or mixed case operators to uppercase if we saw them.
// only do this for the lucene query part and not for phrase query boosting
// since some fields might not be case insensitive.
// We don't use a regex for this because it might change and AND or OR in
// a phrase query in a case sensitive field.
if (sawLowerAnd || sawLowerOr) {
StringBuilder sb = new StringBuilder();
for (int i=0; i<clauses.size(); i++) {
Clause clause = clauses.get(i);
String s = clause.raw;
// and and or won't be operators at the start or end
if (i>0 && i+1<clauses.size()) {
if ("AND".equalsIgnoreCase(s)) {
s="AND";
} else if ("OR".equalsIgnoreCase(s)) {
s="OR";
}
}
sb.append(s);
sb.append(' ');
}
mainUserQuery = sb.toString();
}
// For correct lucene queries, turn off mm processing if there
// were explicit operators (except for AND).
boolean doMinMatched = (numOR + numNOT + numPluses + numMinuses) == 0;
try {
up.setRemoveStopFilter(!stopwords);
up.exceptions = true;
parsedUserQuery = up.parse(mainUserQuery);
if (stopwords && isEmpty(parsedUserQuery)) {
// if the query was all stop words, remove none of them
up.setRemoveStopFilter(true);
parsedUserQuery = up.parse(mainUserQuery);
}
} catch (Exception e) {
// ignore failure and reparse later after escaping reserved chars
up.exceptions = false;
}
if (parsedUserQuery != null && doMinMatched) {
String minShouldMatch = solrParams.get(DMP.MM, "100%");
if (parsedUserQuery instanceof BooleanQuery) {
U.setMinShouldMatch((BooleanQuery)parsedUserQuery, minShouldMatch);
}
}
if (parsedUserQuery == null) {
StringBuilder sb = new StringBuilder();
for (Clause clause : clauses) {
boolean doQuote = clause.isPhrase;
String s=clause.val;
if (!clause.isPhrase && ("OR".equals(s) || "AND".equals(s) || "NOT".equals(s))) {
doQuote=true;
}
if (clause.must != 0) {
sb.append(clause.must);
}
if (clause.field != null) {
sb.append(clause.field);
sb.append(':');
}
if (doQuote) {
sb.append('"');
}
sb.append(clause.val);
if (doQuote) {
sb.append('"');
}
sb.append(' ');
}
String escapedUserQuery = sb.toString();
parsedUserQuery = up.parse(escapedUserQuery);
// Only do minimum-match logic
String minShouldMatch = solrParams.get(DMP.MM, "100%");
if (parsedUserQuery instanceof BooleanQuery) {
BooleanQuery t = new BooleanQuery();
U.flattenBooleanQuery(t, (BooleanQuery)parsedUserQuery);
U.setMinShouldMatch(t, minShouldMatch);
parsedUserQuery = t;
}
}
query.add(parsedUserQuery, BooleanClause.Occur.MUST);
// sloppy phrase queries for proximity
if (phraseFields.size() > 0 ||
phraseFields2.size() > 0 ||
phraseFields3.size() > 0) {
// find non-field clauses
List<Clause> normalClauses = new ArrayList<Clause>(clauses.size());
for (Clause clause : clauses) {
if (clause.field != null || clause.isPhrase) continue;
// check for keywords "AND,OR,TO"
if (clause.isBareWord()) {
String s = clause.val.toString();
// avoid putting explict operators in the phrase query
if ("OR".equals(s) || "AND".equals(s) || "NOT".equals(s) || "TO".equals(s)) continue;
}
normalClauses.add(clause);
}
// full phrase...
addShingledPhraseQueries(query, normalClauses, phraseFields, 0,
tiebreaker, pslop);
// shingles...
addShingledPhraseQueries(query, normalClauses, phraseFields2, 2,
tiebreaker, pslop);
addShingledPhraseQueries(query, normalClauses, phraseFields3, 3,
tiebreaker, pslop);
}
}
/* * * Boosting Query * * */
boostParams = solrParams.getParams(DMP.BQ);
//List<Query> boostQueries = U.parseQueryStrings(req, boostParams);
boostQueries=null;
if (boostParams!=null && boostParams.length>0) {
boostQueries = new ArrayList<Query>();
for (String qs : boostParams) {
if (qs.trim().length()==0) continue;
Query q = subQuery(qs, null).getQuery();
boostQueries.add(q);
}
}
if (null != boostQueries) {
for(Query f : boostQueries) {
query.add(f, BooleanClause.Occur.SHOULD);
}
}
/* * * Boosting Functions * * */
String[] boostFuncs = solrParams.getParams(DMP.BF);
if (null != boostFuncs && 0 != boostFuncs.length) {
for (String boostFunc : boostFuncs) {
if(null == boostFunc || "".equals(boostFunc)) continue;
Map<String,Float> ff = SolrPluginUtils.parseFieldBoosts(boostFunc);
for (String f : ff.keySet()) {
Query fq = subQuery(f, FunctionQParserPlugin.NAME).getQuery();
Float b = ff.get(f);
if (null != b) {
fq.setBoost(b);
}
query.add(fq, BooleanClause.Occur.SHOULD);
}
}
}
//
// create a boosted query (scores multiplied by boosts)
//
Query topQuery = query;
multBoosts = solrParams.getParams("boost");
if (multBoosts!=null && multBoosts.length>0) {
List<ValueSource> boosts = new ArrayList<ValueSource>();
for (String boostStr : multBoosts) {
if (boostStr==null || boostStr.length()==0) continue;