| Index: solr/core/src/java/org/apache/solr/search/join/BlockJoinChildQParser.java |
| =================================================================== |
| --- solr/core/src/java/org/apache/solr/search/join/BlockJoinChildQParser.java (revision 1713172) |
| +++ solr/core/src/java/org/apache/solr/search/join/BlockJoinChildQParser.java (working copy) |
| @@ -18,6 +18,7 @@ |
| package org.apache.solr.search.join; |
| |
| import org.apache.lucene.search.Query; |
| +import org.apache.lucene.search.join.BitSetProducer; |
| import org.apache.lucene.search.join.ToChildBlockJoinQuery; |
| import org.apache.solr.common.params.SolrParams; |
| import org.apache.solr.request.SolrQueryRequest; |
| @@ -29,8 +30,8 @@ |
| } |
| |
| @Override |
| - protected Query createQuery(Query parentListQuery, Query query, String scoreMode) { |
| - return new ToChildBlockJoinQuery(query, getFilter(parentListQuery).filter); |
| + protected Query createQuery(BitSetProducer parentFilter, Query query, String scoreMode) { |
| + return new ToChildBlockJoinQuery(query, parentFilter); |
| } |
| |
| @Override |
| Index: solr/core/src/java/org/apache/solr/search/join/BlockJoinParentQParser.java |
| =================================================================== |
| --- solr/core/src/java/org/apache/solr/search/join/BlockJoinParentQParser.java (revision 1713172) |
| +++ solr/core/src/java/org/apache/solr/search/join/BlockJoinParentQParser.java (working copy) |
| @@ -18,11 +18,19 @@ |
| package org.apache.solr.search.join; |
| |
| import java.io.IOException; |
| +import java.util.Collections; |
| +import java.util.Map; |
| +import java.util.WeakHashMap; |
| |
| +import org.apache.lucene.index.CheckIndex; |
| +import org.apache.lucene.index.DirectoryReader; |
| +import org.apache.lucene.index.IndexReaderContext; |
| +import org.apache.lucene.index.LeafReader; |
| import org.apache.lucene.index.LeafReaderContext; |
| import org.apache.lucene.search.DocIdSet; |
| import org.apache.lucene.search.Query; |
| import org.apache.lucene.search.join.BitSetProducer; |
| +import org.apache.lucene.search.join.CheckJoinIndex; |
| import org.apache.lucene.search.join.QueryBitSetProducer; |
| import org.apache.lucene.search.join.ScoreMode; |
| import org.apache.lucene.search.join.ToParentBlockJoinQuery; |
| @@ -29,6 +37,8 @@ |
| import org.apache.lucene.util.BitDocIdSet; |
| import org.apache.lucene.util.BitSet; |
| import org.apache.lucene.util.Bits; |
| +import org.apache.solr.common.SolrException; |
| +import org.apache.solr.common.SolrException.ErrorCode; |
| import org.apache.solr.common.params.SolrParams; |
| import org.apache.solr.request.SolrQueryRequest; |
| import org.apache.solr.search.BitsFilteredDocIdSet; |
| @@ -57,25 +67,35 @@ |
| String scoreMode = localParams.get("score", ScoreMode.None.name()); |
| QParser parentParser = subQuery(filter, null); |
| Query parentQ = parentParser.getQuery(); |
| - |
| - String queryText = localParams.get(QueryParsing.V); |
| - // there is no child query, return parent filter from cache |
| - if (queryText == null || queryText.length()==0) { |
| - SolrConstantScoreQuery wrapped = new SolrConstantScoreQuery(getFilter(parentQ)); |
| - wrapped.setCache(false); |
| - return wrapped; |
| + |
| + try{ |
| + String queryText = localParams.get(QueryParsing.V); |
| + // there is no child query, return parent filter from cache |
| + if (queryText == null || queryText.length()==0) { |
| + SolrConstantScoreQuery wrapped = new SolrConstantScoreQuery(getFilter(parentQ,false)); |
| + wrapped.setCache(false); |
| + return wrapped; |
| + } |
| + QParser childrenParser = subQuery(queryText, null); |
| + Query childrenQuery = childrenParser.getQuery(); |
| + // checking parent filter is necessary, but only for the first level of nesting |
| + final BitDocIdSetFilterWrapper parentFilterWrapper = getFilter(parentQ, this.recurseCount==0); |
| + |
| + return createQuery(parentFilterWrapper.filter , childrenQuery, scoreMode); |
| + }catch(IOException ioe){ // very rare when checking searches for parent |
| + throw new SolrException(ErrorCode.SERVER_ERROR, ioe); |
| } |
| - QParser childrenParser = subQuery(queryText, null); |
| - Query childrenQuery = childrenParser.getQuery(); |
| - return createQuery(parentQ, childrenQuery, scoreMode); |
| + catch(IllegalStateException ise){ // check index exceptions |
| + throw new SolrException(ErrorCode.BAD_REQUEST, ise); |
| + } |
| } |
| |
| - protected Query createQuery(Query parentList, Query query, String scoreMode) throws SyntaxError { |
| - return new ToParentBlockJoinQuery(query, getFilter(parentList).filter, |
| + protected Query createQuery(BitSetProducer parentFilter, Query query, String scoreMode) throws SyntaxError { |
| + return new ToParentBlockJoinQuery(query, parentFilter, |
| ScoreModeParser.parse(scoreMode)); |
| } |
| |
| - BitDocIdSetFilterWrapper getFilter(Query parentList) { |
| + BitDocIdSetFilterWrapper getFilter(Query parentList, boolean checkIndex) throws IOException { |
| SolrCache parentCache = req.getSearcher().getCache(CACHE_NAME); |
| // lazily retrieve from solr cache |
| Filter filter = null; |
| @@ -82,7 +102,7 @@ |
| if (parentCache != null) { |
| filter = (Filter) parentCache.get(parentList); |
| } |
| - BitDocIdSetFilterWrapper result; |
| + final BitDocIdSetFilterWrapper result; |
| if (filter instanceof BitDocIdSetFilterWrapper) { |
| result = (BitDocIdSetFilterWrapper) filter; |
| } else { |
| @@ -91,6 +111,10 @@ |
| parentCache.put(parentList, result); |
| } |
| } |
| + // only when necessary |
| + if(checkIndex){ |
| + result.checkIndex(req.getSearcher().getIndexReader()); |
| + } |
| return result; |
| } |
| |
| @@ -102,11 +126,19 @@ |
| static class BitDocIdSetFilterWrapper extends Filter { |
| |
| final BitSetProducer filter; |
| + private final Map<Object,Boolean> checkedTopReaders = Collections.synchronizedMap(new WeakHashMap<>()); |
| |
| BitDocIdSetFilterWrapper(BitSetProducer filter) { |
| this.filter = filter; |
| } |
| |
| + public void checkIndex(DirectoryReader directoryReader) throws IOException { |
| + if(checkedTopReaders.get(directoryReader)!=Boolean.TRUE){ |
| + CheckJoinIndex.check(directoryReader, filter); |
| + checkedTopReaders.put(directoryReader,Boolean.TRUE); |
| + } |
| + } |
| + |
| @Override |
| public DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException { |
| BitSet set = filter.getBitSet(context); |
| Index: solr/core/src/test/org/apache/solr/search/join/BJQParserTest.java |
| =================================================================== |
| --- solr/core/src/test/org/apache/solr/search/join/BJQParserTest.java (revision 1713172) |
| +++ solr/core/src/test/org/apache/solr/search/join/BJQParserTest.java (working copy) |
| @@ -17,10 +17,23 @@ |
| |
| package org.apache.solr.search.join; |
| |
| +import org.apache.lucene.index.LeafReaderContext; |
| +import org.apache.lucene.queryparser.xml.builders.RangeQueryBuilder; |
| +import org.apache.lucene.search.IndexSearcher; |
| +import org.apache.lucene.search.Sort; |
| +import org.apache.lucene.search.SortField; |
| +import org.apache.lucene.search.TermRangeQuery; |
| +import org.apache.lucene.search.TopFieldDocs; |
| +import org.apache.lucene.search.SortField.Type; |
| +import org.apache.lucene.search.join.CheckJoinIndex; |
| import org.apache.lucene.search.join.ScoreMode; |
| import org.apache.solr.SolrTestCaseJ4; |
| import org.apache.solr.common.SolrException; |
| +import org.apache.solr.common.SolrException.ErrorCode; |
| +import org.apache.solr.common.params.MapSolrParams; |
| +import org.apache.solr.common.params.MultiMapSolrParams; |
| import org.apache.solr.common.util.NamedList; |
| +import org.apache.solr.request.SolrQueryRequest; |
| import org.apache.solr.search.SolrCache; |
| import org.apache.solr.util.BaseTestHarness; |
| import org.junit.BeforeClass; |
| @@ -30,6 +43,7 @@ |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| +import java.util.HashMap; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Locale; |
| @@ -139,6 +153,31 @@ |
| assertQ(req("q", childb), sixParents); |
| } |
| |
| + @Test |
| + public void testBrokenParentFilter() throws IOException, Exception { |
| + final SolrQueryRequest req = req(); |
| + final List<LeafReaderContext> segments = req.getSearcher().getLeafReader().leaves(); |
| + final LeafReaderContext aSegment = segments.get(random().nextInt(segments.size())); |
| + final IndexSearcher segmentSearcher = new IndexSearcher(aSegment, null); |
| + final TopFieldDocs parentsFromSegment = segmentSearcher.search( |
| + TermRangeQuery.newStringRange("parent_s", null, null, true, true) |
| + , sixParents.length, new Sort( new SortField(null, Type.DOC, true))); |
| + |
| + final StringBuilder noParentsClasuse = new StringBuilder(); |
| + for(int p=0;p<parentsFromSegment.totalHits;p++){ |
| + final String parent = segmentSearcher.doc( |
| + parentsFromSegment.scoreDocs[p].doc).get("parent_s"); |
| + noParentsClasuse.append(" -parent_s:"+parent); |
| + if(p==0 && !rarely()){// most times I wish to test absebse of last parent in a segment |
| + break; |
| + } |
| + } |
| + req.setParams(new MapSolrParams(new HashMap<String,String>(){{ |
| + put("q","{!parent which=\"parent_s:[* TO *]"+noParentsClasuse.toString()+"\"}child_s:l"); |
| + }})); |
| + assertQEx("expecting an exeption from "+CheckJoinIndex.class.getSimpleName(),"segment", req, ErrorCode.BAD_REQUEST); |
| + } |
| + |
| private static final String sixParents[] = new String[] { |
| "//*[@numFound='6']", "//doc/arr[@name=\"parent_s\"]/str='a'", |
| "//doc/arr[@name=\"parent_s\"]/str='b'", |