@@ -16,6 +16,7 @@ You should have received a copy of the GNU Affero General Public License
1616along with this program. If not, see <http://www.gnu.org/licenses/>.
1717*/
1818
19+ using System . Collections ;
1920using System . Runtime . CompilerServices ;
2021using System . Text . Json ;
2122using Figment . Common ;
@@ -192,19 +193,88 @@ public override async IAsyncEnumerable<Reference> GetBySchemaAsync(string schema
192193 Type = Reference . ReferenceType . Thing
193194 } ;
194195 }
196+ }
197+ else
198+ {
199+ // No index, so go the expensive route
200+ AmbientErrorContext . Provider . LogWarning ( $ "Missing index at: { indexFilePath } ") ;
201+ await foreach ( var ( reference , name ) in GetAll ( cancellationToken ) )
202+ {
203+ if ( cancellationToken . IsCancellationRequested )
204+ yield break ;
205+
206+ var thing = await LoadAsync ( reference . Guid , cancellationToken ) ;
207+ if ( thing != null && thing . SchemaGuids . Any ( s => string . Equals ( s , schemaGuid , StringComparison . Ordinal ) ) )
208+ yield return reference ;
209+ }
210+ }
211+ }
212+
213+ /// <inheritdoc/>
214+ public override async IAsyncEnumerable < Thing > FindBySchemaAndPropertyValue (
215+ string schemaGuid ,
216+ string propName ,
217+ object ? propValue ,
218+ IComparer comparer ,
219+ [ EnumeratorCancellation ] CancellationToken cancellationToken )
220+ {
221+ // This might look a lot like GetBySchemaAsync, but it is a special version
222+ // that holds onto the loaded thing object for property testing.
223+ // It also cannot use quite the same index-shortcut in GetBySchemaAsync because
224+ // it returns a fully loaded Thing, not just a reference.
225+ ArgumentException . ThrowIfNullOrWhiteSpace ( schemaGuid ) ;
226+ ArgumentException . ThrowIfNullOrWhiteSpace ( propName ) ;
227+ ArgumentNullException . ThrowIfNull ( comparer ) ;
228+
229+ var thingDir = new DirectoryInfo ( ThingDirectoryPath ) ;
230+ if ( ! thingDir . Exists )
195231 yield break ;
232+
233+ async Task < bool > thingMatches ( Thing thing )
234+ {
235+ var prop = await thing . GetPropertyByTrueNameAsync ( propName , cancellationToken ) ;
236+ if ( prop == null )
237+ return false ; // Property is missing from thing, so it's not a valid thing of the specified schema.
238+
239+ if ( ! prop . HasValue && propValue == null )
240+ return true ; // We want nulls, and this is null.
241+
242+ if ( ! prop . HasValue )
243+ return false ; // We do not want nulls, and this is null.
244+
245+ return comparer . Compare ( prop . Value . Value , propValue ) == 0 ;
196246 }
197247
198- // No index, so go the expensive route
199- AmbientErrorContext . Provider . LogWarning ( $ "Missing index at: { indexFilePath } ") ;
200- await foreach ( var thingRef in GetAll ( cancellationToken ) )
248+ var indexFilePath = Path . Combine ( thingDir . FullName , $ "_thing.schema.{ schemaGuid } .csv") ;
249+ if ( File . Exists ( indexFilePath ) )
201250 {
202- if ( cancellationToken . IsCancellationRequested )
203- yield break ;
251+ // Use index
252+ await foreach ( var entry in IndexManager . LookupAsync ( indexFilePath , e => true , cancellationToken ) )
253+ {
254+ if ( cancellationToken . IsCancellationRequested )
255+ yield break ;
204256
205- var thing = await LoadAsync ( thingRef . reference . Guid , cancellationToken ) ;
206- if ( thing != null && thing . SchemaGuids . Any ( s => string . Equals ( s , schemaGuid , StringComparison . Ordinal ) ) )
207- yield return thingRef . reference ;
257+ var guid = Path . GetFileName ( entry . Value ) . Split ( '.' ) [ 0 ] ;
258+ var thing = await LoadAsync ( guid , cancellationToken ) ;
259+ if ( thing != null && await thingMatches ( thing ) )
260+ yield return thing ;
261+ }
262+ }
263+ else
264+ {
265+ // No index, so go the expensive route
266+ AmbientErrorContext . Provider . LogWarning ( $ "Missing index at: { indexFilePath } ") ;
267+ await foreach ( var ( reference , name ) in GetAll ( cancellationToken ) )
268+ {
269+ if ( cancellationToken . IsCancellationRequested )
270+ yield break ;
271+
272+ var thing = await LoadAsync ( reference . Guid , cancellationToken ) ;
273+ if ( thing != null
274+ && thing . SchemaGuids . Any ( s => string . Equals ( s , schemaGuid , StringComparison . Ordinal ) )
275+ && await thingMatches ( thing ) )
276+ yield return thing ;
277+ }
208278 }
209279 }
210280
0 commit comments