/* * 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 ScorerDocQueue = Lucene.Net.Util.ScorerDocQueue; namespace Lucene.Net.Search { /// A Scorer for OR like queries, counterpart of ConjunctionScorer. /// This Scorer implements and uses skipTo() on the given Scorers. /// class DisjunctionSumScorer:Scorer { /// The number of subscorers. private int nrScorers; /// The subscorers. protected internal System.Collections.Generic.IList subScorers; /// The minimum number of scorers that should match. private int minimumNrMatchers; /// The scorerDocQueue contains all subscorers ordered by their current doc(), /// with the minimum at the top. ///
The scorerDocQueue is initialized the first time next() or skipTo() is called. ///
An exhausted scorer is immediately removed from the scorerDocQueue. ///
If less than the minimumNrMatchers scorers /// remain in the scorerDocQueue next() and skipTo() return false. ///

/// After each to call to next() or skipTo() /// currentSumScore is the total score of the current matching doc, /// nrMatchers is the number of matching scorers, /// and all scorers are after the matching doc, or are exhausted. ///

private ScorerDocQueue scorerDocQueue; /// The document number of the current match. private int currentDoc = - 1; /// The number of subscorers that provide the current match. protected internal int nrMatchers = - 1; private float currentScore = System.Single.NaN; /// Construct a DisjunctionScorer. /// A collection of at least two subscorers. /// /// The positive minimum number of subscorers that should /// match to match this query. ///
When minimumNrMatchers is bigger than /// the number of subScorers, /// no matches will be produced. ///
When minimumNrMatchers equals the number of subScorers, /// it more efficient to use ConjunctionScorer. /// public DisjunctionSumScorer(System.Collections.Generic.IList subScorers, int minimumNrMatchers):base(null) { nrScorers = subScorers.Count; if (minimumNrMatchers <= 0) { throw new System.ArgumentException("Minimum nr of matchers must be positive"); } if (nrScorers <= 1) { throw new System.ArgumentException("There must be at least 2 subScorers"); } this.minimumNrMatchers = minimumNrMatchers; this.subScorers = subScorers; InitScorerDocQueue(); } /// Construct a DisjunctionScorer, using one as the minimum number /// of matching subscorers. /// public DisjunctionSumScorer(System.Collections.Generic.IList subScorers) : this(subScorers, 1) { } /// Called the first time next() or skipTo() is called to /// initialize scorerDocQueue. /// private void InitScorerDocQueue() { scorerDocQueue = new ScorerDocQueue(nrScorers); foreach(Scorer se in subScorers) { if (se.NextDoc() != NO_MORE_DOCS) { // doc() method will be used in scorerDocQueue. scorerDocQueue.Insert(se); } } } /// Scores and collects all matching documents. /// The collector to which all matching documents are passed through. public override void Score(Collector collector) { collector.SetScorer(this); while (NextDoc() != NO_MORE_DOCS) { collector.Collect(currentDoc); } } /// Expert: Collects matching documents in a range. Hook for optimization. /// Note that must be called once before this method is called /// for the first time. /// /// The collector to which all matching documents are passed through. /// /// Do not score documents past this. /// /// /// true if more matching documents may remain. /// public /*protected internal*/ override bool Score(Collector collector, int max, int firstDocID) { // firstDocID is ignored since nextDoc() sets 'currentDoc' collector.SetScorer(this); while (currentDoc < max) { collector.Collect(currentDoc); if (NextDoc() == NO_MORE_DOCS) { return false; } } return true; } public override int NextDoc() { if (scorerDocQueue.Size() < minimumNrMatchers || !AdvanceAfterCurrent()) { currentDoc = NO_MORE_DOCS; } return currentDoc; } /// Advance all subscorers after the current document determined by the /// top of the scorerDocQueue. /// Repeat until at least the minimum number of subscorers match on the same /// document and all subscorers are after that document or are exhausted. ///
On entry the scorerDocQueue has at least minimumNrMatchers /// available. At least the scorer with the minimum document number will be advanced. ///
/// true iff there is a match. ///
In case there is a match, currentDoc, currentSumScore, /// and nrMatchers describe the match. /// /// TODO: Investigate whether it is possible to use skipTo() when /// the minimum number of matchers is bigger than one, ie. try and use the /// character of ConjunctionScorer for the minimum number of matchers. /// Also delay calling score() on the sub scorers until the minimum number of /// matchers is reached. ///
For this, a Scorer array with minimumNrMatchers elements might /// hold Scorers at currentDoc that are temporarily popped from scorerQueue. ///
protected internal virtual bool AdvanceAfterCurrent() { do { // repeat until minimum nr of matchers currentDoc = scorerDocQueue.TopDoc(); currentScore = scorerDocQueue.TopScore(); nrMatchers = 1; do { // Until all subscorers are after currentDoc if (!scorerDocQueue.TopNextAndAdjustElsePop()) { if (scorerDocQueue.Size() == 0) { break; // nothing more to advance, check for last match. } } if (scorerDocQueue.TopDoc() != currentDoc) { break; // All remaining subscorers are after currentDoc. } currentScore += scorerDocQueue.TopScore(); nrMatchers++; } while (true); if (nrMatchers >= minimumNrMatchers) { return true; } else if (scorerDocQueue.Size() < minimumNrMatchers) { return false; } } while (true); } /// Returns the score of the current document matching the query. /// Initially invalid, until is called the first time. /// public override float Score() { return currentScore; } public override int DocID() { return currentDoc; } /// Returns the number of subscorers matching the current document. /// Initially invalid, until is called the first time. /// public virtual int NrMatchers() { return nrMatchers; } /// Advances to the first match beyond the current whose document number is /// greater than or equal to a given target.
/// The implementation uses the skipTo() method on the subscorers. /// ///
/// The target document number. /// /// the document whose number is greater than or equal to the given /// target, or -1 if none exist. /// public override int Advance(int target) { if (scorerDocQueue.Size() < minimumNrMatchers) { return currentDoc = NO_MORE_DOCS; } if (target <= currentDoc) { return currentDoc; } do { if (scorerDocQueue.TopDoc() >= target) { return AdvanceAfterCurrent()?currentDoc:(currentDoc = NO_MORE_DOCS); } else if (!scorerDocQueue.TopSkipToAndAdjustElsePop(target)) { if (scorerDocQueue.Size() < minimumNrMatchers) { return currentDoc = NO_MORE_DOCS; } } } while (true); } } }