/* * 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; using Lucene.Net.Index; using IndexReader = Lucene.Net.Index.IndexReader; using ToStringUtils = Lucene.Net.Util.ToStringUtils; using ComplexExplanation = Lucene.Net.Search.ComplexExplanation; using Explanation = Lucene.Net.Search.Explanation; using Query = Lucene.Net.Search.Query; using Scorer = Lucene.Net.Search.Scorer; using Searcher = Lucene.Net.Search.Searcher; using Similarity = Lucene.Net.Search.Similarity; using Weight = Lucene.Net.Search.Weight; namespace Lucene.Net.Search.Function { /// Query that sets document score as a programmatic function of several (sub) scores: /// /// the score of its subQuery (any query) /// (optional) the score of its ValueSourceQuery (or queries). /// For most simple/convenient use cases this query is likely to be a /// FieldScoreQuery /// /// Subclasses can modify the computation by overriding . /// ///

/// WARNING: The status of the Search.Function package is experimental. /// The APIs introduced here might change in the future and will not be /// supported anymore in such a case. ///

[Serializable] public class CustomScoreQuery:Query, System.ICloneable { private Query subQuery; private ValueSourceQuery[] valSrcQueries; // never null (empty array if there are no valSrcQueries). private bool strict = false; // if true, valueSource part of query does not take part in weights normalization. /// Create a CustomScoreQuery over input subQuery. /// the sub query whose scored is being customed. Must not be null. /// public CustomScoreQuery(Query subQuery):this(subQuery, new ValueSourceQuery[0]) { } /// Create a CustomScoreQuery over input subQuery and a . /// the sub query whose score is being customed. Must not be null. /// /// a value source query whose scores are used in the custom score /// computation. For most simple/convineient use case this would be a /// FieldScoreQuery. /// This parameter is optional - it can be null or even an empty array. /// public CustomScoreQuery(Query subQuery, ValueSourceQuery valSrcQuery):this(subQuery, valSrcQuery != null?new ValueSourceQuery[]{valSrcQuery}:new ValueSourceQuery[0]) { } /// Create a CustomScoreQuery over input subQuery and a . /// the sub query whose score is being customized. Must not be null. /// /// value source queries whose scores are used in the custom score /// computation. For most simple/convenient use case these would be /// FieldScoreQueries. /// This parameter is optional - it can be null or even an empty array. /// public CustomScoreQuery(Query subQuery, params ValueSourceQuery[] valSrcQueries) { this.subQuery = subQuery; this.valSrcQueries = valSrcQueries != null?valSrcQueries:new ValueSourceQuery[0]; if (subQuery == null) throw new System.ArgumentException(" must not be null!"); } /*(non-Javadoc) terms) { subQuery.ExtractTerms(terms); for (int i = 0; i < valSrcQueries.Length; i++) { valSrcQueries[i].ExtractTerms(terms); } } /*(non-Javadoc) Returns true if o is equal to this. public override bool Equals(System.Object o) { if (GetType() != o.GetType()) { return false; } CustomScoreQuery other = (CustomScoreQuery) o; if (this.Boost != other.Boost || !this.subQuery.Equals(other.subQuery) || this.strict != other.strict || this.valSrcQueries.Length != other.valSrcQueries.Length) { return false; } // SequenceEqual should properly mimic java's Array.equals() return valSrcQueries.SequenceEqual(other.valSrcQueries); } /// Returns a hash code value for this object. public override int GetHashCode() { int valSrcHash = 0; for (int i = 0; i < valSrcQueries.Length; i++) { // TODO: Simplify this hash code generation valSrcHash += valSrcQueries[i].GetHashCode(); } return (GetType().GetHashCode() + subQuery.GetHashCode() + valSrcHash) ^ BitConverter.ToInt32(BitConverter.GetBytes(Boost), 0) ^ (strict ? 1234 : 4321); } /// /// Returns a that calculates the custom scores /// for the given . The default implementation returns a default /// implementation as specified in the docs of . /// protected virtual CustomScoreProvider GetCustomScoreProvider(IndexReader reader) { // when deprecated methods are removed, do not extend class here, just return new default CustomScoreProvider return new AnonymousCustomScoreProvider(this, reader); } class AnonymousCustomScoreProvider : CustomScoreProvider { CustomScoreQuery parent; public AnonymousCustomScoreProvider(CustomScoreQuery parent, IndexReader reader) : base(reader) { this.parent = parent; } public override float CustomScore(int doc, float subQueryScore, float[] valSrcScores) { return parent.CustomScore(doc, subQueryScore, valSrcScores); } public override float CustomScore(int doc, float subQueryScore, float valSrcScore) { return parent.CustomScore(doc, subQueryScore, valSrcScore); } public override Explanation CustomExplain(int doc, Explanation subQueryExpl, Explanation[] valSrcExpls) { return parent.CustomExplain(doc, subQueryExpl, valSrcExpls); } public override Explanation CustomExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl) { return parent.CustomExplain(doc, subQueryExpl, valSrcExpl); } } /// /// Compute a custom score by the subQuery score and a number of /// ValueSourceQuery scores. /// /// The doc is relative to the current reader, which is /// unknown to CustomScoreQuery when using per-segment search (since Lucene 2.9). /// Please override and return a subclass /// of for the given . /// see CustomScoreProvider#customScore(int,float,float[]) /// [Obsolete("Will be removed in Lucene 3.1")] public virtual float CustomScore(int doc, float subQueryScore, float[] valSrcScores) { if (valSrcScores.Length == 1) { return CustomScore(doc, subQueryScore, valSrcScores[0]); } if (valSrcScores.Length == 0) { return CustomScore(doc, subQueryScore, 1); } float score = subQueryScore; for (int i = 0; i < valSrcScores.Length; i++) { score *= valSrcScores[i]; } return score; } /// Compute a custom score by the subQuery score and the ValueSourceQuery score. /// /// The doc is relative to the current reader, which is /// unknown to CustomScoreQuery when using per-segment search (since Lucene 2.9). /// Please override and return a subclass /// of for the given . /// /// [Obsolete("Will be removed in Lucene 3.1")] public virtual float CustomScore(int doc, float subQueryScore, float valSrcScore) { return subQueryScore * valSrcScore; } /// Explain the custom score. /// /// The doc is relative to the current reader, which is /// unknown to CustomScoreQuery when using per-segment search (since Lucene 2.9). /// Please override and return a subclass /// of for the given . /// [Obsolete("Will be removed in Lucene 3.1")] public virtual Explanation CustomExplain(int doc, Explanation subQueryExpl, Explanation[] valSrcExpls) { if (valSrcExpls.Length == 1) { return CustomExplain(doc, subQueryExpl, valSrcExpls[0]); } if (valSrcExpls.Length == 0) { return subQueryExpl; } float valSrcScore = 1; for (int i = 0; i < valSrcExpls.Length; i++) { valSrcScore *= valSrcExpls[i].Value; } Explanation exp = new Explanation(valSrcScore * subQueryExpl.Value, "custom score: product of:"); exp.AddDetail(subQueryExpl); for (int i = 0; i < valSrcExpls.Length; i++) { exp.AddDetail(valSrcExpls[i]); } return exp; } /// Explain the custom score. /// The doc is relative to the current reader, which is /// unknown to CustomScoreQuery when using per-segment search (since Lucene 2.9). /// Please override and return a subclass /// of for the given . /// [Obsolete("Will be removed in Lucene 3.1")] public virtual Explanation CustomExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl) { float valSrcScore = 1; if (valSrcExpl != null) { valSrcScore *= valSrcExpl.Value; } Explanation exp = new Explanation(valSrcScore * subQueryExpl.Value, "custom score: product of:"); exp.AddDetail(subQueryExpl); exp.AddDetail(valSrcExpl); return exp; } //=========================== W E I G H T ============================ [Serializable] private class CustomWeight:Weight { private void InitBlock(CustomScoreQuery enclosingInstance) { this.enclosingInstance = enclosingInstance; } private CustomScoreQuery enclosingInstance; public CustomScoreQuery Enclosing_Instance { get { return enclosingInstance; } } internal Similarity similarity; internal Weight subQueryWeight; internal Weight[] valSrcWeights; internal bool qStrict; public CustomWeight(CustomScoreQuery enclosingInstance, Searcher searcher) { InitBlock(enclosingInstance); this.similarity = Enclosing_Instance.GetSimilarity(searcher); this.subQueryWeight = Enclosing_Instance.subQuery.Weight(searcher); this.valSrcWeights = new Weight[Enclosing_Instance.valSrcQueries.Length]; for (int i = 0; i < Enclosing_Instance.valSrcQueries.Length; i++) { this.valSrcWeights[i] = Enclosing_Instance.valSrcQueries[i].CreateWeight(searcher); } this.qStrict = Enclosing_Instance.strict; } /*(non-Javadoc) A scorer that applies a (callback) function on scores of the subQuery. private class CustomScorer:Scorer { private void InitBlock(CustomScoreQuery enclosingInstance) { this.enclosingInstance = enclosingInstance; } private CustomScoreQuery enclosingInstance; public CustomScoreQuery Enclosing_Instance { get { return enclosingInstance; } } private float qWeight; private Scorer subQueryScorer; private Scorer[] valSrcScorers; private IndexReader reader; private CustomScoreProvider provider; private float[] vScores; // reused in score() to avoid allocating this array for each doc // constructor internal CustomScorer(CustomScoreQuery enclosingInstance, Similarity similarity, IndexReader reader, CustomWeight w, Scorer subQueryScorer, Scorer[] valSrcScorers):base(similarity) { InitBlock(enclosingInstance); this.qWeight = w.Value; this.subQueryScorer = subQueryScorer; this.valSrcScorers = valSrcScorers; this.reader = reader; this.vScores = new float[valSrcScorers.Length]; this.provider = this.Enclosing_Instance.GetCustomScoreProvider(reader); } public override int NextDoc() { int doc = subQueryScorer.NextDoc(); if (doc != NO_MORE_DOCS) { for (int i = 0; i < valSrcScorers.Length; i++) { valSrcScorers[i].Advance(doc); } } return doc; } public override int DocID() { return subQueryScorer.DocID(); } /*(non-Javadoc) The strict mode to set. /// /// /// public virtual void SetStrict(bool strict) { this.strict = strict; } /// A short name of this query, used in . public virtual System.String Name() { return "custom"; } } }