1010using NuGet . Common ;
1111using NuGet . Protocol ;
1212using NuGet . Protocol . Core . Types ;
13- using NuGet . Versioning ;
1413using PackageManager . Logging ;
1514using PackageManager . Models ;
1615
@@ -61,29 +60,11 @@ private SearchOptions EnsureOptions(SearchOptions options)
6160 return options ;
6261 }
6362
64- private Task < IEnumerable < IPackageSearchMetadata > > SearchAsync ( PackageSearchResource search , string searchText , SearchOptions options , CancellationToken cancellationToken )
65- => search . SearchAsync ( searchText , new SearchFilter ( options . IsPrereleaseIncluded ) , options . PageIndex * options . PageSize , options . PageSize , nuGetLog , cancellationToken ) ;
66-
6763 public async Task < IEnumerable < IPackage > > SearchAsync ( IEnumerable < IPackageSource > packageSources , string searchText , SearchOptions options = default , CancellationToken cancellationToken = default )
6864 {
69- NuGetSearchTerm term = new NuGetSearchTerm ( ) ;
70- term . Id . Add ( searchText ) ;
65+ var ( feedTerm , localTerm ) = PrepareSearchTerms ( searchText ) ;
7166
72- queryTransformer . Transform ( term ) ;
73- term . Id . Remove ( searchText ) ;
74-
75- NuGetSearchTerm lateTerm = null ;
76- if ( term . IsEmpty ( ) )
77- {
78- term . Id . Add ( searchText ) ;
79- }
80- else
81- {
82- lateTerm = term . Clone ( ) ;
83- lateTerm . Id . Add ( searchText ) ;
84- }
85-
86- log . Debug ( $ "Searching - user text:'{ searchText } '; target query:'{ term } '.") ;
67+ log . Debug ( $ "Searching - user text:'{ searchText } '; feed query:'{ feedTerm } '.") ;
8768
8869 options = EnsureOptions ( options ) ;
8970
@@ -96,7 +77,6 @@ public async Task<IEnumerable<IPackage>> SearchAsync(IEnumerable<IPackageSource>
9677 {
9778 log . Debug ( $ "Loading page '{ options . PageIndex } '.") ;
9879
99- bool hasItems = false ;
10080 foreach ( IPackageSource packageSource in sources )
10181 {
10282 log . Debug ( $ "Searching in '{ packageSource . Uri } '.") ;
@@ -106,60 +86,15 @@ public async Task<IEnumerable<IPackage>> SearchAsync(IEnumerable<IPackageSource>
10686 if ( search == null )
10787 {
10888 log . Debug ( $ "Source skipped, because it doesn't provide '{ nameof ( PackageSearchResource ) } '.") ;
109- continue ;
110- }
111-
112- NuGetSearchTerm localTerm = null ;
113- bool clearLateTerm = false ;
114- if ( search is LocalPackageSearchResource )
115- {
116- // Searching a feed from folder.
117- localTerm = term ;
118- term = new NuGetSearchTerm ( ) ;
119-
120- if ( lateTerm == null )
121- {
122- lateTerm = localTerm ;
123- clearLateTerm = true ;
124- }
125- }
126-
127- int sourceSearchPackageCount = 0 ;
128- foreach ( IPackageSearchMetadata package in await SearchAsync ( search , term . ToString ( ) , options , cancellationToken ) )
129- {
130- log . Debug ( $ "Found '{ package . Identity } '.") ;
13189
132- hasItems = true ;
133- if ( result . Count >= options . PageSize )
134- break ;
135-
136- if ( lateTerm != null && ! lateTerm . IsMatched ( package ) )
137- {
138- log . Debug ( $ "Package skipped by late search term '{ lateTerm } '.") ;
139- continue ;
140- }
141-
142- await AddPackageAsync ( result , repository , package , options . IsPrereleaseIncluded , cancellationToken ) ;
143- sourceSearchPackageCount ++ ;
90+ sourcesToSkip . Add ( packageSource ) ;
91+ continue ;
14492 }
14593
146- // If package source reached end, skip it from next probing.
147- if ( sourceSearchPackageCount < options . PageSize )
94+ if ( ! await ApplyLocalResourceSearchAsync ( result , repository , search , feedTerm , localTerm , options , cancellationToken ) )
14895 sourcesToSkip . Add ( packageSource ) ;
149-
150- if ( localTerm != null )
151- {
152- term = localTerm ;
153- localTerm = null ;
154-
155- if ( clearLateTerm )
156- lateTerm = null ;
157- }
15896 }
15997
160- if ( ! hasItems )
161- break ;
162-
16398 options = new SearchOptions ( )
16499 {
165100 PageIndex = options . PageIndex + 1 ,
@@ -168,12 +103,99 @@ public async Task<IEnumerable<IPackage>> SearchAsync(IEnumerable<IPackageSource>
168103
169104 foreach ( IPackageSource source in sourcesToSkip )
170105 sources . Remove ( source ) ;
106+
107+ if ( sources . Count == 0 )
108+ break ;
171109 }
172110
173111 log . Debug ( $ "Search completed. Found '{ result . Count } ' items.") ;
174112 return result ;
175113 }
176114
115+ /// <summary>
116+ /// Prepares instance of terms for filtering in feed and in-app.
117+ /// </summary>
118+ /// <remarks>
119+ /// localTerm should always have all search terms.
120+ /// </remarks>
121+ private ( NuGetSearchTerm feedTerm , NuGetSearchTerm localTerm ) PrepareSearchTerms ( string searchText )
122+ {
123+ NuGetSearchTerm feedTerm = new NuGetSearchTerm ( ) ;
124+ feedTerm . Id . Add ( searchText ) ;
125+
126+ queryTransformer . Transform ( feedTerm ) ;
127+ feedTerm . Id . Remove ( searchText ) ;
128+
129+ NuGetSearchTerm localTerm = null ;
130+ if ( feedTerm . IsEmpty ( ) )
131+ {
132+ feedTerm . Id . Add ( searchText ) ;
133+ }
134+ else
135+ {
136+ localTerm = feedTerm . Clone ( ) ;
137+ localTerm . Id . Add ( searchText ) ;
138+ }
139+
140+ return ( feedTerm , localTerm ) ;
141+ }
142+
143+ /// <summary>
144+ /// Tries to apply special conditions for looking in local folder feed.
145+ /// </summary>
146+ /// <returns><c>true</c> if search reached the end of the feed; <c>false</c> otherwise.</returns>
147+ private Task < bool > ApplyLocalResourceSearchAsync ( List < IPackage > result , SourceRepository repository , PackageSearchResource search , NuGetSearchTerm feedTerm , NuGetSearchTerm localTerm , SearchOptions options , CancellationToken cancellationToken )
148+ {
149+ if ( search is LocalPackageSearchResource )
150+ {
151+ // Searching a feed from folder. Look for all packages and then filter in-app.
152+ if ( localTerm == null )
153+ localTerm = feedTerm ;
154+
155+ feedTerm = new NuGetSearchTerm ( ) ;
156+ }
157+
158+ return ApplySearchAsync ( result , repository , search , feedTerm , localTerm , options , cancellationToken ) ;
159+ }
160+
161+ /// <summary>
162+ /// Execute search on <paramref name="search"/>.
163+ /// </summary>
164+ /// <returns><c>true</c> if search reached the end of the feed; <c>false</c> otherwise.</returns>
165+ private async Task < bool > ApplySearchAsync ( List < IPackage > result , SourceRepository repository , PackageSearchResource search , NuGetSearchTerm feedTerm , NuGetSearchTerm localTerm , SearchOptions options , CancellationToken cancellationToken )
166+ {
167+ if ( localTerm != null && options . PageSize == 1 )
168+ options . PageSize = 10 ;
169+
170+ int sourceSearchPackageCount = 0 ;
171+ foreach ( IPackageSearchMetadata package in await SearchAsync ( search , feedTerm . ToString ( ) , options , cancellationToken ) )
172+ {
173+ sourceSearchPackageCount ++ ;
174+
175+ log . Debug ( $ "Found '{ package . Identity } '.") ;
176+
177+ if ( result . Count >= options . PageSize )
178+ break ;
179+
180+ if ( localTerm != null && ! localTerm . IsMatched ( package ) )
181+ {
182+ log . Debug ( $ "Package skipped by late search term '{ localTerm } '.") ;
183+ continue ;
184+ }
185+
186+ await AddPackageAsync ( result , repository , package , options . IsPrereleaseIncluded , cancellationToken ) ;
187+ }
188+
189+ // If package source reached end, skip it from next probing.
190+ if ( sourceSearchPackageCount < options . PageSize )
191+ return false ;
192+
193+ return true ;
194+ }
195+
196+ private Task < IEnumerable < IPackageSearchMetadata > > SearchAsync ( PackageSearchResource search , string searchText , SearchOptions options , CancellationToken cancellationToken )
197+ => search . SearchAsync ( searchText , new SearchFilter ( options . IsPrereleaseIncluded ) , options . PageIndex * options . PageSize , options . PageSize , nuGetLog , cancellationToken ) ;
198+
177199 private async Task AddPackageAsync ( List < IPackage > result , SourceRepository repository , IPackageSearchMetadata package , bool isPrereleaseIncluded , CancellationToken cancellationToken )
178200 {
179201 NuGetPackageFilterResult filterResult = await filter . IsPassedAsync ( repository , package , cancellationToken ) ;
0 commit comments