| using Lucene.Net.Diagnostics; |
| using System; |
| using System.Collections.Generic; |
| using System.Text; |
| |
| namespace Lucene.Net.Search |
| { |
| /* |
| * 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 AtomicReader = Lucene.Net.Index.AtomicReader; |
| using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext; |
| using DocsEnum = Lucene.Net.Index.DocsEnum; |
| using IBits = Lucene.Net.Util.IBits; |
| using IndexReaderContext = Lucene.Net.Index.IndexReaderContext; |
| using ReaderUtil = Lucene.Net.Index.ReaderUtil; |
| using Similarity = Lucene.Net.Search.Similarities.Similarity; |
| using SimScorer = Lucene.Net.Search.Similarities.Similarity.SimScorer; |
| using Term = Lucene.Net.Index.Term; |
| using TermContext = Lucene.Net.Index.TermContext; |
| using TermsEnum = Lucene.Net.Index.TermsEnum; |
| using TermState = Lucene.Net.Index.TermState; |
| using ToStringUtils = Lucene.Net.Util.ToStringUtils; |
| |
| /// <summary> |
| /// A <see cref="Query"/> that matches documents containing a term. |
| /// this may be combined with other terms with a <see cref="BooleanQuery"/>. |
| /// </summary> |
| #if FEATURE_SERIALIZABLE |
| [Serializable] |
| #endif |
| public class TermQuery : Query |
| { |
| private readonly Term term; |
| private readonly int docFreq; |
| private readonly TermContext perReaderTermState; |
| |
| internal sealed class TermWeight : Weight |
| { |
| private readonly TermQuery outerInstance; |
| |
| internal readonly Similarity similarity; |
| internal readonly Similarity.SimWeight stats; |
| internal readonly TermContext termStates; |
| |
| public TermWeight(TermQuery outerInstance, IndexSearcher searcher, TermContext termStates) |
| { |
| this.outerInstance = outerInstance; |
| if (Debugging.AssertsEnabled) Debugging.Assert(termStates != null, "TermContext must not be null"); |
| this.termStates = termStates; |
| this.similarity = searcher.Similarity; |
| this.stats = similarity.ComputeWeight(outerInstance.Boost, searcher.CollectionStatistics(outerInstance.term.Field), searcher.TermStatistics(outerInstance.term, termStates)); |
| } |
| |
| public override string ToString() |
| { |
| return "weight(" + outerInstance + ")"; |
| } |
| |
| public override Query Query => outerInstance; |
| |
| public override float GetValueForNormalization() |
| { |
| return stats.GetValueForNormalization(); |
| } |
| |
| public override void Normalize(float queryNorm, float topLevelBoost) |
| { |
| stats.Normalize(queryNorm, topLevelBoost); |
| } |
| |
| public override Scorer GetScorer(AtomicReaderContext context, IBits acceptDocs) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(termStates.TopReaderContext == ReaderUtil.GetTopLevelContext(context), () => "The top-reader used to create Weight (" + termStates.TopReaderContext + ") is not the same as the current reader's top-reader (" + ReaderUtil.GetTopLevelContext(context)); |
| TermsEnum termsEnum = GetTermsEnum(context); |
| if (termsEnum == null) |
| { |
| return null; |
| } |
| DocsEnum docs = termsEnum.Docs(acceptDocs, null); |
| if (Debugging.AssertsEnabled) Debugging.Assert(docs != null); |
| return new TermScorer(this, docs, similarity.GetSimScorer(stats, context)); |
| } |
| |
| /// <summary> |
| /// Returns a <see cref="TermsEnum"/> positioned at this weights <see cref="Index.Term"/> or <c>null</c> if |
| /// the term does not exist in the given context. |
| /// </summary> |
| private TermsEnum GetTermsEnum(AtomicReaderContext context) |
| { |
| TermState state = termStates.Get(context.Ord); |
| if (state == null) // term is not present in that reader |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(TermNotInReader(context.AtomicReader, outerInstance.term), () => "no termstate found but term exists in reader term=" + outerInstance.term); |
| return null; |
| } |
| //System.out.println("LD=" + reader.getLiveDocs() + " set?=" + (reader.getLiveDocs() != null ? reader.getLiveDocs().get(0) : "null")); |
| TermsEnum termsEnum = context.AtomicReader.GetTerms(outerInstance.term.Field).GetEnumerator(); |
| termsEnum.SeekExact(outerInstance.term.Bytes, state); |
| return termsEnum; |
| } |
| |
| private bool TermNotInReader(AtomicReader reader, Term term) |
| { |
| // only called from assert |
| //System.out.println("TQ.termNotInReader reader=" + reader + " term=" + field + ":" + bytes.utf8ToString()); |
| return reader.DocFreq(term) == 0; |
| } |
| |
| public override Explanation Explain(AtomicReaderContext context, int doc) |
| { |
| Scorer scorer = GetScorer(context, context.AtomicReader.LiveDocs); |
| if (scorer != null) |
| { |
| int newDoc = scorer.Advance(doc); |
| if (newDoc == doc) |
| { |
| float freq = scorer.Freq; |
| SimScorer docScorer = similarity.GetSimScorer(stats, context); |
| ComplexExplanation result = new ComplexExplanation(); |
| result.Description = "weight(" + Query + " in " + doc + ") [" + similarity.GetType().Name + "], result of:"; |
| Explanation scoreExplanation = docScorer.Explain(doc, new Explanation(freq, "termFreq=" + freq)); |
| result.AddDetail(scoreExplanation); |
| result.Value = scoreExplanation.Value; |
| result.Match = true; |
| return result; |
| } |
| } |
| return new ComplexExplanation(false, 0.0f, "no matching term"); |
| } |
| } |
| |
| /// <summary> |
| /// Constructs a query for the term <paramref name="t"/>. </summary> |
| public TermQuery(Term t) |
| : this(t, -1) |
| { |
| } |
| |
| /// <summary> |
| /// Expert: constructs a <see cref="TermQuery"/> that will use the |
| /// provided <paramref name="docFreq"/> instead of looking up the docFreq |
| /// against the searcher. |
| /// </summary> |
| public TermQuery(Term t, int docFreq) |
| { |
| term = t; |
| this.docFreq = docFreq; |
| perReaderTermState = null; |
| } |
| |
| /// <summary> |
| /// Expert: constructs a <see cref="TermQuery"/> that will use the |
| /// provided docFreq instead of looking up the docFreq |
| /// against the searcher. |
| /// </summary> |
| public TermQuery(Term t, TermContext states) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(states != null); |
| term = t; |
| docFreq = states.DocFreq; |
| perReaderTermState = states; |
| } |
| |
| /// <summary> |
| /// Returns the term of this query. </summary> |
| public virtual Term Term => term; |
| |
| public override Weight CreateWeight(IndexSearcher searcher) |
| { |
| IndexReaderContext context = searcher.TopReaderContext; |
| TermContext termState; |
| if (perReaderTermState == null || perReaderTermState.TopReaderContext != context) |
| { |
| // make TermQuery single-pass if we don't have a PRTS or if the context differs! |
| termState = TermContext.Build(context, term); |
| } |
| else |
| { |
| // PRTS was pre-build for this IS |
| termState = this.perReaderTermState; |
| } |
| |
| // we must not ignore the given docFreq - if set use the given value (lie) |
| if (docFreq != -1) |
| { |
| termState.DocFreq = docFreq; |
| } |
| |
| return new TermWeight(this, searcher, termState); |
| } |
| |
| public override void ExtractTerms(ISet<Term> terms) |
| { |
| terms.Add(Term); |
| } |
| |
| /// <summary> |
| /// Prints a user-readable version of this query. </summary> |
| public override string ToString(string field) |
| { |
| StringBuilder buffer = new StringBuilder(); |
| if (!term.Field.Equals(field, StringComparison.Ordinal)) |
| { |
| buffer.Append(term.Field); |
| buffer.Append(":"); |
| } |
| buffer.Append(term.Text()); |
| buffer.Append(ToStringUtils.Boost(Boost)); |
| return buffer.ToString(); |
| } |
| |
| /// <summary> |
| /// Returns <c>true</c> if <paramref name="o"/> is equal to this. </summary> |
| public override bool Equals(object o) |
| { |
| if (!(o is TermQuery)) |
| { |
| return false; |
| } |
| TermQuery other = (TermQuery)o; |
| return (this.Boost == other.Boost) && this.term.Equals(other.term); |
| } |
| |
| /// <summary> |
| /// Returns a hash code value for this object. </summary> |
| public override int GetHashCode() |
| { |
| return J2N.BitConversion.SingleToInt32Bits(Boost) ^ term.GetHashCode(); |
| } |
| } |
| } |