SOLR-4785: New MaxScoreQParserPlugin

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1481651 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jan Høydahl 2013-05-12 21:23:37 +00:00
parent 91f908f513
commit 1cd6be0712
5 changed files with 239 additions and 0 deletions

View File

@ -78,6 +78,8 @@ New Features
in cases where exact hit-counts are unnecessary. Also, when "collateExtendedResults"
is false, this optimization is always made (James Dyer).
* SOLR-4785: New MaxScoreQParserPlugin returning max() instead of sum() of terms (janhoy)
Bug Fixes
----------------------

View File

@ -0,0 +1,83 @@
/*
* 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.
*/
package org.apache.solr.search;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.DisjunctionMaxQuery;
import org.apache.lucene.search.Query;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.request.SolrQueryRequest;
import java.util.ArrayList;
import java.util.Collection;
/**
* @see MaxScoreQParserPlugin
*/
public class MaxScoreQParser extends LuceneQParser {
float tie = 0.0f;
public MaxScoreQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
super(qstr, localParams, params, req);
if (getParam("tie") != null) {
tie = Float.parseFloat(getParam("tie"));
}
}
/**
* Parses the query exactly like the Lucene parser does, but
* delegates all SHOULD clauses to DisjunctionMaxQuery with
* meaning only the clause with the max score will contribute
* to the overall score, unless the tie parameter is specified.
* <br/>
* The max() is only calculated from the SHOULD clauses.
* Any MUST clauses will be passed through as separate
* BooleanClauses and thus always contribute to the score.
* @return the resulting Query
* @throws org.apache.solr.search.SyntaxError if parsing fails
*/
@Override
public Query parse() throws SyntaxError {
Query q = super.parse();
if (!(q instanceof BooleanQuery)) {
return q;
}
BooleanQuery obq = (BooleanQuery)q;
Collection<Query> should = new ArrayList<Query>();
Collection<BooleanClause> prohibOrReq = new ArrayList<BooleanClause>();
BooleanQuery newq = new BooleanQuery();
for (BooleanClause clause : obq.getClauses()) {
if(clause.isProhibited() || clause.isRequired()) {
prohibOrReq.add(clause);
} else {
BooleanQuery bq = new BooleanQuery();
bq.add(clause);
should.add(bq);
}
}
if (should.size() > 0) {
DisjunctionMaxQuery dmq = new DisjunctionMaxQuery(should, tie);
newq.add(dmq, BooleanClause.Occur.SHOULD);
}
for(BooleanClause c : prohibOrReq) {
newq.add(c);
}
return newq;
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.
*/
package org.apache.solr.search;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.request.SolrQueryRequest;
/**
* Parses a query like Lucene query parser, but scoring with max score, not sum
* <br>Accepts the "tie" request parameter as with dismax. 0.0=max, 1.0=sum
* <br>All other parameters are as with Lucene parser
* <br>Example: <code>q=foo {!maxscore v=$myq}&myq=A OR B OR C</code>
*/
public class MaxScoreQParserPlugin extends LuceneQParserPlugin {
public static String NAME = "maxscore";
@Override
public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
return new MaxScoreQParser(qstr, localParams, params, req);
}
}

View File

@ -43,6 +43,7 @@ public abstract class QParserPlugin implements NamedListInitializedPlugin {
JoinQParserPlugin.NAME, JoinQParserPlugin.class,
SurroundQParserPlugin.NAME, SurroundQParserPlugin.class,
SwitchQParserPlugin.NAME, SwitchQParserPlugin.class,
MaxScoreQParserPlugin.NAME, MaxScoreQParserPlugin.class
};
/** return a {@link QParser} */

View File

@ -0,0 +1,117 @@
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.*;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.util.AbstractSolrTestCase;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
public class TestMaxScoreQueryParser extends AbstractSolrTestCase {
Query q;
BooleanClause[] clauses;
@BeforeClass
public static void beforeClass() throws Exception {
initCore("solrconfig.xml", "schema.xml");
}
@Test
public void testFallbackToLucene() {
q = parse("foo");
assertTrue(q instanceof TermQuery);
q = parse("price:[0 TO 10]");
assertTrue(q instanceof NumericRangeQuery);
}
@Test
public void testNoShouldClauses() {
q = parse("+foo +bar");
clauses = clauses(q);
assertEquals(2, clauses.length);
assertTrue(clauses[0].isRequired());
assertTrue(clauses[1].isRequired());
q = parse("+foo -bar");
clauses = clauses(q);
assertEquals(2, clauses.length);
assertTrue(clauses[0].isRequired());
assertTrue(clauses[1].isProhibited());
}
@Test
public void testPureMax() {
q = parse("foo bar");
clauses = clauses(q);
assertEquals(1, clauses.length);
assertTrue(clauses[0].getQuery() instanceof DisjunctionMaxQuery);
assertEquals(0.0, ((DisjunctionMaxQuery) clauses[0].getQuery()).getTieBreakerMultiplier(), 1e-15);
ArrayList<Query> qa = ((DisjunctionMaxQuery) clauses[0].getQuery()).getDisjuncts();
assertEquals(2, qa.size());
assertEquals("text:foo", qa.get(0).toString());
}
@Test
public void testMaxAndProhibited() {
q = parse("foo bar -baz");
clauses = clauses(q);
assertEquals(2, clauses.length);
assertTrue(clauses[0].getQuery() instanceof DisjunctionMaxQuery);
assertTrue(clauses[1].getQuery() instanceof TermQuery);
assertEquals("text:baz", clauses[1].getQuery().toString());
assertTrue(clauses[1].isProhibited());
}
@Test
public void testTie() {
q = parse("foo bar", "tie", "0.5");
clauses = clauses(q);
assertEquals(1, clauses.length);
assertTrue(clauses[0].getQuery() instanceof DisjunctionMaxQuery);
assertEquals(0.5, ((DisjunctionMaxQuery) clauses[0].getQuery()).getTieBreakerMultiplier(), 1e-15);
}
//
// Helper methods
//
private Query parse(String q, String... params) {
try {
ModifiableSolrParams p = new ModifiableSolrParams();
ArrayList<String> al = new ArrayList<String>(Arrays.asList(params));
while(al.size() >= 2) {
p.add(al.remove(0), al.remove(0));
}
return new MaxScoreQParser(q, p, new ModifiableSolrParams(), req(q)).parse();
} catch (SyntaxError syntaxError) {
fail("Failed with exception "+syntaxError.getMessage());
}
fail("Parse failed");
return null;
}
private BooleanClause[] clauses(Query q) {
return ((BooleanQuery) q).getClauses();
}
}