Lucene.Net  3.0.3
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
ParallelMultiSearcher.cs
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #if !NET35
19 
20 using System;
21 using System.Threading;
22 using System.Threading.Tasks;
23 using System.Linq;
24 using Lucene.Net.Support;
25 using Lucene.Net.Util;
26 using IndexReader = Lucene.Net.Index.IndexReader;
27 using Term = Lucene.Net.Index.Term;
28 
29 namespace Lucene.Net.Search
30 {
31  /// <summary>Implements parallel search over a set of <c>Searchables</c>.
32  ///
33  /// <p/>Applications usually need only call the inherited <see cref="Searcher.Search(Query, int)" />
34  /// or <see cref="Searcher.Search(Query,Filter,int)" /> methods.
35  /// </summary>
36  public class ParallelMultiSearcher : MultiSearcher/*, IDisposable*/ //No need to implement IDisposable like java, nothing to dispose with the TPL
37  {
38  private class AnonymousClassCollector1:Collector
39  {
40  public AnonymousClassCollector1(Lucene.Net.Search.Collector collector, int start, ParallelMultiSearcher enclosingInstance)
41  {
42  InitBlock(collector, start, enclosingInstance);
43  }
44  private void InitBlock(Lucene.Net.Search.Collector collector, int start, ParallelMultiSearcher enclosingInstance)
45  {
46  this.collector = collector;
47  this.start = start;
48  this.enclosingInstance = enclosingInstance;
49  }
50  private Lucene.Net.Search.Collector collector;
51  private int start;
52  private ParallelMultiSearcher enclosingInstance;
53  public ParallelMultiSearcher Enclosing_Instance
54  {
55  get
56  {
57  return enclosingInstance;
58  }
59 
60  }
61  public override void SetScorer(Scorer scorer)
62  {
63  collector.SetScorer(scorer);
64  }
65  public override void Collect(int doc)
66  {
67  collector.Collect(doc);
68  }
69  public override void SetNextReader(IndexReader reader, int docBase)
70  {
71  collector.SetNextReader(reader, start + docBase);
72  }
73 
74  public override bool AcceptsDocsOutOfOrder
75  {
76  get { return collector.AcceptsDocsOutOfOrder; }
77  }
78  }
79 
80  private Searchable[] searchables;
81  private int[] starts;
82 
83  /// <summary>Creates a <see cref="Searchable"/> which searches <i>searchables</i>. </summary>
84  public ParallelMultiSearcher(params Searchable[] searchables)
85  : base(searchables)
86  {
87  this.searchables = searchables;
88  this.starts = GetStarts();
89  }
90 
91  /// <summary>
92  /// Executes each <see cref="Searchable"/>'s docFreq() in its own thread and
93  /// waits for each search to complete and merge the results back together.
94  /// </summary>
95  public override int DocFreq(Term term)
96  {
97  Task<int>[] tasks = new Task<int>[searchables.Length];
98  for (int i = 0; i < searchables.Length; i++)
99  {
100  Searchable searchable = searchables[i];
101  tasks[i] = Task.Factory.StartNew(() => searchable.DocFreq(term));
102  }
103 
104  Task.WaitAll(tasks);
105  return tasks.Sum(task => task.Result);
106  }
107 
108  /// <summary> A search implementation which executes each
109  /// <see cref="Searchable"/> in its own thread and waits for each search to complete
110  /// and merge the results back together.
111  /// </summary>
112  public override TopDocs Search(Weight weight, Filter filter, int nDocs)
113  {
114  HitQueue hq = new HitQueue(nDocs, false);
115  object lockObj = new object();
116 
117  Task<TopDocs>[] tasks = new Task<TopDocs>[searchables.Length];
118  //search each searchable
119  for (int i = 0; i < searchables.Length; i++)
120  {
121  int cur = i;
122  tasks[i] =
123  Task.Factory.StartNew(() => MultiSearcherCallableNoSort(ThreadLock.MonitorLock, lockObj, searchables[cur], weight, filter,
124  nDocs, hq, cur, starts));
125  }
126 
127  int totalHits = 0;
128  float maxScore = float.NegativeInfinity;
129 
130 
131  Task.WaitAll(tasks);
132  foreach(TopDocs topDocs in tasks.Select(x => x.Result))
133  {
134  totalHits += topDocs.TotalHits;
135  maxScore = Math.Max(maxScore, topDocs.MaxScore);
136  }
137 
138  ScoreDoc[] scoreDocs = new ScoreDoc[hq.Size()];
139  for (int i = hq.Size() - 1; i >= 0; i--) // put docs in array
140  scoreDocs[i] = hq.Pop();
141 
142  return new TopDocs(totalHits, scoreDocs, maxScore);
143  }
144 
145  /// <summary> A search implementation allowing sorting which spans a new thread for each
146  /// Searchable, waits for each search to complete and merges
147  /// the results back together.
148  /// </summary>
149  public override TopFieldDocs Search(Weight weight, Filter filter, int nDocs, Sort sort)
150  {
151  if (sort == null) throw new ArgumentNullException("sort");
152 
154  object lockObj = new object();
155 
156  Task<TopFieldDocs>[] tasks = new Task<TopFieldDocs>[searchables.Length];
157  for (int i = 0; i < searchables.Length; i++) // search each searchable
158  {
159  int cur = i;
160  tasks[i] =
161  Task<TopFieldDocs>.Factory.StartNew(
162  () => MultiSearcherCallableWithSort(ThreadLock.MonitorLock, lockObj, searchables[cur], weight, filter, nDocs, hq, sort, cur,
163  starts));
164  }
165 
166  int totalHits = 0;
167  float maxScore = float.NegativeInfinity;
168 
169  Task.WaitAll(tasks);
170  foreach (TopFieldDocs topFieldDocs in tasks.Select(x => x.Result))
171  {
172  totalHits += topFieldDocs.TotalHits;
173  maxScore = Math.Max(maxScore, topFieldDocs.MaxScore);
174  }
175 
176  ScoreDoc[] scoreDocs = new ScoreDoc[hq.Size()];
177  for (int i = hq.Size() - 1; i >= 0; i--)
178  scoreDocs[i] = hq.Pop();
179 
180  return new TopFieldDocs(totalHits, scoreDocs, hq.GetFields(), maxScore);
181  }
182 
183  /// <summary>Lower-level search API.
184  ///
185  /// <p/><see cref="Collector.Collect(int)" /> is called for every matching document.
186  ///
187  /// <p/>Applications should only use this if they need <i>all</i> of the
188  /// matching documents. The high-level search API (<see cref="Searcher.Search(Query, int)" />)
189  /// is usually more efficient, as it skips
190  /// non-high-scoring hits.
191  /// <p/>This method cannot be parallelized, because <see cref="Collector"/>
192  /// supports no concurrent access.
193  /// </summary>
194  /// <param name="weight">to match documents
195  /// </param>
196  /// <param name="filter">if non-null, a bitset used to eliminate some documents
197  /// </param>
198  /// <param name="collector">to receive hits
199  ///
200  /// TODO: parallelize this one too
201  /// </param>
202  public override void Search(Weight weight, Filter filter, Collector collector)
203  {
204  for (int i = 0; i < searchables.Length; i++)
205  {
206 
207  int start = starts[i];
208 
209  Collector hc = new AnonymousClassCollector1(collector, start, this);
210 
211  searchables[i].Search(weight, filter, hc);
212  }
213  }
214  }
215 }
216 
217 #endif