/*
* 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.
*/
using System;
using System.Linq;
using Lucene.Net.Index;
using Document = Lucene.Net.Documents.Document;
using FieldSelector = Lucene.Net.Documents.FieldSelector;
using CorruptIndexException = Lucene.Net.Index.CorruptIndexException;
using IndexReader = Lucene.Net.Index.IndexReader;
using Term = Lucene.Net.Index.Term;
using Directory = Lucene.Net.Store.Directory;
using ReaderUtil = Lucene.Net.Util.ReaderUtil;
namespace Lucene.Net.Search
{
/// Implements search over a single IndexReader.
///
/// Applications usually need only call the inherited
/// or methods. For performance reasons it is
/// recommended to open only one IndexSearcher and use it for all of your searches.
///
/// NOTE:
/// instances are completely
/// thread safe, meaning multiple threads can call any of its
/// methods, concurrently. If your application requires
/// external synchronization, you should not
/// synchronize on the IndexSearcher instance;
/// use your own (non-Lucene) objects instead.
///
[Serializable]
public class IndexSearcher : Searcher
{
internal IndexReader reader;
private bool closeReader;
private bool isDisposed;
// NOTE: these members might change in incompatible ways
// in the next release
private IndexReader[] subReaders;
private int[] docStarts;
/// Creates a searcher searching the index in the named
/// directory, with readOnly=true
/// CorruptIndexException if the index is corrupt
/// IOException if there is a low-level IO error
public IndexSearcher(Directory path)
: this(IndexReader.Open(path, true), true)
{
}
/// Creates a searcher searching the index in the named
/// directory. You should pass readOnly=true, since it
/// gives much better concurrent performance, unless you
/// intend to do write operations (delete documents or
/// change norms) with the underlying IndexReader.
///
/// CorruptIndexException if the index is corrupt
/// IOException if there is a low-level IO error
/// directory where IndexReader will be opened
///
/// if true, the underlying IndexReader
/// will be opened readOnly
///
public IndexSearcher(Directory path, bool readOnly):this(IndexReader.Open(path, readOnly), true)
{
}
/// Creates a searcher searching the provided index
///
/// Note that the underlying IndexReader is not closed, if
/// IndexSearcher was constructed with IndexSearcher(IndexReader r).
/// If the IndexReader was supplied implicitly by specifying a directory, then
/// the IndexReader gets closed.
///
///
public IndexSearcher(IndexReader r):this(r, false)
{
}
///
/// Expert: directly specify the reader, subReaders and their
/// DocID starts
///
/// NOTE: This API is experimental and
/// might change in incompatible ways in the next
/// release
///
public IndexSearcher(IndexReader reader, IndexReader[] subReaders, int[] docStarts)
{
this.reader = reader;
this.subReaders = subReaders;
this.docStarts = docStarts;
this.closeReader = false;
}
private IndexSearcher(IndexReader r, bool closeReader)
{
reader = r;
this.closeReader = closeReader;
System.Collections.Generic.IList subReadersList = new System.Collections.Generic.List();
GatherSubReaders(subReadersList, reader);
subReaders = subReadersList.ToArray();
docStarts = new int[subReaders.Length];
int maxDoc = 0;
for (int i = 0; i < subReaders.Length; i++)
{
docStarts[i] = maxDoc;
maxDoc += subReaders[i].MaxDoc;
}
}
protected internal virtual void GatherSubReaders(System.Collections.Generic.IList allSubReaders, IndexReader r)
{
ReaderUtil.GatherSubReaders(allSubReaders, r);
}
/// Return the this searches.
public virtual IndexReader IndexReader
{
get { return reader; }
}
protected override void Dispose(bool disposing)
{
if (isDisposed) return;
if (disposing)
{
if (closeReader)
reader.Close();
}
isDisposed = true;
}
// inherit javadoc
public override int DocFreq(Term term)
{
return reader.DocFreq(term);
}
// inherit javadoc
public override Document Doc(int i)
{
return reader.Document(i);
}
// inherit javadoc
public override Document Doc(int i, FieldSelector fieldSelector)
{
return reader.Document(i, fieldSelector);
}
// inherit javadoc
public override int MaxDoc
{
get { return reader.MaxDoc; }
}
// inherit javadoc
public override TopDocs Search(Weight weight, Filter filter, int nDocs)
{
if (nDocs <= 0)
{
throw new System.ArgumentException("nDocs must be > 0");
}
nDocs = Math.Min(nDocs, reader.MaxDoc);
TopScoreDocCollector collector = TopScoreDocCollector.Create(nDocs, !weight.GetScoresDocsOutOfOrder());
Search(weight, filter, collector);
return collector.TopDocs();
}
public override TopFieldDocs Search(Weight weight, Filter filter, int nDocs, Sort sort)
{
return Search(weight, filter, nDocs, sort, true);
}
/// Just like , but you choose
/// whether or not the fields in the returned instances
/// should be set by specifying fillFields.
///
/// NOTE: this does not compute scores by default. If you need scores, create
/// a instance by calling
/// and then pass that to
/// .
///
///
public virtual TopFieldDocs Search(Weight weight, Filter filter, int nDocs, Sort sort, bool fillFields)
{
nDocs = Math.Min(nDocs, reader.MaxDoc);
TopFieldCollector collector2 = TopFieldCollector.Create(sort, nDocs, fillFields, fieldSortDoTrackScores, fieldSortDoMaxScore, !weight.GetScoresDocsOutOfOrder());
Search(weight, filter, collector2);
return (TopFieldDocs) collector2.TopDocs();
}
public override void Search(Weight weight, Filter filter, Collector collector)
{
if (filter == null)
{
for (int i = 0; i < subReaders.Length; i++)
{
// search each subreader
collector.SetNextReader(subReaders[i], docStarts[i]);
Scorer scorer = weight.Scorer(subReaders[i], !collector.AcceptsDocsOutOfOrder, true);
if (scorer != null)
{
scorer.Score(collector);
}
}
}
else
{
for (int i = 0; i < subReaders.Length; i++)
{
// search each subreader
collector.SetNextReader(subReaders[i], docStarts[i]);
SearchWithFilter(subReaders[i], weight, filter, collector);
}
}
}
private void SearchWithFilter(IndexReader reader, Weight weight, Filter filter, Collector collector)
{
System.Diagnostics.Debug.Assert(filter != null);
Scorer scorer = weight.Scorer(reader, true, false);
if (scorer == null)
{
return ;
}
int docID = scorer.DocID();
System.Diagnostics.Debug.Assert(docID == - 1 || docID == DocIdSetIterator.NO_MORE_DOCS);
// CHECKME: use ConjunctionScorer here?
DocIdSet filterDocIdSet = filter.GetDocIdSet(reader);
if (filterDocIdSet == null)
{
// this means the filter does not accept any documents.
return ;
}
DocIdSetIterator filterIter = filterDocIdSet.Iterator();
if (filterIter == null)
{
// this means the filter does not accept any documents.
return ;
}
int filterDoc = filterIter.NextDoc();
int scorerDoc = scorer.Advance(filterDoc);
collector.SetScorer(scorer);
while (true)
{
if (scorerDoc == filterDoc)
{
// Check if scorer has exhausted, only before collecting.
if (scorerDoc == DocIdSetIterator.NO_MORE_DOCS)
{
break;
}
collector.Collect(scorerDoc);
filterDoc = filterIter.NextDoc();
scorerDoc = scorer.Advance(filterDoc);
}
else if (scorerDoc > filterDoc)
{
filterDoc = filterIter.Advance(scorerDoc);
}
else
{
scorerDoc = scorer.Advance(filterDoc);
}
}
}
public override Query Rewrite(Query original)
{
Query query = original;
for (Query rewrittenQuery = query.Rewrite(reader); rewrittenQuery != query; rewrittenQuery = query.Rewrite(reader))
{
query = rewrittenQuery;
}
return query;
}
public override Explanation Explain(Weight weight, int doc)
{
int n = ReaderUtil.SubIndex(doc, docStarts);
int deBasedDoc = doc - docStarts[n];
return weight.Explain(subReaders[n], deBasedDoc);
}
private bool fieldSortDoTrackScores;
private bool fieldSortDoMaxScore;
/// By default, no scores are computed when sorting by field (using
/// ). You can change that, per
/// IndexSearcher instance, by calling this method. Note that this will incur
/// a CPU cost.
///
///
/// If true, then scores are returned for every matching document
/// in .
///
///
/// If true, then the max score for all matching docs is computed.
///
public virtual void SetDefaultFieldSortScoring(bool doTrackScores, bool doMaxScore)
{
fieldSortDoTrackScores = doTrackScores;
fieldSortDoMaxScore = doMaxScore;
}
public IndexReader reader_ForNUnit
{
get { return reader; }
}
}
}