1212namespace Umbraco . Cms . Infrastructure . Examine ;
1313
1414/// <summary>
15- /// An indexer for Umbraco content and media
15+ /// An indexer for Umbraco content and media.
1616/// </summary>
1717public class UmbracoContentIndex : UmbracoExamineIndex , IUmbracoContentIndex
1818{
1919 private readonly ISet < string > _idOnlyFieldSet = new HashSet < string > { "id" } ;
2020 private readonly ILogger < UmbracoContentIndex > _logger ;
2121
22+ /// <summary>
23+ /// Initializes a new instance of the <see cref="UmbracoContentIndex"/> class.
24+ /// </summary>
2225 public UmbracoContentIndex (
2326 ILoggerFactory loggerFactory ,
2427 string name ,
@@ -31,12 +34,8 @@ public UmbracoContentIndex(
3134 LanguageService = languageService ;
3235 _logger = loggerFactory . CreateLogger < UmbracoContentIndex > ( ) ;
3336
34- LuceneDirectoryIndexOptions namedOptions = indexOptions . Get ( name ) ;
35- if ( namedOptions == null )
36- {
37- throw new InvalidOperationException (
38- $ "No named { typeof ( LuceneDirectoryIndexOptions ) } options with name { name } ") ;
39- }
37+ LuceneDirectoryIndexOptions namedOptions = indexOptions . Get ( name )
38+ ?? throw new InvalidOperationException ( $ "No named { typeof ( LuceneDirectoryIndexOptions ) } options with name { name } ") ;
4039
4140 if ( namedOptions . Validator is IContentValueSetValidator contentValueSetValidator )
4241 {
@@ -45,25 +44,25 @@ public UmbracoContentIndex(
4544 }
4645 }
4746
47+ /// <summary>
48+ /// Gets the <see cref="ILocalizationService"/>.
49+ /// </summary>
4850 protected ILocalizationService ? LanguageService { get ; }
4951
5052 /// <summary>
5153 /// Explicitly override because we need to do validation differently than the underlying logic
5254 /// </summary>
53- /// <param name="values"></param>
5455 void IIndex . IndexItems ( IEnumerable < ValueSet > values ) => PerformIndexItems ( values , OnIndexOperationComplete ) ;
5556
5657 /// <summary>
57- /// Special check for invalid paths
58+ /// Special check for invalid paths.
5859 /// </summary>
59- /// <param name="values"></param>
60- /// <param name="onComplete"></param>
6160 protected override void PerformIndexItems ( IEnumerable < ValueSet > values , Action < IndexOperationEventArgs > onComplete )
6261 {
6362 // We don't want to re-enumerate this list, but we need to split it into 2x enumerables: invalid and valid items.
6463 // The Invalid items will be deleted, these are items that have invalid paths (i.e. moved to the recycle bin, etc...)
6564 // Then we'll index the Value group all together.
66- var invalidOrValid = values . GroupBy ( v =>
65+ IGrouping < ValueSetValidationStatus , ValueSet > [ ] invalidOrValid = values . GroupBy ( v =>
6766 {
6867 if ( ! v . Values . TryGetValue ( "path" , out IReadOnlyList < object > ? paths ) || paths . Count <= 0 || paths [ 0 ] == null )
6968 {
@@ -82,24 +81,24 @@ protected override void PerformIndexItems(IEnumerable<ValueSet> values, Action<I
8281 var hasDeletes = false ;
8382 var hasUpdates = false ;
8483
85- // ordering by descending so that Filtered/Failed processes first
84+ // Ordering by descending so that Filtered/Failed processes first.
8685 foreach ( IGrouping < ValueSetValidationStatus , ValueSet > group in invalidOrValid . OrderByDescending ( x => x . Key ) )
8786 {
8887 switch ( group . Key )
8988 {
9089 case ValueSetValidationStatus . Valid :
9190 hasUpdates = true ;
9291
93- //these are the valid ones, so just index them all at once
92+ // These are the valid ones, so just index them all at once.
9493 base . PerformIndexItems ( group . ToArray ( ) , onComplete ) ;
9594 break ;
9695 case ValueSetValidationStatus . Failed :
97- // don 't index anything that is invalid
96+ // Don 't index anything that is invalid.
9897 break ;
9998 case ValueSetValidationStatus . Filtered :
10099 hasDeletes = true ;
101100
102- // these are the invalid/filtered items so we'll delete them
101+ // These are the invalid/filtered items so we'll delete them
103102 // since the path is not valid we need to delete this item in
104103 // case it exists in the index already and has now
105104 // been moved to an invalid parent.
@@ -110,7 +109,7 @@ protected override void PerformIndexItems(IEnumerable<ValueSet> values, Action<I
110109
111110 if ( ( hasDeletes && ! hasUpdates ) || ( ! hasDeletes && ! hasUpdates ) )
112111 {
113- //we need to manually call the completed method
112+ // We need to manually call the completed method.
114113 onComplete ( new IndexOperationEventArgs ( this , 0 ) ) ;
115114 }
116115 }
@@ -133,26 +132,44 @@ protected override void PerformDeleteFromIndex(IEnumerable<string> itemIds, Acti
133132 {
134133 var nodeId = idsAsList [ i ] ;
135134
136- //find all descendants based on path
135+ // Find all descendants based on path.
137136 var descendantPath = $@ "\-1\,*{ nodeId } \,*";
138137 var rawQuery = $ "{ UmbracoExamineFieldNames . IndexPathFieldName } :{ descendantPath } ";
139138 IQuery ? c = Searcher . CreateQuery ( ) ;
140139 IBooleanOperation ? filtered = c . NativeQuery ( rawQuery ) ;
141140 IOrdering ? selectedFields = filtered . SelectFields ( _idOnlyFieldSet ) ;
142141 ISearchResults ? results = selectedFields . Execute ( ) ;
143- if ( _logger . IsEnabled ( Microsoft . Extensions . Logging . LogLevel . Debug ) )
142+ if ( _logger . IsEnabled ( LogLevel . Debug ) )
144143 {
145144 _logger . LogDebug ( "DeleteFromIndex with query: {Query} (found {TotalItems} results)" , rawQuery , results . TotalItemCount ) ;
146145 }
147146
147+ // Avoid unnecessary operations when we have no items to handle. This is necessary for ExamineX's Elastic implementation
148+ // which doesn't support making requests with an empty collection.
149+ if ( results . TotalItemCount == 0 )
150+ {
151+ continue ;
152+ }
153+
148154 var toRemove = results . Select ( x => x . Id ) . ToList ( ) ;
149- // delete those descendants (ensure base. is used here so we aren't calling ourselves!)
155+
156+ // Delete those descendants (ensure base. is used here so we aren't calling ourselves!).
150157 base . PerformDeleteFromIndex ( toRemove , null ) ;
151158
152- // remove any ids from our list that were part of the descendants
159+ // Remove any ids from our list that were part of the descendants.
153160 idsAsList . RemoveAll ( x => toRemove . Contains ( x ) ) ;
154161 }
155162
156- base . PerformDeleteFromIndex ( idsAsList , onComplete ) ;
163+ // Avoid unnecessary operations when we have no items to handle. This is necessary for ExamineX's Elastic implementation
164+ // which doesn't support making requests with an empty collection.
165+ if ( idsAsList . Count > 0 )
166+ {
167+ base . PerformDeleteFromIndex ( idsAsList , onComplete ) ;
168+ }
169+ else
170+ {
171+ // Manually invoke the complete callback if provided, so we maintain consistency when there is or isn't anything to delete.
172+ onComplete ? . Invoke ( new IndexOperationEventArgs ( this , 0 ) ) ;
173+ }
157174 }
158175}
0 commit comments