SOLR-5882: introducing local param {!parent score=..}..

fixing ScoreMode.Min for ToParentBlockJoinQuery
fixing ScoreMode parsing exception in ScoreJoinQParserPlugin

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1693926 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Mikhail Khludnev 2015-08-03 16:14:52 +00:00
parent 8914ec5f75
commit 83520603c1
10 changed files with 131 additions and 40 deletions

View File

@ -308,6 +308,8 @@ Bug fixes
* LUCENE-6704: GeoPointDistanceQuery was visiting too many term ranges, * LUCENE-6704: GeoPointDistanceQuery was visiting too many term ranges,
consuming too much heap for a large radius (Nick Knize via Mike McCandless) consuming too much heap for a large radius (Nick Knize via Mike McCandless)
* SOLR-5882: fix ScoreMode.Min at ToParentBlockJoinQuery (Mikhail Khludnev)
Changes in Runtime Behavior Changes in Runtime Behavior
* LUCENE-6501: The subreader structure in ParallelCompositeReader * LUCENE-6501: The subreader structure in ParallelCompositeReader

View File

@ -297,7 +297,7 @@ public class ToParentBlockJoinQuery extends Query {
pendingChildScores[childDocUpto] = childScore; pendingChildScores[childDocUpto] = childScore;
} }
maxScore = Math.max(childScore, maxScore); maxScore = Math.max(childScore, maxScore);
minScore = Math.min(childFreq, minScore); minScore = Math.min(childScore, minScore);
totalScore += childScore; totalScore += childScore;
parentFreq += childFreq; parentFreq += childFreq;
} }

View File

@ -174,6 +174,8 @@ New Features
* SOLR-6234: Scoring for query time join (Mikhail Khludnev) * SOLR-6234: Scoring for query time join (Mikhail Khludnev)
* SOLR-5882: score local parameter for block join query parser {!parent} (Andrey Kudryavtsev, Mikhail Khludnev)
Bug Fixes Bug Fixes
---------------------- ----------------------

View File

@ -28,7 +28,8 @@ public class BlockJoinChildQParser extends BlockJoinParentQParser {
super(qstr, localParams, params, req); super(qstr, localParams, params, req);
} }
protected Query createQuery(Query parentListQuery, Query query) { @Override
protected Query createQuery(Query parentListQuery, Query query, String scoreMode) {
return new ToChildBlockJoinQuery(query, getFilter(parentListQuery).filter); return new ToChildBlockJoinQuery(query, getFilter(parentListQuery).filter);
} }
@ -37,5 +38,3 @@ public class BlockJoinChildQParser extends BlockJoinParentQParser {
return "of"; return "of";
} }
} }

View File

@ -55,6 +55,7 @@ class BlockJoinParentQParser extends QParser {
@Override @Override
public Query parse() throws SyntaxError { public Query parse() throws SyntaxError {
String filter = localParams.get(getParentFilterLocalParamName()); String filter = localParams.get(getParentFilterLocalParamName());
String scoreMode = localParams.get("score", ScoreMode.None.name());
QParser parentParser = subQuery(filter, null); QParser parentParser = subQuery(filter, null);
Query parentQ = parentParser.getQuery(); Query parentQ = parentParser.getQuery();
@ -67,11 +68,12 @@ class BlockJoinParentQParser extends QParser {
} }
QParser childrenParser = subQuery(queryText, null); QParser childrenParser = subQuery(queryText, null);
Query childrenQuery = childrenParser.getQuery(); Query childrenQuery = childrenParser.getQuery();
return createQuery(parentQ, childrenQuery); return createQuery(parentQ, childrenQuery, scoreMode);
} }
protected Query createQuery(Query parentList, Query query) { protected Query createQuery(Query parentList, Query query, String scoreMode) throws SyntaxError {
return new ToParentBlockJoinQuery(query, getFilter(parentList).filter, ScoreMode.None); return new ToParentBlockJoinQuery(query, getFilter(parentList).filter,
ScoreModeParser.parse(scoreMode));
} }
BitDocIdSetFilterWrapper getFilter(Query parentList) { BitDocIdSetFilterWrapper getFilter(Query parentList) {

View File

@ -17,6 +17,7 @@
package org.apache.solr.search.join; package org.apache.solr.search.join;
import org.apache.lucene.search.join.ScoreMode;
import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
@ -25,7 +26,8 @@ import org.apache.solr.search.QParserPlugin;
/** /**
* Usage: {!parent which="PARENT:true"}CHILD_PRICE:10 * Usage: {!parent which="PARENT:true"}CHILD_PRICE:10
* * supports optional <code>score</code> parameter with one of {@link ScoreMode} values:
* None,Avg,Total,Min,Max. Lowercase is also accepted.
**/ **/
public class BlockJoinParentQParserPlugin extends QParserPlugin { public class BlockJoinParentQParserPlugin extends QParserPlugin {
public static final String NAME = "parent"; public static final String NAME = "parent";

View File

@ -18,10 +18,6 @@
package org.apache.solr.search.join; package org.apache.solr.search.join;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader;
@ -207,15 +203,6 @@ public class ScoreJoinQParserPlugin extends QParserPlugin {
} }
} }
final static Map<String, ScoreMode> lowercase = Collections.unmodifiableMap( new HashMap<String, ScoreMode>() {
{
for (ScoreMode s : ScoreMode.values()) {
put(s.name().toLowerCase(Locale.ROOT), s);
put(s.name(), s);
}
}
});
@Override @Override
public void init(NamedList args) { public void init(NamedList args) {
} }
@ -229,7 +216,7 @@ public class ScoreJoinQParserPlugin extends QParserPlugin {
final String fromField = localParams.get("from"); final String fromField = localParams.get("from");
final String fromIndex = localParams.get("fromIndex"); final String fromIndex = localParams.get("fromIndex");
final String toField = localParams.get("to"); final String toField = localParams.get("to");
final ScoreMode scoreMode = parseScore(); final ScoreMode scoreMode = ScoreModeParser.parse(getParam(SCORE));
final String v = localParams.get(CommonParams.VALUE); final String v = localParams.get(CommonParams.VALUE);
@ -279,16 +266,8 @@ public class ScoreJoinQParserPlugin extends QParserPlugin {
return new SameCoreJoinQuery(fromQuery, fromField, toField, scoreMode); return new SameCoreJoinQuery(fromQuery, fromField, toField, scoreMode);
} }
} }
private ScoreMode parseScore() {
String score = getParam(SCORE);
final ScoreMode scoreMode = lowercase.get(score);
if (scoreMode == null) {
throw new IllegalArgumentException("Unable to parse ScoreMode from: " + score);
}
return scoreMode;
}
}; };
} }
} }

View File

@ -0,0 +1,54 @@
package org.apache.solr.search.join;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.lucene.search.join.ScoreMode;
import org.apache.solr.search.SyntaxError;
/*
* 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.
*/
class ScoreModeParser {
final private static Map<String, ScoreMode> lowerAndCapitalCase =
Collections.unmodifiableMap( new HashMap<String, ScoreMode>() {
{
for (ScoreMode s : ScoreMode.values()) {
put(s.name().toLowerCase(Locale.ROOT), s);
put(s.name(), s);
}
}
});
private ScoreModeParser(){}
/**
* recognizes as-is {@link ScoreMode} names, and lowercase as well,
* otherwise throws exception
* @throws SyntaxError when it's unable to parse
* */
static ScoreMode parse(String score) throws SyntaxError {
final ScoreMode scoreMode = lowerAndCapitalCase.get(score);
if (scoreMode == null) {
throw new SyntaxError("Unable to parse ScoreMode from: " + score);
}
return scoreMode;
}
}

View File

