| using Lucene.Net.Analysis; | 
 | using Lucene.Net.Benchmarks.ByTask.Feeds; | 
 | using Lucene.Net.Documents; | 
 | using Lucene.Net.Index; | 
 | using Lucene.Net.Search; | 
 | using Lucene.Net.Store; | 
 | using Lucene.Net.Util; | 
 | using System; | 
 | using System.Collections.Generic; | 
 | using JCG = J2N.Collections.Generic; | 
 | using Console = Lucene.Net.Util.SystemConsole; | 
 | using System.Diagnostics.CodeAnalysis; | 
 |  | 
 | namespace Lucene.Net.Benchmarks.ByTask.Tasks | 
 | { | 
 |     /* | 
 |      * 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> | 
 |     /// Read index (abstract) task. | 
 |     /// Sub classes implement <see cref="WithSearch"/>, <see cref="WithWarm"/>, <see cref="WithTraverse"/> and <see cref="WithRetrieve"/> | 
 |     /// </summary> | 
 |     /// <remarks> | 
 |     /// Note: All ReadTasks reuse the reader if it is already open. | 
 |     /// Otherwise a reader is opened at start and closed at the end. | 
 |     /// <para/> | 
 |     /// The <c>search.num.hits</c> config parameter sets | 
 |     /// the top number of hits to collect during searching.  If | 
 |     /// <c>print.hits.field</c> is set, then each hit is | 
 |     /// printed along with the value of that field. | 
 |     /// <para/> | 
 |     /// Other side effects: none. | 
 |     /// </remarks> | 
 |     public abstract class ReadTask : PerfTask | 
 |     { | 
 |         #nullable enable | 
 |         private readonly IQueryMaker? queryMaker; | 
 |  | 
 |         [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 ReadTask(PerfRunData runData) // LUCENENET: CA1012: Abstract types should not have constructors (marked protected) | 
 |             : this(runData, queryMaker: null) | 
 |         { | 
 |             if (WithSearch) | 
 |             { | 
 |                 queryMaker = GetQueryMaker(); | 
 |             } | 
 |             else | 
 |             { | 
 |                 queryMaker = null; | 
 |             } | 
 |         } | 
 |         // LUCENENET specific - added this constructor to allow subclasses to initialize it | 
 |         // without having to call constructor that makes a virtual method call | 
 |         protected ReadTask(PerfRunData runData, IQueryMaker? queryMaker) | 
 |             : base(runData) | 
 |         { | 
 |             this.queryMaker = queryMaker; | 
 |         } | 
 |         #nullable restore | 
 |  | 
 |         public override int DoLogic() | 
 |         { | 
 |             int res = 0; | 
 |  | 
 |             // open reader or use existing one | 
 |             IndexSearcher searcher = RunData.GetIndexSearcher(); | 
 |  | 
 |             IndexReader reader; | 
 |  | 
 |             bool closeSearcher; | 
 |             if (searcher is null) | 
 |             { | 
 |                 // open our own reader | 
 |                 Directory dir = RunData.Directory; | 
 |                 reader = DirectoryReader.Open(dir); | 
 |                 searcher = new IndexSearcher(reader); | 
 |                 closeSearcher = true; | 
 |             } | 
 |             else | 
 |             { | 
 |                 // use existing one; this passes +1 ref to us | 
 |                 reader = searcher.IndexReader; | 
 |                 closeSearcher = false; | 
 |             } | 
 |  | 
 |             // optionally warm and add num docs traversed to count | 
 |             if (WithWarm) | 
 |             { | 
 |                 Document doc; // LUCENENET: IDE0059: Remove unnecessary value assignment | 
 |                 IBits liveDocs = MultiFields.GetLiveDocs(reader); | 
 |                 for (int m = 0; m < reader.MaxDoc; m++) | 
 |                 { | 
 |                     if (null == liveDocs || liveDocs.Get(m)) | 
 |                     { | 
 |                         doc = reader.Document(m); | 
 |                         res += (doc is null ? 0 : 1); | 
 |                     } | 
 |                 } | 
 |             } | 
 |  | 
 |             if (WithSearch) | 
 |             { | 
 |                 res++; | 
 |                 Query q = queryMaker.MakeQuery(); | 
 |                 Sort sort = Sort; | 
 |                 TopDocs hits = null; | 
 |                 int numHits = NumHits; | 
 |                 if (numHits > 0) | 
 |                 { | 
 |                     if (WithCollector == false) | 
 |                     { | 
 |                         if (sort != null) | 
 |                         { | 
 |                             // TODO: instead of always passing false we | 
 |                             // should detect based on the query; if we make | 
 |                             // the IndexSearcher search methods that take | 
 |                             // Weight public again, we can go back to | 
 |                             // pulling the Weight ourselves: | 
 |                             TopFieldCollector collector = TopFieldCollector.Create(sort, numHits, | 
 |                                                                                    true, WithScore, | 
 |                                                                                    WithMaxScore, | 
 |                                                                                    false); | 
 |                             searcher.Search(q, null, collector); | 
 |                             hits = collector.GetTopDocs(); | 
 |                         } | 
 |                         else | 
 |                         { | 
 |                             hits = searcher.Search(q, numHits); | 
 |                         } | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         ICollector collector = CreateCollector(); | 
 |                         searcher.Search(q, null, collector); | 
 |                         //hits = collector.topDocs(); | 
 |                     } | 
 |  | 
 |                     string printHitsField = RunData.Config.Get("print.hits.field", null); | 
 |                     if (hits != null && printHitsField != null && printHitsField.Length > 0) | 
 |                     { | 
 |                         Console.WriteLine("totalHits = " + hits.TotalHits); | 
 |                         Console.WriteLine("maxDoc()  = " + reader.MaxDoc); | 
 |                         Console.WriteLine("numDocs() = " + reader.NumDocs); | 
 |                         for (int i = 0; i < hits.ScoreDocs.Length; i++) | 
 |                         { | 
 |                             int docID = hits.ScoreDocs[i].Doc; | 
 |                             Document doc = reader.Document(docID); | 
 |                             Console.WriteLine("  " + i + ": doc=" + docID + " score=" + hits.ScoreDocs[i].Score + " " + printHitsField + " =" + doc.Get(printHitsField)); | 
 |                         } | 
 |                     } | 
 |  | 
 |                     if (WithTraverse) | 
 |                     { | 
 |                         ScoreDoc[] scoreDocs = hits.ScoreDocs; | 
 |                         int traversalSize = Math.Min(scoreDocs.Length, TraversalSize); | 
 |  | 
 |                         if (traversalSize > 0) | 
 |                         { | 
 |                             bool retrieve = WithRetrieve; | 
 |                             int numHighlight = Math.Min(NumToHighlight, scoreDocs.Length); | 
 |                             Analyzer analyzer = RunData.Analyzer; | 
 |                             BenchmarkHighlighter highlighter = null; | 
 |                             if (numHighlight > 0) | 
 |                             { | 
 |                                 highlighter = GetBenchmarkHighlighter(q); | 
 |                             } | 
 |                             for (int m = 0; m < traversalSize; m++) | 
 |                             { | 
 |                                 int id = scoreDocs[m].Doc; | 
 |                                 res++; | 
 |                                 if (retrieve) | 
 |                                 { | 
 |                                     Document document = RetrieveDoc(reader, id); | 
 |                                     res += document != null ? 1 : 0; | 
 |                                     if (numHighlight > 0 && m < numHighlight) | 
 |                                     { | 
 |                                         ICollection<string> fieldsToHighlight = GetFieldsToHighlight(document); | 
 |                                         foreach (string field in fieldsToHighlight) | 
 |                                         { | 
 |                                             string text = document.Get(field); | 
 |                                             res += highlighter.DoHighlight(reader, id, field, document, analyzer, text); | 
 |                                         } | 
 |                                     } | 
 |                                 } | 
 |                             } | 
 |                         } | 
 |                     } | 
 |                 } | 
 |             } | 
 |  | 
 |             if (closeSearcher) | 
 |             { | 
 |                 reader.Dispose(); | 
 |             } | 
 |             else | 
 |             { | 
 |                 // Release our +1 ref from above | 
 |                 reader.DecRef(); | 
 |             } | 
 |             return res; | 
 |         } | 
 |  | 
 |         protected virtual ICollector CreateCollector() | 
 |         { | 
 |             return TopScoreDocCollector.Create(NumHits, docsScoredInOrder: true); | 
 |         } | 
 |  | 
 |  | 
 |         protected virtual Document RetrieveDoc(IndexReader ir, int id) | 
 |         { | 
 |             return ir.Document(id); | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Return query maker used for this task. | 
 |         /// </summary> | 
 |         public abstract IQueryMaker GetQueryMaker(); | 
 |  | 
 |         /// <summary> | 
 |         /// Return <c>true</c> if search should be performed. | 
 |         /// </summary> | 
 |         public abstract bool WithSearch { get; } | 
 |  | 
 |         public virtual bool WithCollector => false; | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Return <c>true</c> if warming should be performed. | 
 |         /// </summary> | 
 |         public abstract bool WithWarm { get; } | 
 |  | 
 |         /// <summary> | 
 |         /// Return <c>true</c> if, with search, results should be traversed. | 
 |         /// </summary> | 
 |         public abstract bool WithTraverse { get; } | 
 |  | 
 |         /// <summary> | 
 |         /// Whether scores should be computed (only useful with | 
 |         /// field sort) | 
 |         /// </summary> | 
 |         public virtual bool WithScore => true; | 
 |  | 
 |         /// <summary> | 
 |         /// Whether maxScores should be computed (only useful with | 
 |         /// field sort) | 
 |         /// </summary> | 
 |         public virtual bool WithMaxScore => true; | 
 |  | 
 |         /// <summary> | 
 |         /// Specify the number of hits to traverse.  Tasks should override this if they want to restrict the number | 
 |         /// of hits that are traversed when <see cref="WithTraverse"/> is <c>true</c>. Must be greater than 0. | 
 |         /// <para/> | 
 |         /// Read task calculates the traversal as: <c>Math.Min(hits.Length, TraversalSize)</c> | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// Unless overridden, the return value is <see cref="int.MaxValue"/>. | 
 |         /// </remarks> | 
 |         public virtual int TraversalSize => int.MaxValue; | 
 |  | 
 |         internal const int DEFAULT_SEARCH_NUM_HITS = 10; | 
 |         private int numHits; | 
 |  | 
 |         public override void Setup() | 
 |         { | 
 |             base.Setup(); | 
 |             numHits = RunData.Config.Get("search.num.hits", DEFAULT_SEARCH_NUM_HITS); | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Specify the number of hits to retrieve.  Tasks should override this if they want to restrict the number | 
 |         /// of hits that are collected during searching. Must be greater than 0. | 
 |         /// <para/> | 
 |         /// Returns 10 by default, or <c>search.num.hits</c> config if set. | 
 |         /// </summary> | 
 |         public virtual int NumHits => numHits; | 
 |  | 
 |         /// <summary> | 
 |         /// Return <c>true</c> if, with search & results traversing, docs should be retrieved. | 
 |         /// </summary> | 
 |         public abstract bool WithRetrieve { get; } | 
 |  | 
 |         /// <summary> | 
 |         /// The number of documents to highlight. 0 means no docs will be highlighted. | 
 |         /// </summary> | 
 |         public virtual int NumToHighlight => 0; | 
 |  | 
 |         /// <summary> | 
 |         /// Return an appropriate highlighter to be used with | 
 |         /// highlighting tasks. | 
 |         /// </summary> | 
 |         /// <param name="q"></param> | 
 |         /// <returns></returns> | 
 |         protected virtual BenchmarkHighlighter GetBenchmarkHighlighter(Query q) | 
 |         { | 
 |             return null; | 
 |         } | 
 |  | 
 |         public virtual Sort Sort => null; | 
 |  | 
 |         /// <summary> | 
 |         /// Define the fields to highlight.  Base implementation returns all fields. | 
 |         /// </summary> | 
 |         /// <param name="document">The <see cref="Document"/>.</param> | 
 |         /// <returns>An <see cref="T:ICollection{string}"/> of <see cref="Field"/> names.</returns> | 
 |         protected virtual ICollection<string> GetFieldsToHighlight(Document document) | 
 |         { | 
 |             IList<IIndexableField> fields = document.Fields; | 
 |             ISet<string> result = new JCG.HashSet<string>(fields.Count); | 
 |             foreach (IIndexableField f in fields) | 
 |             { | 
 |                 result.Add(f.Name); | 
 |             } | 
 |             return result; | 
 |         } | 
 |     } | 
 | } |