/*
* Copyright 2005 The Apache Software Foundation
*
* Licensed 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{
sealed class BooleanScorer : Scorer
{
private void InitBlock()
{
bucketTable = new BucketTable(this);
}
private SubScorer scorers = null;
private BucketTable bucketTable;
private int maxCoord = 1;
private float[] coordFactors = null;
private int requiredMask = 0;
private int prohibitedMask = 0;
private int nextMask = 1;
internal BooleanScorer(Similarity similarity) : base(similarity)
{
InitBlock();
}
internal sealed class SubScorer
{
public Scorer scorer;
public bool done;
public bool required = false;
public bool prohibited = false;
public HitCollector collector;
public SubScorer next;
public SubScorer(Scorer scorer, bool required, bool prohibited, HitCollector collector, SubScorer next)
{
this.scorer = scorer;
this.done = !scorer.Next();
this.required = required;
this.prohibited = prohibited;
this.collector = collector;
this.next = next;
}
}
internal void Add(Scorer scorer, bool required, bool prohibited)
{
int mask = 0;
if (required || prohibited)
{
if (nextMask == 0)
throw new System.IndexOutOfRangeException("More than 32 required/prohibited clauses in query.");
mask = nextMask;
nextMask = nextMask << 1;
}
else
mask = 0;
if (!prohibited)
maxCoord++;
if (prohibited)
prohibitedMask |= mask;
// update prohibited mask
else if (required)
requiredMask |= mask; // update required mask
scorers = new SubScorer(scorer, required, prohibited, bucketTable.NewCollector(mask), scorers);
}
private void ComputeCoordFactors()
{
coordFactors = new float[maxCoord];
for (int i = 0; i < maxCoord; i++)
coordFactors[i] = GetSimilarity().Coord(i, maxCoord - 1);
}
private int end;
private Bucket current;
public override void Score(HitCollector hc)
{
Next();
Score(hc, System.Int32.MaxValue);
}
protected internal override bool Score(HitCollector hc, int max)
{
if (coordFactors == null)
ComputeCoordFactors();
bool more;
Bucket tmp;
do
{
bucketTable.first = null;
while (current != null)
{
// more queued
// check prohibited & required
if ((current.bits & prohibitedMask) == 0 && (current.bits & requiredMask) == requiredMask)
{
if (current.doc >= max)
{
tmp = current;
current = current.next;
tmp.next = bucketTable.first;
bucketTable.first = tmp;
continue;
}
hc.Collect(current.doc, current.score * coordFactors[current.coord]);
}
current = current.next; // pop the queue
}
if (bucketTable.first != null)
{
current = bucketTable.first;
bucketTable.first = current.next;
return true;
}
// refill the queue
more = false;
end += BucketTable.SIZE;
for (SubScorer sub = scorers; sub != null; sub = sub.next)
{
if (!sub.done)
{
sub.done = !sub.scorer.Score(sub.collector, end);
if (!sub.done)
more = true;
}
}
current = bucketTable.first;
}
while (current != null || more);
return false;
}
public override int Doc()
{
return current.doc;
}
public override bool Next()
{
bool more;
do
{
while (bucketTable.first != null)
{
// more queued
current = bucketTable.first;
bucketTable.first = current.next; // pop the queue
// check prohibited & required
if ((current.bits & prohibitedMask) == 0 && (current.bits & requiredMask) == requiredMask)
{
return true;
}
}
// refill the queue
more = false;
end += BucketTable.SIZE;
for (SubScorer sub = scorers; sub != null; sub = sub.next)
{
Scorer scorer = sub.scorer;
while (!sub.done && scorer.Doc() < end)
{
sub.collector.Collect(scorer.Doc(), scorer.Score());
sub.done = !scorer.Next();
}
if (!sub.done)
{
more = true;
}
}
}
while (bucketTable.first != null || more);
return false;
}
public override float Score()
{
if (coordFactors == null)
ComputeCoordFactors();
return current.score * coordFactors[current.coord];
}
internal sealed class Bucket
{
internal int doc = - 1; // tells if bucket is valid
internal float score; // incremental score
internal int bits; // used for bool constraints
internal int coord; // count of terms in score
internal Bucket next; // next valid bucket
}
/// <summary>A simple hash table of document scores within a range. </summary>
internal sealed class BucketTable
{
private void InitBlock()
{
buckets = new Bucket[SIZE];
}
public const int SIZE = 1 << 11;
public static readonly int MASK;
internal Bucket[] buckets;
internal Bucket first = null; // head of valid list
private BooleanScorer scorer;
public BucketTable(BooleanScorer scorer)
{
this.scorer = scorer;
}
public int Size()
{
return SIZE;
}
public HitCollector NewCollector(int mask)
{
return new Collector(mask, this);
}
static BucketTable()
{
MASK = SIZE - 1;
}
}
internal sealed class Collector : HitCollector
{
private BucketTable bucketTable;
private int mask;
public Collector(int mask, BucketTable bucketTable)
{
this.mask = mask;
this.bucketTable = bucketTable;
}
public override void Collect(int doc, float score)
{
BucketTable table = bucketTable;
int i = doc & Lucene.Net.Search.BooleanScorer.BucketTable.MASK;
Bucket bucket = table.buckets[i];
if (bucket == null)
table.buckets[i] = bucket = new Bucket();
if (bucket.doc != doc)
{
// invalid bucket
bucket.doc = doc; // set doc
bucket.score = score; // initialize score
bucket.bits = mask; // initialize mask
bucket.coord = 1; // initialize coord
bucket.next = table.first; // push onto valid list
table.first = bucket;
}
else
{
// valid bucket
bucket.score += score; // increment score
bucket.bits |= mask; // add bits in mask
bucket.coord++; // increment coord
}
}
}
public override bool SkipTo(int target)
{
throw new System.NotSupportedException();
}
public override Explanation Explain(int doc)
{
throw new System.NotSupportedException();
}
public override System.String ToString()
{
System.Text.StringBuilder buffer = new System.Text.StringBuilder();
buffer.Append("boolean(");
for (SubScorer sub = scorers; sub != null; sub = sub.next)
{
buffer.Append(sub.scorer.ToString());
buffer.Append(" ");
}
buffer.Append(")");
return buffer.ToString();
}
}
}
|