From 3d404c5d3c6079621d2b179d6c0c16dc4845b534 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 09:43:32 -0700 Subject: [PATCH 01/11] introduce protected constructor that allows to avoid virtual calls --- src/Lucene.Net/Search/IndexSearcher.cs | 35 +++++++++++++++----------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index abdee3c172..d1a1108705 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -135,13 +135,30 @@ public IndexSearcher(IndexReader r, TaskScheduler executor) /// /// public IndexSearcher(IndexReaderContext context, TaskScheduler executor) + : this(context, executor, allocateLeafSlices: executor is not null) + { + } + + /// + /// LUCENENET specific constructor that can be used by the subclasses to + /// control whether or not the leaf slices are allocated. + /// If you choose to skip allocating the leaf slices here, you must + /// call in your subclass's constructor. + /// + [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] + [SuppressMessage("CodeQuality", "S1699:Constructors should only call non-overridable methods", Justification = "Required for continuity with Lucene's design")] + protected IndexSearcher(IndexReaderContext context, TaskScheduler executor, bool allocateLeafSlices) { if (Debugging.AssertsEnabled) Debugging.Assert(context.IsTopLevel,"IndexSearcher's ReaderContext must be topLevel for reader {0}", context.Reader); reader = context.Reader; this.executor = executor; this.m_readerContext = context; m_leafContexts = context.Leaves; - this.m_leafSlices = executor is null ? null : GetSlicesInternal(m_leafContexts); + + if (allocateLeafSlices) + { + this.m_leafSlices = Slices(m_leafContexts); + } } /// @@ -160,18 +177,8 @@ public IndexSearcher(IndexReaderContext context) /// Expert: Creates an array of leaf slices each holding a subset of the given leaves. /// Each is executed in a single thread. By default there /// will be one per leaf (). - /// - /// NOTE: When overriding this method, be aware that the constructor of this class calls - /// a private method and not this virtual method. So if you need to override - /// the behavior during the initialization, call your own private method from the constructor - /// with whatever custom behavior you need. /// - // LUCENENET specific - renamed to GetSlices to better indicate the purpose of this method - protected virtual LeafSlice[] GetSlices(IList leaves) - => GetSlicesInternal(leaves); - - // LUCENENET specific - creating this so that we can call it from the constructor - protected LeafSlice[] GetSlicesInternal(IList leaves) + protected virtual LeafSlice[] Slices(IList leaves) { LeafSlice[] slices = new LeafSlice[leaves.Count]; for (int i = 0; i < slices.Length; i++) @@ -460,7 +467,7 @@ protected virtual TopDocs Search(Weight weight, ScoreDoc after, int nDocs) } else { - HitQueue hq = new HitQueue(nDocs, prePopulate: false); + HitQueue hq = new HitQueue(nDocs, false); ReentrantLock @lock = new ReentrantLock(); ExecutionHelper runner = new ExecutionHelper(executor); @@ -538,7 +545,7 @@ protected virtual TopFieldDocs Search(Weight weight, FieldDoc after, int nDocs, { if (sort is null) { - throw new ArgumentNullException(nameof(sort), "Sort must not be null"); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) + throw new ArgumentNullException("Sort must not be null"); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) } int limit = reader.MaxDoc; From 6a15e75fccc2d5fc04fb96d9e766b6138f208a0d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 09:43:48 -0700 Subject: [PATCH 02/11] introduce protected constructor that allows to avoid virtual calls --- src/Lucene.Net/Search/IndexSearcher.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index d1a1108705..c9e8d27cb3 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -5,6 +5,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using System.Threading.Tasks; From cf309715964a8e32715c8c4644aff6610fb94540 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 09:52:08 -0700 Subject: [PATCH 03/11] introduce protected constructor that allows to avoid virtual calls --- src/Lucene.Net/Search/IndexSearcher.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index c9e8d27cb3..7e4ae33289 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -472,6 +472,10 @@ protected virtual TopDocs Search(Weight weight, ScoreDoc after, int nDocs) ReentrantLock @lock = new ReentrantLock(); ExecutionHelper runner = new ExecutionHelper(executor); + if (m_leafSlices == null) + throw new InvalidOperationException("m_leafSlices must not be null and initialized in the constructor"); + + for (int i = 0; i < m_leafSlices.Length; i++) // search each sub { runner.Submit(new SearcherCallableNoSort(@lock, this, m_leafSlices[i], weight, after, nDocs, hq).Call); From 71ff3a8812c1af47921fd978653116ef21fb1268 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 09:55:50 -0700 Subject: [PATCH 04/11] introduce protected constructor that allows to avoid virtual calls --- src/Lucene.Net/Search/IndexSearcher.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index 7e4ae33289..2696c7b7a3 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -1,5 +1,4 @@ using Lucene.Net.Diagnostics; -using Lucene.Net.Support; using Lucene.Net.Support.Threading; using Lucene.Net.Util; using System; @@ -7,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Threading; using System.Threading.Tasks; namespace Lucene.Net.Search @@ -144,7 +142,7 @@ public IndexSearcher(IndexReaderContext context, TaskScheduler executor) /// LUCENENET specific constructor that can be used by the subclasses to /// control whether or not the leaf slices are allocated. /// If you choose to skip allocating the leaf slices here, you must - /// call in your subclass's constructor. + /// call in your subclass's constructor. /// [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] [SuppressMessage("CodeQuality", "S1699:Constructors should only call non-overridable methods", Justification = "Required for continuity with Lucene's design")] @@ -158,7 +156,7 @@ protected IndexSearcher(IndexReaderContext context, TaskScheduler executor, bool if (allocateLeafSlices) { - this.m_leafSlices = Slices(m_leafContexts); + this.m_leafSlices = GetSlices(m_leafContexts); } } @@ -179,7 +177,7 @@ public IndexSearcher(IndexReaderContext context) /// Each is executed in a single thread. By default there /// will be one per leaf (). /// - protected virtual LeafSlice[] Slices(IList leaves) + protected virtual LeafSlice[] GetSlices(IList leaves) { LeafSlice[] slices = new LeafSlice[leaves.Count]; for (int i = 0; i < slices.Length; i++) @@ -468,7 +466,7 @@ protected virtual TopDocs Search(Weight weight, ScoreDoc after, int nDocs) } else { - HitQueue hq = new HitQueue(nDocs, false); + HitQueue hq = new HitQueue(nDocs, prePopulate: false); ReentrantLock @lock = new ReentrantLock(); ExecutionHelper runner = new ExecutionHelper(executor); @@ -550,7 +548,7 @@ protected virtual TopFieldDocs Search(Weight weight, FieldDoc after, int nDocs, { if (sort is null) { - throw new ArgumentNullException("Sort must not be null"); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) + throw new ArgumentNullException(nameof(sort), "Sort must not be null"); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) } int limit = reader.MaxDoc; From e92da0586478a932b40fffa388740eab0e020266 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 10:03:35 -0700 Subject: [PATCH 05/11] adjust comments --- src/Lucene.Net/Search/IndexSearcher.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index 2696c7b7a3..1a87e8986c 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -142,7 +142,8 @@ public IndexSearcher(IndexReaderContext context, TaskScheduler executor) /// LUCENENET specific constructor that can be used by the subclasses to /// control whether or not the leaf slices are allocated. /// If you choose to skip allocating the leaf slices here, you must - /// call in your subclass's constructor. + /// initialize in your subclass's constructor, either + /// by calling or using your own logic /// [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] [SuppressMessage("CodeQuality", "S1699:Constructors should only call non-overridable methods", Justification = "Required for continuity with Lucene's design")] @@ -472,7 +473,6 @@ protected virtual TopDocs Search(Weight weight, ScoreDoc after, int nDocs) if (m_leafSlices == null) throw new InvalidOperationException("m_leafSlices must not be null and initialized in the constructor"); - for (int i = 0; i < m_leafSlices.Length; i++) // search each sub { @@ -570,6 +570,9 @@ protected virtual TopFieldDocs Search(Weight weight, FieldDoc after, int nDocs, ReentrantLock @lock = new ReentrantLock(); ExecutionHelper runner = new ExecutionHelper(executor); + if (m_leafSlices == null) + throw new InvalidOperationException("m_leafSlices must not be null and initialized in the constructor"); + for (int i = 0; i < m_leafSlices.Length; i++) // search each leaf slice { runner.Submit(new SearcherCallableWithSort(@lock, this, m_leafSlices[i], weight, after, nDocs, topCollector, sort, doDocScores, doMaxScore).Call); From daf510ed612de3d8bd63f9dfef1c5f74c5c22b4d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 10:55:43 -0700 Subject: [PATCH 06/11] feedback --- src/Lucene.Net/Search/IndexSearcher.cs | 33 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index 1a87e8986c..665e4f711a 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -117,7 +117,7 @@ public IndexSearcher(IndexReader r) /// @lucene.experimental /// public IndexSearcher(IndexReader r, TaskScheduler executor) - : this(r.Context, executor) + : this(r?.Context, executor) { } @@ -140,16 +140,29 @@ public IndexSearcher(IndexReaderContext context, TaskScheduler executor) /// /// LUCENENET specific constructor that can be used by the subclasses to - /// control whether or not the leaf slices are allocated. - /// If you choose to skip allocating the leaf slices here, you must - /// initialize in your subclass's constructor, either - /// by calling or using your own logic + /// control whether the leaf slices are allocated in the base class or subclass. /// + /// + /// If executor is non-null and you choose to skip allocating the leaf slices + /// (i.e. == false), you must + /// set the field in your subclass constructor. + /// This is commonly done by calling + /// and using the result to set . You may wish to do this if you + /// have state to pass into your constructor and need to set it prior to the call to + /// so it is available for use + /// as a member field or property inside a custom override of + /// . + /// [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] [SuppressMessage("CodeQuality", "S1699:Constructors should only call non-overridable methods", Justification = "Required for continuity with Lucene's design")] protected IndexSearcher(IndexReaderContext context, TaskScheduler executor, bool allocateLeafSlices) { - if (Debugging.AssertsEnabled) Debugging.Assert(context.IsTopLevel,"IndexSearcher's ReaderContext must be topLevel for reader {0}", context.Reader); + if (context is null) + throw new ArgumentNullException(nameof(context)); + + if (Debugging.AssertsEnabled) + Debugging.Assert(context.IsTopLevel,"IndexSearcher's ReaderContext must be topLevel for reader {0}", context.Reader); + reader = context.Reader; this.executor = executor; this.m_readerContext = context; @@ -471,8 +484,8 @@ protected virtual TopDocs Search(Weight weight, ScoreDoc after, int nDocs) ReentrantLock @lock = new ReentrantLock(); ExecutionHelper runner = new ExecutionHelper(executor); - if (m_leafSlices == null) - throw new InvalidOperationException("m_leafSlices must not be null and initialized in the constructor"); + if (m_leafSlices is null) + throw new InvalidOperationException($"When the constructor is passed a non-null {nameof(TaskScheduler)}, {nameof(m_leafSlices)} must also be set to a non-null value in the constructor."); for (int i = 0; i < m_leafSlices.Length; i++) // search each sub { @@ -570,8 +583,8 @@ protected virtual TopFieldDocs Search(Weight weight, FieldDoc after, int nDocs, ReentrantLock @lock = new ReentrantLock(); ExecutionHelper runner = new ExecutionHelper(executor); - if (m_leafSlices == null) - throw new InvalidOperationException("m_leafSlices must not be null and initialized in the constructor"); + if (m_leafSlices is null) + throw new InvalidOperationException($"When the constructor is passed a non-null {nameof(TaskScheduler)}, {nameof(m_leafSlices)} must also be set to a non-null value in the constructor."); for (int i = 0; i < m_leafSlices.Length; i++) // search each leaf slice { From 00843c3e2454eedc6ff237908379bece26ae28e2 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 11:20:43 -0700 Subject: [PATCH 07/11] nullable enable --- src/Lucene.Net/Search/IndexSearcher.cs | 82 ++++++++++++++------------ 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index 665e4f711a..974aee4f48 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -1,4 +1,5 @@ -using Lucene.Net.Diagnostics; +#nullable enable +using Lucene.Net.Diagnostics; using Lucene.Net.Support.Threading; using Lucene.Net.Util; using System; @@ -79,10 +80,10 @@ public class IndexSearcher /// /// Used with executor - each slice holds a set of leafs executed within one thread - protected readonly LeafSlice[] m_leafSlices; + protected readonly LeafSlice[]? m_leafSlices; // These are only used for multi-threaded search - private readonly TaskScheduler executor; + private readonly TaskScheduler? executor; // the default Similarity private static readonly Similarity defaultSimilarity = new DefaultSimilarity(); @@ -104,7 +105,7 @@ public class IndexSearcher /// /// Creates a searcher searching the provided index. public IndexSearcher(IndexReader r) - : this(r, null) + : this(r, executor: null) { } @@ -116,8 +117,8 @@ public IndexSearcher(IndexReader r) /// /// @lucene.experimental /// - public IndexSearcher(IndexReader r, TaskScheduler executor) - : this(r?.Context, executor) + public IndexSearcher(IndexReader r, TaskScheduler? executor) + : this(r.Context, executor) { } @@ -133,7 +134,7 @@ public IndexSearcher(IndexReader r, TaskScheduler executor) /// /// /// - public IndexSearcher(IndexReaderContext context, TaskScheduler executor) + public IndexSearcher(IndexReaderContext context, TaskScheduler? executor) : this(context, executor, allocateLeafSlices: executor is not null) { } @@ -155,7 +156,7 @@ public IndexSearcher(IndexReaderContext context, TaskScheduler executor) /// [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] [SuppressMessage("CodeQuality", "S1699:Constructors should only call non-overridable methods", Justification = "Required for continuity with Lucene's design")] - protected IndexSearcher(IndexReaderContext context, TaskScheduler executor, bool allocateLeafSlices) + protected IndexSearcher(IndexReaderContext context, TaskScheduler? executor, bool allocateLeafSlices) { if (context is null) throw new ArgumentNullException(nameof(context)); @@ -247,7 +248,7 @@ public virtual Similarity Similarity /// /// @lucene.internal - protected virtual Query WrapFilter(Query query, Filter filter) + protected virtual Query WrapFilter(Query query, Filter? filter) { return (filter is null) ? query : new FilteredQuery(query, filter); } @@ -292,7 +293,7 @@ public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, i /// clauses. public virtual TopDocs Search(Query query, int n) { - return Search(query, null, n); + return Search(query, filter: null, n); } /// @@ -301,9 +302,9 @@ public virtual TopDocs Search(Query query, int n) /// /// If a query would exceed /// clauses. - public virtual TopDocs Search(Query query, Filter filter, int n) + public virtual TopDocs Search(Query query, Filter? filter, int n) { - return Search(CreateNormalizedWeight(WrapFilter(query, filter)), null, n); + return Search(CreateNormalizedWeight(WrapFilter(query, filter)), after: null, n); } /// @@ -383,13 +384,27 @@ public virtual TopFieldDocs Search(Query query, Filter filter, int n, Sort sort, /// clauses. public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, int n, Sort sort) { + FieldDoc? fieldDoc = GetScoreDocAsFieldDocIfNotNull(after); + + return Search(CreateNormalizedWeight(WrapFilter(query, filter)), fieldDoc, n, sort, true, false, false); + } + + private static FieldDoc? GetScoreDocAsFieldDocIfNotNull(ScoreDoc after) + { + FieldDoc? fieldDoc = null; if (after != null && !(after is FieldDoc)) { // TODO: if we fix type safety of TopFieldDocs we can // remove this throw new ArgumentException("after must be a FieldDoc; got " + after); } - return Search(CreateNormalizedWeight(WrapFilter(query, filter)), (FieldDoc)after, n, sort, true, false, false); + + if (after != null && after is FieldDoc) + { + fieldDoc = (FieldDoc)after; + } + + return fieldDoc; } /// @@ -417,13 +432,9 @@ public virtual TopFieldDocs Search(Query query, int n, Sort sort) /// clauses. public virtual TopDocs SearchAfter(ScoreDoc after, Query query, int n, Sort sort) { - if (after != null && !(after is FieldDoc)) - { - // TODO: if we fix type safety of TopFieldDocs we can - // remove this - throw new ArgumentException("after must be a FieldDoc; got " + after); - } - return Search(CreateNormalizedWeight(query), (FieldDoc)after, n, sort, true, false, false); + var fieldDoc = GetScoreDocAsFieldDocIfNotNull(after); + + return Search(CreateNormalizedWeight(query), fieldDoc, n, sort, true, false, false); } /// @@ -444,13 +455,9 @@ public virtual TopDocs SearchAfter(ScoreDoc after, Query query, int n, Sort sort /// clauses. public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, int n, Sort sort, bool doDocScores, bool doMaxScore) { - if (after != null && !(after is FieldDoc)) - { - // TODO: if we fix type safety of TopFieldDocs we can - // remove this - throw new ArgumentException("after must be a FieldDoc; got " + after); - } - return Search(CreateNormalizedWeight(WrapFilter(query, filter)), (FieldDoc)after, n, sort, true, doDocScores, doMaxScore); + var fieldDoc = GetScoreDocAsFieldDocIfNotNull(after); + + return Search(CreateNormalizedWeight(WrapFilter(query, filter)), fieldDoc, n, sort, true, doDocScores, doMaxScore); } /// @@ -461,7 +468,7 @@ public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, i /// instead. /// If a query would exceed /// clauses. - protected virtual TopDocs Search(Weight weight, ScoreDoc after, int nDocs) + protected virtual TopDocs Search(Weight weight, ScoreDoc? after, int nDocs) { int limit = reader.MaxDoc; if (limit == 0) @@ -521,7 +528,7 @@ protected virtual TopDocs Search(Weight weight, ScoreDoc after, int nDocs) /// instead. /// If a query would exceed /// clauses. - protected virtual TopDocs Search(IList leaves, Weight weight, ScoreDoc after, int nDocs) + protected virtual TopDocs Search(IList leaves, Weight weight, ScoreDoc? after, int nDocs) { // single thread int limit = reader.MaxDoc; @@ -549,7 +556,7 @@ protected virtual TopDocs Search(IList leaves, Weight weigh /// clauses. protected virtual TopFieldDocs Search(Weight weight, int nDocs, Sort sort, bool doDocScores, bool doMaxScore) { - return Search(weight, null, nDocs, sort, true, doDocScores, doMaxScore); + return Search(weight, after: null, nDocs, sort, true, doDocScores, doMaxScore); } /// @@ -557,7 +564,7 @@ protected virtual TopFieldDocs Search(Weight weight, int nDocs, Sort sort, bool /// whether or not the fields in the returned instances should /// be set by specifying . /// - protected virtual TopFieldDocs Search(Weight weight, FieldDoc after, int nDocs, Sort sort, bool fillFields, bool doDocScores, bool doMaxScore) + protected virtual TopFieldDocs Search(Weight weight, FieldDoc? after, int nDocs, Sort sort, bool fillFields, bool doDocScores, bool doMaxScore) { if (sort is null) { @@ -613,7 +620,7 @@ protected virtual TopFieldDocs Search(Weight weight, FieldDoc after, int nDocs, /// whether or not the fields in the returned instances should /// be set by specifying . /// - protected virtual TopFieldDocs Search(IList leaves, Weight weight, FieldDoc after, int nDocs, Sort sort, bool fillFields, bool doDocScores, bool doMaxScore) + protected virtual TopFieldDocs Search(IList leaves, Weight weight, FieldDoc? after, int nDocs, Sort sort, bool fillFields, bool doDocScores, bool doMaxScore) { // single thread int limit = reader.MaxDoc; @@ -765,12 +772,12 @@ public virtual Weight CreateNormalizedWeight(Query query) private readonly ReentrantLock @lock; private readonly IndexSearcher searcher; private readonly Weight weight; - private readonly ScoreDoc after; + private readonly ScoreDoc? after; private readonly int nDocs; private readonly HitQueue hq; private readonly LeafSlice slice; - public SearcherCallableNoSort(ReentrantLock @lock, IndexSearcher searcher, LeafSlice slice, Weight weight, ScoreDoc after, int nDocs, HitQueue hq) + public SearcherCallableNoSort(ReentrantLock @lock, IndexSearcher searcher, LeafSlice slice, Weight weight, ScoreDoc? after, int nDocs, HitQueue hq) { this.@lock = @lock; this.searcher = searcher; @@ -818,11 +825,11 @@ public TopDocs Call() private readonly TopFieldCollector hq; private readonly Sort sort; private readonly LeafSlice slice; - private readonly FieldDoc after; + private readonly FieldDoc? after; private readonly bool doDocScores; private readonly bool doMaxScore; - public SearcherCallableWithSort(ReentrantLock @lock, IndexSearcher searcher, LeafSlice slice, Weight weight, FieldDoc after, int nDocs, TopFieldCollector hq, Sort sort, bool doDocScores, bool doMaxScore) + public SearcherCallableWithSort(ReentrantLock @lock, IndexSearcher searcher, LeafSlice slice, Weight weight, FieldDoc? after, int nDocs, TopFieldCollector hq, Sort sort, bool doDocScores, bool doMaxScore) { this.@lock = @lock; this.searcher = searcher; @@ -1016,4 +1023,5 @@ public virtual CollectionStatistics CollectionStatistics(string field) return new CollectionStatistics(field, reader.MaxDoc, docCount, sumTotalTermFreq, sumDocFreq); } } -} \ No newline at end of file +} +#nullable restore \ No newline at end of file From 8f93c532302d182e7abfa7459947e8c3ba800ac3 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 11:25:29 -0700 Subject: [PATCH 08/11] nullable enable --- src/Lucene.Net/Search/IndexSearcher.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index 974aee4f48..3f4c899c47 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -878,6 +878,7 @@ public TopFieldDocs Call() } } + #nullable restore /// /// A helper class that wraps a and provides an /// iterable interface to the completed delegates. @@ -956,7 +957,8 @@ IEnumerator IEnumerable.GetEnumerator() return this; } } - + #nullable enable + /// /// A class holding a subset of the s leaf contexts to be /// executed within a single thread. From ca71b4e0a3a15bfb835c0c217a83ec578eec2f8b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 11:27:52 -0700 Subject: [PATCH 09/11] update comments --- src/Lucene.Net/Search/IndexSearcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index 3f4c899c47..2ef5a9e783 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -144,7 +144,7 @@ public IndexSearcher(IndexReaderContext context, TaskScheduler? executor) /// control whether the leaf slices are allocated in the base class or subclass. /// /// - /// If executor is non-null and you choose to skip allocating the leaf slices + /// If is non-null and you choose to skip allocating the leaf slices /// (i.e. == false), you must /// set the field in your subclass constructor. /// This is commonly done by calling From 9113263642c564d03ba2f4742fd3f5925956f6a5 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sat, 15 Apr 2023 05:41:16 +0700 Subject: [PATCH 10/11] Lucene.Net.Search.IndexSearcher: Reviewed and added null guard clauses for all non-nullable parameters. Marked nullable parameters as such. Documented when ArgumentNullReferenceException is expected to be thrown from each method overload. --- src/Lucene.Net/Search/IndexSearcher.cs | 160 +++++++++++++++++++------ 1 file changed, 123 insertions(+), 37 deletions(-) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index 2ef5a9e783..17fa5b1324 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -104,6 +104,7 @@ public class IndexSearcher /// /// Creates a searcher searching the provided index. + /// is null. public IndexSearcher(IndexReader r) : this(r, executor: null) { @@ -117,8 +118,9 @@ public IndexSearcher(IndexReader r) /// /// @lucene.experimental /// + /// is null. public IndexSearcher(IndexReader r, TaskScheduler? executor) - : this(r.Context, executor) + : this(r?.Context!, executor) { } @@ -132,6 +134,7 @@ public IndexSearcher(IndexReader r, TaskScheduler? executor) /// /// @lucene.experimental /// + /// is null. /// /// public IndexSearcher(IndexReaderContext context, TaskScheduler? executor) @@ -154,6 +157,7 @@ public IndexSearcher(IndexReaderContext context, TaskScheduler? executor) /// as a member field or property inside a custom override of /// . /// + /// is null. [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] [SuppressMessage("CodeQuality", "S1699:Constructors should only call non-overridable methods", Justification = "Required for continuity with Lucene's design")] protected IndexSearcher(IndexReaderContext context, TaskScheduler? executor, bool allocateLeafSlices) @@ -180,6 +184,7 @@ protected IndexSearcher(IndexReaderContext context, TaskScheduler? executor, boo /// /// @lucene.experimental /// + /// is null. /// /// public IndexSearcher(IndexReaderContext context) @@ -192,8 +197,13 @@ public IndexSearcher(IndexReaderContext context) /// Each is executed in a single thread. By default there /// will be one per leaf (). /// + /// is null. protected virtual LeafSlice[] GetSlices(IList leaves) { + // LUCENENET: Added guard clause + if (leaves is null) + throw new ArgumentNullException(nameof(leaves)); + LeafSlice[] slices = new LeafSlice[leaves.Count]; for (int i = 0; i < slices.Length; i++) { @@ -217,6 +227,7 @@ public virtual Document Doc(int docID) /// /// Sugar for .IndexReader.Document(docID, fieldVisitor) /// + /// is null. public virtual void Doc(int docID, StoredFieldVisitor fieldVisitor) { reader.Document(docID, fieldVisitor); @@ -225,7 +236,7 @@ public virtual void Doc(int docID, StoredFieldVisitor fieldVisitor) /// /// Sugar for .IndexReader.Document(docID, fieldsToLoad) /// - public virtual Document Doc(int docID, ISet fieldsToLoad) + public virtual Document Doc(int docID, ISet? fieldsToLoad) { return reader.Document(docID, fieldsToLoad); } @@ -248,6 +259,7 @@ public virtual Similarity Similarity /// /// @lucene.internal + /// is null. protected virtual Query WrapFilter(Query query, Filter? filter) { return (filter is null) ? query : new FilteredQuery(query, filter); @@ -264,7 +276,8 @@ protected virtual Query WrapFilter(Query query, Filter? filter) /// /// If a query would exceed /// clauses. - public virtual TopDocs SearchAfter(ScoreDoc after, Query query, int n) + /// is null. + public virtual TopDocs SearchAfter(ScoreDoc? after, Query query, int n) { return Search(CreateNormalizedWeight(query), after, n); } @@ -280,7 +293,8 @@ public virtual TopDocs SearchAfter(ScoreDoc after, Query query, int n) /// /// If a query would exceed /// clauses. - public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, int n) + /// is null. + public virtual TopDocs SearchAfter(ScoreDoc? after, Query query, Filter? filter, int n) { return Search(CreateNormalizedWeight(WrapFilter(query, filter)), after, n); } @@ -291,6 +305,7 @@ public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, i /// /// If a query would exceed /// clauses. + /// is null. public virtual TopDocs Search(Query query, int n) { return Search(query, filter: null, n); @@ -298,7 +313,7 @@ public virtual TopDocs Search(Query query, int n) /// /// Finds the top - /// hits for , applying if non-null. + /// hits for , applying if non-null. /// /// If a query would exceed /// clauses. @@ -314,11 +329,13 @@ public virtual TopDocs Search(Query query, Filter? filter, int n) /// document. /// /// To match documents - /// Ef non-null, used to permit documents to be collected. + /// Ef non-null, used to permit documents to be collected. /// To receive hits /// If a query would exceed /// clauses. - public virtual void Search(Query query, Filter filter, ICollector results) + /// or + /// is null. + public virtual void Search(Query query, Filter? filter, ICollector results) { Search(m_leafContexts, CreateNormalizedWeight(WrapFilter(query, filter)), results); } @@ -330,6 +347,8 @@ public virtual void Search(Query query, Filter filter, ICollector results) /// /// If a query would exceed /// clauses. + /// or + /// is null. public virtual void Search(Query query, ICollector results) { Search(m_leafContexts, CreateNormalizedWeight(query), results); @@ -347,7 +366,9 @@ public virtual void Search(Query query, ICollector results) /// /// If a query would exceed /// clauses. - public virtual TopFieldDocs Search(Query query, Filter filter, int n, Sort sort) + /// or + /// is null. + public virtual TopFieldDocs Search(Query query, Filter? filter, int n, Sort sort) { return Search(CreateNormalizedWeight(WrapFilter(query, filter)), n, sort, false, false); } @@ -366,7 +387,9 @@ public virtual TopFieldDocs Search(Query query, Filter filter, int n, Sort sort) /// /// If a query would exceed /// clauses. - public virtual TopFieldDocs Search(Query query, Filter filter, int n, Sort sort, bool doDocScores, bool doMaxScore) + /// or + /// is null. + public virtual TopFieldDocs Search(Query query, Filter? filter, int n, Sort sort, bool doDocScores, bool doMaxScore) { return Search(CreateNormalizedWeight(WrapFilter(query, filter)), n, sort, doDocScores, doMaxScore); } @@ -382,26 +405,24 @@ public virtual TopFieldDocs Search(Query query, Filter filter, int n, Sort sort, /// /// If a query would exceed /// clauses. - public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, int n, Sort sort) + /// or + /// is null. + public virtual TopDocs SearchAfter(ScoreDoc? after, Query query, Filter? filter, int n, Sort sort) { FieldDoc? fieldDoc = GetScoreDocAsFieldDocIfNotNull(after); return Search(CreateNormalizedWeight(WrapFilter(query, filter)), fieldDoc, n, sort, true, false, false); } - private static FieldDoc? GetScoreDocAsFieldDocIfNotNull(ScoreDoc after) + private static FieldDoc? GetScoreDocAsFieldDocIfNotNull(ScoreDoc? after) { FieldDoc? fieldDoc = null; - if (after != null && !(after is FieldDoc)) + // LUCENENET: Simplified type check + if (after is not null) { // TODO: if we fix type safety of TopFieldDocs we can // remove this - throw new ArgumentException("after must be a FieldDoc; got " + after); - } - - if (after != null && after is FieldDoc) - { - fieldDoc = (FieldDoc)after; + fieldDoc = after as FieldDoc ?? throw new ArgumentException($"{nameof(after)} must be a {nameof(FieldDoc)}; got {after}"); } return fieldDoc; @@ -414,6 +435,8 @@ public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, i /// The object /// The top docs, sorted according to the supplied instance /// if there is a low-level I/O error + /// or + /// is null. public virtual TopFieldDocs Search(Query query, int n, Sort sort) { return Search(CreateNormalizedWeight(query), n, sort, false, false); @@ -430,7 +453,9 @@ public virtual TopFieldDocs Search(Query query, int n, Sort sort) /// /// If a query would exceed /// clauses. - public virtual TopDocs SearchAfter(ScoreDoc after, Query query, int n, Sort sort) + /// or + /// is null. + public virtual TopDocs SearchAfter(ScoreDoc? after, Query query, int n, Sort sort) { var fieldDoc = GetScoreDocAsFieldDocIfNotNull(after); @@ -453,7 +478,9 @@ public virtual TopDocs SearchAfter(ScoreDoc after, Query query, int n, Sort sort /// /// If a query would exceed /// clauses. - public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, int n, Sort sort, bool doDocScores, bool doMaxScore) + /// or + /// is null. + public virtual TopDocs SearchAfter(ScoreDoc? after, Query query, Filter? filter, int n, Sort sort, bool doDocScores, bool doMaxScore) { var fieldDoc = GetScoreDocAsFieldDocIfNotNull(after); @@ -468,6 +495,7 @@ public virtual TopDocs SearchAfter(ScoreDoc after, Query query, Filter filter, i /// instead. /// If a query would exceed /// clauses. + /// is null. protected virtual TopDocs Search(Weight weight, ScoreDoc? after, int nDocs) { int limit = reader.MaxDoc; @@ -487,13 +515,16 @@ protected virtual TopDocs Search(Weight weight, ScoreDoc? after, int nDocs) } else { + // LUCENENET: Added guard clauses + if (weight is null) + throw new ArgumentNullException(nameof(weight)); + if (m_leafSlices is null) + throw new InvalidOperationException($"When the constructor is passed a non-null {nameof(TaskScheduler)}, {nameof(m_leafSlices)} must also be set to a non-null value in the constructor."); + HitQueue hq = new HitQueue(nDocs, prePopulate: false); ReentrantLock @lock = new ReentrantLock(); ExecutionHelper runner = new ExecutionHelper(executor); - if (m_leafSlices is null) - throw new InvalidOperationException($"When the constructor is passed a non-null {nameof(TaskScheduler)}, {nameof(m_leafSlices)} must also be set to a non-null value in the constructor."); - for (int i = 0; i < m_leafSlices.Length; i++) // search each sub { runner.Submit(new SearcherCallableNoSort(@lock, this, m_leafSlices[i], weight, after, nDocs, hq).Call); @@ -528,8 +559,14 @@ protected virtual TopDocs Search(Weight weight, ScoreDoc? after, int nDocs) /// instead. /// If a query would exceed /// clauses. + /// or + /// is null. protected virtual TopDocs Search(IList leaves, Weight weight, ScoreDoc? after, int nDocs) { + // LUCENENET: Added guard clause + if (weight is null) + throw new ArgumentNullException(nameof(weight)); + // single thread int limit = reader.MaxDoc; if (limit == 0) @@ -554,6 +591,8 @@ protected virtual TopDocs Search(IList leaves, Weight weigh /// /// If a query would exceed /// clauses. + /// or + /// is null. protected virtual TopFieldDocs Search(Weight weight, int nDocs, Sort sort, bool doDocScores, bool doMaxScore) { return Search(weight, after: null, nDocs, sort, true, doDocScores, doMaxScore); @@ -564,12 +603,12 @@ protected virtual TopFieldDocs Search(Weight weight, int nDocs, Sort sort, bool /// whether or not the fields in the returned instances should /// be set by specifying . /// + /// or + /// is null. protected virtual TopFieldDocs Search(Weight weight, FieldDoc? after, int nDocs, Sort sort, bool fillFields, bool doDocScores, bool doMaxScore) { if (sort is null) - { throw new ArgumentNullException(nameof(sort), "Sort must not be null"); // LUCENENET specific - changed from IllegalArgumentException to ArgumentNullException (.NET convention) - } int limit = reader.MaxDoc; if (limit == 0) @@ -585,14 +624,17 @@ protected virtual TopFieldDocs Search(Weight weight, FieldDoc? after, int nDocs, } else { + // LUCENENET: Added guard clauses + if (weight is null) + throw new ArgumentNullException(nameof(weight)); + if (m_leafSlices is null) + throw new InvalidOperationException($"When the constructor is passed a non-null {nameof(TaskScheduler)}, {nameof(m_leafSlices)} must also be set to a non-null value in the constructor."); + TopFieldCollector topCollector = TopFieldCollector.Create(sort, nDocs, after, fillFields, doDocScores, doMaxScore, false); ReentrantLock @lock = new ReentrantLock(); ExecutionHelper runner = new ExecutionHelper(executor); - if (m_leafSlices is null) - throw new InvalidOperationException($"When the constructor is passed a non-null {nameof(TaskScheduler)}, {nameof(m_leafSlices)} must also be set to a non-null value in the constructor."); - for (int i = 0; i < m_leafSlices.Length; i++) // search each leaf slice { runner.Submit(new SearcherCallableWithSort(@lock, this, m_leafSlices[i], weight, after, nDocs, topCollector, sort, doDocScores, doMaxScore).Call); @@ -620,8 +662,14 @@ protected virtual TopFieldDocs Search(Weight weight, FieldDoc? after, int nDocs, /// whether or not the fields in the returned instances should /// be set by specifying . /// + /// or + /// is null. protected virtual TopFieldDocs Search(IList leaves, Weight weight, FieldDoc? after, int nDocs, Sort sort, bool fillFields, bool doDocScores, bool doMaxScore) { + // LUCENENET: Added guard clause + if (weight is null) + throw new ArgumentNullException(nameof(weight)); + // single thread int limit = reader.MaxDoc; if (limit == 0) @@ -653,8 +701,18 @@ protected virtual TopFieldDocs Search(IList leaves, Weight /// To receive hits /// If a query would exceed /// clauses. + /// , , + /// or is null. protected virtual void Search(IList leaves, Weight weight, ICollector collector) { + // LUCENENET: Added guard clauses + if (leaves is null) + throw new ArgumentNullException(nameof(leaves)); + if (weight is null) + throw new ArgumentNullException(nameof(weight)); + if (collector is null) + throw new ArgumentNullException(nameof(collector)); + // TODO: should we make this // threaded...? the Collector could be sync'd? // always use single thread: @@ -690,9 +748,13 @@ protected virtual void Search(IList leaves, Weight weight, /// Expert: called to re-write queries into primitive queries. /// If a query would exceed /// clauses. - public virtual Query Rewrite(Query original) + /// is null. + public virtual Query Rewrite(Query query) // LUCENENET: renamed parameter from "original" to "query" so our exception message is consistent across the API { - Query query = original; + // LUCENENET: Added guard clause + if (query is null) + throw new ArgumentNullException(nameof(query)); + for (Query rewrittenQuery = query.Rewrite(reader); rewrittenQuery != query; rewrittenQuery = query.Rewrite(reader)) { query = rewrittenQuery; @@ -709,6 +771,7 @@ public virtual Query Rewrite(Query original) /// Computing an explanation is as expensive as executing the query over the /// entire index. /// + /// is null. public virtual Explanation Explain(Query query, int doc) { return Explain(CreateNormalizedWeight(query), doc); @@ -726,8 +789,13 @@ public virtual Explanation Explain(Query query, int doc) /// Applications should call . /// If a query would exceed /// clauses. + /// is null. protected virtual Explanation Explain(Weight weight, int doc) { + // LUCENENET: Added guard clause + if (weight is null) + throw new ArgumentNullException(nameof(weight)); + int n = ReaderUtil.SubIndex(doc, m_leafContexts); AtomicReaderContext ctx = m_leafContexts[n]; int deBasedDoc = doc - ctx.DocBase; @@ -743,6 +811,7 @@ protected virtual Explanation Explain(Weight weight, int doc) /// /// @lucene.internal /// + /// is null. public virtual Weight CreateNormalizedWeight(Query query) { query = Rewrite(query); @@ -878,7 +947,7 @@ public TopFieldDocs Call() } } - #nullable restore +#nullable restore /// /// A helper class that wraps a and provides an /// iterable interface to the completed delegates. @@ -957,7 +1026,7 @@ IEnumerator IEnumerable.GetEnumerator() return this; } } - #nullable enable +#nullable enable /// /// A class holding a subset of the s leaf contexts to be @@ -969,9 +1038,15 @@ public class LeafSlice { internal AtomicReaderContext[] Leaves { get; private set; } + /// + /// Initializes a new instance of with + /// the specified . + /// + /// The collection of leaves. + /// is null. public LeafSlice(params AtomicReaderContext[] leaves) { - this.Leaves = leaves; + this.Leaves = leaves ?? throw new ArgumentNullException(nameof(leaves)); // LUCENENET: Added guard clause } } @@ -988,8 +1063,16 @@ public override string ToString() /// /// @lucene.experimental /// + /// or + /// is null. public virtual TermStatistics TermStatistics(Term term, TermContext context) { + // LUCENENET: Added guard clauses + if (term is null) + throw new ArgumentNullException(nameof(term)); + if (context is null) + throw new ArgumentNullException(nameof(context)); + return new TermStatistics(term.Bytes, context.DocFreq, context.TotalTermFreq); } @@ -1003,13 +1086,17 @@ public virtual TermStatistics TermStatistics(Term term, TermContext context) /// public virtual CollectionStatistics CollectionStatistics(string field) { + // LUCENENET: Added guard clause + if (field is null) + throw new ArgumentNullException(nameof(field)); + int docCount; long sumTotalTermFreq; long sumDocFreq; - if (Debugging.AssertsEnabled) Debugging.Assert(field != null); + // LUCENENET specific - replaced debug assert check for field being null with above guard clause - Terms terms = MultiFields.GetTerms(reader, field); + Terms? terms = MultiFields.GetTerms(reader, field); if (terms is null) { docCount = 0; @@ -1025,5 +1112,4 @@ public virtual CollectionStatistics CollectionStatistics(string field) return new CollectionStatistics(field, reader.MaxDoc, docCount, sumTotalTermFreq, sumDocFreq); } } -} -#nullable restore \ No newline at end of file +} \ No newline at end of file From d9f4daa8264d322d96459d9e12c2f2a3a4f455e3 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sat, 15 Apr 2023 06:15:16 +0700 Subject: [PATCH 11/11] Lucene.Net.Search.IndexSearcher: Added null guard clause for Doc(int, StoredFieldVisitor) --- src/Lucene.Net/Search/IndexSearcher.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Lucene.Net/Search/IndexSearcher.cs b/src/Lucene.Net/Search/IndexSearcher.cs index 17fa5b1324..b7f0f13bdd 100644 --- a/src/Lucene.Net/Search/IndexSearcher.cs +++ b/src/Lucene.Net/Search/IndexSearcher.cs @@ -165,8 +165,7 @@ protected IndexSearcher(IndexReaderContext context, TaskScheduler? executor, boo if (context is null) throw new ArgumentNullException(nameof(context)); - if (Debugging.AssertsEnabled) - Debugging.Assert(context.IsTopLevel,"IndexSearcher's ReaderContext must be topLevel for reader {0}", context.Reader); + if (Debugging.AssertsEnabled) Debugging.Assert(context.IsTopLevel, "IndexSearcher's ReaderContext must be topLevel for reader {0}", context.Reader); reader = context.Reader; this.executor = executor; @@ -230,6 +229,9 @@ public virtual Document Doc(int docID) /// is null. public virtual void Doc(int docID, StoredFieldVisitor fieldVisitor) { + if (fieldVisitor is null) + throw new ArgumentNullException(nameof(fieldVisitor)); + reader.Document(docID, fieldVisitor); }