/*
* 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 System.Linq;
namespace Lucene.Net.Search
{
/// Scorer for conjunctions, sets of queries, all of which are required.
class ConjunctionScorer:Scorer
{
private Scorer[] scorers;
private float coord;
private int lastDoc = - 1;
public ConjunctionScorer(Similarity similarity, System.Collections.Generic.ICollection scorers)
: this(similarity, scorers.ToArray())
{
}
public ConjunctionScorer(Similarity similarity, params Scorer[] scorers):base(similarity)
{
this.scorers = scorers;
coord = similarity.Coord(scorers.Length, scorers.Length);
for (int i = 0; i < scorers.Length; i++)
{
if (scorers[i].NextDoc() == NO_MORE_DOCS)
{
// If even one of the sub-scorers does not have any documents, this
// scorer should not attempt to do any more work.
lastDoc = NO_MORE_DOCS;
return ;
}
}
// Sort the array the first time...
// We don't need to sort the array in any future calls because we know
// it will already start off sorted (all scorers on same doc).
// note that this comparator is not consistent with equals!
System.Array.Sort(scorers, (a, b) => a.DocID() - b.DocID());
// NOTE: doNext() must be called before the re-sorting of the array later on.
// The reason is this: assume there are 5 scorers, whose first docs are 1,
// 2, 3, 5, 5 respectively. Sorting (above) leaves the array as is. Calling
// doNext() here advances all the first scorers to 5 (or a larger doc ID
// they all agree on).
// However, if we re-sort before doNext() is called, the order will be 5, 3,
// 2, 1, 5 and then doNext() will stop immediately, since the first scorer's
// docs equals the last one. So the invariant that after calling doNext()
// all scorers are on the same doc ID is broken.);
if (DoNext() == NO_MORE_DOCS)
{
// The scorers did not agree on any document.
lastDoc = NO_MORE_DOCS;
return ;
}
// If first-time skip distance is any predictor of
// scorer sparseness, then we should always try to skip first on
// those scorers.
// Keep last scorer in it's last place (it will be the first
// to be skipped on), but reverse all of the others so that
// they will be skipped on in order of original high skip.
int end = scorers.Length - 1;
int max = end >> 1;
for (int i = 0; i < max; i++)
{
Scorer tmp = scorers[i];
int idx = end - i - 1;
scorers[i] = scorers[idx];
scorers[idx] = tmp;
}
}
private int DoNext()
{
int first = 0;
int doc = scorers[scorers.Length - 1].DocID();
Scorer firstScorer;
while ((firstScorer = scorers[first]).DocID() < doc)
{
doc = firstScorer.Advance(doc);
first = first == scorers.Length - 1?0:first + 1;
}
return doc;
}
public override int Advance(int target)
{
if (lastDoc == NO_MORE_DOCS)
{
return lastDoc;
}
else if (scorers[(scorers.Length - 1)].DocID() < target)
{
scorers[(scorers.Length - 1)].Advance(target);
}
return lastDoc = DoNext();
}
public override int DocID()
{
return lastDoc;
}
public override int NextDoc()
{
if (lastDoc == NO_MORE_DOCS)
{
return lastDoc;
}
else if (lastDoc == - 1)
{
return lastDoc = scorers[scorers.Length - 1].DocID();
}
scorers[(scorers.Length - 1)].NextDoc();
return lastDoc = DoNext();
}
public override float Score()
{
float sum = 0.0f;
for (int i = 0; i < scorers.Length; i++)
{
sum += scorers[i].Score();
}
return sum * coord;
}
}
}