@@ -28,11 +28,11 @@ public sealed class SearchClient
2828 {
2929 public SearchClient ( SearchConfig config )
3030 {
31- Config = config ;
31+ Config = config ;
3232
3333 Results = new List < SearchResult > ( ) ;
3434 FilteredResults = new List < SearchResult > ( ) ;
35-
35+ DirectResults = new List < ImageResult > ( ) ;
3636 Reload ( ) ;
3737 }
3838
@@ -55,8 +55,8 @@ public SearchClient(SearchConfig config)
5555 /// Contains search results
5656 /// </summary>
5757 public List < SearchResult > Results { get ; }
58-
5958
59+ public List < ImageResult > DirectResults { get ; }
6060
6161 /// <summary>
6262 /// Contains filtered search results
@@ -87,12 +87,20 @@ public void Reload()
8787 public void Reset ( )
8888 {
8989 Results . Clear ( ) ;
90+ DirectResults . Clear ( ) ;
9091 FilteredResults . Clear ( ) ;
9192 IsComplete = false ;
9293 Reload ( ) ;
9394 }
9495
95- #region Primary operations
96+ /*
97+ * TODO
98+ *
99+ * Queue a thread to run in the background upon each result completion
100+ * in which the thread scans for direct images, instead of doing the scanning post hoc
101+ */
102+
103+ #region
96104
97105 /// <summary>
98106 /// Performs an image search asynchronously.
@@ -106,7 +114,7 @@ public async Task RunSearchAsync()
106114 var tasks = new List < Task < SearchResult > > ( Engines . Select ( e =>
107115 {
108116 var task = e . GetResultAsync ( Config . Query ) ;
109-
117+
110118 return task ;
111119 } ) ) ;
112120
@@ -151,6 +159,8 @@ public async Task RunSearchAsync()
151159 isFiltered = null ;
152160 }
153161
162+ ThreadPool . QueueUserWorkItem ( ( c ) => FindDirectResults ( c , value ) ) ;
163+
154164 // Call event
155165 ResultCompleted ? . Invoke ( null , new ResultCompletedEventArgs ( value )
156166 {
@@ -163,34 +173,78 @@ public async Task RunSearchAsync()
163173
164174 Trace . WriteLine ( $ "{ nameof ( SearchClient ) } : Search complete", C_SUCCESS ) ;
165175
176+
166177 var args = new SearchCompletedEventArgs
167178 {
168- Results = Results ,
169- Detailed = new Lazy < ImageResult > ( ( ) => GetDetailedImageResults ( ) . FirstOrDefault ( ) ) ,
170- Direct = new Lazy < ImageResult [ ] > ( ( ) =>
179+ Results = Results ,
180+ FirstDetailed = RefineFilter ( DetailPredicate ) . FirstOrDefault ( ) ,
181+ /* Direct = new Lazy<ImageResult[]>(() =>
171182 {
172183 Debug.WriteLine($"{nameof(SearchClient)}: Finding direct results", C_DEBUG);
173184 ImageResult[] direct = GetDirectImageResults();
174185
175186 return direct;
176187 }),
177- FirstDirect = new Lazy < ImageResult > ( GetDirectImageResult )
188+ FirstDirect = new Lazy<ImageResult>(GetDirectImageResult)*/
189+ Direct = DirectResults ,
190+ FirstDirect = DirectResults . FirstOrDefault ( )
178191 } ;
179192
180193 SearchCompleted ? . Invoke ( null , args ) ;
194+
195+
181196 }
182197
198+ private void FindDirectResults ( object state , SearchResult value , int count = 5 , int i = 10 )
199+ {
200+ var u = value . OtherResults . Union ( new [ ] { value . PrimaryResult } ) . ToList ( ) ;
201+ var u2 = RefineFilter ( u , DirectFilterPredicate ) . ToList ( ) ;
183202
184- /*
185- * TODO
186- *
187- * Queue a thread to run in the background upon each result completion
188- * in which the thread scans for direct images, instead of doing the scanning post hoc
189- */
203+ Debug . WriteLine ( $ "*{ nameof ( SearchClient ) } : Found { u2 . Count } best results", C_DEBUG ) ;
190204
191- #endregion
205+ var query = u2 . Where ( x => x . CheckDirect ( DirectImageCriterion . Regex ) )
206+ . Take ( i )
207+ . AsParallel ( ) ;
208+
209+ List < ImageResult > images = query . Where ( x => x . CheckDirect ( DirectImageCriterion . Binary ) )
210+ . Take ( count )
211+ . ToList ( ) ;
212+
213+ if ( images . Any ( ) ) {
214+ Debug . WriteLine ( $ "*{ nameof ( SearchClient ) } : Found { images . Count } direct results", C_DEBUG ) ;
215+ DirectResults . AddRange ( images ) ;
192216
193- #region Secondary operations
217+ DirectFound ? . Invoke ( null , new DirectFoundEventArgs ( )
218+ {
219+ Direct = images ,
220+ } ) ;
221+ }
222+ }
223+
224+
225+ public static IEnumerable < ImageResult > RefineFilter ( List < ImageResult > results ,
226+ Predicate < SearchResult > predicate )
227+ {
228+ var query = results . AsParallel ( )
229+ . OrderByDescending ( r => r . Similarity )
230+ . ThenByDescending ( r => r . PixelResolution )
231+ . ThenByDescending ( r => r . DetailScore ) ;
232+
233+ return query ;
234+ }
235+
236+ public IEnumerable < ImageResult > RefineFilter ( Predicate < SearchResult > predicate )
237+ {
238+ var query = Results . Where ( r => predicate ( r ) )
239+ . SelectMany ( r =>
240+ {
241+ List < ImageResult > otherResults = r . OtherResults ;
242+ otherResults . Insert ( 0 , r . PrimaryResult ) ;
243+ return otherResults ;
244+ } ) . ToList ( ) ;
245+
246+ return RefineFilter ( query , predicate ) ;
247+ }
194248
195249 /// <summary>
196250 /// Refines search results by searching with the most-detailed result (<see cref="GetDirectImageResult" />).
@@ -203,7 +257,7 @@ public async Task RefineSearchAsync()
203257
204258 Debug . WriteLine ( $ "{ nameof ( SearchClient ) } : Finding best result", C_DEBUG ) ;
205259
206- var directResult = GetDirectImageResult ( ) ;
260+ var directResult = DirectResults . FirstOrDefault ( ) ;
207261
208262 if ( directResult == null ) {
209263 throw new SmartImageException ( "Could not find best result" ) ;
@@ -237,67 +291,6 @@ public List<SearchResult> MaximizeResults<T>(Func<SearchResult, T> property)
237291 return res ;
238292 }
239293
240- [ CanBeNull ]
241- public ImageResult GetDirectImageResult ( ) => GetDirectImageResults ( 1 ) ? . FirstOrDefault ( ) ;
242-
243- public ImageResult [ ] GetDirectImageResults ( int count = 5 )
244- {
245- var imageResults = RefineFilter ( DirectFilterPredicate ) . ToList ( ) ;
246-
247- Debug . WriteLine ( $ "{ nameof ( SearchClient ) } : Found { imageResults . Count } best results", C_DEBUG ) ;
248-
249- const int i = 10 ;
250-
251- var query = imageResults . Where ( x => x . CheckDirect ( DirectImageCriterion . Regex ) )
252- . Take ( i )
253- . AsParallel ( ) ;
254-
255- List < ImageResult > images ;
256-
257- if ( count == 1 )
258- {
259- images = new List < ImageResult >
260- {
261- query . FirstOrDefault ( x => x . CheckDirect ( DirectImageCriterion . Binary ) )
262- } ;
263-
264- }
265- else
266- {
267- images = query . Where ( x => x . CheckDirect ( DirectImageCriterion . Binary ) )
268- . Take ( count )
269- // .OrderByDescending(r => r.Similarity)
270- . ToList ( ) ;
271- }
272-
273- Debug . WriteLine ( $ "{ nameof ( SearchClient ) } : Found { images . Count } direct results", C_DEBUG ) ;
274-
275- return images . ToArray ( ) ;
276- }
277-
278- /// <summary>
279- /// Selects the most detailed results.
280- /// </summary>
281- /// <returns>The <see cref="ImageResult" />s of the best <see cref="Results" /></returns>
282- public ImageResult [ ] GetDetailedImageResults ( ) => RefineFilter ( DetailPredicate ) . ToArray ( ) ;
283-
284- public IEnumerable < ImageResult > RefineFilter ( Predicate < SearchResult > predicate )
285- {
286- var query = Results . Where ( r => predicate ( r ) )
287- . SelectMany ( r =>
288- {
289- List < ImageResult > otherResults = r . OtherResults ;
290- otherResults . Insert ( 0 , r . PrimaryResult ) ;
291- return otherResults ;
292- } )
293- . AsParallel ( )
294- . OrderByDescending ( r => r . Similarity )
295- . ThenByDescending ( r => r . PixelResolution )
296- . ThenByDescending ( r => r . DetailScore ) ;
297-
298- return query ;
299- }
300-
301294 #endregion
302295
303296 public static BaseUploadEngine [ ] GetAllUploadEngines ( )
@@ -326,29 +319,35 @@ public static BaseSearchEngine[] GetAllSearchEngines()
326319 /// </summary>
327320 public event EventHandler < SearchCompletedEventArgs > SearchCompleted ;
328321
322+ public event EventHandler < DirectFoundEventArgs > DirectFound ;
323+
329324 private static readonly Predicate < SearchResult > DetailPredicate = r => r . IsNonPrimitive ;
330325
331326 private static readonly SmartImageException SearchException = new ( "Search must be completed" ) ;
332327
333328 private static readonly Predicate < SearchResult > DirectFilterPredicate = r => DetailPredicate ( r )
334- && r . Engine . SearchType . HasFlag ( EngineSearchType . Image ) ;
329+ && r . Engine . SearchType . HasFlag ( EngineSearchType . Image ) ;
330+ }
331+
332+ public sealed class DirectFoundEventArgs : EventArgs
333+ {
334+ public List < ImageResult > Direct { get ; init ; }
335335 }
336336
337337 public sealed class SearchCompletedEventArgs : EventArgs
338338 {
339339 public List < SearchResult > Results { get ; init ; }
340340
341341 [ CanBeNull ]
342- public Lazy < ImageResult [ ] > Direct { get ; internal set ; }
342+ public List < ImageResult > Direct { get ; internal set ; }
343343
344344 [ CanBeNull ]
345- public Lazy < ImageResult > FirstDirect { get ; internal set ; }
345+ public ImageResult FirstDirect { get ; internal set ; }
346346
347347
348348 [ CanBeNull ]
349- public Lazy < ImageResult > Detailed { get ; internal set ; }
350-
351- // todo: maybe lazy list? i.e., each item is a lazy load
349+ public ImageResult FirstDetailed { get ; internal set ; }
350+
352351 }
353352
354353 public sealed class ResultCompletedEventArgs : EventArgs
0 commit comments