@ -17,9 +17,12 @@
package org.apache.solr.search.join; package org.apache.solr.search.join;
import org.apache.lucene.search.join.ScoreMode;
import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.NamedList;
import org.apache.solr.search.SolrCache; import org.apache.solr.search.SolrCache;
import org.apache.solr.util.BaseTestHarness;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -29,6 +32,10 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Locale;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPathConstants;
public class BJQParserTest extends SolrTestCaseJ4 { public class BJQParserTest extends SolrTestCaseJ4 {
@ -165,6 +172,52 @@ public class BJQParserTest extends SolrTestCaseJ4 {
beParents); beParents);
} }
public void testScoreNoneScoringForParent() throws Exception {
assertQ("score=none yields 0.0 score",
req("q", "{!parent which=\"parent_s:[* TO *]\" "+(
rarely()? "":(rarely()? "score=None":"score=none")
)+"}child_s:l","fl","score"),
"//*[@numFound='6']",
"(//float[@name='score'])["+(random().nextInt(6)+1)+"]=0.0");
}
public void testWrongScoreExceptionForParent() throws Exception {
final String aMode = ScoreMode.values()[random().nextInt(ScoreMode.values().length)].name();
final String wrongMode = rarely()? "":(rarely()? " ":
rarely()? aMode.substring(1):aMode.toUpperCase(Locale.ROOT));
assertQEx("wrong score mode",
req("q", "{!parent which=\"parent_s:[* TO *]\" score="+wrongMode+"}child_s:l","fl","score")
, SolrException.ErrorCode.BAD_REQUEST.code);
}
public void testScoresForParent() throws Exception{
final ArrayList<ScoreMode> noNone = new ArrayList<>(Arrays.asList(ScoreMode.values()));
noNone.remove(ScoreMode.None);
final String notNoneMode = (noNone.get(random().nextInt(noNone.size()))).name();
String leastScore = getLeastScore("child_s:l");
assertTrue(leastScore+" > 0.0", Float.parseFloat(leastScore)>0.0);
final String notNoneLower = usually() ? notNoneMode: notNoneMode.toLowerCase(Locale.ROOT);
assertQ(req("q", "{!parent which=\"parent_s:[* TO *]\" score="+notNoneLower+"}child_s:l","fl","score"),
"//*[@numFound='6']","(//float[@name='score'])["+(random().nextInt(6)+1)+"]>='"+leastScore+"'");
}
public void testScoresForChild() throws Exception{
String leastScore = getLeastScore("parent_s:a");
assertTrue(leastScore+" > 0.0", Float.parseFloat(leastScore)>0.0);
assertQ(
req("q", "{!child of=\"parent_s:[* TO *]\"}parent_s:a","fl","score"),
"//*[@numFound='6']","(//float[@name='score'])["+(random().nextInt(6)+1)+"]>='"+leastScore+"'");
}
private String getLeastScore(String query) throws Exception {
final String resp = h.query(req("q",query, "sort","score asc", "fl","score"));
return (String) BaseTestHarness.
evaluateXPath(resp,"(//float[@name='score'])[1]/text()",
XPathConstants.STRING);
}
@Test @Test
public void testFq() { public void testFq() {
assertQ( assertQ(

View File

@ -27,6 +27,7 @@ import java.util.Random;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.join.ScoreMode;
import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestInfo; import org.apache.solr.request.SolrRequestInfo;
@ -245,14 +246,11 @@ public class TestScoreJoinQPScore extends SolrTestCaseJ4 {
assertEquals("lowercase shouldn't change anything", resp, repeat); assertEquals("lowercase shouldn't change anything", resp, repeat);
try { final String aMod = score.substring(0, score.length() - 1);
h.query(req("q", "{!join from=" + from + " to=" + to + " score=" + score.substring(0, score.length() - 1) + assertQEx("exception on "+aMod, "ScoreMode",
"}" + q, "fl", "id", "omitHeader", "true") req("q", "{!join from=" + from + " to=" + to + " score=" + aMod +
); "}" + q, "fl", "id", "omitHeader", "true"),
fail("excpecting exception"); SolrException.ErrorCode.BAD_REQUEST);
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("ScoreMode"));
}
} }
// this queries are not overlap, with other in this test case. // this queries are not overlap, with other in this test case.
// however it might be better to extract this method into the separate suite // however it might be better to extract this method into the separate suite