/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xcmis.search.lucene.search;
import java.io.IOException;
import java.util.Set;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.Weight;
import org.xcmis.search.lucene.index.FieldNames;
import org.xcmis.spi.utils.Logger;
/**
* Created by The eXo Platform SAS.
*
* @author <a href="mailto:Sergey.Kabashnyuk@gmail.com">Sergey Kabashnyuk</a>
* @version $Id: DescendantQueryNode.java 2 2010-02-04 17:21:49Z andrew00x $
*/
public class DescendantQueryNode extends Query
{
/** The serialVersionUID. */
private static final long serialVersionUID = -6151493594236655389L;
/**
* Class logger.
*/
private final static Logger log = Logger.getLogger(DescendantQueryNode.class);
private final Query context;
private final Query parentQuery;
/**
*
*/
public DescendantQueryNode(Query context, Query parentQuery)
{
this.context = context;
this.parentQuery = parentQuery;
}
@Override
public void extractTerms(Set terms)
{
context.extractTerms(terms);
parentQuery.extractTerms(terms);
}
@Override
public Query rewrite(IndexReader reader) throws IOException
{
Query cQuery = null;
if (context != null)
{
cQuery = context.rewrite(reader);
}
Query pQuery = parentQuery.rewrite(reader);
if (((cQuery != null && cQuery.equals(context)) || (cQuery == null && context == null))
&& pQuery.equals(parentQuery)
)
{
return this;
}
return new DescendantQueryNode(cQuery, pQuery);
}
@Override
public String toString()
{
return "(DescendantQueryNode Parent:" + parentQuery + " query:" + context + ")";
}
/**
* {@inheritDoc}
*/
@Override
public String toString(String field)
{
return "(DescendantQueryNode Parent:" + parentQuery + " query:" + context + ")";
}
@Override
public Weight createWeight(Searcher searcher) throws IOException
{
return new DescendantQueryNodeWeight(searcher);
}
private Query getConetextQuery()
{
return context;
}
private class DescendantQueryNodeScorer extends Scorer
{
/**
* BitSet storing the id's of selected documents
*/
private final IndexReader reader;
private final Searcher searcher;
private Scorer currentContextScorer;
private final Scorer parentScorer;
private boolean scoreDocsInOrder;
private boolean topScorer;
protected DescendantQueryNodeScorer(Searcher searcher, Scorer parentScorer, IndexReader reader,
boolean scoreDocsInOrder, boolean topScorer)
{
super(searcher.getSimilarity());
this.parentScorer = parentScorer;
this.searcher = searcher;
this.reader = reader;
this.scoreDocsInOrder = scoreDocsInOrder;
this.topScorer = topScorer;
}
@Override
public int docID()
{
return currentContextScorer.docID();
}
@Override
public int nextDoc() throws IOException
{
if (currentContextScorer == null)
{
if (parentScorer == null || parentScorer.nextDoc() == Scorer.NO_MORE_DOCS)
{
log.error("parent not found");
return Scorer.NO_MORE_DOCS;
}
int parentDoc = parentScorer.docID();
Document parentDocument = reader.document(parentDoc, new UUIDFieldSelector());
if (context != null)
{
BooleanQuery bq = new BooleanQuery();
bq.add(context, Occur.MUST);
bq.add(new TermQuery(new Term(FieldNames.PARENT, parentDocument.get(FieldNames.UUID))), Occur.MUST);
if (log.isDebugEnabled())
{
log.debug("Sub query " + bq);
}
currentContextScorer = bq.createWeight(searcher).scorer(reader, scoreDocsInOrder, topScorer);
}
else
{
TermQuery newQuery = new TermQuery(new Term(FieldNames.PARENT, parentDocument.get(FieldNames.UUID)));
log.debug("Sub query " + newQuery);
currentContextScorer = newQuery.createWeight(searcher).scorer(reader, scoreDocsInOrder, topScorer);
}
}
return currentContextScorer.nextDoc();
}
@Override
public float score() throws IOException
{
return currentContextScorer.score();
}
@Override
public int advance(int target) throws IOException
{
return currentContextScorer.advance(target);
}
}
private class DescendantQueryNodeWeight extends Weight
{
private final Searcher searcher;
public DescendantQueryNodeWeight(Searcher searcher)
{
this.searcher = searcher;
}
public Explanation explain(IndexReader reader, int doc) throws IOException
{
return new Explanation();
}
public Query getQuery()
{
return DescendantQueryNode.this;
}
public float getValue()
{
return 1.0f;
}
public void normalize(float norm)
{
}
@Override
public Scorer scorer(IndexReader reader, boolean scoreDocsInOrder, boolean topScorer) throws IOException
{
Scorer parentScorer = parentQuery.createWeight(searcher).scorer(reader, scoreDocsInOrder, topScorer);
return new DescendantQueryNodeScorer(searcher, parentScorer, reader, scoreDocsInOrder, topScorer);
}
public float sumOfSquaredWeights() throws IOException
{
return 1.0f;
}
}
}