package lux;
import java.io.IOException;
import lux.exception.LuxException;
import lux.search.DocIterator;
import lux.search.LuxSearcher;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.trans.XPathException;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Sort;
/**
* Executes a Lucene search and provides the results as a Saxon {@link SequenceIterator}.
* Sort criteria are translated into Lucene SortFields: relevance score, intrinsic document order, and
* field-value orderings are supported.
*/
public class SearchResultIterator extends SearchIteratorBase {
private final Query query;
private final DocIdSetIterator docIter;
private final LuxSearcher searcher;
private CachingDocReader docCache;
private Sort sort;
/**
* Executes a Lucene search.
* @param eval provides the link to the index via its {@link IndexSearcher}.
* @param query the query to execute
* @param sortCriteria sort criteria, formatted as a comma-separated list of sort field names;
* each name may be followed by ascending|descending, and/or by "empty greatest"|"empty least".
* The default is "ascending empty least".
* The sort criteria are Lucene field names, or may be the special name "lux:score", which selects
* relevance score ranking, which is always sorted in descending order: modifiers on relevance orders are ignored.
* If no ordering is provided, results are returned in intrinsic document order (ie ordered by document ID).
* @param start1 the 1-based starting position of the iteration
* @throws IOException
*/
public SearchResultIterator (Evaluator eval, Query query, String[] sortCriteria, int start1) throws IOException {
super (eval, sortCriteria, start1);
this.query = query;
if (stats != null) {
stats.query = query.toString();
}
this.searcher = eval.getSearcher();
this.docCache = eval.getDocReader();
if (searcher == null) {
throw new LuxException("Attempted to search using an Evaluator that has no searcher");
}
if (sortCriteria != null && sortCriteria.length > 0) {
sort = makeSortFromCriteria(sortCriteria);
} else {
sort = null;
}
if (sort != null) {
docIter = searcher.search(query, sort);
} else {
docIter = searcher.searchOrdered(query);
}
if (start1 > 1) {
advanceTo (start1);
}
}
/**
* @return the next result. Returns null when there are no more results.
* Calling this function after null has been returned may result
* in an error.
* @throws XPathException if there is an error while searching
*/
@Override
public NodeInfo next() throws XPathException {
long t = System.nanoTime();
int startPosition = position;
try {
int docID = docIter.nextDoc();
// LoggerFactory.getLogger(ResultIterator.class).trace("GET {} {}", docID, query);
if (docID == Scorer.NO_MORE_DOCS) {
position = -1;
current = null;
} else {
long t1 = System.nanoTime();
XdmItem doc;
if (sort == null) {
// in this case, we are iterating over the readers in order, so we can make the retrieval a bit
// faster by going directly to the appropriate leaf reader
doc = docCache.get(docID, ((DocIterator) docIter).getCurrentReaderContext());
} else {
// FIXME: use relative docID and leaf reader here so we can avoid a binary search to find the
// correct reader. In fact the LuxSearcher *already* has the correct leaf reader - we just need
// to make it available here - probably via the docIter
doc = docCache.get(docID, searcher.getIndexReader());
}
NodeInfo item = (NodeInfo) doc.getUnderlyingValue();
// assert documents in order : Note this is no longer accurate now that we have implemented "order by"
// assert (current == null || ((TinyDocumentImpl)item).getDocumentNumber() > ((TinyDocumentImpl)current).getDocumentNumber());
current = item;
++position;
if (stats != null) {
stats.retrievalTime += System.nanoTime() - t1;
}
}
} catch (IOException e) {
throw new XPathException(e);
} finally {
if (stats != null) {
if (position >= 0) {
stats.docCount += (position - startPosition);
}
stats.totalTime += System.nanoTime() - t;
}
}
return current;
}
/**
* advance the iterator to (just before) the given (1-based) position. Sets current to null: next() must be called
* after this method in order to retrieve the result at the position.
* @param startPosition
* @throws IOException
*/
protected void advanceTo (int startPosition) throws IOException {
long t = System.nanoTime();
int start0 = startPosition-1;
try {
int docID=0;
current = null;
while (position < start0) {
docID = docIter.nextDoc();
if (docID == Scorer.NO_MORE_DOCS) {
position = -1;
break;
}
++position;
}
if (stats != null) {
}
} finally {
if (stats != null) {
long t1 = System.nanoTime();
stats.retrievalTime += t1 - t;
stats.totalTime += t1 - t;
}
}
}
/**
* @return a clone of this iterator, reset to the initial position.
*/
@Override
public SequenceIterator<NodeInfo> getAnother() throws XPathException {
try {
return new SearchResultIterator (eval, query, sortCriteria, start + 1);
} catch (IOException e) {
throw new XPathException (e);
}
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */