blob: b5499114be1960ff3e50039d7dc39c550e5853cd [file] [log] [blame]
using J2N.Collections.Generic.Extensions;
using System;
using System.Collections.Generic;
namespace Lucene.Net.Search
{
/*
* 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 BooleanWeight = Lucene.Net.Search.BooleanQuery.BooleanWeight;
/// <summary>
/// See the description in <see cref="BooleanScorer"/> comparing
/// <see cref="BooleanScorer"/> &amp; <see cref="BooleanScorer2"/>.
/// <para/>
/// An alternative to <see cref="BooleanScorer"/> that also allows a minimum number
/// of optional scorers that should match.
/// <para/>Implements SkipTo(), and has no limitations on the numbers of added scorers.
/// <para/>Uses <see cref="ConjunctionScorer"/>, <see cref="DisjunctionScorer"/>, <see cref="ReqOptSumScorer"/> and <see cref="ReqExclScorer"/>.
/// </summary>
internal class BooleanScorer2 : Scorer
{
private readonly IList<Scorer> requiredScorers;
private readonly IList<Scorer> optionalScorers;
private readonly IList<Scorer> prohibitedScorers;
private class Coordinator
{
internal readonly float[] coordFactors;
internal Coordinator(BooleanScorer2 outerInstance, int maxCoord, bool disableCoord)
{
coordFactors = new float[outerInstance.optionalScorers.Count + outerInstance.requiredScorers.Count + 1];
for (int i = 0; i < coordFactors.Length; i++)
{
coordFactors[i] = disableCoord ? 1.0f : ((BooleanWeight)outerInstance.m_weight).Coord(i, maxCoord);
}
}
internal int nrMatchers; // to be increased by score() of match counting scorers.
}
private readonly Coordinator coordinator;
/// <summary>
/// The scorer to which all scoring will be delegated,
/// except for computing and using the coordination factor.
/// </summary>
private readonly Scorer countingSumScorer;
/// <summary>
/// The number of optionalScorers that need to match (if there are any) </summary>
private readonly int minNrShouldMatch;
private int doc = -1;
/// <summary>
/// Creates a <see cref="Scorer"/> 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.
/// </summary>
/// <param name="weight">
/// The <see cref="BooleanWeight"/> to be used. </param>
/// <param name="disableCoord">
/// If this parameter is <c>true</c>, coordination level matching
/// (<see cref="Similarities.Similarity.Coord(int, int)"/>) is not used. </param>
/// <param name="minNrShouldMatch">
/// 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. </param>
/// <param name="required">
/// The list of required scorers. </param>
/// <param name="prohibited">
/// The list of prohibited scorers. </param>
/// <param name="optional">
/// The list of optional scorers. </param>
/// <param name="maxCoord">
/// The max coord. </param>
public BooleanScorer2(BooleanWeight weight, bool disableCoord, int minNrShouldMatch, IList<Scorer> required, IList<Scorer> prohibited, IList<Scorer> optional, int maxCoord)
: base(weight)
{
if (minNrShouldMatch < 0)
{
throw new ArgumentException("Minimum number of optional scorers should not be negative");
}
this.minNrShouldMatch = minNrShouldMatch;
optionalScorers = optional;
requiredScorers = required;
prohibitedScorers = prohibited;
coordinator = new Coordinator(this, maxCoord, disableCoord);
countingSumScorer = MakeCountingSumScorer(/* disableCoord // LUCENENET: Not referenced */);
}
/// <summary>
/// Count a scorer as a single match. </summary>
private class SingleMatchScorer : Scorer
{
private readonly BooleanScorer2 outerInstance;
internal Scorer scorer;
internal int lastScoredDoc = -1;
// Save the score of lastScoredDoc, so that we don't compute it more than
// once in score().
internal float lastDocScore = float.NaN;
internal SingleMatchScorer(BooleanScorer2 outerInstance, Scorer scorer)
: base(scorer.m_weight)
{
this.outerInstance = outerInstance;
this.scorer = scorer;
}
public override float GetScore()
{
int doc = DocID;
if (doc >= lastScoredDoc)
{
if (doc > lastScoredDoc)
{
lastDocScore = scorer.GetScore();
lastScoredDoc = doc;
}
outerInstance.coordinator.nrMatchers++;
}
return lastDocScore;
}
public override int Freq => 1;
public override int DocID => scorer.DocID;
public override int NextDoc()
{
return scorer.NextDoc();
}
public override int Advance(int target)
{
return scorer.Advance(target);
}
public override long GetCost()
{
return scorer.GetCost();
}
}
private Scorer CountingDisjunctionSumScorer(IList<Scorer> scorers, int minNrShouldMatch)
{
// each scorer from the list counted as a single matcher
if (minNrShouldMatch > 1)
{
return new MinShouldMatchSumScorerAnonymousInnerClassHelper(this, m_weight, scorers, minNrShouldMatch);
}
else
{
// we pass null for coord[] since we coordinate ourselves and override score()
return new DisjunctionSumScorerAnonymousInnerClassHelper(this, m_weight, scorers.ToArray(), null);
}
}
private class MinShouldMatchSumScorerAnonymousInnerClassHelper : MinShouldMatchSumScorer
{
private readonly BooleanScorer2 outerInstance;
public MinShouldMatchSumScorerAnonymousInnerClassHelper(BooleanScorer2 outerInstance, Weight weight, IList<Scorer> scorers, int minNrShouldMatch)
: base(weight, scorers, minNrShouldMatch)
{
this.outerInstance = outerInstance;
}
public override float GetScore()
{
outerInstance.coordinator.nrMatchers += base.m_nrMatchers;
return base.GetScore();
}
}
private class DisjunctionSumScorerAnonymousInnerClassHelper : DisjunctionSumScorer
{
private readonly BooleanScorer2 outerInstance;
public DisjunctionSumScorerAnonymousInnerClassHelper(BooleanScorer2 outerInstance, Weight weight, Scorer[] subScorers, float[] coord)
: base(weight, subScorers, coord)
{
this.outerInstance = outerInstance;
}
public override float GetScore()
{
outerInstance.coordinator.nrMatchers += base.m_nrMatchers;
return (float)base.m_score;
}
}
private Scorer CountingConjunctionSumScorer(/* bool disableCoord, // LUCENENET: Not Referenced */ IList<Scorer> requiredScorers)
{
// each scorer from the list counted as a single matcher
int requiredNrMatchers = requiredScorers.Count;
return new ConjunctionScorerAnonymousInnerClassHelper(this, m_weight, requiredScorers.ToArray(), requiredNrMatchers);
}
private class ConjunctionScorerAnonymousInnerClassHelper : ConjunctionScorer
{
private readonly BooleanScorer2 outerInstance;
private readonly int requiredNrMatchers;
public ConjunctionScorerAnonymousInnerClassHelper(BooleanScorer2 outerInstance, Weight weight, Scorer[] scorers, int requiredNrMatchers)
: base(weight, scorers)
{
this.outerInstance = outerInstance;
this.requiredNrMatchers = requiredNrMatchers;
lastScoredDoc = -1;
lastDocScore = float.NaN;
}
private int lastScoredDoc;
// Save the score of lastScoredDoc, so that we don't compute it more than
// once in score().
private float lastDocScore;
public override float GetScore()
{
int doc = outerInstance.DocID;
if (doc >= lastScoredDoc)
{
if (doc > lastScoredDoc)
{
lastDocScore = base.GetScore();
lastScoredDoc = doc;
}
outerInstance.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 Scorer DualConjunctionSumScorer(/* bool disableCoord, // LUCENENET: Not Referenced */ Scorer req1, Scorer req2) // non counting.
{
return new ConjunctionScorer(m_weight, 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.
}
/// <summary>
/// Returns the scorer to be used for match counting and score summing.
/// Uses requiredScorers, optionalScorers and prohibitedScorers.
/// </summary>
private Scorer MakeCountingSumScorer(/* bool disableCoord // LUCENENET: Not Referenced */) // each scorer counted as a single matcher
{
return (requiredScorers.Count == 0)
? MakeCountingSumScorerNoReq(/* disableCoord // LUCENENET: Not Referenced */)
: MakeCountingSumScorerSomeReq(/* disableCoord // LUCENENET: Not Referenced */);
}
private Scorer MakeCountingSumScorerNoReq(/* bool disableCoord // LUCENENET: Not Referenced */) // 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(/* disableCoord, // LUCENENET: Not Referenced */ optionalScorers);
}
return AddProhibitedScorers(requiredCountingSumScorer);
}
private Scorer MakeCountingSumScorerSomeReq(/* bool disableCoord // LUCENENET: Not Referenced */) // At least one required scorer.
{
if (optionalScorers.Count == minNrShouldMatch) // all optional scorers also required.
{
List<Scorer> allReq = new List<Scorer>(requiredScorers);
allReq.AddRange(optionalScorers);
return AddProhibitedScorers(CountingConjunctionSumScorer(/* disableCoord, // LUCENENET: Not Referenced */ allReq));
} // optionalScorers.size() > minNrShouldMatch, and at least one required scorer
else
{
Scorer requiredCountingSumScorer = requiredScorers.Count == 1 ? new SingleMatchScorer(this, requiredScorers[0]) : CountingConjunctionSumScorer(/* disableCoord, // LUCENENET: Not Referenced */ requiredScorers);
if (minNrShouldMatch > 0) // use a required disjunction scorer over the optional scorers
{
return AddProhibitedScorers(DualConjunctionSumScorer(/* disableCoord, // LUCENENET: Not Referenced */ requiredCountingSumScorer, CountingDisjunctionSumScorer(optionalScorers, minNrShouldMatch))); // non counting
} // minNrShouldMatch == 0
else
{
return new ReqOptSumScorer(AddProhibitedScorers(requiredCountingSumScorer), optionalScorers.Count == 1 ? new SingleMatchScorer(this, optionalScorers[0])
// require 1 in combined, optional scorer.
: CountingDisjunctionSumScorer(optionalScorers, 1));
}
}
}
/// <summary>
/// Returns the scorer to be used for match counting and score summing.
/// Uses the given required scorer and the prohibitedScorers. </summary>
/// <param name="requiredCountingSumScorer"> A required scorer already built. </param>
private Scorer AddProhibitedScorers(Scorer requiredCountingSumScorer)
{
return (prohibitedScorers.Count == 0) ? requiredCountingSumScorer : new ReqExclScorer(requiredCountingSumScorer, ((prohibitedScorers.Count == 1) ? prohibitedScorers[0] : new MinShouldMatchSumScorer(m_weight, prohibitedScorers))); // no prohibited
}
public override int DocID => doc;
public override int NextDoc()
{
return doc = countingSumScorer.NextDoc();
}
public override float GetScore()
{
coordinator.nrMatchers = 0;
float sum = countingSumScorer.GetScore();
return sum * coordinator.coordFactors[coordinator.nrMatchers];
}
public override int Freq => countingSumScorer.Freq;
public override int Advance(int target)
{
return doc = countingSumScorer.Advance(target);
}
public override long GetCost()
{
return countingSumScorer.GetCost();
}
public override ICollection<ChildScorer> GetChildren()
{
List<ChildScorer> children = new List<ChildScorer>();
foreach (Scorer s in optionalScorers)
{
children.Add(new ChildScorer(s, "SHOULD"));
}
foreach (Scorer s in prohibitedScorers)
{
children.Add(new ChildScorer(s, "MUST_NOT"));
}
foreach (Scorer s in requiredScorers)
{
children.Add(new ChildScorer(s, "MUST"));
}
return children;
}
}
}