blob: 0e37451025358d95449177116031d2efed46d808 [file] [log] [blame]
// Lucene version compatibility level 4.8.1
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Util;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace Lucene.Net.Queries.Function
{
/*
* 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.
*/
/// <summary>
/// Returns a score for each document based on a <see cref="Function.ValueSource"/>,
/// often some function of the value of a field.
///
/// <b>Note: This API is experimental and may change in non backward-compatible ways in the future</b>
/// </summary>
public class FunctionQuery : Query
{
private readonly ValueSource func;
/// <param name="func"> defines the function to be used for scoring </param>
public FunctionQuery(ValueSource func)
{
this.func = func;
}
/// <returns> The associated <see cref="Function.ValueSource"/> </returns>
public virtual ValueSource ValueSource => func;
public override Query Rewrite(IndexReader reader)
{
return this;
}
public override void ExtractTerms(ISet<Term> terms)
{
}
protected internal class FunctionWeight : Weight
{
private readonly FunctionQuery outerInstance;
protected readonly IndexSearcher m_searcher;
protected internal float m_queryNorm;
protected float m_queryWeight;
protected internal readonly IDictionary m_context;
public FunctionWeight(FunctionQuery outerInstance, IndexSearcher searcher)
{
this.outerInstance = outerInstance;
this.m_searcher = searcher;
this.m_context = ValueSource.NewContext(searcher);
outerInstance.func.CreateWeight(m_context, searcher);
}
public override Query Query => outerInstance;
public override float GetValueForNormalization()
{
m_queryWeight = outerInstance.Boost;
return m_queryWeight * m_queryWeight;
}
public override void Normalize(float norm, float topLevelBoost)
{
this.m_queryNorm = norm * topLevelBoost;
m_queryWeight *= this.m_queryNorm;
}
public override Scorer GetScorer(AtomicReaderContext ctx, IBits acceptDocs)
{
return new AllScorer(outerInstance, ctx, acceptDocs, this, m_queryWeight);
}
public override Explanation Explain(AtomicReaderContext ctx, int doc)
{
return ((AllScorer)GetScorer(ctx, ctx.AtomicReader.LiveDocs)).Explain(doc);
}
}
protected class AllScorer : Scorer
{
private readonly FunctionQuery outerInstance;
private readonly IndexReader reader;
private readonly FunctionWeight weight;
private readonly int maxDoc;
private readonly float qWeight;
private int doc = -1;
private readonly FunctionValues vals;
private readonly IBits acceptDocs;
/// <exception cref="IOException"/>
public AllScorer(FunctionQuery outerInstance, AtomicReaderContext context, IBits acceptDocs, FunctionWeight w, float qWeight)
: base(w)
{
this.outerInstance = outerInstance;
this.weight = w;
this.qWeight = qWeight;
this.reader = context.Reader;
this.maxDoc = reader.MaxDoc;
this.acceptDocs = acceptDocs;
vals = outerInstance.func.GetValues(weight.m_context, context);
}
public override int DocID => 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 (acceptDocs != null && !acceptDocs.Get(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 GetScore()
{
float score = qWeight * vals.SingleVal(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 long GetCost()
{
return maxDoc;
}
public override int Freq => 1;
public virtual Explanation Explain(int d)
{
float sc = qWeight * vals.SingleVal(d);
Explanation result = new ComplexExplanation(true, sc, "FunctionQuery(" + outerInstance.func + "), product of:");
result.AddDetail(vals.Explain(d));
result.AddDetail(new Explanation(outerInstance.Boost, "boost"));
result.AddDetail(new Explanation(weight.m_queryNorm, "queryNorm"));
return result;
}
}
public override Weight CreateWeight(IndexSearcher searcher)
{
return new FunctionQuery.FunctionWeight(this, searcher);
}
/// <summary>
/// Prints a user-readable version of this query.
/// </summary>
public override string ToString(string field)
{
float boost = Boost;
return (boost != 1.0 ? "(" : "") + func + (boost == 1.0 ? "" : ")^" + boost);
}
/// <summary>
/// Returns true if <paramref name="o"/> is equal to this.
/// </summary>
public override bool Equals(object o)
{
if (o is null) return false;
if (!(o is FunctionQuery other)) return false;
return Boost == other.Boost
&& func.Equals(other.func);
}
/// <summary>
/// Returns a hash code value for this object.
/// </summary>
public override int GetHashCode()
{
return func.GetHashCode() * 31 + J2N.BitConversion.SingleToInt32Bits(Boost);
}
}
}