blob: b4e434ca61213aa88cd7ae22b01e6b9a1c408284 [file] [log] [blame]
/*
* 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.join;
import java.io.IOException;
import java.util.List;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
import org.apache.solr.common.SolrException;
import org.apache.solr.handler.component.ResponseBuilder;
import org.apache.solr.search.BitDocSet;
import org.apache.solr.search.DocSet;
import org.apache.solr.search.Filter;
import org.apache.solr.search.QueryContext;
import org.apache.solr.search.facet.BlockJoin;
import org.apache.solr.search.join.BlockJoinFieldFacetAccumulator.AggregatableDocIter;
/**
* Calculates facets on children documents and aggregates hits by parent documents.
* Enables when child.facet.field parameter specifies a field name for faceting.
* So far it supports string fields only. It requires to search by {@link ToParentBlockJoinQuery}.
*
* @deprecated This functionality is considered deprecated and will be removed at 9.0
* Users are encouraged to use <code>"uniqueBlock(\_root_)"</code> aggregation
* under <code>"terms"</code> facet and <code>"domain": { "blockChildren":...}</code> */
@Deprecated
public class BlockJoinDocSetFacetComponent extends BlockJoinFacetComponentSupport {
private final String bjqKey = this.getClass().getSimpleName()+".bjq";
private static final class SegmentChildren implements AggregatableDocIter {
private final BitDocSet allParentsBitsDocSet;
private int nextDoc = DocIdSetIterator.NO_MORE_DOCS;
private DocIdSetIterator disi;
private int currentParent=-1;
final LeafReaderContext segment;
final DocIdSet childrenMatches;
private SegmentChildren(LeafReaderContext subCtx, DocIdSet dis, BitDocSet allParentsBitsDocSet) {
this.allParentsBitsDocSet = allParentsBitsDocSet;
this.childrenMatches = dis;
this.segment = subCtx;
reset();
}
@Override
public Integer next() {
return nextDoc();
}
@Override
public boolean hasNext() {
return nextDoc != DocIdSetIterator.NO_MORE_DOCS;
}
@Override
public float score() {
return 0;
}
@Override
public int nextDoc() {
int lastDoc = nextDoc;
assert nextDoc != DocIdSetIterator.NO_MORE_DOCS;
if (lastDoc>currentParent) { // we passed the previous block, and need to reevaluate a parent
currentParent = allParentsBitsDocSet.getBits().nextSetBit(lastDoc+segment.docBase)-segment.docBase;
}
try {
nextDoc = disi.nextDoc();
} catch (IOException e) {
throw new RuntimeException(e);
}
return lastDoc;
}
@Override
public void reset() {
currentParent=-1;
try {
disi = childrenMatches.iterator();
if (disi != null) {
nextDoc = disi.nextDoc();
}else{
nextDoc = DocIdSetIterator.NO_MORE_DOCS;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public int getAggKey() {
return currentParent;
}
}
public BlockJoinDocSetFacetComponent() {}
@Override
public void prepare(ResponseBuilder rb) throws IOException {
if (getChildFacetFields(rb.req) != null) {
validateQuery(rb.getQuery());
rb.setNeedDocSet(true);
rb.req.getContext().put(bjqKey, extractChildQuery(rb.getQuery()));
}
}
private ToParentBlockJoinQuery extractChildQuery(Query query) {
if (!(query instanceof ToParentBlockJoinQuery)) {
if (query instanceof BooleanQuery) {
List<BooleanClause> clauses = ((BooleanQuery) query).clauses();
ToParentBlockJoinQuery once = null;
for (BooleanClause clause : clauses) {
if (clause.getQuery() instanceof ToParentBlockJoinQuery) {
if (once==null) {
once = (ToParentBlockJoinQuery) clause.getQuery();
} else {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "can't choose between " +
once + " and " + clause.getQuery());
}
}
}
if (once!=null) {
return once;
}
}
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, NO_TO_PARENT_BJQ_MESSAGE);
}
else{
return (ToParentBlockJoinQuery) query;
}
}
@Override
public void process(ResponseBuilder rb) throws IOException {
final BlockJoinParentQParser.AllParentsAware bjq =
(BlockJoinParentQParser.AllParentsAware) rb.req.getContext().get(bjqKey);
if(bjq!=null){
final DocSet parentResult = rb.getResults().docSet;
final BitDocSet allParentsBitsDocSet = rb.req.getSearcher().getDocSetBits(bjq.getParentQuery());
final DocSet allChildren = BlockJoin.toChildren(parentResult,
allParentsBitsDocSet,
rb.req.getSearcher().getDocSetBits( new MatchAllDocsQuery() ),
QueryContext.newContext(rb.req.getSearcher()));
final DocSet childQueryDocSet = rb.req.getSearcher().getDocSet(bjq.getChildQuery());
final DocSet selectedChildren = allChildren.intersection(childQueryDocSet);
// don't include parent into facet counts
//childResult = childResult.union(parentResult);// just to mimic the current logic
final List<LeafReaderContext> leaves = rb.req.getSearcher().getIndexReader().leaves();
Filter filter = selectedChildren.getTopFilter();
final BlockJoinFacetAccsHolder facetCounter = new BlockJoinFacetAccsHolder(rb.req);
for (int subIdx = 0; subIdx < leaves.size(); subIdx++) {
LeafReaderContext subCtx = leaves.get(subIdx);
DocIdSet dis = filter.getDocIdSet(subCtx, null); // solr docsets already exclude any deleted docs
AggregatableDocIter iter = new SegmentChildren(subCtx, dis, allParentsBitsDocSet);
if (iter.hasNext()){
facetCounter.doSetNextReader(subCtx);
facetCounter.countFacets(iter);
}
}
facetCounter.finish();
rb.req.getContext().put(COLLECTOR_CONTEXT_PARAM,facetCounter);
super.process(rb);
}
}
}