/**
* 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 Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Search.Function;
namespace Lucene.Net.Spatial.Util
{
///
/// Port of Solr's FunctionQuery (v1.4)
///
/// Returns a score for each document based on a ValueSource,
/// often some function of the value of a field.
///
/// Note: This API is experimental and may change in non backward-compatible ways in the future
///
public class FunctionQuery : Query
{
protected readonly ValueSource func;
public FunctionQuery(ValueSource func)
{
this.func = func;
}
///
///
///
/// The associated ValueSource
public ValueSource GetValueSource()
{
return func;
}
public override Query Rewrite(Index.IndexReader reader)
{
return this;
}
public override void ExtractTerms(System.Collections.Generic.ISet terms)
{
//base.ExtractTerms(terms);
}
protected class FunctionWeight : Weight
{
protected Searcher searcher;
protected float queryNorm;
protected float queryWeight;
protected readonly FunctionQuery enclosingInstance;
public FunctionWeight(Searcher searcher, FunctionQuery q)
{
enclosingInstance = q;
this.searcher = searcher;
//q.func.CreateWeight(searcher);
}
internal float GetQueryNorm()
{
return queryNorm;
}
public override Query Query
{
get { return enclosingInstance; }
}
public override float Value
{
get { return queryWeight; }
}
public override float GetSumOfSquaredWeights()
{
queryWeight = enclosingInstance.Boost;
return queryWeight * queryWeight;
}
public override void Normalize(float norm)
{
this.queryNorm = norm;
queryWeight *= this.queryNorm;
}
public override Scorer Scorer(IndexReader reader, bool scoreDocsInOrder, bool topScorer)
{
return new AllScorer(enclosingInstance.GetSimilarity(searcher), reader, this);
}
public override Explanation Explain(IndexReader reader, int doc)
{
return ((AllScorer)Scorer(reader, true, true)).Explain(doc);
}
}
protected class AllScorer : Scorer
{
readonly IndexReader reader;
readonly FunctionWeight weight;
readonly int maxDoc;
readonly float qWeight;
int doc = -1;
readonly DocValues vals;
readonly bool hasDeletions;
public AllScorer(Similarity similarity, IndexReader reader, FunctionWeight w)
: base(similarity)
{
this.weight = w;
this.qWeight = w.Value;
this.reader = reader;
this.maxDoc = reader.MaxDoc;
this.hasDeletions = reader.HasDeletions;
vals = ((FunctionQuery)w.Query).func.GetValues(reader);
}
public override int DocID()
{
return doc;
}
// instead of matching all docs, we could also embed a query.
// the score could either ignore the subscore, or boost it.
// Containment: floatline(foo:myTerm, "myFloatField", 1.0, 0.0f)
// Boost: foo:myTerm^floatline("myFloatField",1.0,0.0f)
public override int NextDoc()
{
for (; ; )
{
++doc;
if (doc >= maxDoc)
{
return doc = NO_MORE_DOCS;
}
if (hasDeletions && reader.IsDeleted(doc)) continue;
return doc;
}
}
public override int Advance(int target)
{
// this will work even if target==NO_MORE_DOCS
doc = target - 1;
return NextDoc();
}
public override float Score()
{
float score = qWeight * vals.FloatVal(doc);
// Current Lucene priority queues can't handle NaN and -Infinity, so
// map to -Float.MAX_VALUE. This conditional handles both -infinity
// and NaN since comparisons with NaN are always false.
return score > float.NegativeInfinity ? score : -float.MaxValue;
}
public /*override*/ Explanation Explain(int doc)
{
float sc = qWeight * vals.FloatVal(doc);
Explanation result = new ComplexExplanation
(true, sc, "FunctionQuery(" + ((FunctionQuery)weight.Query).func + "), product of:");
result.AddDetail(vals.Explain(doc));
result.AddDetail(new Explanation(weight.Query.Boost, "boost"));
result.AddDetail(new Explanation(weight.GetQueryNorm(), "queryNorm"));
return result;
}
}
public override Weight CreateWeight(Searcher searcher)
{
return new FunctionQuery.FunctionWeight(searcher, this);
}
public override string ToString(string field)
{
float boost = Boost;
return (boost != 1.0 ? "(" : "") + func.ToString()
+ (boost == 1.0 ? "" : ")^" + boost);
}
public override bool Equals(object o)
{
var other = o as FunctionQuery;
if (other == null) return false;
return this.Boost == other.Boost && this.func.Equals(other.func);
}
public override int GetHashCode()
{
return (int) (func.GetHashCode() * 31 + BitConverter.DoubleToInt64Bits(Boost));
}
}
}