/*
* 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;
namespace Lucene.Net.Search
{
/* See the description in BooleanScorer.java, comparing
* BooleanScorer & BooleanScorer2 */
/// An alternative to BooleanScorer that also allows a minimum number
/// of optional scorers that should match.
///
Implements skipTo(), and has no limitations on the numbers of added scorers.
///
Uses ConjunctionScorer, DisjunctionScorer, ReqOptScorer and ReqExclScorer.
///
class BooleanScorer2 : Scorer
{
private class AnonymousClassDisjunctionSumScorer:DisjunctionSumScorer
{
private void InitBlock(BooleanScorer2 enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
}
private BooleanScorer2 enclosingInstance;
public BooleanScorer2 Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
internal AnonymousClassDisjunctionSumScorer(BooleanScorer2 enclosingInstance, System.Collections.Generic.IList scorers, int minNrShouldMatch)
: base(scorers, minNrShouldMatch)
{
InitBlock(enclosingInstance);
}
private int lastScoredDoc = - 1;
// Save the score of lastScoredDoc, so that we don't compute it more than
// once in score().
private float lastDocScore = System.Single.NaN;
public override float Score()
{
int doc = DocID();
if (doc >= lastScoredDoc)
{
if (doc > lastScoredDoc)
{
lastDocScore = base.Score();
lastScoredDoc = doc;
}
Enclosing_Instance.coordinator.nrMatchers += base.nrMatchers;
}
return lastDocScore;
}
}
private class AnonymousClassConjunctionScorer:ConjunctionScorer
{
private void InitBlock(int requiredNrMatchers, BooleanScorer2 enclosingInstance)
{
this.requiredNrMatchers = requiredNrMatchers;
this.enclosingInstance = enclosingInstance;
}
private int requiredNrMatchers;
private BooleanScorer2 enclosingInstance;
public BooleanScorer2 Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
internal AnonymousClassConjunctionScorer(int requiredNrMatchers, BooleanScorer2 enclosingInstance, Lucene.Net.Search.Similarity defaultSimilarity, System.Collections.Generic.IList requiredScorers)
: base(defaultSimilarity, requiredScorers)
{
InitBlock(requiredNrMatchers, enclosingInstance);
}
private int lastScoredDoc = - 1;
// Save the score of lastScoredDoc, so that we don't compute it more than
// once in score().
private float lastDocScore = System.Single.NaN;
public override float Score()
{
int doc = DocID();
if (doc >= lastScoredDoc)
{
if (doc > lastScoredDoc)
{
lastDocScore = base.Score();
lastScoredDoc = doc;
}
Enclosing_Instance.coordinator.nrMatchers += requiredNrMatchers;
}
// All scorers match, so defaultSimilarity super.score() always has 1 as
// the coordination factor.
// Therefore the sum of the scores of the requiredScorers
// is used as score.
return lastDocScore;
}
}
private System.Collections.Generic.List requiredScorers;
private System.Collections.Generic.List optionalScorers;
private System.Collections.Generic.List prohibitedScorers;
private class Coordinator
{
public Coordinator(BooleanScorer2 enclosingInstance)
{
InitBlock(enclosingInstance);
}
private void InitBlock(BooleanScorer2 enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
}
private BooleanScorer2 enclosingInstance;
public BooleanScorer2 Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
internal float[] coordFactors = null;
internal int maxCoord = 0; // to be increased for each non prohibited scorer
internal int nrMatchers; // to be increased by score() of match counting scorers.
internal virtual void Init()
{
// use after all scorers have been added.
coordFactors = new float[maxCoord + 1];
Similarity sim = Enclosing_Instance.Similarity;
for (int i = 0; i <= maxCoord; i++)
{
coordFactors[i] = sim.Coord(i, maxCoord);
}
}
}
private Coordinator coordinator;
/// The scorer to which all scoring will be delegated,
/// except for computing and using the coordination factor.
///
private Scorer countingSumScorer;
/// The number of optionalScorers that need to match (if there are any)
private int minNrShouldMatch;
private int doc = - 1;
/// Creates a with the given similarity and lists of required,
/// prohibited and optional scorers. In no required scorers are added, at least
/// one of the optional scorers will have to match during the search.
///
///
/// The similarity to be used.
///
/// The minimum number of optional added scorers that should match
/// during the search. In case no required scorers are added, at least
/// one of the optional scorers will have to match during the search.
///
/// the list of required scorers.
///
/// the list of prohibited scorers.
///
/// the list of optional scorers.
///
public BooleanScorer2(Similarity similarity, int minNrShouldMatch,
System.Collections.Generic.List required,
System.Collections.Generic.List prohibited,
System.Collections.Generic.List optional)
: base(similarity)
{
if (minNrShouldMatch < 0)
{
throw new System.ArgumentException("Minimum number of optional scorers should not be negative");
}
coordinator = new Coordinator(this);
this.minNrShouldMatch = minNrShouldMatch;
optionalScorers = optional;
coordinator.maxCoord += optional.Count;
requiredScorers = required;
coordinator.maxCoord += required.Count;
prohibitedScorers = prohibited;
coordinator.Init();
countingSumScorer = MakeCountingSumScorer();
}
/// Count a scorer as a single match.
private class SingleMatchScorer:Scorer
{
private void InitBlock(BooleanScorer2 enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
}
private BooleanScorer2 enclosingInstance;
public BooleanScorer2 Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
private Scorer scorer;
private int lastScoredDoc = - 1;
// Save the score of lastScoredDoc, so that we don't compute it more than
// once in score().
private float lastDocScore = System.Single.NaN;
internal SingleMatchScorer(BooleanScorer2 enclosingInstance, Scorer scorer):base(scorer.Similarity)
{
InitBlock(enclosingInstance);
this.scorer = scorer;
}
public override float Score()
{
int doc = DocID();
if (doc >= lastScoredDoc)
{
if (doc > lastScoredDoc)
{
lastDocScore = scorer.Score();
lastScoredDoc = doc;
}
Enclosing_Instance.coordinator.nrMatchers++;
}
return lastDocScore;
}
public override int DocID()
{
return scorer.DocID();
}
public override int NextDoc()
{
return scorer.NextDoc();
}
public override int Advance(int target)
{
return scorer.Advance(target);
}
}
private Scorer CountingDisjunctionSumScorer(System.Collections.Generic.List scorers, int minNrShouldMatch)
{
// each scorer from the list counted as a single matcher
return new AnonymousClassDisjunctionSumScorer(this, scorers, minNrShouldMatch);
}
private static readonly Similarity defaultSimilarity;
private Scorer CountingConjunctionSumScorer(System.Collections.Generic.List requiredScorers)
{
// each scorer from the list counted as a single matcher
int requiredNrMatchers = requiredScorers.Count;
return new AnonymousClassConjunctionScorer(requiredNrMatchers, this, defaultSimilarity, requiredScorers);
}
private Scorer DualConjunctionSumScorer(Scorer req1, Scorer req2)
{
// non counting.
return new ConjunctionScorer(defaultSimilarity, new Scorer[]{req1, req2});
// All scorers match, so defaultSimilarity always has 1 as
// the coordination factor.
// Therefore the sum of the scores of two scorers
// is used as score.
}
/// Returns the scorer to be used for match counting and score summing.
/// Uses requiredScorers, optionalScorers and prohibitedScorers.
///
private Scorer MakeCountingSumScorer()
{
// each scorer counted as a single matcher
return (requiredScorers.Count == 0)?MakeCountingSumScorerNoReq():MakeCountingSumScorerSomeReq();
}
private Scorer MakeCountingSumScorerNoReq()
{
// No required scorers
// minNrShouldMatch optional scorers are required, but at least 1
int nrOptRequired = (minNrShouldMatch < 1)?1:minNrShouldMatch;
Scorer requiredCountingSumScorer;
if (optionalScorers.Count > nrOptRequired)
requiredCountingSumScorer = CountingDisjunctionSumScorer(optionalScorers, nrOptRequired);
else if (optionalScorers.Count == 1)
requiredCountingSumScorer = new SingleMatchScorer(this, optionalScorers[0]);
else
requiredCountingSumScorer = CountingConjunctionSumScorer(optionalScorers);
return AddProhibitedScorers(requiredCountingSumScorer);
}
private Scorer MakeCountingSumScorerSomeReq()
{
// At least one required scorer.
if (optionalScorers.Count == minNrShouldMatch)
{
// all optional scorers also required.
var allReq = new System.Collections.Generic.List(requiredScorers);
allReq.AddRange(optionalScorers);
return AddProhibitedScorers(CountingConjunctionSumScorer(allReq));
}
else
{
// optionalScorers.size() > minNrShouldMatch, and at least one required scorer
Scorer requiredCountingSumScorer =
requiredScorers.Count == 1
? new SingleMatchScorer(this, requiredScorers[0])
: CountingConjunctionSumScorer(requiredScorers);
if (minNrShouldMatch > 0)
{
// use a required disjunction scorer over the optional scorers
return AddProhibitedScorers(DualConjunctionSumScorer(requiredCountingSumScorer, CountingDisjunctionSumScorer(optionalScorers, minNrShouldMatch)));
}
else
{
// minNrShouldMatch == 0
return new ReqOptSumScorer(AddProhibitedScorers(requiredCountingSumScorer),
optionalScorers.Count == 1
? new SingleMatchScorer(this, optionalScorers[0])
: CountingDisjunctionSumScorer(optionalScorers, 1));
}
}
}
/// Returns the scorer to be used for match counting and score summing.
/// Uses the given required scorer and the prohibitedScorers.
///
/// A required scorer already built.
///
private Scorer AddProhibitedScorers(Scorer requiredCountingSumScorer)
{
return (prohibitedScorers.Count == 0)
? requiredCountingSumScorer
: new ReqExclScorer(requiredCountingSumScorer,
((prohibitedScorers.Count == 1)
? prohibitedScorers[0]
: new DisjunctionSumScorer(prohibitedScorers)));
}
/// 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 ((doc = countingSumScorer.NextDoc()) != NO_MORE_DOCS)
{
collector.Collect(doc);
}
}
public /*protected internal*/ override bool Score(Collector collector, int max, int firstDocID)
{
doc = firstDocID;
collector.SetScorer(this);
while (doc < max)
{
collector.Collect(doc);
doc = countingSumScorer.NextDoc();
}
return doc != NO_MORE_DOCS;
}
public override int DocID()
{
return doc;
}
public override int NextDoc()
{
return doc = countingSumScorer.NextDoc();
}
public override float Score()
{
coordinator.nrMatchers = 0;
float sum = countingSumScorer.Score();
return sum * coordinator.coordFactors[coordinator.nrMatchers];
}
public override int Advance(int target)
{
return doc = countingSumScorer.Advance(target);
}
static BooleanScorer2()
{
defaultSimilarity = Search.Similarity.Default;
}
}
}