/* * Copyright 2004 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 } /// A simple hash table of document scores within a range. 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(); } } }