/** * 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)); } } }